Line data Source code
1 : #include "fd_solfuzz_private.h"
2 : #include "fd_instr_harness.h"
3 : #include "../fd_executor.h"
4 : #include "../fd_runtime.h"
5 : #include "../program/fd_bpf_loader_program.h"
6 : #include "../program/fd_precompiles.h"
7 : #include "../fd_system_ids.h"
8 : #include "../../progcache/fd_progcache_admin.h"
9 : #include "../../alpenglow/fd_alpenglow.h"
10 : #include "../../log_collector/fd_log_collector.h"
11 :
12 : void
13 : fd_solfuzz_pb_instr_ctx_create( fd_solfuzz_runner_t * runner,
14 : fd_exec_instr_ctx_t * ctx,
15 55498 : fd_exec_test_instr_context_t const * test_ctx ) {
16 :
17 55498 : memset( ctx, 0, sizeof(fd_exec_instr_ctx_t) );
18 :
19 : /* Create temporary fork for account loading */
20 :
21 55498 : runner->bank->progcache_fork_id = fd_progcache_attach_child( runner->progcache->join, fd_progcache_fork_id_initial() );
22 55498 : runner->bank->accdb_fork_id = fd_accdb_attach_child( runner->accdb, runner->root_fork_id );
23 55498 : runner->bank->parent_accdb_fork_id = runner->bank->accdb_fork_id;
24 :
25 55498 : fd_txn_in_t * txn_in = fd_spad_alloc( runner->spad, alignof(fd_txn_in_t), sizeof(fd_txn_in_t) );
26 55498 : fd_txn_out_t * txn_out = fd_spad_alloc( runner->spad, alignof(fd_txn_out_t), sizeof(fd_txn_out_t) );
27 :
28 55498 : fd_log_collector_t * log = fd_spad_alloc( runner->spad, alignof(fd_log_collector_t), sizeof(fd_log_collector_t) );
29 :
30 55498 : fd_runtime_t * runtime = runner->runtime;
31 :
32 55498 : runtime->log.log_collector = log;
33 :
34 55498 : ctx->txn_out = txn_out;
35 55498 : ctx->txn_in = txn_in;
36 :
37 55498 : fd_memset( txn_out->accounts.keys, 0, sizeof(fd_pubkey_t)*MAX_TX_ACCOUNT_LOCKS );
38 55498 : fd_memset( runtime->accounts.account, 0, sizeof(fd_acc_t)*MAX_TX_ACCOUNT_LOCKS );
39 55498 : fd_memset( runtime->accounts.executable, 0, sizeof(fd_acc_t)*MAX_TX_ACCOUNT_LOCKS );
40 3574407 : for( ulong j=0UL; j<MAX_TX_ACCOUNT_LOCKS; j++ ) {
41 3518909 : txn_out->accounts.account[ j ] = &runtime->accounts.account[ j ];
42 3518909 : txn_out->accounts.executable[ j ] = &runtime->accounts.executable[ j ];
43 3518909 : }
44 55498 : txn_out->accounts.executable_cnt = 0UL;
45 55498 : fd_memset( txn_out->accounts.executable_from_parent, 0, sizeof(txn_out->accounts.executable_from_parent) );
46 55498 : fd_memset( txn_out->accounts.executable_pd_write, 0, sizeof(txn_out->accounts.executable_pd_write) );
47 55498 : txn_out->accounts.executable_skipped_cnt = 0;
48 :
49 : /* Bank manager */
50 55498 : fd_banks_clear_bank( runner->banks, runner->bank );
51 :
52 : /* Restore features */
53 55498 : FD_TEST( test_ctx->has_features );
54 55498 : fd_features_t * features = &runner->bank->f.features;
55 55498 : fd_exec_test_feature_set_t const * feature_set = &test_ctx->features;
56 55498 : FD_TEST( fd_solfuzz_pb_restore_features( features, feature_set ) );
57 :
58 : /* Blockhash queue init */
59 55498 : ulong blockhash_seed; FD_TEST( fd_rng_secure( &blockhash_seed, sizeof(ulong) ) );
60 55498 : fd_blockhashes_t * blockhashes = fd_blockhashes_init( &runner->bank->f.block_hash_queue, blockhash_seed );
61 55498 : fd_blockhash_info_t * placeholder = fd_blockhash_deq_push_tail_nocopy( blockhashes->d.deque );
62 55498 : fd_memset( placeholder, 0, sizeof(fd_hash_t) );
63 55498 : placeholder->hash_index = 0UL;
64 :
65 : /* Set up instruction context */
66 55498 : fd_instr_info_t * info = fd_instr_info_new( &runtime->instr.trace[ 0UL ] );
67 55498 : info->stack_height = 1;
68 :
69 : /* Set up mock txn descriptor and payload
70 : FIXME: More fields may need to be initialized. This seems to be
71 : the minimal set of fields needed to retain full context for
72 : precompile execution. */
73 55498 : fd_txn_p_t * txn = fd_spad_alloc_check( runner->spad, alignof(fd_txn_p_t), sizeof(fd_txn_p_t) );
74 55498 : fd_txn_t * txn_descriptor = TXN( txn );
75 :
76 55498 : txn->payload_sz = 0UL;
77 55498 : if( test_ctx->data ) {
78 47811 : if( FD_UNLIKELY( test_ctx->data->size>FD_INSTR_DATA_MAX ) ) {
79 0 : FD_LOG_ERR(( "invariant violation: instr data sz is too large %u > %lu", test_ctx->data->size, FD_INSTR_DATA_MAX ));
80 0 : }
81 47811 : info->data_sz = (ushort)test_ctx->data->size;
82 47811 : memcpy( info->data, test_ctx->data->bytes, info->data_sz );
83 :
84 47811 : ulong payload_sz = fd_ulong_min( test_ctx->data->size, sizeof(txn->payload) );
85 47811 : memcpy( txn->payload, test_ctx->data->bytes, payload_sz );
86 47811 : txn->payload_sz = (ushort)payload_sz;
87 47811 : }
88 55498 : txn_descriptor->transaction_version = FD_TXN_VLEGACY;
89 55498 : txn_descriptor->acct_addr_cnt = (ushort)test_ctx->accounts_count;
90 55498 : txn_descriptor->instr_cnt = 1;
91 55498 : txn_descriptor->instr[0] = (fd_txn_instr_t) {
92 55498 : .acct_cnt = (ushort)test_ctx->accounts_count,
93 55498 : .data_off = 0,
94 55498 : .data_sz = (ushort)txn->payload_sz,
95 55498 : };
96 :
97 55498 : runtime->log.enable_log_collector = 0;
98 :
99 55498 : fd_compute_budget_details_new( &txn_out->details.compute_budget );
100 55498 : runtime->instr.stack_sz = 0;
101 55498 : txn_out->accounts.cnt = 0UL;
102 55498 : txn_out->accounts.executable_cnt = 0UL;
103 :
104 55498 : txn_out->details.loaded_accounts_data_size = 0UL;
105 55498 : txn_out->details.accounts_resize_delta = 0L;
106 :
107 55498 : memset( txn_out->details.return_data.program_id.key, 0, sizeof(fd_pubkey_t) );
108 55498 : txn_out->details.return_data.len = 0;
109 :
110 55498 : runtime->log.capture_ctx = NULL;
111 55498 : runtime->log.dump_proto_ctx = NULL;
112 55498 : runtime->log.txn_dump_ctx = NULL;
113 :
114 55498 : runtime->instr.trace_length = 1UL;
115 :
116 55498 : txn_out->err.exec_err = 0;
117 55498 : txn_out->err.exec_err_kind = FD_EXECUTOR_ERR_KIND_NONE;
118 55498 : runtime->instr.current_idx = 0;
119 :
120 55498 : txn_in->txn = txn;
121 55498 : txn_out->details.compute_budget.compute_unit_limit = test_ctx->cu_avail;
122 55498 : txn_out->details.compute_budget.compute_meter = test_ctx->cu_avail;
123 55498 : runtime->log.enable_vm_tracing = runner->enable_vm_tracing;
124 55498 : runtime->log.tracing_mem = runner->enable_vm_tracing ?
125 0 : fd_spad_alloc_check( runner->spad, FD_RUNTIME_VM_TRACE_STATIC_ALIGN, FD_RUNTIME_VM_TRACE_STATIC_FOOTPRINT * FD_MAX_INSTRUCTION_STACK_DEPTH ) :
126 55498 : NULL;
127 :
128 : /* Prepare borrowed account table (correctly handles aliasing) */
129 :
130 55498 : if( FD_UNLIKELY( test_ctx->accounts_count > MAX_TX_ACCOUNT_LOCKS ) ) {
131 0 : FD_LOG_ERR(( "invariant violation: too many accounts (%lu > %lu)",
132 0 : (ulong)test_ctx->accounts_count, (ulong)MAX_TX_ACCOUNT_LOCKS ));
133 0 : }
134 :
135 : /* Load accounts from input */
136 :
137 : /* Mimic Agave's mock_compile_message: put the program + referenced
138 : instruction accounts in the first txn slots, the rest after. */
139 55498 : uchar account_in_message[ MAX_TX_ACCOUNT_LOCKS ] = {0};
140 55498 : uint input_txn_idx [ MAX_TX_ACCOUNT_LOCKS ];
141 :
142 459856 : for( ulong i=0UL; i<test_ctx->instr_accounts_count; i++ ) {
143 404358 : uint index = test_ctx->instr_accounts[ i ].index;
144 404358 : if( FD_UNLIKELY( index>=test_ctx->accounts_count ) ) {
145 0 : FD_LOG_ERR(( "invariant violation: instruction account index out of range (%u >= %u)",
146 0 : index, test_ctx->accounts_count ));
147 0 : }
148 404358 : account_in_message[ index ] = 1;
149 404358 : }
150 :
151 55498 : ulong program_idx = ULONG_MAX;
152 166142 : for( ulong i=0UL; i<test_ctx->accounts_count; i++ ) {
153 166096 : if( !memcmp( test_ctx->accounts[ i ].address, test_ctx->program_id, sizeof(fd_pubkey_t) ) ) {
154 55452 : account_in_message[ i ] = 1;
155 55452 : program_idx = i;
156 55452 : break;
157 55452 : }
158 166096 : }
159 :
160 : /* Ensure the program id is in the set of accounts */
161 55498 : FD_TEST( program_idx!=ULONG_MAX );
162 :
163 : /* Compile the message by filling the transaction accounts with
164 : only accounts referenced by the instruction. */
165 55498 : uint message_account_cnt = 0U;
166 55498 : uint tail_txn_idx = test_ctx->accounts_count;
167 652371 : for( ulong i=0UL; i<test_ctx->accounts_count; i++ ) {
168 596873 : input_txn_idx[ i ] = account_in_message[ i ] ? message_account_cnt++ : --tail_txn_idx;
169 596873 : }
170 :
171 55498 : info->program_id = (uchar)input_txn_idx[ program_idx ];
172 55498 : txn_out->accounts.cnt = message_account_cnt;
173 :
174 55498 : ulong loaded_data_sz = 0UL;
175 646507 : for( ulong j=0UL; j < test_ctx->accounts_count; j++ ) {
176 591009 : if( !account_in_message[j] ) continue;
177 :
178 310557 : ulong txn_idx = input_txn_idx[j];
179 310557 : fd_pubkey_t * acc_key = (fd_pubkey_t *)test_ctx->accounts[j].address;
180 :
181 310557 : fd_pb_bytes_array_t const * in_data = fd_solfuzz_acct_data( &test_ctx->accounts[j] );
182 310557 : uint dlen = in_data ? in_data->size : 0U;
183 310557 : loaded_data_sz += dlen;
184 310557 : if( FD_UNLIKELY( dlen>FD_RUNTIME_ACC_SZ_MAX || loaded_data_sz>FD_VM_LOADED_ACCOUNTS_DATA_SIZE_LIMIT ) ) {
185 0 : FD_LOG_ERR(( "account data too large (dlen=%u total=%lu)", dlen, loaded_data_sz ));
186 0 : }
187 310557 : uchar * data_buf = fd_spad_alloc( runner->spad, FD_ACCOUNT_REC_ALIGN, FD_RUNTIME_ACC_SZ_MAX );
188 310557 : if( dlen ) {
189 279962 : fd_memcpy( data_buf, in_data->bytes, dlen );
190 279962 : }
191 :
192 : /* Initialize entry with in-memory account data (no DB backing) */
193 310557 : fd_acc_t * acc = txn_out->accounts.account[txn_idx];
194 310557 : memcpy( acc->pubkey, acc_key->key, 32 );
195 310557 : memcpy( acc->owner, test_ctx->accounts[j].owner, 32 );
196 310557 : acc->lamports = test_ctx->accounts[j].lamports;
197 310557 : acc->executable = test_ctx->accounts[j].executable;
198 310557 : acc->data_len = dlen;
199 310557 : acc->data = data_buf;
200 310557 : acc->_writable = 1;
201 310557 : acc->commit = 0;
202 :
203 310557 : txn_out->accounts.is_writable[txn_idx] = 1U;
204 310557 : runtime->accounts.refcnt[txn_idx] = 0UL;
205 310557 : txn_out->accounts.keys[txn_idx] = *acc_key;
206 310557 : }
207 :
208 : /* Load in executable accounts */
209 651412 : for( ulong i=0UL; i<test_ctx->accounts_count; i++ ) {
210 595914 : fd_exec_test_acct_state_t const * prog = &test_ctx->accounts[i];
211 595914 : fd_pubkey_t const * owner = fd_type_pun_const( prog->owner );
212 :
213 595914 : if( !fd_executor_pubkey_is_bpf_loader( owner ) ) {
214 542125 : continue;
215 542125 : }
216 :
217 53789 : if( FD_UNLIKELY( !memcmp( owner, fd_solana_bpf_loader_upgradeable_program_id.key, sizeof(fd_pubkey_t) ) ) ) {
218 30783 : fd_pb_bytes_array_t const * prog_data = fd_solfuzz_acct_data( prog );
219 30783 : if( FD_UNLIKELY( !prog_data ) ) continue;
220 :
221 28271 : fd_bpf_state_t program_loader_state[1];
222 28271 : int err = fd_bpf_loader_program_get_state2( prog_data->bytes, prog_data->size, program_loader_state );
223 28271 : if( FD_UNLIKELY( err!=FD_EXECUTOR_INSTR_SUCCESS ) ) {
224 4034 : continue;
225 4034 : }
226 :
227 24237 : if( program_loader_state->discriminant!=FD_BPF_STATE_PROGRAM ) {
228 14240 : continue;
229 14240 : }
230 :
231 9997 : fd_pubkey_t * programdata_acc = &program_loader_state->inner.program.programdata_address;
232 :
233 9997 : fd_exec_test_acct_state_t const * pd = NULL;
234 81620 : for( ulong j=0UL; j<test_ctx->accounts_count; j++ ) {
235 79834 : if( !memcmp( test_ctx->accounts[j].address, programdata_acc, sizeof(fd_pubkey_t) ) ) {
236 8211 : pd = &test_ctx->accounts[j];
237 8211 : break;
238 8211 : }
239 79834 : }
240 9997 : fd_pb_bytes_array_t const * pd_data = pd ? fd_solfuzz_acct_data( pd ) : NULL;
241 9997 : if( FD_UNLIKELY( pd==NULL || !pd_data ) ) {
242 1982 : continue;
243 1982 : }
244 :
245 8015 : FD_TEST( txn_out->accounts.executable_cnt < MAX_TX_ACCOUNT_LOCKS );
246 8015 : fd_acc_t * exe = txn_out->accounts.executable[ txn_out->accounts.executable_cnt ];
247 8015 : memcpy( exe->pubkey, programdata_acc->key, 32 );
248 8015 : memcpy( exe->owner, pd->owner, sizeof(fd_pubkey_t) );
249 : /* Agave loads a program into its ProgramCache from the
250 : programdata bytes independently of the on-chain lamports
251 : snapshot. An instruction fixture may capture a programdata
252 : account with zero lamports (a real 0-lamport account would
253 : otherwise have all its other fields cleared between
254 : transactions), and still expect the program to execute. The
255 : runtime treats an executable account as "deployed" only when it
256 : exists, so present the programdata as existing whenever it
257 : carries program data. This executable account is read-only on
258 : the invoke path and its lamports are never consumed, only used
259 : as an existence gate. */
260 8015 : exe->lamports = ( !pd->lamports && pd_data->size ) ? 1UL : pd->lamports;
261 8015 : exe->executable = pd->executable;
262 8015 : exe->data_len = pd_data->size;
263 8015 : exe->data = (uchar *)pd_data->bytes;
264 8015 : txn_out->accounts.executable_cnt++;
265 8015 : }
266 53789 : }
267 :
268 : /* Load instruction accounts */
269 :
270 55498 : if( FD_UNLIKELY( test_ctx->instr_accounts_count > FD_TXN_INSTR_ACCT_MAX ) ) {
271 0 : FD_LOG_ERR(( "invariant violation: too many instruction accounts (%lu > %lu)",
272 0 : (ulong)test_ctx->instr_accounts_count, (ulong)FD_TXN_INSTR_ACCT_MAX ));
273 0 : }
274 :
275 : /* Restore sysvar cache */
276 55498 : fd_sysvar_cache_t * sysvar_cache = fd_sysvar_cache_join( fd_sysvar_cache_new( runtime->sysvar_cache ) );
277 55498 : runtime->sysvar_bank_seq = runner->bank->bank_seq;
278 55498 : ctx->sysvar_cache = sysvar_cache;
279 649328 : for( ulong i=0UL; i<test_ctx->accounts_count; i++ ) {
280 593830 : fd_exec_test_acct_state_t const * account = &test_ctx->accounts[i];
281 593830 : fd_pb_bytes_array_t const * data = fd_solfuzz_acct_data( account );
282 593830 : if( FD_UNLIKELY( !data ) ) continue;
283 :
284 553371 : fd_pubkey_t const * address = fd_type_pun_const( account->address );
285 553371 : fd_sysvar_cache_restore_one( sysvar_cache,
286 553371 : address,
287 553371 : account->lamports,
288 553371 : data->bytes,
289 553371 : data->size );
290 553371 : }
291 :
292 55498 : ctx->runtime = runtime;
293 :
294 55498 : fd_sol_sysvar_clock_t clock_[1];
295 55498 : fd_sol_sysvar_clock_t * clock = fd_sysvar_cache_clock_read( ctx->sysvar_cache, clock_ );
296 55498 : FD_TEST( clock );
297 55498 : runner->bank->f.slot = clock->slot;
298 :
299 55498 : runner->bank->progcache_fork_id = fd_progcache_attach_child( runner->progcache->join, runner->bank->progcache_fork_id );
300 :
301 55498 : fd_epoch_schedule_t epoch_schedule_[1];
302 55498 : fd_epoch_schedule_t * epoch_schedule = fd_sysvar_cache_epoch_schedule_read( ctx->sysvar_cache, epoch_schedule_ );
303 55498 : FD_TEST( epoch_schedule );
304 55498 : runner->bank->f.epoch_schedule = *epoch_schedule;
305 :
306 55498 : fd_rent_t rent_[1];
307 55498 : fd_rent_t * rent = fd_sysvar_cache_rent_read( ctx->sysvar_cache, rent_ );
308 55498 : FD_TEST( rent );
309 55498 : runner->bank->f.rent = *rent;
310 55498 : runner->bank->f.alpenglow_migration_slot = fd_alpenglow_migration_slot( runner->bank, runner->accdb );
311 :
312 55498 : if( !fd_sysvar_cache_recent_hashes_is_empty( sysvar_cache ) ) {
313 55451 : uchar const * rbh_data = sysvar_cache->bin_recent_hashes;
314 55451 : ulong rbh_len = FD_LOAD( ulong, rbh_data );
315 55451 : ulong entry_off = sizeof(ulong) + ((rbh_len - 1UL) * 40UL);
316 55451 : uchar const * entry = rbh_data + entry_off;
317 55451 : FD_TEST( entry_off+40UL <= sysvar_cache->desc[ FD_SYSVAR_recent_hashes_IDX ].data_sz );
318 :
319 55451 : fd_blockhashes_t * blockhashes = &runner->bank->f.block_hash_queue;
320 55451 : fd_blockhashes_pop_new( blockhashes );
321 55451 : fd_hash_t hash = FD_LOAD( fd_hash_t, entry );
322 55451 : fd_blockhash_info_t * info = fd_blockhashes_push_new( blockhashes, &hash );
323 55451 : info->lamports_per_signature = runner->bank->f.rbh_lamports_per_sig =
324 55451 : FD_LOAD( ulong, entry+32UL );
325 55451 : }
326 :
327 : /* Agave compiles the instruction into a message, OR-ing duplicate
328 : accounts' writable/signer flags. */
329 55498 : uchar instr_is_writable[ FD_TXN_ACCT_ADDR_MAX ] = {0};
330 55498 : uchar instr_is_signer [ FD_TXN_ACCT_ADDR_MAX ] = {0};
331 55498 : int bpf_upgradeable_present = !memcmp( test_ctx->program_id, fd_solana_bpf_loader_upgradeable_program_id.key, sizeof(fd_pubkey_t) );
332 459007 : for( ulong j=0UL; j < test_ctx->instr_accounts_count; j++ ) {
333 403509 : uint index = test_ctx->instr_accounts[j].index;
334 :
335 403509 : instr_is_writable[index] = (uchar)( instr_is_writable[index] | test_ctx->instr_accounts[j].is_writable );
336 403509 : instr_is_signer[index] = (uchar)( instr_is_signer[index] | test_ctx->instr_accounts[j].is_signer );
337 403509 : if( !memcmp( test_ctx->accounts[index].address, fd_solana_bpf_loader_upgradeable_program_id.key, sizeof(fd_pubkey_t) ) ) {
338 1844 : bpf_upgradeable_present = 1;
339 1844 : }
340 403509 : }
341 :
342 55498 : uchar acc_idx_seen[ FD_TXN_ACCT_ADDR_MAX ] = {0};
343 458365 : for( ulong j=0UL; j < test_ctx->instr_accounts_count; j++ ) {
344 402867 : uint index = test_ctx->instr_accounts[j].index;
345 :
346 : /* A program account is demoted to read-only unless the upgradeable
347 : loader is present. */
348 402867 : uchar is_writable = instr_is_writable[index];
349 402867 : if( !bpf_upgradeable_present && !memcmp( test_ctx->accounts[index].address, test_ctx->program_id, sizeof(fd_pubkey_t) ) ) {
350 13424 : is_writable = 0;
351 13424 : }
352 :
353 : /* Setup instruction accounts */
354 402867 : fd_instr_info_setup_instr_account( info,
355 402867 : acc_idx_seen,
356 402867 : (ushort)input_txn_idx[index],
357 402867 : (ushort)j,
358 402867 : (ushort)j,
359 402867 : is_writable,
360 402867 : instr_is_signer[index] );
361 402867 : }
362 55498 : info->acct_cnt = (ushort)test_ctx->instr_accounts_count;
363 :
364 55498 : ctx->instr = info;
365 55498 : ctx->runtime->progcache = runner->progcache;
366 55498 : ctx->runtime->accdb = runner->accdb;
367 :
368 55498 : runtime->log.enable_log_collector = 0;
369 :
370 55498 : fd_log_collector_init( ctx->runtime->log.log_collector, 0 );
371 55498 : fd_base58_encode_32( txn_out->accounts.keys[ ctx->instr->program_id ].uc, NULL, ctx->program_id_base58 );
372 55498 : }
373 :
374 : void
375 : fd_solfuzz_pb_instr_ctx_destroy( fd_solfuzz_runner_t * runner,
376 55620 : fd_exec_instr_ctx_t * ctx ) {
377 55620 : if( !ctx ) return;
378 :
379 55620 : fd_progcache_reset( runner->progcache->join );
380 :
381 : /* Purge the fork attached in ctx_create so the accdb fork pool slot
382 : is released back for reuse. Without this, repeated harness
383 : invocations (e.g. under a fuzzer) exhaust max_live_slots. */
384 55620 : fd_accdb_purge( runner->accdb, runner->bank->accdb_fork_id );
385 55620 : int charge_busy = 0;
386 55620 : fd_accdb_background( runner->accdb, &charge_busy );
387 :
388 55620 : }
389 :
390 : /* Txn index for addr among the compiled-message accounts [0,cnt).
391 : Returns ULONG_MAX if not found. */
392 : static ulong
393 476639 : instr_harness_idx_of( fd_txn_out_t const * txn_out, uchar const * addr ) {
394 5016871 : for( ulong i=0UL; i<txn_out->accounts.cnt; i++ ) {
395 4784134 : if( !memcmp( txn_out->accounts.keys[i].key, addr, sizeof(fd_pubkey_t) ) ) return i;
396 4784134 : }
397 232737 : return ULONG_MAX;
398 476639 : }
399 :
400 : ulong
401 : fd_solfuzz_pb_instr_run( fd_solfuzz_runner_t * runner,
402 : void const * input_,
403 : void ** output_,
404 : void * output_buf,
405 46960 : ulong output_bufsz ) {
406 46960 : fd_exec_test_instr_context_t const * input = fd_type_pun_const( input_ );
407 46960 : fd_exec_test_instr_effects_t ** output = fd_type_pun( output_ );
408 :
409 : /* Convert the Protobuf inputs to a fd_exec context */
410 46960 : fd_exec_instr_ctx_t ctx[1];
411 46960 : fd_solfuzz_pb_instr_ctx_create( runner, ctx, input );
412 :
413 46960 : fd_instr_info_t * instr = (fd_instr_info_t *) ctx->instr;
414 :
415 : /* Execute the test */
416 46960 : int exec_result = fd_execute_instr( ctx->runtime, runner->bank, ctx->txn_in, ctx->txn_out, instr );
417 :
418 : /* Allocate space to capture outputs */
419 46960 : ulong output_end = (ulong)output_buf + output_bufsz;
420 46960 : FD_SCRATCH_ALLOC_INIT( l, output_buf );
421 :
422 46960 : fd_exec_test_instr_effects_t * effects =
423 46960 : FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_exec_test_instr_effects_t),
424 46960 : sizeof (fd_exec_test_instr_effects_t) );
425 46960 : if( FD_UNLIKELY( _l > output_end ) ) {
426 0 : fd_solfuzz_pb_instr_ctx_destroy( runner, ctx );
427 0 : return 0UL;
428 0 : }
429 46960 : fd_memset( effects, 0, sizeof(fd_exec_test_instr_effects_t) );
430 :
431 : /* Capture error code */
432 :
433 46960 : effects->result = -exec_result;
434 46960 : effects->cu_avail = ctx->txn_out->details.compute_budget.compute_meter;
435 :
436 : /* Don't capture custom error codes if the program is a precompile */
437 46960 : if( FD_LIKELY( effects->result ) ) {
438 30645 : int program_id_idx = ctx->instr[ 0UL ].program_id;
439 30645 : if( exec_result==FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR &&
440 30645 : fd_executor_lookup_native_precompile_program( &ctx->txn_out->accounts.keys[ program_id_idx ] )==NULL ) {
441 1197 : effects->custom_err = ctx->txn_out->err.custom_err;
442 1197 : }
443 30645 : }
444 :
445 : /* Allocate space for captured accounts */
446 46960 : ulong modified_acct_cnt = input->accounts_count;
447 :
448 46960 : fd_exec_test_acct_state_t * modified_accts =
449 46960 : FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_exec_test_acct_state_t),
450 46960 : sizeof (fd_exec_test_acct_state_t) * modified_acct_cnt );
451 46960 : if( FD_UNLIKELY( _l > output_end ) ) {
452 0 : fd_solfuzz_pb_instr_ctx_destroy( runner, ctx );
453 0 : return 0;
454 0 : }
455 46960 : effects->modified_accounts = modified_accts;
456 46960 : effects->modified_accounts_count = 0UL;
457 :
458 : /* Capture any accounts which may have changed */
459 :
460 523629 : for( ulong j=0UL; j < input->accounts_count; j++ ) {
461 476669 : fd_exec_test_acct_state_t const * in_acct = &input->accounts[j];
462 :
463 476669 : ulong lamports;
464 476669 : uchar const * data;
465 476669 : ulong data_len;
466 476669 : int executable;
467 476669 : uchar const * owner;
468 :
469 : /* Capture the account from the message in case it was modified,
470 : or just report the input account otherwise. */
471 476669 : ulong txn_idx = instr_harness_idx_of( ctx->txn_out, in_acct->address );
472 476669 : if( txn_idx!=ULONG_MAX ) {
473 243962 : fd_acc_t const * acc = ctx->txn_out->accounts.account[txn_idx];
474 243962 : lamports = acc->lamports;
475 243962 : data = acc->data;
476 243962 : data_len = acc->data_len;
477 243962 : executable = acc->executable;
478 243962 : owner = acc->owner;
479 243962 : } else {
480 232707 : fd_pb_bytes_array_t const * in_data = fd_solfuzz_acct_data( in_acct );
481 232707 : lamports = in_acct->lamports;
482 232707 : data = in_data ? in_data->bytes : NULL;
483 232707 : data_len = in_data ? in_data->size : 0UL;
484 232707 : executable = in_acct->executable;
485 232707 : owner = in_acct->owner;
486 232707 : }
487 :
488 476669 : ulong modified_idx = effects->modified_accounts_count;
489 476669 : if( FD_UNLIKELY( modified_idx >= modified_acct_cnt ) ) {
490 0 : FD_LOG_CRIT(( "invalid modified account index" ));
491 0 : }
492 :
493 476669 : fd_exec_test_acct_state_t * out_acct = &effects->modified_accounts[ modified_idx ];
494 476669 : memset( out_acct, 0, sizeof(fd_exec_test_acct_state_t) );
495 :
496 : /* Copy over account content */
497 476669 : memcpy( out_acct->address, in_acct->address, sizeof(fd_pubkey_t) );
498 476669 : out_acct->lamports = lamports;
499 476669 : fd_solfuzz_acct_set_data_hash( out_acct, data, data_len );
500 :
501 476669 : out_acct->executable = executable;
502 476669 : memcpy( out_acct->owner, owner, sizeof(fd_pubkey_t) );
503 :
504 476669 : effects->modified_accounts_count++;
505 476669 : }
506 :
507 46960 : fd_solfuzz_direct_mapping_handle_cu_exhaustion(
508 46960 : runner, effects->cu_avail, effects->result,
509 46960 : effects->modified_accounts, (uint)effects->modified_accounts_count );
510 :
511 : /* Capture return data */
512 46960 : fd_txn_return_data_t * return_data = &ctx->txn_out->details.return_data;
513 46960 : effects->return_data_hash = fd_solfuzz_hash( return_data->data, return_data->len );
514 :
515 46960 : ulong actual_end = FD_SCRATCH_ALLOC_FINI( l, 1UL );
516 46960 : fd_solfuzz_pb_instr_ctx_destroy( runner, ctx );
517 :
518 46960 : *output = effects;
519 46960 : return actual_end - (ulong)output_buf;
520 46960 : }
|