LCOV - code coverage report
Current view: top level - flamenco/runtime - fd_runtime.h (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 18 54 33.3 %
Date: 2025-09-17 04:38:03 Functions: 0 104 0.0 %

          Line data    Source code
       1             : #ifndef HEADER_fd_src_flamenco_runtime_fd_runtime_h
       2             : #define HEADER_fd_src_flamenco_runtime_fd_runtime_h
       3             : 
       4             : #include "stdarg.h"
       5             : 
       6             : #include "fd_runtime_err.h"
       7             : #include "fd_runtime_init.h"
       8             : #include "fd_rocksdb.h"
       9             : #include "fd_acc_mgr.h"
      10             : #include "fd_hashes.h"
      11             : #include "../features/fd_features.h"
      12             : #include "context/fd_capture_ctx.h"
      13             : #include "context/fd_exec_txn_ctx.h"
      14             : #include "info/fd_runtime_block_info.h"
      15             : #include "info/fd_instr_info.h"
      16             : #include "../../disco/pack/fd_microblock.h"
      17             : #include "info/fd_microblock_info.h"
      18             : #include "../../ballet/sbpf/fd_sbpf_loader.h"
      19             : #include "../vm/fd_vm_base.h"
      20             : 
      21             : /* Various constant values used by the runtime. */
      22             : 
      23           0 : #define MICRO_LAMPORTS_PER_LAMPORT (1000000UL)
      24             : 
      25             : #define DEFAULT_HASHES_PER_TICK  (12500)
      26             : #define UPDATED_HASHES_PER_TICK2 (17500)
      27             : #define UPDATED_HASHES_PER_TICK3 (27500)
      28             : #define UPDATED_HASHES_PER_TICK4 (47500)
      29             : #define UPDATED_HASHES_PER_TICK5 (57500)
      30             : #define UPDATED_HASHES_PER_TICK6 (62500)
      31             : 
      32             : #define FD_RUNTIME_TRACE_NONE   (0)
      33             : #define FD_RUNTIME_TRACE_SAVE   (1)
      34             : #define FD_RUNTIME_TRACE_REPLAY (2)
      35             : 
      36             : #define FD_RUNTIME_OFFLINE_NUM_ROOT_BLOCKS (6UL) /* 6 root blocks for offline replay */
      37             : 
      38             : #define FD_RENT_EXEMPT_RENT_EPOCH (ULONG_MAX)
      39             : 
      40           0 : #define SECONDS_PER_YEAR ((double)(365.242199 * 24.0 * 60.0 * 60.0))
      41             : 
      42             : /* TODO: increase this to default once we have enough memory to support a 95G status cache. */
      43             : #define MAX_CACHE_TXNS_PER_SLOT (FD_TXNCACHE_DEFAULT_MAX_TRANSACTIONS_PER_SLOT / 8)
      44             : 
      45             : /*
      46             :  * fd_block_entry_batch_t is a microblock/entry batch within a block.
      47             :  * The offset is relative to the start of the block's data region,
      48             :  * and indicates where the batch ends.  The (exclusive) end offset of
      49             :  * batch i is the (inclusive) start offset of batch i+1.  The 0th batch
      50             :  * always starts at offset 0.
      51             :  * On the wire, the presence of one of the COMPLETE flags in a data
      52             :  * shred marks the end of a batch.
      53             :  * In other words, batch ends are aligned with shred ends, and batch
      54             :  * starts are aligned with shred starts.  Usually a batch comprises
      55             :  * multiple shreds, and a block comprises multiple batches.
      56             :  * This information is useful because bincode deserialization needs to
      57             :  * be performed on a per-batch basis.  Precisely a single array of
      58             :  * microblocks/entries is expected to be deserialized from a batch.
      59             :  * Trailing bytes in each batch are ignored by default.
      60             :  */
      61             : struct fd_block_entry_batch {
      62             :   ulong end_off; /* exclusive */
      63             : };
      64             : typedef struct fd_block_entry_batch fd_block_entry_batch_t;
      65             : 
      66             : /* The below logic is used to size out the memory footprint generated by the
      67             :    runtime during transaction execution. */
      68             : 
      69             : /* The prevailing layout we have in the runtime is the meta followed by
      70             :    the account's data. This struct encodes that layout and asserts that
      71             :    the alignment requirements of the constituents are satisfied. */
      72             : // TODO: Use this struct at allocation sites so it's clear we use this layout
      73             : struct __attribute__((packed)) fd_account_rec {
      74             :   fd_account_meta_t meta;
      75             :   uchar data[] __attribute__((aligned(8)));
      76             : };
      77             : typedef struct fd_account_rec fd_account_rec_t;
      78           0 : #define FD_ACCOUNT_REC_ALIGN      (8UL)
      79             : #define FD_ACCOUNT_REC_DATA_ALIGN (8UL)
      80             : FD_STATIC_ASSERT( FD_ACCOUNT_REC_ALIGN>=alignof(fd_account_meta_t), account_rec_meta_align );
      81             : FD_STATIC_ASSERT( FD_ACCOUNT_REC_ALIGN>=FD_ACCOUNT_REC_DATA_ALIGN,  account_rec_data_align );
      82             : FD_STATIC_ASSERT( (offsetof(fd_account_rec_t, meta)%alignof(fd_account_meta_t))==0, account_rec_meta_offset );
      83             : FD_STATIC_ASSERT( (offsetof(fd_account_rec_t, data)%FD_ACCOUNT_REC_DATA_ALIGN )==0, account_rec_data_offset );
      84             : 
      85           0 : #define MAX_PERMITTED_DATA_INCREASE (10240UL) // 10KB
      86           6 : #define FD_BPF_ALIGN_OF_U128        (8UL    )
      87             : FD_STATIC_ASSERT( FD_BPF_ALIGN_OF_U128==FD_ACCOUNT_REC_DATA_ALIGN, input_data_align );
      88           0 : #define FD_RUNTIME_INPUT_REGION_ALLOC_ALIGN_UP (16UL)
      89             : 
      90             : /******** These macros bound out memory footprint ********/
      91             : 
      92             : /* The tight upper bound on borrowed account footprint over the
      93             :    execution of a single transaction. */
      94           6 : #define FD_RUNTIME_BORROWED_ACCOUNT_FOOTPRINT (MAX_TX_ACCOUNT_LOCKS * FD_ULONG_ALIGN_UP( FD_ACC_TOT_SZ_MAX, FD_ACCOUNT_REC_ALIGN ))
      95             : 
      96             : /* The tight-ish upper bound on input region footprint over the
      97             :    execution of a single transaction. See input serialization code for
      98             :    reference: fd_bpf_loader_serialization.c
      99             : 
     100             :    This bound is based off of the transaction MTU. We consider the
     101             :    question of what kind of transaction one would construct to
     102             :    maximally bloat the input region.
     103             :    The worst case scenario is when every nested instruction references
     104             :    all unique accounts in the transaction. A transaction can lock a max
     105             :    of MAX_TX_ACCOUNT_LOCKS accounts. Then all remaining input account
     106             :    references are going to be duplicates, which cost 1 byte to specify
     107             :    offset in payload, and which cost 8 bytes during serialization. Then
     108             :    there would be 0 bytes of instruction data, because they exist byte
     109             :    for byte in the raw payload, which is not a worthwhile bloat factor.
     110             :  */
     111             : #define FD_RUNTIME_INPUT_REGION_UNIQUE_ACCOUNT_FOOTPRINT(direct_mapping)                                                                                              \
     112             :                                                         (1UL                         /* dup byte          */                                                        + \
     113             :                                                          sizeof(uchar)               /* is_signer         */                                                        + \
     114             :                                                          sizeof(uchar)               /* is_writable       */                                                        + \
     115             :                                                          sizeof(uchar)               /* executable        */                                                        + \
     116             :                                                          sizeof(uint)                /* original_data_len */                                                        + \
     117             :                                                          sizeof(fd_pubkey_t)         /* key               */                                                        + \
     118             :                                                          sizeof(fd_pubkey_t)         /* owner             */                                                        + \
     119             :                                                          sizeof(ulong)               /* lamports          */                                                        + \
     120             :                                                          sizeof(ulong)               /* data len          */                                                        + \
     121             :                                                          (direct_mapping ? FD_BPF_ALIGN_OF_U128 : FD_ULONG_ALIGN_UP( FD_RUNTIME_ACC_SZ_MAX, FD_BPF_ALIGN_OF_U128 )) + \
     122             :                                                          MAX_PERMITTED_DATA_INCREASE                                                                                + \
     123             :                                                          sizeof(ulong))              /* rent_epoch        */
     124             : 
     125             : #define FD_RUNTIME_INPUT_REGION_INSN_FOOTPRINT(account_lock_limit, direct_mapping)                                                                       \
     126           6 :                                               (FD_ULONG_ALIGN_UP( (sizeof(ulong)         /* acct_cnt       */                                          + \
     127           6 :                                                                    account_lock_limit*FD_RUNTIME_INPUT_REGION_UNIQUE_ACCOUNT_FOOTPRINT(direct_mapping) + \
     128           6 :                                                                    sizeof(ulong)         /* instr data len */                                          + \
     129           6 :                                                                                          /* No instr data  */                                            \
     130           6 :                                                                    sizeof(fd_pubkey_t)), /* program id     */                                            \
     131           6 :                                                                    FD_RUNTIME_INPUT_REGION_ALLOC_ALIGN_UP ) + FD_BPF_ALIGN_OF_U128)
     132             : 
     133             : #define FD_RUNTIME_INPUT_REGION_TXN_FOOTPRINT(account_lock_limit, direct_mapping)                                                                           \
     134           6 :                                              ((FD_MAX_INSTRUCTION_STACK_DEPTH*FD_RUNTIME_INPUT_REGION_INSN_FOOTPRINT(account_lock_limit, direct_mapping)) + \
     135           6 :                                               ((FD_TXN_MTU-FD_TXN_MIN_SERIALIZED_SZ-account_lock_limit)*8UL)) /* We can have roughly this much duplicate offsets */
     136             : 
     137             : /* Bincode valloc footprint over the execution of a single transaction.
     138             :    As well as other footprint specific to each native program type.
     139             : 
     140             :    N.B. We know that bincode valloc footprint is bounded, because
     141             :    whenever we alloc something, we advance our pointer into the binary
     142             :    buffer, so eventually we are gonna reach the end of the buffer.
     143             :    This buffer is usually backed by and ultimately bounded in size by
     144             :    either accounts data or the transaction MTU.
     145             : 
     146             :    That being said, it's not obvious what the tight upper bound would
     147             :    be for allocations across all possible execution paths of all native
     148             :    programs, including possible CPIs from native programs.  The
     149             :    footprint estimate here is based on a manual review of our native
     150             :    program implementation.  Note that even if the possible paths remain
     151             :    steady at the Solana protocol level, the footprint is subject to
     152             :    change when we change our implementation.
     153             : 
     154             :    ### Native programs
     155             :    ALUT (migrated to BPF)
     156             :    Loader
     157             :      - rodata for bpf program relocation and validation
     158             :    Compute budget (0 allocations)
     159             :    Config (migrated to BPF)
     160             :    Precompile (0 allocations)
     161             :    Stake
     162             :      - The instruction with the largest footprint is deactivate_delinquent
     163             :        - During instruction decode, no allocations
     164             :        - During execution, this is (vote account get_state() + vote convert_to_current()) times 2, once for delinquent_vote_account, and once for reference_vote_account
     165             :    System
     166             :      - system_program_instruction_decode seed
     167             :    Vote
     168             :      - The instruction with the largest footprint is compact vote state update
     169             :        - During instruction decode, this is 9*lockouts_len bytes, MTU bounded
     170             :        - During execution, this is vote account get_state() + vote convert_to_current() + 12*lockouts_len bytes + lockouts_len ulong + deq_fd_landed_vote_t_alloc(lockouts_len)
     171             :    Zk Elgamal (0 allocations)
     172             : 
     173             :    The largest footprint is hence deactivate_delinquent, in which the
     174             :    two get_state() calls dominate the footprint.  In particular, the
     175             :    authorized_voters treaps bloat 40 bytes (epoch+pubkey) in a vote
     176             :    account to 72 bytes (sizeof(fd_vote_authorized_voter_t)) in memory.
     177             :  */
     178           6 : #define FD_RUNTIME_BINCODE_AND_NATIVE_FOOTPRINT (2UL*FD_RUNTIME_ACC_SZ_MAX*72UL/40UL)
     179             : 
     180             : /* Misc other footprint. */
     181           6 : #define FD_RUNTIME_SYSCALL_TABLE_FOOTPRINT (FD_MAX_INSTRUCTION_STACK_DEPTH*FD_ULONG_ALIGN_UP(FD_SBPF_SYSCALLS_FOOTPRINT, FD_SBPF_SYSCALLS_ALIGN))
     182             : 
     183           0 : #define FD_RUNTIME_VM_TRACE_EVENT_MAX      (128UL<<20)
     184           0 : #define FD_RUNTIME_VM_TRACE_EVENT_DATA_MAX (2048UL)
     185           0 : #define FD_RUNTIME_VM_TRACE_FOOTPRINT      (FD_MAX_INSTRUCTION_STACK_DEPTH*fd_ulong_align_up( fd_vm_trace_footprint( FD_RUNTIME_VM_TRACE_EVENT_MAX, FD_RUNTIME_VM_TRACE_EVENT_DATA_MAX ), fd_vm_trace_align() ))
     186             : 
     187           6 : #define FD_RUNTIME_MISC_FOOTPRINT (FD_RUNTIME_SYSCALL_TABLE_FOOTPRINT)
     188           0 : #define FD_SOLFUZZ_MISC_FOOTPRINT (FD_RUNTIME_SYSCALL_TABLE_FOOTPRINT + FD_RUNTIME_VM_TRACE_FOOTPRINT)
     189             : 
     190             : /* Now finally, we bound out the footprint of transaction execution. */
     191             : #define FD_RUNTIME_TRANSACTION_EXECUTION_FOOTPRINT(account_lock_limit, direct_mapping)                                         \
     192           6 :                                                   (FD_RUNTIME_BORROWED_ACCOUNT_FOOTPRINT                                     + \
     193           6 :                                                    FD_RUNTIME_INPUT_REGION_TXN_FOOTPRINT(account_lock_limit, direct_mapping) + \
     194           6 :                                                    FD_RUNTIME_BINCODE_AND_NATIVE_FOOTPRINT                                   + \
     195           6 :                                                    FD_RUNTIME_MISC_FOOTPRINT)
     196             : 
     197             : /* Convenience macros for common use cases.
     198             : 
     199             :    TODO: If account lock limits are increased to 128, this macro will need to be updated. */
     200           0 : #define FD_RUNTIME_TRANSACTION_EXECUTION_FOOTPRINT_FUZZ    FD_RUNTIME_TRANSACTION_EXECUTION_FOOTPRINT(64UL, 0) + FD_SOLFUZZ_MISC_FOOTPRINT
     201           6 : #define FD_RUNTIME_TRANSACTION_EXECUTION_FOOTPRINT_DEFAULT FD_RUNTIME_TRANSACTION_EXECUTION_FOOTPRINT(64UL, 0)
     202             : 
     203             : /* Footprint here is dominated by vote account decode.  See above for
     204             :    why 72/40. */
     205             : #define FD_RUNTIME_TRANSACTION_FINALIZATION_FOOTPRINT      (FD_RUNTIME_ACC_SZ_MAX*72UL/40UL)
     206             : 
     207             : 
     208             : /* FD_SLICE_ALIGN specifies the alignment needed for a block slice.
     209             :    ALIGN is double x86 cache line to mitigate various kinds of false
     210             :    sharing (eg. ACLPF adjacent cache line prefetch). */
     211             : 
     212             : #define FD_SLICE_ALIGN (128UL)
     213             : 
     214             : /* FD_SLICE_MAX specifies the maximum size of an entry batch. This is
     215             :    equivalent to the maximum size of a block (ie. a block with a single
     216             :    entry batch). */
     217             : 
     218             : #define FD_SLICE_MAX (FD_SHRED_DATA_PAYLOAD_MAX_PER_SLOT)
     219             : 
     220             : /* FD_SLICE_MAX_WITH_HEADERS specifies the maximum size of all of the
     221             :    shreds that can be in an entry batch. This is equivalent to max
     222             :    number of shreds (including header and payload) that can be in a
     223             :    single slot. */
     224             : 
     225           0 : #define FD_SLICE_MAX_WITH_HEADERS (FD_SHRED_DATA_HEADER_MAX_PER_SLOT + FD_SHRED_DATA_PAYLOAD_MAX_PER_SLOT)
     226             : 
     227             : /* 64 ticks per slot, and then one min size transaction per microblock
     228             :    for all the remaining microblocks.
     229             :    This bound should be used along with the transaction parser and tick
     230             :    verifier to enforce the assumptions.
     231             :    This is NOT a standalone conservative bound against malicious
     232             :    validators.
     233             :    A tighter bound could probably be derived if necessary. */
     234             : 
     235             : #define FD_MICROBLOCK_MAX_PER_SLOT ((FD_SHRED_DATA_PAYLOAD_MAX_PER_SLOT - 64UL*sizeof(fd_microblock_hdr_t)) / (sizeof(fd_microblock_hdr_t)+FD_TXN_MIN_SERIALIZED_SZ) + 64UL) /* 200,796 */
     236             : /* 64 ticks per slot, and a single gigantic microblock containing min
     237             :    size transactions. */
     238             : #define FD_TXN_MAX_PER_SLOT ((FD_SHRED_DATA_PAYLOAD_MAX_PER_SLOT - 65UL*sizeof(fd_microblock_hdr_t)) / (FD_TXN_MIN_SERIALIZED_SZ)) /* 272,635 */
     239             : 
     240             : // TODO centralize these
     241             : // https://github.com/firedancer-io/solana/blob/v1.17.5/sdk/program/src/clock.rs#L34
     242             : #define FD_MS_PER_TICK 6
     243             : 
     244             : // https://github.com/firedancer-io/solana/blob/v1.17.5/core/src/repair/repair_service.rs#L55
     245             : #define FD_REPAIR_TIMEOUT (200 / FD_MS_PER_TICK)
     246             : 
     247             : /* Helpers for runtime public frame management. */
     248             : 
     249             : /* Helpers for runtime spad frame management. */
     250             : struct fd_runtime_spad_verify_handle_private {
     251             :   fd_spad_t *         spad;
     252             :   fd_exec_txn_ctx_t * txn_ctx;
     253             : };
     254             : typedef struct fd_runtime_spad_verify_handle_private fd_runtime_spad_verify_handle_private_t;
     255             : 
     256             : static inline void
     257           0 : fd_runtime_spad_private_frame_end( fd_runtime_spad_verify_handle_private_t * _spad_handle ) {
     258             :   /* fd_spad_verify() returns 0 if everything looks good, and non-zero
     259             :      otherwise.
     260             : 
     261             :      Since the fast spad alloc API doesn't check for or indicate an OOM
     262             :      situation and is going to happily permit an OOB alloc, we need
     263             :      some way of detecting that. Moreover, we would also like to detect
     264             :      unbalanced frame push/pop or usage of more frames than allowed.
     265             :      While surrounding the spad with guard regions will help detect the
     266             :      former, it won't necessarily catch the latter.
     267             : 
     268             :      On compliant transactions, fd_spad_verify() isn't all that
     269             :      expensive.  Nonetheless, We invoke fd_spad_verify() only at the
     270             :      peak of memory usage, and not gratuitously everywhere. One peak
     271             :      would be right before we do the most deeply nested spad frame pop.
     272             :      However, we do pops through compiler-inserted cleanup functions
     273             :      that take only a single pointer, so we define this helper function
     274             :      to access the needed context info.  The end result is that we do
     275             :      super fast spad calls everywhere in the runtime, and every now and
     276             :      then we invoke verify to check things. */
     277             :   /* -1UL because spad pop is called after instr stack pop. */
     278           0 :   if( FD_UNLIKELY( _spad_handle->txn_ctx->instr_stack_sz>=FD_MAX_INSTRUCTION_STACK_DEPTH-1UL && fd_spad_verify( _spad_handle->txn_ctx->spad ) ) ) {
     279           0 :     uchar const * txn_signature = (uchar const *)fd_txn_get_signatures( TXN( &_spad_handle->txn_ctx->txn ), _spad_handle->txn_ctx->txn.payload );
     280           0 :     FD_BASE58_ENCODE_64_BYTES( txn_signature, sig );
     281           0 :     FD_LOG_ERR(( "spad corrupted or overflown on transaction %s", sig ));
     282           0 :   }
     283           0 :   fd_spad_pop( _spad_handle->spad );
     284           0 : }
     285             : 
     286           0 : #define FD_RUNTIME_TXN_SPAD_FRAME_BEGIN(_spad, _txn_ctx) do {                                                        \
     287           0 :   fd_runtime_spad_verify_handle_private_t _spad_handle __attribute__((cleanup(fd_runtime_spad_private_frame_end))) = \
     288           0 :     (fd_runtime_spad_verify_handle_private_t) { .spad = _spad, .txn_ctx = _txn_ctx };                                \
     289           0 :   fd_spad_push( _spad_handle.spad );                                                                                 \
     290           0 :   do
     291             : 
     292           0 : #define FD_RUNTIME_TXN_SPAD_FRAME_END while(0); } while(0)
     293             : 
     294             : FD_PROTOTYPES_BEGIN
     295             : 
     296             : /* Runtime Helpers ************************************************************/
     297             : 
     298             : /*
     299             :    Returns 0 on success, and non zero otherwise.  On failure, the
     300             :    out values will not be modified.
     301             :  */
     302             : int
     303             : fd_runtime_compute_max_tick_height( ulong   ticks_per_slot,
     304             :                                     ulong   slot,
     305             :                                     ulong * out_max_tick_height /* out */ );
     306             : 
     307             : void
     308             : fd_runtime_update_leaders( fd_bank_t * bank,
     309             :                            ulong       slot,
     310             :                            fd_spad_t * runtime_spad );
     311             : 
     312             : /* TODO: Invoked by fd_executor: layering violation. Rent logic is deprecated
     313             :    and will be torn out entirely very soon. */
     314             : ulong
     315             : fd_runtime_collect_rent_from_account( fd_epoch_schedule_t const * schedule,
     316             :                                       fd_rent_t const *           rent,
     317             :                                       double                      slots_per_year,
     318             :                                       fd_txn_account_t *          acc,
     319             :                                       ulong                       epoch );
     320             : 
     321             : void
     322             : fd_runtime_update_slots_per_epoch( fd_bank_t * bank,
     323             :                                    ulong       slots_per_epoch );
     324             : 
     325             : /* Block Level Execution Prep/Finalize ****************************************/
     326             : 
     327             : #define FD_BLOCK_OK                          (0UL)
     328             : #define FD_BLOCK_ERR_INCOMPLETE              (1UL)
     329             : #define FD_BLOCK_ERR_INVALID_ENTRY_HASH      (2UL)
     330             : #define FD_BLOCK_ERR_INVALID_LAST_TICK       (3UL)
     331             : #define FD_BLOCK_ERR_TOO_FEW_TICKS           (4UL)
     332             : #define FD_BLOCK_ERR_TOO_MANY_TICKS          (5UL)
     333             : #define FD_BLOCK_ERR_INVALID_TICK_HASH_COUNT (6UL)
     334             : #define FD_BLOCK_ERR_TRAILING_ENTRY          (7UL)
     335             : #define FD_BLOCK_ERR_DUPLICATE_BLOCK         (8UL)
     336             : 
     337             : /*
     338             :    https://github.com/anza-xyz/agave/blob/v2.1.0/ledger/src/blockstore_processor.rs#L1096
     339             :    This function assumes a full block.
     340             :    This needs to be called after epoch processing to get the up to date
     341             :    hashes_per_tick.
     342             : 
     343             :    Provide scratch memory >= the max size of a batch to use. This is because we can only
     344             :    assemble shreds by batch, so we iterate and assemble shreds by batch in this function
     345             :    without needing the caller to do so.
     346             :  */
     347             : // FD_FN_UNUSED ulong /* FIXME */
     348             : // fd_runtime_block_verify_ticks( fd_blockstore_t * blockstore,
     349             : //                                ulong             slot,
     350             : //                                uchar *           block_data_mem,
     351             : //                                ulong             block_data_sz,
     352             : //                                ulong             tick_height,
     353             : //                                ulong             max_tick_height,
     354             : //                                ulong             hashes_per_tick );
     355             : 
     356             : /* The following microblock-level functions are exposed and non-static due to also being used for fd_replay.
     357             :    The block-level equivalent functions, on the other hand, are mostly static as they are only used
     358             :    for offline replay */
     359             : 
     360             : /* extra fine-grained streaming tick verification */
     361             : // FD_FN_UNUSED int /* FIXME */
     362             : // fd_runtime_microblock_verify_ticks( fd_blockstore_t *           blockstore,
     363             : //                                     ulong                       slot,
     364             : //                                     fd_microblock_hdr_t const * hdr,
     365             : //                                     bool               slot_complete,
     366             : //                                     ulong              tick_height,
     367             : //                                     ulong              max_tick_height,
     368             : //                                     ulong              hashes_per_tick );
     369             : 
     370             : /*
     371             :    fd_runtime_microblock_verify_read_write_conflicts verifies that a
     372             :    list of txns (e.g., those in a microblock) do not have read-write
     373             :    or write-write conflits. FD_TXN_CONFLICT_MAP_MAX_NACCT defines a
     374             :    conservative estimation of the number of accounts touched by txns
     375             :    in one slot. Given the conservative estimation, the footprint of
     376             :    the account map (fd_conflict_detect_map) is about 2112MB. One can
     377             :    certainly use a better estimation leading to a smaller footprint.
     378             : 
     379             :    this function corresponds to try_lock_accounts in Agave which
     380             :    detects transaction conflicts:
     381             :    https://github.com/anza-xyz/agave/blob/v2.2.3/runtime/src/bank.rs
     382             :    #L3145
     383             : 
     384             :    Specifically, from the replay stage of Agave, the control flow is
     385             :    (1) replay_blockstore_into_bank: https://github.com/anza-xyz/agave/
     386             :    blob/v2.2.3/core/src/replay_stage.rs#L2232
     387             :    (2) confirm_slot: https://github.com/anza-xyz/agave/blob/v2.2.3/
     388             :    ledger/src/blockstore_processor.rs#L1561
     389             :    (3) confirm_slot_entries: https://github.com/anza-xyz/agave/blob/
     390             :    v2.2.3/ledger/src/blockstore_processor.rs#L1609
     391             :    (4) process_entries: https://github.com/anza-xyz/agave/blob/v2.2.3/
     392             :    ledger/src/blockstore_processor.rs#L704
     393             :    (5) queue_batches_with_lock_retry: https://github.com/anza-xyz/agave/
     394             :    blob/v2.2.3/ledger/src/blockstore_processor.rs#L789
     395             :    (6) bank.try_lock_accounts is called in queue_batches_with_lock_retry
     396             :    (7) this try_lock_accounts eventually calls another try_lock_accounts,
     397             :        (see https://github.com/anza-xyz/agave/blob/v2.2.3/accounts-db/src
     398             :        /account_locks.rs#L24), which acquires the locks and returns
     399             :        TransactionError::AccountInUse if r-w or w-w conflict is detected
     400             :    (8) when calling try_lock_accounts, function accounts_with_is_writable
     401             :    is used to decide whether an account is writable (see https://github.
     402             :    com/anza-xyz/agave/blob/v2.2.3/accounts-db/src/accounts.rs#L605) which
     403             :    internally calls the is_writable function depending on whether the txn
     404             :    is legacy or V0:
     405             : 
     406             :    is_writable for legacy: https://github.com/anza-xyz/solana-sdk/blob/
     407             :    message%40v2.2.1/message/src/sanitized.rs#L75
     408             : 
     409             :    is_writable for V0: https://github.com/anza-xyz/solana-sdk/blob/
     410             :    message%40v2.2.1/message/src/versions/v0/loaded.rs#L152
     411             : 
     412             :    In both cases, Agave does the following check in addition to whether
     413             :    an account has been specified as writable in the transaction message
     414             :    (https://github.com/anza-xyz/solana-sdk/blob/message%40v2.2.1/message
     415             :    /src/versions/v0/loaded.rs#L146). This additional check is handled by
     416             :    function fd_txn_account_is_writable_idx_flat in our code.
     417             : 
     418             :    txns is an array containing txn_cnt transactions in fd_txn_p_t type;
     419             :    acct_map is used to detect conflicts and acct_arr is used to clear the
     420             :    map before the function returns; funk and funk_txn are used to read
     421             :    Solana accounts for address lookup tables; slot and slot_hashes are
     422             :    needed for checking certain bounds in the address lookup table system
     423             :    program; features is needed in fd_txn_account_is_writable_idx_flat to
     424             :    decide whether a writable account is demoted to read-only.
     425             : 
     426             :    If an error occurs in runtime, the function returns the runtime error.
     427             :    If there's no conflict, the return value is FD_RUNTIME_EXECUTE_SUCCESS
     428             :    and out_conflict_detected will be 0. If there's a conflict, the return
     429             :    value is FD_RUNTIME_TXN_ERR_ACCOUNT_IN_USE and out_conflict_detected
     430             :    will be 1 (read-write) or 2 (write-write), and out_conflict_addr_opt,
     431             :    if not NULL, will hold the account address causing the conflict.
     432             :  */
     433             : struct fd_conflict_detect_ele {
     434             :   fd_acct_addr_t key;
     435             :   uchar          writable;
     436             : };
     437             : typedef struct fd_conflict_detect_ele fd_conflict_detect_ele_t;
     438           0 : #define FD_TXN_CONFLICT_MAP_SEED        (0UL)
     439             : #define FD_TXN_CONFLICT_MAP_MAX_NACCT   (FD_SHRED_DATA_PAYLOAD_MAX_PER_SLOT / FD_TXN_MIN_SERIALIZED_SZ * FD_TXN_ACCT_ADDR_MAX)
     440             : 
     441             : static const fd_acct_addr_t fd_acct_addr_null = {.b={0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF}};
     442             : #define MAP_NAME              fd_conflict_detect_map
     443           0 : #define MAP_KEY_T             fd_acct_addr_t
     444           0 : #define MAP_T                 fd_conflict_detect_ele_t
     445           0 : #define MAP_HASH_T            ulong
     446           0 : #define MAP_KEY_NULL          (fd_acct_addr_null)
     447           0 : #define MAP_KEY_EQUAL(k0,k1)  (0==memcmp((k0).b,(k1).b,32))
     448           0 : #define MAP_KEY_HASH(key)     fd_hash( FD_TXN_CONFLICT_MAP_SEED, key.b, 32 )
     449           0 : #define MAP_KEY_INVAL(k)      (0==memcmp(&fd_acct_addr_null, (k).b, 32))
     450             : #define MAP_KEY_EQUAL_IS_SLOW 0
     451             : #define MAP_MEMOIZE           0
     452             : 
     453             : #include "../../util/tmpl/fd_map_dynamic.c"
     454             : 
     455           0 : #define FD_RUNTIME_NO_CONFLICT_DETECTED          0
     456           0 : #define FD_RUNTIME_READ_WRITE_CONFLICT_DETECTED  1
     457           0 : #define FD_RUNTIME_WRITE_WRITE_CONFLICT_DETECTED 2
     458             : 
     459             : int
     460             : fd_runtime_microblock_verify_read_write_conflicts( fd_txn_p_t *               txns,
     461             :                                                    ulong                      txn_cnt,
     462             :                                                    fd_conflict_detect_ele_t * acct_map,
     463             :                                                    fd_acct_addr_t *           acct_arr,
     464             :                                                    fd_funk_t *                funk,
     465             :                                                    fd_funk_txn_t *            funk_txn,
     466             :                                                    ulong                      slot,
     467             :                                                    fd_slot_hash_t *           slot_hashes,
     468             :                                                    fd_features_t *            features,
     469             :                                                    int *                      out_conflict_detected,
     470             :                                                    fd_acct_addr_t *           out_conflict_addr_opt );
     471             : 
     472             : /* Load the accounts in the address lookup tables of txn into out_accts_alt */
     473             : int
     474             : fd_runtime_load_txn_address_lookup_tables( fd_txn_t const * txn,
     475             :                                            uchar const *    payload,
     476             :                                            fd_funk_t *      funk,
     477             :                                            fd_funk_txn_t *  funk_txn,
     478             :                                            ulong            slot,
     479             :                                            fd_slot_hash_t const * hashes,
     480             :                                            fd_acct_addr_t * out_accts_alt );
     481             : 
     482             : int
     483             : fd_runtime_block_execute_prepare( fd_exec_slot_ctx_t * slot_ctx,
     484             :                                   fd_spad_t *          runtime_spad );
     485             : 
     486             : void
     487             : fd_runtime_block_execute_finalize( fd_exec_slot_ctx_t * slot_ctx );
     488             : 
     489             : /* Look up the funk transaction for the given slot */
     490             : fd_funk_txn_t *
     491             : fd_runtime_funk_txn_get( fd_funk_t * funk,
     492             :                          ulong       slot );
     493             : 
     494             : /* Transaction Level Execution Management *************************************/
     495             : 
     496             : int
     497             : fd_runtime_pre_execute_check( fd_exec_txn_ctx_t * txn_ctx );
     498             : 
     499             : /* fd_runtime_prepare_and_execute_txn is the main entrypoint from the
     500             :    executor tile. It is responsible for preparing and executing a single
     501             :    transaction. */
     502             : 
     503             : int
     504             : fd_runtime_prepare_and_execute_txn( fd_banks_t *        banks,
     505             :                                     ulong               bank_idx,
     506             :                                     fd_exec_txn_ctx_t * txn_ctx,
     507             :                                     fd_txn_p_t *        txn,
     508             :                                     fd_spad_t *         exec_spad,
     509             :                                     fd_capture_ctx_t *  capture_ctx,
     510             :                                     uchar               do_sigverify );
     511             : 
     512             : void
     513             : fd_runtime_finalize_txn( fd_funk_t *         funk,
     514             :                          fd_funk_txn_t *     funk_txn,
     515             :                          fd_exec_txn_ctx_t * txn_ctx,
     516             :                          fd_bank_t *         bank,
     517             :                          fd_capture_ctx_t *  capture_ctx );
     518             : 
     519             : /* Epoch Boundary *************************************************************/
     520             : 
     521             : uint
     522             : fd_runtime_is_epoch_boundary( fd_exec_slot_ctx_t * slot_ctx,
     523             :                               ulong                curr_slot,
     524             :                               ulong                prev_slot );
     525             : 
     526             : /*
     527             :    This is roughly Agave's process_new_epoch() which gets called from
     528             :    new_from_parent() for every slot.
     529             :    https://github.com/anza-xyz/agave/blob/v1.18.26/runtime/src/bank.rs#L1483
     530             :    This needs to be called after funk_txn_prepare() because the accounts
     531             :    that we modify when processing a new epoch need to be hashed into
     532             :    the bank hash.
     533             :  */
     534             : void
     535             : fd_runtime_block_pre_execute_process_new_epoch( fd_exec_slot_ctx_t * slot_ctx,
     536             :                                                 fd_capture_ctx_t *   capture_ctx,
     537             :                                                 fd_spad_t *          runtime_spad,
     538             :                                                 int *                is_epoch_boundary );
     539             : 
     540             : /* `fd_runtime_update_program_cache()` is responsible for updating the
     541             :    program cache with any programs referenced in the current
     542             :    transaction. See fd_program_cache.h for more details.
     543             : 
     544             :    Note that ALUTs must be resolved because programs referenced in ALUTs
     545             :    can be invoked via CPI.
     546             : 
     547             :    TODO: We need to remove the ALUT resolution from this function
     548             :    because it is redundant (ALUTs get resolved again in the exec tile). */
     549             : void
     550             : fd_runtime_update_program_cache( fd_exec_slot_ctx_t * slot_ctx,
     551             :                                  fd_txn_p_t const *   txn_p,
     552             :                                  fd_spad_t *          runtime_spad );
     553             : 
     554             : /* Debugging Tools ************************************************************/
     555             : 
     556             : void
     557             : fd_runtime_checkpt( fd_capture_ctx_t *   capture_ctx,
     558             :                     fd_exec_slot_ctx_t * slot_ctx,
     559             :                     ulong                slot );
     560             : 
     561             : /* Offline Replay *************************************************************/
     562             : 
     563             : void
     564             : fd_runtime_read_genesis( fd_exec_slot_ctx_t *               slot_ctx,
     565             :                          fd_hash_t const *                  genesis_hash,
     566             :                          fd_lthash_value_t const *          genesis_lthash,
     567             :                          fd_genesis_solana_global_t const * genesis_block,
     568             :                          fd_spad_t *                        runtime_spad );
     569             : 
     570             : 
     571             : /* Returns whether the specified epoch should use the new vote account
     572             :    keyed leader schedule (returns 1) or the old validator identity keyed
     573             :    leader schedule (returns 0). See SIMD-0180.
     574             :    This is the analogous of Agave's Bank::should_use_vote_keyed_leader_schedule():
     575             :    https://github.com/anza-xyz/agave/blob/v2.3.1/runtime/src/bank.rs#L6148 */
     576             : int
     577             : fd_runtime_should_use_vote_keyed_leader_schedule( fd_bank_t * bank );
     578             : 
     579             : FD_PROTOTYPES_END
     580             : 
     581             : #endif /* HEADER_fd_src_flamenco_runtime_fd_runtime_h */

Generated by: LCOV version 1.14