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