Line data Source code
1 : #ifndef HEADER_fd_src_flamenco_runtime_fd_runtime_h 2 : #define HEADER_fd_src_flamenco_runtime_fd_runtime_h 3 : 4 : #include "fd_runtime_helpers.h" 5 : 6 : /* The general structure for executing transactions in Firedancer can 7 : be thought as a state maching where transaction execution is a 8 : deterministic state transition over various data structures. 9 : 10 : The starting and ending state before a transaction is executed is 11 : represented by the bank, accounts database, and status cache. The 12 : bank holds Solana state not represented by accounts (see fd_bank.c/h 13 : for more details) and each bank is per-slot. The latter two data 14 : structures are contained by the runtime. 15 : 16 : The runtime also owns valid joins to important data structures which 17 : are non-deterministically transitioned through execution such as the 18 : program cache which is a pure cache on top of the accounts database. 19 : The runtime also owns bounded out temporary memory regions used for 20 : transaction execution and valid joins to other scratch memory regions 21 : (e.g. acc_pool). 22 : 23 : So we expect the state of the runtime and the bank to change as a 24 : result of execution. 25 : 26 : The transaction, or the input to said state machine is represented by 27 : a fd_txn_in_t. The fd_txn_in_t is just a parsed transaction message 28 : and any state that may have accrued as a result of bundle execution. 29 : 30 : Executing a transaction produces a set of results. This is 31 : represented by a fd_txn_out_t. The fd_txn_out_t consists of any 32 : information that needs to be applied to the bank and runtime. 33 : 34 : We can execute a fd_txn_in_t against a given fd_runtime_t and a 35 : fd_bank_t and expect to produce a fd_txn_out_t. Then a fd_txn_out_t 36 : can be applied/committed on top of a fd_runtime_t and fd_bank_t. 37 : Execution is done via fd_runtime_prepare_and_execute_txn. If a 38 : transaction is committable, it should be committed via 39 : fd_runtime_commit_txn. If a transaction is not committable, it 40 : should be canceled via fd_runtime_cancel_txn. 41 : 42 : TLDR: The runtime is a state machine that executes transactions and 43 : produces results that are applied to various data structures 44 : including the bank, account database, and status cache. The 45 : transaction is executed via a call to 46 : fd_runtime_prepare_and_execute_txn. If the transaction is 47 : committable, it should be committed via fd_runtime_commit_txn. If 48 : the transaction is not committable (txn_out->err.is_committable is 0), 49 : it should be canceled via fd_runtime_cancel_txn. Two calls to 50 : fd_runtime_prepare_and_execute_txn without a call to 51 : fd_runtime_commit_txn or fd_runtime_cancel_txn in between are not 52 : allowed. 53 : 54 : input output 55 : fd_runtime_t -> 56 : fd_txn_in_t -> fd_runtime_prepare_and_execute_txn() -> fd_txn_out_t 57 : fd_bank_t -> 58 : 59 : fd_txn_out_t is the state transition output of a transaction. 60 : 61 : txn_out (committable) --> fd_runtime_commit_txn() 62 : txn_out (not committable) --> fd_runtime_cancel_txn() 63 : */ 64 : 65 : struct fd_runtime { 66 : fd_accdb_user_t * accdb; 67 : fd_txncache_t * status_cache; 68 : fd_progcache_t * progcache; 69 : fd_acc_pool_t * acc_pool; 70 : 71 : struct { 72 : uchar stack_sz; /* Current depth of the instruction execution stack. */ 73 : fd_exec_instr_ctx_t stack[ FD_MAX_INSTRUCTION_STACK_DEPTH ]; /* Instruction execution stack. */ 74 : /* The memory for all of the instructions in the transaction 75 : (including CPI instructions) are preallocated. However, the 76 : order in which the instructions are executed does not match the 77 : order in which they are allocated. The instr_trace will instead 78 : be used to track the order in which the instructions are 79 : executed. We add a +1 to allow any instructions past the max 80 : instr trace limit to be safely allocated, so that we can fail 81 : out like Agave does later at the stack push step within 82 : fd_execute_instr. 83 : 84 : The caller is responsible for updating the trace_length for the 85 : callee. For CPI, the trace length is updated when preparing a 86 : new instruction within cpi_common. For top-level instructions, 87 : the trace length is updated within fd_execute_txn when preparing 88 : an instruction for execution. */ 89 : fd_instr_info_t trace[ FD_MAX_INSTRUCTION_TRACE_LENGTH+1UL ]; 90 : ulong trace_length; 91 : /* The current instruction index being executed */ 92 : int current_idx; 93 : } instr; 94 : 95 : struct { 96 : /* The sysvar instructions account is a special account that is 97 : modified through the course of transaction execution, but its 98 : results are not committed to the bank or accounts database. */ 99 : uchar sysvar_instructions_mem[ FD_ACC_TOT_SZ_MAX ] __attribute__((aligned(FD_ACCOUNT_REC_ALIGN))); 100 : 101 : /* The executable accounts are derived from the accounts in the 102 : transaction and are used by the bpf loader program to validate 103 : the program data account. */ 104 : ulong executable_cnt; /* Number of BPF upgradeable loader accounts. */ 105 : fd_accdb_ro_t executable[ MAX_TX_ACCOUNT_LOCKS ]; /* Array of BPF upgradeable loader program data accounts */ 106 : 107 : ulong starting_lamports[ MAX_TX_ACCOUNT_LOCKS ]; /* Starting lamports for each account */ 108 : ulong starting_dlen[ MAX_TX_ACCOUNT_LOCKS ]; /* Starting data length for each account */ 109 : ulong refcnt[ MAX_TX_ACCOUNT_LOCKS ]; /* Reference count for each account */ 110 : } accounts; 111 : 112 : struct { 113 : int enable_log_collector; 114 : fd_log_collector_t * log_collector; /* Log collector instance */ 115 : fd_capture_ctx_t * capture_ctx; 116 : fd_dump_proto_ctx_t * dump_proto_ctx; 117 : 118 : /* Pointer to buffer used for dumping instructions and transactions 119 : into protobuf files. */ 120 : uchar * dumping_mem; 121 : /* Pointer to buffer used for tracing instructions and transactions 122 : into protobuf files. */ 123 : int enable_vm_tracing; 124 : uchar * tracing_mem; 125 : } log; 126 : 127 : struct { 128 : uchar serialization_mem[ FD_MAX_INSTRUCTION_STACK_DEPTH ][ BPF_LOADER_SERIALIZATION_FOOTPRINT ] __attribute__((aligned(FD_RUNTIME_EBPF_HOST_ALIGN))); 129 : } bpf_loader_serialization; 130 : 131 : struct { 132 : uchar rodata [ FD_RUNTIME_ACC_SZ_MAX ] __attribute__((aligned(FD_SBPF_PROG_RODATA_ALIGN))); 133 : uchar sbpf_footprint[ FD_SBPF_PROGRAM_FOOTPRINT ] __attribute__((aligned(alignof(fd_sbpf_program_t)))); 134 : uchar programdata [ FD_RUNTIME_ACC_SZ_MAX ] __attribute__((aligned(FD_ACCOUNT_REC_ALIGN))); 135 : } bpf_loader_program; 136 : 137 : union { 138 : struct { 139 : uchar vote_state_mem [ FD_VOTE_STATE_VERSIONED_FOOTPRINT ] __attribute__((aligned(FD_VOTE_STATE_VERSIONED_ALIGN))); 140 : uchar authorized_voters_mem[ FD_AUTHORIZED_VOTERS_FOOTPRINT ] __attribute__((aligned(FD_AUTHORIZED_VOTERS_ALIGN))); 141 : uchar landed_votes_mem [ FD_LANDED_VOTES_FOOTPRINT ] __attribute__((aligned(FD_LANDED_VOTES_ALIGN))); 142 : uchar vote_lockout_mem [ FD_VOTE_LOCKOUTS_FOOTPRINT ] __attribute__((aligned(FD_VOTE_LOCKOUTS_ALIGN))); 143 : } authorize; 144 : 145 : struct { 146 : uchar vote_state_mem [ FD_VOTE_STATE_VERSIONED_FOOTPRINT ] __attribute__((aligned(FD_VOTE_STATE_VERSIONED_ALIGN))); 147 : uchar authorized_voters_mem[ FD_AUTHORIZED_VOTERS_FOOTPRINT ] __attribute__((aligned(FD_AUTHORIZED_VOTERS_ALIGN))); 148 : uchar landed_votes_mem [ FD_LANDED_VOTES_FOOTPRINT ] __attribute__((aligned(FD_LANDED_VOTES_ALIGN))); 149 : uchar vote_lockout_mem [ FD_VOTE_LOCKOUTS_FOOTPRINT ] __attribute__((aligned(FD_VOTE_LOCKOUTS_ALIGN))); 150 : } update_validator_identity; 151 : 152 : struct { 153 : uchar vote_state_mem [ FD_VOTE_STATE_VERSIONED_FOOTPRINT ] __attribute__((aligned(FD_VOTE_STATE_VERSIONED_ALIGN))); 154 : uchar authorized_voters_mem[ FD_AUTHORIZED_VOTERS_FOOTPRINT ] __attribute__((aligned(FD_AUTHORIZED_VOTERS_ALIGN))); 155 : uchar landed_votes_mem [ FD_LANDED_VOTES_FOOTPRINT ] __attribute__((aligned(FD_LANDED_VOTES_ALIGN))); 156 : uchar vote_lockout_mem [ FD_VOTE_LOCKOUTS_FOOTPRINT ] __attribute__((aligned(FD_VOTE_LOCKOUTS_ALIGN))); 157 : } update_commission; 158 : 159 : struct { 160 : uchar vote_state_mem [ FD_VOTE_STATE_VERSIONED_FOOTPRINT ] __attribute__((aligned(FD_VOTE_STATE_VERSIONED_ALIGN))); 161 : uchar authorized_voters_mem[ FD_AUTHORIZED_VOTERS_FOOTPRINT ] __attribute__((aligned(FD_AUTHORIZED_VOTERS_ALIGN))); 162 : uchar landed_votes_mem [ FD_LANDED_VOTES_FOOTPRINT ] __attribute__((aligned(FD_LANDED_VOTES_ALIGN))); 163 : uchar vote_lockout_mem [ FD_VOTE_LOCKOUTS_FOOTPRINT ] __attribute__((aligned(FD_VOTE_LOCKOUTS_ALIGN))); 164 : } withdraw; 165 : 166 : struct { 167 : uchar vote_state_mem [ FD_VOTE_STATE_VERSIONED_FOOTPRINT ] __attribute__((aligned(FD_VOTE_STATE_VERSIONED_ALIGN))); 168 : uchar authorized_voters_mem[ FD_AUTHORIZED_VOTERS_FOOTPRINT ] __attribute__((aligned(FD_AUTHORIZED_VOTERS_ALIGN))); 169 : uchar vote_lockout_mem [ FD_VOTE_LOCKOUTS_FOOTPRINT ] __attribute__((aligned(FD_VOTE_LOCKOUTS_ALIGN))); 170 : } init_account; 171 : 172 : struct { 173 : uchar vote_state_mem [ FD_VOTE_STATE_VERSIONED_FOOTPRINT ] __attribute__((aligned(FD_VOTE_STATE_VERSIONED_ALIGN))); 174 : uchar authorized_voters_mem [ FD_AUTHORIZED_VOTERS_FOOTPRINT ] __attribute__((aligned(FD_AUTHORIZED_VOTERS_ALIGN))); 175 : uchar vote_state_landed_votes_mem[ FD_LANDED_VOTES_FOOTPRINT ] __attribute__((aligned(FD_LANDED_VOTES_ALIGN))); 176 : uchar tower_sync_landed_votes_mem[ FD_LANDED_VOTES_FOOTPRINT ] __attribute__((aligned(FD_LANDED_VOTES_ALIGN))); 177 : uchar vote_lockout_mem [ FD_VOTE_LOCKOUTS_FOOTPRINT ] __attribute__((aligned(FD_VOTE_LOCKOUTS_ALIGN))); 178 : } tower_sync; 179 : 180 : struct { 181 : /* Deprecated instructions */ 182 : uchar vote_state_mem [ FD_VOTE_STATE_VERSIONED_FOOTPRINT ] __attribute__((aligned(FD_VOTE_STATE_VERSIONED_ALIGN))); 183 : uchar authorized_voters_mem [ FD_AUTHORIZED_VOTERS_FOOTPRINT ] __attribute__((aligned(FD_AUTHORIZED_VOTERS_ALIGN))); 184 : uchar landed_votes_mem [ FD_LANDED_VOTES_FOOTPRINT ] __attribute__((aligned(FD_LANDED_VOTES_ALIGN))); 185 : uchar vote_lockout_mem [ FD_VOTE_LOCKOUTS_FOOTPRINT ] __attribute__((aligned(FD_VOTE_LOCKOUTS_ALIGN))); 186 : uchar compact_vs_lockout_mem [ FD_VOTE_LOCKOUTS_FOOTPRINT ] __attribute__((aligned(FD_VOTE_LOCKOUTS_ALIGN))); 187 : uchar vs_update_landed_votes_mem[ FD_LANDED_VOTES_FOOTPRINT ] __attribute__((aligned(FD_LANDED_VOTES_ALIGN))); 188 : } process_vote; 189 : 190 : } vote_program; 191 : 192 : union { 193 : struct { 194 : uchar vote_state_mem [ FD_VOTE_STATE_VERSIONED_FOOTPRINT ] __attribute__((aligned(FD_VOTE_STATE_VERSIONED_ALIGN))); 195 : uchar authorized_voters_mem[ FD_VOTE_STATE_VERSIONED_FOOTPRINT ] __attribute__((aligned(128UL))); 196 : uchar landed_votes_mem [ FD_VOTE_STATE_VERSIONED_FOOTPRINT ] __attribute__((aligned(128UL))); 197 : } delegate; 198 : struct { 199 : uchar delinquent_vote_state_mem [ FD_VOTE_STATE_VERSIONED_FOOTPRINT ] __attribute__((aligned(FD_VOTE_STATE_VERSIONED_ALIGN))); 200 : uchar delinquent_authorized_voters_mem[ FD_VOTE_STATE_VERSIONED_FOOTPRINT ] __attribute__((aligned(128UL))); 201 : uchar delinquent_landed_votes_mem [ FD_VOTE_STATE_VERSIONED_FOOTPRINT ] __attribute__((aligned(128UL))); 202 : 203 : uchar reference_vote_state_mem [ FD_VOTE_STATE_VERSIONED_FOOTPRINT ] __attribute__((aligned(FD_VOTE_STATE_VERSIONED_ALIGN))); 204 : uchar reference_authorized_voters_mem[ FD_VOTE_STATE_VERSIONED_FOOTPRINT ] __attribute__((aligned(128UL))); 205 : uchar reference_landed_votes_mem [ FD_VOTE_STATE_VERSIONED_FOOTPRINT ] __attribute__((aligned(128UL))); 206 : } deactivate_delinquent; 207 : } stake_program; 208 : 209 : struct { 210 : 211 : /* Ticks spent spent preparing a txn-level VM (zeroing memory, 212 : copying account data, etc) */ 213 : ulong vm_setup_cum_ticks; 214 : 215 : /* Ticks spent committing txn-level VM results (copying account 216 : data, etc) */ 217 : ulong vm_commit_cum_ticks; 218 : 219 : /* Ticks spent in top-levl VM interpreter (includes CPI setup/commit 220 : ticks) */ 221 : ulong vm_exec_cum_ticks; 222 : 223 : /* Ticks spent preparing/committing a cross-program invocation) */ 224 : ulong cpi_setup_cum_ticks; 225 : ulong cpi_commit_cum_ticks; 226 : 227 : /* Number of user txn account transitions */ 228 : 229 : # define FD_RUNTIME_SAVE_UNCHANGED_NONEXIST 0 /* non-existent account not modified */ 230 : # define FD_RUNTIME_SAVE_CREATE 1 /* account previously non-existent, non-zero balance after txn */ 231 : # define FD_RUNTIME_SAVE_DELETE 2 /* account previously existed, non-existent after txn */ 232 66 : # define FD_RUNTIME_SAVE_MODIFY 3 /* existing account modified */ 233 9 : # define FD_RUNTIME_SAVE_UNCHANGED 4 /* existing account not modified */ 234 : # define FD_RUNTIME_SAVE_MAX 5 /* enum variant count */ 235 : ulong txn_account_save[ FD_RUNTIME_SAVE_MAX ]; 236 : 237 : } metrics; 238 : }; 239 : typedef struct fd_runtime fd_runtime_t; 240 : 241 : struct fd_txn_in { 242 : fd_txn_p_t const * txn; 243 : 244 : struct { 245 : int is_bundle; 246 : fd_txn_out_t * prev_txn_outs[ FD_PACK_MAX_TXN_PER_BUNDLE ]; 247 : ulong prev_txn_cnt; 248 : } bundle; 249 : }; 250 : typedef struct fd_txn_in fd_txn_in_t; 251 : 252 : struct fd_txn_out { 253 : struct { 254 : int is_committable; 255 : int is_fees_only; 256 : int txn_err; 257 : /* These are error fields produced by instruction execution 258 : when txn_err == FD_RUNTIME_TXN_ERR_INSTRUCTION_ERROR (-9). */ 259 : int exec_err; 260 : int exec_err_kind; 261 : int exec_err_idx; 262 : uint custom_err; 263 : } err; 264 : 265 : struct { 266 : long prep_start_timestamp; 267 : long load_start_timestamp; 268 : long exec_start_timestamp; 269 : long commit_start_timestamp; 270 : 271 : fd_compute_budget_details_t compute_budget; /* Compute budget details */ 272 : fd_transaction_cost_t txn_cost; /* Transaction cost */ 273 : ulong loaded_accounts_data_size; /* The actual transaction loaded data size */ 274 : long accounts_resize_delta; /* Transaction level tracking for account resizing */ 275 : 276 : fd_txn_return_data_t return_data; /* Data returned from `return_data` syscalls */ 277 : 278 : fd_hash_t blake_txn_msg_hash; /* Hash of raw transaction message used by the status cache */ 279 : fd_hash_t blockhash; /* Blockhash of the block that the transaction is being executed in */ 280 : 281 : ulong execution_fee; /* Execution fee paid by the fee payer in the transaction */ 282 : ulong priority_fee; /* Priority fee paid by the fee payer in the transaction */ 283 : ulong tips; /* Jito tips paid during execution */ 284 : 285 : ulong signature_count; /* Number of signatures in the transaction */ 286 : int is_simple_vote; /* Whether the transaction is a simple vote */ 287 : /* When a program is deployed or upgraded, we must queue it to be 288 : updated in the program cache (if it exists already) so that 289 : the cache entry's ELF / sBPF information can be updated for 290 : future executions. We keep an array of pubkeys for the 291 : transaction to track which programs need to be reverified. The 292 : actual queueing for reverification is done in the transaction 293 : finalization step. */ 294 : uchar programs_to_reverify_cnt; 295 : fd_pubkey_t programs_to_reverify[ MAX_TX_ACCOUNT_LOCKS ]; 296 : } details; 297 : 298 : /* During sanitization, v0 transactions are allowed to have up to 256 accounts: 299 : https://github.com/anza-xyz/agave/blob/838c1952595809a31520ff1603a13f2c9123aa51/sdk/program/src/message/versions/v0/mod.rs#L139 300 : Nonetheless, when Agave prepares a sanitized batch for execution and tries to lock accounts, a lower limit is enforced: 301 : https://github.com/anza-xyz/agave/blob/838c1952595809a31520ff1603a13f2c9123aa51/accounts-db/src/account_locks.rs#L118 302 : That is the limit we are going to use here. */ 303 : struct { 304 : /* is_setup is set to 1 if account data buffer resources have been 305 : acquired for the transaction and 0 if they have not. If the flag 306 : has been set, memory resources must be released. */ 307 : int is_setup; 308 : ulong cnt; 309 : fd_pubkey_t keys [ MAX_TX_ACCOUNT_LOCKS ]; 310 : fd_accdb_rw_t account [ MAX_TX_ACCOUNT_LOCKS ]; /* FIXME use accdb_ref_t here for safety - some accounts are readonly */ 311 : uchar is_writable[ MAX_TX_ACCOUNT_LOCKS ]; 312 : 313 : /* The fee payer and nonce accounts are treated differently than 314 : other accounts: if an on-transaction fails they are still 315 : committed to the accounts database. However, they are saved at 316 : the point right after a fee is debited or the nonce is advanced 317 : respectively. The rollback accounts store this state because a 318 : failed transaction could have potentially modified the state of 319 : these two accounts. 320 : 321 : The memory for the nonce and fee payer is always provisioned when 322 : the transaction is prepared, but isn't necessarily used. */ 323 : uchar * rollback_fee_payer_mem; 324 : uchar * rollback_nonce_mem; 325 : 326 : ulong nonce_idx_in_txn; /* !=ULONG_MAX if exists */ 327 : fd_account_meta_t * rollback_nonce; 328 : fd_account_meta_t * rollback_fee_payer; 329 : } accounts; 330 : }; 331 : typedef struct fd_txn_out fd_txn_out_t; 332 : 333 : FD_PROTOTYPES_BEGIN 334 : 335 : /* fd_runtime_block_execute_prepare kicks off the execution of a block. 336 : After this function is called, transactions can be executed and 337 : committed against the block. This function handles epoch boundary 338 : and rewards updates if needed and updates sysvars. It assumes that 339 : the bank and accounts database have been setup to execute against 340 : the bank: the bank has already been cloned from the parent bank and 341 : that the database has a transaction that is linked to the parent 342 : block's xid. */ 343 : 344 : void 345 : fd_runtime_block_execute_prepare( fd_banks_t * banks, 346 : fd_bank_t * bank, 347 : fd_accdb_user_t * accdb, 348 : fd_runtime_stack_t * runtime_stack, 349 : fd_capture_ctx_t * capture_ctx, 350 : int * is_epoch_boundary ); 351 : 352 : /* fd_runtime_block_execute_finalize finishes the execution of the block 353 : by paying a fee out to the block leader, updating any sysvars, and 354 : updating the bank hash. The required updates are made to the bank 355 : and the accounts database. */ 356 : 357 : void 358 : fd_runtime_block_execute_finalize( fd_bank_t * bank, 359 : fd_accdb_user_t * accdb, 360 : fd_capture_ctx_t * capture_ctx ); 361 : 362 : /* fd_runtime_prepare_and_execute_txn is responsible for executing a 363 : fd_txn_in_t against a fd_runtime_t and a fd_bank_t. The results of 364 : the transaction execution are set in the fd_txn_out_t. The caller 365 : is responisble for correctly setting up the fd_txn_in_t and the 366 : fd_runtime_t handles. 367 : 368 : TODO: fd_runtime_t and fd_bank_t should be const here. */ 369 : 370 : void 371 : fd_runtime_prepare_and_execute_txn( fd_runtime_t * runtime, 372 : fd_bank_t * bank, 373 : fd_txn_in_t const * txn_in, 374 : fd_txn_out_t * txn_out ); 375 : 376 : /* fd_runtime_commit_txn commits the results of a transaction execution 377 : as represented by the fd_txn_out_t to the bank and the accounts 378 : database. */ 379 : 380 : void 381 : fd_runtime_commit_txn( fd_runtime_t * runtime, 382 : fd_bank_t * bank, 383 : fd_txn_out_t * txn_out ); 384 : 385 : /* fd_runtime_cancel_txn cancels the result of a transaction execution 386 : and frees any resources that may have been acquired. A transaction 387 : should only be canceled when the transaction is not committable. 388 : 1. An invalid transaction that causes a block to be rejected/ 389 : considered invalid/'bad'. 390 : 2. All transactions in a bundle with a failed transaction should be 391 : canceled as they will not be included in the block. */ 392 : 393 : void 394 : fd_runtime_cancel_txn( fd_runtime_t * runtime, 395 : fd_txn_out_t * txn_out ); 396 : 397 : FD_PROTOTYPES_END 398 : 399 : #endif /* HEADER_fd_src_flamenco_runtime_fd_runtime_h */