Line data Source code
1 : #undef FD_SPAD_USE_HANDHOLDING
2 : #define FD_SPAD_USE_HANDHOLDING 1
3 :
4 : #include "fd_solfuzz_private.h"
5 : #include "fd_instr_harness.h"
6 : #include "../fd_executor.h"
7 : #include "../context/fd_exec_txn_ctx.h"
8 : #include "../program/fd_bpf_loader_program.h"
9 : #include "../sysvar/fd_sysvar.h"
10 : #include "../sysvar/fd_sysvar_clock.h"
11 : #include "../sysvar/fd_sysvar_epoch_schedule.h"
12 : #include "../sysvar/fd_sysvar_recent_hashes.h"
13 : #include "../sysvar/fd_sysvar_last_restart_slot.h"
14 : #include "../sysvar/fd_sysvar_rent.h"
15 : #include "../fd_system_ids.h"
16 : #include "../fd_cost_tracker.h"
17 : #include <assert.h>
18 :
19 : int
20 : fd_runtime_fuzz_instr_ctx_create( fd_solfuzz_runner_t * runner,
21 : fd_exec_instr_ctx_t * ctx,
22 : fd_exec_test_instr_context_t const * test_ctx,
23 0 : bool is_syscall ) {
24 :
25 0 : memset( ctx, 0, sizeof(fd_exec_instr_ctx_t) );
26 :
27 0 : fd_funk_t * funk = runner->funk;
28 :
29 : /* Generate unique ID for funk txn */
30 :
31 0 : fd_funk_txn_xid_t xid[1] = {0};
32 0 : xid[0] = fd_funk_generate_xid();
33 :
34 : /* Create temporary funk transaction and txn / slot / epoch contexts */
35 :
36 0 : fd_funk_txn_start_write( funk );
37 0 : fd_funk_txn_t * funk_txn = fd_funk_txn_prepare( funk, NULL, xid, 1 );
38 0 : if( FD_UNLIKELY( !funk_txn ) ) FD_LOG_ERR(( "fd_funk_txn_prepare failed" ));
39 0 : fd_funk_txn_end_write( funk );
40 :
41 : /* Allocate contexts */
42 0 : uchar * slot_ctx_mem = fd_spad_alloc( runner->spad,FD_EXEC_SLOT_CTX_ALIGN, FD_EXEC_SLOT_CTX_FOOTPRINT );
43 0 : uchar * txn_ctx_mem = fd_spad_alloc( runner->spad,FD_EXEC_TXN_CTX_ALIGN, FD_EXEC_TXN_CTX_FOOTPRINT );
44 :
45 0 : fd_exec_slot_ctx_t * slot_ctx = fd_exec_slot_ctx_join ( fd_exec_slot_ctx_new ( slot_ctx_mem ) );
46 0 : fd_exec_txn_ctx_t * txn_ctx = fd_exec_txn_ctx_join ( fd_exec_txn_ctx_new ( txn_ctx_mem ), runner->spad, fd_wksp_containing( runner->spad ) );
47 :
48 0 : assert( slot_ctx );
49 :
50 0 : ctx->txn_ctx = txn_ctx;
51 :
52 : /* Set up slot context */
53 :
54 0 : slot_ctx->funk_txn = funk_txn;
55 0 : slot_ctx->funk = funk;
56 :
57 : /* Bank manager */
58 0 : slot_ctx->banks = runner->banks;
59 0 : slot_ctx->bank = runner->bank;
60 0 : fd_banks_clear_bank( slot_ctx->banks, slot_ctx->bank );
61 :
62 0 : fd_features_t * features = fd_bank_features_modify( slot_ctx->bank );
63 0 : fd_exec_test_feature_set_t const * feature_set = &test_ctx->epoch_context.features;
64 0 : if( !fd_runtime_fuzz_restore_features( features, feature_set ) ) {
65 0 : return 0;
66 0 : }
67 :
68 : /* Setup vote states accounts */
69 0 : fd_vote_states_t * vote_states = fd_vote_states_join( fd_vote_states_new( fd_bank_vote_states_locking_modify( slot_ctx->bank ), FD_RUNTIME_MAX_VOTE_ACCOUNTS, 999UL ) );
70 0 : if( FD_UNLIKELY( !vote_states ) ) FD_LOG_ERR(( "fd_vote_states_new failed" ));
71 0 : fd_bank_vote_states_end_locking_modify( slot_ctx->bank );
72 :
73 0 : fd_vote_states_t * vote_states_prev = fd_vote_states_join( fd_vote_states_new( fd_bank_vote_states_prev_locking_modify( slot_ctx->bank ), FD_RUNTIME_MAX_VOTE_ACCOUNTS, 999UL ) );
74 0 : if( FD_UNLIKELY( !vote_states_prev ) ) FD_LOG_ERR(( "fd_vote_states_new for prev failed" ));
75 0 : fd_bank_vote_states_prev_end_locking_modify( slot_ctx->bank );
76 :
77 0 : fd_vote_states_t * vote_states_prev_prev = fd_vote_states_join( fd_vote_states_new( fd_bank_vote_states_prev_prev_locking_modify( slot_ctx->bank ), FD_RUNTIME_MAX_VOTE_ACCOUNTS, 999UL ) );
78 0 : if( FD_UNLIKELY( !vote_states_prev_prev ) ) FD_LOG_ERR(( "fd_vote_staets_new for prev2 failed" ));
79 0 : fd_bank_vote_states_prev_prev_end_locking_modify( slot_ctx->bank );
80 :
81 : /* Set up epoch context. Defaults obtained from GenesisConfig::Default() */
82 :
83 0 : fd_rent_t * rent_bm = fd_bank_rent_modify( slot_ctx->bank );
84 0 : rent_bm->lamports_per_uint8_year = 3480;
85 0 : rent_bm->exemption_threshold = 2;
86 0 : rent_bm->burn_percent = 50;
87 :
88 : /* Blockhash queue init */
89 :
90 0 : ulong blockhash_seed; FD_TEST( fd_rng_secure( &blockhash_seed, sizeof(ulong) ) );
91 0 : fd_blockhashes_t * blockhashes = fd_blockhashes_init( fd_bank_block_hash_queue_modify( slot_ctx->bank ), blockhash_seed );
92 0 : fd_memset( fd_blockhash_deq_push_tail_nocopy( blockhashes->d.deque ), 0, sizeof(fd_hash_t) );
93 :
94 : /* Set up txn context */
95 :
96 0 : fd_wksp_t * funk_wksp = fd_funk_wksp( funk );
97 0 : ulong funk_txn_gaddr = fd_wksp_gaddr( funk_wksp, funk_txn );
98 0 : ulong funk_gaddr = fd_wksp_gaddr( funk_wksp, funk->shmem );
99 :
100 : /* Set up mock txn descriptor */
101 0 : fd_txn_p_t * txn = fd_spad_alloc( runner->spad, fd_txn_align(), fd_txn_footprint( 1UL, 0UL ) );
102 0 : fd_txn_t * txn_descriptor = TXN( txn );
103 0 : txn_descriptor->transaction_version = FD_TXN_V0;
104 0 : txn_descriptor->acct_addr_cnt = (ushort)test_ctx->accounts_count;
105 :
106 0 : fd_exec_txn_ctx_from_exec_slot_ctx( slot_ctx,
107 0 : txn_ctx,
108 0 : funk_wksp,
109 0 : funk_txn_gaddr,
110 0 : funk_gaddr,
111 0 : NULL );
112 0 : fd_exec_txn_ctx_setup_basic( txn_ctx );
113 :
114 0 : txn_ctx->txn = *txn;
115 0 : txn_ctx->compute_budget_details.compute_unit_limit = test_ctx->cu_avail;
116 0 : txn_ctx->compute_budget_details.compute_meter = test_ctx->cu_avail;
117 0 : txn_ctx->spad = runner->spad;
118 0 : txn_ctx->instr_info_cnt = 1UL;
119 0 : txn_ctx->fuzz_config.enable_vm_tracing = runner->enable_vm_tracing;
120 :
121 : /* Set up instruction context */
122 :
123 0 : fd_instr_info_t * info = fd_spad_alloc( runner->spad, 8UL, sizeof(fd_instr_info_t) );
124 0 : assert( info );
125 0 : memset( info, 0, sizeof(fd_instr_info_t) );
126 :
127 0 : if( test_ctx->data ) {
128 0 : info->data_sz = (ushort)test_ctx->data->size;
129 0 : info->data = test_ctx->data->bytes;
130 0 : }
131 :
132 0 : txn_ctx->instr_infos[ 0UL ] = *info;
133 :
134 : /* Prepare borrowed account table (correctly handles aliasing) */
135 :
136 0 : if( FD_UNLIKELY( test_ctx->accounts_count > MAX_TX_ACCOUNT_LOCKS ) ) {
137 0 : FD_LOG_NOTICE(( "too many accounts" ));
138 0 : return 0;
139 0 : }
140 :
141 : /* Load accounts into database */
142 :
143 0 : fd_txn_account_t * accts = txn_ctx->accounts;
144 0 : fd_memset( accts, 0, test_ctx->accounts_count * sizeof(fd_txn_account_t) );
145 0 : txn_ctx->accounts_cnt = test_ctx->accounts_count;
146 :
147 0 : int has_program_id = 0;
148 :
149 0 : for( ulong j=0UL; j < test_ctx->accounts_count; j++ ) {
150 0 : fd_pubkey_t * acc_key = (fd_pubkey_t *)test_ctx->accounts[j].address;
151 :
152 0 : memcpy( &(txn_ctx->account_keys[j]), test_ctx->accounts[j].address, sizeof(fd_pubkey_t) );
153 0 : if( !fd_runtime_fuzz_load_account( &accts[j], funk, funk_txn, &test_ctx->accounts[j], 0 ) ) {
154 0 : return 0;
155 0 : }
156 :
157 0 : fd_txn_account_t * acc = &accts[j];
158 0 : if( fd_txn_account_get_meta( acc ) ) {
159 0 : uchar * data = fd_spad_alloc( txn_ctx->spad, FD_ACCOUNT_REC_ALIGN, FD_ACC_TOT_SZ_MAX );
160 0 : ulong dlen = fd_txn_account_get_data_len( acc );
161 0 : fd_account_meta_t * meta = (fd_account_meta_t *)data;
162 0 : fd_memcpy( data, fd_txn_account_get_meta( acc ), sizeof(fd_account_meta_t)+dlen );
163 0 : if( FD_UNLIKELY( !fd_txn_account_join( fd_txn_account_new( acc, acc_key, meta, 0 ), txn_ctx->spad_wksp ) ) ) {
164 0 : FD_LOG_CRIT(( "Failed to join and new a txn account" ));
165 0 : }
166 0 : }
167 :
168 0 : if( !memcmp( accts[j].pubkey, test_ctx->program_id, sizeof(fd_pubkey_t) ) ) {
169 0 : has_program_id = 1;
170 0 : info->program_id = (uchar)txn_ctx->accounts_cnt;
171 0 : }
172 :
173 : /* Since the instructions sysvar is set as mutable at the txn level, we need to make it mutable here as well. */
174 0 : if( !memcmp( accts[j].pubkey, &fd_sysvar_instructions_id, sizeof(fd_pubkey_t) ) ) {
175 0 : fd_txn_account_set_mutable( acc );
176 0 : }
177 0 : }
178 :
179 : /* If the program id is not in the set of accounts it must be added to the set of accounts. */
180 0 : if( FD_UNLIKELY( !has_program_id ) ) {
181 0 : fd_txn_account_t * program_acc = &accts[ test_ctx->accounts_count ];
182 0 : fd_pubkey_t * program_key = &txn_ctx->account_keys[ txn_ctx->accounts_cnt ];
183 0 : memcpy( program_key, test_ctx->program_id, sizeof(fd_pubkey_t) );
184 :
185 0 : fd_account_meta_t * meta = fd_spad_alloc( txn_ctx->spad, alignof(fd_account_meta_t), sizeof(fd_account_meta_t) );
186 0 : fd_account_meta_init( meta );
187 :
188 0 : if( FD_UNLIKELY( !fd_txn_account_join( fd_txn_account_new(
189 0 : program_acc,
190 0 : program_key,
191 0 : meta,
192 0 : 1 ), txn_ctx->spad_wksp ) ) ) {
193 0 : FD_LOG_CRIT(( "Failed to join and new a txn account" ));
194 0 : }
195 :
196 0 : info->program_id = (uchar)txn_ctx->accounts_cnt;
197 0 : txn_ctx->accounts_cnt++;
198 0 : }
199 :
200 : /* Load in executable accounts */
201 0 : for( ulong i = 0; i < txn_ctx->accounts_cnt; i++ ) {
202 0 : fd_pubkey_t * acc_key = (fd_pubkey_t *)test_ctx->accounts[i].address;
203 :
204 0 : fd_txn_account_t * acc = &accts[i];
205 0 : if ( !fd_executor_pubkey_is_bpf_loader( fd_txn_account_get_owner( acc ) ) ) {
206 0 : continue;
207 0 : }
208 :
209 0 : fd_account_meta_t const * meta = fd_txn_account_get_meta( acc );
210 0 : if( meta == NULL ) {
211 0 : uchar * mem = fd_spad_alloc( txn_ctx->spad, FD_TXN_ACCOUNT_ALIGN, sizeof(fd_account_meta_t) );
212 0 : fd_account_meta_t * meta = (fd_account_meta_t *)mem;
213 0 : memset( meta, 0, sizeof(fd_account_meta_t) );
214 0 : if( FD_UNLIKELY( !fd_txn_account_join( fd_txn_account_new( acc, acc_key, meta, 0 ), txn_ctx->spad_wksp ) ) ) {
215 0 : FD_LOG_CRIT(( "Failed to join and new a txn account" ));
216 0 : }
217 0 : continue;
218 0 : }
219 :
220 0 : if( FD_UNLIKELY( !memcmp(meta->owner, fd_solana_bpf_loader_upgradeable_program_id.key, sizeof(fd_pubkey_t)) ) ) {
221 0 : fd_bpf_upgradeable_loader_state_t * program_loader_state = fd_bpf_loader_program_get_state( acc,
222 0 : txn_ctx->spad,
223 0 : NULL );
224 :
225 0 : if( FD_UNLIKELY( !program_loader_state ) ) {
226 0 : continue;
227 0 : }
228 :
229 0 : if( !fd_bpf_upgradeable_loader_state_is_program( program_loader_state ) ) {
230 0 : continue;
231 0 : }
232 :
233 0 : fd_pubkey_t * programdata_acc = &program_loader_state->inner.program.programdata_address;
234 0 : if( FD_UNLIKELY( fd_txn_account_init_from_funk_readonly( &txn_ctx->executable_accounts[txn_ctx->executable_cnt],
235 0 : programdata_acc,
236 0 : txn_ctx->funk,
237 0 : txn_ctx->funk_txn ) ) ) {
238 0 : continue;
239 0 : }
240 0 : txn_ctx->executable_cnt++;
241 0 : }
242 0 : }
243 :
244 : /* Fill missing sysvar accounts with defaults */
245 : /* We create mock accounts for each of the sysvars and hardcode the data fields before loading it into the account manager */
246 : /* We use Agave sysvar defaults for data field values */
247 :
248 : /* Clock */
249 : // https://github.com/firedancer-io/solfuzz-agave/blob/agave-v2.0/src/lib.rs#L466-L474
250 0 : fd_sol_sysvar_clock_t clock_[1];
251 0 : fd_sol_sysvar_clock_t const * clock = fd_sysvar_clock_read( funk, funk_txn, clock_ );
252 0 : if( !clock ) {
253 0 : fd_sol_sysvar_clock_t sysvar_clock = {
254 0 : .slot = 10UL,
255 0 : .epoch_start_timestamp = 0L,
256 0 : .epoch = 0UL,
257 0 : .leader_schedule_epoch = 0UL,
258 0 : .unix_timestamp = 0L
259 0 : };
260 0 : fd_sysvar_clock_write( slot_ctx, &sysvar_clock );
261 0 : }
262 :
263 : /* Epoch schedule */
264 : // https://github.com/firedancer-io/solfuzz-agave/blob/agave-v2.0/src/lib.rs#L476-L483
265 0 : fd_epoch_schedule_t epoch_schedule[1];
266 0 : if( FD_UNLIKELY( !fd_sysvar_epoch_schedule_read( funk, funk_txn, epoch_schedule ) ) ) {
267 0 : fd_epoch_schedule_t sysvar_epoch_schedule = {
268 0 : .slots_per_epoch = 432000UL,
269 0 : .leader_schedule_slot_offset = 432000UL,
270 0 : .warmup = 1,
271 0 : .first_normal_epoch = 14UL,
272 0 : .first_normal_slot = 524256UL
273 0 : };
274 0 : fd_sysvar_epoch_schedule_write( slot_ctx, &sysvar_epoch_schedule );
275 0 : }
276 :
277 : /* Rent */
278 : // https://github.com/firedancer-io/solfuzz-agave/blob/agave-v2.0/src/lib.rs#L487-L500
279 0 : fd_rent_t const * rent = fd_sysvar_rent_read( funk, funk_txn, runner->spad );
280 0 : if( !rent ) {
281 0 : fd_rent_t sysvar_rent = {
282 0 : .lamports_per_uint8_year = 3480UL,
283 0 : .exemption_threshold = 2.0,
284 0 : .burn_percent = 50
285 0 : };
286 0 : fd_sysvar_rent_write( slot_ctx, &sysvar_rent );
287 0 : }
288 :
289 0 : fd_sol_sysvar_last_restart_slot_t last_restart_slot_[1];
290 0 : fd_sol_sysvar_last_restart_slot_t const * last_restart_slot = fd_sysvar_last_restart_slot_read( funk, funk_txn, last_restart_slot_ );
291 0 : if( !last_restart_slot ) {
292 0 : fd_sol_sysvar_last_restart_slot_t restart = { .slot = 5000UL };
293 0 : fd_sysvar_account_update( slot_ctx, &fd_sysvar_last_restart_slot_id, &restart.slot, sizeof(ulong) );
294 0 : }
295 :
296 : /* Set slot bank variables */
297 0 : clock = fd_sysvar_clock_read( funk, funk_txn, clock_ );
298 :
299 0 : slot_ctx->bank->eslot_ = fd_eslot( clock->slot, 0UL );
300 :
301 : /* Handle undefined behavior if sysvars are malicious (!!!) */
302 :
303 0 : if( fd_sysvar_epoch_schedule_read( funk, funk_txn, epoch_schedule ) ) {
304 0 : fd_bank_epoch_schedule_set( slot_ctx->bank, *epoch_schedule );
305 0 : }
306 :
307 : /* Override epoch bank rent setting */
308 0 : rent = fd_sysvar_rent_read( funk, funk_txn, runner->spad );
309 0 : if( rent ) {
310 0 : fd_bank_rent_set( slot_ctx->bank, *rent );
311 0 : }
312 :
313 : /* Override most recent blockhash if given */
314 0 : fd_recent_block_hashes_t const * rbh = fd_sysvar_recent_hashes_read( funk, funk_txn, runner->spad );
315 0 : if( rbh && !deq_fd_block_block_hash_entry_t_empty( rbh->hashes ) ) {
316 0 : fd_block_block_hash_entry_t const * last = deq_fd_block_block_hash_entry_t_peek_tail_const( rbh->hashes );
317 0 : if( last ) {
318 0 : fd_blockhashes_t * blockhashes = fd_bank_block_hash_queue_modify( slot_ctx->bank );
319 0 : fd_blockhashes_pop_new( blockhashes );
320 0 : fd_blockhash_info_t * info = fd_blockhashes_push_new( blockhashes, &last->blockhash );
321 0 : info->fee_calculator = last->fee_calculator;
322 :
323 0 : fd_bank_lamports_per_signature_set( slot_ctx->bank, last->fee_calculator.lamports_per_signature );
324 :
325 0 : fd_bank_prev_lamports_per_signature_set( slot_ctx->bank, last->fee_calculator.lamports_per_signature );
326 0 : }
327 0 : }
328 :
329 : /* Refresh the program cache */
330 0 : fd_runtime_fuzz_refresh_program_cache( slot_ctx, test_ctx->accounts, test_ctx->accounts_count, runner->spad );
331 :
332 : /* Load instruction accounts */
333 :
334 0 : if( FD_UNLIKELY( test_ctx->instr_accounts_count > MAX_TX_ACCOUNT_LOCKS ) ) {
335 0 : FD_LOG_NOTICE(( "too many instruction accounts" ));
336 0 : return 0;
337 0 : }
338 :
339 : /* Restore sysvar cache */
340 0 : fd_sysvar_cache_restore_fuzz( slot_ctx );
341 0 : ctx->sysvar_cache = fd_bank_sysvar_cache_modify( slot_ctx->bank );
342 :
343 0 : uchar acc_idx_seen[ FD_INSTR_ACCT_MAX ] = {0};
344 0 : for( ulong j=0UL; j < test_ctx->instr_accounts_count; j++ ) {
345 0 : uint index = test_ctx->instr_accounts[j].index;
346 0 : if( index >= test_ctx->accounts_count ) {
347 0 : FD_LOG_NOTICE( ( "instruction account index out of range (%u > %u)", index, test_ctx->instr_accounts_count ) );
348 0 : return 0;
349 0 : }
350 :
351 0 : fd_txn_account_t * acc = &accts[ index ];
352 :
353 : /* Setup instruction accounts */
354 0 : fd_instr_info_setup_instr_account( info,
355 0 : acc_idx_seen,
356 0 : (ushort)index,
357 0 : (ushort)j,
358 0 : (ushort)j,
359 0 : test_ctx->instr_accounts[j].is_writable,
360 0 : test_ctx->instr_accounts[j].is_signer );
361 :
362 0 : if( test_ctx->instr_accounts[j].is_writable ) {
363 0 : fd_txn_account_set_mutable( acc );
364 0 : }
365 0 : }
366 0 : info->acct_cnt = (uchar)test_ctx->instr_accounts_count;
367 :
368 : /* The remaining checks enforce that the program is in the accounts list. */
369 0 : bool found_program_id = false;
370 0 : for( uint i = 0; i < test_ctx->accounts_count; i++ ) {
371 0 : if( 0 == memcmp( test_ctx->accounts[i].address, test_ctx->program_id, sizeof(fd_pubkey_t) ) ) {
372 0 : info->program_id = (uchar) i;
373 0 : found_program_id = true;
374 0 : break;
375 0 : }
376 0 : }
377 :
378 : /* Early returning only happens in instruction execution. */
379 0 : if( !is_syscall && !found_program_id ) {
380 0 : FD_LOG_NOTICE(( " Unable to find program_id in accounts" ));
381 0 : return 0;
382 0 : }
383 :
384 0 : ctx->instr = info;
385 :
386 : /* Refresh the setup from the updated slot and epoch ctx. */
387 0 : fd_exec_txn_ctx_from_exec_slot_ctx( slot_ctx,
388 0 : txn_ctx,
389 0 : funk_wksp,
390 0 : funk_txn_gaddr,
391 0 : funk_gaddr,
392 0 : NULL );
393 :
394 0 : fd_log_collector_init( &ctx->txn_ctx->log_collector, 1 );
395 0 : fd_base58_encode_32( txn_ctx->account_keys[ ctx->instr->program_id ].uc, NULL, ctx->program_id_base58 );
396 :
397 0 : return 1;
398 0 : }
399 :
400 :
401 :
402 : void
403 : fd_runtime_fuzz_instr_ctx_destroy( fd_solfuzz_runner_t * runner,
404 0 : fd_exec_instr_ctx_t * ctx ) {
405 0 : if( !ctx ) return;
406 0 : fd_funk_txn_cancel_all( runner->funk, 1 );
407 0 : }
408 :
409 :
410 : ulong
411 : fd_solfuzz_instr_run( fd_solfuzz_runner_t * runner,
412 : void const * input_,
413 : void ** output_,
414 : void * output_buf,
415 0 : ulong output_bufsz ) {
416 0 : fd_exec_test_instr_context_t const * input = fd_type_pun_const( input_ );
417 0 : fd_exec_test_instr_effects_t ** output = fd_type_pun( output_ );
418 :
419 : /* Convert the Protobuf inputs to a fd_exec context */
420 0 : fd_exec_instr_ctx_t ctx[1];
421 0 : if( !fd_runtime_fuzz_instr_ctx_create( runner, ctx, input, false ) ) {
422 0 : fd_runtime_fuzz_instr_ctx_destroy( runner, ctx );
423 0 : return 0UL;
424 0 : }
425 :
426 0 : fd_instr_info_t * instr = (fd_instr_info_t *) ctx->instr;
427 :
428 : /* Execute the test */
429 0 : int exec_result = fd_execute_instr(ctx->txn_ctx, instr);
430 :
431 : /* Allocate space to capture outputs */
432 :
433 0 : ulong output_end = (ulong)output_buf + output_bufsz;
434 0 : FD_SCRATCH_ALLOC_INIT( l, output_buf );
435 :
436 0 : fd_exec_test_instr_effects_t * effects =
437 0 : FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_exec_test_instr_effects_t),
438 0 : sizeof (fd_exec_test_instr_effects_t) );
439 0 : if( FD_UNLIKELY( _l > output_end ) ) {
440 0 : fd_runtime_fuzz_instr_ctx_destroy( runner, ctx );
441 0 : return 0UL;
442 0 : }
443 0 : fd_memset( effects, 0, sizeof(fd_exec_test_instr_effects_t) );
444 :
445 : /* Capture error code */
446 :
447 0 : effects->result = -exec_result;
448 0 : effects->cu_avail = ctx->txn_ctx->compute_budget_details.compute_meter;
449 :
450 : /* Don't capture custom error codes if the program is a precompile */
451 0 : if( FD_LIKELY( effects->result ) ) {
452 0 : int program_id_idx = ctx->instr[ 0UL ].program_id;
453 0 : if( exec_result==FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR &&
454 0 : fd_executor_lookup_native_precompile_program( &ctx->txn_ctx->accounts[ program_id_idx ] )==NULL ) {
455 0 : effects->custom_err = ctx->txn_ctx->custom_err;
456 0 : }
457 0 : }
458 :
459 : /* Allocate space for captured accounts */
460 0 : ulong modified_acct_cnt = ctx->txn_ctx->accounts_cnt;
461 :
462 0 : fd_exec_test_acct_state_t * modified_accts =
463 0 : FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_exec_test_acct_state_t),
464 0 : sizeof (fd_exec_test_acct_state_t) * modified_acct_cnt );
465 0 : if( FD_UNLIKELY( _l > output_end ) ) {
466 0 : fd_runtime_fuzz_instr_ctx_destroy( runner, ctx );
467 0 : return 0;
468 0 : }
469 0 : effects->modified_accounts = modified_accts;
470 0 : effects->modified_accounts_count = 0UL;
471 :
472 : /* Capture borrowed accounts */
473 :
474 0 : for( ulong j=0UL; j < ctx->txn_ctx->accounts_cnt; j++ ) {
475 0 : fd_txn_account_t * acc = &ctx->txn_ctx->accounts[j];
476 0 : if( !fd_txn_account_get_meta( acc ) ) {
477 0 : continue;
478 0 : }
479 :
480 0 : ulong modified_idx = effects->modified_accounts_count;
481 0 : assert( modified_idx < modified_acct_cnt );
482 :
483 0 : fd_exec_test_acct_state_t * out_acct = &effects->modified_accounts[ modified_idx ];
484 0 : memset( out_acct, 0, sizeof(fd_exec_test_acct_state_t) );
485 : /* Copy over account content */
486 :
487 0 : memcpy( out_acct->address, acc->pubkey, sizeof(fd_pubkey_t) );
488 0 : out_acct->lamports = fd_txn_account_get_lamports( acc );
489 0 : if( fd_txn_account_get_data_len( acc )>0UL ) {
490 0 : out_acct->data =
491 0 : FD_SCRATCH_ALLOC_APPEND( l, alignof(pb_bytes_array_t),
492 0 : PB_BYTES_ARRAY_T_ALLOCSIZE( fd_txn_account_get_data_len( acc ) ) );
493 0 : if( FD_UNLIKELY( _l > output_end ) ) {
494 0 : fd_runtime_fuzz_instr_ctx_destroy( runner, ctx );
495 0 : return 0UL;
496 0 : }
497 0 : out_acct->data->size = (pb_size_t)fd_txn_account_get_data_len( acc );
498 0 : fd_memcpy( out_acct->data->bytes, fd_txn_account_get_data( acc ), fd_txn_account_get_data_len( acc ) );
499 0 : }
500 :
501 0 : out_acct->executable = fd_txn_account_is_executable( acc );
502 0 : memcpy( out_acct->owner, fd_txn_account_get_owner( acc ), sizeof(fd_pubkey_t) );
503 :
504 0 : effects->modified_accounts_count++;
505 0 : }
506 :
507 : /* Capture return data */
508 0 : fd_txn_return_data_t * return_data = &ctx->txn_ctx->return_data;
509 0 : if( return_data->len>0UL ) {
510 0 : effects->return_data = FD_SCRATCH_ALLOC_APPEND(l, alignof(pb_bytes_array_t),
511 0 : PB_BYTES_ARRAY_T_ALLOCSIZE( return_data->len ) );
512 0 : if( FD_UNLIKELY( _l > output_end ) ) {
513 0 : fd_runtime_fuzz_instr_ctx_destroy( runner, ctx );
514 0 : return 0UL;
515 0 : }
516 0 : effects->return_data->size = (pb_size_t)return_data->len;
517 0 : fd_memcpy( effects->return_data->bytes, return_data->data, return_data->len );
518 0 : }
519 :
520 0 : ulong actual_end = FD_SCRATCH_ALLOC_FINI( l, 1UL );
521 0 : fd_runtime_fuzz_instr_ctx_destroy( runner, ctx );
522 :
523 0 : *output = effects;
524 0 : return actual_end - (ulong)output_buf;
525 :
526 0 : }
|