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-03-20 12:08:36 Functions: 0 97 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 "../sysvar/fd_sysvar_cache.h"
      10             : #include "../fd_txncache.h"
      11             : #include "../fd_bank_hash_cmp.h"
      12             : 
      13             : /* Return data for syscalls */
      14             : 
      15             : struct fd_txn_return_data {
      16             :   fd_pubkey_t program_id;
      17             :   ulong       len;
      18             :   uchar       data[1024];
      19             : };
      20             : 
      21             : typedef struct fd_txn_return_data fd_txn_return_data_t;
      22             : 
      23             : /* fd_exec_txn_ctx_t is the context needed to execute a transaction. */
      24             : 
      25             : /* Cache of deserialized vote accounts to support iteration after replaying a slot (required for fork choice) */
      26             : struct fd_vote_account_cache_entry {
      27             :   fd_pubkey_t pubkey;
      28             :   ulong next;
      29             :   fd_vote_state_t vote_account;
      30             : };
      31             : typedef struct fd_vote_account_cache_entry fd_vote_account_cache_entry_t;
      32             : 
      33             : #define POOL_NAME fd_vote_account_pool
      34             : #define POOL_T fd_vote_account_cache_entry_t
      35             : #include "../../../util/tmpl/fd_pool.c"
      36             : 
      37             : #define MAP_NAME          fd_vote_account_cache
      38             : #define MAP_ELE_T         fd_vote_account_cache_entry_t
      39             : #define MAP_KEY           pubkey
      40             : #define MAP_KEY_T         fd_pubkey_t
      41             : #define MAP_KEY_EQ(k0,k1) (!(memcmp((k0)->key,(k1)->key,sizeof(fd_hash_t))))
      42             : #define MAP_KEY_HASH(key,seed) ( ((key)->ui[0]) ^ (seed) )
      43             : #include "../../../util/tmpl/fd_map_chain.c"
      44             : 
      45             : /* An entry in the instruction trace */
      46             : struct fd_exec_instr_trace_entry {
      47             :   /* Metadata about the instruction */
      48             :   fd_instr_info_t * instr_info;
      49             :   /* Stack height when this instruction was pushed onto the stack (including itself)
      50             :      https://github.com/anza-xyz/agave/blob/d87e23d8d91c32d5f2be2bb3557c730bee1e9434/sdk/src/transaction_context.rs#L475-L480 */
      51             :   ulong stack_height;
      52             : };
      53             : typedef struct fd_exec_instr_trace_entry fd_exec_instr_trace_entry_t;
      54             : 
      55             : /* https://github.com/anza-xyz/agave/blob/0d34a1a160129c4293dac248e14231e9e773b4ce/program-runtime/src/compute_budget.rs#L139 */
      56             : #define FD_MAX_INSTRUCTION_TRACE_LENGTH (64UL)
      57             : /* https://github.com/anza-xyz/agave/blob/f70ab5598ccd86b216c3928e4397bf4a5b58d723/compute-budget/src/compute_budget.rs#L13 */
      58           0 : #define FD_MAX_INSTRUCTION_STACK_DEPTH  (5UL)
      59             : 
      60             : struct __attribute__((aligned(8UL))) fd_exec_txn_ctx {
      61             :   ulong magic; /* ==FD_EXEC_TXN_CTX_MAGIC */
      62             : 
      63             :   /* TODO: These are fields borrowed from the slot and epoch ctx. This
      64             :      could be refactored even further. */
      65             : 
      66             :   /* local */
      67             :   fd_features_t                   features;
      68             :   fd_sysvar_cache_t const *       sysvar_cache;
      69             :   fd_txncache_t *                 status_cache;
      70             :   ulong                           prev_lamports_per_signature;
      71             :   int                             enable_exec_recording;
      72             :   ulong                           total_epoch_stake;
      73             :   fd_bank_hash_cmp_t *            bank_hash_cmp;
      74             :   fd_funk_txn_t *                 funk_txn;
      75             :   fd_acc_mgr_t *                  acc_mgr;
      76             :   fd_wksp_t *                     runtime_pub_wksp;
      77             :   ulong                           slot;
      78             :   fd_fee_rate_governor_t          fee_rate_governor;
      79             :   fd_block_hash_queue_t           block_hash_queue; /* TODO:FIXME: make globally addressable */
      80             : 
      81             :   fd_epoch_schedule_t             schedule;
      82             :   fd_rent_t                       rent;
      83             :   double                          slots_per_year;
      84             :   fd_stakes_t                     stakes; /* TODO:FIXME: Handle global addressable stuff */
      85             : 
      86             :   fd_spad_t *                     spad;                                        /* Sized out to handle the worst case footprint of single transaction execution. */
      87             : 
      88             :   ulong                           paid_fees;
      89             :   ulong                           compute_unit_limit;                          /* Compute unit limit for this transaction. */
      90             :   ulong                           compute_unit_price;                          /* Compute unit price for this transaction. */
      91             :   ulong                           compute_meter;                               /* Remaining compute units */
      92             :   ulong                           heap_size;                                   /* Heap size for VMs for this transaction. */
      93             :   ulong                           loaded_accounts_data_size_limit;             /* Loaded accounts data size limit for this transaction. */
      94             :   ulong                           loaded_accounts_data_size;                   /* The actual transaction loaded data size */
      95             :   uint                            prioritization_fee_type;                     /* The type of prioritization fee to use. */
      96             :   fd_txn_t const *                txn_descriptor;                              /* Descriptor of the transaction. */
      97             :   fd_rawtxn_b_t                   _txn_raw[1];                                 /* Raw bytes of the transaction. */
      98             :   uint                            custom_err;                                  /* When a custom error is returned, this is where the numeric value gets stashed */
      99             :   uchar                           instr_stack_sz;                              /* Current depth of the instruction execution stack. */
     100             :   fd_exec_instr_ctx_t             instr_stack[FD_MAX_INSTRUCTION_STACK_DEPTH]; /* Instruction execution stack. */
     101             :   fd_exec_instr_ctx_t *           failed_instr;
     102             :   int                             instr_err_idx;
     103             :   /* During sanitization, v0 transactions are allowed to have up to 256 accounts:
     104             :      https://github.com/anza-xyz/agave/blob/838c1952595809a31520ff1603a13f2c9123aa51/sdk/program/src/message/versions/v0/mod.rs#L139
     105             :      Nonetheless, when Agave prepares a sanitized batch for execution and tries to lock accounts, a lower limit is enforced:
     106             :      https://github.com/anza-xyz/agave/blob/838c1952595809a31520ff1603a13f2c9123aa51/accounts-db/src/account_locks.rs#L118
     107             :      That is the limit we are going to use here. */
     108             :   ulong                           accounts_cnt;                                /* Number of account pubkeys accessed by this transaction. */
     109             :   fd_pubkey_t                     account_keys[ MAX_TX_ACCOUNT_LOCKS ];            /* Array of account pubkeys accessed by this transaction. */
     110             :   ulong                           executable_cnt;                              /* Number of BPF upgradeable loader accounts. */
     111             :   fd_txn_account_t                executable_accounts[ MAX_TX_ACCOUNT_LOCKS ]; /* Array of BPF upgradeable loader program data accounts */
     112             :   fd_txn_account_t                accounts[ MAX_TX_ACCOUNT_LOCKS ];            /* Array of borrowed accounts accessed by this transaction. */
     113             :   /* This is a bit of a misnomer but Agave calls it "rollback".
     114             :      This is the account state that the nonce account should be in when
     115             :      the txn fails.
     116             :      It will advance the nonce account, rather than "roll back".
     117             :    */
     118             :   fd_txn_account_t                rollback_nonce_account[ 1 ];
     119             :   ulong                           nonce_account_idx_in_txn;                    /* If the transaction has a nonce account that must be advanced, this would be !=ULONG_MAX. */
     120             :   uchar                           nonce_account_advanced;                      /* Nonce account has been advanced. */
     121             :   uint                            num_instructions;                            /* Counter for number of instructions in txn */
     122             :   fd_txn_return_data_t            return_data;                                 /* Data returned from `return_data` syscalls */
     123             :   fd_vote_account_cache_t *       vote_accounts_map;                           /* Cache of bank's deserialized vote accounts to support fork choice */
     124             :   fd_vote_account_cache_entry_t * vote_accounts_pool;                          /* Memory pool for deserialized vote account cache */
     125             :   ulong                           accounts_resize_delta;                       /* Transaction level tracking for account resizing */
     126             :   fd_hash_t                       blake_txn_msg_hash;                          /* Hash of raw transaction message used by the status cache */
     127             :   ulong                           execution_fee;                               /* Execution fee paid by the fee payer in the transaction */
     128             :   ulong                           priority_fee;                                /* Priority fee paid by the fee payer in the transaction */
     129             :   ulong                           collected_rent;                              /* Rent collected from accounts in this transaction */
     130             : 
     131             :   uchar dirty_vote_acc  : 1; /* 1 if this transaction maybe modified a vote account */
     132             :   uchar dirty_stake_acc : 1; /* 1 if this transaction maybe modified a stake account */
     133             : 
     134             :   fd_capture_ctx_t * capture_ctx;
     135             : 
     136             :   /* The instr_infos for the entire transaction are allocated at the start of
     137             :      the transaction. However, this must preserve a different counter because
     138             :      the top level instructions must get set up at once. The instruction
     139             :      error check on a maximum instruction size can be done on the
     140             :      instr_info_cnt instead of the instr_trace_length because it is a proxy
     141             :      for the trace_length: the instr_info_cnt gets incremented faster than
     142             :      the instr_trace_length because it counts all of the top level instructions
     143             :      first. */
     144             :   fd_instr_info_t             instr_infos[FD_MAX_INSTRUCTION_TRACE_LENGTH];
     145             :   ulong                       instr_info_cnt;
     146             : 
     147             :   fd_exec_instr_trace_entry_t instr_trace[FD_MAX_INSTRUCTION_TRACE_LENGTH]; /* Instruction trace */
     148             :   ulong                       instr_trace_length;                           /* Number of instructions in the trace */
     149             : 
     150             :   fd_log_collector_t          log_collector;             /* Log collector instance */
     151             : 
     152             :   /* Execution error and type, to match Agave. */
     153             :   int exec_err;
     154             :   int exec_err_kind;
     155             : 
     156             :    /* The current instruction index being executed */
     157             :   int current_instr_idx;
     158             : };
     159             : 
     160        7665 : #define FD_EXEC_TXN_CTX_ALIGN     (alignof(fd_exec_txn_ctx_t))
     161        7665 : #define FD_EXEC_TXN_CTX_FOOTPRINT ( sizeof(fd_exec_txn_ctx_t))
     162           0 : #define FD_EXEC_TXN_CTX_MAGIC     (0x9AD93EE71469F4D7UL      ) /* random */
     163             : 
     164             : FD_PROTOTYPES_BEGIN
     165             : 
     166             : /* Error logging handholding assertions */
     167             : 
     168             : #ifdef FD_RUNTIME_ERR_HANDHOLDING
     169             : 
     170             : /* Asserts that the error and error kind are not populated (zero) */
     171             : #define FD_TXN_TEST_ERR_OVERWRITE( txn_ctx ) \
     172             :    FD_TEST( !txn_ctx->exec_err );            \
     173             :    FD_TEST( !txn_ctx->exec_err_kind )
     174             : 
     175             : /* Used prior to a FD_TXN_ERR_FOR_LOG_INSTR call to deliberately
     176             :    bypass overwrite handholding checks.
     177             :    Only use this if you know what you're doing. */
     178             : #define FD_TXN_PREPARE_ERR_OVERWRITE( txn_ctx ) \
     179             :    txn_ctx->exec_err = 0;                          \
     180             :    txn_ctx->exec_err_kind = 0
     181             : 
     182             : #else
     183             : 
     184           0 : #define FD_TXN_TEST_ERR_OVERWRITE( txn_ctx ) ( ( void )0 )
     185           0 : #define FD_TXN_PREPARE_ERR_OVERWRITE( txn_ctx ) ( ( void )0 )
     186             : 
     187             : #endif
     188             : 
     189           0 : #define FD_TXN_ERR_FOR_LOG_INSTR( txn_ctx, err, idx ) (__extension__({ \
     190           0 :     FD_TXN_TEST_ERR_OVERWRITE( txn_ctx );                              \
     191           0 :     txn_ctx->exec_err = err;                                           \
     192           0 :     txn_ctx->exec_err_kind = FD_EXECUTOR_ERR_KIND_INSTR;               \
     193           0 :     txn_ctx->instr_err_idx = idx;                                      \
     194           0 :   }))
     195             : 
     196             : void *
     197             : fd_exec_txn_ctx_new( void * mem );
     198             : 
     199             : fd_exec_txn_ctx_t *
     200             : fd_exec_txn_ctx_join( void * mem );
     201             : 
     202             : void *
     203             : fd_exec_txn_ctx_leave( fd_exec_txn_ctx_t * ctx );
     204             : 
     205             : void *
     206             : fd_exec_txn_ctx_delete( void * mem );
     207             : 
     208             : /* Sets up a basic transaction ctx without a txn descriptor or txn raw. Useful
     209             :    for mocking transaction context objects for instructions. */
     210             : void
     211             : fd_exec_txn_ctx_setup_basic( fd_exec_txn_ctx_t * txn_ctx );
     212             : 
     213             : void
     214             : fd_exec_txn_ctx_setup( fd_exec_txn_ctx_t * txn_ctx,
     215             :                        fd_txn_t const * txn_descriptor,
     216             :                        fd_rawtxn_b_t const * txn_raw );
     217             : 
     218             : void
     219             : fd_exec_txn_ctx_from_exec_slot_ctx( fd_exec_slot_ctx_t const * slot_ctx,
     220             :                                     fd_exec_txn_ctx_t *        txn_ctx,
     221             :                                     fd_wksp_t const *          funk_wksp,
     222             :                                     fd_wksp_t const *          runtime_pub_wksp,
     223             :                                     ulong                      funk_txn_gaddr,
     224             :                                     ulong                      acc_mgr_gaddr,
     225             :                                     ulong                      sysvar_cache_gaddr,
     226             :                                     ulong                      funk_gaddr );
     227             : 
     228             : void
     229             : fd_exec_txn_ctx_teardown( fd_exec_txn_ctx_t * txn_ctx );
     230             : 
     231             : int
     232             : fd_exec_txn_ctx_get_account_view_idx( fd_exec_txn_ctx_t *  ctx,
     233             :                                       uchar                idx,
     234             :                                       fd_txn_account_t * * account );
     235             : 
     236             : /* Same as above, except that this function doesn't check if the account
     237             :    is dead (0 balance, 0 data, etc.) or not. When agave obtains a
     238             :    borrowed account, it doesn't always check if the account is dead or
     239             :    not. For example
     240             :    https://github.com/anza-xyz/agave/blob/838c1952595809a31520ff1603a13f2c9123aa51/program-runtime/src/invoke_context.rs#L453
     241             :    This function allows us to more closely emulate that behavior. */
     242             : int
     243             : fd_exec_txn_ctx_get_account_view_idx_allow_dead( fd_exec_txn_ctx_t *  ctx,
     244             :                                                  uchar                idx,
     245             :                                                  fd_txn_account_t * * account );
     246             : 
     247             : int
     248             : fd_exec_txn_ctx_get_account_view( fd_exec_txn_ctx_t *  ctx,
     249             :                                   fd_pubkey_t const *  pubkey,
     250             :                                   fd_txn_account_t * * account );
     251             : 
     252             : int
     253             : fd_exec_txn_ctx_get_account_executable_view( fd_exec_txn_ctx_t *  ctx,
     254             :                                              fd_pubkey_t const *  pubkey,
     255             :                                              fd_txn_account_t * * account );
     256             : 
     257             : /* The fee payer is a valid modifiable account if it is passed in as writable
     258             :    in the message via a valid signature. We ignore if the account has been
     259             :    demoted or not (see fd_txn_account_is_writable_idx) for more details.
     260             :    Agave and Firedancer will reject the fee payer if the transaction message
     261             :    doesn't have a writable signature. */
     262             : int
     263             : fd_exec_txn_ctx_get_account_modify_fee_payer( fd_exec_txn_ctx_t *  ctx,
     264             :                                               fd_txn_account_t * * account );
     265             : 
     266             : int
     267             : fd_exec_txn_ctx_get_account_modify_idx( fd_exec_txn_ctx_t *  ctx,
     268             :                                         uchar                idx,
     269             :                                         ulong                min_data_sz,
     270             :                                         fd_txn_account_t * * account );
     271             : int
     272             : fd_exec_txn_ctx_get_account_modify( fd_exec_txn_ctx_t *  ctx,
     273             :                                     fd_pubkey_t const *  pubkey,
     274             :                                     ulong                min_data_sz,
     275             :                                     fd_txn_account_t * * account );
     276             : void
     277             : fd_exec_txn_ctx_reset_return_data( fd_exec_txn_ctx_t * txn_ctx );
     278             : 
     279             : /* Mirrors Agave function solana_sdk::transaction_context::find_index_of_program_account.
     280             : 
     281             :    Backward scan over transaction accounts.
     282             :    Returns -1 if not found.
     283             : 
     284             :    https://github.com/anza-xyz/agave/blob/v2.1.14/sdk/src/transaction_context.rs#L241 */
     285             : static inline int
     286             : fd_exec_txn_ctx_find_idx_of_program_account( fd_exec_txn_ctx_t const * txn_ctx,
     287           0 :                                              fd_pubkey_t const *       pubkey ) {
     288           0 :   for( ulong i=txn_ctx->accounts_cnt; i>0UL; i-- ) {
     289           0 :     if( 0==memcmp( pubkey, &txn_ctx->account_keys[ i-1UL ], sizeof(fd_pubkey_t) ) ) {
     290           0 :       return (int)((ushort)i);
     291           0 :     }
     292           0 :   }
     293           0 :    return -1;
     294           0 : }
     295             : 
     296             : /* In agave, the writable accounts cache is populated by this below function.
     297             :    This cache is then referenced to determine if a transaction account is
     298             :    writable or not.
     299             : 
     300             :    The overall logic is as follows: an account can be passed
     301             :    in as writable based on the signature and readonly signature as they are
     302             :    passed in by the transaction message. However, the account's writable
     303             :    status will be demoted if either of the two conditions are met:
     304             :    1. If the account is in the set of reserved pubkeys
     305             :    2. If the account is the program id AND the upgradeable loader account is in
     306             :       the set of transaction accounts. */
     307             : /* https://github.com/anza-xyz/agave/blob/v2.1.1/sdk/program/src/message/versions/v0/loaded.rs#L137-L150 */
     308             : int
     309             : fd_txn_account_is_writable_idx( fd_exec_txn_ctx_t const * txn_ctx, int idx );
     310             : 
     311             : FD_PROTOTYPES_END
     312             : 
     313             : #endif /* HEADER_fd_src_flamenco_runtime_context_fd_exec_txn_ctx_h */

Generated by: LCOV version 1.14