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 "stdarg.h"
5 :
6 : #include "../fd_flamenco_base.h"
7 : #include "fd_runtime_err.h"
8 : #include "fd_runtime_init.h"
9 : #include "fd_rocksdb.h"
10 : #include "fd_acc_mgr.h"
11 : #include "fd_hashes.h"
12 : #include "../features/fd_features.h"
13 : #include "fd_rent_lists.h"
14 : #include "../../ballet/poh/fd_poh.h"
15 : #include "../leaders/fd_leaders.h"
16 : #include "context/fd_exec_slot_ctx.h"
17 : #include "context/fd_capture_ctx.h"
18 : #include "context/fd_exec_txn_ctx.h"
19 : #include "info/fd_runtime_block_info.h"
20 : #include "info/fd_instr_info.h"
21 : #include "../gossip/fd_gossip.h"
22 : #include "../repair/fd_repair.h"
23 : #include "../../disco/pack/fd_microblock.h"
24 : #include "info/fd_microblock_info.h"
25 : #include "../../ballet/bmtree/fd_wbmtree.h"
26 : #include "../../ballet/sbpf/fd_sbpf_loader.h"
27 : #include "fd_runtime_public.h"
28 :
29 : /* Various constant values used by the runtime. */
30 :
31 0 : #define MICRO_LAMPORTS_PER_LAMPORT (1000000UL)
32 :
33 : #define DEFAULT_HASHES_PER_TICK (12500)
34 0 : #define UPDATED_HASHES_PER_TICK2 (17500)
35 0 : #define UPDATED_HASHES_PER_TICK3 (27500)
36 0 : #define UPDATED_HASHES_PER_TICK4 (47500)
37 0 : #define UPDATED_HASHES_PER_TICK5 (57500)
38 0 : #define UPDATED_HASHES_PER_TICK6 (62500)
39 :
40 : #define FD_RUNTIME_TRACE_NONE (0)
41 : #define FD_RUNTIME_TRACE_SAVE (1)
42 : #define FD_RUNTIME_TRACE_REPLAY (2)
43 :
44 0 : #define FD_RUNTIME_OFFLINE_NUM_ROOT_BLOCKS (6UL) /* 6 root blocks for offline replay */
45 :
46 0 : #define FD_BLOCKHASH_QUEUE_MAX_ENTRIES (300UL)
47 0 : #define FD_RECENT_BLOCKHASHES_MAX_ENTRIES (150UL)
48 :
49 0 : #define FD_RENT_EXEMPT_RENT_EPOCH (ULONG_MAX)
50 :
51 0 : #define SECONDS_PER_YEAR ((double)(365.242199 * 24.0 * 60.0 * 60.0))
52 :
53 : /* TODO: increase this to default once we have enough memory to support a 95G status cache. */
54 : #define MAX_CACHE_TXNS_PER_SLOT (FD_TXNCACHE_DEFAULT_MAX_TRANSACTIONS_PER_SLOT / 8)
55 :
56 : void
57 : block_finalize_tpool_wrapper( void * para_arg_1,
58 : void * para_arg_2,
59 : void * arg_1,
60 : void * arg_2,
61 : void * arg_3,
62 : void * arg_4 );
63 :
64 : struct fd_execute_txn_task_info {
65 : fd_spad_t * * spads;
66 : fd_spad_t * spad;
67 : fd_exec_txn_ctx_t * txn_ctx;
68 : fd_txn_p_t * txn;
69 : int exec_res;
70 : };
71 : typedef struct fd_execute_txn_task_info fd_execute_txn_task_info_t;
72 :
73 : struct fd_raw_block_txn_iter {
74 : fd_block_entry_batch_t const * curr_batch;
75 : uchar const * orig_data;
76 : ulong remaining_batches;
77 : ulong remaining_microblocks;
78 : ulong remaining_txns;
79 : ulong curr_offset;
80 :
81 : ulong curr_txn_sz;
82 : };
83 :
84 : typedef struct fd_raw_block_txn_iter fd_raw_block_txn_iter_t;
85 :
86 : struct fd_poh_verifier {
87 : union {
88 : fd_microblock_hdr_t const * hdr;
89 : uchar * raw;
90 : } microblock;
91 : fd_hash_t const * in_poh_hash;
92 : ulong microblk_max_sz;
93 : fd_spad_t * spad;
94 : int success;
95 : };
96 : typedef struct fd_poh_verifier fd_poh_verifier_t;
97 :
98 : /* The below logic is used to size out the memory footprint generated by the
99 : runtime during transaction execution. */
100 :
101 : /* The prevailing layout we have in the runtime is the meta followed by
102 : the account's data. This struct encodes that layout and asserts that
103 : the alignment requirements of the constituents are satisfied. */
104 : // TODO: Use this struct at allocation sites so it's clear we use this layout
105 : struct __attribute__((packed)) fd_account_rec {
106 : fd_account_meta_t meta;
107 : uchar data[];
108 : };
109 : typedef struct fd_account_rec fd_account_rec_t;
110 0 : #define FD_ACCOUNT_REC_ALIGN (8UL)
111 : #define FD_ACCOUNT_REC_DATA_ALIGN (8UL)
112 : FD_STATIC_ASSERT( FD_ACCOUNT_REC_ALIGN>=FD_ACCOUNT_META_ALIGN, account_rec_meta_align );
113 : FD_STATIC_ASSERT( FD_ACCOUNT_REC_ALIGN>=FD_ACCOUNT_REC_DATA_ALIGN, account_rec_data_align );
114 : FD_STATIC_ASSERT( (offsetof(fd_account_rec_t, meta)%FD_ACCOUNT_META_ALIGN)==0, account_rec_meta_offset );
115 : FD_STATIC_ASSERT( (offsetof(fd_account_rec_t, data)%FD_ACCOUNT_REC_DATA_ALIGN)==0, account_rec_data_offset );
116 :
117 0 : #define MAX_PERMITTED_DATA_INCREASE (10240UL) // 10KB
118 0 : #define FD_BPF_ALIGN_OF_U128 (8UL )
119 : FD_STATIC_ASSERT( FD_BPF_ALIGN_OF_U128==FD_ACCOUNT_REC_DATA_ALIGN, input_data_align );
120 0 : #define FD_RUNTIME_INPUT_REGION_ALLOC_ALIGN_UP (16UL)
121 :
122 : /******** These macros bound out memory footprint ********/
123 :
124 : /* The tight upper bound on borrowed account footprint over the
125 : execution of a single transaction. */
126 0 : #define FD_RUNTIME_BORROWED_ACCOUNT_FOOTPRINT (MAX_TX_ACCOUNT_LOCKS * FD_ULONG_ALIGN_UP( FD_ACC_TOT_SZ_MAX, FD_ACCOUNT_REC_ALIGN ))
127 :
128 : /* The tight-ish upper bound on input region footprint over the
129 : execution of a single transaction. See input serialization code for
130 : reference: fd_bpf_loader_serialization.c
131 :
132 : This bound is based off of the transaction MTU. We consider the
133 : question of what kind of transaction one would construct to
134 : maximally bloat the input region.
135 : The worst case scenario is when every nested instruction references
136 : all unique accounts in the transaction. A transaction can lock a max
137 : of MAX_TX_ACCOUNT_LOCKS accounts. Then all remaining input account
138 : references are going to be duplicates, which cost 1 byte to specify
139 : offset in payload, and which cost 8 bytes during serialization. Then
140 : there would be 0 bytes of instruction data, because they exist byte
141 : for byte in the raw payload, which is not a worthwhile bloat factor.
142 : */
143 : #define FD_RUNTIME_INPUT_REGION_UNIQUE_ACCOUNT_FOOTPRINT(direct_mapping) \
144 : (1UL /* dup byte */ + \
145 : sizeof(uchar) /* is_signer */ + \
146 : sizeof(uchar) /* is_writable */ + \
147 : sizeof(uchar) /* executable */ + \
148 : sizeof(uint) /* original_data_len */ + \
149 : sizeof(fd_pubkey_t) /* key */ + \
150 : sizeof(fd_pubkey_t) /* owner */ + \
151 : sizeof(ulong) /* lamports */ + \
152 : sizeof(ulong) /* data len */ + \
153 : (direct_mapping ? FD_BPF_ALIGN_OF_U128 : FD_ULONG_ALIGN_UP( FD_ACC_SZ_MAX, FD_BPF_ALIGN_OF_U128 )) + \
154 : MAX_PERMITTED_DATA_INCREASE + \
155 : sizeof(ulong)) /* rent_epoch */
156 :
157 : #define FD_RUNTIME_INPUT_REGION_INSN_FOOTPRINT(account_lock_limit, direct_mapping) \
158 0 : (FD_ULONG_ALIGN_UP( (sizeof(ulong) /* acct_cnt */ + \
159 0 : account_lock_limit*FD_RUNTIME_INPUT_REGION_UNIQUE_ACCOUNT_FOOTPRINT(direct_mapping) + \
160 0 : sizeof(ulong) /* instr data len */ + \
161 0 : /* No instr data */ \
162 0 : sizeof(fd_pubkey_t)), /* program id */ \
163 0 : FD_RUNTIME_INPUT_REGION_ALLOC_ALIGN_UP ) + FD_BPF_ALIGN_OF_U128)
164 :
165 : #define FD_RUNTIME_INPUT_REGION_TXN_FOOTPRINT(account_lock_limit, direct_mapping) \
166 0 : ((FD_MAX_INSTRUCTION_STACK_DEPTH*FD_RUNTIME_INPUT_REGION_INSN_FOOTPRINT(account_lock_limit, direct_mapping)) + \
167 0 : ((FD_TXN_MTU-FD_TXN_MIN_SERIALIZED_SZ-account_lock_limit)*8UL)) /* We can have roughly this much duplicate offsets */
168 :
169 : /* Bincode valloc footprint over the execution of a single transaction.
170 : As well as other footprint specific to each native program type.
171 :
172 : N.B. We know that bincode valloc footprint is bounded, because
173 : whenever we alloc something, we advance our pointer into the binary
174 : buffer, so eventually we are gonna reach the end of the buffer.
175 : This buffer is usually backed by and ultimately bounded in size by
176 : either accounts data or the transaction MTU.
177 :
178 : That being said, it's not obvious what the tight upper bound would
179 : be for allocations across all possible execution paths of all native
180 : programs, including possible CPIs from native programs. The
181 : footprint estimate here is based on a manual review of our native
182 : program implementation. Note that even if the possible paths remain
183 : steady at the Solana protocol level, the footprint is subject to
184 : change when we change our implementation.
185 :
186 : ### Native programs
187 : ALUT (migrated to BPF)
188 : Loader
189 : - rodata for bpf program relocation and validation
190 : Compute budget (0 allocations)
191 : Config (migrated to BPF)
192 : Precompile (0 allocations)
193 : Stake
194 : - The instruction with the largest footprint is deactivate_delinquent
195 : - During instruction decode, no allocations
196 : - During execution, this is (vote account get_state() + vote convert_to_current()) times 2, once for delinquent_vote_account, and once for reference_vote_account
197 : System
198 : - system_program_instruction_decode seed
199 : Vote
200 : - The instruction with the largest footprint is compact vote state update
201 : - During instruction decode, this is 9*lockouts_len bytes, MTU bounded
202 : - During execution, this is vote account get_state() + vote convert_to_current() + 12*lockouts_len bytes + lockouts_len ulong + deq_fd_landed_vote_t_alloc(lockouts_len)
203 : Zk Elgamal (0 allocations)
204 :
205 : The largest footprint is hence deactivate_delinquent, in which the
206 : two get_state() calls dominate the footprint. In particular, the
207 : authorized_voters treaps bloat 40 bytes (epoch+pubkey) in a vote
208 : account to 72 bytes (sizeof(fd_vote_authorized_voter_t)) in memory.
209 : */
210 0 : #define FD_RUNTIME_BINCODE_AND_NATIVE_FOOTPRINT (2UL*FD_ACC_SZ_MAX*72UL/40UL)
211 :
212 : /* Misc other footprint. */
213 0 : #define FD_RUNTIME_SYSCALL_TABLE_FOOTPRINT (FD_MAX_INSTRUCTION_STACK_DEPTH*FD_ULONG_ALIGN_UP(FD_SBPF_SYSCALLS_FOOTPRINT, FD_SBPF_SYSCALLS_ALIGN))
214 :
215 : #ifdef FD_DEBUG_SBPF_TRACES
216 : #define FD_RUNTIME_VM_TRACE_EVENT_MAX (1UL<<30)
217 : #define FD_RUNTIME_VM_TRACE_EVENT_DATA_MAX (2048UL)
218 : #define FD_RUNTIME_VM_TRACE_FOOTPRINT (FD_MAX_INSTRUCTION_STACK_DEPTH*fd_ulong_align_up( fd_vm_trace_footprint( FD_RUNTIME_VM_TRACE_EVENT_MAX, FD_RUNTIME_VM_TRACE_EVENT_DATA_MAX ), fd_vm_trace_align() ))
219 : #else
220 0 : #define FD_RUNTIME_VM_TRACE_FOOTPRINT (0UL)
221 : #endif
222 :
223 0 : #define FD_RUNTIME_MISC_FOOTPRINT (FD_RUNTIME_SYSCALL_TABLE_FOOTPRINT+FD_RUNTIME_VM_TRACE_FOOTPRINT)
224 :
225 : /* Now finally, we bound out the footprint of transaction execution. */
226 : #define FD_RUNTIME_TRANSACTION_EXECUTION_FOOTPRINT(account_lock_limit, direct_mapping) \
227 0 : (FD_RUNTIME_BORROWED_ACCOUNT_FOOTPRINT + \
228 0 : FD_RUNTIME_INPUT_REGION_TXN_FOOTPRINT(account_lock_limit, direct_mapping) + \
229 0 : FD_RUNTIME_BINCODE_AND_NATIVE_FOOTPRINT + \
230 0 : FD_RUNTIME_MISC_FOOTPRINT)
231 :
232 : /* Convenience macros for common use cases.
233 :
234 : TODO: If account lock limits are increased to 128, this macro will need to be updated. */
235 0 : #define FD_RUNTIME_TRANSACTION_EXECUTION_FOOTPRINT_FUZZ FD_RUNTIME_TRANSACTION_EXECUTION_FOOTPRINT(64UL, 0)
236 0 : #define FD_RUNTIME_TRANSACTION_EXECUTION_FOOTPRINT_DEFAULT FD_RUNTIME_TRANSACTION_EXECUTION_FOOTPRINT(64UL, 0)
237 :
238 : /* Footprint here is dominated by vote account decode. See above for
239 : why 72/40. */
240 0 : #define FD_RUNTIME_TRANSACTION_FINALIZATION_FOOTPRINT (FD_ACC_SZ_MAX*72UL/40UL)
241 :
242 : /* The below macros aren't used anywhere, but since the spads are used for PoH tick verification,
243 : we ensure that the default spad size is large enough for the wbmtree and leaves */
244 :
245 : #define FD_RUNTIME_MERKLE_LEAF_CNT_MAX (FD_TXN_MAX_PER_SLOT * FD_TXN_ACTUAL_SIG_MAX)
246 :
247 : #define FD_RUNTIME_MERKLE_VERIFICATION_FOOTPRINT FD_RUNTIME_MERKLE_LEAF_CNT_MAX * sizeof(fd_wbmtree32_leaf_t) /* leaves */ \
248 : + sizeof(fd_wbmtree32_t) + sizeof(fd_wbmtree32_node_t)*(FD_RUNTIME_MERKLE_LEAF_CNT_MAX + (FD_RUNTIME_MERKLE_LEAF_CNT_MAX/2)) /* tree footprint */ \
249 : + FD_RUNTIME_MERKLE_LEAF_CNT_MAX * (sizeof(fd_ed25519_sig_t) + 1) /* sig mbuf */ \
250 : + 1UL + FD_WBMTREE32_ALIGN + alignof(fd_wbmtree32_leaf_t) /* aligns */
251 :
252 : FD_STATIC_ASSERT( FD_RUNTIME_MERKLE_VERIFICATION_FOOTPRINT <= FD_RUNTIME_TRANSACTION_EXECUTION_FOOTPRINT_DEFAULT, merkle verify footprint exceeds txn execution footprint );
253 :
254 : /* Helpers for runtime public frame management. */
255 :
256 : /* Helpers for runtime spad frame management. */
257 : struct fd_runtime_spad_verify_handle_private {
258 : fd_spad_t * spad;
259 : fd_exec_txn_ctx_t * txn_ctx;
260 : };
261 : typedef struct fd_runtime_spad_verify_handle_private fd_runtime_spad_verify_handle_private_t;
262 :
263 : static inline void
264 0 : fd_runtime_spad_private_frame_end( fd_runtime_spad_verify_handle_private_t * _spad_handle ) {
265 : /* fd_spad_verify() returns 0 if everything looks good, and non-zero
266 : otherwise.
267 :
268 : Since the fast spad alloc API doesn't check for or indicate an OOM
269 : situation and is going to happily permit an OOB alloc, we need
270 : some way of detecting that. Moreover, we would also like to detect
271 : unbalanced frame push/pop or usage of more frames than allowed.
272 : While surrounding the spad with guard regions will help detect the
273 : former, it won't necessarily catch the latter.
274 :
275 : On compliant transactions, fd_spad_verify() isn't all that
276 : expensive. Nonetheless, We invoke fd_spad_verify() only at the
277 : peak of memory usage, and not gratuitously everywhere. One peak
278 : would be right before we do the most deeply nested spad frame pop.
279 : However, we do pops through compiler-inserted cleanup functions
280 : that take only a single pointer, so we define this helper function
281 : to access the needed context info. The end result is that we do
282 : super fast spad calls everywhere in the runtime, and every now and
283 : then we invoke verify to check things. */
284 : /* -1UL because spad pop is called after instr stack pop. */
285 0 : if( FD_UNLIKELY( _spad_handle->txn_ctx->instr_stack_sz>=FD_MAX_INSTRUCTION_STACK_DEPTH-1UL && fd_spad_verify( _spad_handle->txn_ctx->spad ) ) ) {
286 0 : uchar const * txn_signature = (uchar const *)fd_txn_get_signatures( _spad_handle->txn_ctx->txn_descriptor, _spad_handle->txn_ctx->_txn_raw->raw );
287 0 : FD_BASE58_ENCODE_64_BYTES( txn_signature, sig );
288 0 : FD_LOG_ERR(( "spad corrupted or overflown on transaction %s", sig ));
289 0 : }
290 0 : fd_spad_pop( _spad_handle->spad );
291 0 : }
292 :
293 0 : #define FD_RUNTIME_TXN_SPAD_FRAME_BEGIN(_spad, _txn_ctx) do { \
294 0 : fd_runtime_spad_verify_handle_private_t _spad_handle __attribute__((cleanup(fd_runtime_spad_private_frame_end))) = \
295 0 : (fd_runtime_spad_verify_handle_private_t) { .spad = _spad, .txn_ctx = _txn_ctx }; \
296 0 : fd_spad_push( _spad_handle.spad ); \
297 0 : do
298 :
299 0 : #define FD_RUNTIME_TXN_SPAD_FRAME_END while(0); } while(0)
300 :
301 : FD_PROTOTYPES_BEGIN
302 :
303 : /* Runtime Helpers ************************************************************/
304 :
305 : /*
306 : Returns 0 on success, and non zero otherwise. On failure, the
307 : out values will not be modified.
308 : */
309 : int
310 : fd_runtime_compute_max_tick_height( ulong ticks_per_slot,
311 : ulong slot,
312 : ulong * out_max_tick_height /* out */ );
313 :
314 : void
315 : fd_runtime_update_leaders( fd_bank_t * bank,
316 : ulong slot,
317 : fd_spad_t * runtime_spad );
318 :
319 : /* TODO: Invoked by fd_executor: layering violation. Rent logic is deprecated
320 : and will be torn out entirely very soon. */
321 : ulong
322 : fd_runtime_collect_rent_from_account( fd_epoch_schedule_t const * schedule,
323 : fd_rent_t const * rent,
324 : double slots_per_year,
325 : fd_txn_account_t * acc,
326 : ulong epoch );
327 :
328 : void
329 : fd_runtime_update_slots_per_epoch( fd_bank_t * bank,
330 : ulong slots_per_epoch );
331 :
332 : /* Block Level Execution Prep/Finalize ****************************************/
333 :
334 0 : #define FD_BLOCK_OK (0UL)
335 : #define FD_BLOCK_ERR_INCOMPLETE (1UL)
336 : #define FD_BLOCK_ERR_INVALID_ENTRY_HASH (2UL)
337 : #define FD_BLOCK_ERR_INVALID_LAST_TICK (3UL)
338 0 : #define FD_BLOCK_ERR_TOO_FEW_TICKS (4UL)
339 0 : #define FD_BLOCK_ERR_TOO_MANY_TICKS (5UL)
340 0 : #define FD_BLOCK_ERR_INVALID_TICK_HASH_COUNT (6UL)
341 0 : #define FD_BLOCK_ERR_TRAILING_ENTRY (7UL)
342 : #define FD_BLOCK_ERR_DUPLICATE_BLOCK (8UL)
343 :
344 : /*
345 : https://github.com/anza-xyz/agave/blob/v2.1.0/ledger/src/blockstore_processor.rs#L1096
346 : This function assumes a full block.
347 : This needs to be called after epoch processing to get the up to date
348 : hashes_per_tick.
349 :
350 : Provide scratch memory >= the max size of a batch to use. This is because we can only
351 : assemble shreds by batch, so we iterate and assemble shreds by batch in this function
352 : without needing the caller to do so.
353 : */
354 : ulong
355 : fd_runtime_block_verify_ticks( fd_blockstore_t * blockstore,
356 : ulong slot,
357 : uchar * block_data_mem,
358 : ulong block_data_sz,
359 : ulong tick_height,
360 : ulong max_tick_height,
361 : ulong hashes_per_tick );
362 :
363 : /* The following microblock-level functions are exposed and non-static due to also being used for fd_replay.
364 : The block-level equivalent functions, on the other hand, are mostly static as they are only used
365 : for offline replay */
366 :
367 : /* extra fine-grained streaming tick verification */
368 : int
369 : fd_runtime_microblock_verify_ticks( fd_blockstore_t * blockstore,
370 : ulong slot,
371 : fd_microblock_hdr_t const * hdr,
372 : bool slot_complete,
373 : ulong tick_height,
374 : ulong max_tick_height,
375 : ulong hashes_per_tick );
376 :
377 : /*
378 : fd_runtime_microblock_verify_read_write_conflicts verifies that a
379 : list of txns (e.g., those in a microblock) do not have read-write
380 : or write-write conflits. FD_TXN_CONFLICT_MAP_MAX_NACCT defines a
381 : conservative estimation of the number of accounts touched by txns
382 : in one slot. Given the conservative estimation, the footprint of
383 : the account map (fd_conflict_detect_map) is about 2112MB. One can
384 : certainly use a better estimation leading to a smaller footprint.
385 :
386 : this function corresponds to try_lock_accounts in Agave which
387 : detects transaction conflicts:
388 : https://github.com/anza-xyz/agave/blob/v2.2.3/runtime/src/bank.rs
389 : #L3145
390 :
391 : Specifically, from the replay stage of Agave, the control flow is
392 : (1) replay_blockstore_into_bank: https://github.com/anza-xyz/agave/
393 : blob/v2.2.3/core/src/replay_stage.rs#L2232
394 : (2) confirm_slot: https://github.com/anza-xyz/agave/blob/v2.2.3/
395 : ledger/src/blockstore_processor.rs#L1561
396 : (3) confirm_slot_entries: https://github.com/anza-xyz/agave/blob/
397 : v2.2.3/ledger/src/blockstore_processor.rs#L1609
398 : (4) process_entries: https://github.com/anza-xyz/agave/blob/v2.2.3/
399 : ledger/src/blockstore_processor.rs#L704
400 : (5) queue_batches_with_lock_retry: https://github.com/anza-xyz/agave/
401 : blob/v2.2.3/ledger/src/blockstore_processor.rs#L789
402 : (6) bank.try_lock_accounts is called in queue_batches_with_lock_retry
403 : (7) this try_lock_accounts eventually calls another try_lock_accounts,
404 : (see https://github.com/anza-xyz/agave/blob/v2.2.3/accounts-db/src
405 : /account_locks.rs#L24), which acquires the locks and returns
406 : TransactionError::AccountInUse if r-w or w-w conflict is detected
407 : (8) when calling try_lock_accounts, function accounts_with_is_writable
408 : is used to decide whether an account is writable (see https://github.
409 : com/anza-xyz/agave/blob/v2.2.3/accounts-db/src/accounts.rs#L605) which
410 : internally calls the is_writable function depending on whether the txn
411 : is legacy or V0:
412 :
413 : is_writable for legacy: https://github.com/anza-xyz/solana-sdk/blob/
414 : message%40v2.2.1/message/src/sanitized.rs#L75
415 :
416 : is_writable for V0: https://github.com/anza-xyz/solana-sdk/blob/
417 : message%40v2.2.1/message/src/versions/v0/loaded.rs#L152
418 :
419 : In both cases, Agave does the following check in addition to whether
420 : an account has been specified as writable in the transaction message
421 : (https://github.com/anza-xyz/solana-sdk/blob/message%40v2.2.1/message
422 : /src/versions/v0/loaded.rs#L146). This additional check is handled by
423 : function fd_txn_account_is_writable_idx_flat in our code.
424 :
425 : txns is an array containing txn_cnt transactions in fd_txn_p_t type;
426 : acct_map is used to detect conflicts and acct_arr is used to clear the
427 : map before the function returns; funk and funk_txn are used to read
428 : Solana accounts for address lookup tables; slot and slot_hashes are
429 : needed for checking certain bounds in the address lookup table system
430 : program; features is needed in fd_txn_account_is_writable_idx_flat to
431 : decide whether a writable account is demoted to read-only.
432 :
433 : If an error occurs in runtime, the function returns the runtime error.
434 : If there's no conflict, the return value is FD_RUNTIME_EXECUTE_SUCCESS
435 : and out_conflict_detected will be 0. If there's a conflict, the return
436 : value is FD_RUNTIME_TXN_ERR_ACCOUNT_IN_USE and out_conflict_detected
437 : will be 1 (read-write) or 2 (write-write), and out_conflict_addr_opt,
438 : if not NULL, will hold the account address causing the conflict.
439 : */
440 : struct fd_conflict_detect_ele {
441 : fd_acct_addr_t key;
442 : uchar writable;
443 : };
444 : typedef struct fd_conflict_detect_ele fd_conflict_detect_ele_t;
445 0 : #define FD_TXN_CONFLICT_MAP_SEED (0UL)
446 : #define FD_TXN_CONFLICT_MAP_MAX_NACCT (FD_SHRED_DATA_PAYLOAD_MAX_PER_SLOT / FD_TXN_MIN_SERIALIZED_SZ * FD_TXN_ACCT_ADDR_MAX)
447 :
448 : static const fd_acct_addr_t fd_acct_addr_null = {.b={0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF}};
449 : #define MAP_NAME fd_conflict_detect_map
450 0 : #define MAP_KEY_T fd_acct_addr_t
451 0 : #define MAP_T fd_conflict_detect_ele_t
452 0 : #define MAP_HASH_T ulong
453 0 : #define MAP_KEY_NULL (fd_acct_addr_null)
454 0 : #define MAP_KEY_EQUAL(k0,k1) (0==memcmp((k0).b,(k1).b,32))
455 0 : #define MAP_KEY_HASH(key) fd_hash( FD_TXN_CONFLICT_MAP_SEED, key.b, 32 )
456 0 : #define MAP_KEY_INVAL(k) (0==memcmp(&fd_acct_addr_null, (k).b, 32))
457 : #define MAP_KEY_EQUAL_IS_SLOW 0
458 : #define MAP_MEMOIZE 0
459 :
460 : #include "../../util/tmpl/fd_map_dynamic.c"
461 :
462 0 : #define FD_RUNTIME_NO_CONFLICT_DETECTED 0
463 0 : #define FD_RUNTIME_READ_WRITE_CONFLICT_DETECTED 1
464 0 : #define FD_RUNTIME_WRITE_WRITE_CONFLICT_DETECTED 2
465 :
466 : int
467 : fd_runtime_microblock_verify_read_write_conflicts( fd_txn_p_t * txns,
468 : ulong txn_cnt,
469 : fd_conflict_detect_ele_t * acct_map,
470 : fd_acct_addr_t * acct_arr,
471 : fd_funk_t * funk,
472 : fd_funk_txn_t * funk_txn,
473 : ulong slot,
474 : fd_slot_hash_t * slot_hashes,
475 : fd_features_t * features,
476 : int * out_conflict_detected,
477 : fd_acct_addr_t * out_conflict_addr_opt );
478 :
479 : /* Load the accounts in the address lookup tables of txn into out_accts_alt */
480 : int
481 : fd_runtime_load_txn_address_lookup_tables( fd_txn_t const * txn,
482 : uchar const * payload,
483 : fd_funk_t * funk,
484 : fd_funk_txn_t * funk_txn,
485 : ulong slot,
486 : fd_slot_hash_t * hashes,
487 : fd_acct_addr_t * out_accts_alt );
488 :
489 : /* fd_runtime_poh_verify is responsible for verifying poh hashes while
490 : streaming in microblocks. */
491 :
492 : void
493 : fd_runtime_poh_verify( fd_poh_verifier_t * poh_info );
494 :
495 : int
496 : fd_runtime_block_execute_prepare( fd_exec_slot_ctx_t * slot_ctx,
497 : fd_blockstore_t * blockstore,
498 : fd_spad_t * runtime_spad );
499 :
500 : void
501 : fd_runtime_block_execute_finalize_start( fd_exec_slot_ctx_t * slot_ctx,
502 : fd_spad_t * runtime_spad,
503 : fd_accounts_hash_task_data_t * * task_data,
504 : ulong lt_hash_cnt );
505 :
506 : int
507 : fd_runtime_block_execute_finalize_finish( fd_exec_slot_ctx_t * slot_ctx,
508 : fd_capture_ctx_t * capture_ctx,
509 : fd_runtime_block_info_t const * block_info,
510 : fd_spad_t * runtime_spad,
511 : fd_accounts_hash_task_data_t * task_data,
512 : ulong lt_hash_cnt );
513 :
514 : int
515 : fd_runtime_block_execute_finalize_para( fd_exec_slot_ctx_t * slot_ctx,
516 : fd_capture_ctx_t * capture_ctx,
517 : fd_runtime_block_info_t const * block_info,
518 : ulong worker_cnt,
519 : fd_spad_t * runtime_spad,
520 : fd_exec_para_cb_ctx_t * exec_para_ctx );
521 :
522 : /* Transaction Level Execution Management *************************************/
523 :
524 : /* fd_runtime_prepare_txns_start and fd_runtime_pre_execute_check are only
525 : publicly exposed for the fuzzing harnesses. These functions are responsible
526 : for various transaction sanitization checks. */
527 :
528 : int
529 : fd_runtime_prepare_txns_start( fd_exec_slot_ctx_t * slot_ctx,
530 : fd_execute_txn_task_info_t * task_info,
531 : fd_txn_p_t * txns,
532 : ulong txn_cnt,
533 : fd_spad_t * runtime_spad );
534 :
535 : void
536 : fd_runtime_pre_execute_check( fd_execute_txn_task_info_t * task_info,
537 : uchar dump_txn );
538 :
539 : /* fd_runtime_process_txns is responsible for end-to-end preparing, executing,
540 : and finalizing a list of transactions. It will execute all of the
541 : transactions on a single core. */
542 : int
543 : fd_runtime_process_txns( fd_exec_slot_ctx_t * slot_ctx,
544 : fd_capture_ctx_t * capture_ctx,
545 : fd_txn_p_t * txns,
546 : ulong txn_cnt,
547 : fd_spad_t * exec_spad,
548 : fd_spad_t * runtime_spad );
549 :
550 : /* fd_runtime_execute_txns_in_microblock_stream is responsible for end-to-end
551 : preparing, executing and finalizng a list of transactions. It assumes that
552 : all transactions are conflict-free. */
553 :
554 : int
555 : fd_runtime_process_txns_in_microblock_stream( fd_exec_slot_ctx_t * slot_ctx,
556 : fd_capture_ctx_t * capture_ctx,
557 : fd_txn_p_t * all_txns,
558 : ulong total_txn_cnt,
559 : fd_tpool_t * tpool,
560 : fd_spad_t * * exec_spads,
561 : ulong exec_spad_cnt,
562 : fd_spad_t * runtime_spad,
563 : fd_cost_tracker_t * cost_tracker_opt );
564 :
565 : void
566 : fd_runtime_finalize_txn( fd_funk_t * funk,
567 : fd_funk_txn_t * funk_txn,
568 : fd_execute_txn_task_info_t * task_info,
569 : fd_spad_t * finalize_spad,
570 : fd_bank_t * bank );
571 :
572 : /* Epoch Boundary *************************************************************/
573 :
574 : uint
575 : fd_runtime_is_epoch_boundary( fd_exec_slot_ctx_t * slot_ctx,
576 : ulong curr_slot,
577 : ulong prev_slot );
578 :
579 : /*
580 : This is roughly Agave's process_new_epoch() which gets called from
581 : new_from_parent() for every slot.
582 : https://github.com/anza-xyz/agave/blob/v1.18.26/runtime/src/bank.rs#L1483
583 : This needs to be called after funk_txn_prepare() because the accounts
584 : that we modify when processing a new epoch need to be hashed into
585 : the bank hash.
586 : */
587 : void
588 : fd_runtime_block_pre_execute_process_new_epoch( fd_exec_slot_ctx_t * slot_ctx,
589 : fd_tpool_t * tpool,
590 : fd_spad_t * * exec_spads,
591 : ulong exec_spad_cnt,
592 : fd_spad_t * runtime_spad,
593 : int * is_epoch_boundary );
594 :
595 : /* This function is responsible for inserting fresh entries or updating existing entries in the program cache.
596 : When the client boots up, the program cache is empty. As programs get invoked, this function is responsible
597 : for verifying and inserting these programs into the cache. Additionally, this function performs reverification
598 : checks for every existing program that is invoked after an epoch boundary once per program per epoch, and invalidates
599 : any programs that fail reverification.
600 :
601 : When the cluster's feature set changes at an epoch, there is a possibility that existing programs
602 : fail new SBPF / ELF header checks. Therefore, after every epoch, we should reverify all programs
603 : and update our program cache so that users cannot invoke those old programs. Since iterating through
604 : all programs every single epoch is expensive, we adopt a lazy approach where we reverify programs as they
605 : are referenced in transactions, since only a small subset of all programs are actually referenced at any
606 : time. We also make sure each program is only verified once per epoch, so repeated executions of a
607 : program within the same epoch will only trigger verification once at the very first invocation.
608 :
609 : Note that ALUTs must be resolved because programs referenced in ALUTs can be invoked via CPI. */
610 : void
611 : fd_runtime_update_program_cache( fd_exec_slot_ctx_t * slot_ctx,
612 : fd_txn_p_t const * txn_p,
613 : fd_spad_t * runtime_spad );
614 :
615 : /* Debugging Tools ************************************************************/
616 :
617 : void
618 : fd_runtime_checkpt( fd_capture_ctx_t * capture_ctx,
619 : fd_exec_slot_ctx_t * slot_ctx,
620 : ulong slot );
621 :
622 : /* Block Parsing **************************************************************/
623 :
624 : /* Live Replay APIs */
625 :
626 : fd_raw_block_txn_iter_t
627 : fd_raw_block_txn_iter_init( uchar const * orig_data,
628 : fd_block_entry_batch_t const * batches,
629 : ulong batch_cnt );
630 :
631 : ulong
632 : fd_raw_block_txn_iter_done( fd_raw_block_txn_iter_t iter );
633 :
634 : fd_raw_block_txn_iter_t
635 : fd_raw_block_txn_iter_next( fd_raw_block_txn_iter_t iter );
636 :
637 : void
638 : fd_raw_block_txn_iter_ele( fd_raw_block_txn_iter_t iter, fd_txn_p_t * out_txn );
639 :
640 : /* Offline Replay *************************************************************/
641 :
642 : int
643 : fd_runtime_block_eval_tpool( fd_exec_slot_ctx_t * slot_ctx,
644 : ulong slot,
645 : fd_block_t * block,
646 : fd_capture_ctx_t * capture_ctx,
647 : fd_tpool_t * tpool,
648 : ulong scheduler,
649 : ulong * txn_cnt,
650 : fd_spad_t * * spads,
651 : ulong spads_cnt,
652 : fd_spad_t * runtime_spad,
653 : fd_blockstore_t * blockstore );
654 :
655 : int
656 : fd_runtime_block_execute_tpool( fd_exec_slot_ctx_t * slot_ctx,
657 : fd_blockstore_t * blockstore,
658 : fd_capture_ctx_t * capture_ctx,
659 : fd_runtime_block_info_t const * block_info,
660 : fd_tpool_t * tpool,
661 : fd_spad_t * * exec_spads,
662 : ulong exec_spad_cnt,
663 : fd_spad_t * runtime_spad );
664 :
665 : /* Genesis ********************************************************************/
666 :
667 : void
668 : fd_runtime_read_genesis( fd_exec_slot_ctx_t * slot_ctx,
669 : char const * genesis_filepath,
670 : uchar is_snapshot,
671 : fd_capture_ctx_t * capture_ctx,
672 : fd_spad_t * spad );
673 :
674 : FD_PROTOTYPES_END
675 :
676 : #endif /* HEADER_fd_src_flamenco_runtime_fd_runtime_h */
|