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