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 */