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 : 8 : #include "../fd_borrowed_account.h" 9 : 10 : #include "../../../ballet/txn/fd_txn.h" 11 : 12 : /* Return data for syscalls */ 13 : 14 : struct fd_txn_return_data { 15 : fd_pubkey_t program_id; 16 : ulong len; 17 : uchar data[1024]; 18 : }; 19 : 20 : typedef struct fd_txn_return_data fd_txn_return_data_t; 21 : 22 : /* fd_exec_txn_ctx_t is the context needed to execute a transaction. */ 23 : 24 : /* Cache of deserialized vote accounts to support iteration after replaying a slot (required for fork choice) */ 25 : struct fd_vote_account_cache_entry { 26 : fd_pubkey_t pubkey; 27 : ulong next; 28 : fd_vote_state_t vote_account; 29 : }; 30 : typedef struct fd_vote_account_cache_entry fd_vote_account_cache_entry_t; 31 : 32 : #define POOL_NAME fd_vote_account_pool 33 : #define POOL_T fd_vote_account_cache_entry_t 34 : #include "../../../util/tmpl/fd_pool.c" 35 : 36 : #define MAP_NAME fd_vote_account_cache 37 : #define MAP_ELE_T fd_vote_account_cache_entry_t 38 : #define MAP_KEY pubkey 39 : #define MAP_KEY_T fd_pubkey_t 40 : #define MAP_KEY_EQ(k0,k1) (!(memcmp((k0)->key,(k1)->key,sizeof(fd_hash_t)))) 41 : #define MAP_KEY_HASH(key,seed) ( ((key)->ui[0]) ^ (seed) ) 42 : #include "../../../util/tmpl/fd_map_chain.c" 43 : 44 : /* An entry in the instruction trace */ 45 : struct fd_exec_instr_trace_entry { 46 : /* Metadata about the instruction */ 47 : fd_instr_info_t * instr_info; 48 : /* Stack height when this instruction was pushed onto the stack (including itself) 49 : https://github.com/anza-xyz/agave/blob/d87e23d8d91c32d5f2be2bb3557c730bee1e9434/sdk/src/transaction_context.rs#L475-L480 */ 50 : ulong stack_height; 51 : }; 52 : typedef struct fd_exec_instr_trace_entry fd_exec_instr_trace_entry_t; 53 : 54 : /* https://github.com/anza-xyz/agave/blob/0d34a1a160129c4293dac248e14231e9e773b4ce/program-runtime/src/compute_budget.rs#L139 */ 55 : #define FD_MAX_INSTRUCTION_TRACE_LENGTH (64UL) 56 : /* https://github.com/anza-xyz/agave/blob/f70ab5598ccd86b216c3928e4397bf4a5b58d723/compute-budget/src/compute_budget.rs#L13 */ 57 817740 : #define FD_MAX_INSTRUCTION_STACK_DEPTH (5UL) 58 : 59 : struct __attribute__((aligned(8UL))) fd_exec_txn_ctx { 60 : ulong magic; /* ==FD_EXEC_TXN_CTX_MAGIC */ 61 : 62 : fd_exec_epoch_ctx_t const * epoch_ctx; 63 : fd_exec_slot_ctx_t const * slot_ctx; 64 : 65 : fd_funk_txn_t * funk_txn; 66 : fd_acc_mgr_t * acc_mgr; 67 : fd_spad_t * spad; /* Sized out to handle the worst case footprint of single transaction execution. */ 68 : 69 : ulong paid_fees; 70 : ulong compute_unit_limit; /* Compute unit limit for this transaction. */ 71 : ulong compute_unit_price; /* Compute unit price for this transaction. */ 72 : ulong compute_meter; /* Remaining compute units */ 73 : ulong heap_size; /* Heap size for VMs for this transaction. */ 74 : ulong loaded_accounts_data_size_limit; /* Loaded accounts data size limit for this transaction. */ 75 : uint prioritization_fee_type; /* The type of prioritization fee to use. */ 76 : fd_txn_t const * txn_descriptor; /* Descriptor of the transaction. */ 77 : fd_rawtxn_b_t _txn_raw[1]; /* Raw bytes of the transaction. */ 78 : uint custom_err; /* When a custom error is returned, this is where the numeric value gets stashed */ 79 : uchar instr_stack_sz; /* Current depth of the instruction execution stack. */ 80 : fd_exec_instr_ctx_t instr_stack[FD_MAX_INSTRUCTION_STACK_DEPTH]; /* Instruction execution stack. */ 81 : fd_exec_instr_ctx_t * failed_instr; 82 : int instr_err_idx; 83 : /* During sanitization, v0 transactions are allowed to have up to 256 accounts: 84 : https://github.com/anza-xyz/agave/blob/838c1952595809a31520ff1603a13f2c9123aa51/sdk/program/src/message/versions/v0/mod.rs#L139 85 : Nonetheless, when Agave prepares a sanitized batch for execution and tries to lock accounts, a lower limit is enforced: 86 : https://github.com/anza-xyz/agave/blob/838c1952595809a31520ff1603a13f2c9123aa51/accounts-db/src/account_locks.rs#L118 87 : That is the limit we are going to use here. */ 88 : ulong accounts_cnt; /* Number of account pubkeys accessed by this transaction. */ 89 : fd_pubkey_t accounts[ MAX_TX_ACCOUNT_LOCKS ]; /* Array of account pubkeys accessed by this transaction. */ 90 : ulong executable_cnt; /* Number of BPF upgradeable loader accounts. */ 91 : fd_borrowed_account_t executable_accounts[ MAX_TX_ACCOUNT_LOCKS ]; /* Array of BPF upgradeable loader program data accounts */ 92 : fd_borrowed_account_t borrowed_accounts[ MAX_TX_ACCOUNT_LOCKS ]; /* Array of borrowed accounts accessed by this transaction. */ 93 : uchar nonce_accounts[ MAX_TX_ACCOUNT_LOCKS ]; /* Nonce accounts in the txn to be saved */ 94 : uint num_instructions; /* Counter for number of instructions in txn */ 95 : fd_txn_return_data_t return_data; /* Data returned from `return_data` syscalls */ 96 : fd_vote_account_cache_t * vote_accounts_map; /* Cache of bank's deserialized vote accounts to support fork choice */ 97 : fd_vote_account_cache_entry_t * vote_accounts_pool; /* Memory pool for deserialized vote account cache */ 98 : ulong accounts_resize_delta; /* Transaction level tracking for account resizing */ 99 : fd_hash_t blake_txn_msg_hash; /* Hash of raw transaction message used by the status cache */ 100 : ulong execution_fee; /* Execution fee paid by the fee payer in the transaction */ 101 : ulong priority_fee; /* Priority fee paid by the fee payer in the transaction */ 102 : ulong collected_rent; /* Rent collected from accounts in this transaction */ 103 : 104 : uchar dirty_vote_acc : 1; /* 1 if this transaction maybe modified a vote account */ 105 : uchar dirty_stake_acc : 1; /* 1 if this transaction maybe modified a stake account */ 106 : 107 : fd_capture_ctx_t * capture_ctx; 108 : 109 : /* The instr_infos for the entire transaction are allocated at the start of 110 : the transaction. However, this must preserve a different counter because 111 : the top level instructions must get set up at once. The instruction 112 : error check on a maximum instruction size can be done on the 113 : instr_info_cnt instead of the instr_trace_length because it is a proxy 114 : for the trace_length: the instr_info_cnt gets incremented faster than 115 : the instr_trace_length because it counts all of the top level instructions 116 : first. */ 117 : fd_instr_info_t instr_infos[FD_MAX_INSTRUCTION_TRACE_LENGTH]; 118 : ulong instr_info_cnt; 119 : 120 : fd_exec_instr_trace_entry_t instr_trace[FD_MAX_INSTRUCTION_TRACE_LENGTH]; /* Instruction trace */ 121 : ulong instr_trace_length; /* Number of instructions in the trace */ 122 : 123 : fd_log_collector_t log_collector; /* Log collector instance */ 124 : 125 : /* Execution error and type, to match Agave. */ 126 : int exec_err; 127 : int exec_err_kind; 128 : 129 : /* The has_program_id flag is used to indicate if the current transaction has valid program indices or not. 130 : It will be set in fd_executor_load_transaction_accounts similar to how program_indices is used in 131 : load_transaction_accounts on the agave side */ 132 : uchar has_program_id; 133 : }; 134 : 135 415524 : #define FD_EXEC_TXN_CTX_ALIGN (alignof(fd_exec_txn_ctx_t)) 136 415524 : #define FD_EXEC_TXN_CTX_FOOTPRINT ( sizeof(fd_exec_txn_ctx_t)) 137 407859 : #define FD_EXEC_TXN_CTX_MAGIC (0x9AD93EE71469F4D7UL ) /* random */ 138 : 139 : FD_PROTOTYPES_BEGIN 140 : 141 59973 : #define FD_TXN_ERR_FOR_LOG_INSTR( txn_ctx, err, idx ) (__extension__({ \ 142 59973 : txn_ctx->exec_err = err; \ 143 59973 : txn_ctx->exec_err_kind = FD_EXECUTOR_ERR_KIND_INSTR; \ 144 59973 : txn_ctx->instr_err_idx = idx; \ 145 59973 : })) 146 : 147 : void * 148 : fd_exec_txn_ctx_new( void * mem ); 149 : 150 : fd_exec_txn_ctx_t * 151 : fd_exec_txn_ctx_join( void * mem ); 152 : 153 : void * 154 : fd_exec_txn_ctx_leave( fd_exec_txn_ctx_t * ctx ); 155 : 156 : void * 157 : fd_exec_txn_ctx_delete( void * mem ); 158 : 159 : /* Sets up a basic transaction ctx without a txn descriptor or txn raw. Useful 160 : for mocking transaction context objects for instructions. */ 161 : void 162 : fd_exec_txn_ctx_setup_basic( fd_exec_txn_ctx_t * txn_ctx ); 163 : 164 : void 165 : fd_exec_txn_ctx_setup( fd_exec_txn_ctx_t * txn_ctx, 166 : fd_txn_t const * txn_descriptor, 167 : fd_rawtxn_b_t const * txn_raw ); 168 : 169 : void 170 : fd_exec_txn_ctx_from_exec_slot_ctx( fd_exec_slot_ctx_t * slot_ctx, 171 : fd_exec_txn_ctx_t * txn_ctx ); 172 : 173 : void 174 : fd_exec_txn_ctx_teardown( fd_exec_txn_ctx_t * txn_ctx ); 175 : 176 : int 177 : fd_txn_borrowed_account_view_idx( fd_exec_txn_ctx_t * ctx, 178 : uchar idx, 179 : fd_borrowed_account_t * * account ); 180 : 181 : /* Same as above, except that this function doesn't check if the account 182 : is dead (0 balance, 0 data, etc.) or not. When agave obtains a 183 : borrowed account, it doesn't always check if the account is dead or 184 : not. For example 185 : https://github.com/anza-xyz/agave/blob/838c1952595809a31520ff1603a13f2c9123aa51/program-runtime/src/invoke_context.rs#L453 186 : This function allows us to more closely emulate that behavior. */ 187 : int 188 : fd_txn_borrowed_account_view_idx_allow_dead( fd_exec_txn_ctx_t * ctx, 189 : uchar idx, 190 : fd_borrowed_account_t * * account ); 191 : 192 : int 193 : fd_txn_borrowed_account_view( fd_exec_txn_ctx_t * ctx, 194 : fd_pubkey_t const * pubkey, 195 : fd_borrowed_account_t * * account ); 196 : 197 : int 198 : fd_txn_borrowed_account_executable_view( fd_exec_txn_ctx_t * ctx, 199 : fd_pubkey_t const * pubkey, 200 : fd_borrowed_account_t * * account ); 201 : 202 : /* The fee payer is a valid modifiable account if it is passed in as writable 203 : in the message via a valid signature. We ignore if the account has been 204 : demoted or not (see fd_txn_account_is_writable_idx) for more details. 205 : Agave and Firedancer will reject the fee payer if the transaction message 206 : doesn't have a writable signature. */ 207 : int 208 : fd_txn_borrowed_account_modify_fee_payer( fd_exec_txn_ctx_t * ctx, 209 : fd_borrowed_account_t * * account ); 210 : 211 : int 212 : fd_txn_borrowed_account_modify_idx( fd_exec_txn_ctx_t * ctx, 213 : uchar idx, 214 : ulong min_data_sz, 215 : fd_borrowed_account_t * * account ); 216 : int 217 : fd_txn_borrowed_account_modify( fd_exec_txn_ctx_t * ctx, 218 : fd_pubkey_t const * pubkey, 219 : ulong min_data_sz, 220 : fd_borrowed_account_t * * account ); 221 : void 222 : fd_exec_txn_ctx_reset_return_data( fd_exec_txn_ctx_t * txn_ctx ); 223 : 224 : /* In agave, the writable accounts cache is populated by this below function. 225 : This cache is then referenced to determine if a transaction account is 226 : writable or not. 227 : 228 : The overall logic is as follows: an account can be passed 229 : in as writable based on the signature and readonly signature as they are 230 : passed in by the transaction message. However, the account's writable 231 : status will be demoted if either of the two conditions are met: 232 : 1. If the account is in the set of reserved pubkeys 233 : 2. If the account is the program id AND the upgradeable loader account is in 234 : the set of transaction accounts. */ 235 : /* https://github.com/anza-xyz/agave/blob/v2.1.1/sdk/program/src/message/versions/v0/loaded.rs#L137-L150 */ 236 : int 237 : fd_txn_account_is_writable_idx( fd_exec_txn_ctx_t const * txn_ctx, int idx ); 238 : 239 : FD_PROTOTYPES_END 240 : 241 : #endif /* HEADER_fd_src_flamenco_runtime_context_fd_exec_txn_ctx_h */