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: 943 1129 83.5 %
Date: 2025-01-08 12:08:44 Functions: 20 21 95.2 %

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

Generated by: LCOV version 1.14