LCOV - code coverage report
Current view: top level - flamenco/runtime/tests - fd_instr_harness.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 330 352 93.8 %
Date: 2026-11-11 12:40:43 Functions: 4 4 100.0 %

          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 : }

Generated by: LCOV version 1.14