LCOV - code coverage report
Current view: top level - flamenco/runtime/context - fd_exec_txn_ctx.h (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 2 20 10.0 %
Date: 2025-07-01 05:00:49 Functions: 0 96 0.0 %

          Line data    Source code
       1             : #ifndef HEADER_fd_src_flamenco_runtime_context_fd_exec_txn_ctx_h
       2             : #define HEADER_fd_src_flamenco_runtime_context_fd_exec_txn_ctx_h
       3             : 
       4             : #include "fd_exec_instr_ctx.h"
       5             : #include "../../../util/fd_util_base.h"
       6             : #include "../../log_collector/fd_log_collector_base.h"
       7             : #include "../../../ballet/txn/fd_txn.h"
       8             : #include "../../features/fd_features.h"
       9             : #include "../fd_acc_mgr.h"
      10             : #include "../fd_txncache.h"
      11             : #include "../fd_bank_hash_cmp.h"
      12             : #include "../fd_bank.h"
      13             : 
      14             : /* Return data for syscalls */
      15             : 
      16             : struct fd_txn_return_data {
      17             :   fd_pubkey_t program_id;
      18             :   ulong       len;
      19             :   uchar       data[1024];
      20             : };
      21             : 
      22             : typedef struct fd_txn_return_data fd_txn_return_data_t;
      23             : 
      24             : /* fd_exec_txn_ctx_t is the context needed to execute a transaction. */
      25             : 
      26             : /* Cache of deserialized vote accounts to support iteration after replaying a slot (required for fork choice) */
      27             : struct fd_vote_account_cache_entry {
      28             :   fd_pubkey_t pubkey;
      29             :   ulong next;
      30             :   fd_vote_state_t vote_account;
      31             : };
      32             : typedef struct fd_vote_account_cache_entry fd_vote_account_cache_entry_t;
      33             : 
      34             : #define POOL_NAME fd_vote_account_pool
      35             : #define POOL_T fd_vote_account_cache_entry_t
      36             : #include "../../../util/tmpl/fd_pool.c"
      37             : 
      38             : #define MAP_NAME          fd_vote_account_cache
      39             : #define MAP_ELE_T         fd_vote_account_cache_entry_t
      40             : #define MAP_KEY           pubkey
      41             : #define MAP_KEY_T         fd_pubkey_t
      42             : #define MAP_KEY_EQ(k0,k1) (!(memcmp((k0)->key,(k1)->key,sizeof(fd_hash_t))))
      43             : #define MAP_KEY_HASH(key,seed) ( ((key)->ui[0]) ^ (seed) )
      44             : #include "../../../util/tmpl/fd_map_chain.c"
      45             : 
      46             : /* An entry in the instruction trace */
      47             : struct fd_exec_instr_trace_entry {
      48             :   /* Metadata about the instruction */
      49             :   fd_instr_info_t * instr_info;
      50             :   /* Stack height when this instruction was pushed onto the stack (including itself)
      51             :      https://github.com/anza-xyz/agave/blob/d87e23d8d91c32d5f2be2bb3557c730bee1e9434/sdk/src/transaction_context.rs#L475-L480 */
      52             :   ulong stack_height;
      53             : };
      54             : typedef struct fd_exec_instr_trace_entry fd_exec_instr_trace_entry_t;
      55             : 
      56             : /* https://github.com/anza-xyz/agave/blob/0d34a1a160129c4293dac248e14231e9e773b4ce/program-runtime/src/compute_budget.rs#L139 */
      57             : #define FD_MAX_INSTRUCTION_TRACE_LENGTH (64UL)
      58             : /* https://github.com/anza-xyz/agave/blob/f70ab5598ccd86b216c3928e4397bf4a5b58d723/compute-budget/src/compute_budget.rs#L13 */
      59           0 : #define FD_MAX_INSTRUCTION_STACK_DEPTH  (5UL)
      60             : 
      61             : struct fd_exec_txn_ctx {
      62             :   ulong magic; /* ==FD_EXEC_TXN_CTX_MAGIC */
      63             : 
      64             :   /* TODO: These are fields borrowed from the slot and epoch ctx. This
      65             :      could be refactored even further. Currently these fields are not
      66             :      all valid local joins within the scope of txn execution. */
      67             : 
      68             :   uint flags;
      69             : 
      70             :   fd_bank_t * bank;
      71             : 
      72             :   /* All pointers starting here are valid local joins in txn execution. */
      73             :   fd_features_t                        features;
      74             :   fd_txncache_t *                      status_cache;
      75             :   int                                  enable_exec_recording;
      76             :   fd_bank_hash_cmp_t *                 bank_hash_cmp;
      77             :   fd_funk_txn_t *                      funk_txn;
      78             :   fd_funk_t                            funk[1];
      79             :   fd_wksp_t *                          runtime_pub_wksp;
      80             :   ulong                                slot;
      81             : 
      82             :   fd_spad_t *                          spad;                                        /* Sized out to handle the worst case footprint of single transaction execution. */
      83             :   fd_wksp_t *                          spad_wksp;                                   /* Workspace for the spad. */
      84             :   /* Fields below here are not guaranteed to be local joins in txn execution. */
      85             : 
      86             :   ulong                                paid_fees;
      87             :   ulong                                compute_unit_limit;                          /* Compute unit limit for this transaction. */
      88             :   ulong                                compute_unit_price;                          /* Compute unit price for this transaction. */
      89             :   ulong                                compute_meter;                               /* Remaining compute units */
      90             :   ulong                                heap_size;                                   /* Heap size for VMs for this transaction. */
      91             :   ulong                                loaded_accounts_data_size_limit;             /* Loaded accounts data size limit for this transaction. */
      92             :   ulong                                loaded_accounts_data_size;                   /* The actual transaction loaded data size */
      93             :   uint                                 prioritization_fee_type;                     /* The type of prioritization fee to use. */
      94             :   fd_txn_t const *                     txn_descriptor;                              /* Descriptor of the transaction. */
      95             :   fd_rawtxn_b_t                        _txn_raw[1];                                 /* Raw bytes of the transaction. */
      96             :   uint                                 custom_err;                                  /* When a custom error is returned, this is where the numeric value gets stashed */
      97             :   uchar                                instr_stack_sz;                              /* Current depth of the instruction execution stack. */
      98             :   fd_exec_instr_ctx_t                  instr_stack[FD_MAX_INSTRUCTION_STACK_DEPTH]; /* Instruction execution stack. */
      99             :   fd_exec_instr_ctx_t *                failed_instr;
     100             :   int                                  instr_err_idx;
     101             :   /* During sanitization, v0 transactions are allowed to have up to 256 accounts:
     102             :      https://github.com/anza-xyz/agave/blob/838c1952595809a31520ff1603a13f2c9123aa51/sdk/program/src/message/versions/v0/mod.rs#L139
     103             :      Nonetheless, when Agave prepares a sanitized batch for execution and tries to lock accounts, a lower limit is enforced:
     104             :      https://github.com/anza-xyz/agave/blob/838c1952595809a31520ff1603a13f2c9123aa51/accounts-db/src/account_locks.rs#L118
     105             :      That is the limit we are going to use here. */
     106             :   ulong                           accounts_cnt;                                /* Number of account pubkeys accessed by this transaction. */
     107             :   fd_pubkey_t                     account_keys[ MAX_TX_ACCOUNT_LOCKS ];        /* Array of account pubkeys accessed by this transaction. */
     108             :   ulong                           executable_cnt;                              /* Number of BPF upgradeable loader accounts. */
     109             :   fd_txn_account_t                executable_accounts[ MAX_TX_ACCOUNT_LOCKS ]; /* Array of BPF upgradeable loader program data accounts */
     110             :   fd_txn_account_t                accounts[ MAX_TX_ACCOUNT_LOCKS ];            /* Array of borrowed accounts accessed by this transaction. */
     111             : 
     112             :   /* The next three fields describe Agave's "rollback" accounts, which
     113             :      are copies of the fee payer and (if applicable) nonce account. If the
     114             :      transaction fails to load, the fee payer is still debited the transaction fee,
     115             :      and the nonce account is advanced. The fee payer must also be rolled back to it's
     116             :      state pre-transaction, plus debited any transaction fees.
     117             : 
     118             :      This is a bit of a misnomer but Agave calls it "rollback".
     119             :      This is the account state that the nonce account should be in when
     120             :      the txn fails.
     121             :      It will advance the nonce account, rather than "roll back".
     122             :    */
     123             :   fd_txn_account_t                rollback_nonce_account[ 1 ];
     124             :   ulong                           nonce_account_idx_in_txn;                    /* If the transaction has a nonce account that must be advanced, this would be !=ULONG_MAX. */
     125             :   fd_txn_account_t                rollback_fee_payer_account[ 1 ];
     126             : 
     127             :   uint                            num_instructions;                            /* Counter for number of instructions in txn */
     128             :   fd_txn_return_data_t            return_data;                                 /* Data returned from `return_data` syscalls */
     129             :   fd_vote_account_cache_t *       vote_accounts_map;                           /* Cache of bank's deserialized vote accounts to support fork choice */
     130             :   fd_vote_account_cache_entry_t * vote_accounts_pool;                          /* Memory pool for deserialized vote account cache */
     131             :   ulong                           accounts_resize_delta;                       /* Transaction level tracking for account resizing */
     132             :   fd_hash_t                       blake_txn_msg_hash;                          /* Hash of raw transaction message used by the status cache */
     133             :   ulong                           execution_fee;                               /* Execution fee paid by the fee payer in the transaction */
     134             :   ulong                           priority_fee;                                /* Priority fee paid by the fee payer in the transaction */
     135             :   ulong                           collected_rent;                              /* Rent collected from accounts in this transaction */
     136             : 
     137             :   uchar dirty_vote_acc  : 1; /* 1 if this transaction maybe modified a vote account */
     138             :   uchar dirty_stake_acc : 1; /* 1 if this transaction maybe modified a stake account */
     139             : 
     140             :   fd_capture_ctx_t * capture_ctx;
     141             : 
     142             :   /* The instr_infos for the entire transaction are allocated at the start of
     143             :      the transaction. However, this must preserve a different counter because
     144             :      the top level instructions must get set up at once. The instruction
     145             :      error check on a maximum instruction size can be done on the
     146             :      instr_info_cnt instead of the instr_trace_length because it is a proxy
     147             :      for the trace_length: the instr_info_cnt gets incremented faster than
     148             :      the instr_trace_length because it counts all of the top level instructions
     149             :      first. */
     150             :   fd_instr_info_t             instr_infos[FD_MAX_INSTRUCTION_TRACE_LENGTH];
     151             :   ulong                       instr_info_cnt;
     152             : 
     153             :   /* These instr infos are statically allocated at the beginning of a transaction
     154             :      and are only written to / referred to within the VM. It's kept
     155             :      at the transaction level because syscalls like `GetProcessedSiblingInstruction()`
     156             :      may refer to instructions processed earlier in the transaction. */
     157             :   fd_instr_info_t             cpi_instr_infos[FD_MAX_INSTRUCTION_TRACE_LENGTH];
     158             :   ulong                       cpi_instr_info_cnt;
     159             : 
     160             :   /* Each instr info within `instr_trace` may refer to an `instr_infos` or `cpi_instr_infos`
     161             :      entry. */
     162             :   fd_exec_instr_trace_entry_t instr_trace[FD_MAX_INSTRUCTION_TRACE_LENGTH]; /* Instruction trace */
     163             :   ulong                       instr_trace_length;                           /* Number of instructions in the trace */
     164             : 
     165             :   fd_log_collector_t          log_collector;             /* Log collector instance */
     166             : 
     167             :   /* Execution error and type, to match Agave. */
     168             :   int exec_err;
     169             :   int exec_err_kind;
     170             : 
     171             :    /* The current instruction index being executed */
     172             :   int current_instr_idx;
     173             : };
     174             : 
     175        7665 : #define FD_EXEC_TXN_CTX_ALIGN     (alignof(fd_exec_txn_ctx_t))
     176        7665 : #define FD_EXEC_TXN_CTX_FOOTPRINT ( sizeof(fd_exec_txn_ctx_t))
     177           0 : #define FD_EXEC_TXN_CTX_MAGIC     (0x9AD93EE71469F4D7UL      ) /* random */
     178             : 
     179             : FD_PROTOTYPES_BEGIN
     180             : 
     181             : /* Error logging handholding assertions */
     182             : 
     183             : #ifdef FD_RUNTIME_ERR_HANDHOLDING
     184             : 
     185             : /* Asserts that the error and error kind are not populated (zero) */
     186             : #define FD_TXN_TEST_ERR_OVERWRITE( txn_ctx ) \
     187             :    FD_TEST( !txn_ctx->exec_err );            \
     188             :    FD_TEST( !txn_ctx->exec_err_kind )
     189             : 
     190             : /* Used prior to a FD_TXN_ERR_FOR_LOG_INSTR call to deliberately
     191             :    bypass overwrite handholding checks.
     192             :    Only use this if you know what you're doing. */
     193             : #define FD_TXN_PREPARE_ERR_OVERWRITE( txn_ctx ) \
     194             :    txn_ctx->exec_err = 0;                          \
     195             :    txn_ctx->exec_err_kind = 0
     196             : 
     197             : #else
     198             : 
     199           0 : #define FD_TXN_TEST_ERR_OVERWRITE( txn_ctx ) ( ( void )0 )
     200           0 : #define FD_TXN_PREPARE_ERR_OVERWRITE( txn_ctx ) ( ( void )0 )
     201             : 
     202             : #endif
     203             : 
     204           0 : #define FD_TXN_ERR_FOR_LOG_INSTR( txn_ctx, err, idx ) (__extension__({ \
     205           0 :     FD_TXN_TEST_ERR_OVERWRITE( txn_ctx );                              \
     206           0 :     txn_ctx->exec_err = err;                                           \
     207           0 :     txn_ctx->exec_err_kind = FD_EXECUTOR_ERR_KIND_INSTR;               \
     208           0 :     txn_ctx->instr_err_idx = idx;                                      \
     209           0 :   }))
     210             : 
     211             : void *
     212             : fd_exec_txn_ctx_new( void * mem );
     213             : 
     214             : fd_exec_txn_ctx_t *
     215             : fd_exec_txn_ctx_join( void * mem, fd_spad_t * spad, fd_wksp_t * spad_wksp );
     216             : 
     217             : void *
     218             : fd_exec_txn_ctx_leave( fd_exec_txn_ctx_t * ctx );
     219             : 
     220             : void *
     221             : fd_exec_txn_ctx_delete( void * mem );
     222             : 
     223             : /* Sets up a basic transaction ctx without a txn descriptor or txn raw. Useful
     224             :    for mocking transaction context objects for instructions. */
     225             : void
     226             : fd_exec_txn_ctx_setup_basic( fd_exec_txn_ctx_t * ctx );
     227             : 
     228             : /* TODO: the constructors for the txn_ctx needs to be properly consolidated. */
     229             : void
     230             : fd_exec_txn_ctx_setup( fd_exec_txn_ctx_t * ctx,
     231             :                        fd_txn_t const * txn_descriptor,
     232             :                        fd_rawtxn_b_t const * txn_raw );
     233             : 
     234             : void
     235             : fd_exec_txn_ctx_teardown( fd_exec_txn_ctx_t * txn_ctx );
     236             : 
     237             : /* Mirrors Agave function solana_sdk::transaction_context::find_index_of_account
     238             : 
     239             :    Backward scan over transaction accounts.
     240             :    Returns -1 if not found.
     241             : 
     242             :    https://github.com/anza-xyz/agave/blob/v2.1.14/sdk/src/transaction_context.rs#L233-L238 */
     243             : 
     244             : static inline int
     245             : fd_exec_txn_ctx_find_index_of_account( fd_exec_txn_ctx_t const * ctx,
     246           0 :                                        fd_pubkey_t const *       pubkey ) {
     247           0 :   for( ulong i=ctx->accounts_cnt; i>0UL; i-- ) {
     248           0 :     if( 0==memcmp( pubkey, &ctx->account_keys[ i-1UL ], sizeof(fd_pubkey_t) ) ) {
     249           0 :       return (int)(i-1UL);
     250           0 :     }
     251           0 :   }
     252           0 :   return -1;
     253           0 : }
     254             : 
     255             : typedef int fd_txn_account_condition_fn_t ( fd_txn_account_t *        acc,
     256             :                                             fd_exec_txn_ctx_t const * ctx,
     257             :                                             ushort                    idx );
     258             : 
     259             : /* Mirrors Agave function solana_sdk::transaction_context::get_account_at_index
     260             : 
     261             :    Takes a function pointer to a condition function to check pre-conditions on the
     262             :    obtained account. If the condition function is NULL, the account is returned without
     263             :    any pre-condition checks.
     264             : 
     265             :    https://github.com/anza-xyz/agave/blob/v2.1.14/sdk/src/transaction_context.rs#L223-L230 */
     266             : 
     267             : int
     268             : fd_exec_txn_ctx_get_account_at_index( fd_exec_txn_ctx_t *             ctx,
     269             :                                       ushort                          idx,
     270             :                                       fd_txn_account_t * *            account,
     271             :                                       fd_txn_account_condition_fn_t * condition );
     272             : 
     273             : /* A wrapper around fd_exec_txn_ctx_get_account_at_index that obtains an
     274             :    account from the transaction context by its pubkey. */
     275             : 
     276             : int
     277             : fd_exec_txn_ctx_get_account_with_key( fd_exec_txn_ctx_t *             ctx,
     278             :                                       fd_pubkey_t const *             pubkey,
     279             :                                       fd_txn_account_t * *            account,
     280             :                                       fd_txn_account_condition_fn_t * condition );
     281             : 
     282             : /* Gets an executable (program data) account via its pubkey. */
     283             : 
     284             : int
     285             : fd_exec_txn_ctx_get_executable_account( fd_exec_txn_ctx_t *             ctx,
     286             :                                         fd_pubkey_t const *             pubkey,
     287             :                                         fd_txn_account_t * *            account,
     288             :                                         fd_txn_account_condition_fn_t * condition );
     289             : 
     290             : /* Mirrors Agave function solana_sdk::transaction_context::get_key_of_account_at_index
     291             : 
     292             :    https://github.com/anza-xyz/agave/blob/v2.1.14/sdk/src/transaction_context.rs#L212 */
     293             : 
     294             : int
     295             : fd_exec_txn_ctx_get_key_of_account_at_index( fd_exec_txn_ctx_t *  ctx,
     296             :                                              ushort               idx,
     297             :                                              fd_pubkey_t const * * key );
     298             : 
     299             : void
     300             : fd_exec_txn_ctx_reset_return_data( fd_exec_txn_ctx_t * ctx );
     301             : 
     302             : /* In agave, the writable accounts cache is populated by this below function.
     303             :    This cache is then referenced to determine if a transaction account is
     304             :    writable or not.
     305             : 
     306             :    The overall logic is as follows: an account can be passed
     307             :    in as writable based on the signature and readonly signature as they are
     308             :    passed in by the transaction message. However, the account's writable
     309             :    status will be demoted if either of the two conditions are met:
     310             :    1. If the account is in the set of reserved pubkeys
     311             :    2. If the account is the program id AND the upgradeable loader account is in
     312             :       the set of transaction accounts. */
     313             : /* https://github.com/anza-xyz/agave/blob/v2.1.1/sdk/program/src/message/versions/v0/loaded.rs#L137-L150 */
     314             : 
     315             : int
     316             : fd_exec_txn_ctx_account_is_writable_idx( fd_exec_txn_ctx_t const * txn_ctx, ushort idx );
     317             : 
     318             : /* This flat function does the same as the function above, but uses the
     319             :    exact arguments needed instead of the full fd_exec_txn_ctx_t */
     320             : 
     321             : int
     322             : fd_exec_txn_account_is_writable_idx_flat( const ulong           slot,
     323             :                                           const ushort          idx,
     324             :                                           const fd_pubkey_t *   addr_at_idx,
     325             :                                           const fd_txn_t *      txn_descriptor,
     326             :                                           const fd_features_t * features,
     327             :                                           const uint            bpf_upgradeable_in_txn );
     328             : 
     329             : /* The bpf_upgradeable_in_txn argument of the above function can be
     330             :    obtained by the function below */
     331             : uint
     332             : fd_txn_account_has_bpf_loader_upgradeable( const fd_pubkey_t * account_keys,
     333             :                                            const ulong         accounts_cnt );
     334             : 
     335             : 
     336             : /* Account pre-condition filtering functions
     337             : 
     338             :    Used to filter accounts based on pre-conditions such as existence, is_writable, etc.
     339             :    when obtaining accounts from the transaction context. Passed as a function pointer. */
     340             : 
     341             : int
     342             : fd_txn_account_check_exists( fd_txn_account_t *        acc,
     343             :                              fd_exec_txn_ctx_t const * ctx,
     344             :                              ushort                    idx );
     345             : 
     346             : int
     347             : fd_txn_account_check_is_writable( fd_txn_account_t *        acc,
     348             :                                   fd_exec_txn_ctx_t const * ctx,
     349             :                                   ushort                    idx );
     350             : 
     351             : /* The fee payer is a valid modifiable account if it is passed in as writable
     352             :    in the message via a valid signature. We ignore if the account has been
     353             :    demoted or not (see fd_exec_txn_ctx_account_is_writable_idx) for more details.
     354             :    Agave and Firedancer will reject the fee payer if the transaction message
     355             :    doesn't have a writable signature. */
     356             : 
     357             : int
     358             : fd_txn_account_check_fee_payer_writable( fd_txn_account_t *        acc,
     359             :                                          fd_exec_txn_ctx_t const * ctx,
     360             :                                          ushort                    idx );
     361             : 
     362             : /* Checks if the account is mutable and borrows the account mutably.
     363             : 
     364             :    The borrow is an acquired write on the account object.
     365             :    The caller is responsible for releasing the write via
     366             :    fd_txn_account_release_write.
     367             : 
     368             :    TODO: Agave doesn't need to check if the account is mutable
     369             :    because it uses Writable/Readable traits for accounts. We
     370             :    should have a similar concept to abstract away fd_txn_account_t's
     371             :    const_meta and meta fields. */
     372             : 
     373             : int
     374             : fd_txn_account_check_borrow_mut( fd_txn_account_t *        acc,
     375             :                                  fd_exec_txn_ctx_t const * ctx,
     376             :                                  ushort                    idx );
     377             : 
     378             : 
     379             : FD_PROTOTYPES_END
     380             : 
     381             : #endif /* HEADER_fd_src_flamenco_runtime_context_fd_exec_txn_ctx_h */

Generated by: LCOV version 1.14