Line data Source code
1 :
2 : #include "generated/invoke.pb.h"
3 : #undef FD_SPAD_USE_HANDHOLDING
4 : #define FD_SPAD_USE_HANDHOLDING 1
5 : #include "fd_exec_instr_test.h"
6 : #include "../fd_acc_mgr.h"
7 : #include "../fd_executor.h"
8 : #include "../fd_hashes.h"
9 : #include "../fd_runtime.h"
10 : #include "../program/fd_bpf_loader_serialization.h"
11 : #include "../../stakes/fd_stakes.h"
12 : #include "../../rewards/fd_rewards.h"
13 : #include "../program/fd_bpf_loader_program.h"
14 : #include "../program/fd_vote_program.h"
15 : #include "../program/fd_stake_program.h"
16 : #include "../program/fd_bpf_program_util.h"
17 : #include "../program/fd_builtin_programs.h"
18 : #include "../context/fd_exec_epoch_ctx.h"
19 : #include "../context/fd_exec_slot_ctx.h"
20 : #include "../context/fd_exec_txn_ctx.h"
21 : #include "../sysvar/fd_sysvar_recent_hashes.h"
22 : #include "../sysvar/fd_sysvar_last_restart_slot.h"
23 : #include "../sysvar/fd_sysvar_slot_hashes.h"
24 : #include "../sysvar/fd_sysvar_stake_history.h"
25 : #include "../sysvar/fd_sysvar_epoch_rewards.h"
26 : #include "../../../funk/fd_funk.h"
27 : #include "../../../util/bits/fd_float.h"
28 : #include "../../../ballet/sbpf/fd_sbpf_loader.h"
29 : #include "../../vm/fd_vm.h"
30 : #include <assert.h>
31 : #include "../sysvar/fd_sysvar_cache.h"
32 : #include "../sysvar/fd_sysvar_epoch_schedule.h"
33 : #include "../sysvar/fd_sysvar_clock.h"
34 : #include "../../../disco/pack/fd_pack.h"
35 : #include "fd_vm_test.h"
36 : #include "../../vm/test_vm_util.h"
37 :
38 : /* LOGFMT_REPORT is the log prefix for instruction processing tests */
39 :
40 : #define LOGFMT_REPORT "%s"
41 : static FD_TL char _report_prefix[100] = {0};
42 :
43 : #define REPORTV( level, fmt, ... ) \
44 0 : FD_LOG_##level(( LOGFMT_REPORT fmt, _report_prefix, __VA_ARGS__ ))
45 :
46 : #define REPORT( level, fmt ) \
47 0 : FD_LOG_##level(( LOGFMT_REPORT fmt, _report_prefix ))
48 :
49 : #define REPORT_ACCTV( level, addr, fmt, ... ) \
50 : do { \
51 : char _acct_log_private_addr[ FD_BASE58_ENCODED_32_SZ ]; \
52 : void const * _acct_log_private_addr_ptr = (addr); \
53 : fd_acct_addr_cstr( _acct_log_private_addr, _acct_log_private_addr_ptr ); \
54 : REPORTV( level, " account %s: " fmt, _acct_log_private_addr, __VA_ARGS__ ); \
55 : } while(0);
56 :
57 : #define REPORT_ACCT( level, addr, fmt ) REPORT_ACCTV( level, addr, fmt, 0 )
58 :
59 : /* Macros to append data to construct a serialized transaction
60 : without exceeding bounds */
61 0 : #define FD_CHECKED_ADD_TO_TXN_DATA( _begin, _cur_data, _to_add, _sz ) __extension__({ \
62 0 : if( FD_UNLIKELY( (*_cur_data)+_sz>_begin+FD_TXN_MTU ) ) return ULONG_MAX; \
63 0 : fd_memcpy( *_cur_data, _to_add, _sz ); \
64 0 : *_cur_data += _sz; \
65 0 : })
66 :
67 0 : #define FD_CHECKED_ADD_CU16_TO_TXN_DATA( _begin, _cur_data, _to_add ) __extension__({ \
68 0 : do { \
69 0 : uchar _buf[3]; \
70 0 : fd_bincode_encode_ctx_t _encode_ctx = { .data = _buf, .dataend = _buf+3 }; \
71 0 : fd_bincode_compact_u16_encode( &_to_add, &_encode_ctx ); \
72 0 : ulong _sz = (ulong) ((uchar *)_encode_ctx.data - _buf ); \
73 0 : FD_CHECKED_ADD_TO_TXN_DATA( _begin, _cur_data, _buf, _sz ); \
74 0 : } while(0); \
75 0 : })
76 :
77 : /* Define routine to sort accounts to support query-by-pubkey via
78 : binary search. */
79 :
80 : #define SORT_NAME sort_pubkey_p
81 : #define SORT_KEY_T void const *
82 : #define SORT_BEFORE(a,b) ( memcmp( (a), (b), sizeof(fd_pubkey_t) )<0 )
83 : #include "../../../util/tmpl/fd_sort.c"
84 : #include "../../vm/fd_vm_base.h"
85 :
86 : ulong
87 0 : fd_exec_instr_test_runner_align( void ) {
88 0 : return alignof(fd_exec_instr_test_runner_t);
89 0 : }
90 :
91 : ulong
92 0 : fd_exec_instr_test_runner_footprint( void ) {
93 0 : ulong l = FD_LAYOUT_INIT;
94 0 : l = FD_LAYOUT_APPEND( l, fd_exec_instr_test_runner_align(), sizeof(fd_exec_instr_test_runner_t) );
95 0 : l = FD_LAYOUT_APPEND( l, fd_funk_align(), fd_funk_footprint() );
96 0 : return FD_LAYOUT_FINI( l, fd_exec_instr_test_runner_align() );
97 0 : }
98 :
99 : fd_exec_instr_test_runner_t *
100 : fd_exec_instr_test_runner_new( void * mem,
101 : void * spad_mem,
102 0 : ulong wksp_tag ) {
103 0 : FD_SCRATCH_ALLOC_INIT( l, mem );
104 0 : void * runner_mem = FD_SCRATCH_ALLOC_APPEND( l, fd_exec_instr_test_runner_align(), sizeof(fd_exec_instr_test_runner_t) );
105 0 : void * funk_mem = FD_SCRATCH_ALLOC_APPEND( l, fd_funk_align(), fd_funk_footprint() );
106 0 : FD_SCRATCH_ALLOC_FINI( l, fd_exec_instr_test_runner_align() );
107 :
108 0 : ulong txn_max = 4+fd_tile_cnt();
109 0 : ulong rec_max = 1024UL;
110 0 : fd_funk_t * funk = fd_funk_join( fd_funk_new( funk_mem, wksp_tag, (ulong)fd_tickcount(), txn_max, rec_max ) );
111 0 : if( FD_UNLIKELY( !funk ) ) {
112 0 : FD_LOG_WARNING(( "fd_funk_new() failed" ));
113 0 : return NULL;
114 0 : }
115 :
116 0 : fd_exec_instr_test_runner_t * runner = runner_mem;
117 0 : runner->funk = funk;
118 :
119 : /* Create spad */
120 0 : runner->spad = fd_spad_join( fd_spad_new( spad_mem, FD_RUNTIME_TRANSACTION_EXECUTION_FOOTPRINT_FUZZ ) );
121 0 : return runner;
122 0 : }
123 :
124 : void *
125 0 : fd_exec_instr_test_runner_delete( fd_exec_instr_test_runner_t * runner ) {
126 0 : if( FD_UNLIKELY( !runner ) ) return NULL;
127 0 : fd_funk_delete( fd_funk_leave( runner->funk ) );
128 0 : runner->funk = NULL;
129 0 : if( FD_UNLIKELY( fd_spad_verify( runner->spad ) ) ) {
130 0 : FD_LOG_ERR(( "fd_spad_verify() failed" ));
131 0 : }
132 0 : if( FD_UNLIKELY( fd_spad_frame_used( runner->spad )!=0 ) ) {
133 0 : FD_LOG_ERR(( "stray spad frame frame_used=%lu", fd_spad_frame_used( runner->spad ) ));
134 0 : }
135 0 : runner->spad = NULL;
136 0 : return runner;
137 0 : }
138 :
139 : fd_spad_t *
140 0 : fd_exec_instr_test_runner_get_spad( fd_exec_instr_test_runner_t * runner ) {
141 0 : return runner->spad;
142 0 : }
143 :
144 : static int
145 0 : fd_double_is_normal( double dbl ) {
146 0 : ulong x = fd_dblbits( dbl );
147 0 : int is_denorm =
148 0 : ( fd_dblbits_bexp( x ) == 0 ) &
149 0 : ( fd_dblbits_mant( x ) != 0 );
150 0 : int is_inf =
151 0 : ( fd_dblbits_bexp( x ) == 2047 ) &
152 0 : ( fd_dblbits_mant( x ) == 0 );
153 0 : int is_nan =
154 0 : ( fd_dblbits_bexp( x ) == 2047 ) &
155 0 : ( fd_dblbits_mant( x ) != 0 );
156 0 : return !( is_denorm | is_inf | is_nan );
157 0 : }
158 :
159 : static int
160 : _load_account( fd_txn_account_t * acc,
161 : fd_acc_mgr_t * acc_mgr,
162 : fd_funk_txn_t * funk_txn,
163 : fd_exec_test_acct_state_t const * state,
164 0 : uchar override_acct_state ) {
165 0 : fd_txn_account_init( acc );
166 0 : ulong size = 0UL;
167 0 : if( state->data ) size = state->data->size;
168 :
169 0 : fd_pubkey_t pubkey[1]; memcpy( pubkey, state->address, sizeof(fd_pubkey_t) );
170 :
171 : /* Account must not yet exist */
172 0 : if( FD_UNLIKELY( !override_acct_state && fd_acc_mgr_view_raw( acc_mgr, funk_txn, pubkey, NULL, NULL, NULL) ) ) {
173 0 : return 0;
174 0 : }
175 :
176 0 : assert( acc_mgr->funk );
177 0 : assert( acc_mgr->funk->magic == FD_FUNK_MAGIC );
178 0 : fd_funk_start_write( acc_mgr->funk );
179 0 : int err = fd_acc_mgr_modify( /* acc_mgr */ acc_mgr,
180 0 : /* txn */ funk_txn,
181 0 : /* pubkey */ pubkey,
182 0 : /* do_create */ 1,
183 0 : /* min_data_sz */ size,
184 0 : acc );
185 0 : assert( err==FD_ACC_MGR_SUCCESS );
186 0 : if( state->data ) fd_memcpy( acc->data, state->data->bytes, size );
187 :
188 0 : acc->starting_lamports = state->lamports;
189 0 : acc->starting_dlen = size;
190 0 : acc->meta->info.lamports = state->lamports;
191 0 : acc->meta->info.executable = state->executable;
192 0 : acc->meta->info.rent_epoch = state->rent_epoch;
193 0 : acc->meta->dlen = size;
194 0 : memcpy( acc->meta->info.owner, state->owner, sizeof(fd_pubkey_t) );
195 :
196 : /* make the account read-only by default */
197 0 : acc->meta = NULL;
198 0 : acc->data = NULL;
199 0 : acc->rec = NULL;
200 0 : fd_funk_end_write( acc_mgr->funk );
201 :
202 0 : return 1;
203 0 : }
204 :
205 : static int
206 : _load_txn_account( fd_txn_account_t * acc,
207 : fd_acc_mgr_t * acc_mgr,
208 : fd_funk_txn_t * funk_txn,
209 : fd_exec_test_acct_state_t const * state,
210 0 : uchar override_acct_state ) {
211 : // In the Agave transaction fuzzing harness, accounts with 0 lamports are not saved in the accounts db.
212 : // When they are fetched for transactions, the fields of the account are 0-set.
213 0 : fd_exec_test_acct_state_t account_state_to_save = FD_EXEC_TEST_ACCT_STATE_INIT_ZERO;
214 0 : memcpy( account_state_to_save.address, state->address, sizeof(fd_pubkey_t) );
215 :
216 : // Restore the account state if it has lamports
217 0 : if( state->lamports ) {
218 0 : account_state_to_save = *state;
219 0 : }
220 :
221 0 : return _load_account( acc, acc_mgr, funk_txn, &account_state_to_save, override_acct_state );
222 0 : }
223 :
224 : static int
225 : _restore_feature_flags( fd_exec_epoch_ctx_t * epoch_ctx,
226 0 : fd_exec_test_feature_set_t const * feature_set ) {
227 0 : fd_features_disable_all( &epoch_ctx->features );
228 0 : for( ulong j=0UL; j < feature_set->features_count; j++ ) {
229 0 : ulong prefix = feature_set->features[j];
230 0 : fd_feature_id_t const * id = fd_feature_id_query( prefix );
231 0 : if( FD_UNLIKELY( !id ) ) {
232 0 : FD_LOG_WARNING(( "unsupported feature ID 0x%016lx", prefix ));
233 0 : return 0;
234 0 : }
235 : /* Enabled since genesis */
236 0 : fd_features_set( &epoch_ctx->features, id, 0UL );
237 0 : }
238 0 : return 1;
239 0 : }
240 :
241 : /* Serializes a Protobuf SanitizedTransaction and returns the number of bytes consumed.
242 : Returns ULONG_MAX if the number of bytes read exceeds 1232 (FD_TXN_MTU).
243 : _txn_raw_begin is assumed to be a pre-allocated buffer of at least 1232 bytes. */
244 : ulong
245 : _serialize_txn( uchar * txn_raw_begin,
246 : const fd_exec_test_sanitized_transaction_t * tx,
247 : ushort * out_instr_cnt,
248 0 : ushort * out_addr_table_cnt ) {
249 0 : const uchar empty_bytes[64] = { 0 };
250 0 : uchar * txn_raw_cur_ptr = txn_raw_begin;
251 :
252 : /* Compact array of signatures (https://solana.com/docs/core/transactions#transaction)
253 : Note that although documentation interchangably refers to the signature cnt as a compact-u16
254 : and a u8, the max signature cnt is capped at 48 (due to txn size limits), so u8 and compact-u16
255 : is represented the same way anyways and can be parsed identically. */
256 : // Note: always create a valid txn with 1+ signatures, add an empty signature if none is provided
257 0 : uchar signature_cnt = fd_uchar_max( 1, (uchar) tx->signatures_count );
258 0 : FD_CHECKED_ADD_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, &signature_cnt, sizeof(uchar) );
259 0 : for( uchar i = 0; i < signature_cnt; ++i ) {
260 0 : FD_CHECKED_ADD_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, tx->signatures && tx->signatures[i] ? tx->signatures[i]->bytes : empty_bytes, FD_TXN_SIGNATURE_SZ );
261 0 : }
262 :
263 : /* Message */
264 : /* For v0 transactions, the highest bit of the num_required_signatures is set, and an extra byte is used for the version.
265 : https://solanacookbook.com/guides/versioned-transactions.html#versioned-transactions-transactionv0
266 :
267 : We will always create a transaction with at least 1 signature, and cap the signature count to 127 to avoid
268 : collisions with the header_b0 tag. */
269 0 : uchar num_required_signatures = fd_uchar_max( 1, fd_uchar_min( 127, (uchar) tx->message.header.num_required_signatures ) );
270 0 : if( !tx->message.is_legacy ) {
271 0 : uchar header_b0 = (uchar) 0x80UL;
272 0 : FD_CHECKED_ADD_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, &header_b0, sizeof(uchar) );
273 0 : }
274 :
275 : /* Header (3 bytes) (https://solana.com/docs/core/transactions#message-header) */
276 0 : FD_CHECKED_ADD_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, &num_required_signatures, sizeof(uchar) );
277 0 : FD_CHECKED_ADD_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, &tx->message.header.num_readonly_signed_accounts, sizeof(uchar) );
278 0 : FD_CHECKED_ADD_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, &tx->message.header.num_readonly_unsigned_accounts, sizeof(uchar) );
279 :
280 : /* Compact array of account addresses (https://solana.com/docs/core/transactions#compact-array-format) */
281 : // Array length is a compact u16
282 0 : ushort num_acct_keys = (ushort) tx->message.account_keys_count;
283 0 : FD_CHECKED_ADD_CU16_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, num_acct_keys );
284 0 : for( ushort i = 0; i < num_acct_keys; ++i ) {
285 0 : FD_CHECKED_ADD_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, tx->message.account_keys[i]->bytes, sizeof(fd_pubkey_t) );
286 0 : }
287 :
288 : /* Recent blockhash (32 bytes) (https://solana.com/docs/core/transactions#recent-blockhash) */
289 : // Note: add an empty blockhash if none is provided
290 0 : FD_CHECKED_ADD_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, tx->message.recent_blockhash ? tx->message.recent_blockhash->bytes : empty_bytes, sizeof(fd_hash_t) );
291 :
292 : /* Compact array of instructions (https://solana.com/docs/core/transactions#array-of-instructions) */
293 : // Instruction count is a compact u16
294 0 : ushort instr_count = (ushort) tx->message.instructions_count;
295 0 : FD_CHECKED_ADD_CU16_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, instr_count );
296 0 : for( ushort i = 0; i < instr_count; ++i ) {
297 : // Program ID index
298 0 : uchar program_id_index = (uchar) tx->message.instructions[i].program_id_index;
299 0 : FD_CHECKED_ADD_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, &program_id_index, sizeof(uchar) );
300 :
301 : // Compact array of account addresses
302 0 : ushort acct_count = (ushort) tx->message.instructions[i].accounts_count;
303 0 : FD_CHECKED_ADD_CU16_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, acct_count );
304 0 : for( ushort j = 0; j < acct_count; ++j ) {
305 0 : uchar account_index = (uchar) tx->message.instructions[i].accounts[j];
306 0 : FD_CHECKED_ADD_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, &account_index, sizeof(uchar) );
307 0 : }
308 :
309 : // Compact array of 8-bit data
310 0 : pb_bytes_array_t * data = tx->message.instructions[i].data;
311 0 : ushort data_len;
312 0 : if( data ) {
313 0 : data_len = (ushort) data->size;
314 0 : FD_CHECKED_ADD_CU16_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, data_len );
315 0 : FD_CHECKED_ADD_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, data->bytes, data_len );
316 0 : } else {
317 0 : data_len = 0;
318 0 : FD_CHECKED_ADD_CU16_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, data_len );
319 0 : }
320 0 : }
321 :
322 : /* Address table lookups (N/A for legacy transactions) */
323 0 : ushort addr_table_cnt = 0;
324 0 : if( !tx->message.is_legacy ) {
325 : /* Compact array of address table lookups (https://solanacookbook.com/guides/versioned-transactions.html#compact-array-of-address-table-lookups) */
326 : // NOTE: The diagram is slightly wrong - the account key is a 32 byte pubkey, not a u8
327 0 : addr_table_cnt = (ushort) tx->message.address_table_lookups_count;
328 0 : FD_CHECKED_ADD_CU16_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, addr_table_cnt );
329 0 : for( ushort i = 0; i < addr_table_cnt; ++i ) {
330 : // Account key
331 0 : FD_CHECKED_ADD_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, tx->message.address_table_lookups[i].account_key, sizeof(fd_pubkey_t) );
332 :
333 : // Compact array of writable indexes
334 0 : ushort writable_count = (ushort) tx->message.address_table_lookups[i].writable_indexes_count;
335 0 : FD_CHECKED_ADD_CU16_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, writable_count );
336 0 : for( ushort j = 0; j < writable_count; ++j ) {
337 0 : uchar writable_index = (uchar) tx->message.address_table_lookups[i].writable_indexes[j];
338 0 : FD_CHECKED_ADD_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, &writable_index, sizeof(uchar) );
339 0 : }
340 :
341 : // Compact array of readonly indexes
342 0 : ushort readonly_count = (ushort) tx->message.address_table_lookups[i].readonly_indexes_count;
343 0 : FD_CHECKED_ADD_CU16_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, readonly_count );
344 0 : for( ushort j = 0; j < readonly_count; ++j ) {
345 0 : uchar readonly_index = (uchar) tx->message.address_table_lookups[i].readonly_indexes[j];
346 0 : FD_CHECKED_ADD_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, &readonly_index, sizeof(uchar) );
347 0 : }
348 0 : }
349 0 : }
350 0 : *out_instr_cnt = instr_count;
351 0 : *out_addr_table_cnt = addr_table_cnt;
352 0 : return (ulong)(txn_raw_cur_ptr - txn_raw_begin);
353 0 : }
354 :
355 :
356 : int
357 : fd_exec_test_instr_context_create( fd_exec_instr_test_runner_t * runner,
358 : fd_exec_instr_ctx_t * ctx,
359 : fd_exec_test_instr_context_t const * test_ctx,
360 0 : bool is_syscall ) {
361 0 : memset( ctx, 0, sizeof(fd_exec_instr_ctx_t) );
362 :
363 0 : fd_funk_t * funk = runner->funk;
364 :
365 : /* Generate unique ID for funk txn */
366 :
367 0 : fd_funk_txn_xid_t xid[1] = {0};
368 0 : xid[0] = fd_funk_generate_xid();
369 :
370 : /* Create temporary funk transaction and txn / slot / epoch contexts */
371 :
372 0 : fd_funk_start_write( funk );
373 0 : fd_funk_txn_t * funk_txn = fd_funk_txn_prepare( funk, NULL, xid, 1 );
374 0 : fd_funk_end_write( funk );
375 :
376 0 : ulong vote_acct_max = MAX_TX_ACCOUNT_LOCKS;
377 :
378 : /* Allocate contexts */
379 0 : uchar * epoch_ctx_mem = fd_spad_alloc( runner->spad, fd_exec_epoch_ctx_align(), fd_exec_epoch_ctx_footprint( vote_acct_max ) );
380 0 : uchar * slot_ctx_mem = fd_spad_alloc( runner->spad,FD_EXEC_SLOT_CTX_ALIGN, FD_EXEC_SLOT_CTX_FOOTPRINT );
381 0 : uchar * txn_ctx_mem = fd_spad_alloc( runner->spad,FD_EXEC_TXN_CTX_ALIGN, FD_EXEC_TXN_CTX_FOOTPRINT );
382 :
383 0 : fd_exec_epoch_ctx_t * epoch_ctx = fd_exec_epoch_ctx_join( fd_exec_epoch_ctx_new( epoch_ctx_mem, vote_acct_max ) );
384 0 : fd_exec_slot_ctx_t * slot_ctx = fd_exec_slot_ctx_join ( fd_exec_slot_ctx_new ( slot_ctx_mem, runner->spad ) );
385 0 : fd_exec_txn_ctx_t * txn_ctx = fd_exec_txn_ctx_join ( fd_exec_txn_ctx_new ( txn_ctx_mem ) );
386 :
387 0 : assert( epoch_ctx );
388 0 : assert( slot_ctx );
389 :
390 0 : ctx->txn_ctx = txn_ctx;
391 :
392 : /* Set up epoch context. Defaults obtained from GenesisConfig::Default() */
393 0 : fd_epoch_bank_t * epoch_bank = fd_exec_epoch_ctx_epoch_bank( epoch_ctx );
394 0 : epoch_bank->rent.lamports_per_uint8_year = 3480;
395 0 : epoch_bank->rent.exemption_threshold = 2;
396 0 : epoch_bank->rent.burn_percent = 50;
397 :
398 : /* Create account manager */
399 :
400 0 : fd_acc_mgr_t * acc_mgr = fd_acc_mgr_new( fd_spad_alloc( runner->spad, FD_ACC_MGR_ALIGN, FD_ACC_MGR_FOOTPRINT ), funk );
401 0 : assert( acc_mgr );
402 :
403 : /* Set up slot context */
404 :
405 0 : slot_ctx->epoch_ctx = epoch_ctx;
406 0 : slot_ctx->funk_txn = funk_txn;
407 0 : slot_ctx->acc_mgr = acc_mgr;
408 :
409 : /* Restore feature flags */
410 :
411 0 : fd_exec_test_feature_set_t const * feature_set = &test_ctx->epoch_context.features;
412 0 : if( !_restore_feature_flags( epoch_ctx, feature_set ) ) {
413 0 : return 0;
414 0 : }
415 :
416 : /* Restore slot_bank */
417 :
418 0 : fd_slot_bank_new( &slot_ctx->slot_bank );
419 :
420 : /* Blockhash queue init */
421 0 : fd_block_hash_queue_t * blockhash_queue = &slot_ctx->slot_bank.block_hash_queue;
422 0 : blockhash_queue->max_age = FD_BLOCKHASH_QUEUE_MAX_ENTRIES;
423 0 : blockhash_queue->last_hash = fd_spad_alloc( runner->spad, FD_HASH_ALIGN, FD_HASH_FOOTPRINT );
424 0 : fd_memset( blockhash_queue->last_hash, 0, FD_HASH_FOOTPRINT );
425 :
426 : /* Set up txn context */
427 :
428 0 : fd_wksp_t * funk_wksp = fd_funk_wksp( funk );
429 0 : fd_wksp_t * runtime_wksp = fd_wksp_containing( slot_ctx );
430 0 : ulong funk_txn_gaddr = fd_wksp_gaddr( funk_wksp, funk_txn );
431 0 : ulong acc_mgr_gaddr = fd_wksp_gaddr( runtime_wksp, acc_mgr );
432 0 : ulong funk_gaddr = fd_wksp_gaddr( funk_wksp, funk );
433 0 : ulong sysvar_cache_gaddr = fd_wksp_gaddr( runtime_wksp, slot_ctx->sysvar_cache );
434 :
435 0 : fd_exec_txn_ctx_from_exec_slot_ctx( slot_ctx,
436 0 : txn_ctx,
437 0 : funk_wksp,
438 0 : runtime_wksp,
439 0 : funk_txn_gaddr,
440 0 : acc_mgr_gaddr,
441 0 : sysvar_cache_gaddr,
442 0 : funk_gaddr );
443 0 : fd_exec_txn_ctx_setup_basic( txn_ctx );
444 :
445 0 : txn_ctx->compute_unit_limit = test_ctx->cu_avail;
446 0 : txn_ctx->compute_meter = test_ctx->cu_avail;
447 0 : txn_ctx->vote_accounts_pool = NULL;
448 0 : txn_ctx->spad = runner->spad;
449 :
450 : /* Set up instruction context */
451 :
452 0 : fd_instr_info_t * info = fd_spad_alloc( runner->spad, 8UL, sizeof(fd_instr_info_t) );
453 0 : assert( info );
454 0 : memset( info, 0, sizeof(fd_instr_info_t) );
455 :
456 0 : if( test_ctx->data ) {
457 0 : info->data_sz = (ushort)test_ctx->data->size;
458 0 : info->data = test_ctx->data->bytes;
459 0 : }
460 :
461 0 : memcpy( info->program_id_pubkey.uc, test_ctx->program_id, sizeof(fd_pubkey_t) );
462 :
463 : /* Prepare borrowed account table (correctly handles aliasing) */
464 :
465 0 : if( FD_UNLIKELY( test_ctx->accounts_count > MAX_TX_ACCOUNT_LOCKS ) ) {
466 0 : REPORT( NOTICE, "too many accounts" );
467 0 : return 0;
468 0 : }
469 :
470 : /* Load accounts into database */
471 :
472 0 : assert( acc_mgr->funk );
473 :
474 0 : fd_txn_account_t * accts = txn_ctx->accounts;
475 0 : fd_memset( accts, 0, test_ctx->accounts_count * sizeof(fd_txn_account_t) );
476 0 : txn_ctx->accounts_cnt = test_ctx->accounts_count;
477 :
478 0 : int has_program_id = 0;
479 :
480 0 : for( ulong j=0UL; j < test_ctx->accounts_count; j++ ) {
481 0 : memcpy( &(txn_ctx->account_keys[j]), test_ctx->accounts[j].address, sizeof(fd_pubkey_t) );
482 0 : if( !_load_account( &accts[j], acc_mgr, funk_txn, &test_ctx->accounts[j], 0 ) ) {
483 0 : return 0;
484 0 : }
485 :
486 0 : if( accts[j].const_meta ) {
487 0 : uchar * data = fd_spad_alloc( txn_ctx->spad, FD_ACCOUNT_REC_ALIGN, FD_ACC_TOT_SZ_MAX );
488 0 : ulong dlen = accts[j].const_meta->dlen;
489 0 : fd_memcpy( data, accts[j].const_meta, sizeof(fd_account_meta_t)+dlen );
490 0 : accts[j].const_meta = (fd_account_meta_t*)data;
491 0 : accts[j].const_data = data + sizeof(fd_account_meta_t);
492 0 : }
493 :
494 0 : if( !memcmp( accts[j].pubkey, test_ctx->program_id, sizeof(fd_pubkey_t) ) ) {
495 0 : has_program_id = 1;
496 0 : info->program_id = (uchar)txn_ctx->accounts_cnt;
497 0 : }
498 0 : }
499 :
500 : /* If the program id is not in the set of accounts it must be added to the set of accounts. */
501 0 : if( FD_UNLIKELY( !has_program_id ) ) {
502 0 : fd_txn_account_t * program_acc = &accts[ test_ctx->accounts_count ];
503 0 : fd_pubkey_t * program_key = &txn_ctx->account_keys[ txn_ctx->accounts_cnt ];
504 0 : fd_txn_account_init( program_acc );
505 0 : memcpy( program_key, test_ctx->program_id, sizeof(fd_pubkey_t) );
506 0 : memcpy( program_acc->pubkey, test_ctx->program_id, sizeof(fd_pubkey_t) );
507 0 : program_acc->meta = fd_spad_alloc( txn_ctx->spad, alignof(fd_account_meta_t*), sizeof(fd_account_meta_t*) );
508 0 : program_acc->const_meta = program_acc->meta;
509 0 : fd_account_meta_init( program_acc->meta );
510 0 : info->program_id = (uchar)txn_ctx->accounts_cnt;
511 0 : txn_ctx->accounts_cnt++;
512 0 : }
513 :
514 : /* Load in executable accounts */
515 0 : for( ulong i = 0; i < txn_ctx->accounts_cnt; i++ ) {
516 0 : if ( memcmp( accts[i].const_meta->info.owner, fd_solana_bpf_loader_deprecated_program_id.key, sizeof(fd_pubkey_t) ) != 0
517 0 : && memcmp( accts[i].const_meta->info.owner, fd_solana_bpf_loader_program_id.key, sizeof(fd_pubkey_t) ) != 0
518 0 : && memcmp( accts[i].const_meta->info.owner, fd_solana_bpf_loader_upgradeable_program_id.key, sizeof(fd_pubkey_t) ) != 0
519 0 : && memcmp( accts[i].const_meta->info.owner, fd_solana_bpf_loader_v4_program_id.key, sizeof(fd_pubkey_t) ) != 0
520 0 : ) {
521 0 : continue;
522 0 : }
523 :
524 0 : fd_account_meta_t const * meta = accts[i].const_meta ? accts[i].const_meta : accts[i].meta;
525 0 : if (meta == NULL) {
526 0 : static const fd_account_meta_t sentinel = { .magic = FD_ACCOUNT_META_MAGIC };
527 0 : accts[i].const_meta = &sentinel;
528 0 : accts[i].starting_lamports = 0UL;
529 0 : accts[i].starting_dlen = 0UL;
530 0 : continue;
531 0 : }
532 :
533 0 : if( meta->info.executable ) {
534 0 : FD_TXN_ACCOUNT_DECL( owner_borrowed_account );
535 0 : int err = fd_acc_mgr_view( txn_ctx->acc_mgr, txn_ctx->funk_txn, (fd_pubkey_t *)meta->info.owner, owner_borrowed_account );
536 0 : if( FD_UNLIKELY( err ) ) {
537 0 : accts[i].starting_owner_dlen = 0;
538 0 : } else {
539 0 : accts[i].starting_owner_dlen = owner_borrowed_account->const_meta->dlen;
540 0 : }
541 0 : }
542 :
543 0 : if( FD_UNLIKELY( 0 == memcmp(meta->info.owner, fd_solana_bpf_loader_upgradeable_program_id.key, sizeof(fd_pubkey_t)) ) ) {
544 0 : int err = 0;
545 0 : fd_bpf_upgradeable_loader_state_t * program_loader_state = read_bpf_upgradeable_loader_state_for_program( txn_ctx,
546 0 : (uchar)i,
547 0 : &err );
548 :
549 0 : if( FD_UNLIKELY( !program_loader_state ) ) {
550 0 : continue;
551 0 : }
552 :
553 0 : fd_pubkey_t * programdata_acc = &program_loader_state->inner.program.programdata_address;
554 0 : fd_txn_account_t * executable_account = fd_txn_account_init( &txn_ctx->executable_accounts[txn_ctx->executable_cnt] );
555 0 : fd_acc_mgr_view(txn_ctx->acc_mgr, txn_ctx->funk_txn, programdata_acc, executable_account);
556 0 : txn_ctx->executable_cnt++;
557 0 : }
558 0 : }
559 :
560 : /* Add accounts to bpf program cache */
561 0 : fd_funk_start_write( acc_mgr->funk );
562 0 : fd_bpf_scan_and_create_bpf_program_cache_entry( slot_ctx, funk_txn, runner->spad );
563 0 : fd_funk_end_write( acc_mgr->funk );
564 :
565 : /* Restore sysvar cache */
566 0 : fd_sysvar_cache_restore( slot_ctx->sysvar_cache, acc_mgr, funk_txn, runner->spad, runtime_wksp );
567 :
568 : /* Fill missing sysvar cache values with defaults */
569 : /* We create mock accounts for each of the sysvars and hardcode the data fields before loading it into the account manager */
570 : /* We use Agave sysvar defaults for data field values */
571 :
572 : /* Clock */
573 : // https://github.com/firedancer-io/solfuzz-agave/blob/agave-v2.0/src/lib.rs#L466-L474
574 0 : if( !slot_ctx->sysvar_cache->has_clock ) {
575 0 : slot_ctx->sysvar_cache->has_clock = 1;
576 0 : fd_sol_sysvar_clock_t sysvar_clock = {
577 0 : .slot = 10,
578 0 : .epoch_start_timestamp = 0,
579 0 : .epoch = 0,
580 0 : .leader_schedule_epoch = 0,
581 0 : .unix_timestamp = 0
582 0 : };
583 0 : memcpy( slot_ctx->sysvar_cache->val_clock, &sysvar_clock, sizeof(fd_sol_sysvar_clock_t) );
584 0 : }
585 :
586 : /* Epoch schedule */
587 : // https://github.com/firedancer-io/solfuzz-agave/blob/agave-v2.0/src/lib.rs#L476-L483
588 0 : if ( !slot_ctx->sysvar_cache->has_epoch_schedule ) {
589 0 : slot_ctx->sysvar_cache->has_epoch_schedule = 1;
590 0 : fd_epoch_schedule_t sysvar_epoch_schedule = {
591 0 : .slots_per_epoch = 432000,
592 0 : .leader_schedule_slot_offset = 432000,
593 0 : .warmup = 1,
594 0 : .first_normal_epoch = 14,
595 0 : .first_normal_slot = 524256
596 0 : };
597 0 : memcpy( slot_ctx->sysvar_cache->val_epoch_schedule, &sysvar_epoch_schedule, sizeof(fd_epoch_schedule_t) );
598 0 : }
599 :
600 : /* Rent */
601 : // https://github.com/firedancer-io/solfuzz-agave/blob/agave-v2.0/src/lib.rs#L487-L500
602 0 : if ( !slot_ctx->sysvar_cache->has_rent ) {
603 0 : slot_ctx->sysvar_cache->has_rent = 1;
604 0 : fd_rent_t sysvar_rent = {
605 0 : .lamports_per_uint8_year = 3480,
606 0 : .exemption_threshold = 2.0,
607 0 : .burn_percent = 50
608 0 : };
609 0 : memcpy( slot_ctx->sysvar_cache->val_rent, &sysvar_rent, sizeof(fd_rent_t) );
610 0 : }
611 :
612 0 : if ( !slot_ctx->sysvar_cache->has_last_restart_slot ) {
613 0 : slot_ctx->sysvar_cache->has_last_restart_slot = 1;
614 :
615 0 : fd_sol_sysvar_last_restart_slot_t restart = {.slot = 5000};
616 :
617 0 : memcpy( slot_ctx->sysvar_cache->val_last_restart_slot, &restart, sizeof(fd_sol_sysvar_last_restart_slot_t) );
618 0 : }
619 :
620 : /* Set slot bank variables */
621 0 : slot_ctx->slot_bank.slot = fd_sysvar_cache_clock( slot_ctx->sysvar_cache )->slot;
622 :
623 : /* Handle undefined behavior if sysvars are malicious (!!!) */
624 :
625 : /* Override epoch bank rent setting */
626 0 : fd_rent_t const * rent = (fd_rent_t const *)fd_sysvar_cache_rent( slot_ctx->sysvar_cache );
627 0 : if( rent ) {
628 0 : epoch_bank->rent = *rent;
629 0 : }
630 :
631 : /* Override most recent blockhash if given */
632 0 : fd_recent_block_hashes_global_t const * rbh_global = fd_sysvar_cache_recent_block_hashes( slot_ctx->sysvar_cache );
633 0 : fd_recent_block_hashes_t rbh[1];
634 0 : if( rbh_global ) {
635 0 : fd_bincode_decode_ctx_t decode = { .wksp = runtime_wksp };
636 0 : fd_recent_block_hashes_convert_global_to_local( rbh_global, rbh, &decode );
637 0 : }
638 :
639 0 : if( rbh_global && !deq_fd_block_block_hash_entry_t_empty( rbh->hashes ) ) {
640 0 : fd_block_block_hash_entry_t const * last = deq_fd_block_block_hash_entry_t_peek_tail_const( rbh->hashes );
641 0 : if( last ) {
642 0 : *blockhash_queue->last_hash = last->blockhash;
643 0 : slot_ctx->slot_bank.lamports_per_signature = last->fee_calculator.lamports_per_signature;
644 0 : slot_ctx->prev_lamports_per_signature = last->fee_calculator.lamports_per_signature;
645 0 : }
646 0 : }
647 :
648 : /* Load instruction accounts */
649 :
650 0 : if( FD_UNLIKELY( test_ctx->instr_accounts_count > MAX_TX_ACCOUNT_LOCKS ) ) {
651 0 : REPORT( NOTICE, "too many instruction accounts" );
652 0 : return 0;
653 0 : }
654 :
655 0 : uchar acc_idx_seen[256] = {0};
656 0 : for( ulong j=0UL; j < test_ctx->instr_accounts_count; j++ ) {
657 0 : uint index = test_ctx->instr_accounts[j].index;
658 0 : if( index >= test_ctx->accounts_count ) {
659 0 : REPORTV( NOTICE, " instruction account index out of range (%u > %u)", index, test_ctx->instr_accounts_count );
660 0 : return 0;
661 0 : }
662 :
663 0 : fd_txn_account_t * acc = &accts[ index ];
664 0 : uint flags = 0;
665 0 : flags |= test_ctx->instr_accounts[j].is_writable ? FD_INSTR_ACCT_FLAGS_IS_WRITABLE : 0;
666 0 : flags |= test_ctx->instr_accounts[j].is_signer ? FD_INSTR_ACCT_FLAGS_IS_SIGNER : 0;
667 :
668 0 : info->accounts[j] = acc;
669 0 : info->acct_flags [j] = (uchar)flags;
670 0 : memcpy( info->acct_pubkeys[j].uc, acc->pubkey, sizeof(fd_pubkey_t) );
671 0 : info->acct_txn_idxs[j] = (uchar) index;
672 :
673 0 : if( test_ctx->instr_accounts[j].is_writable ) {
674 0 : acc->meta = (void *)acc->const_meta;
675 0 : acc->data = (void *)acc->const_data;
676 0 : acc->rec = (void *)acc->const_rec;
677 0 : }
678 :
679 0 : if (acc_idx_seen[index]) {
680 0 : info->is_duplicate[j] = 1;
681 0 : }
682 0 : acc_idx_seen[index] = 1;
683 0 : }
684 0 : info->acct_cnt = (uchar)test_ctx->instr_accounts_count;
685 :
686 : // FIXME: Specifically for CPI syscalls, flag guard this?
687 0 : fd_instr_info_sum_account_lamports( info, &info->starting_lamports_h, &info->starting_lamports_l );
688 :
689 : /* The remaining checks enforce that the program is in the accounts list. */
690 0 : bool found_program_id = false;
691 0 : for( uint i = 0; i < test_ctx->accounts_count; i++ ) {
692 0 : if( 0 == memcmp( test_ctx->accounts[i].address, test_ctx->program_id, sizeof(fd_pubkey_t) ) ) {
693 0 : info->program_id = (uchar) i;
694 0 : found_program_id = true;
695 0 : break;
696 0 : }
697 0 : }
698 :
699 : /* Early returning only happens in instruction execution. */
700 0 : if( !is_syscall && !found_program_id ) {
701 0 : FD_LOG_NOTICE(( " Unable to find program_id in accounts" ));
702 0 : return 0;
703 0 : }
704 :
705 0 : ctx->funk_txn = funk_txn;
706 0 : ctx->acc_mgr = acc_mgr;
707 0 : ctx->instr = info;
708 :
709 : /* Refresh the setup from the updated slot and epoch ctx. */
710 0 : fd_exec_txn_ctx_from_exec_slot_ctx( slot_ctx,
711 0 : txn_ctx,
712 0 : funk_wksp,
713 0 : runtime_wksp,
714 0 : funk_txn_gaddr,
715 0 : acc_mgr_gaddr,
716 0 : sysvar_cache_gaddr,
717 0 : funk_gaddr );
718 :
719 0 : fd_log_collector_init( &ctx->txn_ctx->log_collector, 1 );
720 0 : fd_base58_encode_32( ctx->instr->program_id_pubkey.uc, NULL, ctx->program_id_base58 );
721 :
722 0 : return 1;
723 0 : }
724 :
725 : static fd_execute_txn_task_info_t *
726 : _txn_context_create_and_exec( fd_exec_instr_test_runner_t * runner,
727 : fd_exec_slot_ctx_t * slot_ctx,
728 0 : fd_exec_test_txn_context_t const * test_ctx ) {
729 0 : const uchar empty_bytes[64] = { 0 };
730 0 : fd_funk_t * funk = runner->funk;
731 :
732 : /* Generate unique ID for funk txn */
733 :
734 0 : fd_funk_txn_xid_t xid[1] = {0};
735 0 : xid[0] = fd_funk_generate_xid();
736 :
737 : /* Create temporary funk transaction and spad contexts */
738 :
739 0 : fd_funk_start_write( runner->funk );
740 0 : fd_funk_txn_t * funk_txn = fd_funk_txn_prepare( funk, NULL, xid, 1 );
741 0 : fd_funk_end_write( runner->funk );
742 :
743 0 : ulong vote_acct_max = MAX_TX_ACCOUNT_LOCKS;
744 :
745 : /* Allocate contexts */
746 0 : uchar * epoch_ctx_mem = fd_spad_alloc( runner->spad, fd_exec_epoch_ctx_align(), fd_exec_epoch_ctx_footprint( vote_acct_max ) );
747 0 : fd_exec_epoch_ctx_t * epoch_ctx = fd_exec_epoch_ctx_join( fd_exec_epoch_ctx_new( epoch_ctx_mem, vote_acct_max ) );
748 :
749 0 : assert( epoch_ctx );
750 0 : assert( slot_ctx );
751 :
752 : /* Set up epoch context */
753 0 : fd_epoch_bank_t * epoch_bank = fd_exec_epoch_ctx_epoch_bank( epoch_ctx );
754 :
755 : /* Create account manager */
756 0 : fd_acc_mgr_t * acc_mgr = fd_acc_mgr_new( fd_spad_alloc( runner->spad, FD_ACC_MGR_ALIGN, FD_ACC_MGR_FOOTPRINT ), funk );
757 0 : assert( acc_mgr );
758 :
759 : /* Set up slot context */
760 :
761 0 : slot_ctx->epoch_ctx = epoch_ctx;
762 0 : slot_ctx->funk_txn = funk_txn;
763 0 : slot_ctx->acc_mgr = acc_mgr;
764 :
765 : /* Restore feature flags */
766 :
767 0 : fd_exec_test_feature_set_t const * feature_set = &test_ctx->epoch_ctx.features;
768 0 : if( !_restore_feature_flags( epoch_ctx, feature_set ) ) {
769 0 : return NULL;
770 0 : }
771 :
772 : /* Restore slot bank */
773 0 : fd_slot_bank_new( &slot_ctx->slot_bank );
774 :
775 : /* Initialize builtin accounts */
776 0 : fd_funk_start_write( runner->funk );
777 0 : fd_builtin_programs_init( slot_ctx );
778 0 : fd_funk_end_write( runner->funk );
779 :
780 : /* Load account states into funk (note this is different from the account keys):
781 : Account state = accounts to populate Funk
782 : Account keys = account keys that the transaction needs */
783 0 : for( ulong i = 0; i < test_ctx->account_shared_data_count; i++ ) {
784 : /* Load the accounts into the account manager
785 : Borrowed accounts get reset anyways - we just need to load the account somewhere */
786 0 : FD_TXN_ACCOUNT_DECL(acc);
787 0 : _load_txn_account( acc, acc_mgr, funk_txn, &test_ctx->account_shared_data[i], 0 );
788 0 : }
789 :
790 : /* Restore sysvar cache */
791 0 : fd_sysvar_cache_restore( slot_ctx->sysvar_cache, acc_mgr, funk_txn, runner->spad, fd_wksp_containing( slot_ctx ) );
792 :
793 : /* Add accounts to bpf program cache */
794 0 : fd_funk_start_write( runner->funk );
795 0 : fd_bpf_scan_and_create_bpf_program_cache_entry( slot_ctx, funk_txn, runner->spad );
796 :
797 : /* Default slot */
798 0 : ulong slot = test_ctx->slot_ctx.slot ? test_ctx->slot_ctx.slot : 10; // Arbitrary default > 0
799 :
800 : /* Set slot bank variables (defaults obtained from GenesisConfig::default() in Agave) */
801 0 : slot_ctx->slot_bank.slot = slot;
802 0 : slot_ctx->slot_bank.prev_slot = slot_ctx->slot_bank.slot - 1; // Can underflow, but its fine since it will correctly be ULONG_MAX
803 0 : slot_ctx->slot_bank.fee_rate_governor.burn_percent = 50;
804 0 : slot_ctx->slot_bank.fee_rate_governor.min_lamports_per_signature = 0;
805 0 : slot_ctx->slot_bank.fee_rate_governor.max_lamports_per_signature = 0;
806 0 : slot_ctx->slot_bank.fee_rate_governor.target_lamports_per_signature = 10000;
807 0 : slot_ctx->slot_bank.fee_rate_governor.target_signatures_per_slot = 20000;
808 0 : slot_ctx->slot_bank.lamports_per_signature = 5000;
809 0 : slot_ctx->prev_lamports_per_signature = 5000;
810 :
811 : /* Set epoch bank variables if not present (defaults obtained from GenesisConfig::default() in Agave) */
812 0 : fd_epoch_schedule_t default_epoch_schedule = {
813 0 : .slots_per_epoch = 432000,
814 0 : .leader_schedule_slot_offset = 432000,
815 0 : .warmup = 1,
816 0 : .first_normal_epoch = 14,
817 0 : .first_normal_slot = 524256
818 0 : };
819 0 : fd_rent_t default_rent = {
820 0 : .lamports_per_uint8_year = 3480,
821 0 : .exemption_threshold = 2.0,
822 0 : .burn_percent = 50
823 0 : };
824 0 : epoch_bank->epoch_schedule = default_epoch_schedule;
825 0 : epoch_bank->rent_epoch_schedule = default_epoch_schedule;
826 0 : epoch_bank->rent = default_rent;
827 0 : epoch_bank->ticks_per_slot = 64;
828 0 : epoch_bank->slots_per_year = SECONDS_PER_YEAR * (1000000000.0 / (double)6250000) / (double)epoch_bank->ticks_per_slot;
829 :
830 : // Override default values if provided
831 0 : if( slot_ctx->sysvar_cache->has_epoch_schedule ) {
832 0 : epoch_bank->epoch_schedule = *(fd_epoch_schedule_t *)fd_type_pun_const( slot_ctx->sysvar_cache->val_epoch_schedule );
833 0 : epoch_bank->rent_epoch_schedule = *(fd_epoch_schedule_t *)fd_type_pun_const( slot_ctx->sysvar_cache->val_epoch_schedule );
834 0 : }
835 :
836 0 : if( slot_ctx->sysvar_cache->has_rent ) {
837 0 : epoch_bank->rent = *(fd_rent_t *)fd_type_pun_const( slot_ctx->sysvar_cache->val_rent );
838 0 : }
839 :
840 : /* Provide default slot hashes of size 1 if not provided */
841 0 : if( !slot_ctx->sysvar_cache->has_slot_hashes ) {
842 0 : uchar * deque_mem = fd_spad_alloc( runner->spad, deq_fd_slot_hash_t_align(), deq_fd_slot_hash_t_footprint( 1 ) );
843 0 : fd_slot_hash_t * slot_hashes = deq_fd_slot_hash_t_join( deq_fd_slot_hash_t_new( deque_mem, 1 ) );
844 0 : fd_slot_hash_t * dummy_elem = deq_fd_slot_hash_t_push_tail_nocopy( slot_hashes );
845 0 : memset( dummy_elem, 0, sizeof(fd_slot_hash_t) );
846 0 : fd_slot_hashes_t default_slot_hashes = { .hashes = slot_hashes };
847 0 : fd_sysvar_slot_hashes_init( slot_ctx, &default_slot_hashes );
848 0 : }
849 :
850 : /* Provide default stake history if not provided */
851 0 : if( !slot_ctx->sysvar_cache->has_stake_history ) {
852 : // Provide a 0-set default entry
853 0 : fd_stake_history_entry_t entry = {0};
854 0 : fd_sysvar_stake_history_init( slot_ctx );
855 0 : fd_sysvar_stake_history_update( slot_ctx, &entry, runner->spad );
856 0 : }
857 :
858 : /* Provide default last restart slot sysvar if not provided */
859 0 : if( !slot_ctx->sysvar_cache->has_last_restart_slot ) {
860 0 : fd_sysvar_last_restart_slot_init( slot_ctx );
861 0 : }
862 :
863 : /* Provide a default clock if not present */
864 0 : if( !slot_ctx->sysvar_cache->has_clock ) {
865 0 : fd_sysvar_clock_init( slot_ctx );
866 0 : fd_sysvar_clock_update( slot_ctx, runner->spad );
867 0 : }
868 :
869 : /* Epoch schedule and rent get set from the epoch bank */
870 0 : fd_sysvar_epoch_schedule_init( slot_ctx );
871 0 : fd_sysvar_rent_init( slot_ctx );
872 :
873 : /* Set the epoch rewards sysvar if partition epoch rewards feature is enabled
874 :
875 : TODO: The init parameters are not exactly conformant with Agave's epoch rewards sysvar. We should
876 : be calling `fd_begin_partitioned_rewards` with the same parameters as Agave. However,
877 : we just need the `active` field to be conformant due to a single Stake program check.
878 : THIS MAY CHANGE IN THE FUTURE. If there are other parts of transaction execution that use
879 : the epoch rewards sysvar, we may need to update this.
880 : */
881 0 : if( (FD_FEATURE_ACTIVE( slot_ctx->slot_bank.slot, slot_ctx->epoch_ctx->features, enable_partitioned_epoch_reward ) ||
882 0 : FD_FEATURE_ACTIVE( slot_ctx->slot_bank.slot, slot_ctx->epoch_ctx->features, partitioned_epoch_rewards_superfeature ))
883 0 : && !slot_ctx->sysvar_cache->has_epoch_rewards ) {
884 0 : fd_point_value_t point_value = {0};
885 0 : fd_hash_t const * last_hash = test_ctx->blockhash_queue_count > 0 ? (fd_hash_t const *)test_ctx->blockhash_queue[0]->bytes : (fd_hash_t const *)empty_bytes;
886 0 : fd_sysvar_epoch_rewards_init( slot_ctx, 0UL, 0UL, 2UL, 1UL, point_value, last_hash);
887 0 : }
888 :
889 : /* Restore sysvar cache (again, since we may need to provide default sysvars) */
890 0 : fd_sysvar_cache_restore( slot_ctx->sysvar_cache, acc_mgr, funk_txn, runner->spad, fd_wksp_containing( slot_ctx ) );
891 :
892 : /* A NaN rent exemption threshold is U.B. in Solana Labs */
893 0 : fd_rent_t const * rent = (fd_rent_t const *)fd_sysvar_cache_rent( slot_ctx->sysvar_cache );
894 0 : if( ( !fd_double_is_normal( rent->exemption_threshold ) ) |
895 0 : ( rent->exemption_threshold < 0.0 ) |
896 0 : ( rent->exemption_threshold > 999.0 ) |
897 0 : ( rent->lamports_per_uint8_year > UINT_MAX ) |
898 0 : ( rent->burn_percent > 100 ) ) {
899 0 : fd_funk_end_write( runner->funk );
900 0 : return NULL;
901 0 : }
902 :
903 : /* Blockhash queue is given in txn message. We need to populate the following two fields:
904 : - slot_ctx->slot_bank.block_hash_queue
905 : - slot_ctx->slot_bank.recent_block_hashes */
906 0 : ulong num_blockhashes = test_ctx->blockhash_queue_count;
907 :
908 : /* Blockhash queue init */
909 0 : slot_ctx->slot_bank.block_hash_queue.max_age = FD_BLOCKHASH_QUEUE_MAX_ENTRIES;
910 0 : slot_ctx->slot_bank.block_hash_queue.ages_root = NULL;
911 0 : uchar * pool_mem = fd_spad_alloc( runner->spad, fd_hash_hash_age_pair_t_map_align(), fd_hash_hash_age_pair_t_map_footprint( 400 ) );
912 0 : slot_ctx->slot_bank.block_hash_queue.ages_pool = fd_hash_hash_age_pair_t_map_join( fd_hash_hash_age_pair_t_map_new( pool_mem, 400 ) );
913 0 : slot_ctx->slot_bank.block_hash_queue.last_hash = fd_spad_alloc( runner->spad, FD_HASH_ALIGN, FD_HASH_FOOTPRINT );
914 :
915 : // Save lamports per signature for most recent blockhash, if sysvar cache contains recent block hashes
916 0 : fd_recent_block_hashes_global_t const * rbh_global = fd_sysvar_cache_recent_block_hashes( slot_ctx->sysvar_cache );
917 0 : fd_recent_block_hashes_t rbh[1];
918 0 : if( rbh_global ) {
919 0 : fd_bincode_decode_ctx_t decode = { .wksp = fd_wksp_containing( runner->spad ) };
920 0 : fd_recent_block_hashes_convert_global_to_local( rbh_global, rbh, &decode );
921 0 : }
922 :
923 0 : if( rbh_global && !deq_fd_block_block_hash_entry_t_empty( rbh->hashes ) ) {
924 0 : fd_block_block_hash_entry_t const * last = deq_fd_block_block_hash_entry_t_peek_head_const( rbh->hashes );
925 0 : if( last && last->fee_calculator.lamports_per_signature!=0UL ) {
926 0 : slot_ctx->slot_bank.lamports_per_signature = last->fee_calculator.lamports_per_signature;
927 0 : slot_ctx->prev_lamports_per_signature = last->fee_calculator.lamports_per_signature;
928 0 : }
929 0 : }
930 :
931 : // Blockhash_queue[end] = last (latest) hash
932 : // Blockhash_queue[0] = genesis hash
933 0 : if( num_blockhashes > 0 ) {
934 0 : memcpy( &epoch_bank->genesis_hash, test_ctx->blockhash_queue[0]->bytes, sizeof(fd_hash_t) );
935 :
936 0 : for( ulong i = 0; i < num_blockhashes; ++i ) {
937 : // Recent block hashes cap is 150 (actually 151), while blockhash queue capacity is 300 (actually 301)
938 0 : fd_block_block_hash_entry_t blockhash_entry;
939 0 : memcpy( &blockhash_entry.blockhash, test_ctx->blockhash_queue[i]->bytes, sizeof(fd_hash_t) );
940 0 : slot_ctx->slot_bank.poh = blockhash_entry.blockhash;
941 0 : fd_sysvar_recent_hashes_update( slot_ctx, runner->spad );
942 0 : }
943 0 : } else {
944 : // Add a default empty blockhash and use it as genesis
945 0 : num_blockhashes = 1;
946 0 : memcpy( &epoch_bank->genesis_hash, empty_bytes, sizeof(fd_hash_t) );
947 0 : fd_block_block_hash_entry_t blockhash_entry;
948 0 : memcpy( &blockhash_entry.blockhash, empty_bytes, sizeof(fd_hash_t) );
949 0 : slot_ctx->slot_bank.poh = blockhash_entry.blockhash;
950 0 : fd_sysvar_recent_hashes_update( slot_ctx, runner->spad );
951 0 : }
952 0 : fd_sysvar_cache_restore_recent_block_hashes( slot_ctx->sysvar_cache, acc_mgr, funk_txn, runner->spad, fd_wksp_containing( slot_ctx ) );
953 :
954 0 : fd_funk_end_write( runner->funk );
955 :
956 : /* Create the raw txn (https://solana.com/docs/core/transactions#transaction-size) */
957 0 : uchar * txn_raw_begin = fd_spad_alloc( runner->spad, alignof(uchar), 1232 );
958 0 : ushort instr_count, addr_table_cnt;
959 0 : ulong msg_sz = _serialize_txn( txn_raw_begin, &test_ctx->tx, &instr_count, &addr_table_cnt );
960 0 : if( FD_UNLIKELY( msg_sz==ULONG_MAX ) ) {
961 0 : return NULL;
962 0 : }
963 :
964 : /* Set up txn descriptor from raw data */
965 0 : fd_txn_t * txn_descriptor = (fd_txn_t *) fd_spad_alloc( runner->spad, fd_txn_align(), fd_txn_footprint( instr_count, addr_table_cnt ) );
966 0 : if( FD_UNLIKELY( !fd_txn_parse( txn_raw_begin, msg_sz, txn_descriptor, NULL ) ) ) {
967 0 : return NULL;
968 0 : }
969 :
970 : /* Run txn preparation phases and execution
971 : NOTE: This should be modified accordingly if transaction setup logic changes */
972 0 : fd_txn_p_t * txn = fd_spad_alloc( runner->spad, alignof(fd_txn_p_t), sizeof(fd_txn_p_t) );
973 0 : memcpy( txn->payload, txn_raw_begin, msg_sz );
974 0 : txn->payload_sz = msg_sz;
975 0 : txn->flags = FD_TXN_P_FLAGS_SANITIZE_SUCCESS;
976 0 : memcpy( txn->_, txn_descriptor, fd_txn_footprint( instr_count, addr_table_cnt ) );
977 :
978 0 : fd_execute_txn_task_info_t * task_info = fd_spad_alloc( runner->spad, alignof(fd_execute_txn_task_info_t), sizeof(fd_execute_txn_task_info_t) );
979 0 : memset( task_info, 0, sizeof(fd_execute_txn_task_info_t) );
980 0 : task_info->txn = txn;
981 0 : task_info->txn_ctx = fd_spad_alloc( runner->spad, FD_EXEC_TXN_CTX_ALIGN, FD_EXEC_TXN_CTX_FOOTPRINT );
982 :
983 0 : fd_tpool_t tpool[1];
984 0 : tpool->worker_cnt = 1;
985 0 : tpool->worker_max = 1;
986 :
987 0 : fd_runtime_prepare_txns_start( slot_ctx, task_info, txn, 1UL, runner->spad );
988 :
989 : /* Setup the spad for account allocation */
990 0 : task_info->txn_ctx->spad = runner->spad;
991 :
992 0 : fd_runtime_pre_execute_check( task_info );
993 :
994 0 : if( task_info->txn->flags & FD_TXN_P_FLAGS_SANITIZE_SUCCESS ) {
995 0 : task_info->txn->flags |= FD_TXN_P_FLAGS_EXECUTE_SUCCESS;
996 0 : task_info->exec_res = fd_execute_txn( task_info );
997 0 : }
998 :
999 0 : slot_ctx->slot_bank.collected_execution_fees += task_info->txn_ctx->execution_fee;
1000 0 : slot_ctx->slot_bank.collected_priority_fees += task_info->txn_ctx->priority_fee;
1001 0 : slot_ctx->slot_bank.collected_rent += task_info->txn_ctx->collected_rent;
1002 0 : return task_info;
1003 0 : }
1004 :
1005 : static int
1006 : _block_context_create_and_exec( fd_exec_instr_test_runner_t * runner,
1007 : fd_exec_slot_ctx_t * slot_ctx,
1008 0 : fd_exec_test_block_context_t const * test_ctx ) {
1009 0 : fd_funk_t * funk = runner->funk;
1010 :
1011 : /* Generate unique ID for funk txn */
1012 0 : fd_funk_txn_xid_t xid[1] = {0};
1013 0 : xid[0] = fd_funk_generate_xid();
1014 :
1015 : /* Create temporary funk transaction and slot / epoch contexts */
1016 0 : fd_funk_start_write( runner->funk );
1017 0 : fd_funk_txn_t * funk_txn = fd_funk_txn_prepare( funk, NULL, xid, 1 );
1018 0 : fd_funk_end_write( runner->funk );
1019 :
1020 : /* Allocate contexts */
1021 0 : ulong vote_acct_max = fd_ulong_max( 128UL,
1022 0 : fd_ulong_max( test_ctx->epoch_ctx.stake_accounts_count,
1023 0 : fd_ulong_max( test_ctx->epoch_ctx.vote_accounts_t_count,
1024 0 : fd_ulong_max( test_ctx->epoch_ctx.vote_accounts_t_1_count,
1025 0 : test_ctx->epoch_ctx.vote_accounts_t_2_count ) ) ) );
1026 0 : uchar * epoch_ctx_mem = fd_spad_alloc( runner->spad, 128UL, fd_exec_epoch_ctx_footprint( vote_acct_max ) );
1027 0 : fd_exec_epoch_ctx_t * epoch_ctx = fd_exec_epoch_ctx_join( fd_exec_epoch_ctx_new( epoch_ctx_mem, vote_acct_max ) );
1028 :
1029 : /* Create account manager */
1030 0 : fd_acc_mgr_t * acc_mgr = fd_acc_mgr_new( fd_spad_alloc( runner->spad, FD_ACC_MGR_ALIGN, FD_ACC_MGR_FOOTPRINT ), funk );
1031 :
1032 : /* Restore feature flags */
1033 0 : if( !_restore_feature_flags( epoch_ctx, &test_ctx->epoch_ctx.features ) ) {
1034 0 : return 1;
1035 0 : }
1036 :
1037 : /* Set up slot context */
1038 0 : slot_ctx->funk_txn = funk_txn;
1039 0 : slot_ctx->acc_mgr = acc_mgr;
1040 0 : slot_ctx->enable_exec_recording = 0;
1041 0 : slot_ctx->epoch_ctx = epoch_ctx;
1042 0 : slot_ctx->runtime_wksp = fd_wksp_containing( slot_ctx );
1043 0 : fd_memcpy( &slot_ctx->slot_bank.banks_hash, test_ctx->slot_ctx.parent_bank_hash, sizeof( fd_hash_t ) );
1044 :
1045 : /* Set up slot bank */
1046 0 : ulong slot = test_ctx->slot_ctx.slot;
1047 0 : fd_slot_bank_t * slot_bank = &slot_ctx->slot_bank;
1048 :
1049 0 : fd_memcpy( slot_bank->lthash.lthash, test_ctx->slot_ctx.parent_lt_hash, FD_LTHASH_LEN_BYTES );
1050 0 : slot_bank->slot = slot;
1051 0 : slot_bank->block_height = test_ctx->slot_ctx.block_height;
1052 0 : slot_bank->prev_slot = test_ctx->slot_ctx.prev_slot;
1053 0 : slot_bank->fee_rate_governor = (fd_fee_rate_governor_t) {
1054 0 : .target_lamports_per_signature = 10000UL,
1055 0 : .target_signatures_per_slot = 20000UL,
1056 0 : .min_lamports_per_signature = 5000UL,
1057 0 : .max_lamports_per_signature = 100000UL,
1058 0 : .burn_percent = 50,
1059 0 : };
1060 0 : slot_bank->capitalization = test_ctx->slot_ctx.prev_epoch_capitalization;
1061 :
1062 : /* Set up epoch context and epoch bank */
1063 : /* TODO: Do we need any more of these? */
1064 0 : fd_epoch_bank_t * epoch_bank = fd_exec_epoch_ctx_epoch_bank( epoch_ctx );
1065 :
1066 : // self.max_tick_height = (self.slot + 1) * self.ticks_per_slot;
1067 0 : epoch_bank->hashes_per_tick = test_ctx->epoch_ctx.hashes_per_tick;
1068 0 : epoch_bank->ticks_per_slot = test_ctx->epoch_ctx.ticks_per_slot;
1069 0 : epoch_bank->slots_per_year = test_ctx->epoch_ctx.slots_per_year;
1070 0 : epoch_bank->inflation = (fd_inflation_t) {
1071 0 : .initial = test_ctx->epoch_ctx.inflation.initial,
1072 0 : .terminal = test_ctx->epoch_ctx.inflation.terminal,
1073 0 : .taper = test_ctx->epoch_ctx.inflation.taper,
1074 0 : .foundation = test_ctx->epoch_ctx.inflation.foundation,
1075 0 : .foundation_term = test_ctx->epoch_ctx.inflation.foundation_term
1076 0 : };
1077 0 : epoch_bank->genesis_creation_time = test_ctx->epoch_ctx.genesis_creation_time;
1078 :
1079 : /* Load in all accounts provided in the context */
1080 0 : for( ushort i=0; i<test_ctx->acct_states_count; i++ ) {
1081 0 : FD_TXN_ACCOUNT_DECL(acc);
1082 0 : _load_txn_account( acc, acc_mgr, funk_txn, &test_ctx->acct_states[i], 0 );
1083 0 : }
1084 :
1085 : /* Restore sysvar cache */
1086 0 : fd_runtime_sysvar_cache_load( slot_ctx, runner->spad );
1087 :
1088 : /* Finish init epoch bank sysvars */
1089 0 : fd_memcpy( &epoch_bank->epoch_schedule, slot_ctx->sysvar_cache->val_epoch_schedule, sizeof(fd_epoch_schedule_t) );
1090 0 : fd_memcpy( &epoch_bank->rent_epoch_schedule, slot_ctx->sysvar_cache->val_epoch_schedule, sizeof(fd_epoch_schedule_t) );
1091 0 : fd_memcpy( &epoch_bank->rent, slot_ctx->sysvar_cache->val_rent, sizeof(fd_rent_t) );
1092 0 : epoch_bank->stakes.epoch = fd_slot_to_epoch( &epoch_bank->epoch_schedule, slot_bank->prev_slot, NULL );
1093 :
1094 : /* Update stake cache for epoch T */
1095 0 : for( uint i=0U; i<test_ctx->epoch_ctx.stake_accounts_count; i++ ) {
1096 0 : fd_pubkey_t voter_pubkey;
1097 0 : fd_memcpy( &voter_pubkey, test_ctx->epoch_ctx.stake_accounts[i].voter_pubkey, sizeof(fd_pubkey_t) );
1098 :
1099 0 : fd_pubkey_t stake_account;
1100 0 : fd_memcpy( &stake_account, test_ctx->epoch_ctx.stake_accounts[i].stake_account_pubkey, sizeof(fd_pubkey_t) );
1101 :
1102 0 : ulong stake = test_ctx->epoch_ctx.stake_accounts[i].stake;
1103 0 : ulong activation_epoch = test_ctx->epoch_ctx.stake_accounts[i].activation_epoch;
1104 0 : ulong deactivation_epoch = test_ctx->epoch_ctx.stake_accounts[i].deactivation_epoch;
1105 0 : double warmup_cooldown_rate = test_ctx->epoch_ctx.stake_accounts[i].warmup_cooldown_rate;
1106 :
1107 0 : fd_delegation_pair_t_mapnode_t * stake_node = fd_delegation_pair_t_map_acquire( epoch_bank->stakes.stake_delegations_pool );
1108 0 : fd_memcpy( &stake_node->elem.account, &stake_account, sizeof(fd_pubkey_t) );
1109 0 : fd_memcpy( &stake_node->elem.delegation.voter_pubkey, &voter_pubkey, sizeof(fd_pubkey_t) );
1110 0 : stake_node->elem.delegation.stake = stake;
1111 0 : stake_node->elem.delegation.activation_epoch = activation_epoch;
1112 0 : stake_node->elem.delegation.deactivation_epoch = deactivation_epoch;
1113 0 : stake_node->elem.delegation.warmup_cooldown_rate = warmup_cooldown_rate;
1114 :
1115 0 : fd_delegation_pair_t_map_insert( epoch_bank->stakes.stake_delegations_pool,
1116 0 : &epoch_bank->stakes.stake_delegations_root,
1117 0 : stake_node );
1118 0 : }
1119 :
1120 : /* Update vote cache for epoch T */
1121 0 : for( uint i=0U; i<test_ctx->epoch_ctx.vote_accounts_t_count; i++ ) {
1122 0 : fd_exec_test_acct_state_t * vote_account = &test_ctx->epoch_ctx.vote_accounts_t[i].vote_account;
1123 0 : ulong stake = test_ctx->epoch_ctx.vote_accounts_t[i].stake;
1124 :
1125 0 : fd_vote_accounts_pair_t_mapnode_t * vote_node = fd_vote_accounts_pair_t_map_acquire( epoch_bank->stakes.vote_accounts.vote_accounts_pool );
1126 0 : vote_node->elem.stake = stake;
1127 0 : fd_memcpy( &vote_node->elem.key, vote_account->address, sizeof(fd_pubkey_t) );
1128 0 : vote_node->elem.value.executable = vote_account->executable;
1129 0 : vote_node->elem.value.lamports = vote_account->lamports;
1130 0 : vote_node->elem.value.rent_epoch = vote_account->rent_epoch;
1131 0 : vote_node->elem.value.data_len = vote_account->data->size;
1132 0 : vote_node->elem.value.data = fd_spad_alloc( runner->spad, alignof(uchar), vote_account->data->size );
1133 0 : fd_memcpy( vote_node->elem.value.data, vote_account->data->bytes, vote_account->data->size );
1134 0 : fd_memcpy( &vote_node->elem.value.owner, vote_account->owner, sizeof(fd_pubkey_t) );
1135 :
1136 0 : fd_vote_accounts_pair_t_map_insert( epoch_bank->stakes.vote_accounts.vote_accounts_pool,
1137 0 : &epoch_bank->stakes.vote_accounts.vote_accounts_root,
1138 0 : vote_node );
1139 0 : }
1140 :
1141 : /* Update vote cache for epoch T-1 */
1142 0 : for( uint i=0U; i<test_ctx->epoch_ctx.vote_accounts_t_1_count; i++ ) {
1143 0 : fd_exec_test_acct_state_t * vote_account = &test_ctx->epoch_ctx.vote_accounts_t_1[i].vote_account;
1144 0 : ulong stake = test_ctx->epoch_ctx.vote_accounts_t_1[i].stake;
1145 :
1146 0 : fd_vote_accounts_pair_t_mapnode_t * vote_node = fd_vote_accounts_pair_t_map_acquire( epoch_bank->next_epoch_stakes.vote_accounts_pool );
1147 0 : vote_node->elem.stake = stake;
1148 0 : fd_memcpy( &vote_node->elem.key, vote_account->address, sizeof(fd_pubkey_t) );
1149 0 : vote_node->elem.value.executable = vote_account->executable;
1150 0 : vote_node->elem.value.lamports = vote_account->lamports;
1151 0 : vote_node->elem.value.rent_epoch = vote_account->rent_epoch;
1152 0 : vote_node->elem.value.data_len = vote_account->data->size;
1153 0 : vote_node->elem.value.data = fd_spad_alloc( runner->spad, alignof(uchar), vote_account->data->size );
1154 0 : fd_memcpy( vote_node->elem.value.data, vote_account->data->bytes, vote_account->data->size );
1155 0 : fd_memcpy( &vote_node->elem.value.owner, vote_account->owner, sizeof(fd_pubkey_t) );
1156 :
1157 0 : fd_vote_accounts_pair_t_map_insert( epoch_bank->next_epoch_stakes.vote_accounts_pool,
1158 0 : &epoch_bank->next_epoch_stakes.vote_accounts_root,
1159 0 : vote_node );
1160 0 : }
1161 :
1162 : /* Update vote cache for epoch T-2 */
1163 0 : uchar * pool_mem = fd_spad_alloc( runner->spad,
1164 0 : fd_vote_accounts_pair_t_map_align(),
1165 0 : fd_vote_accounts_pair_t_map_footprint( vote_acct_max ) );
1166 0 : slot_bank->epoch_stakes.vote_accounts_pool = fd_vote_accounts_pair_t_map_join( fd_vote_accounts_pair_t_map_new( pool_mem, vote_acct_max ) );
1167 0 : slot_bank->epoch_stakes.vote_accounts_root = NULL;
1168 0 : for( uint i=0U; i<test_ctx->epoch_ctx.vote_accounts_t_2_count; i++ ) {
1169 0 : fd_exec_test_acct_state_t * vote_account = &test_ctx->epoch_ctx.vote_accounts_t_2[i].vote_account;
1170 0 : ulong stake = test_ctx->epoch_ctx.vote_accounts_t_2[i].stake;
1171 :
1172 0 : fd_vote_accounts_pair_t_mapnode_t * vote_node = fd_vote_accounts_pair_t_map_acquire( slot_bank->epoch_stakes.vote_accounts_pool );
1173 0 : vote_node->elem.stake = stake;
1174 0 : fd_memcpy( &vote_node->elem.key, vote_account->address, sizeof(fd_pubkey_t) );
1175 0 : vote_node->elem.value.executable = vote_account->executable;
1176 0 : vote_node->elem.value.lamports = vote_account->lamports;
1177 0 : vote_node->elem.value.rent_epoch = vote_account->rent_epoch;
1178 0 : vote_node->elem.value.data_len = vote_account->data->size;
1179 0 : vote_node->elem.value.data = fd_spad_alloc( runner->spad, alignof(uchar), vote_account->data->size );
1180 0 : fd_memcpy( vote_node->elem.value.data, vote_account->data->bytes, vote_account->data->size );
1181 0 : fd_memcpy( &vote_node->elem.value.owner, vote_account->owner, sizeof(fd_pubkey_t) );
1182 :
1183 0 : fd_vote_accounts_pair_t_map_insert( slot_bank->epoch_stakes.vote_accounts_pool,
1184 0 : &slot_bank->epoch_stakes.vote_accounts_root,
1185 0 : vote_node );
1186 0 : }
1187 :
1188 : /* Initialize the current running epoch stake and vote accounts */
1189 0 : pool_mem = fd_spad_alloc( runner->spad,
1190 0 : fd_account_keys_pair_t_map_align(),
1191 0 : fd_account_keys_pair_t_map_footprint( vote_acct_max ) );
1192 0 : slot_bank->stake_account_keys.account_keys_pool = fd_account_keys_pair_t_map_join( fd_account_keys_pair_t_map_new( pool_mem, vote_acct_max ) );
1193 0 : slot_bank->stake_account_keys.account_keys_root = NULL;
1194 0 : for( uint i=0U; i<test_ctx->epoch_ctx.new_stake_accounts_count; i++ ) {
1195 0 : FD_TXN_ACCOUNT_DECL( acc );
1196 :
1197 0 : fd_pubkey_t stake_pubkey;
1198 0 : fd_memcpy( &stake_pubkey, test_ctx->epoch_ctx.new_stake_accounts[i]->bytes, sizeof(fd_pubkey_t) );
1199 :
1200 : // Fetch and store the stake account using acc mgr
1201 0 : if( fd_acc_mgr_view( slot_ctx->acc_mgr, slot_ctx->funk_txn, &stake_pubkey, acc ) ) {
1202 0 : continue;
1203 0 : }
1204 :
1205 0 : fd_store_stake_delegation( slot_ctx, acc );
1206 0 : }
1207 :
1208 0 : pool_mem = fd_spad_alloc( runner->spad,
1209 0 : fd_account_keys_pair_t_map_align(),
1210 0 : fd_account_keys_pair_t_map_footprint( vote_acct_max ) );
1211 0 : slot_bank->vote_account_keys.account_keys_pool = fd_account_keys_pair_t_map_join( fd_account_keys_pair_t_map_new( pool_mem, vote_acct_max ) );
1212 0 : slot_bank->vote_account_keys.account_keys_root = NULL;
1213 0 : for( uint i=0U; i<test_ctx->epoch_ctx.new_vote_accounts_count; i++ ) {
1214 0 : FD_TXN_ACCOUNT_DECL( acc );
1215 :
1216 0 : fd_pubkey_t vote_pubkey;
1217 0 : memcpy( &vote_pubkey, test_ctx->epoch_ctx.new_vote_accounts[i]->bytes, sizeof(fd_pubkey_t) );
1218 :
1219 : // Fetch and store the vote account from the acc mgr
1220 0 : if( fd_acc_mgr_view( slot_ctx->acc_mgr, slot_ctx->funk_txn, &vote_pubkey, acc ) ) {
1221 0 : continue;
1222 0 : }
1223 :
1224 0 : fd_vote_store_account( slot_ctx, acc );
1225 0 : }
1226 :
1227 : /* Update leader schedule */
1228 0 : fd_runtime_update_leaders( slot_ctx, slot_ctx->slot_bank.slot, runner->spad );
1229 :
1230 : /* Initialize the blockhash queue and recent blockhashes sysvar from the input blockhash queue */
1231 0 : slot_bank->block_hash_queue.max_age = FD_BLOCKHASH_QUEUE_MAX_ENTRIES; // Max age is fixed at 300
1232 0 : slot_bank->block_hash_queue.ages_root = NULL;
1233 0 : pool_mem = fd_spad_alloc( runner->spad, fd_hash_hash_age_pair_t_map_align(), fd_hash_hash_age_pair_t_map_footprint( FD_BLOCKHASH_QUEUE_MAX_ENTRIES+1UL ) );
1234 0 : slot_bank->block_hash_queue.ages_pool = fd_hash_hash_age_pair_t_map_join( fd_hash_hash_age_pair_t_map_new( pool_mem, FD_BLOCKHASH_QUEUE_MAX_ENTRIES+1UL ) );
1235 0 : slot_bank->block_hash_queue.last_hash = fd_valloc_malloc( fd_spad_virtual( runner->spad ), FD_HASH_ALIGN, FD_HASH_FOOTPRINT );
1236 :
1237 : /* TODO: Restore this from input */
1238 0 : pool_mem = fd_spad_alloc( runner->spad, fd_clock_timestamp_vote_t_map_align(), fd_clock_timestamp_vote_t_map_footprint( 10000UL ) );
1239 0 : slot_bank->timestamp_votes.votes_pool = fd_clock_timestamp_vote_t_map_join( fd_clock_timestamp_vote_t_map_new( pool_mem, 10000UL ) );
1240 0 : slot_bank->timestamp_votes.votes_root = NULL;
1241 :
1242 : /* TODO: We might need to load this in from the input. We also need to size this out for worst case, but this also blows up the memory requirement. */
1243 : /* Allocate all the memory for the rent fresh accounts lists */
1244 0 : slot_ctx->rent_fresh_accounts.partitions_root = NULL;
1245 0 : slot_ctx->rent_fresh_accounts.partitions_pool = fd_rent_fresh_accounts_partition_t_map_join(
1246 0 : fd_rent_fresh_accounts_partition_t_map_new(
1247 0 : fd_spad_alloc(
1248 0 : runner->spad,
1249 0 : fd_rent_fresh_accounts_partition_t_map_align(),
1250 0 : fd_rent_fresh_accounts_partition_t_map_footprint( 100UL * 2UL ) ), /* MAX_SLOTS_PER_EPOCH * 2 */
1251 0 : 100UL * 2UL
1252 0 : )
1253 0 : );
1254 0 : for( ulong i = 0; i < 100UL * 2UL; i++ ) {
1255 0 : ulong partition = i;
1256 0 : fd_rent_fresh_accounts_partition_t_mapnode_t * new_node = fd_rent_fresh_accounts_partition_t_map_acquire(
1257 0 : slot_ctx->rent_fresh_accounts.partitions_pool
1258 0 : );
1259 0 : if( FD_UNLIKELY(( new_node == NULL )) ) {
1260 0 : FD_LOG_ERR(( "fd_rent_fresh_accounts_partition_t_map_acquire failed" ));
1261 0 : }
1262 :
1263 0 : new_node->elem.partition = partition;
1264 0 : new_node->elem.accounts_root = NULL;
1265 0 : new_node->elem.accounts_pool = fd_pubkey_node_t_map_join( fd_pubkey_node_t_map_new(
1266 0 : fd_spad_alloc( runner->spad, fd_pubkey_node_t_map_align(), fd_pubkey_node_t_map_footprint( 100 ) ),
1267 0 : 100
1268 0 : ) );
1269 0 : fd_rent_fresh_accounts_partition_t_map_insert(
1270 0 : slot_ctx->rent_fresh_accounts.partitions_pool,
1271 0 : &slot_ctx->rent_fresh_accounts.partitions_root,
1272 0 : new_node
1273 0 : );
1274 0 : }
1275 :
1276 : // Set genesis hash to {0}
1277 0 : fd_memset( &epoch_bank->genesis_hash, 0, sizeof(fd_hash_t) );
1278 0 : fd_memset( slot_bank->block_hash_queue.last_hash, 0, sizeof(fd_hash_t) );
1279 :
1280 0 : fd_funk_start_write( runner->funk );
1281 :
1282 : // Use the latest lamports per signature
1283 0 : fd_recent_block_hashes_global_t const * rbh_global = fd_sysvar_cache_recent_block_hashes( slot_ctx->sysvar_cache );
1284 0 : fd_recent_block_hashes_t rbh[1];
1285 0 : if( rbh_global ) {
1286 0 : fd_bincode_decode_ctx_t decode = { .wksp = fd_wksp_containing( runner->spad ) };
1287 0 : fd_recent_block_hashes_convert_global_to_local( rbh_global, rbh, &decode );
1288 0 : }
1289 :
1290 0 : if( rbh_global && !deq_fd_block_block_hash_entry_t_empty( rbh->hashes ) ) {
1291 0 : fd_block_block_hash_entry_t const * last = deq_fd_block_block_hash_entry_t_peek_head_const( rbh->hashes );
1292 0 : if( last && last->fee_calculator.lamports_per_signature!=0UL ) {
1293 0 : slot_bank->lamports_per_signature = last->fee_calculator.lamports_per_signature;
1294 0 : slot_ctx->prev_lamports_per_signature = last->fee_calculator.lamports_per_signature;
1295 0 : }
1296 0 : }
1297 :
1298 : // Populate blockhash queue and recent blockhashes sysvar
1299 0 : for( ushort i=0; i<test_ctx->blockhash_queue_count; ++i ) {
1300 0 : fd_block_block_hash_entry_t blockhash_entry;
1301 0 : memcpy( &blockhash_entry.blockhash, test_ctx->blockhash_queue[i]->bytes, sizeof(fd_hash_t) );
1302 0 : slot_bank->poh = blockhash_entry.blockhash;
1303 0 : fd_sysvar_recent_hashes_update( slot_ctx, runner->spad );
1304 0 : }
1305 :
1306 : // Set the current poh from the input (we skip POH verification in this fuzzing target)
1307 0 : fd_memcpy( slot_ctx->slot_bank.poh.uc, test_ctx->slot_ctx.poh, FD_HASH_FOOTPRINT );
1308 :
1309 : /* Make a new funk transaction since we're done loading in accounts for context */
1310 0 : fd_funk_txn_xid_t fork_xid[1] = {0};
1311 0 : fork_xid[0] = fd_funk_generate_xid();
1312 0 : slot_ctx->funk_txn = fd_funk_txn_prepare( funk, slot_ctx->funk_txn, fork_xid, 1 );
1313 :
1314 : /* Calculate epoch account hash values. This sets epoch_bank.eah_{start_slot, stop_slot, interval} */
1315 0 : fd_calculate_epoch_accounts_hash_values( slot_ctx );
1316 :
1317 : /* Prepare raw transaction pointers and block / microblock infos */
1318 0 : ulong microblock_cnt = test_ctx->microblocks_count;
1319 :
1320 : // For fuzzing, we're using a single microblock batch that contains all microblocks
1321 0 : fd_runtime_block_info_t * block_info = fd_spad_alloc( runner->spad, alignof(fd_runtime_block_info_t), sizeof(fd_runtime_block_info_t) );
1322 0 : fd_microblock_batch_info_t * batch_info = fd_spad_alloc( runner->spad, alignof(fd_microblock_batch_info_t), sizeof(fd_microblock_batch_info_t) );
1323 0 : fd_microblock_info_t * microblock_infos = fd_spad_alloc( runner->spad, alignof(fd_microblock_info_t), microblock_cnt * sizeof(fd_microblock_info_t) );
1324 0 : fd_memset( block_info, 0, sizeof(fd_runtime_block_info_t) );
1325 0 : fd_memset( batch_info, 0, sizeof(fd_microblock_batch_info_t) );
1326 0 : fd_memset( microblock_infos, 0, microblock_cnt * sizeof(fd_microblock_info_t) );
1327 :
1328 0 : block_info->microblock_batch_cnt = 1UL;
1329 0 : block_info->microblock_cnt = microblock_cnt;
1330 0 : block_info->microblock_batch_infos = batch_info;
1331 :
1332 0 : batch_info->microblock_cnt = microblock_cnt;
1333 0 : batch_info->microblock_infos = microblock_infos;
1334 :
1335 0 : ulong batch_signature_cnt = 0UL;
1336 0 : ulong batch_txn_cnt = 0UL;
1337 0 : ulong batch_account_cnt = 0UL;
1338 :
1339 0 : for( ulong i=0UL; i<microblock_cnt; i++ ) {
1340 0 : fd_exec_test_microblock_t const * input_microblock = &test_ctx->microblocks[i];
1341 0 : fd_microblock_info_t * microblock_info = µblock_infos[i];
1342 0 : fd_microblock_hdr_t * microblock_hdr = fd_spad_alloc( runner->spad, alignof(fd_microblock_hdr_t), sizeof(fd_microblock_hdr_t) );
1343 0 : fd_memset( microblock_hdr, 0, sizeof(fd_microblock_hdr_t) );
1344 :
1345 0 : ulong txn_cnt = input_microblock->txns_count;
1346 0 : ulong signature_cnt = 0UL;
1347 0 : ulong account_cnt = 0UL;
1348 :
1349 0 : fd_txn_p_t * txn_ptrs = fd_spad_alloc( runner->spad, alignof(fd_txn_p_t), txn_cnt * sizeof(fd_txn_p_t) );
1350 :
1351 0 : for( ulong j=0UL; j<txn_cnt; j++ ) {
1352 0 : fd_txn_p_t * txn = &txn_ptrs[j];
1353 :
1354 0 : ushort _instr_count, _addr_table_cnt;
1355 0 : ulong msg_sz = _serialize_txn( txn->payload, &input_microblock->txns[j], &_instr_count, &_addr_table_cnt );
1356 :
1357 : // Reject any transactions over 1232 bytes
1358 0 : if( FD_UNLIKELY( msg_sz==ULONG_MAX ) ) {
1359 0 : return 1;
1360 0 : }
1361 0 : txn->payload_sz = msg_sz;
1362 :
1363 : // Reject any transactions that cannot be parsed
1364 0 : if( FD_UNLIKELY( !fd_txn_parse( txn->payload, msg_sz, TXN( txn ), NULL ) ) ) {
1365 0 : return 1;
1366 0 : }
1367 :
1368 0 : signature_cnt += TXN( txn )->signature_cnt;
1369 0 : account_cnt += fd_txn_account_cnt( TXN( txn ), FD_TXN_ACCT_CAT_ALL );
1370 0 : }
1371 :
1372 0 : microblock_hdr->txn_cnt = txn_cnt;
1373 0 : microblock_info->microblock.raw = (uchar *)microblock_hdr;
1374 :
1375 0 : microblock_info->signature_cnt = signature_cnt;
1376 0 : microblock_info->account_cnt = account_cnt;
1377 0 : microblock_info->txns = txn_ptrs;
1378 :
1379 0 : batch_signature_cnt += signature_cnt;
1380 0 : batch_txn_cnt += txn_cnt;
1381 0 : batch_account_cnt += account_cnt;
1382 0 : }
1383 :
1384 0 : block_info->signature_cnt = batch_info->signature_cnt = batch_signature_cnt;
1385 0 : block_info->txn_cnt = batch_info->txn_cnt = batch_txn_cnt;
1386 0 : block_info->account_cnt = batch_info->account_cnt = batch_account_cnt;
1387 :
1388 0 : fd_funk_end_write( runner->funk );
1389 :
1390 : /* Initialize tpool and spad(s)
1391 : TODO: We should decide how many workers to use for the execution tpool. We might have a bunch of
1392 : transactions within a single block, but increasing the worker cnt increases the memory requirements by
1393 : 1.28 GB per additional worker (for spad memory allocation). We also fuzz block execution using
1394 : multiple cores, so it may be possible to get away with only 1 worker. Additionally, Agave will more than
1395 : likely always be the execution speed bottleneck, so we can play around with numbers and see what yields
1396 : the best results. */
1397 0 : ulong worker_max = 2UL;
1398 0 : void * tpool_mem = fd_spad_alloc( runner->spad, FD_TPOOL_ALIGN, FD_TPOOL_FOOTPRINT( worker_max ) );
1399 0 : fd_tpool_t * tpool = fd_tpool_init( tpool_mem, worker_max );
1400 0 : fd_tpool_worker_push( tpool, 1UL, NULL, 0UL );
1401 :
1402 0 : fd_spad_t * runtime_spad = runner->spad;
1403 :
1404 : /* Format chunks of memory for the exec spads
1405 : TODO: This memory needs a better bound. */
1406 0 : fd_spad_t * exec_spads[2] = { 0 };
1407 0 : ulong exec_spads_cnt = 2UL;
1408 0 : ulong exec_spad_mem_max = 1UL << 30;
1409 0 : for( ulong i=0UL; i<worker_max; i++ ) {
1410 0 : void * exec_spad_mem = fd_spad_alloc( runtime_spad, FD_SPAD_ALIGN, FD_SPAD_FOOTPRINT( exec_spad_mem_max ) );
1411 0 : fd_spad_t * exec_spad = fd_spad_join( fd_spad_new( exec_spad_mem, exec_spad_mem_max ) );
1412 0 : exec_spads[i] = exec_spad;
1413 0 : }
1414 :
1415 : // Prepare. Execute. Finalize.
1416 0 : int res = 0UL;
1417 0 : FD_SPAD_FRAME_BEGIN( runtime_spad ) {
1418 0 : fd_rewards_recalculate_partitioned_rewards( slot_ctx, tpool, exec_spads, exec_spads_cnt, runtime_spad );
1419 :
1420 : /* Process new epoch may push a new spad frame onto the runtime spad. We should make sure this frame gets
1421 : cleared (if it was allocated) before executing the block. */
1422 0 : ulong spad_frame_ct = fd_spad_frame_used( runtime_spad );
1423 0 : fd_runtime_block_pre_execute_process_new_epoch( slot_ctx, tpool, exec_spads, exec_spads_cnt, runtime_spad );
1424 0 : while( fd_spad_frame_used( runtime_spad )>spad_frame_ct ) {
1425 0 : fd_spad_pop( runtime_spad );
1426 0 : }
1427 :
1428 0 : res = fd_runtime_block_execute_tpool( slot_ctx, NULL, block_info, tpool, exec_spads, exec_spads_cnt, runtime_spad );
1429 0 : } FD_SPAD_FRAME_END;
1430 :
1431 0 : fd_tpool_worker_pop( tpool );
1432 :
1433 0 : return res;
1434 0 : }
1435 :
1436 : void
1437 : fd_exec_test_instr_context_destroy( fd_exec_instr_test_runner_t * runner,
1438 0 : fd_exec_instr_ctx_t * ctx ) {
1439 0 : if( !ctx ) return;
1440 0 : fd_acc_mgr_t * acc_mgr = ctx->txn_ctx->acc_mgr;
1441 0 : fd_funk_txn_t * funk_txn = ctx->txn_ctx->funk_txn;
1442 :
1443 0 : fd_acc_mgr_delete( acc_mgr );
1444 :
1445 0 : fd_funk_start_write( runner->funk );
1446 0 : fd_funk_txn_cancel( runner->funk, funk_txn, 1 );
1447 0 : fd_funk_end_write( runner->funk );
1448 0 : }
1449 :
1450 : static void
1451 : _txn_context_destroy( fd_exec_instr_test_runner_t * runner,
1452 0 : fd_exec_slot_ctx_t * slot_ctx ) {
1453 0 : if( !slot_ctx ) return; // This shouldn't be false either
1454 0 : fd_acc_mgr_t * acc_mgr = slot_ctx->acc_mgr;
1455 0 : fd_funk_txn_t * funk_txn = slot_ctx->funk_txn;
1456 :
1457 0 : fd_acc_mgr_delete( acc_mgr );
1458 :
1459 0 : fd_funk_start_write( runner->funk );
1460 0 : fd_funk_txn_cancel( runner->funk, funk_txn, 1 );
1461 0 : fd_funk_end_write( runner->funk );
1462 0 : }
1463 :
1464 : static void
1465 : _block_context_destroy( fd_exec_instr_test_runner_t * runner,
1466 : fd_exec_slot_ctx_t * slot_ctx,
1467 : fd_wksp_t * wksp,
1468 0 : fd_alloc_t * alloc ) {
1469 0 : if( !slot_ctx ) return; // This shouldn't be false either
1470 0 : fd_acc_mgr_t * acc_mgr = slot_ctx->acc_mgr;
1471 :
1472 0 : fd_acc_mgr_delete( acc_mgr );
1473 :
1474 0 : fd_wksp_free_laddr( fd_alloc_delete( fd_alloc_leave( alloc ) ) );
1475 0 : fd_wksp_detach( wksp );
1476 :
1477 0 : fd_funk_start_write( runner->funk );
1478 0 : fd_funk_txn_cancel_all( runner->funk, 1 );
1479 0 : fd_funk_end_write( runner->funk );
1480 0 : }
1481 :
1482 : static fd_sbpf_syscalls_t *
1483 : lookup_syscall_func( fd_sbpf_syscalls_t *syscalls,
1484 : const char *syscall_name,
1485 0 : size_t len) {
1486 0 : ulong i;
1487 :
1488 0 : if (!syscall_name) return NULL;
1489 :
1490 0 : for (i = 0; i < fd_sbpf_syscalls_slot_cnt(); ++i) {
1491 0 : if (!fd_sbpf_syscalls_key_inval(syscalls[i].key) && syscalls[i].name && strlen(syscalls[i].name) == len) {
1492 0 : if (!memcmp(syscalls[i].name, syscall_name, len)) {
1493 0 : return syscalls + i;
1494 0 : }
1495 0 : }
1496 0 : }
1497 :
1498 0 : return NULL;
1499 0 : }
1500 :
1501 : ulong
1502 : fd_exec_instr_test_run( fd_exec_instr_test_runner_t * runner,
1503 : void const * input_,
1504 : void ** output_,
1505 : void * output_buf,
1506 0 : ulong output_bufsz ) {
1507 0 : fd_exec_test_instr_context_t const * input = fd_type_pun_const( input_ );
1508 0 : fd_exec_test_instr_effects_t ** output = fd_type_pun( output_ );
1509 :
1510 0 : FD_SPAD_FRAME_BEGIN( runner->spad ) {
1511 :
1512 : /* Convert the Protobuf inputs to a fd_exec context */
1513 0 : fd_exec_instr_ctx_t ctx[1];
1514 0 : if( !fd_exec_test_instr_context_create( runner, ctx, input, false ) ) {
1515 0 : fd_exec_test_instr_context_destroy( runner, ctx );
1516 0 : return 0UL;
1517 0 : }
1518 :
1519 0 : fd_instr_info_t * instr = (fd_instr_info_t *) ctx->instr;
1520 :
1521 : /* Execute the test */
1522 0 : int exec_result = fd_execute_instr(ctx->txn_ctx, instr);
1523 :
1524 : /* Allocate space to capture outputs */
1525 :
1526 0 : ulong output_end = (ulong)output_buf + output_bufsz;
1527 0 : FD_SCRATCH_ALLOC_INIT( l, output_buf );
1528 :
1529 0 : fd_exec_test_instr_effects_t * effects =
1530 0 : FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_exec_test_instr_effects_t),
1531 0 : sizeof (fd_exec_test_instr_effects_t) );
1532 0 : if( FD_UNLIKELY( _l > output_end ) ) {
1533 0 : fd_exec_test_instr_context_destroy( runner, ctx );
1534 0 : return 0UL;
1535 0 : }
1536 0 : fd_memset( effects, 0, sizeof(fd_exec_test_instr_effects_t) );
1537 :
1538 : /* Capture error code */
1539 :
1540 0 : effects->result = -exec_result;
1541 0 : effects->cu_avail = ctx->txn_ctx->compute_meter;
1542 :
1543 0 : if( exec_result == FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR ) {
1544 0 : effects->custom_err = ctx->txn_ctx->custom_err;
1545 0 : }
1546 :
1547 : /* Allocate space for captured accounts */
1548 0 : ulong modified_acct_cnt = ctx->txn_ctx->accounts_cnt;
1549 :
1550 0 : fd_exec_test_acct_state_t * modified_accts =
1551 0 : FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_exec_test_acct_state_t),
1552 0 : sizeof (fd_exec_test_acct_state_t) * modified_acct_cnt );
1553 0 : if( FD_UNLIKELY( _l > output_end ) ) {
1554 0 : fd_exec_test_instr_context_destroy( runner, ctx );
1555 0 : return 0;
1556 0 : }
1557 0 : effects->modified_accounts = modified_accts;
1558 0 : effects->modified_accounts_count = 0UL;
1559 :
1560 : /* Capture borrowed accounts */
1561 :
1562 0 : for( ulong j=0UL; j < ctx->txn_ctx->accounts_cnt; j++ ) {
1563 0 : fd_txn_account_t * acc = &ctx->txn_ctx->accounts[j];
1564 0 : if( !acc->const_meta ) {
1565 0 : continue;
1566 0 : }
1567 :
1568 0 : ulong modified_idx = effects->modified_accounts_count;
1569 0 : assert( modified_idx < modified_acct_cnt );
1570 :
1571 0 : fd_exec_test_acct_state_t * out_acct = &effects->modified_accounts[ modified_idx ];
1572 0 : memset( out_acct, 0, sizeof(fd_exec_test_acct_state_t) );
1573 : /* Copy over account content */
1574 :
1575 0 : memcpy( out_acct->address, acc->pubkey, sizeof(fd_pubkey_t) );
1576 0 : out_acct->lamports = acc->const_meta->info.lamports;
1577 0 : if( acc->const_meta->dlen>0UL ) {
1578 0 : out_acct->data =
1579 0 : FD_SCRATCH_ALLOC_APPEND( l, alignof(pb_bytes_array_t),
1580 0 : PB_BYTES_ARRAY_T_ALLOCSIZE( acc->const_meta->dlen ) );
1581 0 : if( FD_UNLIKELY( _l > output_end ) ) {
1582 0 : fd_exec_test_instr_context_destroy( runner, ctx );
1583 0 : return 0UL;
1584 0 : }
1585 0 : out_acct->data->size = (pb_size_t)acc->const_meta->dlen;
1586 0 : fd_memcpy( out_acct->data->bytes, acc->const_data, acc->const_meta->dlen );
1587 0 : }
1588 :
1589 0 : out_acct->executable = acc->const_meta->info.executable;
1590 0 : out_acct->rent_epoch = acc->const_meta->info.rent_epoch;
1591 0 : memcpy( out_acct->owner, acc->const_meta->info.owner, sizeof(fd_pubkey_t) );
1592 :
1593 0 : effects->modified_accounts_count++;
1594 0 : }
1595 :
1596 : /* Capture return data */
1597 0 : fd_txn_return_data_t * return_data = &ctx->txn_ctx->return_data;
1598 0 : if( return_data->len>0UL ) {
1599 0 : effects->return_data = FD_SCRATCH_ALLOC_APPEND(l, alignof(pb_bytes_array_t),
1600 0 : PB_BYTES_ARRAY_T_ALLOCSIZE( return_data->len ) );
1601 0 : if( FD_UNLIKELY( _l > output_end ) ) {
1602 0 : fd_exec_test_instr_context_destroy( runner, ctx );
1603 0 : return 0UL;
1604 0 : }
1605 0 : effects->return_data->size = (pb_size_t)return_data->len;
1606 0 : fd_memcpy( effects->return_data->bytes, return_data->data, return_data->len );
1607 0 : }
1608 :
1609 0 : ulong actual_end = FD_SCRATCH_ALLOC_FINI( l, 1UL );
1610 0 : fd_exec_test_instr_context_destroy( runner, ctx );
1611 :
1612 0 : *output = effects;
1613 0 : return actual_end - (ulong)output_buf;
1614 :
1615 0 : } FD_SPAD_FRAME_END;
1616 0 : }
1617 :
1618 : ulong
1619 : fd_exec_block_test_run( fd_exec_instr_test_runner_t * runner,
1620 : void const * input_,
1621 : void ** output_,
1622 : void * output_buf,
1623 0 : ulong output_bufsz ) {
1624 0 : fd_exec_test_block_context_t const * input = fd_type_pun_const( input_ );
1625 0 : fd_exec_test_block_effects_t ** output = fd_type_pun( output_ );
1626 :
1627 0 : FD_SPAD_FRAME_BEGIN( runner->spad ) {
1628 : /* Initialize memory */
1629 0 : fd_wksp_t * wksp = fd_wksp_attach( "wksp" );
1630 0 : fd_alloc_t * alloc = fd_alloc_join( fd_alloc_new( fd_wksp_alloc_laddr( wksp, fd_alloc_align(), fd_alloc_footprint(), 2 ), 2 ), 0 );
1631 0 : uchar * slot_ctx_mem = fd_spad_alloc( runner->spad, FD_EXEC_SLOT_CTX_ALIGN, FD_EXEC_SLOT_CTX_FOOTPRINT );
1632 0 : fd_exec_slot_ctx_t * slot_ctx = fd_exec_slot_ctx_join ( fd_exec_slot_ctx_new ( slot_ctx_mem, runner->spad ) );
1633 :
1634 0 : int res = _block_context_create_and_exec( runner, slot_ctx, input );
1635 0 : if( res>0 ) {
1636 0 : _block_context_destroy( runner, slot_ctx, wksp, alloc );
1637 0 : return 0;
1638 0 : }
1639 :
1640 : /* Start saving block exec results */
1641 0 : FD_SCRATCH_ALLOC_INIT( l, output_buf );
1642 0 : ulong output_end = (ulong)output_buf + output_bufsz;
1643 :
1644 0 : fd_exec_test_block_effects_t * effects =
1645 0 : FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_exec_test_block_effects_t),
1646 0 : sizeof (fd_exec_test_block_effects_t) );
1647 0 : if( FD_UNLIKELY( _l > output_end ) ) {
1648 0 : abort();
1649 0 : }
1650 0 : fd_memset( effects, 0, sizeof(fd_exec_test_block_effects_t) );
1651 :
1652 : /* Capture capitalization */
1653 0 : effects->slot_capitalization = slot_ctx->slot_bank.capitalization;
1654 :
1655 : /* Capture hashes */
1656 0 : uchar out_lt_hash[32];
1657 0 : fd_lthash_hash( (fd_lthash_value_t const *)slot_ctx->slot_bank.lthash.lthash, out_lt_hash );
1658 0 : fd_memcpy( effects->bank_hash, slot_ctx->slot_bank.banks_hash.hash, sizeof(fd_hash_t) );
1659 0 : fd_memcpy( effects->lt_hash, out_lt_hash, sizeof(fd_hash_t) );
1660 0 : fd_memcpy( effects->account_delta_hash, slot_ctx->account_delta_hash.hash, sizeof(fd_hash_t) );
1661 :
1662 : /* Capture accounts. Since the only input accounts list comes from the input transactions, we have to iterate through
1663 : all input transactions, gather the account keys in order, and skip any duplicate accounts. */
1664 : // TODO: implement me
1665 :
1666 0 : ulong actual_end = FD_SCRATCH_ALLOC_FINI( l, 1UL );
1667 0 : _block_context_destroy( runner, slot_ctx, wksp, alloc );
1668 :
1669 0 : *output = effects;
1670 0 : return actual_end - (ulong)output_buf;
1671 0 : } FD_SPAD_FRAME_END;
1672 0 : }
1673 :
1674 :
1675 : ulong
1676 : fd_exec_txn_test_run( fd_exec_instr_test_runner_t * runner, // Runner only contains funk instance, so we can borrow instr test runner
1677 : void const * input_,
1678 : void ** output_,
1679 : void * output_buf,
1680 0 : ulong output_bufsz ) {
1681 0 : fd_exec_test_txn_context_t const * input = fd_type_pun_const( input_ );
1682 0 : fd_exec_test_txn_result_t ** output = fd_type_pun( output_ );
1683 :
1684 0 : FD_SPAD_FRAME_BEGIN( runner->spad ) {
1685 :
1686 : /* Initialize memory */
1687 0 : uchar * slot_ctx_mem = fd_spad_alloc( runner->spad, FD_EXEC_SLOT_CTX_ALIGN, FD_EXEC_SLOT_CTX_FOOTPRINT );
1688 0 : fd_exec_slot_ctx_t * slot_ctx = fd_exec_slot_ctx_join( fd_exec_slot_ctx_new( slot_ctx_mem, runner->spad ) );
1689 :
1690 : /* Create and exec transaction */
1691 0 : fd_execute_txn_task_info_t * task_info = _txn_context_create_and_exec( runner, slot_ctx, input );
1692 0 : if( task_info == NULL ) {
1693 0 : _txn_context_destroy( runner, slot_ctx );
1694 0 : return 0UL;
1695 0 : }
1696 0 : fd_exec_txn_ctx_t * txn_ctx = task_info->txn_ctx;
1697 :
1698 0 : int exec_res = task_info->exec_res;
1699 :
1700 : /* Start saving txn exec results */
1701 0 : FD_SCRATCH_ALLOC_INIT( l, output_buf );
1702 0 : ulong output_end = (ulong)output_buf + output_bufsz;
1703 :
1704 0 : fd_exec_test_txn_result_t * txn_result =
1705 0 : FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_exec_test_txn_result_t),
1706 0 : sizeof (fd_exec_test_txn_result_t) );
1707 0 : if( FD_UNLIKELY( _l > output_end ) ) {
1708 0 : abort();
1709 0 : }
1710 0 : fd_memset( txn_result, 0, sizeof(fd_exec_test_txn_result_t) );
1711 :
1712 : /* Capture basic results fields */
1713 0 : txn_result->executed = task_info->txn->flags & FD_TXN_P_FLAGS_EXECUTE_SUCCESS;
1714 0 : txn_result->sanitization_error = !( task_info->txn->flags & FD_TXN_P_FLAGS_SANITIZE_SUCCESS );
1715 0 : txn_result->has_resulting_state = false;
1716 0 : txn_result->resulting_state.acct_states_count = 0;
1717 0 : txn_result->is_ok = !exec_res;
1718 0 : txn_result->status = (uint32_t) -exec_res;
1719 0 : txn_result->instruction_error = 0;
1720 0 : txn_result->instruction_error_index = 0;
1721 0 : txn_result->custom_error = 0;
1722 0 : txn_result->executed_units = txn_ctx->compute_unit_limit - txn_ctx->compute_meter;
1723 0 : txn_result->has_fee_details = false;
1724 :
1725 0 : if( txn_result->sanitization_error ) {
1726 0 : if( exec_res==FD_RUNTIME_TXN_ERR_INSTRUCTION_ERROR ) {
1727 : /* If exec_res was an instruction error and we have a sanitization error, it was a precompile error */
1728 0 : txn_result->instruction_error = (uint32_t) -txn_ctx->exec_err;
1729 0 : txn_result->instruction_error_index = (uint32_t) txn_ctx->instr_err_idx;
1730 :
1731 : /*
1732 : TODO: precompile error codes are not conformant, so we're ignoring custom error codes for them for now. This should be revisited in the future.
1733 : For now, only precompiles throw custom error codes, so we can ignore all custom error codes thrown in the sanitization phase. If this changes,
1734 : this logic will have to be revisited.
1735 :
1736 : if( task_info->txn_ctx->exec_err == FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR ) {
1737 : txn_result->custom_error = txn_ctx->custom_err;
1738 : }
1739 : */
1740 0 : }
1741 :
1742 0 : ulong actual_end = FD_SCRATCH_ALLOC_FINI( l, 1UL );
1743 0 : _txn_context_destroy( runner, slot_ctx );
1744 :
1745 0 : *output = txn_result;
1746 0 : return actual_end - (ulong)output_buf;
1747 :
1748 0 : } else {
1749 : /* Capture the instruction error code */
1750 0 : if( exec_res==FD_RUNTIME_TXN_ERR_INSTRUCTION_ERROR ) {
1751 0 : int instr_err_idx = txn_ctx->instr_err_idx;
1752 0 : int program_id_idx = txn_ctx->instr_infos[instr_err_idx].program_id;
1753 :
1754 0 : txn_result->instruction_error = (uint32_t) -txn_ctx->exec_err;
1755 0 : txn_result->instruction_error_index = (uint32_t) instr_err_idx;
1756 :
1757 : /* If the exec err was a custom instr error and came from a precompile instruction, don't capture the custom error code. */
1758 0 : if( txn_ctx->exec_err==FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR &&
1759 0 : fd_executor_lookup_native_precompile_program( &txn_ctx->accounts[ program_id_idx ] )==NULL ) {
1760 0 : txn_result->custom_error = txn_ctx->custom_err;
1761 0 : }
1762 0 : }
1763 0 : }
1764 :
1765 0 : txn_result->has_fee_details = true;
1766 0 : txn_result->fee_details.transaction_fee = slot_ctx->slot_bank.collected_execution_fees;
1767 0 : txn_result->fee_details.prioritization_fee = slot_ctx->slot_bank.collected_priority_fees;
1768 :
1769 : /* Rent is only collected on successfully loaded transactions */
1770 0 : txn_result->rent = txn_ctx->collected_rent;
1771 :
1772 0 : if( txn_ctx->return_data.len > 0 ) {
1773 0 : txn_result->return_data = FD_SCRATCH_ALLOC_APPEND( l, alignof(pb_bytes_array_t),
1774 0 : PB_BYTES_ARRAY_T_ALLOCSIZE( txn_ctx->return_data.len ) );
1775 0 : if( FD_UNLIKELY( _l > output_end ) ) {
1776 0 : abort();
1777 0 : }
1778 :
1779 0 : txn_result->return_data->size = (pb_size_t)txn_ctx->return_data.len;
1780 0 : fd_memcpy( txn_result->return_data->bytes, txn_ctx->return_data.data, txn_ctx->return_data.len );
1781 0 : }
1782 :
1783 : /* Allocate space for captured accounts */
1784 0 : ulong modified_acct_cnt = txn_ctx->accounts_cnt;
1785 :
1786 0 : txn_result->has_resulting_state = true;
1787 0 : txn_result->resulting_state.acct_states =
1788 0 : FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_exec_test_acct_state_t),
1789 0 : sizeof (fd_exec_test_acct_state_t) * modified_acct_cnt );
1790 0 : if( FD_UNLIKELY( _l > output_end ) ) {
1791 0 : abort();
1792 0 : }
1793 :
1794 : /* Capture borrowed accounts */
1795 0 : for( ulong j=0UL; j < txn_ctx->accounts_cnt; j++ ) {
1796 0 : fd_txn_account_t * acc = &txn_ctx->accounts[j];
1797 :
1798 : /* For fees-only transactions, only save the fee payer (and potentially the nonce) only */
1799 0 : if( task_info->txn->flags & FD_TXN_P_FLAGS_FEES_ONLY ) {
1800 0 : if( j!=FD_FEE_PAYER_TXN_IDX && j!=task_info->txn_ctx->nonce_account_idx_in_txn ) {
1801 0 : continue;
1802 0 : }
1803 0 : }
1804 :
1805 0 : if( !( fd_txn_account_is_writable_idx( txn_ctx, (int)j ) || j==FD_FEE_PAYER_TXN_IDX ) ) continue;
1806 0 : assert( acc->meta );
1807 :
1808 0 : ulong modified_idx = txn_result->resulting_state.acct_states_count;
1809 0 : assert( modified_idx < modified_acct_cnt );
1810 :
1811 0 : fd_exec_test_acct_state_t * out_acct = &txn_result->resulting_state.acct_states[ modified_idx ];
1812 0 : memset( out_acct, 0, sizeof(fd_exec_test_acct_state_t) );
1813 : /* Copy over account content */
1814 :
1815 0 : memcpy( out_acct->address, acc->pubkey, sizeof(fd_pubkey_t) );
1816 :
1817 0 : out_acct->lamports = acc->const_meta->info.lamports;
1818 :
1819 0 : if( acc->const_meta->dlen > 0 ) {
1820 0 : out_acct->data =
1821 0 : FD_SCRATCH_ALLOC_APPEND( l, alignof(pb_bytes_array_t),
1822 0 : PB_BYTES_ARRAY_T_ALLOCSIZE( acc->const_meta->dlen ) );
1823 0 : if( FD_UNLIKELY( _l > output_end ) ) {
1824 0 : abort();
1825 0 : }
1826 0 : out_acct->data->size = (pb_size_t)acc->const_meta->dlen;
1827 0 : fd_memcpy( out_acct->data->bytes, acc->const_data, acc->const_meta->dlen );
1828 0 : }
1829 :
1830 0 : out_acct->executable = acc->const_meta->info.executable;
1831 0 : out_acct->rent_epoch = acc->const_meta->info.rent_epoch;
1832 0 : memcpy( out_acct->owner, acc->const_meta->info.owner, sizeof(fd_pubkey_t) );
1833 :
1834 0 : txn_result->resulting_state.acct_states_count++;
1835 0 : }
1836 :
1837 0 : ulong actual_end = FD_SCRATCH_ALLOC_FINI( l, 1UL );
1838 0 : _txn_context_destroy( runner, slot_ctx );
1839 :
1840 0 : *output = txn_result;
1841 0 : return actual_end - (ulong)output_buf;
1842 0 : } FD_SPAD_FRAME_END;
1843 0 : }
1844 :
1845 :
1846 : ulong
1847 : fd_sbpf_program_load_test_run( fd_exec_instr_test_runner_t * runner,
1848 : void const * input_,
1849 : void ** output_,
1850 : void * output_buf,
1851 0 : ulong output_bufsz ) {
1852 0 : fd_exec_test_elf_loader_ctx_t const * input = fd_type_pun_const( input_ );
1853 0 : fd_exec_test_elf_loader_effects_t ** output = fd_type_pun( output_ );
1854 :
1855 0 : fd_sbpf_elf_info_t info;
1856 0 : fd_valloc_t valloc = fd_spad_virtual( runner->spad );
1857 :
1858 0 : if ( FD_UNLIKELY( !input->has_elf || !input->elf.data ) ){
1859 0 : return 0UL;
1860 0 : }
1861 :
1862 0 : ulong elf_sz = input->elf_sz;
1863 0 : void const * _bin;
1864 :
1865 : /* elf_sz will be passed as arguments to elf loader functions.
1866 : pb decoder allocates memory for elf.data based on its actual size,
1867 : not elf_sz !.
1868 : If elf_sz is larger than the size of actual elf data, this may result
1869 : in out-of-bounds accesses which will upset ASAN (however intentional).
1870 : So in this case we just copy the data into a memory region of elf_sz bytes
1871 :
1872 : ! The decoupling of elf_sz and the actual binary size is intentional to test
1873 : underflow/overflow behavior */
1874 0 : if ( elf_sz > input->elf.data->size ){
1875 0 : void * tmp = fd_valloc_malloc( valloc, 1UL, elf_sz );
1876 0 : if ( FD_UNLIKELY( !tmp ) ){
1877 0 : return 0UL;
1878 0 : }
1879 0 : fd_memcpy( tmp, input->elf.data->bytes, input->elf.data->size );
1880 0 : _bin = tmp;
1881 0 : } else {
1882 0 : _bin = input->elf.data->bytes;
1883 0 : }
1884 :
1885 : // Allocate space for captured effects
1886 0 : ulong output_end = (ulong)output_buf + output_bufsz;
1887 0 : FD_SCRATCH_ALLOC_INIT( l, output_buf );
1888 :
1889 0 : fd_exec_test_elf_loader_effects_t * elf_effects =
1890 0 : FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_exec_test_elf_loader_effects_t),
1891 0 : sizeof (fd_exec_test_elf_loader_effects_t) );
1892 0 : if( FD_UNLIKELY( _l > output_end ) ) {
1893 : /* return 0 on fuzz-specific failures */
1894 0 : return 0UL;
1895 0 : }
1896 0 : fd_memset( elf_effects, 0, sizeof(fd_exec_test_elf_loader_effects_t) );
1897 :
1898 : /* wrap the loader code in do-while(0) block so that we can exit
1899 : immediately if execution fails at any point */
1900 :
1901 0 : do{
1902 :
1903 0 : if( FD_UNLIKELY( !fd_sbpf_elf_peek( &info, _bin, elf_sz, input->deploy_checks, FD_SBPF_V0, FD_SBPF_V3 ) ) ) {
1904 : /* return incomplete effects on execution failures */
1905 0 : break;
1906 0 : }
1907 :
1908 0 : void* rodata = fd_valloc_malloc( valloc, FD_SBPF_PROG_RODATA_ALIGN, info.rodata_footprint );
1909 0 : FD_TEST( rodata );
1910 :
1911 0 : fd_sbpf_program_t * prog = fd_sbpf_program_new( fd_valloc_malloc( valloc, fd_sbpf_program_align(), fd_sbpf_program_footprint( &info ) ), &info, rodata );
1912 0 : FD_TEST( prog );
1913 :
1914 0 : fd_sbpf_syscalls_t * syscalls = fd_sbpf_syscalls_new( fd_valloc_malloc( valloc, fd_sbpf_syscalls_align(), fd_sbpf_syscalls_footprint() ));
1915 0 : FD_TEST( syscalls );
1916 :
1917 0 : fd_vm_syscall_register_all( syscalls, 0 );
1918 :
1919 0 : int res = fd_sbpf_program_load( prog, _bin, elf_sz, syscalls, input->deploy_checks );
1920 0 : if( FD_UNLIKELY( res ) ) {
1921 0 : break;
1922 0 : }
1923 :
1924 0 : fd_memset( elf_effects, 0, sizeof(fd_exec_test_elf_loader_effects_t) );
1925 0 : elf_effects->rodata_sz = prog->rodata_sz;
1926 :
1927 : // Load rodata section
1928 0 : elf_effects->rodata = FD_SCRATCH_ALLOC_APPEND(l, 8UL, PB_BYTES_ARRAY_T_ALLOCSIZE( prog->rodata_sz ));
1929 0 : if( FD_UNLIKELY( _l > output_end ) ) {
1930 0 : return 0UL;
1931 0 : }
1932 0 : elf_effects->rodata->size = (pb_size_t) prog->rodata_sz;
1933 0 : fd_memcpy( &(elf_effects->rodata->bytes), prog->rodata, prog->rodata_sz );
1934 :
1935 0 : elf_effects->text_cnt = prog->text_cnt;
1936 0 : elf_effects->text_off = prog->text_off;
1937 :
1938 0 : elf_effects->entry_pc = prog->entry_pc;
1939 :
1940 :
1941 0 : pb_size_t calldests_sz = (pb_size_t) fd_sbpf_calldests_cnt( prog->calldests);
1942 0 : elf_effects->calldests_count = calldests_sz;
1943 0 : elf_effects->calldests = FD_SCRATCH_ALLOC_APPEND(l, 8UL, calldests_sz * sizeof(uint64_t));
1944 0 : if( FD_UNLIKELY( _l > output_end ) ) {
1945 0 : return 0UL;
1946 0 : }
1947 :
1948 0 : ulong i = 0;
1949 0 : for(ulong target_pc = fd_sbpf_calldests_const_iter_init(prog->calldests); !fd_sbpf_calldests_const_iter_done(target_pc);
1950 0 : target_pc = fd_sbpf_calldests_const_iter_next(prog->calldests, target_pc)) {
1951 0 : elf_effects->calldests[i] = target_pc;
1952 0 : ++i;
1953 0 : }
1954 0 : } while(0);
1955 :
1956 0 : ulong actual_end = FD_SCRATCH_ALLOC_FINI( l, 1UL );
1957 :
1958 0 : *output = elf_effects;
1959 0 : return actual_end - (ulong) output_buf;
1960 0 : }
1961 :
1962 : static fd_exec_test_instr_effects_t const * cpi_exec_effects = NULL;
1963 :
1964 : ulong
1965 : fd_exec_vm_syscall_test_run( fd_exec_instr_test_runner_t * runner,
1966 : void const * input_,
1967 : void ** output_,
1968 : void * output_buf,
1969 0 : ulong output_bufsz ) {
1970 0 : fd_exec_test_syscall_context_t const * input = fd_type_pun_const( input_ );
1971 0 : fd_exec_test_syscall_effects_t ** output = fd_type_pun( output_ );
1972 :
1973 : /* Create execution context */
1974 0 : const fd_exec_test_instr_context_t * input_instr_ctx = &input->instr_ctx;
1975 0 : fd_exec_instr_ctx_t ctx[1];
1976 : // Skip extra checks for non-CPI syscalls
1977 0 : int is_cpi = !strncmp( (const char *)input->syscall_invocation.function_name.bytes, "sol_invoke_signed", 17 );
1978 0 : int skip_extra_checks = !is_cpi;
1979 :
1980 0 : if( !fd_exec_test_instr_context_create( runner, ctx, input_instr_ctx, skip_extra_checks ) )
1981 0 : goto error;
1982 0 : fd_valloc_t valloc = fd_spad_virtual( runner->spad );
1983 0 : fd_spad_t * spad = fd_exec_instr_test_runner_get_spad( runner );
1984 :
1985 0 : if (is_cpi) {
1986 0 : ctx->txn_ctx->instr_info_cnt = 1;
1987 :
1988 : /* Need to setup txn_descriptor for txn account write checks (see fd_txn_account_is_writable_idx)
1989 : FIXME: this could probably go in fd_exec_test_instr_context_create? */
1990 0 : fd_txn_t * txn_descriptor = (fd_txn_t *)fd_spad_alloc_debug( spad, fd_txn_align(), fd_txn_footprint( ctx->txn_ctx->instr_info_cnt, 0UL ) );
1991 0 : txn_descriptor->transaction_version = FD_TXN_V0;
1992 0 : txn_descriptor->acct_addr_cnt = (ushort)ctx->txn_ctx->accounts_cnt;
1993 :
1994 0 : ctx->txn_ctx->txn_descriptor = txn_descriptor;
1995 0 : }
1996 :
1997 0 : ctx->txn_ctx->instr_trace[0].instr_info = (fd_instr_info_t *)ctx->instr;
1998 0 : ctx->txn_ctx->instr_trace[0].stack_height = 1;
1999 :
2000 : /* Capture outputs */
2001 0 : ulong output_end = (ulong)output_buf + output_bufsz;
2002 0 : FD_SCRATCH_ALLOC_INIT( l, output_buf );
2003 0 : fd_exec_test_syscall_effects_t * effects =
2004 0 : FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_exec_test_syscall_effects_t),
2005 0 : sizeof (fd_exec_test_syscall_effects_t) );
2006 0 : if( FD_UNLIKELY( _l > output_end ) ) {
2007 0 : goto error;
2008 0 : }
2009 :
2010 0 : if (input->vm_ctx.return_data.program_id && input->vm_ctx.return_data.program_id->size == sizeof(fd_pubkey_t)) {
2011 0 : fd_memcpy( ctx->txn_ctx->return_data.program_id.uc, input->vm_ctx.return_data.program_id->bytes, sizeof(fd_pubkey_t) );
2012 0 : ctx->txn_ctx->return_data.len = input->vm_ctx.return_data.data->size;
2013 0 : fd_memcpy( ctx->txn_ctx->return_data.data, input->vm_ctx.return_data.data->bytes, ctx->txn_ctx->return_data.len );
2014 0 : }
2015 :
2016 0 : *effects = (fd_exec_test_syscall_effects_t) FD_EXEC_TEST_SYSCALL_EFFECTS_INIT_ZERO;
2017 :
2018 : /* Set up the VM instance */
2019 0 : fd_sha256_t _sha[1];
2020 0 : fd_sha256_t * sha = fd_sha256_join( fd_sha256_new( _sha ) );
2021 0 : fd_sbpf_syscalls_t * syscalls = fd_sbpf_syscalls_new( fd_valloc_malloc( valloc, fd_sbpf_syscalls_align(), fd_sbpf_syscalls_footprint() ) );
2022 0 : fd_vm_syscall_register_all( syscalls, 0 );
2023 :
2024 : /* Pull out the memory regions */
2025 0 : if( !input->has_vm_ctx ) {
2026 0 : goto error;
2027 0 : }
2028 0 : if( input->has_exec_effects ){
2029 0 : cpi_exec_effects = &input->exec_effects;
2030 0 : }
2031 :
2032 0 : ulong rodata_sz = input->vm_ctx.rodata ? input->vm_ctx.rodata->size : 0UL;
2033 0 : uchar * rodata = fd_valloc_malloc( valloc, 8UL, rodata_sz );
2034 0 : if ( input->vm_ctx.rodata != NULL ) {
2035 0 : fd_memcpy( rodata, input->vm_ctx.rodata->bytes, rodata_sz );
2036 0 : }
2037 :
2038 0 : if( input->vm_ctx.heap_max > FD_VM_HEAP_MAX ) {
2039 0 : goto error;
2040 0 : }
2041 :
2042 0 : fd_vm_t * vm = fd_vm_join( fd_vm_new( fd_valloc_malloc( valloc, fd_vm_align(), fd_vm_footprint() ) ) );
2043 0 : if ( !vm ) {
2044 0 : goto error;
2045 0 : }
2046 :
2047 : /* If the program ID account owner is the v1 BPF loader, then alignment is disabled (controlled by
2048 : the `is_deprecated` flag) */
2049 :
2050 0 : ulong input_sz = 0UL;
2051 0 : ulong pre_lens[256] = {0};
2052 0 : fd_vm_input_region_t input_mem_regions[1000] = {0}; /* We can have a max of (3 * num accounts + 1) regions */
2053 0 : fd_vm_acc_region_meta_t acc_region_metas[256] = {0}; /* instr acc idx to idx */
2054 0 : uint input_mem_regions_cnt = 0U;
2055 0 : int direct_mapping = FD_FEATURE_ACTIVE( ctx->txn_ctx->slot, ctx->txn_ctx->features, bpf_account_data_direct_mapping );
2056 :
2057 0 : uchar * input_ptr = NULL;
2058 0 : uchar program_id_idx = ctx->instr->program_id;
2059 0 : uchar is_deprecated = ( program_id_idx < ctx->txn_ctx->accounts_cnt ) &&
2060 0 : ( !memcmp( ctx->txn_ctx->accounts[program_id_idx].const_meta->info.owner, fd_solana_bpf_loader_deprecated_program_id.key, sizeof(fd_pubkey_t) ) );
2061 :
2062 : /* TODO: Check for an error code. Probably unlikely during fuzzing though */
2063 0 : fd_bpf_loader_input_serialize_parameters( ctx,
2064 0 : &input_sz,
2065 0 : pre_lens,
2066 0 : input_mem_regions,
2067 0 : &input_mem_regions_cnt,
2068 0 : acc_region_metas,
2069 0 : direct_mapping,
2070 0 : is_deprecated,
2071 0 : &input_ptr );
2072 :
2073 0 : fd_vm_init( vm,
2074 0 : ctx,
2075 0 : input->vm_ctx.heap_max,
2076 0 : ctx->txn_ctx->compute_meter,
2077 0 : rodata,
2078 0 : rodata_sz,
2079 0 : NULL, // TODO
2080 0 : 0, // TODO
2081 0 : 0, // TODO
2082 0 : 0, // TODO, text_sz
2083 0 : 0, // TODO
2084 0 : NULL, // TODO
2085 0 : TEST_VM_DEFAULT_SBPF_VERSION,
2086 0 : syscalls,
2087 0 : NULL, // TODO
2088 0 : sha,
2089 0 : input_mem_regions,
2090 0 : input_mem_regions_cnt,
2091 0 : acc_region_metas,
2092 0 : is_deprecated,
2093 0 : FD_FEATURE_ACTIVE( ctx->txn_ctx->slot, ctx->txn_ctx->features, bpf_account_data_direct_mapping ) );
2094 :
2095 : // Override some execution state values from the syscall fuzzer input
2096 : // This is so we can test if the syscall mutates any of these erroneously
2097 0 : vm->reg[0] = input->vm_ctx.r0;
2098 0 : vm->reg[1] = input->vm_ctx.r1;
2099 0 : vm->reg[2] = input->vm_ctx.r2;
2100 0 : vm->reg[3] = input->vm_ctx.r3;
2101 0 : vm->reg[4] = input->vm_ctx.r4;
2102 0 : vm->reg[5] = input->vm_ctx.r5;
2103 0 : vm->reg[6] = input->vm_ctx.r6;
2104 0 : vm->reg[7] = input->vm_ctx.r7;
2105 0 : vm->reg[8] = input->vm_ctx.r8;
2106 0 : vm->reg[9] = input->vm_ctx.r9;
2107 0 : vm->reg[10] = input->vm_ctx.r10;
2108 0 : vm->reg[11] = input->vm_ctx.r11;
2109 :
2110 : // Override initial part of the heap, if specified the syscall fuzzer input
2111 0 : if( input->syscall_invocation.heap_prefix ) {
2112 0 : fd_memcpy( vm->heap, input->syscall_invocation.heap_prefix->bytes,
2113 0 : fd_ulong_min(input->syscall_invocation.heap_prefix->size, vm->heap_max) );
2114 0 : }
2115 :
2116 : // Override initial part of the stack, if specified the syscall fuzzer input
2117 0 : if( input->syscall_invocation.stack_prefix ) {
2118 0 : fd_memcpy( vm->stack, input->syscall_invocation.stack_prefix->bytes,
2119 0 : fd_ulong_min(input->syscall_invocation.stack_prefix->size, FD_VM_STACK_MAX) );
2120 0 : }
2121 :
2122 : // Look up the syscall to execute
2123 0 : char * syscall_name = (char *)input->syscall_invocation.function_name.bytes;
2124 0 : fd_sbpf_syscalls_t const * syscall = lookup_syscall_func(syscalls, syscall_name, input->syscall_invocation.function_name.size);
2125 0 : if( !syscall ) {
2126 0 : goto error;
2127 0 : }
2128 :
2129 : /* Actually invoke the syscall */
2130 0 : int stack_push_err = fd_instr_stack_push( ctx->txn_ctx, (fd_instr_info_t *)ctx->instr );
2131 0 : if( FD_UNLIKELY( stack_push_err ) ) {
2132 0 : FD_LOG_WARNING(( "instr stack push err" ));
2133 0 : goto error;
2134 0 : }
2135 : /* There's an instr ctx struct embedded in the txn ctx instr stack. */
2136 0 : fd_exec_instr_ctx_t * instr_ctx = &ctx->txn_ctx->instr_stack[ ctx->txn_ctx->instr_stack_sz - 1 ];
2137 0 : *instr_ctx = (fd_exec_instr_ctx_t) {
2138 0 : .instr = ctx->instr,
2139 0 : .txn_ctx = ctx->txn_ctx,
2140 0 : .acc_mgr = ctx->acc_mgr,
2141 0 : .funk_txn = ctx->funk_txn,
2142 0 : .parent = NULL,
2143 0 : .index = 0U,
2144 0 : .depth = 0U,
2145 0 : .child_cnt = 0U,
2146 0 : };
2147 0 : int syscall_err = syscall->func( vm, vm->reg[1], vm->reg[2], vm->reg[3], vm->reg[4], vm->reg[5], &vm->reg[0] );
2148 0 : int stack_pop_err = fd_instr_stack_pop( ctx->txn_ctx, ctx->instr );
2149 0 : if( FD_UNLIKELY( stack_pop_err ) ) {
2150 0 : FD_LOG_WARNING(( "instr stack pop err" ));
2151 0 : goto error;
2152 0 : }
2153 0 : if( syscall_err ) {
2154 0 : fd_log_collector_program_failure( vm->instr_ctx );
2155 0 : }
2156 :
2157 : /* Capture the effects */
2158 0 : int exec_err = vm->instr_ctx->txn_ctx->exec_err;
2159 0 : effects->error = 0;
2160 0 : if( syscall_err ) {
2161 0 : if( exec_err==0 ) {
2162 0 : FD_LOG_WARNING(( "TODO: syscall returns error, but exec_err not set. this is probably missing a log." ));
2163 0 : effects->error = -1;
2164 0 : } else {
2165 0 : effects->error = (exec_err <= 0) ? -exec_err : -1;
2166 :
2167 : /* Map error kind, equivalent to:
2168 : effects->error_kind = (fd_exec_test_err_kind_t)(vm->instr_ctx->txn_ctx->exec_err_kind); */
2169 0 : switch (vm->instr_ctx->txn_ctx->exec_err_kind) {
2170 0 : case FD_EXECUTOR_ERR_KIND_EBPF:
2171 0 : effects->error_kind = FD_EXEC_TEST_ERR_KIND_EBPF;
2172 0 : break;
2173 0 : case FD_EXECUTOR_ERR_KIND_SYSCALL:
2174 0 : effects->error_kind = FD_EXEC_TEST_ERR_KIND_SYSCALL;
2175 0 : break;
2176 0 : case FD_EXECUTOR_ERR_KIND_INSTR:
2177 0 : effects->error_kind = FD_EXEC_TEST_ERR_KIND_INSTRUCTION;
2178 0 : break;
2179 0 : default:
2180 0 : effects->error_kind = FD_EXEC_TEST_ERR_KIND_UNSPECIFIED;
2181 0 : break;
2182 0 : }
2183 0 : }
2184 0 : }
2185 0 : effects->r0 = syscall_err ? 0 : vm->reg[0]; // Save only on success
2186 0 : effects->cu_avail = (ulong)vm->cu;
2187 :
2188 0 : if( vm->heap_max ) {
2189 0 : effects->heap = FD_SCRATCH_ALLOC_APPEND(
2190 0 : l, alignof(uint), PB_BYTES_ARRAY_T_ALLOCSIZE( vm->heap_max ) );
2191 0 : if( FD_UNLIKELY( _l > output_end ) ) {
2192 0 : goto error;
2193 0 : }
2194 0 : effects->heap->size = (uint)vm->heap_max;
2195 0 : fd_memcpy( effects->heap->bytes, vm->heap, vm->heap_max );
2196 0 : } else {
2197 0 : effects->heap = NULL;
2198 0 : }
2199 :
2200 0 : effects->stack = FD_SCRATCH_ALLOC_APPEND(
2201 0 : l, alignof(pb_bytes_array_t), PB_BYTES_ARRAY_T_ALLOCSIZE( FD_VM_STACK_MAX ) );
2202 0 : if( FD_UNLIKELY( _l > output_end ) ) {
2203 0 : goto error;
2204 0 : }
2205 0 : effects->stack->size = (uint)FD_VM_STACK_MAX;
2206 0 : fd_memcpy( effects->stack->bytes, vm->stack, FD_VM_STACK_MAX );
2207 :
2208 0 : if( vm->rodata_sz ) {
2209 0 : effects->rodata = FD_SCRATCH_ALLOC_APPEND(
2210 0 : l, alignof(pb_bytes_array_t), PB_BYTES_ARRAY_T_ALLOCSIZE( rodata_sz ) );
2211 0 : if( FD_UNLIKELY( _l > output_end ) ) {
2212 0 : goto error;
2213 0 : }
2214 0 : effects->rodata->size = (uint)rodata_sz;
2215 0 : fd_memcpy( effects->rodata->bytes, vm->rodata, rodata_sz );
2216 0 : } else {
2217 0 : effects->rodata = NULL;
2218 0 : }
2219 :
2220 0 : effects->frame_count = vm->frame_cnt;
2221 :
2222 0 : fd_log_collector_t * log = &vm->instr_ctx->txn_ctx->log_collector;
2223 : /* Only collect log on valid errors (i.e., != -1). Follows
2224 : https://github.com/firedancer-io/solfuzz-agave/blob/99758d3c4f3a342d56e2906936458d82326ae9a8/src/utils/err_map.rs#L148 */
2225 0 : if( effects->error != -1 && log->buf_sz ) {
2226 0 : effects->log = FD_SCRATCH_ALLOC_APPEND(
2227 0 : l, alignof(pb_bytes_array_t), PB_BYTES_ARRAY_T_ALLOCSIZE( log->buf_sz ) );
2228 0 : if( FD_UNLIKELY( _l > output_end ) ) {
2229 0 : goto error;
2230 0 : }
2231 0 : effects->log->size = (uint)fd_log_collector_debug_sprintf( log, (char *)effects->log->bytes, 0 );
2232 0 : } else {
2233 0 : effects->log = NULL;
2234 0 : }
2235 :
2236 : /* Capture input regions */
2237 0 : effects->inputdata = NULL; /* Deprecated, using input_data_regions instead */
2238 0 : ulong tmp_end = FD_SCRATCH_ALLOC_FINI( l, 1UL );
2239 0 : ulong input_regions_size = load_from_vm_input_regions( vm->input_mem_regions,
2240 0 : vm->input_mem_regions_cnt,
2241 0 : &effects->input_data_regions,
2242 0 : &effects->input_data_regions_count,
2243 0 : (void *)tmp_end,
2244 0 : fd_ulong_sat_sub( output_end, tmp_end ) );
2245 :
2246 0 : if( !!vm->input_mem_regions_cnt && !effects->input_data_regions ) {
2247 0 : goto error;
2248 0 : }
2249 :
2250 : /* Return the effects */
2251 0 : ulong actual_end = tmp_end + input_regions_size;
2252 0 : fd_exec_test_instr_context_destroy( runner, ctx );
2253 0 : cpi_exec_effects = NULL;
2254 :
2255 0 : *output = effects;
2256 0 : return actual_end - (ulong)output_buf;
2257 :
2258 0 : error:
2259 0 : fd_exec_test_instr_context_destroy( runner, ctx );
2260 0 : cpi_exec_effects = NULL;
2261 0 : return 0;
2262 0 : }
2263 :
2264 : /* Stubs fd_execute_instr for binaries compiled with
2265 : `-Xlinker --wrap=fd_execute_instr` */
2266 : int
2267 : __wrap_fd_execute_instr( fd_exec_txn_ctx_t * txn_ctx,
2268 : fd_instr_info_t * instr_info )
2269 0 : {
2270 0 : static const pb_byte_t zero_blk[32] = {0};
2271 :
2272 0 : if( cpi_exec_effects == NULL ) {
2273 0 : FD_LOG_WARNING(( "fd_execute_instr is disabled" ));
2274 0 : return FD_EXECUTOR_INSTR_SUCCESS;
2275 0 : }
2276 :
2277 : // Iterate through instruction accounts
2278 0 : for( ushort i = 0UL; i < instr_info->acct_cnt; ++i ) {
2279 0 : uchar idx_in_txn = instr_info->acct_txn_idxs[i];
2280 0 : fd_pubkey_t * acct_pubkey = &instr_info->acct_pubkeys[i];
2281 :
2282 0 : fd_txn_account_t * acct = NULL;
2283 : /* Find (first) account in cpi_exec_effects->modified_accounts that matches pubkey */
2284 0 : for( uint j = 0UL; j < cpi_exec_effects->modified_accounts_count; ++j ) {
2285 0 : fd_exec_test_acct_state_t * acct_state = &cpi_exec_effects->modified_accounts[j];
2286 0 : if( memcmp( acct_state->address, acct_pubkey, sizeof(fd_pubkey_t) ) != 0 ) continue;
2287 :
2288 : /* Fetch borrowed account */
2289 : /* First check if account is read-only.
2290 : TODO: Once direct mapping is enabled we _technically_ don't need
2291 : this check */
2292 :
2293 0 : if( fd_exec_txn_ctx_get_account_view_idx( txn_ctx, idx_in_txn, &acct ) ) {
2294 0 : break;
2295 0 : }
2296 0 : if( acct->meta == NULL ){
2297 0 : break;
2298 0 : }
2299 :
2300 : /* Now borrow mutably (with resize) */
2301 0 : int err = fd_exec_txn_ctx_get_account_modify_idx( txn_ctx,
2302 0 : idx_in_txn,
2303 : /* Do not reallocate if data is not going to be modified */
2304 0 : acct_state->data ? acct_state->data->size : 0UL,
2305 0 : &acct );
2306 0 : if( err ) break;
2307 :
2308 : /* Update account state */
2309 0 : acct->meta->info.lamports = acct_state->lamports;
2310 0 : acct->meta->info.executable = acct_state->executable;
2311 0 : acct->meta->info.rent_epoch = acct_state->rent_epoch;
2312 :
2313 : /* TODO: use lower level API (i.e., fd_borrowed_account_resize) to avoid memcpy here */
2314 0 : if( acct_state->data ){
2315 0 : fd_memcpy( acct->data, acct_state->data->bytes, acct_state->data->size );
2316 0 : acct->meta->dlen = acct_state->data->size;
2317 0 : }
2318 :
2319 : /* Follow solfuzz-agave, which skips if pubkey is malformed */
2320 0 : if( memcmp( acct_state->owner, zero_blk, sizeof(fd_pubkey_t) ) != 0 ) {
2321 0 : fd_memcpy( acct->meta->info.owner, acct_state->owner, sizeof(fd_pubkey_t) );
2322 0 : }
2323 :
2324 0 : break;
2325 0 : }
2326 0 : }
2327 0 : return FD_EXECUTOR_INSTR_SUCCESS;
2328 0 : }
|