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: 0 19 0.0 %
Date: 2025-11-21 04:43:39 Functions: 0 105 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 "../../log_collector/fd_log_collector_base.h"
       6             : #include "../../../ballet/txn/fd_txn.h"
       7             : #include "../../features/fd_features.h"
       8             : #include "../fd_txncache.h"
       9             : #include "../../progcache/fd_progcache_user.h"
      10             : #include "../fd_compute_budget_details.h"
      11             : #include "../../../disco/pack/fd_microblock.h"
      12             : #include "../../../disco/pack/fd_pack.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             : /* An entry in the instruction trace */
      27             : struct fd_exec_instr_trace_entry {
      28             :   /* Metadata about the instruction */
      29             :   fd_instr_info_t * instr_info;
      30             :   /* Stack height when this instruction was pushed onto the stack (including itself)
      31             :      https://github.com/anza-xyz/agave/blob/d87e23d8d91c32d5f2be2bb3557c730bee1e9434/sdk/src/transaction_context.rs#L475-L480 */
      32             :   ulong stack_height;
      33             : };
      34             : typedef struct fd_exec_instr_trace_entry fd_exec_instr_trace_entry_t;
      35             : 
      36             : /* https://github.com/anza-xyz/agave/blob/0d34a1a160129c4293dac248e14231e9e773b4ce/program-runtime/src/compute_budget.rs#L139 */
      37             : #define FD_MAX_INSTRUCTION_TRACE_LENGTH (64UL)
      38             : /* https://github.com/anza-xyz/agave/blob/f70ab5598ccd86b216c3928e4397bf4a5b58d723/compute-budget/src/compute_budget.rs#L13 */
      39           0 : #define FD_MAX_INSTRUCTION_STACK_DEPTH  (5UL)
      40             : 
      41             : struct fd_exec_txn_ctx {
      42             :   /* Input fields: memory, bank, acc db, funk, prog cache, and txn */
      43             :   fd_bank_t *          bank;
      44             :   fd_txncache_t *      status_cache;
      45             :   fd_funk_t            funk[1];
      46             :   fd_funk_txn_xid_t    xid[1];
      47             :   fd_progcache_t *     progcache;
      48             :   fd_progcache_t       _progcache[1];
      49             :   fd_txn_p_t           txn;
      50             :   fd_exec_stack_t *    exec_stack;
      51             :   fd_exec_accounts_t * exec_accounts;
      52             : 
      53             :   /* During sanitization, v0 transactions are allowed to have up to 256 accounts:
      54             :      https://github.com/anza-xyz/agave/blob/838c1952595809a31520ff1603a13f2c9123aa51/sdk/program/src/message/versions/v0/mod.rs#L139
      55             :      Nonetheless, when Agave prepares a sanitized batch for execution and tries to lock accounts, a lower limit is enforced:
      56             :      https://github.com/anza-xyz/agave/blob/838c1952595809a31520ff1603a13f2c9123aa51/accounts-db/src/account_locks.rs#L118
      57             :      That is the limit we are going to use here. */
      58             :   struct {
      59             :     ulong                           accounts_cnt;                                /* Number of account pubkeys accessed by this transaction. */
      60             :     fd_pubkey_t                     account_keys[ MAX_TX_ACCOUNT_LOCKS ];        /* Array of account pubkeys accessed by this transaction. */
      61             :     fd_txn_account_t                accounts[ MAX_TX_ACCOUNT_LOCKS ];            /* Array of borrowed accounts accessed by this transaction. */
      62             :     ulong                           executable_cnt;                              /* Number of BPF upgradeable loader accounts. */
      63             :     fd_txn_account_t                executable_accounts[ MAX_TX_ACCOUNT_LOCKS ]; /* Array of BPF upgradeable loader program data accounts */
      64             :   /* The next three fields describe Agave's "rollback" accounts, which
      65             :      are copies of the fee payer and (if applicable) nonce account.  If
      66             :      the transaction fails to load, the fee payer is still debited the
      67             :      transaction fee, and the nonce account is advanced.  The fee payer
      68             :      must also be rolled back to its state pre-transaction, plus
      69             :      debited any transaction fees.
      70             :      This is a bit of a misnomer but Agave calls it "rollback".
      71             :      This is the account state that the nonce account should be in when
      72             :      the txn fails. It will advance the nonce account, rather than "roll
      73             :      back". */
      74             :     fd_txn_account_t                rollback_nonce[ 1 ];
      75             :     /* If the transaction has a nonce account that must be advanced,
      76             :        this would be !=ULONG_MAX. */
      77             :     ulong                           nonce_idx_in_txn;
      78             :     fd_txn_account_t                rollback_fee_payer[ 1 ];
      79             : 
      80             :   } accounts;
      81             : 
      82             :   struct {
      83             :     uchar                       stack_sz;                              /* Current depth of the instruction execution stack. */
      84             :     fd_exec_instr_ctx_t         stack[FD_MAX_INSTRUCTION_STACK_DEPTH]; /* Instruction execution stack. */
      85             :     /* The memory for all of the instructions in the transaction
      86             :        (including CPI instructions) are preallocated.  However, the order
      87             :        in which the instructions are executed does not match the order in
      88             :        which they are allocated.  The instr_trace will instead be used to
      89             :        track the order in which the instructions are executed.  We leave
      90             :        space for an extra instruction to account for the case where the
      91             :        transaction has too many instructions leading to
      92             :        FD_EXECUTOR_INSTR_ERR_MAX_INSN_TRACE_LENS_EXCEEDED.
      93             :        TODO: In reality, we should just be allocating instr_infos per
      94             :        instruction and not up front.  The dependency on using instr_info
      95             :        for the sysvar instruction setup is not needed and should be
      96             :        removed.  At this point, instr_info snad instr_trace should be
      97             :        combined. */
      98             :     fd_instr_info_t             infos[FD_MAX_INSTRUCTION_TRACE_LENGTH * 2UL];
      99             :     ulong                       info_cnt;
     100             :     fd_exec_instr_trace_entry_t trace[FD_MAX_INSTRUCTION_TRACE_LENGTH]; /* Instruction trace */
     101             :     ulong                       trace_length;                           /* Number of instructions in the trace */
     102             :     /* The current instruction index being executed */
     103             :     int current_idx;
     104             :   } instr;
     105             : 
     106             :   struct {
     107             :     int                 is_bundle;
     108             :     fd_exec_txn_ctx_t * prev_txn_ctxs[ FD_PACK_MAX_TXN_PER_BUNDLE ];
     109             :     ulong               prev_txn_ctxs_cnt;
     110             :   } bundle;
     111             : 
     112             :   struct {
     113             :     int is_committable;
     114             :     int is_fees_only;
     115             :     int txn_err;
     116             :     /* These are error fields produced by instruction execution
     117             :        when txn_err == FD_RUNTIME_TXN_ERR_INSTRUCTION_ERROR (-9). */
     118             :     int  exec_err;
     119             :     int  exec_err_kind;
     120             :     int  exec_err_idx;
     121             :     uint custom_err;
     122             :   } err;
     123             : 
     124             :   struct {
     125             :     fd_compute_budget_details_t compute_budget;                 /* Compute budget details */
     126             :     ulong                       loaded_accounts_data_size;      /* The actual transaction loaded data size */
     127             :     ulong                       loaded_accounts_data_size_cost; /* The cost of the loaded accounts data size in CUs */
     128             :     ulong                       accounts_resize_delta;          /* Transaction level tracking for account resizing */
     129             :     fd_txn_return_data_t        return_data;                    /* Data returned from `return_data` syscalls */
     130             :     fd_hash_t                   blake_txn_msg_hash;             /* Hash of raw transaction message used by the status cache */
     131             :     ulong                       execution_fee;                  /* Execution fee paid by the fee payer in the transaction */
     132             :     ulong                       priority_fee;                   /* Priority fee paid by the fee payer in the transaction */
     133             :     /* When a program is deployed or upgraded, we must queue it to be
     134             :        updated in the program cache (if it exists already) so that
     135             :        the cache entry's ELF / sBPF information can be updated for
     136             :        future executions.  We keep an array of pubkeys for the
     137             :        transaction to track which programs need to be reverified.  The
     138             :        actual queueing for reverification is done in the transaction
     139             :        finalization step. */
     140             :     uchar                       programs_to_reverify_cnt;
     141             :     fd_pubkey_t                 programs_to_reverify[ MAX_TX_ACCOUNT_LOCKS ];
     142             :   } details;
     143             : 
     144             :   struct {
     145             :     int                enable_exec_recording;
     146             :     fd_log_collector_t log_collector; /* Log collector instance */
     147             :     fd_capture_ctx_t * capture_ctx;
     148             :     /* Pointer to buffer used for dumping instructions and transactions
     149             :        into protobuf files. */
     150             :     uchar *            dumping_mem;
     151             :     /* Pointer to buffer used for tracing instructions and transactions
     152             :        into protobuf files. */
     153             :     int                enable_vm_tracing;
     154             :     uchar *            tracing_mem;
     155             :   } log;
     156             : };
     157             : 
     158           0 : #define FD_EXEC_TXN_CTX_ALIGN     (alignof(fd_exec_txn_ctx_t))
     159           0 : #define FD_EXEC_TXN_CTX_FOOTPRINT ( sizeof(fd_exec_txn_ctx_t))
     160             : 
     161             : FD_PROTOTYPES_BEGIN
     162             : 
     163             : /* Error logging handholding assertions */
     164             : 
     165             : #ifdef FD_RUNTIME_ERR_HANDHOLDING
     166             : 
     167             : /* Asserts that the error and error kind are not populated (zero) */
     168             : #define FD_TXN_TEST_ERR_OVERWRITE( txn_ctx ) \
     169             :    FD_TEST( !txn_ctx->err.exec_err );        \
     170             :    FD_TEST( !txn_ctx->err.exec_err_kind )
     171             : 
     172             : /* Used prior to a FD_TXN_ERR_FOR_LOG_INSTR call to deliberately
     173             :    bypass overwrite handholding checks.
     174             :    Only use this if you know what you're doing. */
     175             : #define FD_TXN_PREPARE_ERR_OVERWRITE( txn_ctx ) \
     176             :    txn_ctx->err.exec_err = 0;                   \
     177             :    txn_ctx->err.exec_err_kind = 0
     178             : 
     179             : #else
     180             : 
     181           0 : #define FD_TXN_TEST_ERR_OVERWRITE( txn_ctx ) ( ( void )0 )
     182           0 : #define FD_TXN_PREPARE_ERR_OVERWRITE( txn_ctx ) ( ( void )0 )
     183             : 
     184             : #endif
     185             : 
     186           0 : #define FD_TXN_ERR_FOR_LOG_INSTR( txn_ctx, err_, idx ) (__extension__({ \
     187           0 :     FD_TXN_TEST_ERR_OVERWRITE( txn_ctx );                               \
     188           0 :     txn_ctx->err.exec_err = err_;                                       \
     189           0 :     txn_ctx->err.exec_err_kind = FD_EXECUTOR_ERR_KIND_INSTR;            \
     190           0 :     txn_ctx->err.exec_err_idx = idx;                                    \
     191           0 :   }))
     192             : 
     193             : void *
     194             : fd_exec_txn_ctx_new( void * mem );
     195             : 
     196             : fd_exec_txn_ctx_t *
     197             : fd_exec_txn_ctx_join( void * mem );
     198             : 
     199             : void *
     200             : fd_exec_txn_ctx_leave( fd_exec_txn_ctx_t * ctx );
     201             : 
     202             : void *
     203             : fd_exec_txn_ctx_delete( void * mem );
     204             : 
     205             : /* Mirrors Agave function solana_sdk::transaction_context::find_index_of_account
     206             : 
     207             :    Backward scan over transaction accounts.
     208             :    Returns -1 if not found.
     209             : 
     210             :    https://github.com/anza-xyz/agave/blob/v2.1.14/sdk/src/transaction_context.rs#L233-L238 */
     211             : 
     212             : static inline int
     213             : fd_exec_txn_ctx_find_index_of_account( fd_exec_txn_ctx_t const * ctx,
     214           0 :                                        fd_pubkey_t const *       pubkey ) {
     215           0 :   for( ulong i=ctx->accounts.accounts_cnt; i>0UL; i-- ) {
     216           0 :     if( 0==memcmp( pubkey, &ctx->accounts.account_keys[ i-1UL ], sizeof(fd_pubkey_t) ) ) {
     217           0 :       return (int)(i-1UL);
     218           0 :     }
     219           0 :   }
     220           0 :   return -1;
     221           0 : }
     222             : 
     223             : typedef int fd_txn_account_condition_fn_t ( fd_txn_account_t *        acc,
     224             :                                             fd_exec_txn_ctx_t const * ctx,
     225             :                                             ushort                    idx );
     226             : 
     227             : /* Mirrors Agave function solana_sdk::transaction_context::get_account_at_index
     228             : 
     229             :    Takes a function pointer to a condition function to check pre-conditions on the
     230             :    obtained account. If the condition function is NULL, the account is returned without
     231             :    any pre-condition checks.
     232             : 
     233             :    https://github.com/anza-xyz/agave/blob/v2.1.14/sdk/src/transaction_context.rs#L223-L230 */
     234             : 
     235             : int
     236             : fd_exec_txn_ctx_get_account_at_index( fd_exec_txn_ctx_t *             ctx,
     237             :                                       ushort                          idx,
     238             :                                       fd_txn_account_t * *            account,
     239             :                                       fd_txn_account_condition_fn_t * condition );
     240             : 
     241             : /* A wrapper around fd_exec_txn_ctx_get_account_at_index that obtains an
     242             :    account from the transaction context by its pubkey. */
     243             : 
     244             : int
     245             : fd_exec_txn_ctx_get_account_with_key( fd_exec_txn_ctx_t *             ctx,
     246             :                                       fd_pubkey_t const *             pubkey,
     247             :                                       fd_txn_account_t * *            account,
     248             :                                       fd_txn_account_condition_fn_t * condition );
     249             : 
     250             : /* Gets an executable (program data) account via its pubkey. */
     251             : 
     252             : int
     253             : fd_exec_txn_ctx_get_executable_account( fd_exec_txn_ctx_t *             ctx,
     254             :                                         fd_pubkey_t const *             pubkey,
     255             :                                         fd_txn_account_t * *            account,
     256             :                                         fd_txn_account_condition_fn_t * condition );
     257             : 
     258             : /* Mirrors Agave function solana_sdk::transaction_context::get_key_of_account_at_index
     259             : 
     260             :    https://github.com/anza-xyz/agave/blob/v2.1.14/sdk/src/transaction_context.rs#L212 */
     261             : 
     262             : int
     263             : fd_exec_txn_ctx_get_key_of_account_at_index( fd_exec_txn_ctx_t *  ctx,
     264             :                                              ushort               idx,
     265             :                                              fd_pubkey_t const * * key );
     266             : 
     267             : /* In agave, the writable accounts cache is populated by this below function.
     268             :    This cache is then referenced to determine if a transaction account is
     269             :    writable or not.
     270             : 
     271             :    The overall logic is as follows: an account can be passed
     272             :    in as writable based on the signature and readonly signature as they are
     273             :    passed in by the transaction message. However, the account's writable
     274             :    status will be demoted if either of the two conditions are met:
     275             :    1. If the account is in the set of reserved pubkeys
     276             :    2. If the account is the program id AND the upgradeable loader account is in
     277             :       the set of transaction accounts. */
     278             : /* https://github.com/anza-xyz/agave/blob/v2.1.1/sdk/program/src/message/versions/v0/loaded.rs#L137-L150 */
     279             : 
     280             : int
     281             : fd_exec_txn_ctx_account_is_writable_idx( fd_exec_txn_ctx_t const * txn_ctx, ushort idx );
     282             : 
     283             : /* This flat function does the same as the function above, but uses the
     284             :    exact arguments needed instead of the full fd_exec_txn_ctx_t */
     285             : 
     286             : int
     287             : fd_exec_txn_account_is_writable_idx_flat( const ulong           slot,
     288             :                                           const ushort          idx,
     289             :                                           const fd_pubkey_t *   addr_at_idx,
     290             :                                           const fd_txn_t *      txn_descriptor,
     291             :                                           const fd_features_t * features,
     292             :                                           const uint            bpf_upgradeable_in_txn );
     293             : 
     294             : /* The bpf_upgradeable_in_txn argument of the above function can be
     295             :    obtained by the function below */
     296             : uint
     297             : fd_txn_account_has_bpf_loader_upgradeable( const fd_pubkey_t * account_keys,
     298             :                                            const ulong         accounts_cnt );
     299             : 
     300             : 
     301             : /* Account pre-condition filtering functions
     302             : 
     303             :    Used to filter accounts based on pre-conditions such as existence, is_writable, etc.
     304             :    when obtaining accounts from the transaction context. Passed as a function pointer. */
     305             : 
     306             : int
     307             : fd_txn_account_check_exists( fd_txn_account_t *        acc,
     308             :                              fd_exec_txn_ctx_t const * ctx,
     309             :                              ushort                    idx );
     310             : 
     311             : int
     312             : fd_txn_account_check_is_writable( fd_txn_account_t *        acc,
     313             :                                   fd_exec_txn_ctx_t const * ctx,
     314             :                                   ushort                    idx );
     315             : 
     316             : /* The fee payer is a valid modifiable account if it is passed in as writable
     317             :    in the message via a valid signature. We ignore if the account has been
     318             :    demoted or not (see fd_exec_txn_ctx_account_is_writable_idx) for more details.
     319             :    Agave and Firedancer will reject the fee payer if the transaction message
     320             :    doesn't have a writable signature. */
     321             : 
     322             : int
     323             : fd_txn_account_check_fee_payer_writable( fd_txn_account_t *        acc,
     324             :                                          fd_exec_txn_ctx_t const * ctx,
     325             :                                          ushort                    idx );
     326             : 
     327             : /* Checks if the account is mutable and borrows the account mutably.
     328             : 
     329             :    The borrow is an acquired write on the account object.
     330             :    The caller is responsible for releasing the write via
     331             :    fd_txn_account_release_write.
     332             : 
     333             :    TODO: Agave doesn't need to check if the account is mutable
     334             :    because it uses Writable/Readable traits for accounts. We
     335             :    should have a similar concept to abstract away fd_txn_account_t's
     336             :    const_meta and meta fields. */
     337             : 
     338             : int
     339             : fd_txn_account_check_borrow_mut( fd_txn_account_t *        acc,
     340             :                                  fd_exec_txn_ctx_t const * ctx,
     341             :                                  ushort                    idx );
     342             : 
     343             : 
     344             : FD_PROTOTYPES_END
     345             : 
     346             : #endif /* HEADER_fd_src_flamenco_runtime_context_fd_exec_txn_ctx_h */

Generated by: LCOV version 1.14