LCOV - code coverage report
Current view: top level - flamenco/runtime/tests - fd_exec_instr_test.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 1028 1325 77.6 %
Date: 2024-11-13 11:58:15 Functions: 22 25 88.0 %

          Line data    Source code
       1             : 
       2             : #include "generated/invoke.pb.h"
       3             : #undef FD_SCRATCH_USE_HANDHOLDING
       4             : #define FD_SCRATCH_USE_HANDHOLDING 1
       5             : #include "fd_exec_instr_test.h"
       6             : #include "../fd_acc_mgr.h"
       7             : #include "../fd_account.h"
       8             : #include "../fd_executor.h"
       9             : #include "../fd_runtime.h"
      10             : #include "../program/fd_bpf_loader_program.h"
      11             : #include "../program/fd_bpf_program_util.h"
      12             : #include "../program/fd_builtin_programs.h"
      13             : #include "../context/fd_exec_epoch_ctx.h"
      14             : #include "../context/fd_exec_slot_ctx.h"
      15             : #include "../context/fd_exec_txn_ctx.h"
      16             : #include "../sysvar/fd_sysvar_recent_hashes.h"
      17             : #include "../sysvar/fd_sysvar_last_restart_slot.h"
      18             : #include "../sysvar/fd_sysvar_slot_hashes.h"
      19             : #include "../sysvar/fd_sysvar_stake_history.h"
      20             : #include "../sysvar/fd_sysvar_epoch_rewards.h"
      21             : #include "../../../funk/fd_funk.h"
      22             : #include "../../../util/bits/fd_float.h"
      23             : #include "../../../ballet/sbpf/fd_sbpf_loader.h"
      24             : #include "../../../ballet/elf/fd_elf.h"
      25             : #include "../../vm/fd_vm.h"
      26             : #include <assert.h>
      27             : #include "../sysvar/fd_sysvar_cache.h"
      28             : #include "../sysvar/fd_sysvar_epoch_schedule.h"
      29             : #include "../sysvar/fd_sysvar_clock.h"
      30             : #include "../../../ballet/pack/fd_pack.h"
      31             : #include "fd_vm_test.h"
      32             : 
      33             : #pragma GCC diagnostic ignored "-Wformat-extra-args"
      34             : 
      35             : /* LOGFMT_REPORT is the log prefix for instruction processing tests */
      36             : 
      37             : #define LOGFMT_REPORT "%s"
      38             : static FD_TL char _report_prefix[100] = {0};
      39             : 
      40             : #define REPORTV( level, fmt, ... ) \
      41           0 :   FD_LOG_##level(( LOGFMT_REPORT fmt, _report_prefix, __VA_ARGS__ ))
      42             : 
      43           0 : #define REPORT( level, fmt ) REPORTV( level, fmt, 0 )
      44             : 
      45             : #define REPORT_ACCTV( level, addr, fmt, ... )                                  \
      46           0 :   do {                                                                         \
      47           0 :     char         _acct_log_private_addr[ FD_BASE58_ENCODED_32_SZ ];            \
      48           0 :     void const * _acct_log_private_addr_ptr = (addr);                          \
      49           0 :     fd_acct_addr_cstr( _acct_log_private_addr, _acct_log_private_addr_ptr );        \
      50           0 :     REPORTV( level, " account %s: " fmt, _acct_log_private_addr, __VA_ARGS__ ); \
      51           0 :   } while(0);
      52             : 
      53           0 : #define REPORT_ACCT( level, addr, fmt ) REPORT_ACCTV( level, addr, fmt, 0 )
      54             : 
      55             : /* Define routine to sort accounts to support query-by-pubkey via
      56             :    binary search. */
      57             : 
      58             : #define SORT_NAME sort_pubkey_p
      59     1881819 : #define SORT_KEY_T void const *
      60     1717095 : #define SORT_BEFORE(a,b) ( memcmp( (a), (b), sizeof(fd_pubkey_t) )<0 )
      61             : #include "../../../util/tmpl/fd_sort.c"
      62             : #include "../../vm/fd_vm_base.h"
      63             : 
      64             : struct __attribute__((aligned(32UL))) fd_exec_instr_test_runner_private {
      65             :   fd_funk_t * funk;
      66             :   fd_spad_t * spad;
      67             : };
      68             : 
      69             : ulong
      70      110235 : fd_exec_instr_test_runner_align( void ) {
      71      110235 :   return alignof(fd_exec_instr_test_runner_t);
      72      110235 : }
      73             : 
      74             : ulong
      75      110235 : fd_exec_instr_test_runner_footprint( void ) {
      76      110235 :   ulong l = FD_LAYOUT_INIT;
      77      110235 :   l = FD_LAYOUT_APPEND( l, alignof(fd_exec_instr_test_runner_t), sizeof(fd_exec_instr_test_runner_t) );
      78      110235 :   l = FD_LAYOUT_APPEND( l, fd_funk_align(),                      fd_funk_footprint()                 );
      79      110235 :   return l;
      80      110235 : }
      81             : 
      82             : fd_exec_instr_test_runner_t *
      83             : fd_exec_instr_test_runner_new( void * mem,
      84             :                                void * spad_mem,
      85      110235 :                                ulong  wksp_tag ) {
      86      110235 :   FD_SCRATCH_ALLOC_INIT( l, mem );
      87      110235 :   void * runner_mem = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_exec_instr_test_runner_t), sizeof(fd_exec_instr_test_runner_t) );
      88      110235 :   void * funk_mem   = FD_SCRATCH_ALLOC_APPEND( l, fd_funk_align(),                      fd_funk_footprint()                 );
      89      110235 :   FD_SCRATCH_ALLOC_FINI( l, alignof(fd_exec_instr_test_runner_t) );
      90             : 
      91      110235 :   ulong txn_max = 4+fd_tile_cnt();
      92      110235 :   ulong rec_max = 1024UL;
      93      110235 :   fd_funk_t * funk = fd_funk_join( fd_funk_new( funk_mem, wksp_tag, (ulong)fd_tickcount(), txn_max, rec_max ) );
      94      110235 :   if( FD_UNLIKELY( !funk ) ) {
      95           0 :     FD_LOG_WARNING(( "fd_funk_new() failed" ));
      96           0 :     return NULL;
      97           0 :   }
      98             : 
      99      110235 :   fd_exec_instr_test_runner_t * runner = runner_mem;
     100      110235 :   runner->funk = funk;
     101             : 
     102             :   /* Create spad */
     103      110235 :   runner->spad = fd_spad_join( fd_spad_new( spad_mem, fd_spad_footprint( MAX_TX_ACCOUNT_LOCKS * fd_ulong_align_up( FD_ACC_TOT_SZ_MAX, FD_ACCOUNT_REC_ALIGN ) ) ) );
     104      110235 :   return runner;
     105      110235 : }
     106             : 
     107             : void *
     108      110235 : fd_exec_instr_test_runner_delete( fd_exec_instr_test_runner_t * runner ) {
     109      110235 :   if( FD_UNLIKELY( !runner ) ) return NULL;
     110      110235 :   fd_funk_delete( fd_funk_leave( runner->funk ) );
     111      110235 :   runner->funk = NULL;
     112      110235 :   runner->spad = NULL;
     113      110235 :   return runner;
     114      110235 : }
     115             : 
     116             : static int
     117      108786 : fd_double_is_normal( double dbl ) {
     118      108786 :   ulong x = fd_dblbits( dbl );
     119      108786 :   int is_denorm =
     120      108786 :     ( fd_dblbits_bexp( x ) == 0 ) &
     121      108786 :     ( fd_dblbits_mant( x ) != 0 );
     122      108786 :   int is_inf =
     123      108786 :     ( fd_dblbits_bexp( x ) == 2047 ) &
     124      108786 :     ( fd_dblbits_mant( x ) ==    0 );
     125      108786 :   int is_nan =
     126      108786 :     ( fd_dblbits_bexp( x ) == 2047 ) &
     127      108786 :     ( fd_dblbits_mant( x ) !=    0 );
     128      108786 :   return !( is_denorm | is_inf | is_nan );
     129      108786 : }
     130             : 
     131             : static int
     132             : _load_account( fd_borrowed_account_t *           acc,
     133             :                fd_acc_mgr_t *                    acc_mgr,
     134             :                fd_funk_txn_t *                   funk_txn,
     135     1098279 :                fd_exec_test_acct_state_t const * state ) {
     136     1098279 :   fd_borrowed_account_init( acc );
     137     1098279 :   ulong size = 0UL;
     138     1098279 :   if( state->data ) size = state->data->size;
     139             : 
     140     1098279 :   fd_pubkey_t pubkey[1];  memcpy( pubkey, state->address, sizeof(fd_pubkey_t) );
     141             : 
     142             :   /* Account must not yet exist */
     143     1098279 :   if( FD_UNLIKELY( fd_acc_mgr_view_raw( acc_mgr, funk_txn, pubkey, NULL, NULL, NULL) ) )
     144          21 :     return 0;
     145             : 
     146     1098258 :   assert( acc_mgr->funk );
     147     1098258 :   assert( acc_mgr->funk->magic == FD_FUNK_MAGIC );
     148     1098258 :   fd_funk_start_write( acc_mgr->funk );
     149     1098258 :   int err = fd_acc_mgr_modify( /* acc_mgr     */ acc_mgr,
     150     1098258 :                                /* txn         */ funk_txn,
     151     1098258 :                                /* pubkey      */ pubkey,
     152     1098258 :                                /* do_create   */ 1,
     153     1098258 :                                /* min_data_sz */ size,
     154     1098258 :                                acc );
     155     1098258 :   assert( err==FD_ACC_MGR_SUCCESS );
     156     1098258 :   if( state->data ) fd_memcpy( acc->data, state->data->bytes, size );
     157             : 
     158     1098258 :   acc->starting_lamports     = state->lamports;
     159     1098258 :   acc->starting_dlen         = size;
     160     1098258 :   acc->meta->info.lamports   = state->lamports;
     161     1098258 :   acc->meta->info.executable = state->executable;
     162     1098258 :   acc->meta->info.rent_epoch = state->rent_epoch;
     163     1098258 :   acc->meta->dlen            = size;
     164     1098258 :   memcpy( acc->meta->info.owner, state->owner, sizeof(fd_pubkey_t) );
     165             : 
     166             :   /* make the account read-only by default */
     167     1098258 :   acc->meta = NULL;
     168     1098258 :   acc->data = NULL;
     169     1098258 :   acc->rec  = NULL;
     170     1098258 :   fd_funk_end_write( acc_mgr->funk );
     171             : 
     172     1098258 :   return 1;
     173     1098258 : }
     174             : 
     175             : static int
     176             : _load_txn_account( fd_borrowed_account_t *           acc,
     177             :                    fd_acc_mgr_t *                    acc_mgr,
     178             :                    fd_funk_txn_t *                   funk_txn,
     179       47313 :                    fd_exec_test_acct_state_t const * state ) {
     180             :   // In the Agave transaction fuzzing harness, accounts with 0 lamports are not saved in the accounts db.
     181             :   // When they are fetched for transactions, the fields of the account are 0-set.
     182       47313 :   fd_exec_test_acct_state_t account_state_to_save = FD_EXEC_TEST_ACCT_STATE_INIT_ZERO;
     183       47313 :   memcpy( account_state_to_save.address, state->address, sizeof(fd_pubkey_t) );
     184             : 
     185             :   // Restore the account state if it has lamports
     186       47313 :   if( state->lamports ) {
     187       46815 :     account_state_to_save = *state;
     188       46815 :   }
     189             : 
     190       47313 :   return _load_account( acc, acc_mgr, funk_txn, &account_state_to_save );
     191       47313 : }
     192             : 
     193             : int
     194             : _restore_feature_flags( fd_exec_epoch_ctx_t *              epoch_ctx,
     195      108786 :                         fd_exec_test_feature_set_t const * feature_set ) {
     196      108786 :   fd_features_disable_all( &epoch_ctx->features );
     197    14906619 :   for( ulong j=0UL; j < feature_set->features_count; j++ ) {
     198    14797833 :     ulong                   prefix = feature_set->features[j];
     199    14797833 :     fd_feature_id_t const * id     = fd_feature_id_query( prefix );
     200    14797833 :     if( FD_UNLIKELY( !id ) ) {
     201           0 :       FD_LOG_WARNING(( "unsupported feature ID 0x%016lx", prefix ));
     202           0 :       return 0;
     203           0 :     }
     204             :     /* Enabled since genesis */
     205    14797833 :     fd_features_set( &epoch_ctx->features, id, 0UL );
     206    14797833 :   }
     207      108786 :   return 1;
     208      108786 : }
     209             : 
     210             : int
     211             : fd_exec_test_instr_context_create( fd_exec_instr_test_runner_t *        runner,
     212             :                                    fd_exec_instr_ctx_t *                ctx,
     213             :                                    fd_exec_test_instr_context_t const * test_ctx,
     214             :                                    fd_alloc_t *                         alloc,
     215       97416 :                                    bool                                 is_syscall ) {
     216       97416 :   memset( ctx, 0, sizeof(fd_exec_instr_ctx_t) );
     217             : 
     218       97416 :   fd_funk_t * funk = runner->funk;
     219             : 
     220             :   /* Generate unique ID for funk txn */
     221             : 
     222       97416 :   fd_funk_txn_xid_t xid[1] = {0};
     223       97416 :   xid[0] = fd_funk_generate_xid();
     224             : 
     225             :   /* Create temporary funk transaction and scratch contexts */
     226             : 
     227       97416 :   fd_funk_start_write( funk );
     228       97416 :   fd_funk_txn_t * funk_txn = fd_funk_txn_prepare( funk, NULL, xid, 1 );
     229       97416 :   fd_funk_end_write( funk );
     230       97416 :   fd_scratch_push();
     231             : 
     232       97416 :   ulong vote_acct_max = MAX_TX_ACCOUNT_LOCKS;
     233             : 
     234             :   /* Allocate contexts */
     235       97416 :   uchar *               epoch_ctx_mem = fd_scratch_alloc( fd_exec_epoch_ctx_align(), fd_exec_epoch_ctx_footprint( vote_acct_max ) );
     236       97416 :   uchar *               slot_ctx_mem  = fd_scratch_alloc( FD_EXEC_SLOT_CTX_ALIGN,  FD_EXEC_SLOT_CTX_FOOTPRINT  );
     237       97416 :   uchar *               txn_ctx_mem   = fd_scratch_alloc( FD_EXEC_TXN_CTX_ALIGN,   FD_EXEC_TXN_CTX_FOOTPRINT   );
     238             : 
     239       97416 :   fd_exec_epoch_ctx_t * epoch_ctx     = fd_exec_epoch_ctx_join( fd_exec_epoch_ctx_new( epoch_ctx_mem, vote_acct_max ) );
     240       97416 :   fd_exec_slot_ctx_t *  slot_ctx      = fd_exec_slot_ctx_join ( fd_exec_slot_ctx_new ( slot_ctx_mem, fd_alloc_virtual( alloc ) ) );
     241       97416 :   fd_exec_txn_ctx_t *   txn_ctx       = fd_exec_txn_ctx_join  ( fd_exec_txn_ctx_new  ( txn_ctx_mem   ) );
     242             : 
     243       97416 :   assert( epoch_ctx );
     244       97416 :   assert( slot_ctx  );
     245             : 
     246       97416 :   ctx->slot_ctx   = slot_ctx;
     247       97416 :   ctx->txn_ctx    = txn_ctx;
     248       97416 :   txn_ctx->valloc = slot_ctx->valloc;
     249             : 
     250             :   /* Initial variables */
     251       97416 :   txn_ctx->loaded_accounts_data_size_limit = FD_VM_LOADED_ACCOUNTS_DATA_SIZE_LIMIT;
     252       97416 :   txn_ctx->heap_size                       = FD_VM_HEAP_DEFAULT;
     253             : 
     254             :   /* Set up epoch context. Defaults obtained from GenesisConfig::Default() */
     255       97416 :   fd_epoch_bank_t * epoch_bank = fd_exec_epoch_ctx_epoch_bank( epoch_ctx );
     256       97416 :   epoch_bank->rent.lamports_per_uint8_year = 3480;
     257       97416 :   epoch_bank->rent.exemption_threshold = 2;
     258       97416 :   epoch_bank->rent.burn_percent = 50;
     259             : 
     260             :   /* Create account manager */
     261             : 
     262       97416 :   fd_acc_mgr_t * acc_mgr = fd_acc_mgr_new( fd_scratch_alloc( FD_ACC_MGR_ALIGN, FD_ACC_MGR_FOOTPRINT ), funk );
     263       97416 :   assert( acc_mgr );
     264             : 
     265             :   /* Set up slot context */
     266             : 
     267       97416 :   slot_ctx->epoch_ctx = epoch_ctx;
     268       97416 :   slot_ctx->funk_txn  = funk_txn;
     269       97416 :   slot_ctx->acc_mgr   = acc_mgr;
     270             : 
     271             :   /* Restore feature flags */
     272             : 
     273       97416 :   fd_exec_test_feature_set_t const * feature_set = &test_ctx->epoch_context.features;
     274       97416 :   if( !_restore_feature_flags( epoch_ctx, feature_set ) ) {
     275           0 :     return 0;
     276           0 :   }
     277             : 
     278             :   /* TODO: Restore slot_bank */
     279             : 
     280       97416 :   fd_slot_bank_new( &slot_ctx->slot_bank );
     281       97416 :   fd_block_block_hash_entry_t * recent_block_hashes = deq_fd_block_block_hash_entry_t_alloc( slot_ctx->valloc, FD_SYSVAR_RECENT_HASHES_CAP );
     282       97416 :   slot_ctx->slot_bank.recent_block_hashes.hashes = recent_block_hashes;
     283       97416 :   fd_block_block_hash_entry_t * recent_block_hash = deq_fd_block_block_hash_entry_t_push_tail_nocopy( recent_block_hashes );
     284       97416 :   fd_memset( recent_block_hash, 0, sizeof(fd_block_block_hash_entry_t) );
     285             : 
     286             :   /* Set up txn context */
     287             : 
     288       97416 :   txn_ctx->epoch_ctx               = epoch_ctx;
     289       97416 :   txn_ctx->slot_ctx                = slot_ctx;
     290       97416 :   txn_ctx->funk_txn                = funk_txn;
     291       97416 :   txn_ctx->acc_mgr                 = acc_mgr;
     292       97416 :   txn_ctx->compute_unit_limit      = test_ctx->cu_avail;
     293       97416 :   txn_ctx->compute_unit_price      = 0;
     294       97416 :   txn_ctx->compute_meter           = test_ctx->cu_avail;
     295       97416 :   txn_ctx->prioritization_fee_type = FD_COMPUTE_BUDGET_PRIORITIZATION_FEE_TYPE_DEPRECATED;
     296       97416 :   txn_ctx->custom_err              = UINT_MAX;
     297       97416 :   txn_ctx->instr_stack_sz          = 0;
     298       97416 :   txn_ctx->executable_cnt          = 0;
     299       97416 :   txn_ctx->paid_fees               = 0;
     300       97416 :   txn_ctx->num_instructions        = 0;
     301       97416 :   txn_ctx->dirty_vote_acc          = 0;
     302       97416 :   txn_ctx->dirty_stake_acc         = 0;
     303       97416 :   txn_ctx->failed_instr            = NULL;
     304       97416 :   txn_ctx->instr_err_idx           = INT_MAX;
     305       97416 :   txn_ctx->capture_ctx             = NULL;
     306       97416 :   txn_ctx->vote_accounts_pool      = NULL;
     307       97416 :   txn_ctx->accounts_resize_delta   = 0;
     308       97416 :   txn_ctx->instr_info_cnt          = 0;
     309       97416 :   txn_ctx->instr_trace_length      = 0;
     310       97416 :   txn_ctx->exec_err                = 0;
     311       97416 :   txn_ctx->exec_err_kind           = FD_EXECUTOR_ERR_KIND_EBPF;
     312             : 
     313       97416 :   memset( txn_ctx->_txn_raw, 0, sizeof(fd_rawtxn_b_t) );
     314       97416 :   memset( txn_ctx->return_data.program_id.key, 0, sizeof(fd_pubkey_t) );
     315       97416 :   txn_ctx->return_data.len         = 0;
     316       97416 :   txn_ctx->spad                    = runner->spad;
     317             : 
     318             :   /* Set up instruction context */
     319             : 
     320       97416 :   fd_instr_info_t * info = fd_valloc_malloc( fd_scratch_virtual(), 8UL, sizeof(fd_instr_info_t) );
     321       97416 :   assert( info );
     322       97416 :   memset( info, 0, sizeof(fd_instr_info_t) );
     323             : 
     324       97416 :   if( test_ctx->data ) {
     325       82761 :     info->data_sz = (ushort)test_ctx->data->size;
     326       82761 :     info->data    = test_ctx->data->bytes;
     327       82761 :   }
     328             : 
     329       97416 :   memcpy( info->program_id_pubkey.uc, test_ctx->program_id, sizeof(fd_pubkey_t) );
     330             : 
     331             :   /* Prepare borrowed account table (correctly handles aliasing) */
     332             : 
     333       97416 :   if( FD_UNLIKELY( test_ctx->accounts_count > MAX_TX_ACCOUNT_LOCKS ) ) {
     334           0 :     REPORT( NOTICE, "too many accounts" );
     335           0 :     return 0;
     336           0 :   }
     337             : 
     338       97416 :   fd_borrowed_account_t * borrowed_accts = txn_ctx->borrowed_accounts;
     339       97416 :   fd_memset( borrowed_accts, 0, test_ctx->accounts_count * sizeof(fd_borrowed_account_t) );
     340       97416 :   txn_ctx->accounts_cnt = test_ctx->accounts_count;
     341     1148382 :   for ( uint i = 0; i < test_ctx->accounts_count; i++ ) {
     342     1050966 :     memcpy( &(txn_ctx->accounts[i]), test_ctx->accounts[i].address, sizeof(fd_pubkey_t) );
     343     1050966 :   }
     344       97416 :   fd_txn_t * txn_descriptor = (fd_txn_t *) fd_scratch_alloc( fd_txn_align(), fd_txn_footprint(1, 0) );
     345       97416 :   fd_memset(txn_descriptor, 0, fd_txn_footprint(1, 0) );
     346       97416 :   txn_descriptor->acct_addr_cnt = (ushort) test_ctx->accounts_count;
     347       97416 :   txn_descriptor->addr_table_adtl_cnt = 0;
     348       97416 :   txn_ctx->txn_descriptor = txn_descriptor;
     349             : 
     350             :   /* Precompiles are allowed to read data from all instructions.
     351             :      We need to at least set pointers to the current instruction.
     352             :      Note: for simplicity we point the entire raw tx data to the
     353             :      instruction data, this is probably something we can improve. */
     354       97416 :   txn_descriptor->instr_cnt = 1;
     355       97416 :   txn_ctx->_txn_raw->raw = info->data;
     356       97416 :   txn_descriptor->instr[0].data_off = 0;
     357       97416 :   txn_descriptor->instr[0].data_sz = info->data_sz;
     358             : 
     359             :   /* Load accounts into database */
     360             : 
     361       97416 :   assert( acc_mgr->funk );
     362     1148382 :   for( ulong j=0UL; j < test_ctx->accounts_count; j++ ) {
     363     1050966 :     if( !_load_account( &borrowed_accts[j], acc_mgr, funk_txn, &test_ctx->accounts[j] ) ) {
     364           0 :       return 0;
     365           0 :     }
     366             : 
     367     1050966 :     if( borrowed_accts[j].const_meta ) {
     368     1050966 :       uchar * data = fd_spad_alloc( txn_ctx->spad, FD_ACCOUNT_REC_ALIGN, FD_ACC_TOT_SZ_MAX );
     369     1050966 :       ulong   dlen = borrowed_accts[j].const_meta->dlen;
     370     1050966 :       fd_memcpy( data, borrowed_accts[j].const_meta, sizeof(fd_account_meta_t)+dlen );
     371     1050966 :       borrowed_accts[j].const_meta = (fd_account_meta_t*)data;
     372     1050966 :       borrowed_accts[j].const_data = data + sizeof(fd_account_meta_t);
     373     1050966 :     }
     374     1050966 :   }
     375             : 
     376             :   /* Load in executable accounts */
     377     1148382 :   for( ulong i = 0; i < txn_ctx->accounts_cnt; i++ ) {
     378     1050966 :     if ( memcmp( borrowed_accts[i].const_meta->info.owner, fd_solana_bpf_loader_deprecated_program_id.key, sizeof(fd_pubkey_t) ) != 0
     379     1050966 :       && memcmp( borrowed_accts[i].const_meta->info.owner, fd_solana_bpf_loader_program_id.key, sizeof(fd_pubkey_t) ) != 0
     380     1050966 :       && memcmp( borrowed_accts[i].const_meta->info.owner, fd_solana_bpf_loader_upgradeable_program_id.key, sizeof(fd_pubkey_t) ) != 0
     381     1050966 :       && memcmp( borrowed_accts[i].const_meta->info.owner, fd_solana_bpf_loader_v4_program_id.key, sizeof(fd_pubkey_t) ) != 0
     382     1050966 :     ) {
     383      869139 :       continue;
     384      869139 :     }
     385             : 
     386      181827 :     fd_account_meta_t const * meta = borrowed_accts[i].const_meta ? borrowed_accts[i].const_meta : borrowed_accts[i].meta;
     387      181827 :     if (meta == NULL) {
     388           0 :       static const fd_account_meta_t sentinel = { .magic = FD_ACCOUNT_META_MAGIC };
     389           0 :       borrowed_accts[i].const_meta        = &sentinel;
     390           0 :       borrowed_accts[i].starting_lamports = 0UL;
     391           0 :       borrowed_accts[i].starting_dlen     = 0UL;
     392           0 :       continue;
     393           0 :     }
     394             : 
     395      181827 :     if( meta->info.executable ) {
     396      160755 :       FD_BORROWED_ACCOUNT_DECL(owner_borrowed_account);
     397      160755 :       int err = fd_acc_mgr_view( txn_ctx->acc_mgr, txn_ctx->funk_txn, (fd_pubkey_t *)meta->info.owner, owner_borrowed_account );
     398      160755 :       if( FD_UNLIKELY( err ) ) {
     399      112983 :         borrowed_accts[i].starting_owner_dlen = 0;
     400      112983 :       } else {
     401       47772 :         borrowed_accts[i].starting_owner_dlen = owner_borrowed_account->const_meta->dlen;
     402       47772 :       }
     403      160755 :     }
     404             : 
     405      181827 :     if ( FD_UNLIKELY( 0 == memcmp(meta->info.owner, fd_solana_bpf_loader_upgradeable_program_id.key, sizeof(fd_pubkey_t)) ) ) {
     406       85020 :       fd_bpf_upgradeable_loader_state_t program_loader_state = {0};
     407       85020 :       int err = 0;
     408       85020 :       if( FD_UNLIKELY( !read_bpf_upgradeable_loader_state_for_program( txn_ctx, (uchar) i, &program_loader_state, &err ) ) ) {
     409       53775 :         continue;
     410       53775 :       }
     411             : 
     412       31245 :       fd_pubkey_t * programdata_acc = &program_loader_state.inner.program.programdata_address;
     413       31245 :       fd_borrowed_account_t * executable_account = fd_borrowed_account_init( &txn_ctx->executable_accounts[txn_ctx->executable_cnt] );
     414       31245 :       fd_acc_mgr_view(txn_ctx->acc_mgr, txn_ctx->funk_txn, programdata_acc, executable_account);
     415       31245 :       txn_ctx->executable_cnt++;
     416       31245 :     }
     417      181827 :   }
     418             : 
     419             :   /* Add accounts to bpf program cache */
     420       97416 :   fd_funk_start_write( acc_mgr->funk );
     421       97416 :   fd_bpf_scan_and_create_bpf_program_cache_entry( slot_ctx, funk_txn );
     422       97416 :   fd_funk_end_write( acc_mgr->funk );
     423             : 
     424             :   /* Restore sysvar cache */
     425       97416 :   fd_sysvar_cache_restore( slot_ctx->sysvar_cache, acc_mgr, funk_txn );
     426             : 
     427             :   /* Fill missing sysvar cache values with defaults */
     428             :   /* We create mock accounts for each of the sysvars and hardcode the data fields before loading it into the account manager */
     429             :   /* We use Agave sysvar defaults for data field values */
     430             : 
     431             :   /* Clock */
     432             :   // https://github.com/firedancer-io/solfuzz-agave/blob/agave-v2.0/src/lib.rs#L466-L474
     433       97416 :   if( !slot_ctx->sysvar_cache->has_clock ) {
     434       62277 :     slot_ctx->sysvar_cache->has_clock = 1;
     435       62277 :     fd_sol_sysvar_clock_t sysvar_clock = {
     436       62277 :                                           .slot = 10,
     437       62277 :                                           .epoch_start_timestamp = 0,
     438       62277 :                                           .epoch = 0,
     439       62277 :                                           .leader_schedule_epoch = 0,
     440       62277 :                                           .unix_timestamp = 0
     441       62277 :                                         };
     442       62277 :     memcpy( slot_ctx->sysvar_cache->val_clock, &sysvar_clock, sizeof(fd_sol_sysvar_clock_t) );
     443       62277 :   }
     444             : 
     445             :   /* Epoch schedule */
     446             :   // https://github.com/firedancer-io/solfuzz-agave/blob/agave-v2.0/src/lib.rs#L476-L483
     447       97416 :   if ( !slot_ctx->sysvar_cache->has_epoch_schedule ) {
     448       87069 :     slot_ctx->sysvar_cache->has_epoch_schedule = 1;
     449       87069 :     fd_epoch_schedule_t sysvar_epoch_schedule = {
     450       87069 :                                                   .slots_per_epoch = 432000,
     451       87069 :                                                   .leader_schedule_slot_offset = 432000,
     452       87069 :                                                   .warmup = 1,
     453       87069 :                                                   .first_normal_epoch = 14,
     454       87069 :                                                   .first_normal_slot = 524256
     455       87069 :                                                 };
     456       87069 :     memcpy( slot_ctx->sysvar_cache->val_epoch_schedule, &sysvar_epoch_schedule, sizeof(fd_epoch_schedule_t) );
     457       87069 :   }
     458             : 
     459             :   /* Rent */
     460             :   // https://github.com/firedancer-io/solfuzz-agave/blob/agave-v2.0/src/lib.rs#L487-L500
     461       97416 :   if ( !slot_ctx->sysvar_cache->has_rent ) {
     462       80877 :     slot_ctx->sysvar_cache->has_rent = 1;
     463       80877 :     fd_rent_t sysvar_rent = {
     464       80877 :                               .lamports_per_uint8_year = 3480,
     465       80877 :                               .exemption_threshold = 2.0,
     466       80877 :                               .burn_percent = 50
     467       80877 :                             };
     468       80877 :     memcpy( slot_ctx->sysvar_cache->val_rent, &sysvar_rent, sizeof(fd_rent_t) );
     469       80877 :   }
     470             : 
     471       97416 :   if ( !slot_ctx->sysvar_cache->has_last_restart_slot ) {
     472       94077 :     slot_ctx->sysvar_cache->has_last_restart_slot = 1;
     473             : 
     474       94077 :     fd_sol_sysvar_last_restart_slot_t restart = {.slot = 5000};
     475             : 
     476       94077 :     memcpy( slot_ctx->sysvar_cache->val_last_restart_slot, &restart, sizeof(fd_sol_sysvar_last_restart_slot_t) );
     477       94077 :   }
     478             : 
     479             :   /* Set slot bank variables */
     480       97416 :   slot_ctx->slot_bank.slot = fd_sysvar_cache_clock( slot_ctx->sysvar_cache )->slot;
     481             : 
     482             :   /* Handle undefined behavior if sysvars are malicious (!!!) */
     483             : 
     484             :   /* A NaN rent exemption threshold is U.B. in Solana Labs */
     485       97416 :   fd_rent_t const * rent = fd_sysvar_cache_rent( slot_ctx->sysvar_cache );
     486       97416 :   if( rent ) {
     487       97416 :     if( ( !fd_double_is_normal( rent->exemption_threshold ) ) |
     488       97416 :         ( rent->exemption_threshold     <      0.0 ) |
     489       97416 :         ( rent->exemption_threshold     >    999.0 ) |
     490       97416 :         ( rent->lamports_per_uint8_year > UINT_MAX ) |
     491       97416 :         ( rent->burn_percent            >      100 ) )
     492           0 :       return 0;
     493             : 
     494             :     /* Override epoch bank settings */
     495       97416 :     epoch_bank->rent = *rent;
     496       97416 :   }
     497             : 
     498             :   /* Override most recent blockhash if given */
     499       97416 :   fd_recent_block_hashes_t const * rbh = fd_sysvar_cache_recent_block_hashes( slot_ctx->sysvar_cache );
     500       97416 :   if( rbh && !deq_fd_block_block_hash_entry_t_empty( rbh->hashes ) ) {
     501        4629 :     fd_block_block_hash_entry_t const * last = deq_fd_block_block_hash_entry_t_peek_tail_const( rbh->hashes );
     502        4629 :     if( last ) {
     503        4629 :       *recent_block_hash = *last;
     504        4629 :       slot_ctx->slot_bank.lamports_per_signature = last->fee_calculator.lamports_per_signature;
     505        4629 :       slot_ctx->prev_lamports_per_signature = last->fee_calculator.lamports_per_signature;
     506        4629 :     }
     507        4629 :   }
     508             : 
     509             :   /* Load instruction accounts */
     510             : 
     511       97416 :   if( FD_UNLIKELY( test_ctx->instr_accounts_count > MAX_TX_ACCOUNT_LOCKS ) ) {
     512           0 :     REPORT( NOTICE, "too many instruction accounts" );
     513           0 :     return 0;
     514           0 :   }
     515             : 
     516       97416 :   uchar acc_idx_seen[256] = {0};
     517      950703 :   for( ulong j=0UL; j < test_ctx->instr_accounts_count; j++ ) {
     518      853287 :     uint index = test_ctx->instr_accounts[j].index;
     519      853287 :     if( index >= test_ctx->accounts_count ) {
     520           0 :       REPORTV( NOTICE, " instruction account index out of range (%u > %u)", index, test_ctx->instr_accounts_count );
     521           0 :       return 0;
     522           0 :     }
     523             : 
     524      853287 :     fd_borrowed_account_t * acc = &borrowed_accts[ index ];
     525      853287 :     uint flags = 0;
     526      853287 :     flags |= test_ctx->instr_accounts[j].is_writable ? FD_INSTR_ACCT_FLAGS_IS_WRITABLE : 0;
     527      853287 :     flags |= test_ctx->instr_accounts[j].is_signer   ? FD_INSTR_ACCT_FLAGS_IS_SIGNER   : 0;
     528             : 
     529      853287 :     info->borrowed_accounts[j] = acc;
     530      853287 :     info->acct_flags       [j] = (uchar)flags;
     531      853287 :     memcpy( info->acct_pubkeys[j].uc, acc->pubkey, sizeof(fd_pubkey_t) );
     532      853287 :     info->acct_txn_idxs[j]     = (uchar) index;
     533             : 
     534      853287 :     if( test_ctx->instr_accounts[j].is_writable ) {
     535      436701 :       acc->meta = (void *)acc->const_meta;
     536      436701 :       acc->data = (void *)acc->const_data;
     537      436701 :       acc->rec  = (void *)acc->const_rec;
     538      436701 :     }
     539             : 
     540      853287 :     if (acc_idx_seen[index]) {
     541       27546 :       info->is_duplicate[j] = 1;
     542       27546 :     }
     543      853287 :     acc_idx_seen[index] = 1;
     544      853287 :   }
     545       97416 :   info->acct_cnt = (uchar)test_ctx->instr_accounts_count;
     546             : 
     547             :   //  FIXME: Specifically for CPI syscalls, flag guard this?
     548       97416 :   fd_instr_info_sum_account_lamports( info, &info->starting_lamports_h, &info->starting_lamports_l );
     549             : 
     550             :   /* The remaining checks enforce that the program is one of the accounts and
     551             :     owned by native loader. */
     552       97416 :   bool found_program_id = false;
     553      781002 :   for( uint i = 0; i < test_ctx->accounts_count; i++ ) {
     554      770484 :     if( 0 == memcmp( test_ctx->accounts[i].address, test_ctx->program_id, sizeof(fd_pubkey_t) ) ) {
     555       86898 :       info->program_id = (uchar) i;
     556       86898 :       found_program_id = true;
     557       86898 :       break;
     558       86898 :     }
     559      770484 :   }
     560             : 
     561             :   /* Aborting is only important for instr execution */
     562       97416 :   if( !is_syscall && !found_program_id ) {
     563           0 :     FD_LOG_NOTICE(( " Unable to find program_id in accounts" ));
     564           0 :     return 0;
     565           0 :   }
     566             : 
     567       97416 :   ctx->epoch_ctx = epoch_ctx;
     568       97416 :   ctx->funk_txn  = funk_txn;
     569       97416 :   ctx->acc_mgr   = acc_mgr;
     570       97416 :   ctx->valloc    = fd_scratch_virtual();
     571       97416 :   ctx->instr     = info;
     572             : 
     573       97416 :   fd_log_collector_init( &ctx->txn_ctx->log_collector, 1 );
     574       97416 :   fd_base58_encode_32( ctx->instr->program_id_pubkey.uc, NULL, ctx->program_id_base58 );
     575             : 
     576       97416 :   return 1;
     577       97416 : }
     578             : 
     579             : static void
     580      245115 : _add_to_data(uchar ** data, void const * to_add, ulong size) {
     581     5615685 :   while( size-- ) {
     582     5370570 :     **data = *(uchar *)to_add;
     583     5370570 :     (*data)++;
     584     5370570 :     to_add = (uchar *)to_add + 1;
     585     5370570 :   }
     586      245115 : }
     587             : 
     588             : static void
     589       82596 : _add_compact_u16(uchar ** data, ushort to_add) {
     590       82596 :   fd_bincode_encode_ctx_t encode_ctx = { .data = *data, .dataend = *data + 3 };  // Up to 3 bytes
     591       82596 :   fd_bincode_compact_u16_encode( &to_add, &encode_ctx );
     592       82596 :   *data = (uchar *) encode_ctx.data;
     593       82596 : }
     594             : 
     595             : static fd_execute_txn_task_info_t *
     596             : _txn_context_create_and_exec( fd_exec_instr_test_runner_t *      runner,
     597             :                               fd_exec_slot_ctx_t *               slot_ctx,
     598       11370 :                               fd_exec_test_txn_context_t const * test_ctx ) {
     599       11370 :   uchar empty_bytes[64] = { 0 };
     600       11370 :   fd_funk_t * funk = runner->funk;
     601             : 
     602             :   /* Generate unique ID for funk txn */
     603             : 
     604       11370 :   fd_funk_txn_xid_t xid[1] = {0};
     605       11370 :   xid[0] = fd_funk_generate_xid();
     606             : 
     607             :   /* Create temporary funk transaction and scratch contexts */
     608             : 
     609       11370 :   fd_funk_start_write( runner->funk );
     610       11370 :   fd_funk_txn_t * funk_txn = fd_funk_txn_prepare( funk, NULL, xid, 1 );
     611       11370 :   fd_funk_end_write( runner->funk );
     612             : 
     613       11370 :   ulong vote_acct_max = MAX_TX_ACCOUNT_LOCKS;
     614             : 
     615             :   /* Allocate contexts */
     616       11370 :   uchar *               epoch_ctx_mem = fd_scratch_alloc( fd_exec_epoch_ctx_align(), fd_exec_epoch_ctx_footprint( vote_acct_max ) );
     617       11370 :   fd_exec_epoch_ctx_t * epoch_ctx     = fd_exec_epoch_ctx_join( fd_exec_epoch_ctx_new( epoch_ctx_mem, vote_acct_max ) );
     618             : 
     619       11370 :   assert( epoch_ctx );
     620       11370 :   assert( slot_ctx  );
     621             : 
     622             :   /* Set up epoch context */
     623       11370 :   fd_epoch_bank_t * epoch_bank = fd_exec_epoch_ctx_epoch_bank( epoch_ctx );
     624             : 
     625             :   /* Create account manager */
     626       11370 :   fd_acc_mgr_t * acc_mgr = fd_acc_mgr_new( fd_scratch_alloc( FD_ACC_MGR_ALIGN, FD_ACC_MGR_FOOTPRINT ), funk );
     627       11370 :   assert( acc_mgr );
     628             : 
     629             :   /* Set up slot context */
     630             : 
     631       11370 :   slot_ctx->epoch_ctx = epoch_ctx;
     632       11370 :   slot_ctx->funk_txn  = funk_txn;
     633       11370 :   slot_ctx->acc_mgr   = acc_mgr;
     634             : 
     635             :   /* Restore feature flags */
     636             : 
     637       11370 :   fd_exec_test_feature_set_t const * feature_set = &test_ctx->epoch_ctx.features;
     638       11370 :   if( !_restore_feature_flags( epoch_ctx, feature_set ) ) {
     639           0 :     return NULL;
     640           0 :   }
     641             : 
     642             :   /* Restore slot bank */
     643       11370 :   fd_slot_bank_new( &slot_ctx->slot_bank );
     644             : 
     645             :   /* Initialize builtin accounts */
     646       11370 :   fd_funk_start_write( runner->funk );
     647       11370 :   fd_builtin_programs_init( slot_ctx );
     648       11370 :   fd_funk_end_write( runner->funk );
     649             : 
     650             :   /* Load account states into funk (note this is different from the account keys):
     651             :     Account state = accounts to populate Funk
     652             :     Account keys = account keys that the transaction needs */
     653       58683 :   for( ulong i = 0; i < test_ctx->tx.message.account_shared_data_count; i++ ) {
     654             :     /* Load the accounts into the account manager
     655             :        Borrowed accounts get reset anyways - we just need to load the account somewhere */
     656       47313 :     FD_BORROWED_ACCOUNT_DECL(acc);
     657       47313 :     _load_txn_account( acc, acc_mgr, funk_txn, &test_ctx->tx.message.account_shared_data[i] );
     658       47313 :   }
     659             : 
     660             :   /* Restore sysvar cache */
     661       11370 :   fd_sysvar_cache_restore( slot_ctx->sysvar_cache, acc_mgr, funk_txn );
     662             : 
     663             :   /* Add accounts to bpf program cache */
     664       11370 :   fd_funk_start_write( runner->funk );
     665       11370 :   fd_bpf_scan_and_create_bpf_program_cache_entry( slot_ctx, funk_txn );
     666             : 
     667             :   /* Default slot */
     668       11370 :   ulong slot = test_ctx->slot_ctx.slot ? test_ctx->slot_ctx.slot : 10; // Arbitrary default > 0
     669             : 
     670             :   /* Set slot bank variables (defaults obtained from GenesisConfig::default() in Agave) */
     671       11370 :   slot_ctx->slot_bank.slot                                            = slot;
     672       11370 :   slot_ctx->slot_bank.prev_slot                                       = slot_ctx->slot_bank.slot - 1; // Can underflow, but its fine since it will correctly be ULONG_MAX
     673       11370 :   slot_ctx->slot_bank.fee_rate_governor.burn_percent                  = 50;
     674       11370 :   slot_ctx->slot_bank.fee_rate_governor.min_lamports_per_signature    = 0;
     675       11370 :   slot_ctx->slot_bank.fee_rate_governor.max_lamports_per_signature    = 0;
     676       11370 :   slot_ctx->slot_bank.fee_rate_governor.target_lamports_per_signature = 10000;
     677       11370 :   slot_ctx->slot_bank.fee_rate_governor.target_signatures_per_slot    = 20000;
     678       11370 :   slot_ctx->slot_bank.lamports_per_signature                          = 5000;
     679       11370 :   slot_ctx->prev_lamports_per_signature                               = 5000;
     680             : 
     681             :   /* Set epoch bank variables if not present (defaults obtained from GenesisConfig::default() in Agave) */
     682       11370 :   fd_epoch_schedule_t default_epoch_schedule = {
     683       11370 :                                                 .slots_per_epoch             = 432000,
     684       11370 :                                                 .leader_schedule_slot_offset = 432000,
     685       11370 :                                                 .warmup                      = 1,
     686       11370 :                                                 .first_normal_epoch          = 14,
     687       11370 :                                                 .first_normal_slot           = 524256
     688       11370 :                                                };
     689       11370 :   fd_rent_t           default_rent           = {
     690       11370 :                                                 .lamports_per_uint8_year     = 3480,
     691       11370 :                                                 .exemption_threshold         = 2.0,
     692       11370 :                                                 .burn_percent                = 50
     693       11370 :                                                };
     694       11370 :   epoch_bank->epoch_schedule      = default_epoch_schedule;
     695       11370 :   epoch_bank->rent_epoch_schedule = default_epoch_schedule;
     696       11370 :   epoch_bank->rent                = default_rent;
     697       11370 :   epoch_bank->ticks_per_slot      = 64;
     698       11370 :   epoch_bank->slots_per_year      = SECONDS_PER_YEAR * (1000000000.0 / (double)6250000) / (double)epoch_bank->ticks_per_slot;
     699             : 
     700             :   // Override default values if provided
     701       11370 :   if( slot_ctx->sysvar_cache->has_epoch_schedule ) {
     702         150 :     epoch_bank->epoch_schedule      = *slot_ctx->sysvar_cache->val_epoch_schedule;
     703         150 :     epoch_bank->rent_epoch_schedule = *slot_ctx->sysvar_cache->val_epoch_schedule;
     704         150 :   }
     705             : 
     706       11370 :   if( slot_ctx->sysvar_cache->has_rent ) {
     707         684 :     epoch_bank->rent = *slot_ctx->sysvar_cache->val_rent;
     708         684 :   }
     709             : 
     710             :   /* Provde default slot hashes of size 1 if not provided */
     711       11370 :   if( !slot_ctx->sysvar_cache->has_slot_hashes ) {
     712       10368 :     fd_slot_hash_t * slot_hashes = deq_fd_slot_hash_t_alloc( fd_scratch_virtual(), 1 );
     713       10368 :     fd_slot_hash_t * dummy_elem = deq_fd_slot_hash_t_push_tail_nocopy( slot_hashes );
     714       10368 :     memset( dummy_elem, 0, sizeof(fd_slot_hash_t) );
     715       10368 :     fd_slot_hashes_t default_slot_hashes = { .hashes = slot_hashes };
     716       10368 :     fd_sysvar_slot_hashes_init( slot_ctx, &default_slot_hashes );
     717       10368 :   }
     718             : 
     719             :   /* Provide default stake history if not provided */
     720       11370 :   if( !slot_ctx->sysvar_cache->has_stake_history ) {
     721             :     // Provide a 0-set default entry
     722       10914 :     fd_stake_history_entry_t entry = {0};
     723       10914 :     fd_sysvar_stake_history_init( slot_ctx );
     724       10914 :     fd_sysvar_stake_history_update( slot_ctx, &entry );
     725       10914 :   }
     726             : 
     727             :   /* Provide default last restart slot sysvar if not provided */
     728       11370 :   if( !slot_ctx->sysvar_cache->has_last_restart_slot ) {
     729       11361 :     fd_sysvar_last_restart_slot_init( slot_ctx );
     730       11361 :   }
     731             : 
     732             :   /* Provide a default clock if not present */
     733       11370 :   if( !slot_ctx->sysvar_cache->has_clock ) {
     734        8634 :     fd_sysvar_clock_init( slot_ctx );
     735        8634 :     fd_sysvar_clock_update( slot_ctx );
     736        8634 :   }
     737             : 
     738             :   /* Epoch schedule and rent get set from the epoch bank */
     739       11370 :   fd_sysvar_epoch_schedule_init( slot_ctx );
     740       11370 :   fd_sysvar_rent_init( slot_ctx );
     741             : 
     742             :   /* Set the epoch rewards sysvar if partition epoch rewards feature is enabled
     743             : 
     744             :      TODO: The init parameters are not exactly conformant with Agave's epoch rewards sysvar. We should
     745             :      be calling `fd_begin_partitioned_rewards` with the same parameters as Agave. However,
     746             :      we just need the `active` field to be conformant due to a single Stake program check.
     747             :      THIS MAY CHANGE IN THE FUTURE. If there are other parts of transaction execution that use
     748             :      the epoch rewards sysvar, we may need to update this.
     749             :   */
     750       11370 :   if ( (
     751       11370 :       FD_FEATURE_ACTIVE( slot_ctx, enable_partitioned_epoch_reward ) ||
     752       11370 :       FD_FEATURE_ACTIVE( slot_ctx, partitioned_epoch_rewards_superfeature )
     753       11370 :       ) && !slot_ctx->sysvar_cache->has_epoch_rewards ) {
     754        1800 :     fd_point_value_t point_value = {0};
     755        1800 :     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;
     756        1800 :     fd_sysvar_epoch_rewards_init( slot_ctx, 0UL, 0UL, 2UL, 1UL, point_value, last_hash);
     757        1800 :   }
     758             : 
     759             :   /* Restore sysvar cache (again, since we may need to provide default sysvars) */
     760       11370 :   fd_sysvar_cache_restore( slot_ctx->sysvar_cache, acc_mgr, funk_txn );
     761             : 
     762             :   /* A NaN rent exemption threshold is U.B. in Solana Labs */
     763       11370 :   fd_rent_t const * rent = fd_sysvar_cache_rent( slot_ctx->sysvar_cache );
     764       11370 :   if( ( !fd_double_is_normal( rent->exemption_threshold ) ) |
     765       11370 :       ( rent->exemption_threshold     <      0.0 ) |
     766       11370 :       ( rent->exemption_threshold     >    999.0 ) |
     767       11370 :       ( rent->lamports_per_uint8_year > UINT_MAX ) |
     768       11370 :       ( rent->burn_percent            >      100 ) )
     769           0 :     return NULL;
     770             : 
     771             :   /* Blockhash queue is given in txn message. We need to populate the following three:
     772             :      - slot_ctx->slot_bank.block_hash_queue (TODO: Does more than just the last_hash need to be populated?)
     773             :      - sysvar_cache_recent_block_hashes
     774             :      - slot_ctx->slot_bank.recent_block_hashes */
     775       11370 :   ulong num_blockhashes = test_ctx->blockhash_queue_count;
     776             : 
     777             :   /* Recent blockhashes init */
     778       11370 :   fd_block_block_hash_entry_t * recent_block_hashes = deq_fd_block_block_hash_entry_t_alloc( slot_ctx->valloc, FD_SYSVAR_RECENT_HASHES_CAP );
     779             : 
     780             :   /* Blockhash queue init */
     781       11370 :   slot_ctx->slot_bank.block_hash_queue.max_age   = FD_BLOCKHASH_QUEUE_MAX_ENTRIES;
     782       11370 :   slot_ctx->slot_bank.block_hash_queue.ages_root = NULL;
     783       11370 :   slot_ctx->slot_bank.block_hash_queue.ages_pool = fd_hash_hash_age_pair_t_map_alloc( slot_ctx->valloc, 400 );
     784       11370 :   slot_ctx->slot_bank.block_hash_queue.last_hash = fd_valloc_malloc( slot_ctx->valloc, FD_HASH_ALIGN, FD_HASH_FOOTPRINT );
     785             : 
     786             :   // Save lamports per signature for most recent blockhash, if sysvar cache contains recent block hashes
     787       11370 :   fd_recent_block_hashes_t const * rbh = fd_sysvar_cache_recent_block_hashes( slot_ctx->sysvar_cache );
     788       11370 :   if( rbh && !deq_fd_block_block_hash_entry_t_empty( rbh->hashes ) ) {
     789         993 :     fd_block_block_hash_entry_t const * last = deq_fd_block_block_hash_entry_t_peek_head_const( rbh->hashes );
     790         993 :     if( last ) {
     791         993 :       slot_ctx->slot_bank.lamports_per_signature = last->fee_calculator.lamports_per_signature;
     792         993 :       slot_ctx->prev_lamports_per_signature      = last->fee_calculator.lamports_per_signature;
     793         993 :     }
     794         993 :   }
     795             : 
     796             :   // Clear and reset recent block hashes sysvar
     797       11370 :   slot_ctx->sysvar_cache->has_recent_block_hashes         = 1;
     798       11370 :   slot_ctx->sysvar_cache->val_recent_block_hashes->hashes = recent_block_hashes;
     799       11370 :   slot_ctx->slot_bank.recent_block_hashes.hashes          = recent_block_hashes;
     800             : 
     801             :   // Blockhash_queue[end] = last (latest) hash
     802             :   // Blockhash_queue[0] = genesis hash
     803       11370 :   if( num_blockhashes > 0 ) {
     804        8028 :     memcpy( &epoch_bank->genesis_hash, test_ctx->blockhash_queue[0]->bytes, sizeof(fd_hash_t) );
     805             : 
     806      835281 :     for( ulong i = 0; i < num_blockhashes; ++i ) {
     807             :       // Recent block hashes cap is 150 (actually 151), while blockhash queue capacity is 300 (actually 301)
     808      827253 :       fd_block_block_hash_entry_t blockhash_entry;
     809      827253 :       memcpy( &blockhash_entry.blockhash, test_ctx->blockhash_queue[i]->bytes, sizeof(fd_hash_t) );
     810      827253 :       slot_ctx->slot_bank.poh = blockhash_entry.blockhash;
     811      827253 :       fd_sysvar_recent_hashes_update( slot_ctx );
     812      827253 :     }
     813        8028 :   } else {
     814             :     // Add a default empty blockhash and use it as genesis
     815        3342 :     num_blockhashes = 1;
     816        3342 :     memcpy( &epoch_bank->genesis_hash, empty_bytes, sizeof(fd_hash_t) );
     817        3342 :     fd_block_block_hash_entry_t blockhash_entry;
     818        3342 :     memcpy( &blockhash_entry.blockhash, empty_bytes, sizeof(fd_hash_t) );
     819        3342 :     slot_ctx->slot_bank.poh = blockhash_entry.blockhash;
     820        3342 :     fd_sysvar_recent_hashes_update( slot_ctx );
     821        3342 :   }
     822       11370 :   fd_funk_end_write( runner->funk );
     823             : 
     824             :   /* Create the raw txn (https://solana.com/docs/core/transactions#transaction-size) */
     825       11370 :   uchar * txn_raw_begin = fd_scratch_alloc( alignof(uchar), 10000 ); // max txn size is 1232 but we allocate extra for safety
     826       11370 :   uchar * txn_raw_cur_ptr = txn_raw_begin;
     827             : 
     828             :   /* Compact array of signatures (https://solana.com/docs/core/transactions#transaction)
     829             :      Note that although documentation interchangably refers to the signature cnt as a compact-u16
     830             :      and a u8, the max signature cnt is capped at 48 (due to txn size limits), so u8 and compact-u16
     831             :      is represented the same way anyways and can be parsed identically. */
     832             :   // Note: always create a valid txn with 1+ signatures, add an empty signature if none is provided
     833       11370 :   uchar signature_cnt = fd_uchar_max( 1, (uchar) test_ctx->tx.signatures_count );
     834       11370 :   _add_to_data( &txn_raw_cur_ptr, &signature_cnt, sizeof(uchar) );
     835       40551 :   for( uchar i = 0; i < signature_cnt; ++i ) {
     836       29181 :     _add_to_data( &txn_raw_cur_ptr, test_ctx->tx.signatures && test_ctx->tx.signatures[i] ? test_ctx->tx.signatures[i]->bytes : empty_bytes, FD_TXN_SIGNATURE_SZ );
     837       29181 :   }
     838             : 
     839             :   /* Message */
     840             :   /* For v0 transactions, the highest bit of the num_required_signatures is set, and an extra byte is used for the version.
     841             :      https://solanacookbook.com/guides/versioned-transactions.html#versioned-transactions-transactionv0
     842             : 
     843             :      We will always create a transaction with at least 1 signature, and cap the signature count to 127 to avoid
     844             :      collisions with the header_b0 tag. */
     845       11370 :   uchar num_required_signatures = fd_uchar_max( 1, fd_uchar_min( 127, (uchar) test_ctx->tx.message.header.num_required_signatures ) );
     846       11370 :   if( !test_ctx->tx.message.is_legacy ) {
     847       10878 :     uchar header_b0 = (uchar) 0x80UL;
     848       10878 :     _add_to_data( &txn_raw_cur_ptr, &header_b0, sizeof(uchar) );
     849       10878 :   }
     850             : 
     851             :   /* Header (3 bytes) (https://solana.com/docs/core/transactions#message-header) */
     852       11370 :   _add_to_data( &txn_raw_cur_ptr, &num_required_signatures, sizeof(uchar) );
     853       11370 :   _add_to_data( &txn_raw_cur_ptr, &test_ctx->tx.message.header.num_readonly_signed_accounts, sizeof(uchar) );
     854       11370 :   _add_to_data( &txn_raw_cur_ptr, &test_ctx->tx.message.header.num_readonly_unsigned_accounts, sizeof(uchar) );
     855             : 
     856             :   /* Compact array of account addresses (https://solana.com/docs/core/transactions#compact-array-format) */
     857             :   // Array length is a compact u16
     858       11370 :   ushort num_acct_keys = (ushort) test_ctx->tx.message.account_keys_count;
     859       11370 :   _add_compact_u16( &txn_raw_cur_ptr, num_acct_keys );
     860       55050 :   for( ushort i = 0; i < num_acct_keys; ++i ) {
     861       43680 :     _add_to_data( &txn_raw_cur_ptr, test_ctx->tx.message.account_keys[i]->bytes, sizeof(fd_pubkey_t) );
     862       43680 :   }
     863             : 
     864             :   /* Recent blockhash (32 bytes) (https://solana.com/docs/core/transactions#recent-blockhash) */
     865             :   // Note: add an empty blockhash if none is provided
     866       11370 :   _add_to_data( &txn_raw_cur_ptr, test_ctx->tx.message.recent_blockhash ? test_ctx->tx.message.recent_blockhash->bytes : empty_bytes, sizeof(fd_hash_t) );
     867             : 
     868             :   /* Compact array of instructions (https://solana.com/docs/core/transactions#array-of-instructions) */
     869             :   // Instruction count is a compact u16
     870       11370 :   ushort instr_count = (ushort) test_ctx->tx.message.instructions_count;
     871       11370 :   _add_compact_u16( &txn_raw_cur_ptr, instr_count );
     872       28062 :   for( ushort i = 0; i < instr_count; ++i ) {
     873             :     // Program ID index
     874       16692 :     uchar program_id_index = (uchar) test_ctx->tx.message.instructions[i].program_id_index;
     875       16692 :     _add_to_data( &txn_raw_cur_ptr, &program_id_index, sizeof(uchar) );
     876             : 
     877             :     // Compact array of account addresses
     878       16692 :     ushort acct_count = (ushort) test_ctx->tx.message.instructions[i].accounts_count;
     879       16692 :     _add_compact_u16( &txn_raw_cur_ptr, acct_count );
     880       62871 :     for( ushort j = 0; j < acct_count; ++j ) {
     881       46179 :       uchar account_index = (uchar) test_ctx->tx.message.instructions[i].accounts[j];
     882       46179 :       _add_to_data( &txn_raw_cur_ptr, &account_index, sizeof(uchar) );
     883       46179 :     }
     884             : 
     885             :     // Compact array of 8-bit data
     886       16692 :     pb_bytes_array_t * data = test_ctx->tx.message.instructions[i].data;
     887       16692 :     if( data ) {
     888       13590 :       ushort data_len = (ushort) data->size;
     889       13590 :       _add_compact_u16( &txn_raw_cur_ptr, data_len );
     890       13590 :       _add_to_data( &txn_raw_cur_ptr, data->bytes, data_len );
     891       13590 :     } else {
     892        3102 :       _add_compact_u16( &txn_raw_cur_ptr, 0 );
     893        3102 :     }
     894       16692 :   }
     895             : 
     896             :   /* Address table lookups (N/A for legacy transactions) */
     897       11370 :   ushort addr_table_cnt = 0;
     898       11370 :   if( !test_ctx->tx.message.is_legacy ) {
     899             :     /* Compact array of address table lookups (https://solanacookbook.com/guides/versioned-transactions.html#compact-array-of-address-table-lookups) */
     900             :     // NOTE: The diagram is slightly wrong - the account key is a 32 byte pubkey, not a u8
     901       10878 :     addr_table_cnt = (ushort) test_ctx->tx.message.address_table_lookups_count;
     902       10878 :     _add_compact_u16( &txn_raw_cur_ptr, addr_table_cnt );
     903       18675 :     for( ushort i = 0; i < addr_table_cnt; ++i ) {
     904             :       // Account key
     905        7797 :       _add_to_data( &txn_raw_cur_ptr, test_ctx->tx.message.address_table_lookups[i].account_key, sizeof(fd_pubkey_t) );
     906             : 
     907             :       // Compact array of writable indexes
     908        7797 :       ushort writable_count = (ushort) test_ctx->tx.message.address_table_lookups[i].writable_indexes_count;
     909        7797 :       _add_compact_u16( &txn_raw_cur_ptr, writable_count );
     910       19896 :       for( ushort j = 0; j < writable_count; ++j ) {
     911       12099 :         uchar writable_index = (uchar) test_ctx->tx.message.address_table_lookups[i].writable_indexes[j];
     912       12099 :         _add_to_data( &txn_raw_cur_ptr, &writable_index, sizeof(uchar) );
     913       12099 :       }
     914             : 
     915             :       // Compact array of readonly indexes
     916        7797 :       ushort readonly_count = (ushort) test_ctx->tx.message.address_table_lookups[i].readonly_indexes_count;
     917        7797 :       _add_compact_u16( &txn_raw_cur_ptr, readonly_count );
     918       15966 :       for( ushort j = 0; j < readonly_count; ++j ) {
     919        8169 :         uchar readonly_index = (uchar) test_ctx->tx.message.address_table_lookups[i].readonly_indexes[j];
     920        8169 :         _add_to_data( &txn_raw_cur_ptr, &readonly_index, sizeof(uchar) );
     921        8169 :       }
     922        7797 :     }
     923       10878 :   }
     924             : 
     925             :   /* Set up txn descriptor from raw data */
     926       11370 :   fd_txn_t * txn_descriptor = (fd_txn_t *) fd_scratch_alloc( fd_txn_align(), fd_txn_footprint( instr_count, addr_table_cnt ) );
     927       11370 :   ushort txn_raw_sz = (ushort) (txn_raw_cur_ptr - txn_raw_begin);
     928       11370 :   if( !fd_txn_parse( txn_raw_begin, txn_raw_sz, txn_descriptor, NULL ) ) {
     929           0 :     FD_LOG_WARNING(("could not parse txn descriptor"));
     930           0 :     return NULL;
     931           0 :   }
     932             : 
     933             :   /* Run txn preparation phases and execution
     934             :      NOTE: This should be modified accordingly if transaction setup logic changes */
     935       11370 :   fd_txn_p_t * txn = fd_scratch_alloc( alignof(fd_txn_p_t), sizeof(fd_txn_p_t) );
     936       11370 :   memcpy( txn->payload, txn_raw_begin, txn_raw_sz );
     937       11370 :   txn->payload_sz = (ulong) txn_raw_sz;
     938       11370 :   txn->flags = FD_TXN_P_FLAGS_SANITIZE_SUCCESS;
     939       11370 :   memcpy( txn->_, txn_descriptor, fd_txn_footprint( instr_count, addr_table_cnt ) );
     940             : 
     941       11370 :   fd_execute_txn_task_info_t * task_info = fd_scratch_alloc( alignof(fd_execute_txn_task_info_t), sizeof(fd_execute_txn_task_info_t) );
     942       11370 :   memset( task_info, 0, sizeof(fd_execute_txn_task_info_t) );
     943       11370 :   task_info->txn = txn;
     944             : 
     945       11370 :   fd_tpool_t tpool[1];
     946       11370 :   tpool->worker_cnt = 1;
     947       11370 :   tpool->worker_max = 1;
     948             : 
     949       11370 :   fd_runtime_prepare_txns_start( slot_ctx, task_info, txn, 1UL );
     950             : 
     951             :   /* Setup the spad for account allocation */
     952       11370 :   task_info->txn_ctx->spad = runner->spad;
     953             : 
     954       11370 :   fd_runtime_pre_execute_check( task_info );
     955             : 
     956       11370 :   if( !task_info->exec_res ) {
     957        5355 :     task_info->txn->flags |= FD_TXN_P_FLAGS_EXECUTE_SUCCESS;
     958        5355 :     task_info->exec_res    = fd_execute_txn( task_info->txn_ctx );
     959        5355 :   }
     960             : 
     961       11370 :   slot_ctx->slot_bank.collected_execution_fees += task_info->txn_ctx->execution_fee;
     962       11370 :   slot_ctx->slot_bank.collected_priority_fees  += task_info->txn_ctx->priority_fee;
     963       11370 :   slot_ctx->slot_bank.collected_rent           += task_info->txn_ctx->collected_rent;
     964       11370 :   return task_info;
     965       11370 : }
     966             : 
     967             : void
     968             : fd_exec_test_instr_context_destroy( fd_exec_instr_test_runner_t * runner,
     969             :                                     fd_exec_instr_ctx_t *         ctx,
     970             :                                     fd_wksp_t *                   wksp,
     971       97416 :                                     fd_alloc_t *                  alloc ) {
     972       97416 :   if( !ctx ) return;
     973       97416 :   fd_exec_slot_ctx_t *  slot_ctx  = (fd_exec_slot_ctx_t *)ctx->slot_ctx;
     974       97416 :   if( !slot_ctx ) return;
     975       97416 :   fd_acc_mgr_t *        acc_mgr   = slot_ctx->acc_mgr;
     976       97416 :   fd_funk_txn_t *       funk_txn  = slot_ctx->funk_txn;
     977             : 
     978             :   // Free alloc
     979       97416 :   if( alloc ) {
     980       97416 :     fd_wksp_free_laddr( fd_alloc_delete( fd_alloc_leave( alloc ) ) );
     981       97416 :   }
     982             : 
     983             :   // Detach from workspace
     984       97416 :   fd_wksp_detach( wksp );
     985             : 
     986       97416 :   fd_exec_slot_ctx_free( slot_ctx );
     987       97416 :   fd_acc_mgr_delete( acc_mgr );
     988       97416 :   fd_scratch_pop();
     989             : 
     990       97416 :   fd_funk_start_write( runner->funk );
     991       97416 :   fd_funk_txn_cancel( runner->funk, funk_txn, 1 );
     992       97416 :   fd_funk_end_write( runner->funk );
     993             : 
     994       97416 :   ctx->slot_ctx = NULL;
     995       97416 : }
     996             : 
     997             : static void
     998             : _txn_context_destroy( fd_exec_instr_test_runner_t * runner,
     999             :                       fd_exec_slot_ctx_t *          slot_ctx,
    1000             :                       fd_wksp_t *                   wksp,
    1001       11370 :                       fd_alloc_t *                  alloc ) {
    1002       11370 :   if( !slot_ctx ) return; // This shouldn't be false either
    1003       11370 :   fd_acc_mgr_t *        acc_mgr   = slot_ctx->acc_mgr;
    1004       11370 :   fd_funk_txn_t *       funk_txn  = slot_ctx->funk_txn;
    1005             : 
    1006             :   // Free alloc
    1007       11370 :   if( alloc ) {
    1008       11370 :     fd_wksp_free_laddr( fd_alloc_delete( fd_alloc_leave( alloc ) ) );
    1009       11370 :   }
    1010             : 
    1011             :   // Detach from workspace
    1012       11370 :   fd_wksp_detach( wksp );
    1013             : 
    1014       11370 :   fd_exec_slot_ctx_free( slot_ctx );
    1015       11370 :   fd_acc_mgr_delete( acc_mgr );
    1016             : 
    1017       11370 :   fd_funk_start_write( runner->funk );
    1018       11370 :   fd_funk_txn_cancel( runner->funk, funk_txn, 1 );
    1019       11370 :   fd_funk_end_write( runner->funk );
    1020       11370 : }
    1021             : 
    1022             : /* fd_exec_instr_fixture_diff_t compares a test fixture against the
    1023             :    actual execution results. */
    1024             : 
    1025             : struct fd_exec_instr_fixture_diff {
    1026             :   fd_exec_instr_ctx_t *                ctx;
    1027             :   fd_exec_test_instr_context_t const * input;
    1028             :   fd_exec_test_instr_effects_t const * expected;
    1029             :   int                                  exec_result;
    1030             : 
    1031             :   int has_diff;
    1032             : };
    1033             : 
    1034             : typedef struct fd_exec_instr_fixture_diff fd_exec_instr_fixture_diff_t;
    1035             : 
    1036             : static int
    1037             : _diff_acct( fd_exec_test_acct_state_t const * want,
    1038      424866 :             fd_borrowed_account_t const *     have ) {
    1039             : 
    1040      424866 :   int diff = 0;
    1041             : 
    1042      424866 :   assert( 0==memcmp( want->address, have->pubkey->uc, sizeof(fd_pubkey_t) ) );
    1043             : 
    1044      424866 :   if( want->lamports != have->meta->info.lamports ) {
    1045           0 :     REPORT_ACCTV( NOTICE, want->address, "expected %lu lamports, got %lu",
    1046           0 :                   want->lamports, have->meta->info.lamports );
    1047           0 :     diff = 1;
    1048           0 :   }
    1049             : 
    1050      424866 :   if( !want->data && have->meta->dlen > 0 ) {
    1051           0 :     REPORT_ACCTV( NOTICE, want->address, "expected no data, but got %lu bytes",
    1052           0 :                   have->meta->dlen );
    1053           0 :     diff = 1;
    1054           0 :   }
    1055             : 
    1056      424866 :   if( want->data && want->data->size != have->meta->dlen ) {
    1057           0 :     REPORT_ACCTV( NOTICE, want->address, "expected data sz %u, got %lu",
    1058           0 :                   want->data->size, have->meta->dlen );
    1059           0 :     diff = 1;
    1060           0 :   }
    1061             : 
    1062      424866 :   if( want->executable != have->meta->info.executable ) {
    1063           0 :     REPORT_ACCTV( NOTICE, want->address, "expected account to be %s, but is %s",
    1064           0 :                   (want->executable           ) ? "executable" : "not executable",
    1065           0 :                   (have->meta->info.executable) ? "executable" : "not executable" );
    1066           0 :     diff = 1;
    1067           0 :   }
    1068             : 
    1069      424866 :   if( want->rent_epoch != have->meta->info.rent_epoch ) {
    1070           0 :     REPORT_ACCTV( NOTICE, want->address, "expected rent epoch %lu, got %lu",
    1071           0 :                   want->rent_epoch, have->meta->info.rent_epoch );
    1072           0 :     diff = 1;
    1073           0 :   }
    1074             : 
    1075      424866 :   if( 0!=memcmp( want->owner, have->meta->info.owner, sizeof(fd_pubkey_t) ) ) {
    1076           0 :     char a[ FD_BASE58_ENCODED_32_SZ ];
    1077           0 :     char b[ FD_BASE58_ENCODED_32_SZ ];
    1078           0 :     REPORT_ACCTV( NOTICE, want->address, "expected owner %s, got %s",
    1079           0 :                   fd_acct_addr_cstr( a, want->owner            ),
    1080           0 :                   fd_acct_addr_cstr( b, have->meta->info.owner ) );
    1081           0 :     diff = 1;
    1082           0 :   }
    1083             : 
    1084      424866 :   if( want->data && 0!=memcmp( want->data->bytes, have->data, want->data->size ) ) {
    1085           0 :     REPORT_ACCT( NOTICE, want->address, "data mismatch" );
    1086           0 :     diff = 1;
    1087           0 :   }
    1088             : 
    1089      424866 :   return diff;
    1090      424866 : }
    1091             : 
    1092             : static void
    1093             : _unexpected_acct_modify_in_fixture( fd_exec_instr_fixture_diff_t * check,
    1094           0 :                                     void const *                   pubkey ) {
    1095             : 
    1096             :   /* At this point, an account was reported as modified in the test
    1097             :      fixture, but no changes were seen locally. */
    1098             : 
    1099           0 :   check->has_diff = 1;
    1100             : 
    1101           0 :   REPORT_ACCT( NOTICE, pubkey, "expected changes, but none found" );
    1102           0 : }
    1103             : 
    1104             : static void
    1105             : _unexpected_acct_modify_locally( fd_exec_instr_fixture_diff_t * check,
    1106      413823 :                                  fd_borrowed_account_t const *  have ) {
    1107             : 
    1108             :   /* At this point, an account was reported as modified locally, but no
    1109             :      changes contained in fixture.  Thus, diff against the original
    1110             :      state in the fixture. */
    1111             : 
    1112             :   /* Find matching test input */
    1113             : 
    1114      413823 :   fd_exec_test_instr_context_t const * input = check->input;
    1115             : 
    1116      413823 :   fd_exec_test_acct_state_t * want = NULL;
    1117     9397083 :   for( ulong i=0UL; i < input->accounts_count; i++ ) {
    1118     9397083 :     fd_exec_test_acct_state_t * acct_state = &input->accounts[i];
    1119     9397083 :     if( 0==memcmp( acct_state->address, have->pubkey, sizeof(fd_pubkey_t) ) ) {
    1120      413823 :       want = acct_state;
    1121      413823 :       break;
    1122      413823 :     }
    1123     9397083 :   }
    1124      413823 :   if( FD_UNLIKELY( !want ) ) {
    1125           0 :     check->has_diff = 1;
    1126             : 
    1127           0 :     REPORT_ACCT( NOTICE, have->pubkey, "found unexpected changes" );
    1128             :     /* TODO: dump the account that changed unexpectedly */
    1129           0 :     return;
    1130           0 :   }
    1131             : 
    1132             :   /* Compare against original state */
    1133             : 
    1134      413823 :   check->has_diff |= _diff_acct( want, have );
    1135      413823 : }
    1136             : 
    1137             : static void
    1138       86199 : _diff_effects( fd_exec_instr_fixture_diff_t * check ) {
    1139             : 
    1140       86199 :   fd_exec_instr_ctx_t *                ctx         = check->ctx;
    1141       86199 :   fd_exec_test_instr_effects_t const * expected    = check->expected;
    1142       86199 :   int                                  exec_result = check->exec_result;
    1143             : 
    1144       86199 :   if( expected->result != exec_result ) {
    1145           0 :     check->has_diff = 1;
    1146           0 :     REPORTV( NOTICE, " expected result (%d-%s), got (%d-%s)",
    1147           0 :              expected->result, fd_executor_instr_strerror( -expected->result ),
    1148           0 :              exec_result,      fd_executor_instr_strerror( -exec_result      ) );
    1149             : 
    1150           0 :     if( ( expected->result == FD_EXECUTOR_INSTR_SUCCESS ) |
    1151           0 :         ( exec_result      == FD_EXECUTOR_INSTR_SUCCESS ) ) {
    1152             :       /* If one (and only one) of the results is success, stop diffing
    1153             :          for sake of brevity. */
    1154           0 :       return;
    1155           0 :     }
    1156           0 :   }
    1157       86199 :   else if( ( exec_result==FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR    ) &
    1158       86199 :            ( expected->custom_err != ctx->txn_ctx->custom_err ) ) {
    1159           0 :     check->has_diff = 1;
    1160           0 :     REPORTV( NOTICE, " expected custom error %u, got %u",
    1161           0 :              expected->custom_err, ctx->txn_ctx->custom_err );
    1162           0 :     return;
    1163           0 :   }
    1164             : 
    1165             :   /* Sort the transaction's write-locked accounts */
    1166             : 
    1167       86199 :   void const ** modified_pubkeys =
    1168       86199 :       fd_scratch_alloc( alignof(void *), ctx->txn_ctx->accounts_cnt * sizeof(void *) );
    1169       86199 :   ulong modified_acct_cnt = 0UL;
    1170             : 
    1171     1135911 :   for( ulong i=0UL; i < ctx->txn_ctx->accounts_cnt; i++ ) {
    1172     1049712 :     fd_borrowed_account_t * acc = &ctx->txn_ctx->borrowed_accounts[i];
    1173     1049712 :     if( acc->meta )  /* instruction took a writable handle? */
    1174      424866 :       modified_pubkeys[ modified_acct_cnt++ ] = &acc->pubkey->uc;
    1175     1049712 :   }
    1176             : 
    1177       86199 :   sort_pubkey_p_inplace( modified_pubkeys, modified_acct_cnt );
    1178             : 
    1179             :   /* Bitmask of which transaction accounts we've visited */
    1180             : 
    1181       86199 :   ulong   visited_sz = fd_ulong_align_up( modified_acct_cnt, 64UL )>>3;
    1182       86199 :   ulong * visited    = fd_scratch_alloc( alignof(ulong), visited_sz );
    1183       86199 :   fd_memset( visited, 0, visited_sz );
    1184             : 
    1185             :   /* Verify each of the expected accounts */
    1186             : 
    1187       97242 :   for( ulong i=0UL; i < expected->modified_accounts_count; i++ ) {
    1188       11043 :     fd_exec_test_acct_state_t const * want = &expected->modified_accounts[i];
    1189             : 
    1190       11043 :     void const * query = want->address;
    1191       11043 :     ulong idx = sort_pubkey_p_search_geq( modified_pubkeys, modified_acct_cnt, query );
    1192       11043 :     if( FD_UNLIKELY( idx >= modified_acct_cnt ) ) {
    1193           0 :       _unexpected_acct_modify_in_fixture( check, query );
    1194           0 :       continue;
    1195           0 :     }
    1196             : 
    1197       11043 :     if( FD_UNLIKELY( 0!=memcmp( modified_pubkeys[idx], query, sizeof(fd_pubkey_t) ) ) ) {
    1198           0 :       _unexpected_acct_modify_in_fixture( check, query );
    1199           0 :       continue;
    1200           0 :     }
    1201             : 
    1202       11043 :     visited[ idx>>6 ] |= fd_ulong_mask_bit( idx&63UL );
    1203             : 
    1204       11043 :     ulong acct_laddr = ( (ulong)modified_pubkeys[idx] - offsetof( fd_borrowed_account_t, pubkey ) );
    1205       11043 :     fd_borrowed_account_t const * acct = (fd_borrowed_account_t const *)acct_laddr;
    1206             : 
    1207       11043 :     check->has_diff |= _diff_acct( want, acct );
    1208       11043 :   }
    1209             : 
    1210             :   /* Visit accounts that were write-locked locally, but are not in
    1211             :      expected list */
    1212             : 
    1213      511065 :   for( ulong i=0UL; i < modified_acct_cnt; i++ ) {
    1214      424866 :     ulong acct_laddr = ( (ulong)modified_pubkeys[i] - offsetof( fd_borrowed_account_t, pubkey ) );
    1215      424866 :     fd_borrowed_account_t const * acct = (fd_borrowed_account_t const *)acct_laddr;
    1216             : 
    1217      424866 :     int was_visited = !!( visited[ i>>6 ] & fd_ulong_mask_bit( i&63UL ) );
    1218      424866 :     if( FD_UNLIKELY( !was_visited ) )
    1219      413823 :       _unexpected_acct_modify_locally( check, acct );
    1220      424866 :   }
    1221             : 
    1222             :   /* Check return data */
    1223       86199 :   ulong data_sz = expected->return_data ? expected->return_data->size : 0UL; /* support expected->return_data==NULL */
    1224       86199 :   if (data_sz != ctx->txn_ctx->return_data.len) {
    1225           0 :     check->has_diff = 1;
    1226           0 :     REPORTV( WARNING, " expected return data size %lu, got %lu",
    1227           0 :              (ulong) data_sz, ctx->txn_ctx->return_data.len );
    1228           0 :   }
    1229       86199 :   else if (data_sz > 0 ) {
    1230        2208 :     if( memcmp( expected->return_data->bytes, ctx->txn_ctx->return_data.data, expected->return_data->size ) ) {
    1231           0 :       check->has_diff = 1;
    1232           0 :       REPORT( WARNING, " return data mismatch" );
    1233           0 :     }
    1234        2208 :   }
    1235             : 
    1236             :   /* TODO: Capture account side effects outside of the access list by
    1237             :            looking at the funk record delta (technically a scheduling
    1238             :            violation) */
    1239       86199 : }
    1240             : 
    1241             : static fd_sbpf_syscalls_t *
    1242             : lookup_syscall_func( fd_sbpf_syscalls_t *syscalls,
    1243             :                      const char *syscall_name,
    1244       11211 :                      size_t len) {
    1245       11211 :   ulong i;
    1246             : 
    1247       11211 :   if (!syscall_name) return NULL;
    1248             : 
    1249      467625 :   for (i = 0; i < fd_sbpf_syscalls_slot_cnt(); ++i) {
    1250      467625 :     if (!fd_sbpf_syscalls_key_inval(syscalls[i].key) && syscalls[i].name && strlen(syscalls[i].name) == len) {
    1251       17532 :       if (!memcmp(syscalls[i].name, syscall_name, len)) {
    1252       11211 :         return syscalls + i;
    1253       11211 :       }
    1254       17532 :     }
    1255      467625 :   }
    1256             : 
    1257           0 :   return NULL;
    1258       11211 : }
    1259             : 
    1260             : int
    1261             : fd_exec_instr_fixture_run( fd_exec_instr_test_runner_t *        runner,
    1262             :                            fd_exec_test_instr_fixture_t const * test,
    1263       86199 :                            char const *                         log_name ) {
    1264       86199 :   fd_wksp_t *  wksp  = fd_wksp_attach( "wksp" );
    1265       86199 :   fd_alloc_t * alloc = fd_alloc_join( fd_alloc_new( fd_wksp_alloc_laddr( wksp, fd_alloc_align(), fd_alloc_footprint(), 2 ), 2 ), 0 );
    1266       86199 :   fd_exec_instr_ctx_t ctx[1];
    1267       86199 :   if( FD_UNLIKELY( !fd_exec_test_instr_context_create( runner, ctx, &test->input, alloc, false ) ) ) {
    1268           0 :     fd_exec_test_instr_context_destroy( runner, ctx, wksp, alloc );
    1269           0 :     return 0;
    1270           0 :   }
    1271             : 
    1272       86199 :   fd_instr_info_t * instr = (fd_instr_info_t *) ctx->instr;
    1273             : 
    1274             :   /* Execute the test */
    1275       86199 :   int exec_result = fd_execute_instr(ctx->txn_ctx, instr);
    1276             : 
    1277       86199 :   int has_diff;
    1278       86199 :   do {
    1279             :     /* Compare local execution results against fixture */
    1280             : 
    1281       86199 :     fd_cstr_printf( _report_prefix, sizeof(_report_prefix), NULL, "%s: ", log_name );
    1282             : 
    1283       86199 :     fd_exec_instr_fixture_diff_t diff =
    1284       86199 :       { .ctx         = ctx,
    1285       86199 :         .input       = &test->input,
    1286       86199 :         .expected    = &test->output,
    1287       86199 :         .exec_result = -exec_result };
    1288       86199 :     _diff_effects( &diff );
    1289             : 
    1290       86199 :     _report_prefix[0] = '\0';
    1291             : 
    1292       86199 :     has_diff = diff.has_diff;
    1293       86199 :   } while(0);
    1294             : 
    1295       86199 :   fd_exec_test_instr_context_destroy( runner, ctx, wksp, alloc );
    1296       86199 :   return !has_diff;
    1297       86199 : }
    1298             : 
    1299             : ulong
    1300             : fd_exec_instr_test_run( fd_exec_instr_test_runner_t * runner,
    1301             :                         void const *                  input_,
    1302             :                         void **                       output_,
    1303             :                         void *                        output_buf,
    1304           0 :                         ulong                         output_bufsz ) {
    1305           0 :   fd_exec_test_instr_context_t const * input  = fd_type_pun_const( input_ );
    1306           0 :   fd_exec_test_instr_effects_t **      output = fd_type_pun( output_ );
    1307           0 :   fd_wksp_t *  wksp  = fd_wksp_attach( "wksp" );
    1308           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 );
    1309             : 
    1310             :   /* Convert the Protobuf inputs to a fd_exec context */
    1311           0 :   fd_exec_instr_ctx_t ctx[1];
    1312           0 :   if( !fd_exec_test_instr_context_create( runner, ctx, input, alloc, false ) ) {
    1313           0 :     fd_exec_test_instr_context_destroy( runner, ctx, wksp, alloc );
    1314           0 :     return 0UL;
    1315           0 :   }
    1316             : 
    1317           0 :   fd_instr_info_t * instr = (fd_instr_info_t *) ctx->instr;
    1318             : 
    1319             :   /* Execute the test */
    1320           0 :   int exec_result = fd_execute_instr(ctx->txn_ctx, instr);
    1321             : 
    1322             :   /* Allocate space to capture outputs */
    1323             : 
    1324           0 :   ulong output_end = (ulong)output_buf + output_bufsz;
    1325           0 :   FD_SCRATCH_ALLOC_INIT( l, output_buf );
    1326             : 
    1327           0 :   fd_exec_test_instr_effects_t * effects =
    1328           0 :     FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_exec_test_instr_effects_t),
    1329           0 :                                 sizeof (fd_exec_test_instr_effects_t) );
    1330           0 :   if( FD_UNLIKELY( _l > output_end ) ) {
    1331           0 :     fd_exec_test_instr_context_destroy( runner, ctx, wksp, alloc );
    1332           0 :     return 0UL;
    1333           0 :   }
    1334           0 :   fd_memset( effects, 0, sizeof(fd_exec_test_instr_effects_t) );
    1335             : 
    1336             :   /* Capture error code */
    1337             : 
    1338           0 :   effects->result   = -exec_result;
    1339           0 :   effects->cu_avail = ctx->txn_ctx->compute_meter;
    1340             : 
    1341           0 :   if( exec_result == FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR ) {
    1342           0 :     effects->custom_err     = ctx->txn_ctx->custom_err;
    1343           0 :   }
    1344             : 
    1345             :   /* Allocate space for captured accounts */
    1346           0 :   ulong modified_acct_cnt = ctx->txn_ctx->accounts_cnt;
    1347             : 
    1348           0 :   fd_exec_test_acct_state_t * modified_accts =
    1349           0 :     FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_exec_test_acct_state_t),
    1350           0 :                                 sizeof (fd_exec_test_acct_state_t) * modified_acct_cnt );
    1351           0 :   if( FD_UNLIKELY( _l > output_end ) ) {
    1352           0 :     fd_exec_test_instr_context_destroy( runner, ctx, wksp, alloc );
    1353           0 :     return 0;
    1354           0 :   }
    1355           0 :   effects->modified_accounts       = modified_accts;
    1356           0 :   effects->modified_accounts_count = 0UL;
    1357             : 
    1358             :   /* Capture borrowed accounts */
    1359             : 
    1360           0 :   for( ulong j=0UL; j < ctx->txn_ctx->accounts_cnt; j++ ) {
    1361           0 :     fd_borrowed_account_t * acc = &ctx->txn_ctx->borrowed_accounts[j];
    1362           0 :     if( !acc->meta ) continue;
    1363             : 
    1364           0 :     ulong modified_idx = effects->modified_accounts_count;
    1365           0 :     assert( modified_idx < modified_acct_cnt );
    1366             : 
    1367           0 :     fd_exec_test_acct_state_t * out_acct = &effects->modified_accounts[ modified_idx ];
    1368           0 :     memset( out_acct, 0, sizeof(fd_exec_test_acct_state_t) );
    1369             :     /* Copy over account content */
    1370             : 
    1371           0 :     memcpy( out_acct->address, acc->pubkey, sizeof(fd_pubkey_t) );
    1372           0 :     out_acct->lamports     = acc->const_meta->info.lamports;
    1373           0 :     out_acct->data =
    1374           0 :       FD_SCRATCH_ALLOC_APPEND( l, alignof(pb_bytes_array_t),
    1375           0 :                                   PB_BYTES_ARRAY_T_ALLOCSIZE( acc->const_meta->dlen ) );
    1376           0 :     if( FD_UNLIKELY( _l > output_end ) ) {
    1377           0 :       fd_exec_test_instr_context_destroy( runner, ctx, wksp, alloc );
    1378           0 :       return 0UL;
    1379           0 :     }
    1380           0 :     out_acct->data->size = (pb_size_t)acc->const_meta->dlen;
    1381           0 :     fd_memcpy( out_acct->data->bytes, acc->const_data, acc->const_meta->dlen );
    1382             : 
    1383           0 :     out_acct->executable     = acc->const_meta->info.executable;
    1384           0 :     out_acct->rent_epoch     = acc->const_meta->info.rent_epoch;
    1385           0 :     memcpy( out_acct->owner, acc->const_meta->info.owner, sizeof(fd_pubkey_t) );
    1386             : 
    1387           0 :     effects->modified_accounts_count++;
    1388           0 :   }
    1389             : 
    1390             :   /* Capture return data */
    1391           0 :   fd_txn_return_data_t * return_data = &ctx->txn_ctx->return_data;
    1392           0 :   effects->return_data = FD_SCRATCH_ALLOC_APPEND(l, alignof(pb_bytes_array_t),
    1393           0 :                               PB_BYTES_ARRAY_T_ALLOCSIZE( return_data->len ) );
    1394           0 :   if( FD_UNLIKELY( _l > output_end ) ) {
    1395           0 :     fd_exec_test_instr_context_destroy( runner, ctx, wksp, alloc );
    1396           0 :     return 0UL;
    1397           0 :   }
    1398           0 :   effects->return_data->size = (pb_size_t)return_data->len;
    1399           0 :   fd_memcpy( effects->return_data->bytes, return_data->data, return_data->len );
    1400             : 
    1401           0 :   ulong actual_end = FD_SCRATCH_ALLOC_FINI( l, 1UL );
    1402           0 :   fd_exec_test_instr_context_destroy( runner, ctx, wksp, alloc );
    1403             : 
    1404           0 :   *output = effects;
    1405           0 :   return actual_end - (ulong)output_buf;
    1406           0 : }
    1407             : 
    1408             : ulong
    1409             : fd_exec_txn_test_run( fd_exec_instr_test_runner_t * runner, // Runner only contains funk instance, so we can borrow instr test runner
    1410             :                       void const *                  input_,
    1411             :                       void **                       output_,
    1412             :                       void *                        output_buf,
    1413       11370 :                       ulong                         output_bufsz ) {
    1414       11370 :   fd_exec_test_txn_context_t const * input  = fd_type_pun_const( input_ );
    1415       11370 :   fd_exec_test_txn_result_t **       output = fd_type_pun( output_ );
    1416             : 
    1417       11370 :   FD_SCRATCH_SCOPE_BEGIN {
    1418             :     /* Initialize memory */
    1419       11370 :     fd_wksp_t *           wksp          = fd_wksp_attach( "wksp" );
    1420       11370 :     fd_alloc_t *          alloc         = fd_alloc_join( fd_alloc_new( fd_wksp_alloc_laddr( wksp, fd_alloc_align(), fd_alloc_footprint(), 2 ), 2 ), 0 );
    1421       11370 :     uchar *               slot_ctx_mem  = fd_scratch_alloc( FD_EXEC_SLOT_CTX_ALIGN,  FD_EXEC_SLOT_CTX_FOOTPRINT  );
    1422       11370 :     fd_exec_slot_ctx_t *  slot_ctx      = fd_exec_slot_ctx_join ( fd_exec_slot_ctx_new ( slot_ctx_mem, fd_alloc_virtual( alloc ) ) );
    1423             : 
    1424             :     /* Create and exec transaction */
    1425       11370 :     fd_execute_txn_task_info_t * task_info = _txn_context_create_and_exec( runner, slot_ctx, input );
    1426       11370 :     if( task_info == NULL ) {
    1427           0 :       _txn_context_destroy( runner, slot_ctx, wksp, alloc );
    1428           0 :       return 0UL;
    1429           0 :     }
    1430       11370 :     fd_exec_txn_ctx_t * txn_ctx = task_info->txn_ctx;
    1431             : 
    1432       11370 :     int exec_res = task_info->exec_res;
    1433             : 
    1434             :     /* Start saving txn exec results */
    1435       11370 :     FD_SCRATCH_ALLOC_INIT( l, output_buf );
    1436       11370 :     ulong output_end = (ulong)output_buf + output_bufsz;
    1437             : 
    1438       11370 :     fd_exec_test_txn_result_t * txn_result =
    1439       11370 :     FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_exec_test_txn_result_t),
    1440       11370 :                                   sizeof (fd_exec_test_txn_result_t) );
    1441       11370 :     if( FD_UNLIKELY( _l > output_end ) ) {
    1442           0 :       abort();
    1443           0 :     }
    1444       11370 :     fd_memset( txn_result, 0, sizeof(fd_exec_test_txn_result_t) );
    1445             : 
    1446             :     /* Capture basic results fields */
    1447       11370 :     txn_result->executed                          = task_info->txn->flags & FD_TXN_P_FLAGS_EXECUTE_SUCCESS;
    1448       11370 :     txn_result->sanitization_error                = !( task_info->txn->flags & FD_TXN_P_FLAGS_SANITIZE_SUCCESS );
    1449       11370 :     txn_result->has_resulting_state               = false;
    1450       11370 :     txn_result->resulting_state.acct_states_count = 0;
    1451       11370 :     txn_result->is_ok                             = !exec_res;
    1452       11370 :     txn_result->status                            = (uint32_t) -exec_res;
    1453       11370 :     txn_result->instruction_error                 = 0;
    1454       11370 :     txn_result->instruction_error_index           = 0;
    1455       11370 :     txn_result->custom_error                      = 0;
    1456       11370 :     txn_result->executed_units                    = txn_ctx->compute_unit_limit - txn_ctx->compute_meter;
    1457       11370 :     txn_result->has_fee_details                   = false;
    1458             : 
    1459       11370 :     if( txn_result->sanitization_error ) {
    1460             :       /* If exec_res was an instruction error, capture the error number and idx */
    1461        6015 :       if( exec_res == FD_RUNTIME_TXN_ERR_INSTRUCTION_ERROR ) {
    1462        3540 :         txn_result->instruction_error = (uint32_t) -task_info->txn_ctx->exec_err;
    1463        3540 :         txn_result->instruction_error_index = (uint32_t) task_info->txn_ctx->instr_err_idx;
    1464             : 
    1465             :         /* 
    1466             :         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. 
    1467             :         For now, only precompiles throw custom error codes, so we can ignore all custom error codes thrown in the sanitization phase. If this changes,
    1468             :         this logic will have to be revisited.
    1469             : 
    1470             :         if( task_info->txn_ctx->exec_err == FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR ) {
    1471             :           txn_result->custom_error = txn_ctx->custom_err;
    1472             :         } 
    1473             :         */
    1474        3540 :       }
    1475        6015 :       ulong actual_end = FD_SCRATCH_ALLOC_FINI( l, 1UL );
    1476        6015 :       _txn_context_destroy( runner, slot_ctx, wksp, alloc );
    1477             : 
    1478        6015 :       *output = txn_result;
    1479        6015 :       return actual_end - (ulong)output_buf;
    1480        6015 :     }
    1481             : 
    1482        5355 :     txn_result->has_fee_details                   = true;
    1483        5355 :     txn_result->fee_details.transaction_fee       = slot_ctx->slot_bank.collected_execution_fees;
    1484        5355 :     txn_result->fee_details.prioritization_fee    = slot_ctx->slot_bank.collected_priority_fees;
    1485             : 
    1486             :     /* Rent is only collected on successfully loaded transactions */
    1487        5355 :     txn_result->rent                              = txn_ctx->collected_rent;
    1488             : 
    1489             :     /* At this point, the transaction has executed */
    1490        5355 :     if( exec_res ) {
    1491             :       /* Instruction error index must be set for the txn error to be an instruction error */
    1492        4104 :       if( txn_ctx->instr_err_idx != INT32_MAX ) {
    1493        4098 :         txn_result->status = (uint32_t) -FD_RUNTIME_TXN_ERR_INSTRUCTION_ERROR;
    1494        4098 :         txn_result->instruction_error = (uint32_t) -exec_res;
    1495        4098 :         txn_result->instruction_error_index = (uint32_t) txn_ctx->instr_err_idx;
    1496        4098 :         if( exec_res == FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR ) {
    1497         684 :           txn_result->custom_error = txn_ctx->custom_err;
    1498         684 :         }
    1499        4098 :       } else {
    1500           6 :         txn_result->status = (uint32_t) -exec_res;
    1501           6 :       }
    1502        4104 :     }
    1503             : 
    1504        5355 :     if( txn_ctx->return_data.len > 0 ) {
    1505           9 :       txn_result->return_data = FD_SCRATCH_ALLOC_APPEND( l, alignof(pb_bytes_array_t),
    1506           9 :                                       PB_BYTES_ARRAY_T_ALLOCSIZE( txn_ctx->return_data.len ) );
    1507           9 :       if( FD_UNLIKELY( _l > output_end ) ) {
    1508           0 :         abort();
    1509           0 :       }
    1510             : 
    1511           9 :       txn_result->return_data->size = (pb_size_t)txn_ctx->return_data.len;
    1512           9 :       fd_memcpy( txn_result->return_data->bytes, txn_ctx->return_data.data, txn_ctx->return_data.len );
    1513           9 :     }
    1514             : 
    1515             :     /* Allocate space for captured accounts */
    1516        5355 :     ulong modified_acct_cnt = txn_ctx->accounts_cnt;
    1517             : 
    1518        5355 :     txn_result->has_resulting_state         = true;
    1519        5355 :     txn_result->resulting_state.acct_states =
    1520        5355 :       FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_exec_test_acct_state_t),
    1521        5355 :                                   sizeof (fd_exec_test_acct_state_t) * modified_acct_cnt );
    1522        5355 :     if( FD_UNLIKELY( _l > output_end ) ) {
    1523           0 :       abort();
    1524           0 :     }
    1525             : 
    1526             :     /* Capture borrowed accounts */
    1527       39048 :     for( ulong j=0UL; j < txn_ctx->accounts_cnt; j++ ) {
    1528       33693 :       fd_borrowed_account_t * acc = &txn_ctx->borrowed_accounts[j];
    1529       33693 :       if( !acc->meta ) continue;
    1530             : 
    1531       15834 :       ulong modified_idx = txn_result->resulting_state.acct_states_count;
    1532       15834 :       assert( modified_idx < modified_acct_cnt );
    1533             : 
    1534       15834 :       fd_exec_test_acct_state_t * out_acct = &txn_result->resulting_state.acct_states[ modified_idx ];
    1535       15834 :       memset( out_acct, 0, sizeof(fd_exec_test_acct_state_t) );
    1536             :       /* Copy over account content */
    1537             : 
    1538       15834 :       memcpy( out_acct->address, acc->pubkey, sizeof(fd_pubkey_t) );
    1539             : 
    1540       15834 :       out_acct->lamports = acc->const_meta->info.lamports;
    1541             : 
    1542       15834 :       if( acc->const_meta->dlen > 0 ) {
    1543        5430 :         out_acct->data =
    1544        5430 :           FD_SCRATCH_ALLOC_APPEND( l, alignof(pb_bytes_array_t),
    1545        5430 :                                       PB_BYTES_ARRAY_T_ALLOCSIZE( acc->const_meta->dlen ) );
    1546        5430 :         if( FD_UNLIKELY( _l > output_end ) ) {
    1547           0 :           abort();
    1548           0 :         }
    1549        5430 :         out_acct->data->size = (pb_size_t)acc->const_meta->dlen;
    1550        5430 :         fd_memcpy( out_acct->data->bytes, acc->const_data, acc->const_meta->dlen );
    1551        5430 :       }
    1552             : 
    1553       15834 :       out_acct->executable     = acc->const_meta->info.executable;
    1554       15834 :       out_acct->rent_epoch     = acc->const_meta->info.rent_epoch;
    1555       15834 :       memcpy( out_acct->owner, acc->const_meta->info.owner, sizeof(fd_pubkey_t) );
    1556             : 
    1557       15834 :       txn_result->resulting_state.acct_states_count++;
    1558       15834 :     }
    1559             : 
    1560        5355 :     ulong actual_end = FD_SCRATCH_ALLOC_FINI( l, 1UL );
    1561        5355 :     _txn_context_destroy( runner, slot_ctx, wksp, alloc );
    1562             : 
    1563        5355 :     *output = txn_result;
    1564        5355 :     return actual_end - (ulong)output_buf;
    1565       11370 :   } FD_SCRATCH_SCOPE_END;
    1566       11370 : }
    1567             : 
    1568             : 
    1569             : ulong
    1570             : fd_sbpf_program_load_test_run( FD_PARAM_UNUSED fd_exec_instr_test_runner_t * runner,
    1571             :                                void const *                  input_,
    1572             :                                void **                       output_,
    1573             :                                void *                        output_buf,
    1574         591 :                                ulong                         output_bufsz ) {
    1575         591 :   fd_exec_test_elf_loader_ctx_t const * input  = fd_type_pun_const( input_ );
    1576         591 :   fd_exec_test_elf_loader_effects_t **  output = fd_type_pun( output_ );
    1577             : 
    1578         591 :   fd_sbpf_elf_info_t info;
    1579         591 :   fd_valloc_t valloc = fd_scratch_virtual();
    1580             : 
    1581         591 :   if ( FD_UNLIKELY( !input->has_elf || !input->elf.data ) ){
    1582           0 :     return 0UL;
    1583           0 :   }
    1584             : 
    1585         591 :   ulong elf_sz = input->elf_sz;
    1586         591 :   void const * _bin;
    1587             : 
    1588             :   /* elf_sz will be passed as arguments to elf loader functions.
    1589             :      pb decoder allocates memory for elf.data based on its actual size,
    1590             :      not elf_sz !.
    1591             :      If elf_sz is larger than the size of actual elf data, this may result
    1592             :      in out-of-bounds accesses which will upset ASAN (however intentional).
    1593             :      So in this case we just copy the data into a memory region of elf_sz bytes
    1594             : 
    1595             :      ! The decoupling of elf_sz and the actual binary size is intentional to test
    1596             :       underflow/overflow behavior */
    1597         591 :   if ( elf_sz > input->elf.data->size ){
    1598           0 :     void * tmp = fd_valloc_malloc( valloc, 1UL, elf_sz );
    1599           0 :     if ( FD_UNLIKELY( !tmp ) ){
    1600           0 :       return 0UL;
    1601           0 :     }
    1602           0 :     fd_memcpy( tmp, input->elf.data->bytes, input->elf.data->size );
    1603           0 :     _bin = tmp;
    1604         591 :   } else {
    1605         591 :     _bin = input->elf.data->bytes;
    1606         591 :   }
    1607             : 
    1608             :   // Allocate space for captured effects
    1609         591 :   ulong output_end = (ulong)output_buf + output_bufsz;
    1610         591 :   FD_SCRATCH_ALLOC_INIT( l, output_buf );
    1611             : 
    1612         591 :   fd_exec_test_elf_loader_effects_t * elf_effects =
    1613         591 :     FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_exec_test_elf_loader_effects_t),
    1614         591 :                                 sizeof (fd_exec_test_elf_loader_effects_t) );
    1615         591 :   if( FD_UNLIKELY( _l > output_end ) ) {
    1616             :     /* return 0 on fuzz-specific failures */
    1617           0 :     return 0UL;
    1618           0 :   }
    1619         591 :   fd_memset( elf_effects, 0, sizeof(fd_exec_test_elf_loader_effects_t) );
    1620             : 
    1621             :   /* wrap the loader code in do-while(0) block so that we can exit
    1622             :      immediately if execution fails at any point */
    1623             : 
    1624         591 :   do{
    1625             : 
    1626         591 :     if( FD_UNLIKELY( !fd_sbpf_elf_peek( &info, _bin, elf_sz, input->deploy_checks ) ) ) {
    1627             :       /* return incomplete effects on execution failures */
    1628           0 :       break;
    1629           0 :     }
    1630             : 
    1631         591 :     void* rodata = fd_valloc_malloc( valloc, FD_SBPF_PROG_RODATA_ALIGN, info.rodata_footprint );
    1632         591 :     FD_TEST( rodata );
    1633             : 
    1634         591 :     fd_sbpf_program_t * prog = fd_sbpf_program_new( fd_valloc_malloc( valloc, fd_sbpf_program_align(), fd_sbpf_program_footprint( &info ) ), &info, rodata );
    1635         591 :     FD_TEST( prog );
    1636             : 
    1637         591 :     fd_sbpf_syscalls_t * syscalls = fd_sbpf_syscalls_new( fd_valloc_malloc( valloc, fd_sbpf_syscalls_align(), fd_sbpf_syscalls_footprint() ));
    1638         591 :     FD_TEST( syscalls );
    1639             : 
    1640         591 :     fd_vm_syscall_register_all( syscalls, 0 );
    1641             : 
    1642         591 :     int res = fd_sbpf_program_load( prog, _bin, elf_sz, syscalls, input->deploy_checks );
    1643         591 :     if( FD_UNLIKELY( res ) ) {
    1644           3 :       break;
    1645           3 :     }
    1646             : 
    1647         588 :     fd_memset( elf_effects, 0, sizeof(fd_exec_test_elf_loader_effects_t) );
    1648         588 :     elf_effects->rodata_sz = prog->rodata_sz;
    1649             : 
    1650             :     // Load rodata section
    1651         588 :     elf_effects->rodata = FD_SCRATCH_ALLOC_APPEND(l, 8UL, PB_BYTES_ARRAY_T_ALLOCSIZE( prog->rodata_sz ));
    1652         588 :     if( FD_UNLIKELY( _l > output_end ) ) {
    1653           0 :       return 0UL;
    1654           0 :     }
    1655         588 :     elf_effects->rodata->size = (pb_size_t) prog->rodata_sz;
    1656         588 :     fd_memcpy( &(elf_effects->rodata->bytes), prog->rodata, prog->rodata_sz );
    1657             : 
    1658         588 :     elf_effects->text_cnt = prog->text_cnt;
    1659         588 :     elf_effects->text_off = prog->text_off;
    1660             : 
    1661         588 :     elf_effects->entry_pc = prog->entry_pc;
    1662             : 
    1663             : 
    1664         588 :     pb_size_t calldests_sz = (pb_size_t) fd_sbpf_calldests_cnt( prog->calldests);
    1665         588 :     elf_effects->calldests_count = calldests_sz;
    1666         588 :     elf_effects->calldests = FD_SCRATCH_ALLOC_APPEND(l, 8UL, calldests_sz * sizeof(uint64_t));
    1667         588 :     if( FD_UNLIKELY( _l > output_end ) ) {
    1668           0 :       return 0UL;
    1669           0 :     }
    1670             : 
    1671         588 :     ulong i = 0;
    1672      161052 :     for(ulong target_pc = fd_sbpf_calldests_const_iter_init(prog->calldests); !fd_sbpf_calldests_const_iter_done(target_pc);
    1673      160464 :     target_pc = fd_sbpf_calldests_const_iter_next(prog->calldests, target_pc)) {
    1674      160464 :       elf_effects->calldests[i] = target_pc;
    1675      160464 :       ++i;
    1676      160464 :     }
    1677         588 :   } while(0);
    1678             : 
    1679         591 :   ulong actual_end = FD_SCRATCH_ALLOC_FINI( l, 1UL );
    1680             : 
    1681         591 :   *output = elf_effects;
    1682         591 :   return actual_end - (ulong) output_buf;
    1683         591 : }
    1684             : 
    1685             : static fd_exec_test_instr_effects_t const * cpi_exec_effects = NULL;
    1686             : 
    1687             : ulong
    1688             : fd_exec_vm_syscall_test_run( fd_exec_instr_test_runner_t * runner,
    1689             :                              void const *                  input_,
    1690             :                              void **                       output_,
    1691             :                              void *                        output_buf,
    1692       11211 :                              ulong                         output_bufsz ) {
    1693       11211 :   fd_exec_test_syscall_context_t const * input =  fd_type_pun_const( input_ );
    1694       11211 :   fd_exec_test_syscall_effects_t **      output = fd_type_pun( output_ );
    1695       11211 :   fd_wksp_t *  wksp  = fd_wksp_attach( "wksp" );
    1696       11211 :   fd_alloc_t * alloc = fd_alloc_join( fd_alloc_new( fd_wksp_alloc_laddr( wksp, fd_alloc_align(), fd_alloc_footprint(), 2 ), 2 ), 0 );
    1697             : 
    1698             :   /* Create execution context */
    1699       11211 :   const fd_exec_test_instr_context_t * input_instr_ctx = &input->instr_ctx;
    1700       11211 :   fd_exec_instr_ctx_t ctx[1];
    1701             :   // Skip extra checks for non-CPI syscalls
    1702       11211 :   int is_cpi            = !strncmp( (const char *)input->syscall_invocation.function_name.bytes, "sol_invoke_signed", 17 );
    1703       11211 :   int skip_extra_checks = !is_cpi;
    1704             : 
    1705       11211 :   if( !fd_exec_test_instr_context_create( runner, ctx, input_instr_ctx, alloc, skip_extra_checks ) )
    1706           0 :     goto error;
    1707       11211 :   fd_valloc_t valloc = fd_scratch_virtual();
    1708             : 
    1709             :   /* Capture outputs */
    1710       11211 :   ulong output_end = (ulong)output_buf + output_bufsz;
    1711       11211 :   FD_SCRATCH_ALLOC_INIT( l, output_buf );
    1712       11211 :   fd_exec_test_syscall_effects_t * effects =
    1713       11211 :     FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_exec_test_syscall_effects_t),
    1714       11211 :                                 sizeof (fd_exec_test_syscall_effects_t) );
    1715       11211 :   if( FD_UNLIKELY( _l > output_end ) ) {
    1716           0 :     goto error;
    1717           0 :   }
    1718             : 
    1719       11211 :   if (input->vm_ctx.return_data.program_id && input->vm_ctx.return_data.program_id->size == sizeof(fd_pubkey_t)) {
    1720         714 :     fd_memcpy( ctx->txn_ctx->return_data.program_id.uc, input->vm_ctx.return_data.program_id->bytes, sizeof(fd_pubkey_t) );
    1721         714 :     ctx->txn_ctx->return_data.len = input->vm_ctx.return_data.data->size;
    1722         714 :     fd_memcpy( ctx->txn_ctx->return_data.data, input->vm_ctx.return_data.data->bytes, ctx->txn_ctx->return_data.len );
    1723         714 :   }
    1724             : 
    1725       11211 :   *effects = (fd_exec_test_syscall_effects_t) FD_EXEC_TEST_SYSCALL_EFFECTS_INIT_ZERO;
    1726             : 
    1727             :   /* Set up the VM instance */
    1728       11211 :   fd_sha256_t _sha[1];
    1729       11211 :   fd_sha256_t * sha = fd_sha256_join( fd_sha256_new( _sha ) );
    1730       11211 :   fd_sbpf_syscalls_t * syscalls = fd_sbpf_syscalls_new( fd_valloc_malloc( valloc, fd_sbpf_syscalls_align(), fd_sbpf_syscalls_footprint() ) );
    1731       11211 :   fd_vm_syscall_register_all( syscalls, 0 );
    1732             : 
    1733             :   /* Pull out the memory regions */
    1734       11211 :   if( !input->has_vm_ctx ) {
    1735           0 :     goto error;
    1736           0 :   }
    1737       11211 :   if( input->has_exec_effects ){
    1738         171 :     cpi_exec_effects = &input->exec_effects;
    1739         171 :   }
    1740             : 
    1741       11211 :   ulong rodata_sz = input->vm_ctx.rodata ? input->vm_ctx.rodata->size : 0UL;
    1742       11211 :   uchar * rodata = fd_valloc_malloc( valloc, 8UL, rodata_sz );
    1743       11211 :   if ( input->vm_ctx.rodata != NULL ) {
    1744         714 :     fd_memcpy( rodata, input->vm_ctx.rodata->bytes, rodata_sz );
    1745         714 :   }
    1746             : 
    1747             :   /* Load input data regions */
    1748       11211 :   fd_vm_input_region_t * input_regions = NULL;
    1749       11211 :   uint input_regions_count = 0U;
    1750       11211 :   if( !!(input->vm_ctx.input_data_regions_count) ) {
    1751         714 :     input_regions       = fd_valloc_malloc( valloc, alignof(fd_vm_input_region_t), sizeof(fd_vm_input_region_t) * input->vm_ctx.input_data_regions_count );
    1752         714 :     input_regions_count = setup_vm_input_regions( input_regions, input->vm_ctx.input_data_regions, input->vm_ctx.input_data_regions_count, valloc );
    1753         714 :     if ( !input_regions_count ) {
    1754           0 :       goto error;
    1755           0 :     }
    1756         714 :   }
    1757             : 
    1758       11211 :   if( input->vm_ctx.heap_max > FD_VM_HEAP_MAX ) {
    1759           0 :     goto error;
    1760           0 :   }
    1761             : 
    1762       11211 :   fd_vm_t * vm = fd_vm_join( fd_vm_new( fd_valloc_malloc( valloc, fd_vm_align(), fd_vm_footprint() ) ) );
    1763       11211 :   if ( !vm ) {
    1764           0 :     goto error;
    1765           0 :   }
    1766             : 
    1767             :   /* If the program ID account owner is the v1 BPF loader, then alignment is disabled (controlled by
    1768             :      the `is_deprecated` flag) */
    1769       11211 :   uchar program_id_idx = ctx->instr->program_id;
    1770       11211 :   uchar is_deprecated = ( program_id_idx < ctx->txn_ctx->accounts_cnt ) && 
    1771       11211 :                         ( !memcmp( ctx->txn_ctx->borrowed_accounts[program_id_idx].const_meta->info.owner, fd_solana_bpf_loader_deprecated_program_id.key, sizeof(fd_pubkey_t) ) );
    1772             : 
    1773       11211 :   fd_vm_init(
    1774       11211 :     vm,
    1775       11211 :     ctx,
    1776       11211 :     input->vm_ctx.heap_max,
    1777       11211 :     ctx->txn_ctx->compute_meter,
    1778       11211 :     rodata,
    1779       11211 :     rodata_sz,
    1780       11211 :     NULL, // TODO
    1781       11211 :     0, // TODO
    1782       11211 :     0, // TODO
    1783       11211 :     0, // TODO, text_sz
    1784       11211 :     0, // TODO
    1785       11211 :     NULL, // TODO
    1786       11211 :     syscalls,
    1787       11211 :     NULL, // TODO
    1788       11211 :     sha,
    1789       11211 :     input_regions,
    1790       11211 :     input_regions_count,
    1791       11211 :     NULL,
    1792       11211 :     is_deprecated,
    1793       11211 :     FD_FEATURE_ACTIVE( ctx->slot_ctx, bpf_account_data_direct_mapping ) );
    1794             : 
    1795             :   // Setup the vm state for execution
    1796       11211 :   if( fd_vm_setup_state_for_execution( vm ) != FD_VM_SUCCESS ) {
    1797           0 :     goto error;
    1798           0 :   }
    1799             : 
    1800             :   // Override some execution state values from the syscall fuzzer input
    1801             :   // This is so we can test if the syscall mutates any of these erroneously
    1802       11211 :   vm->reg[0] = input->vm_ctx.r0;
    1803       11211 :   vm->reg[1] = input->vm_ctx.r1;
    1804       11211 :   vm->reg[2] = input->vm_ctx.r2;
    1805       11211 :   vm->reg[3] = input->vm_ctx.r3;
    1806       11211 :   vm->reg[4] = input->vm_ctx.r4;
    1807       11211 :   vm->reg[5] = input->vm_ctx.r5;
    1808       11211 :   vm->reg[6] = input->vm_ctx.r6;
    1809       11211 :   vm->reg[7] = input->vm_ctx.r7;
    1810       11211 :   vm->reg[8] = input->vm_ctx.r8;
    1811       11211 :   vm->reg[9] = input->vm_ctx.r9;
    1812       11211 :   vm->reg[10] = input->vm_ctx.r10;
    1813       11211 :   vm->reg[11] = input->vm_ctx.r11;
    1814             : 
    1815             :   // Override initial part of the heap, if specified the syscall fuzzer input
    1816       11211 :   if( input->syscall_invocation.heap_prefix ) {
    1817       11169 :     fd_memcpy( vm->heap, input->syscall_invocation.heap_prefix->bytes,
    1818       11169 :                fd_ulong_min(input->syscall_invocation.heap_prefix->size, vm->heap_max) );
    1819       11169 :   }
    1820             : 
    1821             :   // Override initial part of the stack, if specified the syscall fuzzer input
    1822       11211 :   if( input->syscall_invocation.stack_prefix ) {
    1823         714 :     fd_memcpy( vm->stack, input->syscall_invocation.stack_prefix->bytes,
    1824         714 :                fd_ulong_min(input->syscall_invocation.stack_prefix->size, FD_VM_STACK_MAX) );
    1825         714 :   }
    1826             : 
    1827             :   // Propogate the acc_regions_meta to the vm
    1828       11211 :   vm->acc_region_metas = fd_valloc_malloc( valloc, alignof(fd_vm_acc_region_meta_t), sizeof(fd_vm_acc_region_meta_t) * input->vm_ctx.input_data_regions_count );
    1829       11211 :   setup_vm_acc_region_metas( vm->acc_region_metas, vm, vm->instr_ctx );
    1830             : 
    1831             :   // Look up the syscall to execute
    1832       11211 :   char * syscall_name = (char *)input->syscall_invocation.function_name.bytes;
    1833       11211 :   fd_sbpf_syscalls_t const * syscall = lookup_syscall_func(syscalls, syscall_name, input->syscall_invocation.function_name.size);
    1834       11211 :   if( !syscall ) {
    1835           0 :     goto error;
    1836           0 :   }
    1837             : 
    1838             :   /* Actually invoke the syscall */
    1839       11211 :   int stack_push_err = fd_instr_stack_push( ctx->txn_ctx, (fd_instr_info_t *)ctx->instr );
    1840       11211 :   if( FD_UNLIKELY( stack_push_err ) ) {
    1841           0 :       FD_LOG_WARNING(( "instr stack push err" ));
    1842           0 :       goto error;
    1843           0 :   }
    1844       11211 :   int syscall_err = syscall->func( vm, vm->reg[1], vm->reg[2], vm->reg[3], vm->reg[4], vm->reg[5], &vm->reg[0] );
    1845       11211 :   int stack_pop_err = fd_instr_stack_pop( ctx->txn_ctx, ctx->instr );
    1846       11211 :   if( FD_UNLIKELY( stack_pop_err ) ) {
    1847           0 :       FD_LOG_WARNING(( "instr stack pop err" ));
    1848           0 :       goto error;
    1849           0 :   }
    1850       11211 :   if( syscall_err ) {
    1851             :     /*  In the CPI syscall, certain checks are performed out of order between Firedancer and Agave's
    1852             :         implementation. Certain checks in FD (whose error codes mapped below)
    1853             :         do not have a (sequentially) equivalent one in Agave. Thus, it doesn't make sense
    1854             :         to declare a mismatch if Firedancer fails such a check when Agave doesn't, as long
    1855             :         as both end up error'ing out at some point. We also have other metrics (namely CU count)
    1856             :         to rely on. */
    1857             : 
    1858             :     /*  Certain pre-flight checks are not performed in Agave. These manifest as
    1859             :         access violations in Agave. The agave_access_violation_mask bitset sets
    1860             :         the error codes that are expected to be access violations in Agave. */
    1861        1137 :     if( is_cpi &&
    1862        1137 :       ( syscall_err == FD_VM_ERR_SYSCALL_TOO_MANY_SIGNERS ||
    1863          18 :         syscall_err == FD_VM_ERR_SYSCALL_INSTRUCTION_TOO_LARGE ||
    1864          18 :         syscall_err == FD_VM_ERR_SYSCALL_MAX_INSTRUCTION_ACCOUNTS_EXCEEDED ||
    1865          18 :         syscall_err == FD_VM_ERR_SYSCALL_MAX_INSTRUCTION_ACCOUNT_INFOS_EXCEEDED ) ) {
    1866             : 
    1867             :       /* FD performs pre-flight checks that manifest as access violations in Agave */
    1868           0 :       vm->instr_ctx->txn_ctx->exec_err      = FD_VM_ERR_EBPF_ACCESS_VIOLATION;
    1869           0 :       vm->instr_ctx->txn_ctx->exec_err_kind = FD_EXECUTOR_ERR_KIND_EBPF;
    1870           0 :     }
    1871             : 
    1872        1137 :     fd_log_collector_program_failure( vm->instr_ctx );
    1873        1137 :   }
    1874             : 
    1875             :   /* Capture the effects */
    1876       11211 :   int exec_err = vm->instr_ctx->txn_ctx->exec_err;
    1877       11211 :   effects->error = 0;
    1878       11211 :   if( syscall_err ) {
    1879        1137 :     if( exec_err==0 ) {
    1880           0 :       FD_LOG_WARNING(( "TODO: syscall returns error, but exec_err not set. this is probably missing a log." ));
    1881           0 :       effects->error = -1;
    1882        1137 :     } else {
    1883        1137 :       effects->error = (exec_err <= 0) ? -exec_err : -1;
    1884             : 
    1885             :       /* Map error kind, equivalent to:
    1886             :           effects->error_kind = (fd_exec_test_err_kind_t)(vm->instr_ctx->txn_ctx->exec_err_kind + 1); */
    1887        1137 :       switch (vm->instr_ctx->txn_ctx->exec_err_kind) {
    1888         774 :         case FD_EXECUTOR_ERR_KIND_EBPF:
    1889         774 :           effects->error_kind = FD_EXEC_TEST_ERR_KIND_EBPF;
    1890         774 :           break;
    1891         183 :         case FD_EXECUTOR_ERR_KIND_SYSCALL:
    1892         183 :           effects->error_kind = FD_EXEC_TEST_ERR_KIND_SYSCALL;
    1893         183 :           break;
    1894         180 :         case FD_EXECUTOR_ERR_KIND_INSTR:
    1895         180 :           effects->error_kind = FD_EXEC_TEST_ERR_KIND_INSTRUCTION;
    1896         180 :           break;
    1897           0 :         default:
    1898           0 :           effects->error_kind = FD_EXEC_TEST_ERR_KIND_UNSPECIFIED;
    1899           0 :           break;
    1900        1137 :       }
    1901        1137 :     }
    1902        1137 :   }
    1903       11211 :   effects->r0 = syscall_err ? 0 : vm->reg[0]; // Save only on success
    1904       11211 :   effects->cu_avail = (ulong)vm->cu;
    1905             : 
    1906       11211 :   if( vm->heap_max ) {
    1907       11169 :     effects->heap = FD_SCRATCH_ALLOC_APPEND(
    1908       11169 :       l, alignof(uint), PB_BYTES_ARRAY_T_ALLOCSIZE( vm->heap_max ) );
    1909       11169 :     if( FD_UNLIKELY( _l > output_end ) ) {
    1910           0 :       goto error;
    1911           0 :     }
    1912       11169 :     effects->heap->size = (uint)vm->heap_max;
    1913       11169 :     fd_memcpy( effects->heap->bytes, vm->heap, vm->heap_max );
    1914       11169 :   } else {
    1915          42 :     effects->heap = NULL;
    1916          42 :   }
    1917             : 
    1918       11211 :   effects->stack = FD_SCRATCH_ALLOC_APPEND(
    1919       11211 :     l, alignof(uint), PB_BYTES_ARRAY_T_ALLOCSIZE( FD_VM_STACK_MAX ) );
    1920       11211 :     if( FD_UNLIKELY( _l > output_end ) ) {
    1921           0 :       goto error;
    1922           0 :     }
    1923       11211 :   effects->stack->size = (uint)FD_VM_STACK_MAX;
    1924       11211 :   fd_memcpy( effects->stack->bytes, vm->stack, FD_VM_STACK_MAX );
    1925             : 
    1926       11211 :   if( vm->rodata_sz ) {
    1927         714 :     effects->rodata = FD_SCRATCH_ALLOC_APPEND(
    1928         714 :       l, alignof(uint), PB_BYTES_ARRAY_T_ALLOCSIZE( rodata_sz ) );
    1929         714 :     if( FD_UNLIKELY( _l > output_end ) ) {
    1930           0 :       goto error;
    1931           0 :     }
    1932         714 :     effects->rodata->size = (uint)rodata_sz;
    1933         714 :     fd_memcpy( effects->rodata->bytes, vm->rodata, rodata_sz );
    1934       10497 :   } else {
    1935       10497 :     effects->rodata = NULL;
    1936       10497 :   }
    1937             : 
    1938       11211 :   effects->frame_count = vm->frame_cnt;
    1939             : 
    1940       11211 :   fd_log_collector_t * log = &vm->instr_ctx->txn_ctx->log_collector;
    1941             :   /* Only collect log on valid errors (i.e., != -1). Follows
    1942             :      https://github.com/firedancer-io/solfuzz-agave/blob/99758d3c4f3a342d56e2906936458d82326ae9a8/src/utils/err_map.rs#L148 */
    1943       11211 :   if( effects->error != -1 && log->buf_sz ) {
    1944        1206 :     effects->log = FD_SCRATCH_ALLOC_APPEND(
    1945        1206 :       l, alignof(uchar), PB_BYTES_ARRAY_T_ALLOCSIZE( log->buf_sz ) );
    1946        1206 :     if( FD_UNLIKELY( _l > output_end ) ) {
    1947           0 :       goto error;
    1948           0 :     }
    1949        1206 :     effects->log->size = (uint)fd_log_collector_debug_sprintf( log, (char *)effects->log->bytes, 0 );
    1950       10005 :   } else {
    1951       10005 :     effects->log = NULL;
    1952       10005 :   }
    1953             : 
    1954             :   /* Capture input regions */
    1955       11211 :   effects->inputdata = NULL; /* Deprecated, using input_data_regions instead */
    1956       11211 :   ulong tmp_end = FD_SCRATCH_ALLOC_FINI( l, 1UL );
    1957       11211 :   ulong input_regions_size = load_from_vm_input_regions( vm->input_mem_regions,
    1958       11211 :                                                         vm->input_mem_regions_cnt,
    1959       11211 :                                                         &effects->input_data_regions,
    1960       11211 :                                                         &effects->input_data_regions_count,
    1961       11211 :                                                         (void *)tmp_end,
    1962       11211 :                                                         fd_ulong_sat_sub( output_end, tmp_end ) );
    1963             : 
    1964       11211 :   if( !!vm->input_mem_regions_cnt && !effects->input_data_regions ) {
    1965           0 :     goto error;
    1966           0 :   }
    1967             : 
    1968             :   /* Return the effects */
    1969       11211 :   ulong actual_end = tmp_end + input_regions_size;
    1970       11211 :   fd_exec_test_instr_context_destroy( runner, ctx, wksp, alloc );
    1971       11211 :   cpi_exec_effects = NULL;
    1972             : 
    1973       11211 :   *output = effects;
    1974       11211 :   return actual_end - (ulong)output_buf;
    1975             : 
    1976           0 : error:
    1977           0 :   fd_exec_test_instr_context_destroy( runner, ctx, wksp, alloc );
    1978           0 :   cpi_exec_effects = NULL;
    1979           0 :   return 0;
    1980       11211 : }
    1981             : 
    1982             : /* Stubs fd_execute_instr for binaries compiled with
    1983             :    `-Xlinker --wrap=fd_execute_instr` */
    1984             : int
    1985             : __wrap_fd_execute_instr( fd_exec_txn_ctx_t * txn_ctx,
    1986             :                          fd_instr_info_t *   instr_info )
    1987           0 : {
    1988           0 :     static const pb_byte_t zero_blk[32] = {0};
    1989             : 
    1990           0 :     if( cpi_exec_effects == NULL ) {
    1991           0 :       FD_LOG_WARNING(( "fd_execute_instr is disabled" ));
    1992           0 :       return FD_EXECUTOR_INSTR_SUCCESS;
    1993           0 :     }
    1994             : 
    1995             :     // Iterate through instruction accounts
    1996           0 :     for( ushort i = 0UL; i < instr_info->acct_cnt; ++i ) {
    1997           0 :       uchar idx_in_txn = instr_info->acct_txn_idxs[i];
    1998           0 :       fd_pubkey_t * acct_pubkey = &instr_info->acct_pubkeys[i];
    1999             : 
    2000           0 :       fd_borrowed_account_t * acct = NULL;
    2001             :       /* Find (first) account in cpi_exec_effects->modified_accounts that matches pubkey */
    2002           0 :       for( uint j = 0UL; j < cpi_exec_effects->modified_accounts_count; ++j ) {
    2003           0 :         fd_exec_test_acct_state_t * acct_state = &cpi_exec_effects->modified_accounts[j];
    2004           0 :         if( memcmp( acct_state->address, acct_pubkey, sizeof(fd_pubkey_t) ) != 0 ) continue;
    2005             : 
    2006             :         /* Fetch borrowed account */
    2007             :         /* First check if account is read-only.
    2008             :            TODO: Once direct mapping is enabled we _technically_ don't need
    2009             :                  this check */
    2010             : 
    2011           0 :         if( fd_txn_borrowed_account_view_idx( txn_ctx, idx_in_txn, &acct ) ) {
    2012           0 :           break;
    2013           0 :         }
    2014           0 :         if( acct->meta == NULL ){
    2015           0 :           break;
    2016           0 :         }
    2017             : 
    2018             :         /* Now borrow mutably (with resize) */
    2019           0 :         int err = fd_txn_borrowed_account_modify_idx( txn_ctx,
    2020           0 :                                                       idx_in_txn,
    2021             :                                                       /* Do not reallocate if data is not going to be modified */
    2022           0 :                                                       acct_state->data ? acct_state->data->size : 0UL,
    2023           0 :                                                       &acct );
    2024           0 :         if( err ) break;
    2025             : 
    2026             :         /* Update account state */
    2027           0 :         acct->meta->info.lamports = acct_state->lamports;
    2028           0 :         acct->meta->info.executable = acct_state->executable;
    2029           0 :         acct->meta->info.rent_epoch = acct_state->rent_epoch;
    2030             : 
    2031             :         /* TODO: use lower level API (i.e., fd_borrowed_account_resize) to avoid memcpy here */
    2032           0 :         if( acct_state->data ){
    2033           0 :           fd_memcpy( acct->data, acct_state->data->bytes, acct_state->data->size );
    2034           0 :           acct->meta->dlen = acct_state->data->size;
    2035           0 :         }
    2036             : 
    2037             :         /* Follow solfuzz-agave, which skips if pubkey is malformed */
    2038           0 :         if( memcmp( acct_state->owner, zero_blk, sizeof(fd_pubkey_t) ) != 0 ) {
    2039           0 :           fd_memcpy( acct->meta->info.owner, acct_state->owner, sizeof(fd_pubkey_t) );
    2040           0 :         }
    2041             : 
    2042           0 :         break;
    2043           0 :       }
    2044           0 :     }
    2045           0 :     return FD_EXECUTOR_INSTR_SUCCESS;
    2046           0 : }

Generated by: LCOV version 1.14