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: 0 1543 0.0 %
Date: 2025-03-20 12:08:36 Functions: 0 23 0.0 %

          Line data    Source code
       1             : 
       2             : #include "generated/invoke.pb.h"
       3             : #undef FD_SPAD_USE_HANDHOLDING
       4             : #define FD_SPAD_USE_HANDHOLDING 1
       5             : #include "fd_exec_instr_test.h"
       6             : #include "../fd_acc_mgr.h"
       7             : #include "../fd_executor.h"
       8             : #include "../fd_hashes.h"
       9             : #include "../fd_runtime.h"
      10             : #include "../program/fd_bpf_loader_serialization.h"
      11             : #include "../../stakes/fd_stakes.h"
      12             : #include "../../rewards/fd_rewards.h"
      13             : #include "../program/fd_bpf_loader_program.h"
      14             : #include "../program/fd_vote_program.h"
      15             : #include "../program/fd_stake_program.h"
      16             : #include "../program/fd_bpf_program_util.h"
      17             : #include "../program/fd_builtin_programs.h"
      18             : #include "../context/fd_exec_epoch_ctx.h"
      19             : #include "../context/fd_exec_slot_ctx.h"
      20             : #include "../context/fd_exec_txn_ctx.h"
      21             : #include "../sysvar/fd_sysvar_recent_hashes.h"
      22             : #include "../sysvar/fd_sysvar_last_restart_slot.h"
      23             : #include "../sysvar/fd_sysvar_slot_hashes.h"
      24             : #include "../sysvar/fd_sysvar_stake_history.h"
      25             : #include "../sysvar/fd_sysvar_epoch_rewards.h"
      26             : #include "../../../funk/fd_funk.h"
      27             : #include "../../../util/bits/fd_float.h"
      28             : #include "../../../ballet/sbpf/fd_sbpf_loader.h"
      29             : #include "../../vm/fd_vm.h"
      30             : #include <assert.h>
      31             : #include "../sysvar/fd_sysvar_cache.h"
      32             : #include "../sysvar/fd_sysvar_epoch_schedule.h"
      33             : #include "../sysvar/fd_sysvar_clock.h"
      34             : #include "../../../disco/pack/fd_pack.h"
      35             : #include "fd_vm_test.h"
      36             : #include "../../vm/test_vm_util.h"
      37             : 
      38             : /* LOGFMT_REPORT is the log prefix for instruction processing tests */
      39             : 
      40             : #define LOGFMT_REPORT "%s"
      41             : static FD_TL char _report_prefix[100] = {0};
      42             : 
      43             : #define REPORTV( level, fmt, ... ) \
      44           0 :   FD_LOG_##level(( LOGFMT_REPORT fmt, _report_prefix, __VA_ARGS__ ))
      45             : 
      46             : #define REPORT( level, fmt ) \
      47           0 :   FD_LOG_##level(( LOGFMT_REPORT fmt, _report_prefix ))
      48             : 
      49             : #define REPORT_ACCTV( level, addr, fmt, ... )                                  \
      50             :   do {                                                                         \
      51             :     char         _acct_log_private_addr[ FD_BASE58_ENCODED_32_SZ ];            \
      52             :     void const * _acct_log_private_addr_ptr = (addr);                          \
      53             :     fd_acct_addr_cstr( _acct_log_private_addr, _acct_log_private_addr_ptr );        \
      54             :     REPORTV( level, " account %s: " fmt, _acct_log_private_addr, __VA_ARGS__ ); \
      55             :   } while(0);
      56             : 
      57             : #define REPORT_ACCT( level, addr, fmt ) REPORT_ACCTV( level, addr, fmt, 0 )
      58             : 
      59             : /* Macros to append data to construct a serialized transaction
      60             :    without exceeding bounds */
      61           0 : #define FD_CHECKED_ADD_TO_TXN_DATA( _begin, _cur_data, _to_add, _sz ) __extension__({ \
      62           0 :   if( FD_UNLIKELY( (*_cur_data)+_sz>_begin+FD_TXN_MTU ) ) return ULONG_MAX;           \
      63           0 :   fd_memcpy( *_cur_data, _to_add, _sz );                                              \
      64           0 :   *_cur_data += _sz;                                                                  \
      65           0 : })
      66             : 
      67           0 : #define FD_CHECKED_ADD_CU16_TO_TXN_DATA( _begin, _cur_data, _to_add ) __extension__({ \
      68           0 :   do {                                                                                \
      69           0 :     uchar _buf[3];                                                                    \
      70           0 :     fd_bincode_encode_ctx_t _encode_ctx = { .data = _buf, .dataend = _buf+3 };        \
      71           0 :     fd_bincode_compact_u16_encode( &_to_add, &_encode_ctx );                          \
      72           0 :     ulong _sz = (ulong) ((uchar *)_encode_ctx.data - _buf );                          \
      73           0 :     FD_CHECKED_ADD_TO_TXN_DATA( _begin, _cur_data, _buf, _sz );                       \
      74           0 :   } while(0);                                                                         \
      75           0 : })
      76             : 
      77             : /* Define routine to sort accounts to support query-by-pubkey via
      78             :    binary search. */
      79             : 
      80             : #define SORT_NAME sort_pubkey_p
      81             : #define SORT_KEY_T void const *
      82             : #define SORT_BEFORE(a,b) ( memcmp( (a), (b), sizeof(fd_pubkey_t) )<0 )
      83             : #include "../../../util/tmpl/fd_sort.c"
      84             : #include "../../vm/fd_vm_base.h"
      85             : 
      86             : ulong
      87           0 : fd_exec_instr_test_runner_align( void ) {
      88           0 :   return alignof(fd_exec_instr_test_runner_t);
      89           0 : }
      90             : 
      91             : ulong
      92           0 : fd_exec_instr_test_runner_footprint( void ) {
      93           0 :   ulong l = FD_LAYOUT_INIT;
      94           0 :   l = FD_LAYOUT_APPEND( l, fd_exec_instr_test_runner_align(), sizeof(fd_exec_instr_test_runner_t) );
      95           0 :   l = FD_LAYOUT_APPEND( l, fd_funk_align(),                   fd_funk_footprint()                 );
      96           0 :   return FD_LAYOUT_FINI( l, fd_exec_instr_test_runner_align() );
      97           0 : }
      98             : 
      99             : fd_exec_instr_test_runner_t *
     100             : fd_exec_instr_test_runner_new( void * mem,
     101             :                                void * spad_mem,
     102           0 :                                ulong  wksp_tag ) {
     103           0 :   FD_SCRATCH_ALLOC_INIT( l, mem );
     104           0 :   void * runner_mem = FD_SCRATCH_ALLOC_APPEND( l, fd_exec_instr_test_runner_align(), sizeof(fd_exec_instr_test_runner_t) );
     105           0 :   void * funk_mem   = FD_SCRATCH_ALLOC_APPEND( l, fd_funk_align(),                   fd_funk_footprint()                 );
     106           0 :   FD_SCRATCH_ALLOC_FINI( l, fd_exec_instr_test_runner_align() );
     107             : 
     108           0 :   ulong txn_max = 4+fd_tile_cnt();
     109           0 :   ulong rec_max = 1024UL;
     110           0 :   fd_funk_t * funk = fd_funk_join( fd_funk_new( funk_mem, wksp_tag, (ulong)fd_tickcount(), txn_max, rec_max ) );
     111           0 :   if( FD_UNLIKELY( !funk ) ) {
     112           0 :     FD_LOG_WARNING(( "fd_funk_new() failed" ));
     113           0 :     return NULL;
     114           0 :   }
     115             : 
     116           0 :   fd_exec_instr_test_runner_t * runner = runner_mem;
     117           0 :   runner->funk = funk;
     118             : 
     119             :   /* Create spad */
     120           0 :   runner->spad = fd_spad_join( fd_spad_new( spad_mem, FD_RUNTIME_TRANSACTION_EXECUTION_FOOTPRINT_FUZZ ) );
     121           0 :   return runner;
     122           0 : }
     123             : 
     124             : void *
     125           0 : fd_exec_instr_test_runner_delete( fd_exec_instr_test_runner_t * runner ) {
     126           0 :   if( FD_UNLIKELY( !runner ) ) return NULL;
     127           0 :   fd_funk_delete( fd_funk_leave( runner->funk ) );
     128           0 :   runner->funk = NULL;
     129           0 :   if( FD_UNLIKELY( fd_spad_verify( runner->spad ) ) ) {
     130           0 :     FD_LOG_ERR(( "fd_spad_verify() failed" ));
     131           0 :   }
     132           0 :   if( FD_UNLIKELY( fd_spad_frame_used( runner->spad )!=0 ) ) {
     133           0 :     FD_LOG_ERR(( "stray spad frame frame_used=%lu", fd_spad_frame_used( runner->spad ) ));
     134           0 :   }
     135           0 :   runner->spad = NULL;
     136           0 :   return runner;
     137           0 : }
     138             : 
     139             : fd_spad_t *
     140           0 : fd_exec_instr_test_runner_get_spad( fd_exec_instr_test_runner_t * runner ) {
     141           0 :   return runner->spad;
     142           0 : }
     143             : 
     144             : static int
     145           0 : fd_double_is_normal( double dbl ) {
     146           0 :   ulong x = fd_dblbits( dbl );
     147           0 :   int is_denorm =
     148           0 :     ( fd_dblbits_bexp( x ) == 0 ) &
     149           0 :     ( fd_dblbits_mant( x ) != 0 );
     150           0 :   int is_inf =
     151           0 :     ( fd_dblbits_bexp( x ) == 2047 ) &
     152           0 :     ( fd_dblbits_mant( x ) ==    0 );
     153           0 :   int is_nan =
     154           0 :     ( fd_dblbits_bexp( x ) == 2047 ) &
     155           0 :     ( fd_dblbits_mant( x ) !=    0 );
     156           0 :   return !( is_denorm | is_inf | is_nan );
     157           0 : }
     158             : 
     159             : static int
     160             : _load_account( fd_txn_account_t *                acc,
     161             :                fd_acc_mgr_t *                    acc_mgr,
     162             :                fd_funk_txn_t *                   funk_txn,
     163             :                fd_exec_test_acct_state_t const * state,
     164           0 :                uchar                             override_acct_state ) {
     165           0 :   fd_txn_account_init( acc );
     166           0 :   ulong size = 0UL;
     167           0 :   if( state->data ) size = state->data->size;
     168             : 
     169           0 :   fd_pubkey_t pubkey[1];  memcpy( pubkey, state->address, sizeof(fd_pubkey_t) );
     170             : 
     171             :   /* Account must not yet exist */
     172           0 :   if( FD_UNLIKELY( !override_acct_state && fd_acc_mgr_view_raw( acc_mgr, funk_txn, pubkey, NULL, NULL, NULL) ) ) {
     173           0 :     return 0;
     174           0 :   }
     175             : 
     176           0 :   assert( acc_mgr->funk );
     177           0 :   assert( acc_mgr->funk->magic == FD_FUNK_MAGIC );
     178           0 :   fd_funk_start_write( acc_mgr->funk );
     179           0 :   int err = fd_acc_mgr_modify( /* acc_mgr     */ acc_mgr,
     180           0 :                                /* txn         */ funk_txn,
     181           0 :                                /* pubkey      */ pubkey,
     182           0 :                                /* do_create   */ 1,
     183           0 :                                /* min_data_sz */ size,
     184           0 :                                acc );
     185           0 :   assert( err==FD_ACC_MGR_SUCCESS );
     186           0 :   if( state->data ) fd_memcpy( acc->data, state->data->bytes, size );
     187             : 
     188           0 :   acc->starting_lamports     = state->lamports;
     189           0 :   acc->starting_dlen         = size;
     190           0 :   acc->meta->info.lamports   = state->lamports;
     191           0 :   acc->meta->info.executable = state->executable;
     192           0 :   acc->meta->info.rent_epoch = state->rent_epoch;
     193           0 :   acc->meta->dlen            = size;
     194           0 :   memcpy( acc->meta->info.owner, state->owner, sizeof(fd_pubkey_t) );
     195             : 
     196             :   /* make the account read-only by default */
     197           0 :   acc->meta = NULL;
     198           0 :   acc->data = NULL;
     199           0 :   acc->rec  = NULL;
     200           0 :   fd_funk_end_write( acc_mgr->funk );
     201             : 
     202           0 :   return 1;
     203           0 : }
     204             : 
     205             : static int
     206             : _load_txn_account( fd_txn_account_t *                acc,
     207             :                    fd_acc_mgr_t *                    acc_mgr,
     208             :                    fd_funk_txn_t *                   funk_txn,
     209             :                    fd_exec_test_acct_state_t const * state,
     210           0 :                    uchar                             override_acct_state ) {
     211             :   // In the Agave transaction fuzzing harness, accounts with 0 lamports are not saved in the accounts db.
     212             :   // When they are fetched for transactions, the fields of the account are 0-set.
     213           0 :   fd_exec_test_acct_state_t account_state_to_save = FD_EXEC_TEST_ACCT_STATE_INIT_ZERO;
     214           0 :   memcpy( account_state_to_save.address, state->address, sizeof(fd_pubkey_t) );
     215             : 
     216             :   // Restore the account state if it has lamports
     217           0 :   if( state->lamports ) {
     218           0 :     account_state_to_save = *state;
     219           0 :   }
     220             : 
     221           0 :   return _load_account( acc, acc_mgr, funk_txn, &account_state_to_save, override_acct_state );
     222           0 : }
     223             : 
     224             : static int
     225             : _restore_feature_flags( fd_exec_epoch_ctx_t *              epoch_ctx,
     226           0 :                         fd_exec_test_feature_set_t const * feature_set ) {
     227           0 :   fd_features_disable_all( &epoch_ctx->features );
     228           0 :   for( ulong j=0UL; j < feature_set->features_count; j++ ) {
     229           0 :     ulong                   prefix = feature_set->features[j];
     230           0 :     fd_feature_id_t const * id     = fd_feature_id_query( prefix );
     231           0 :     if( FD_UNLIKELY( !id ) ) {
     232           0 :       FD_LOG_WARNING(( "unsupported feature ID 0x%016lx", prefix ));
     233           0 :       return 0;
     234           0 :     }
     235             :     /* Enabled since genesis */
     236           0 :     fd_features_set( &epoch_ctx->features, id, 0UL );
     237           0 :   }
     238           0 :   return 1;
     239           0 : }
     240             : 
     241             : /* Serializes a Protobuf SanitizedTransaction and returns the number of bytes consumed.
     242             :    Returns ULONG_MAX if the number of bytes read exceeds 1232 (FD_TXN_MTU).
     243             :    _txn_raw_begin is assumed to be a pre-allocated buffer of at least 1232 bytes. */
     244             : ulong
     245             : _serialize_txn( uchar * txn_raw_begin,
     246             :                 const fd_exec_test_sanitized_transaction_t * tx,
     247             :                 ushort * out_instr_cnt,
     248           0 :                 ushort * out_addr_table_cnt ) {
     249           0 :   const uchar empty_bytes[64] = { 0 };
     250           0 :   uchar * txn_raw_cur_ptr = txn_raw_begin;
     251             : 
     252             :   /* Compact array of signatures (https://solana.com/docs/core/transactions#transaction)
     253             :      Note that although documentation interchangably refers to the signature cnt as a compact-u16
     254             :      and a u8, the max signature cnt is capped at 48 (due to txn size limits), so u8 and compact-u16
     255             :      is represented the same way anyways and can be parsed identically. */
     256             :   // Note: always create a valid txn with 1+ signatures, add an empty signature if none is provided
     257           0 :   uchar signature_cnt = fd_uchar_max( 1, (uchar) tx->signatures_count );
     258           0 :   FD_CHECKED_ADD_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, &signature_cnt, sizeof(uchar) );
     259           0 :   for( uchar i = 0; i < signature_cnt; ++i ) {
     260           0 :     FD_CHECKED_ADD_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, tx->signatures && tx->signatures[i] ? tx->signatures[i]->bytes : empty_bytes, FD_TXN_SIGNATURE_SZ );
     261           0 :   }
     262             : 
     263             :   /* Message */
     264             :   /* For v0 transactions, the highest bit of the num_required_signatures is set, and an extra byte is used for the version.
     265             :      https://solanacookbook.com/guides/versioned-transactions.html#versioned-transactions-transactionv0
     266             : 
     267             :      We will always create a transaction with at least 1 signature, and cap the signature count to 127 to avoid
     268             :      collisions with the header_b0 tag. */
     269           0 :   uchar num_required_signatures = fd_uchar_max( 1, fd_uchar_min( 127, (uchar) tx->message.header.num_required_signatures ) );
     270           0 :   if( !tx->message.is_legacy ) {
     271           0 :     uchar header_b0 = (uchar) 0x80UL;
     272           0 :     FD_CHECKED_ADD_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, &header_b0, sizeof(uchar) );
     273           0 :   }
     274             : 
     275             :   /* Header (3 bytes) (https://solana.com/docs/core/transactions#message-header) */
     276           0 :   FD_CHECKED_ADD_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, &num_required_signatures, sizeof(uchar) );
     277           0 :   FD_CHECKED_ADD_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, &tx->message.header.num_readonly_signed_accounts, sizeof(uchar) );
     278           0 :   FD_CHECKED_ADD_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, &tx->message.header.num_readonly_unsigned_accounts, sizeof(uchar) );
     279             : 
     280             :   /* Compact array of account addresses (https://solana.com/docs/core/transactions#compact-array-format) */
     281             :   // Array length is a compact u16
     282           0 :   ushort num_acct_keys = (ushort) tx->message.account_keys_count;
     283           0 :   FD_CHECKED_ADD_CU16_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, num_acct_keys );
     284           0 :   for( ushort i = 0; i < num_acct_keys; ++i ) {
     285           0 :     FD_CHECKED_ADD_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, tx->message.account_keys[i]->bytes, sizeof(fd_pubkey_t) );
     286           0 :   }
     287             : 
     288             :   /* Recent blockhash (32 bytes) (https://solana.com/docs/core/transactions#recent-blockhash) */
     289             :   // Note: add an empty blockhash if none is provided
     290           0 :   FD_CHECKED_ADD_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, tx->message.recent_blockhash ? tx->message.recent_blockhash->bytes : empty_bytes, sizeof(fd_hash_t) );
     291             : 
     292             :   /* Compact array of instructions (https://solana.com/docs/core/transactions#array-of-instructions) */
     293             :   // Instruction count is a compact u16
     294           0 :   ushort instr_count = (ushort) tx->message.instructions_count;
     295           0 :   FD_CHECKED_ADD_CU16_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, instr_count );
     296           0 :   for( ushort i = 0; i < instr_count; ++i ) {
     297             :     // Program ID index
     298           0 :     uchar program_id_index = (uchar) tx->message.instructions[i].program_id_index;
     299           0 :     FD_CHECKED_ADD_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, &program_id_index, sizeof(uchar) );
     300             : 
     301             :     // Compact array of account addresses
     302           0 :     ushort acct_count = (ushort) tx->message.instructions[i].accounts_count;
     303           0 :     FD_CHECKED_ADD_CU16_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, acct_count );
     304           0 :     for( ushort j = 0; j < acct_count; ++j ) {
     305           0 :       uchar account_index = (uchar) tx->message.instructions[i].accounts[j];
     306           0 :       FD_CHECKED_ADD_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, &account_index, sizeof(uchar) );
     307           0 :     }
     308             : 
     309             :     // Compact array of 8-bit data
     310           0 :     pb_bytes_array_t * data = tx->message.instructions[i].data;
     311           0 :     ushort data_len;
     312           0 :     if( data ) {
     313           0 :       data_len = (ushort) data->size;
     314           0 :       FD_CHECKED_ADD_CU16_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, data_len );
     315           0 :       FD_CHECKED_ADD_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, data->bytes, data_len );
     316           0 :     } else {
     317           0 :       data_len = 0;
     318           0 :       FD_CHECKED_ADD_CU16_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, data_len );
     319           0 :     }
     320           0 :   }
     321             : 
     322             :   /* Address table lookups (N/A for legacy transactions) */
     323           0 :   ushort addr_table_cnt = 0;
     324           0 :   if( !tx->message.is_legacy ) {
     325             :     /* Compact array of address table lookups (https://solanacookbook.com/guides/versioned-transactions.html#compact-array-of-address-table-lookups) */
     326             :     // NOTE: The diagram is slightly wrong - the account key is a 32 byte pubkey, not a u8
     327           0 :     addr_table_cnt = (ushort) tx->message.address_table_lookups_count;
     328           0 :     FD_CHECKED_ADD_CU16_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, addr_table_cnt );
     329           0 :     for( ushort i = 0; i < addr_table_cnt; ++i ) {
     330             :       // Account key
     331           0 :       FD_CHECKED_ADD_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, tx->message.address_table_lookups[i].account_key, sizeof(fd_pubkey_t) );
     332             : 
     333             :       // Compact array of writable indexes
     334           0 :       ushort writable_count = (ushort) tx->message.address_table_lookups[i].writable_indexes_count;
     335           0 :       FD_CHECKED_ADD_CU16_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, writable_count );
     336           0 :       for( ushort j = 0; j < writable_count; ++j ) {
     337           0 :         uchar writable_index = (uchar) tx->message.address_table_lookups[i].writable_indexes[j];
     338           0 :         FD_CHECKED_ADD_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, &writable_index, sizeof(uchar) );
     339           0 :       }
     340             : 
     341             :       // Compact array of readonly indexes
     342           0 :       ushort readonly_count = (ushort) tx->message.address_table_lookups[i].readonly_indexes_count;
     343           0 :       FD_CHECKED_ADD_CU16_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, readonly_count );
     344           0 :       for( ushort j = 0; j < readonly_count; ++j ) {
     345           0 :         uchar readonly_index = (uchar) tx->message.address_table_lookups[i].readonly_indexes[j];
     346           0 :         FD_CHECKED_ADD_TO_TXN_DATA( txn_raw_begin, &txn_raw_cur_ptr, &readonly_index, sizeof(uchar) );
     347           0 :       }
     348           0 :     }
     349           0 :   }
     350           0 :   *out_instr_cnt = instr_count;
     351           0 :   *out_addr_table_cnt = addr_table_cnt;
     352           0 :   return (ulong)(txn_raw_cur_ptr - txn_raw_begin);
     353           0 : }
     354             : 
     355             : 
     356             : int
     357             : fd_exec_test_instr_context_create( fd_exec_instr_test_runner_t *        runner,
     358             :                                    fd_exec_instr_ctx_t *                ctx,
     359             :                                    fd_exec_test_instr_context_t const * test_ctx,
     360           0 :                                    bool                                 is_syscall ) {
     361           0 :   memset( ctx, 0, sizeof(fd_exec_instr_ctx_t) );
     362             : 
     363           0 :   fd_funk_t * funk = runner->funk;
     364             : 
     365             :   /* Generate unique ID for funk txn */
     366             : 
     367           0 :   fd_funk_txn_xid_t xid[1] = {0};
     368           0 :   xid[0] = fd_funk_generate_xid();
     369             : 
     370             :   /* Create temporary funk transaction and txn / slot / epoch contexts */
     371             : 
     372           0 :   fd_funk_start_write( funk );
     373           0 :   fd_funk_txn_t * funk_txn = fd_funk_txn_prepare( funk, NULL, xid, 1 );
     374           0 :   fd_funk_end_write( funk );
     375             : 
     376           0 :   ulong vote_acct_max = MAX_TX_ACCOUNT_LOCKS;
     377             : 
     378             :   /* Allocate contexts */
     379           0 :   uchar *               epoch_ctx_mem = fd_spad_alloc( runner->spad, fd_exec_epoch_ctx_align(), fd_exec_epoch_ctx_footprint( vote_acct_max ) );
     380           0 :   uchar *               slot_ctx_mem  = fd_spad_alloc( runner->spad,FD_EXEC_SLOT_CTX_ALIGN,  FD_EXEC_SLOT_CTX_FOOTPRINT  );
     381           0 :   uchar *               txn_ctx_mem   = fd_spad_alloc( runner->spad,FD_EXEC_TXN_CTX_ALIGN,   FD_EXEC_TXN_CTX_FOOTPRINT   );
     382             : 
     383           0 :   fd_exec_epoch_ctx_t * epoch_ctx     = fd_exec_epoch_ctx_join( fd_exec_epoch_ctx_new( epoch_ctx_mem, vote_acct_max ) );
     384           0 :   fd_exec_slot_ctx_t *  slot_ctx      = fd_exec_slot_ctx_join ( fd_exec_slot_ctx_new ( slot_ctx_mem, runner->spad ) );
     385           0 :   fd_exec_txn_ctx_t *   txn_ctx       = fd_exec_txn_ctx_join  ( fd_exec_txn_ctx_new  ( txn_ctx_mem   ) );
     386             : 
     387           0 :   assert( epoch_ctx );
     388           0 :   assert( slot_ctx  );
     389             : 
     390           0 :   ctx->txn_ctx = txn_ctx;
     391             : 
     392             :   /* Set up epoch context. Defaults obtained from GenesisConfig::Default() */
     393           0 :   fd_epoch_bank_t * epoch_bank = fd_exec_epoch_ctx_epoch_bank( epoch_ctx );
     394           0 :   epoch_bank->rent.lamports_per_uint8_year = 3480;
     395           0 :   epoch_bank->rent.exemption_threshold = 2;
     396           0 :   epoch_bank->rent.burn_percent = 50;
     397             : 
     398             :   /* Create account manager */
     399             : 
     400           0 :   fd_acc_mgr_t * acc_mgr = fd_acc_mgr_new( fd_spad_alloc( runner->spad, FD_ACC_MGR_ALIGN, FD_ACC_MGR_FOOTPRINT ), funk );
     401           0 :   assert( acc_mgr );
     402             : 
     403             :   /* Set up slot context */
     404             : 
     405           0 :   slot_ctx->epoch_ctx = epoch_ctx;
     406           0 :   slot_ctx->funk_txn  = funk_txn;
     407           0 :   slot_ctx->acc_mgr   = acc_mgr;
     408             : 
     409             :   /* Restore feature flags */
     410             : 
     411           0 :   fd_exec_test_feature_set_t const * feature_set = &test_ctx->epoch_context.features;
     412           0 :   if( !_restore_feature_flags( epoch_ctx, feature_set ) ) {
     413           0 :     return 0;
     414           0 :   }
     415             : 
     416             :   /* Restore slot_bank */
     417             : 
     418           0 :   fd_slot_bank_new( &slot_ctx->slot_bank );
     419             : 
     420             :   /* Blockhash queue init */
     421           0 :   fd_block_hash_queue_t * blockhash_queue = &slot_ctx->slot_bank.block_hash_queue;
     422           0 :   blockhash_queue->max_age   = FD_BLOCKHASH_QUEUE_MAX_ENTRIES;
     423           0 :   blockhash_queue->last_hash = fd_spad_alloc( runner->spad, FD_HASH_ALIGN, FD_HASH_FOOTPRINT );
     424           0 :   fd_memset( blockhash_queue->last_hash, 0, FD_HASH_FOOTPRINT );
     425             : 
     426             :   /* Set up txn context */
     427             : 
     428           0 :   fd_wksp_t * funk_wksp          = fd_funk_wksp( funk );
     429           0 :   fd_wksp_t * runtime_wksp       = fd_wksp_containing( slot_ctx );
     430           0 :   ulong       funk_txn_gaddr     = fd_wksp_gaddr( funk_wksp, funk_txn );
     431           0 :   ulong       acc_mgr_gaddr      = fd_wksp_gaddr( runtime_wksp, acc_mgr );
     432           0 :   ulong       funk_gaddr         = fd_wksp_gaddr( funk_wksp, funk );
     433           0 :   ulong       sysvar_cache_gaddr = fd_wksp_gaddr( runtime_wksp, slot_ctx->sysvar_cache );
     434             : 
     435           0 :   fd_exec_txn_ctx_from_exec_slot_ctx( slot_ctx,
     436           0 :                                       txn_ctx,
     437           0 :                                       funk_wksp,
     438           0 :                                       runtime_wksp,
     439           0 :                                       funk_txn_gaddr,
     440           0 :                                       acc_mgr_gaddr,
     441           0 :                                       sysvar_cache_gaddr,
     442           0 :                                       funk_gaddr );
     443           0 :   fd_exec_txn_ctx_setup_basic( txn_ctx );
     444             : 
     445           0 :   txn_ctx->compute_unit_limit      = test_ctx->cu_avail;
     446           0 :   txn_ctx->compute_meter           = test_ctx->cu_avail;
     447           0 :   txn_ctx->vote_accounts_pool      = NULL;
     448           0 :   txn_ctx->spad                    = runner->spad;
     449             : 
     450             :   /* Set up instruction context */
     451             : 
     452           0 :   fd_instr_info_t * info = fd_spad_alloc( runner->spad, 8UL, sizeof(fd_instr_info_t) );
     453           0 :   assert( info );
     454           0 :   memset( info, 0, sizeof(fd_instr_info_t) );
     455             : 
     456           0 :   if( test_ctx->data ) {
     457           0 :     info->data_sz = (ushort)test_ctx->data->size;
     458           0 :     info->data    = test_ctx->data->bytes;
     459           0 :   }
     460             : 
     461           0 :   memcpy( info->program_id_pubkey.uc, test_ctx->program_id, sizeof(fd_pubkey_t) );
     462             : 
     463             :   /* Prepare borrowed account table (correctly handles aliasing) */
     464             : 
     465           0 :   if( FD_UNLIKELY( test_ctx->accounts_count > MAX_TX_ACCOUNT_LOCKS ) ) {
     466           0 :     REPORT( NOTICE, "too many accounts" );
     467           0 :     return 0;
     468           0 :   }
     469             : 
     470             :   /* Load accounts into database */
     471             : 
     472           0 :   assert( acc_mgr->funk );
     473             : 
     474           0 :   fd_txn_account_t * accts = txn_ctx->accounts;
     475           0 :   fd_memset( accts, 0, test_ctx->accounts_count * sizeof(fd_txn_account_t) );
     476           0 :   txn_ctx->accounts_cnt = test_ctx->accounts_count;
     477             : 
     478           0 :   int has_program_id = 0;
     479             : 
     480           0 :   for( ulong j=0UL; j < test_ctx->accounts_count; j++ ) {
     481           0 :     memcpy(  &(txn_ctx->account_keys[j]), test_ctx->accounts[j].address, sizeof(fd_pubkey_t) );
     482           0 :     if( !_load_account( &accts[j], acc_mgr, funk_txn, &test_ctx->accounts[j], 0 ) ) {
     483           0 :       return 0;
     484           0 :     }
     485             : 
     486           0 :     if( accts[j].const_meta ) {
     487           0 :       uchar * data = fd_spad_alloc( txn_ctx->spad, FD_ACCOUNT_REC_ALIGN, FD_ACC_TOT_SZ_MAX );
     488           0 :       ulong   dlen = accts[j].const_meta->dlen;
     489           0 :       fd_memcpy( data, accts[j].const_meta, sizeof(fd_account_meta_t)+dlen );
     490           0 :       accts[j].const_meta = (fd_account_meta_t*)data;
     491           0 :       accts[j].const_data = data + sizeof(fd_account_meta_t);
     492           0 :     }
     493             : 
     494           0 :     if( !memcmp( accts[j].pubkey, test_ctx->program_id, sizeof(fd_pubkey_t) ) ) {
     495           0 :       has_program_id = 1;
     496           0 :       info->program_id = (uchar)txn_ctx->accounts_cnt;
     497           0 :     }
     498           0 :   }
     499             : 
     500             :   /* If the program id is not in the set of accounts it must be added to the set of accounts. */
     501           0 :   if( FD_UNLIKELY( !has_program_id ) ) {
     502           0 :     fd_txn_account_t * program_acc = &accts[ test_ctx->accounts_count ];
     503           0 :     fd_pubkey_t *           program_key = &txn_ctx->account_keys[ txn_ctx->accounts_cnt ];
     504           0 :     fd_txn_account_init( program_acc );
     505           0 :     memcpy( program_key, test_ctx->program_id, sizeof(fd_pubkey_t) );
     506           0 :     memcpy( program_acc->pubkey, test_ctx->program_id, sizeof(fd_pubkey_t) );
     507           0 :     program_acc->meta = fd_spad_alloc( txn_ctx->spad, alignof(fd_account_meta_t*), sizeof(fd_account_meta_t*) );
     508           0 :     program_acc->const_meta = program_acc->meta;
     509           0 :     fd_account_meta_init( program_acc->meta );
     510           0 :     info->program_id = (uchar)txn_ctx->accounts_cnt;
     511           0 :     txn_ctx->accounts_cnt++;
     512           0 :   }
     513             : 
     514             :   /* Load in executable accounts */
     515           0 :   for( ulong i = 0; i < txn_ctx->accounts_cnt; i++ ) {
     516           0 :     if ( memcmp( accts[i].const_meta->info.owner, fd_solana_bpf_loader_deprecated_program_id.key, sizeof(fd_pubkey_t) ) != 0
     517           0 :       && memcmp( accts[i].const_meta->info.owner, fd_solana_bpf_loader_program_id.key, sizeof(fd_pubkey_t) ) != 0
     518           0 :       && memcmp( accts[i].const_meta->info.owner, fd_solana_bpf_loader_upgradeable_program_id.key, sizeof(fd_pubkey_t) ) != 0
     519           0 :       && memcmp( accts[i].const_meta->info.owner, fd_solana_bpf_loader_v4_program_id.key, sizeof(fd_pubkey_t) ) != 0
     520           0 :     ) {
     521           0 :       continue;
     522           0 :     }
     523             : 
     524           0 :     fd_account_meta_t const * meta = accts[i].const_meta ? accts[i].const_meta : accts[i].meta;
     525           0 :     if (meta == NULL) {
     526           0 :       static const fd_account_meta_t sentinel = { .magic = FD_ACCOUNT_META_MAGIC };
     527           0 :       accts[i].const_meta        = &sentinel;
     528           0 :       accts[i].starting_lamports = 0UL;
     529           0 :       accts[i].starting_dlen     = 0UL;
     530           0 :       continue;
     531           0 :     }
     532             : 
     533           0 :     if( meta->info.executable ) {
     534           0 :       FD_TXN_ACCOUNT_DECL( owner_borrowed_account );
     535           0 :       int err = fd_acc_mgr_view( txn_ctx->acc_mgr, txn_ctx->funk_txn, (fd_pubkey_t *)meta->info.owner, owner_borrowed_account );
     536           0 :       if( FD_UNLIKELY( err ) ) {
     537           0 :         accts[i].starting_owner_dlen = 0;
     538           0 :       } else {
     539           0 :         accts[i].starting_owner_dlen = owner_borrowed_account->const_meta->dlen;
     540           0 :       }
     541           0 :     }
     542             : 
     543           0 :     if( FD_UNLIKELY( 0 == memcmp(meta->info.owner, fd_solana_bpf_loader_upgradeable_program_id.key, sizeof(fd_pubkey_t)) ) ) {
     544           0 :       int err = 0;
     545           0 :       fd_bpf_upgradeable_loader_state_t * program_loader_state = read_bpf_upgradeable_loader_state_for_program( txn_ctx,
     546           0 :                                                                                                                 (uchar)i,
     547           0 :                                                                                                                 &err );
     548             : 
     549           0 :       if( FD_UNLIKELY( !program_loader_state ) ) {
     550           0 :         continue;
     551           0 :       }
     552             : 
     553           0 :       fd_pubkey_t *      programdata_acc         = &program_loader_state->inner.program.programdata_address;
     554           0 :       fd_txn_account_t * executable_account      = fd_txn_account_init( &txn_ctx->executable_accounts[txn_ctx->executable_cnt] );
     555           0 :       fd_acc_mgr_view(txn_ctx->acc_mgr, txn_ctx->funk_txn, programdata_acc, executable_account);
     556           0 :       txn_ctx->executable_cnt++;
     557           0 :     }
     558           0 :   }
     559             : 
     560             :   /* Add accounts to bpf program cache */
     561           0 :   fd_funk_start_write( acc_mgr->funk );
     562           0 :   fd_bpf_scan_and_create_bpf_program_cache_entry( slot_ctx, funk_txn, runner->spad );
     563           0 :   fd_funk_end_write( acc_mgr->funk );
     564             : 
     565             :   /* Restore sysvar cache */
     566           0 :   fd_sysvar_cache_restore( slot_ctx->sysvar_cache, acc_mgr, funk_txn, runner->spad, runtime_wksp );
     567             : 
     568             :   /* Fill missing sysvar cache values with defaults */
     569             :   /* We create mock accounts for each of the sysvars and hardcode the data fields before loading it into the account manager */
     570             :   /* We use Agave sysvar defaults for data field values */
     571             : 
     572             :   /* Clock */
     573             :   // https://github.com/firedancer-io/solfuzz-agave/blob/agave-v2.0/src/lib.rs#L466-L474
     574           0 :   if( !slot_ctx->sysvar_cache->has_clock ) {
     575           0 :     slot_ctx->sysvar_cache->has_clock = 1;
     576           0 :     fd_sol_sysvar_clock_t sysvar_clock = {
     577           0 :                                           .slot = 10,
     578           0 :                                           .epoch_start_timestamp = 0,
     579           0 :                                           .epoch = 0,
     580           0 :                                           .leader_schedule_epoch = 0,
     581           0 :                                           .unix_timestamp = 0
     582           0 :                                         };
     583           0 :     memcpy( slot_ctx->sysvar_cache->val_clock, &sysvar_clock, sizeof(fd_sol_sysvar_clock_t) );
     584           0 :   }
     585             : 
     586             :   /* Epoch schedule */
     587             :   // https://github.com/firedancer-io/solfuzz-agave/blob/agave-v2.0/src/lib.rs#L476-L483
     588           0 :   if ( !slot_ctx->sysvar_cache->has_epoch_schedule ) {
     589           0 :     slot_ctx->sysvar_cache->has_epoch_schedule = 1;
     590           0 :     fd_epoch_schedule_t sysvar_epoch_schedule = {
     591           0 :                                                   .slots_per_epoch = 432000,
     592           0 :                                                   .leader_schedule_slot_offset = 432000,
     593           0 :                                                   .warmup = 1,
     594           0 :                                                   .first_normal_epoch = 14,
     595           0 :                                                   .first_normal_slot = 524256
     596           0 :                                                 };
     597           0 :     memcpy( slot_ctx->sysvar_cache->val_epoch_schedule, &sysvar_epoch_schedule, sizeof(fd_epoch_schedule_t) );
     598           0 :   }
     599             : 
     600             :   /* Rent */
     601             :   // https://github.com/firedancer-io/solfuzz-agave/blob/agave-v2.0/src/lib.rs#L487-L500
     602           0 :   if ( !slot_ctx->sysvar_cache->has_rent ) {
     603           0 :     slot_ctx->sysvar_cache->has_rent = 1;
     604           0 :     fd_rent_t sysvar_rent = {
     605           0 :                               .lamports_per_uint8_year = 3480,
     606           0 :                               .exemption_threshold = 2.0,
     607           0 :                               .burn_percent = 50
     608           0 :                             };
     609           0 :     memcpy( slot_ctx->sysvar_cache->val_rent, &sysvar_rent, sizeof(fd_rent_t) );
     610           0 :   }
     611             : 
     612           0 :   if ( !slot_ctx->sysvar_cache->has_last_restart_slot ) {
     613           0 :     slot_ctx->sysvar_cache->has_last_restart_slot = 1;
     614             : 
     615           0 :     fd_sol_sysvar_last_restart_slot_t restart = {.slot = 5000};
     616             : 
     617           0 :     memcpy( slot_ctx->sysvar_cache->val_last_restart_slot, &restart, sizeof(fd_sol_sysvar_last_restart_slot_t) );
     618           0 :   }
     619             : 
     620             :   /* Set slot bank variables */
     621           0 :   slot_ctx->slot_bank.slot = fd_sysvar_cache_clock( slot_ctx->sysvar_cache )->slot;
     622             : 
     623             :   /* Handle undefined behavior if sysvars are malicious (!!!) */
     624             : 
     625             :   /* Override epoch bank rent setting */
     626           0 :   fd_rent_t const * rent = (fd_rent_t const *)fd_sysvar_cache_rent( slot_ctx->sysvar_cache );
     627           0 :   if( rent ) {
     628           0 :     epoch_bank->rent = *rent;
     629           0 :   }
     630             : 
     631             :   /* Override most recent blockhash if given */
     632           0 :   fd_recent_block_hashes_global_t const * rbh_global = fd_sysvar_cache_recent_block_hashes( slot_ctx->sysvar_cache );
     633           0 :   fd_recent_block_hashes_t rbh[1];
     634           0 :   if( rbh_global ) {
     635           0 :     fd_bincode_decode_ctx_t decode = { .wksp = runtime_wksp };
     636           0 :     fd_recent_block_hashes_convert_global_to_local( rbh_global, rbh, &decode );
     637           0 :   }
     638             : 
     639           0 :   if( rbh_global && !deq_fd_block_block_hash_entry_t_empty( rbh->hashes ) ) {
     640           0 :     fd_block_block_hash_entry_t const * last = deq_fd_block_block_hash_entry_t_peek_tail_const( rbh->hashes );
     641           0 :     if( last ) {
     642           0 :       *blockhash_queue->last_hash                = last->blockhash;
     643           0 :       slot_ctx->slot_bank.lamports_per_signature = last->fee_calculator.lamports_per_signature;
     644           0 :       slot_ctx->prev_lamports_per_signature      = last->fee_calculator.lamports_per_signature;
     645           0 :     }
     646           0 :   }
     647             : 
     648             :   /* Load instruction accounts */
     649             : 
     650           0 :   if( FD_UNLIKELY( test_ctx->instr_accounts_count > MAX_TX_ACCOUNT_LOCKS ) ) {
     651           0 :     REPORT( NOTICE, "too many instruction accounts" );
     652           0 :     return 0;
     653           0 :   }
     654             : 
     655           0 :   uchar acc_idx_seen[256] = {0};
     656           0 :   for( ulong j=0UL; j < test_ctx->instr_accounts_count; j++ ) {
     657           0 :     uint index = test_ctx->instr_accounts[j].index;
     658           0 :     if( index >= test_ctx->accounts_count ) {
     659           0 :       REPORTV( NOTICE, " instruction account index out of range (%u > %u)", index, test_ctx->instr_accounts_count );
     660           0 :       return 0;
     661           0 :     }
     662             : 
     663           0 :     fd_txn_account_t * acc = &accts[ index ];
     664           0 :     uint flags = 0;
     665           0 :     flags |= test_ctx->instr_accounts[j].is_writable ? FD_INSTR_ACCT_FLAGS_IS_WRITABLE : 0;
     666           0 :     flags |= test_ctx->instr_accounts[j].is_signer   ? FD_INSTR_ACCT_FLAGS_IS_SIGNER   : 0;
     667             : 
     668           0 :     info->accounts[j] = acc;
     669           0 :     info->acct_flags       [j] = (uchar)flags;
     670           0 :     memcpy( info->acct_pubkeys[j].uc, acc->pubkey, sizeof(fd_pubkey_t) );
     671           0 :     info->acct_txn_idxs[j]     = (uchar) index;
     672             : 
     673           0 :     if( test_ctx->instr_accounts[j].is_writable ) {
     674           0 :       acc->meta = (void *)acc->const_meta;
     675           0 :       acc->data = (void *)acc->const_data;
     676           0 :       acc->rec  = (void *)acc->const_rec;
     677           0 :     }
     678             : 
     679           0 :     if (acc_idx_seen[index]) {
     680           0 :       info->is_duplicate[j] = 1;
     681           0 :     }
     682           0 :     acc_idx_seen[index] = 1;
     683           0 :   }
     684           0 :   info->acct_cnt = (uchar)test_ctx->instr_accounts_count;
     685             : 
     686             :   //  FIXME: Specifically for CPI syscalls, flag guard this?
     687           0 :   fd_instr_info_sum_account_lamports( info, &info->starting_lamports_h, &info->starting_lamports_l );
     688             : 
     689             :   /* The remaining checks enforce that the program is in the accounts list. */
     690           0 :   bool found_program_id = false;
     691           0 :   for( uint i = 0; i < test_ctx->accounts_count; i++ ) {
     692           0 :     if( 0 == memcmp( test_ctx->accounts[i].address, test_ctx->program_id, sizeof(fd_pubkey_t) ) ) {
     693           0 :       info->program_id = (uchar) i;
     694           0 :       found_program_id = true;
     695           0 :       break;
     696           0 :     }
     697           0 :   }
     698             : 
     699             :   /* Early returning only happens in instruction execution. */
     700           0 :   if( !is_syscall && !found_program_id ) {
     701           0 :     FD_LOG_NOTICE(( " Unable to find program_id in accounts" ));
     702           0 :     return 0;
     703           0 :   }
     704             : 
     705           0 :   ctx->funk_txn  = funk_txn;
     706           0 :   ctx->acc_mgr   = acc_mgr;
     707           0 :   ctx->instr     = info;
     708             : 
     709             :   /* Refresh the setup from the updated slot and epoch ctx. */
     710           0 :   fd_exec_txn_ctx_from_exec_slot_ctx( slot_ctx,
     711           0 :                                       txn_ctx,
     712           0 :                                       funk_wksp,
     713           0 :                                       runtime_wksp,
     714           0 :                                       funk_txn_gaddr,
     715           0 :                                       acc_mgr_gaddr,
     716           0 :                                       sysvar_cache_gaddr,
     717           0 :                                       funk_gaddr );
     718             : 
     719           0 :   fd_log_collector_init( &ctx->txn_ctx->log_collector, 1 );
     720           0 :   fd_base58_encode_32( ctx->instr->program_id_pubkey.uc, NULL, ctx->program_id_base58 );
     721             : 
     722           0 :   return 1;
     723           0 : }
     724             : 
     725             : static fd_execute_txn_task_info_t *
     726             : _txn_context_create_and_exec( fd_exec_instr_test_runner_t *      runner,
     727             :                               fd_exec_slot_ctx_t *               slot_ctx,
     728           0 :                               fd_exec_test_txn_context_t const * test_ctx ) {
     729           0 :   const uchar empty_bytes[64] = { 0 };
     730           0 :   fd_funk_t * funk = runner->funk;
     731             : 
     732             :   /* Generate unique ID for funk txn */
     733             : 
     734           0 :   fd_funk_txn_xid_t xid[1] = {0};
     735           0 :   xid[0] = fd_funk_generate_xid();
     736             : 
     737             :   /* Create temporary funk transaction and spad contexts */
     738             : 
     739           0 :   fd_funk_start_write( runner->funk );
     740           0 :   fd_funk_txn_t * funk_txn = fd_funk_txn_prepare( funk, NULL, xid, 1 );
     741           0 :   fd_funk_end_write( runner->funk );
     742             : 
     743           0 :   ulong vote_acct_max = MAX_TX_ACCOUNT_LOCKS;
     744             : 
     745             :   /* Allocate contexts */
     746           0 :   uchar *               epoch_ctx_mem = fd_spad_alloc( runner->spad, fd_exec_epoch_ctx_align(), fd_exec_epoch_ctx_footprint( vote_acct_max ) );
     747           0 :   fd_exec_epoch_ctx_t * epoch_ctx     = fd_exec_epoch_ctx_join( fd_exec_epoch_ctx_new( epoch_ctx_mem, vote_acct_max ) );
     748             : 
     749           0 :   assert( epoch_ctx );
     750           0 :   assert( slot_ctx  );
     751             : 
     752             :   /* Set up epoch context */
     753           0 :   fd_epoch_bank_t * epoch_bank = fd_exec_epoch_ctx_epoch_bank( epoch_ctx );
     754             : 
     755             :   /* Create account manager */
     756           0 :   fd_acc_mgr_t * acc_mgr = fd_acc_mgr_new( fd_spad_alloc( runner->spad, FD_ACC_MGR_ALIGN, FD_ACC_MGR_FOOTPRINT ), funk );
     757           0 :   assert( acc_mgr );
     758             : 
     759             :   /* Set up slot context */
     760             : 
     761           0 :   slot_ctx->epoch_ctx = epoch_ctx;
     762           0 :   slot_ctx->funk_txn  = funk_txn;
     763           0 :   slot_ctx->acc_mgr   = acc_mgr;
     764             : 
     765             :   /* Restore feature flags */
     766             : 
     767           0 :   fd_exec_test_feature_set_t const * feature_set = &test_ctx->epoch_ctx.features;
     768           0 :   if( !_restore_feature_flags( epoch_ctx, feature_set ) ) {
     769           0 :     return NULL;
     770           0 :   }
     771             : 
     772             :   /* Restore slot bank */
     773           0 :   fd_slot_bank_new( &slot_ctx->slot_bank );
     774             : 
     775             :   /* Initialize builtin accounts */
     776           0 :   fd_funk_start_write( runner->funk );
     777           0 :   fd_builtin_programs_init( slot_ctx );
     778           0 :   fd_funk_end_write( runner->funk );
     779             : 
     780             :   /* Load account states into funk (note this is different from the account keys):
     781             :     Account state = accounts to populate Funk
     782             :     Account keys = account keys that the transaction needs */
     783           0 :   for( ulong i = 0; i < test_ctx->account_shared_data_count; i++ ) {
     784             :     /* Load the accounts into the account manager
     785             :        Borrowed accounts get reset anyways - we just need to load the account somewhere */
     786           0 :     FD_TXN_ACCOUNT_DECL(acc);
     787           0 :     _load_txn_account( acc, acc_mgr, funk_txn, &test_ctx->account_shared_data[i], 0 );
     788           0 :   }
     789             : 
     790             :   /* Restore sysvar cache */
     791           0 :   fd_sysvar_cache_restore( slot_ctx->sysvar_cache, acc_mgr, funk_txn, runner->spad, fd_wksp_containing( slot_ctx ) );
     792             : 
     793             :   /* Add accounts to bpf program cache */
     794           0 :   fd_funk_start_write( runner->funk );
     795           0 :   fd_bpf_scan_and_create_bpf_program_cache_entry( slot_ctx, funk_txn, runner->spad );
     796             : 
     797             :   /* Default slot */
     798           0 :   ulong slot = test_ctx->slot_ctx.slot ? test_ctx->slot_ctx.slot : 10; // Arbitrary default > 0
     799             : 
     800             :   /* Set slot bank variables (defaults obtained from GenesisConfig::default() in Agave) */
     801           0 :   slot_ctx->slot_bank.slot                                            = slot;
     802           0 :   slot_ctx->slot_bank.prev_slot                                       = slot_ctx->slot_bank.slot - 1; // Can underflow, but its fine since it will correctly be ULONG_MAX
     803           0 :   slot_ctx->slot_bank.fee_rate_governor.burn_percent                  = 50;
     804           0 :   slot_ctx->slot_bank.fee_rate_governor.min_lamports_per_signature    = 0;
     805           0 :   slot_ctx->slot_bank.fee_rate_governor.max_lamports_per_signature    = 0;
     806           0 :   slot_ctx->slot_bank.fee_rate_governor.target_lamports_per_signature = 10000;
     807           0 :   slot_ctx->slot_bank.fee_rate_governor.target_signatures_per_slot    = 20000;
     808           0 :   slot_ctx->slot_bank.lamports_per_signature                          = 5000;
     809           0 :   slot_ctx->prev_lamports_per_signature                               = 5000;
     810             : 
     811             :   /* Set epoch bank variables if not present (defaults obtained from GenesisConfig::default() in Agave) */
     812           0 :   fd_epoch_schedule_t default_epoch_schedule = {
     813           0 :                                                 .slots_per_epoch             = 432000,
     814           0 :                                                 .leader_schedule_slot_offset = 432000,
     815           0 :                                                 .warmup                      = 1,
     816           0 :                                                 .first_normal_epoch          = 14,
     817           0 :                                                 .first_normal_slot           = 524256
     818           0 :                                                };
     819           0 :   fd_rent_t           default_rent           = {
     820           0 :                                                 .lamports_per_uint8_year     = 3480,
     821           0 :                                                 .exemption_threshold         = 2.0,
     822           0 :                                                 .burn_percent                = 50
     823           0 :                                                };
     824           0 :   epoch_bank->epoch_schedule      = default_epoch_schedule;
     825           0 :   epoch_bank->rent_epoch_schedule = default_epoch_schedule;
     826           0 :   epoch_bank->rent                = default_rent;
     827           0 :   epoch_bank->ticks_per_slot      = 64;
     828           0 :   epoch_bank->slots_per_year      = SECONDS_PER_YEAR * (1000000000.0 / (double)6250000) / (double)epoch_bank->ticks_per_slot;
     829             : 
     830             :   // Override default values if provided
     831           0 :   if( slot_ctx->sysvar_cache->has_epoch_schedule ) {
     832           0 :     epoch_bank->epoch_schedule      = *(fd_epoch_schedule_t *)fd_type_pun_const( slot_ctx->sysvar_cache->val_epoch_schedule );
     833           0 :     epoch_bank->rent_epoch_schedule = *(fd_epoch_schedule_t *)fd_type_pun_const( slot_ctx->sysvar_cache->val_epoch_schedule );
     834           0 :   }
     835             : 
     836           0 :   if( slot_ctx->sysvar_cache->has_rent ) {
     837           0 :     epoch_bank->rent = *(fd_rent_t *)fd_type_pun_const( slot_ctx->sysvar_cache->val_rent );
     838           0 :   }
     839             : 
     840             :   /* Provide default slot hashes of size 1 if not provided */
     841           0 :   if( !slot_ctx->sysvar_cache->has_slot_hashes ) {
     842           0 :     uchar * deque_mem = fd_spad_alloc( runner->spad, deq_fd_slot_hash_t_align(), deq_fd_slot_hash_t_footprint( 1 ) );
     843           0 :     fd_slot_hash_t * slot_hashes = deq_fd_slot_hash_t_join( deq_fd_slot_hash_t_new( deque_mem, 1 ) );
     844           0 :     fd_slot_hash_t * dummy_elem = deq_fd_slot_hash_t_push_tail_nocopy( slot_hashes );
     845           0 :     memset( dummy_elem, 0, sizeof(fd_slot_hash_t) );
     846           0 :     fd_slot_hashes_t default_slot_hashes = { .hashes = slot_hashes };
     847           0 :     fd_sysvar_slot_hashes_init( slot_ctx, &default_slot_hashes );
     848           0 :   }
     849             : 
     850             :   /* Provide default stake history if not provided */
     851           0 :   if( !slot_ctx->sysvar_cache->has_stake_history ) {
     852             :     // Provide a 0-set default entry
     853           0 :     fd_stake_history_entry_t entry = {0};
     854           0 :     fd_sysvar_stake_history_init( slot_ctx );
     855           0 :     fd_sysvar_stake_history_update( slot_ctx, &entry, runner->spad );
     856           0 :   }
     857             : 
     858             :   /* Provide default last restart slot sysvar if not provided */
     859           0 :   if( !slot_ctx->sysvar_cache->has_last_restart_slot ) {
     860           0 :     fd_sysvar_last_restart_slot_init( slot_ctx );
     861           0 :   }
     862             : 
     863             :   /* Provide a default clock if not present */
     864           0 :   if( !slot_ctx->sysvar_cache->has_clock ) {
     865           0 :     fd_sysvar_clock_init( slot_ctx );
     866           0 :     fd_sysvar_clock_update( slot_ctx, runner->spad );
     867           0 :   }
     868             : 
     869             :   /* Epoch schedule and rent get set from the epoch bank */
     870           0 :   fd_sysvar_epoch_schedule_init( slot_ctx );
     871           0 :   fd_sysvar_rent_init( slot_ctx );
     872             : 
     873             :   /* Set the epoch rewards sysvar if partition epoch rewards feature is enabled
     874             : 
     875             :      TODO: The init parameters are not exactly conformant with Agave's epoch rewards sysvar. We should
     876             :      be calling `fd_begin_partitioned_rewards` with the same parameters as Agave. However,
     877             :      we just need the `active` field to be conformant due to a single Stake program check.
     878             :      THIS MAY CHANGE IN THE FUTURE. If there are other parts of transaction execution that use
     879             :      the epoch rewards sysvar, we may need to update this.
     880             :   */
     881           0 :   if( (FD_FEATURE_ACTIVE( slot_ctx->slot_bank.slot, slot_ctx->epoch_ctx->features, enable_partitioned_epoch_reward ) ||
     882           0 :        FD_FEATURE_ACTIVE( slot_ctx->slot_bank.slot, slot_ctx->epoch_ctx->features, partitioned_epoch_rewards_superfeature ))
     883           0 :       && !slot_ctx->sysvar_cache->has_epoch_rewards ) {
     884           0 :     fd_point_value_t point_value = {0};
     885           0 :     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;
     886           0 :     fd_sysvar_epoch_rewards_init( slot_ctx, 0UL, 0UL, 2UL, 1UL, point_value, last_hash);
     887           0 :   }
     888             : 
     889             :   /* Restore sysvar cache (again, since we may need to provide default sysvars) */
     890           0 :   fd_sysvar_cache_restore( slot_ctx->sysvar_cache, acc_mgr, funk_txn, runner->spad, fd_wksp_containing( slot_ctx ) );
     891             : 
     892             :   /* A NaN rent exemption threshold is U.B. in Solana Labs */
     893           0 :   fd_rent_t const * rent = (fd_rent_t const *)fd_sysvar_cache_rent( slot_ctx->sysvar_cache );
     894           0 :   if( ( !fd_double_is_normal( rent->exemption_threshold ) ) |
     895           0 :       ( rent->exemption_threshold     <      0.0 ) |
     896           0 :       ( rent->exemption_threshold     >    999.0 ) |
     897           0 :       ( rent->lamports_per_uint8_year > UINT_MAX ) |
     898           0 :       ( rent->burn_percent            >      100 ) ) {
     899           0 :     fd_funk_end_write( runner->funk );
     900           0 :     return NULL;
     901           0 :   }
     902             : 
     903             :   /* Blockhash queue is given in txn message. We need to populate the following two fields:
     904             :      - slot_ctx->slot_bank.block_hash_queue
     905             :      - slot_ctx->slot_bank.recent_block_hashes */
     906           0 :   ulong num_blockhashes = test_ctx->blockhash_queue_count;
     907             : 
     908             :   /* Blockhash queue init */
     909           0 :   slot_ctx->slot_bank.block_hash_queue.max_age   = FD_BLOCKHASH_QUEUE_MAX_ENTRIES;
     910           0 :   slot_ctx->slot_bank.block_hash_queue.ages_root = NULL;
     911           0 :   uchar * pool_mem = fd_spad_alloc( runner->spad, fd_hash_hash_age_pair_t_map_align(), fd_hash_hash_age_pair_t_map_footprint( 400 ) );
     912           0 :   slot_ctx->slot_bank.block_hash_queue.ages_pool = fd_hash_hash_age_pair_t_map_join( fd_hash_hash_age_pair_t_map_new( pool_mem, 400 ) );
     913           0 :   slot_ctx->slot_bank.block_hash_queue.last_hash = fd_spad_alloc( runner->spad, FD_HASH_ALIGN, FD_HASH_FOOTPRINT );
     914             : 
     915             :   // Save lamports per signature for most recent blockhash, if sysvar cache contains recent block hashes
     916           0 :   fd_recent_block_hashes_global_t const * rbh_global = fd_sysvar_cache_recent_block_hashes( slot_ctx->sysvar_cache );
     917           0 :   fd_recent_block_hashes_t rbh[1];
     918           0 :   if( rbh_global ) {
     919           0 :     fd_bincode_decode_ctx_t decode = { .wksp = fd_wksp_containing( runner->spad ) };
     920           0 :     fd_recent_block_hashes_convert_global_to_local( rbh_global, rbh, &decode );
     921           0 :   }
     922             : 
     923           0 :   if( rbh_global && !deq_fd_block_block_hash_entry_t_empty( rbh->hashes ) ) {
     924           0 :     fd_block_block_hash_entry_t const * last = deq_fd_block_block_hash_entry_t_peek_head_const( rbh->hashes );
     925           0 :     if( last && last->fee_calculator.lamports_per_signature!=0UL ) {
     926           0 :       slot_ctx->slot_bank.lamports_per_signature = last->fee_calculator.lamports_per_signature;
     927           0 :       slot_ctx->prev_lamports_per_signature      = last->fee_calculator.lamports_per_signature;
     928           0 :     }
     929           0 :   }
     930             : 
     931             :   // Blockhash_queue[end] = last (latest) hash
     932             :   // Blockhash_queue[0] = genesis hash
     933           0 :   if( num_blockhashes > 0 ) {
     934           0 :     memcpy( &epoch_bank->genesis_hash, test_ctx->blockhash_queue[0]->bytes, sizeof(fd_hash_t) );
     935             : 
     936           0 :     for( ulong i = 0; i < num_blockhashes; ++i ) {
     937             :       // Recent block hashes cap is 150 (actually 151), while blockhash queue capacity is 300 (actually 301)
     938           0 :       fd_block_block_hash_entry_t blockhash_entry;
     939           0 :       memcpy( &blockhash_entry.blockhash, test_ctx->blockhash_queue[i]->bytes, sizeof(fd_hash_t) );
     940           0 :       slot_ctx->slot_bank.poh = blockhash_entry.blockhash;
     941           0 :       fd_sysvar_recent_hashes_update( slot_ctx, runner->spad );
     942           0 :     }
     943           0 :   } else {
     944             :     // Add a default empty blockhash and use it as genesis
     945           0 :     num_blockhashes = 1;
     946           0 :     memcpy( &epoch_bank->genesis_hash, empty_bytes, sizeof(fd_hash_t) );
     947           0 :     fd_block_block_hash_entry_t blockhash_entry;
     948           0 :     memcpy( &blockhash_entry.blockhash, empty_bytes, sizeof(fd_hash_t) );
     949           0 :     slot_ctx->slot_bank.poh = blockhash_entry.blockhash;
     950           0 :     fd_sysvar_recent_hashes_update( slot_ctx, runner->spad );
     951           0 :   }
     952           0 :   fd_sysvar_cache_restore_recent_block_hashes( slot_ctx->sysvar_cache, acc_mgr, funk_txn, runner->spad, fd_wksp_containing( slot_ctx ) );
     953             : 
     954           0 :   fd_funk_end_write( runner->funk );
     955             : 
     956             :   /* Create the raw txn (https://solana.com/docs/core/transactions#transaction-size) */
     957           0 :   uchar * txn_raw_begin = fd_spad_alloc( runner->spad, alignof(uchar), 1232 );
     958           0 :   ushort instr_count, addr_table_cnt;
     959           0 :   ulong msg_sz = _serialize_txn( txn_raw_begin, &test_ctx->tx, &instr_count, &addr_table_cnt );
     960           0 :   if( FD_UNLIKELY( msg_sz==ULONG_MAX ) ) {
     961           0 :     return NULL;
     962           0 :   }
     963             : 
     964             :   /* Set up txn descriptor from raw data */
     965           0 :   fd_txn_t * txn_descriptor = (fd_txn_t *) fd_spad_alloc( runner->spad, fd_txn_align(), fd_txn_footprint( instr_count, addr_table_cnt ) );
     966           0 :   if( FD_UNLIKELY( !fd_txn_parse( txn_raw_begin, msg_sz, txn_descriptor, NULL ) ) ) {
     967           0 :     return NULL;
     968           0 :   }
     969             : 
     970             :   /* Run txn preparation phases and execution
     971             :      NOTE: This should be modified accordingly if transaction setup logic changes */
     972           0 :   fd_txn_p_t * txn = fd_spad_alloc( runner->spad, alignof(fd_txn_p_t), sizeof(fd_txn_p_t) );
     973           0 :   memcpy( txn->payload, txn_raw_begin, msg_sz );
     974           0 :   txn->payload_sz = msg_sz;
     975           0 :   txn->flags = FD_TXN_P_FLAGS_SANITIZE_SUCCESS;
     976           0 :   memcpy( txn->_, txn_descriptor, fd_txn_footprint( instr_count, addr_table_cnt ) );
     977             : 
     978           0 :   fd_execute_txn_task_info_t * task_info = fd_spad_alloc( runner->spad, alignof(fd_execute_txn_task_info_t), sizeof(fd_execute_txn_task_info_t) );
     979           0 :   memset( task_info, 0, sizeof(fd_execute_txn_task_info_t) );
     980           0 :   task_info->txn     = txn;
     981           0 :   task_info->txn_ctx = fd_spad_alloc( runner->spad, FD_EXEC_TXN_CTX_ALIGN, FD_EXEC_TXN_CTX_FOOTPRINT );
     982             : 
     983           0 :   fd_tpool_t tpool[1];
     984           0 :   tpool->worker_cnt = 1;
     985           0 :   tpool->worker_max = 1;
     986             : 
     987           0 :   fd_runtime_prepare_txns_start( slot_ctx, task_info, txn, 1UL, runner->spad );
     988             : 
     989             :   /* Setup the spad for account allocation */
     990           0 :   task_info->txn_ctx->spad = runner->spad;
     991             : 
     992           0 :   fd_runtime_pre_execute_check( task_info );
     993             : 
     994           0 :   if( task_info->txn->flags & FD_TXN_P_FLAGS_SANITIZE_SUCCESS ) {
     995           0 :     task_info->txn->flags |= FD_TXN_P_FLAGS_EXECUTE_SUCCESS;
     996           0 :     task_info->exec_res    = fd_execute_txn( task_info );
     997           0 :   }
     998             : 
     999           0 :   slot_ctx->slot_bank.collected_execution_fees += task_info->txn_ctx->execution_fee;
    1000           0 :   slot_ctx->slot_bank.collected_priority_fees  += task_info->txn_ctx->priority_fee;
    1001           0 :   slot_ctx->slot_bank.collected_rent           += task_info->txn_ctx->collected_rent;
    1002           0 :   return task_info;
    1003           0 : }
    1004             : 
    1005             : static int
    1006             : _block_context_create_and_exec( fd_exec_instr_test_runner_t *        runner,
    1007             :                                 fd_exec_slot_ctx_t *                 slot_ctx,
    1008           0 :                                 fd_exec_test_block_context_t const * test_ctx ) {
    1009           0 :   fd_funk_t * funk = runner->funk;
    1010             : 
    1011             :   /* Generate unique ID for funk txn */
    1012           0 :   fd_funk_txn_xid_t xid[1] = {0};
    1013           0 :   xid[0] = fd_funk_generate_xid();
    1014             : 
    1015             :   /* Create temporary funk transaction and slot / epoch contexts */
    1016           0 :   fd_funk_start_write( runner->funk );
    1017           0 :   fd_funk_txn_t * funk_txn = fd_funk_txn_prepare( funk, NULL, xid, 1 );
    1018           0 :   fd_funk_end_write( runner->funk );
    1019             : 
    1020             :   /* Allocate contexts */
    1021           0 :   ulong                 vote_acct_max = fd_ulong_max( 128UL,
    1022           0 :                                                       fd_ulong_max( test_ctx->epoch_ctx.stake_accounts_count,
    1023           0 :                                                                        fd_ulong_max( test_ctx->epoch_ctx.vote_accounts_t_count,
    1024           0 :                                                                                         fd_ulong_max( test_ctx->epoch_ctx.vote_accounts_t_1_count,
    1025           0 :                                                                                                          test_ctx->epoch_ctx.vote_accounts_t_2_count ) ) ) );
    1026           0 :   uchar *               epoch_ctx_mem = fd_spad_alloc( runner->spad, 128UL, fd_exec_epoch_ctx_footprint( vote_acct_max ) );
    1027           0 :   fd_exec_epoch_ctx_t * epoch_ctx     = fd_exec_epoch_ctx_join( fd_exec_epoch_ctx_new( epoch_ctx_mem, vote_acct_max ) );
    1028             : 
    1029             :   /* Create account manager */
    1030           0 :   fd_acc_mgr_t * acc_mgr = fd_acc_mgr_new( fd_spad_alloc( runner->spad, FD_ACC_MGR_ALIGN, FD_ACC_MGR_FOOTPRINT ), funk );
    1031             : 
    1032             :   /* Restore feature flags */
    1033           0 :   if( !_restore_feature_flags( epoch_ctx, &test_ctx->epoch_ctx.features ) ) {
    1034           0 :     return 1;
    1035           0 :   }
    1036             : 
    1037             :   /* Set up slot context */
    1038           0 :   slot_ctx->funk_txn              = funk_txn;
    1039           0 :   slot_ctx->acc_mgr               = acc_mgr;
    1040           0 :   slot_ctx->enable_exec_recording = 0;
    1041           0 :   slot_ctx->epoch_ctx             = epoch_ctx;
    1042           0 :   slot_ctx->runtime_wksp          = fd_wksp_containing( slot_ctx );
    1043           0 :   fd_memcpy( &slot_ctx->slot_bank.banks_hash, test_ctx->slot_ctx.parent_bank_hash, sizeof( fd_hash_t ) );
    1044             : 
    1045             :   /* Set up slot bank */
    1046           0 :   ulong slot = test_ctx->slot_ctx.slot;
    1047           0 :   fd_slot_bank_t * slot_bank = &slot_ctx->slot_bank;
    1048             : 
    1049           0 :   fd_memcpy( slot_bank->lthash.lthash, test_ctx->slot_ctx.parent_lt_hash, FD_LTHASH_LEN_BYTES );
    1050           0 :   slot_bank->slot                  = slot;
    1051           0 :   slot_bank->block_height          = test_ctx->slot_ctx.block_height;
    1052           0 :   slot_bank->prev_slot             = test_ctx->slot_ctx.prev_slot;
    1053           0 :   slot_bank->fee_rate_governor     = (fd_fee_rate_governor_t) {
    1054           0 :     .target_lamports_per_signature = 10000UL,
    1055           0 :     .target_signatures_per_slot    = 20000UL,
    1056           0 :     .min_lamports_per_signature    = 5000UL,
    1057           0 :     .max_lamports_per_signature    = 100000UL,
    1058           0 :     .burn_percent                  = 50,
    1059           0 :   };
    1060           0 :   slot_bank->capitalization        = test_ctx->slot_ctx.prev_epoch_capitalization;
    1061             : 
    1062             :   /* Set up epoch context and epoch bank */
    1063             :   /* TODO: Do we need any more of these? */
    1064           0 :   fd_epoch_bank_t * epoch_bank    = fd_exec_epoch_ctx_epoch_bank( epoch_ctx );
    1065             : 
    1066             :   // self.max_tick_height = (self.slot + 1) * self.ticks_per_slot;
    1067           0 :   epoch_bank->hashes_per_tick       = test_ctx->epoch_ctx.hashes_per_tick;
    1068           0 :   epoch_bank->ticks_per_slot        = test_ctx->epoch_ctx.ticks_per_slot;
    1069           0 :   epoch_bank->slots_per_year        = test_ctx->epoch_ctx.slots_per_year;
    1070           0 :   epoch_bank->inflation             = (fd_inflation_t) {
    1071           0 :     .initial         = test_ctx->epoch_ctx.inflation.initial,
    1072           0 :     .terminal        = test_ctx->epoch_ctx.inflation.terminal,
    1073           0 :     .taper           = test_ctx->epoch_ctx.inflation.taper,
    1074           0 :     .foundation      = test_ctx->epoch_ctx.inflation.foundation,
    1075           0 :     .foundation_term = test_ctx->epoch_ctx.inflation.foundation_term
    1076           0 :   };
    1077           0 :   epoch_bank->genesis_creation_time = test_ctx->epoch_ctx.genesis_creation_time;
    1078             : 
    1079             :   /* Load in all accounts provided in the context */
    1080           0 :   for( ushort i=0; i<test_ctx->acct_states_count; i++ ) {
    1081           0 :     FD_TXN_ACCOUNT_DECL(acc);
    1082           0 :     _load_txn_account( acc, acc_mgr, funk_txn, &test_ctx->acct_states[i], 0 );
    1083           0 :   }
    1084             : 
    1085             :   /* Restore sysvar cache */
    1086           0 :   fd_runtime_sysvar_cache_load( slot_ctx, runner->spad );
    1087             : 
    1088             :   /* Finish init epoch bank sysvars */
    1089           0 :   fd_memcpy( &epoch_bank->epoch_schedule, slot_ctx->sysvar_cache->val_epoch_schedule, sizeof(fd_epoch_schedule_t) );
    1090           0 :   fd_memcpy( &epoch_bank->rent_epoch_schedule, slot_ctx->sysvar_cache->val_epoch_schedule, sizeof(fd_epoch_schedule_t) );
    1091           0 :   fd_memcpy( &epoch_bank->rent, slot_ctx->sysvar_cache->val_rent, sizeof(fd_rent_t) );
    1092           0 :   epoch_bank->stakes.epoch = fd_slot_to_epoch( &epoch_bank->epoch_schedule, slot_bank->prev_slot, NULL );
    1093             : 
    1094             :   /* Update stake cache for epoch T */
    1095           0 :   for( uint i=0U; i<test_ctx->epoch_ctx.stake_accounts_count; i++ ) {
    1096           0 :     fd_pubkey_t voter_pubkey;
    1097           0 :     fd_memcpy( &voter_pubkey, test_ctx->epoch_ctx.stake_accounts[i].voter_pubkey, sizeof(fd_pubkey_t) );
    1098             : 
    1099           0 :     fd_pubkey_t stake_account;
    1100           0 :     fd_memcpy( &stake_account, test_ctx->epoch_ctx.stake_accounts[i].stake_account_pubkey, sizeof(fd_pubkey_t) );
    1101             : 
    1102           0 :     ulong  stake                = test_ctx->epoch_ctx.stake_accounts[i].stake;
    1103           0 :     ulong  activation_epoch     = test_ctx->epoch_ctx.stake_accounts[i].activation_epoch;
    1104           0 :     ulong  deactivation_epoch   = test_ctx->epoch_ctx.stake_accounts[i].deactivation_epoch;
    1105           0 :     double warmup_cooldown_rate = test_ctx->epoch_ctx.stake_accounts[i].warmup_cooldown_rate;
    1106             : 
    1107           0 :     fd_delegation_pair_t_mapnode_t * stake_node = fd_delegation_pair_t_map_acquire( epoch_bank->stakes.stake_delegations_pool );
    1108           0 :     fd_memcpy( &stake_node->elem.account, &stake_account, sizeof(fd_pubkey_t) );
    1109           0 :     fd_memcpy( &stake_node->elem.delegation.voter_pubkey, &voter_pubkey, sizeof(fd_pubkey_t) );
    1110           0 :     stake_node->elem.delegation.stake                = stake;
    1111           0 :     stake_node->elem.delegation.activation_epoch     = activation_epoch;
    1112           0 :     stake_node->elem.delegation.deactivation_epoch   = deactivation_epoch;
    1113           0 :     stake_node->elem.delegation.warmup_cooldown_rate = warmup_cooldown_rate;
    1114             : 
    1115           0 :     fd_delegation_pair_t_map_insert( epoch_bank->stakes.stake_delegations_pool,
    1116           0 :                                      &epoch_bank->stakes.stake_delegations_root,
    1117           0 :                                      stake_node );
    1118           0 :   }
    1119             : 
    1120             :   /* Update vote cache for epoch T */
    1121           0 :   for( uint i=0U; i<test_ctx->epoch_ctx.vote_accounts_t_count; i++ ) {
    1122           0 :     fd_exec_test_acct_state_t * vote_account = &test_ctx->epoch_ctx.vote_accounts_t[i].vote_account;
    1123           0 :     ulong                       stake        = test_ctx->epoch_ctx.vote_accounts_t[i].stake;
    1124             : 
    1125           0 :     fd_vote_accounts_pair_t_mapnode_t * vote_node = fd_vote_accounts_pair_t_map_acquire( epoch_bank->stakes.vote_accounts.vote_accounts_pool );
    1126           0 :     vote_node->elem.stake = stake;
    1127           0 :     fd_memcpy( &vote_node->elem.key, vote_account->address, sizeof(fd_pubkey_t) );
    1128           0 :     vote_node->elem.value.executable = vote_account->executable;
    1129           0 :     vote_node->elem.value.lamports   = vote_account->lamports;
    1130           0 :     vote_node->elem.value.rent_epoch = vote_account->rent_epoch;
    1131           0 :     vote_node->elem.value.data_len   = vote_account->data->size;
    1132           0 :     vote_node->elem.value.data       = fd_spad_alloc( runner->spad, alignof(uchar), vote_account->data->size );
    1133           0 :     fd_memcpy( vote_node->elem.value.data, vote_account->data->bytes, vote_account->data->size );
    1134           0 :     fd_memcpy( &vote_node->elem.value.owner, vote_account->owner, sizeof(fd_pubkey_t) );
    1135             : 
    1136           0 :     fd_vote_accounts_pair_t_map_insert( epoch_bank->stakes.vote_accounts.vote_accounts_pool,
    1137           0 :                                         &epoch_bank->stakes.vote_accounts.vote_accounts_root,
    1138           0 :                                         vote_node );
    1139           0 :   }
    1140             : 
    1141             :   /* Update vote cache for epoch T-1 */
    1142           0 :   for( uint i=0U; i<test_ctx->epoch_ctx.vote_accounts_t_1_count; i++ ) {
    1143           0 :     fd_exec_test_acct_state_t * vote_account = &test_ctx->epoch_ctx.vote_accounts_t_1[i].vote_account;
    1144           0 :     ulong                       stake        = test_ctx->epoch_ctx.vote_accounts_t_1[i].stake;
    1145             : 
    1146           0 :     fd_vote_accounts_pair_t_mapnode_t * vote_node = fd_vote_accounts_pair_t_map_acquire( epoch_bank->next_epoch_stakes.vote_accounts_pool );
    1147           0 :     vote_node->elem.stake = stake;
    1148           0 :     fd_memcpy( &vote_node->elem.key, vote_account->address, sizeof(fd_pubkey_t) );
    1149           0 :     vote_node->elem.value.executable = vote_account->executable;
    1150           0 :     vote_node->elem.value.lamports   = vote_account->lamports;
    1151           0 :     vote_node->elem.value.rent_epoch = vote_account->rent_epoch;
    1152           0 :     vote_node->elem.value.data_len   = vote_account->data->size;
    1153           0 :     vote_node->elem.value.data       = fd_spad_alloc( runner->spad, alignof(uchar), vote_account->data->size );
    1154           0 :     fd_memcpy( vote_node->elem.value.data, vote_account->data->bytes, vote_account->data->size );
    1155           0 :     fd_memcpy( &vote_node->elem.value.owner, vote_account->owner, sizeof(fd_pubkey_t) );
    1156             : 
    1157           0 :     fd_vote_accounts_pair_t_map_insert( epoch_bank->next_epoch_stakes.vote_accounts_pool,
    1158           0 :                                         &epoch_bank->next_epoch_stakes.vote_accounts_root,
    1159           0 :                                         vote_node );
    1160           0 :   }
    1161             : 
    1162             :   /* Update vote cache for epoch T-2 */
    1163           0 :   uchar * pool_mem                           = fd_spad_alloc( runner->spad,
    1164           0 :                                                               fd_vote_accounts_pair_t_map_align(),
    1165           0 :                                                               fd_vote_accounts_pair_t_map_footprint( vote_acct_max ) );
    1166           0 :   slot_bank->epoch_stakes.vote_accounts_pool = fd_vote_accounts_pair_t_map_join( fd_vote_accounts_pair_t_map_new( pool_mem, vote_acct_max ) );
    1167           0 :   slot_bank->epoch_stakes.vote_accounts_root = NULL;
    1168           0 :   for( uint i=0U; i<test_ctx->epoch_ctx.vote_accounts_t_2_count; i++ ) {
    1169           0 :     fd_exec_test_acct_state_t * vote_account = &test_ctx->epoch_ctx.vote_accounts_t_2[i].vote_account;
    1170           0 :     ulong                       stake        = test_ctx->epoch_ctx.vote_accounts_t_2[i].stake;
    1171             : 
    1172           0 :     fd_vote_accounts_pair_t_mapnode_t * vote_node = fd_vote_accounts_pair_t_map_acquire( slot_bank->epoch_stakes.vote_accounts_pool );
    1173           0 :     vote_node->elem.stake = stake;
    1174           0 :     fd_memcpy( &vote_node->elem.key, vote_account->address, sizeof(fd_pubkey_t) );
    1175           0 :     vote_node->elem.value.executable = vote_account->executable;
    1176           0 :     vote_node->elem.value.lamports   = vote_account->lamports;
    1177           0 :     vote_node->elem.value.rent_epoch = vote_account->rent_epoch;
    1178           0 :     vote_node->elem.value.data_len   = vote_account->data->size;
    1179           0 :     vote_node->elem.value.data       = fd_spad_alloc( runner->spad, alignof(uchar), vote_account->data->size );
    1180           0 :     fd_memcpy( vote_node->elem.value.data, vote_account->data->bytes, vote_account->data->size );
    1181           0 :     fd_memcpy( &vote_node->elem.value.owner, vote_account->owner, sizeof(fd_pubkey_t) );
    1182             : 
    1183           0 :     fd_vote_accounts_pair_t_map_insert( slot_bank->epoch_stakes.vote_accounts_pool,
    1184           0 :                                         &slot_bank->epoch_stakes.vote_accounts_root,
    1185           0 :                                         vote_node );
    1186           0 :   }
    1187             : 
    1188             :   /* Initialize the current running epoch stake and vote accounts */
    1189           0 :   pool_mem                                        = fd_spad_alloc( runner->spad,
    1190           0 :                                                                    fd_account_keys_pair_t_map_align(),
    1191           0 :                                                                    fd_account_keys_pair_t_map_footprint( vote_acct_max ) );
    1192           0 :   slot_bank->stake_account_keys.account_keys_pool = fd_account_keys_pair_t_map_join( fd_account_keys_pair_t_map_new( pool_mem, vote_acct_max ) );
    1193           0 :   slot_bank->stake_account_keys.account_keys_root = NULL;
    1194           0 :   for( uint i=0U; i<test_ctx->epoch_ctx.new_stake_accounts_count; i++ ) {
    1195           0 :     FD_TXN_ACCOUNT_DECL( acc );
    1196             : 
    1197           0 :     fd_pubkey_t stake_pubkey;
    1198           0 :     fd_memcpy( &stake_pubkey, test_ctx->epoch_ctx.new_stake_accounts[i]->bytes, sizeof(fd_pubkey_t) );
    1199             : 
    1200             :     // Fetch and store the stake account using acc mgr
    1201           0 :     if( fd_acc_mgr_view( slot_ctx->acc_mgr, slot_ctx->funk_txn, &stake_pubkey, acc ) ) {
    1202           0 :       continue;
    1203           0 :     }
    1204             : 
    1205           0 :     fd_store_stake_delegation( slot_ctx, acc );
    1206           0 :   }
    1207             : 
    1208           0 :   pool_mem                                       = fd_spad_alloc( runner->spad,
    1209           0 :                                                                   fd_account_keys_pair_t_map_align(),
    1210           0 :                                                                   fd_account_keys_pair_t_map_footprint( vote_acct_max ) );
    1211           0 :   slot_bank->vote_account_keys.account_keys_pool = fd_account_keys_pair_t_map_join( fd_account_keys_pair_t_map_new( pool_mem, vote_acct_max ) );
    1212           0 :   slot_bank->vote_account_keys.account_keys_root = NULL;
    1213           0 :   for( uint i=0U; i<test_ctx->epoch_ctx.new_vote_accounts_count; i++ ) {
    1214           0 :     FD_TXN_ACCOUNT_DECL( acc );
    1215             : 
    1216           0 :     fd_pubkey_t vote_pubkey;
    1217           0 :     memcpy( &vote_pubkey, test_ctx->epoch_ctx.new_vote_accounts[i]->bytes, sizeof(fd_pubkey_t) );
    1218             : 
    1219             :     // Fetch and store the vote account from the acc mgr
    1220           0 :     if( fd_acc_mgr_view( slot_ctx->acc_mgr, slot_ctx->funk_txn, &vote_pubkey, acc ) ) {
    1221           0 :       continue;
    1222           0 :     }
    1223             : 
    1224           0 :     fd_vote_store_account( slot_ctx, acc );
    1225           0 :   }
    1226             : 
    1227             :   /* Update leader schedule */
    1228           0 :   fd_runtime_update_leaders( slot_ctx, slot_ctx->slot_bank.slot, runner->spad );
    1229             : 
    1230             :   /* Initialize the blockhash queue and recent blockhashes sysvar from the input blockhash queue */
    1231           0 :   slot_bank->block_hash_queue.max_age   = FD_BLOCKHASH_QUEUE_MAX_ENTRIES; // Max age is fixed at 300
    1232           0 :   slot_bank->block_hash_queue.ages_root = NULL;
    1233           0 :   pool_mem = fd_spad_alloc( runner->spad, fd_hash_hash_age_pair_t_map_align(), fd_hash_hash_age_pair_t_map_footprint( FD_BLOCKHASH_QUEUE_MAX_ENTRIES+1UL ) );
    1234           0 :   slot_bank->block_hash_queue.ages_pool = fd_hash_hash_age_pair_t_map_join( fd_hash_hash_age_pair_t_map_new( pool_mem, FD_BLOCKHASH_QUEUE_MAX_ENTRIES+1UL ) );
    1235           0 :   slot_bank->block_hash_queue.last_hash = fd_valloc_malloc( fd_spad_virtual( runner->spad ), FD_HASH_ALIGN, FD_HASH_FOOTPRINT );
    1236             : 
    1237             :   /* TODO: Restore this from input */
    1238           0 :   pool_mem                              = fd_spad_alloc( runner->spad, fd_clock_timestamp_vote_t_map_align(), fd_clock_timestamp_vote_t_map_footprint( 10000UL ) );
    1239           0 :   slot_bank->timestamp_votes.votes_pool = fd_clock_timestamp_vote_t_map_join( fd_clock_timestamp_vote_t_map_new( pool_mem, 10000UL ) );
    1240           0 :   slot_bank->timestamp_votes.votes_root = NULL;
    1241             : 
    1242             :   /* TODO: We might need to load this in from the input. We also need to size this out for worst case, but this also blows up the memory requirement. */
    1243             :   /* Allocate all the memory for the rent fresh accounts lists */
    1244           0 :   slot_ctx->rent_fresh_accounts.partitions_root = NULL;
    1245           0 :   slot_ctx->rent_fresh_accounts.partitions_pool = fd_rent_fresh_accounts_partition_t_map_join(
    1246           0 :     fd_rent_fresh_accounts_partition_t_map_new(
    1247           0 :       fd_spad_alloc(
    1248           0 :         runner->spad,
    1249           0 :         fd_rent_fresh_accounts_partition_t_map_align(),
    1250           0 :         fd_rent_fresh_accounts_partition_t_map_footprint( 100UL * 2UL ) ), /* MAX_SLOTS_PER_EPOCH * 2 */
    1251           0 :         100UL * 2UL
    1252           0 :     )
    1253           0 :   );
    1254           0 :   for( ulong i = 0; i < 100UL * 2UL; i++ ) {
    1255           0 :     ulong partition = i;
    1256           0 :     fd_rent_fresh_accounts_partition_t_mapnode_t * new_node = fd_rent_fresh_accounts_partition_t_map_acquire(
    1257           0 :       slot_ctx->rent_fresh_accounts.partitions_pool
    1258           0 :     );
    1259           0 :     if( FD_UNLIKELY(( new_node == NULL )) ) {
    1260           0 :       FD_LOG_ERR(( "fd_rent_fresh_accounts_partition_t_map_acquire failed" ));
    1261           0 :     }
    1262             : 
    1263           0 :     new_node->elem.partition     = partition;
    1264           0 :     new_node->elem.accounts_root = NULL;
    1265           0 :     new_node->elem.accounts_pool = fd_pubkey_node_t_map_join( fd_pubkey_node_t_map_new(
    1266           0 :       fd_spad_alloc( runner->spad, fd_pubkey_node_t_map_align(), fd_pubkey_node_t_map_footprint( 100 ) ),
    1267           0 :       100
    1268           0 :     ) );
    1269           0 :     fd_rent_fresh_accounts_partition_t_map_insert(
    1270           0 :       slot_ctx->rent_fresh_accounts.partitions_pool,
    1271           0 :       &slot_ctx->rent_fresh_accounts.partitions_root,
    1272           0 :       new_node
    1273           0 :     );
    1274           0 :   }
    1275             : 
    1276             :   // Set genesis hash to {0}
    1277           0 :   fd_memset( &epoch_bank->genesis_hash, 0, sizeof(fd_hash_t) );
    1278           0 :   fd_memset( slot_bank->block_hash_queue.last_hash, 0, sizeof(fd_hash_t) );
    1279             : 
    1280           0 :   fd_funk_start_write( runner->funk );
    1281             : 
    1282             :   // Use the latest lamports per signature
    1283           0 :   fd_recent_block_hashes_global_t const * rbh_global = fd_sysvar_cache_recent_block_hashes( slot_ctx->sysvar_cache );
    1284           0 :   fd_recent_block_hashes_t rbh[1];
    1285           0 :   if( rbh_global ) {
    1286           0 :     fd_bincode_decode_ctx_t decode = { .wksp = fd_wksp_containing( runner->spad ) };
    1287           0 :     fd_recent_block_hashes_convert_global_to_local( rbh_global, rbh, &decode );
    1288           0 :   }
    1289             : 
    1290           0 :   if( rbh_global && !deq_fd_block_block_hash_entry_t_empty( rbh->hashes ) ) {
    1291           0 :     fd_block_block_hash_entry_t const * last = deq_fd_block_block_hash_entry_t_peek_head_const( rbh->hashes );
    1292           0 :     if( last && last->fee_calculator.lamports_per_signature!=0UL ) {
    1293           0 :       slot_bank->lamports_per_signature     = last->fee_calculator.lamports_per_signature;
    1294           0 :       slot_ctx->prev_lamports_per_signature = last->fee_calculator.lamports_per_signature;
    1295           0 :     }
    1296           0 :   }
    1297             : 
    1298             :   // Populate blockhash queue and recent blockhashes sysvar
    1299           0 :   for( ushort i=0; i<test_ctx->blockhash_queue_count; ++i ) {
    1300           0 :     fd_block_block_hash_entry_t blockhash_entry;
    1301           0 :     memcpy( &blockhash_entry.blockhash, test_ctx->blockhash_queue[i]->bytes, sizeof(fd_hash_t) );
    1302           0 :     slot_bank->poh = blockhash_entry.blockhash;
    1303           0 :     fd_sysvar_recent_hashes_update( slot_ctx, runner->spad );
    1304           0 :   }
    1305             : 
    1306             :   // Set the current poh from the input (we skip POH verification in this fuzzing target)
    1307           0 :   fd_memcpy( slot_ctx->slot_bank.poh.uc, test_ctx->slot_ctx.poh, FD_HASH_FOOTPRINT );
    1308             : 
    1309             :   /* Make a new funk transaction since we're done loading in accounts for context */
    1310           0 :   fd_funk_txn_xid_t fork_xid[1] = {0};
    1311           0 :   fork_xid[0] = fd_funk_generate_xid();
    1312           0 :   slot_ctx->funk_txn = fd_funk_txn_prepare( funk, slot_ctx->funk_txn, fork_xid, 1 );
    1313             : 
    1314             :   /* Calculate epoch account hash values. This sets epoch_bank.eah_{start_slot, stop_slot, interval} */
    1315           0 :   fd_calculate_epoch_accounts_hash_values( slot_ctx );
    1316             : 
    1317             :   /* Prepare raw transaction pointers and block / microblock infos */
    1318           0 :   ulong microblock_cnt = test_ctx->microblocks_count;
    1319             : 
    1320             :   // For fuzzing, we're using a single microblock batch that contains all microblocks
    1321           0 :   fd_runtime_block_info_t *    block_info       = fd_spad_alloc( runner->spad, alignof(fd_runtime_block_info_t), sizeof(fd_runtime_block_info_t) );
    1322           0 :   fd_microblock_batch_info_t * batch_info       = fd_spad_alloc( runner->spad, alignof(fd_microblock_batch_info_t), sizeof(fd_microblock_batch_info_t) );
    1323           0 :   fd_microblock_info_t *       microblock_infos = fd_spad_alloc( runner->spad, alignof(fd_microblock_info_t), microblock_cnt * sizeof(fd_microblock_info_t) );
    1324           0 :   fd_memset( block_info, 0, sizeof(fd_runtime_block_info_t) );
    1325           0 :   fd_memset( batch_info, 0, sizeof(fd_microblock_batch_info_t) );
    1326           0 :   fd_memset( microblock_infos, 0, microblock_cnt * sizeof(fd_microblock_info_t) );
    1327             : 
    1328           0 :   block_info->microblock_batch_cnt   = 1UL;
    1329           0 :   block_info->microblock_cnt         = microblock_cnt;
    1330           0 :   block_info->microblock_batch_infos = batch_info;
    1331             : 
    1332           0 :   batch_info->microblock_cnt         = microblock_cnt;
    1333           0 :   batch_info->microblock_infos       = microblock_infos;
    1334             : 
    1335           0 :   ulong batch_signature_cnt          = 0UL;
    1336           0 :   ulong batch_txn_cnt                = 0UL;
    1337           0 :   ulong batch_account_cnt            = 0UL;
    1338             : 
    1339           0 :   for( ulong i=0UL; i<microblock_cnt; i++ ) {
    1340           0 :     fd_exec_test_microblock_t const * input_microblock = &test_ctx->microblocks[i];
    1341           0 :     fd_microblock_info_t *            microblock_info  = &microblock_infos[i];
    1342           0 :     fd_microblock_hdr_t *             microblock_hdr   = fd_spad_alloc( runner->spad, alignof(fd_microblock_hdr_t), sizeof(fd_microblock_hdr_t) );
    1343           0 :     fd_memset( microblock_hdr, 0, sizeof(fd_microblock_hdr_t) );
    1344             : 
    1345           0 :     ulong txn_cnt       = input_microblock->txns_count;
    1346           0 :     ulong signature_cnt = 0UL;
    1347           0 :     ulong account_cnt   = 0UL;
    1348             : 
    1349           0 :     fd_txn_p_t * txn_ptrs = fd_spad_alloc( runner->spad, alignof(fd_txn_p_t), txn_cnt * sizeof(fd_txn_p_t) );
    1350             : 
    1351           0 :     for( ulong j=0UL; j<txn_cnt; j++ ) {
    1352           0 :       fd_txn_p_t * txn = &txn_ptrs[j];
    1353             : 
    1354           0 :       ushort _instr_count, _addr_table_cnt;
    1355           0 :       ulong msg_sz = _serialize_txn( txn->payload, &input_microblock->txns[j], &_instr_count, &_addr_table_cnt );
    1356             : 
    1357             :       // Reject any transactions over 1232 bytes
    1358           0 :       if( FD_UNLIKELY( msg_sz==ULONG_MAX ) ) {
    1359           0 :         return 1;
    1360           0 :       }
    1361           0 :       txn->payload_sz = msg_sz;
    1362             : 
    1363             :       // Reject any transactions that cannot be parsed
    1364           0 :       if( FD_UNLIKELY( !fd_txn_parse( txn->payload, msg_sz, TXN( txn ), NULL ) ) ) {
    1365           0 :         return 1;
    1366           0 :       }
    1367             : 
    1368           0 :       signature_cnt += TXN( txn )->signature_cnt;
    1369           0 :       account_cnt   += fd_txn_account_cnt( TXN( txn ), FD_TXN_ACCT_CAT_ALL );
    1370           0 :     }
    1371             : 
    1372           0 :     microblock_hdr->txn_cnt         = txn_cnt;
    1373           0 :     microblock_info->microblock.raw = (uchar *)microblock_hdr;
    1374             : 
    1375           0 :     microblock_info->signature_cnt  = signature_cnt;
    1376           0 :     microblock_info->account_cnt    = account_cnt;
    1377           0 :     microblock_info->txns           = txn_ptrs;
    1378             : 
    1379           0 :     batch_signature_cnt            += signature_cnt;
    1380           0 :     batch_txn_cnt                  += txn_cnt;
    1381           0 :     batch_account_cnt              += account_cnt;
    1382           0 :   }
    1383             : 
    1384           0 :   block_info->signature_cnt = batch_info->signature_cnt = batch_signature_cnt;
    1385           0 :   block_info->txn_cnt       = batch_info->txn_cnt       = batch_txn_cnt;
    1386           0 :   block_info->account_cnt   = batch_info->account_cnt   = batch_account_cnt;
    1387             : 
    1388           0 :   fd_funk_end_write( runner->funk );
    1389             : 
    1390             :   /* Initialize tpool and spad(s)
    1391             :     TODO: We should decide how many workers to use for the execution tpool. We might have a bunch of
    1392             :     transactions within a single block, but increasing the worker cnt increases the memory requirements by
    1393             :     1.28 GB per additional worker (for spad memory allocation). We also fuzz block execution using
    1394             :     multiple cores, so it may be possible to get away with only 1 worker. Additionally, Agave will more than
    1395             :     likely always be the execution speed bottleneck, so we can play around with numbers and see what yields
    1396             :     the best results. */
    1397           0 :   ulong worker_max = 2UL;
    1398           0 :   void * tpool_mem = fd_spad_alloc( runner->spad, FD_TPOOL_ALIGN, FD_TPOOL_FOOTPRINT( worker_max ) );
    1399           0 :   fd_tpool_t * tpool = fd_tpool_init( tpool_mem, worker_max );
    1400           0 :   fd_tpool_worker_push( tpool, 1UL, NULL, 0UL );
    1401             : 
    1402           0 :   fd_spad_t * runtime_spad = runner->spad;
    1403             : 
    1404             :   /* Format chunks of memory for the exec spads
    1405             :      TODO: This memory needs a better bound. */
    1406           0 :   fd_spad_t * exec_spads[2]  = { 0 };
    1407           0 :   ulong       exec_spads_cnt = 2UL;
    1408           0 :   ulong exec_spad_mem_max = 1UL << 30;
    1409           0 :   for( ulong i=0UL; i<worker_max; i++ ) {
    1410           0 :     void *      exec_spad_mem = fd_spad_alloc( runtime_spad, FD_SPAD_ALIGN, FD_SPAD_FOOTPRINT( exec_spad_mem_max ) );
    1411           0 :     fd_spad_t * exec_spad     = fd_spad_join( fd_spad_new( exec_spad_mem, exec_spad_mem_max ) );
    1412           0 :     exec_spads[i] = exec_spad;
    1413           0 :   }
    1414             : 
    1415             :   // Prepare. Execute. Finalize.
    1416           0 :   int res = 0UL;
    1417           0 :   FD_SPAD_FRAME_BEGIN( runtime_spad ) {
    1418           0 :     fd_rewards_recalculate_partitioned_rewards( slot_ctx, tpool, exec_spads, exec_spads_cnt, runtime_spad );
    1419             : 
    1420             :     /* Process new epoch may push a new spad frame onto the runtime spad. We should make sure this frame gets
    1421             :        cleared (if it was allocated) before executing the block. */
    1422           0 :     ulong spad_frame_ct = fd_spad_frame_used( runtime_spad );
    1423           0 :     fd_runtime_block_pre_execute_process_new_epoch( slot_ctx, tpool, exec_spads, exec_spads_cnt, runtime_spad );
    1424           0 :     while( fd_spad_frame_used( runtime_spad )>spad_frame_ct ) {
    1425           0 :       fd_spad_pop( runtime_spad );
    1426           0 :     }
    1427             : 
    1428           0 :     res = fd_runtime_block_execute_tpool( slot_ctx, NULL, block_info, tpool, exec_spads, exec_spads_cnt, runtime_spad );
    1429           0 :   } FD_SPAD_FRAME_END;
    1430             : 
    1431           0 :   fd_tpool_worker_pop( tpool );
    1432             : 
    1433           0 :   return res;
    1434           0 : }
    1435             : 
    1436             : void
    1437             : fd_exec_test_instr_context_destroy( fd_exec_instr_test_runner_t * runner,
    1438           0 :                                     fd_exec_instr_ctx_t *         ctx ) {
    1439           0 :   if( !ctx ) return;
    1440           0 :   fd_acc_mgr_t *        acc_mgr   = ctx->txn_ctx->acc_mgr;
    1441           0 :   fd_funk_txn_t *       funk_txn  = ctx->txn_ctx->funk_txn;
    1442             : 
    1443           0 :   fd_acc_mgr_delete( acc_mgr );
    1444             : 
    1445           0 :   fd_funk_start_write( runner->funk );
    1446           0 :   fd_funk_txn_cancel( runner->funk, funk_txn, 1 );
    1447           0 :   fd_funk_end_write( runner->funk );
    1448           0 : }
    1449             : 
    1450             : static void
    1451             : _txn_context_destroy( fd_exec_instr_test_runner_t * runner,
    1452           0 :                       fd_exec_slot_ctx_t *          slot_ctx ) {
    1453           0 :   if( !slot_ctx ) return; // This shouldn't be false either
    1454           0 :   fd_acc_mgr_t *        acc_mgr   = slot_ctx->acc_mgr;
    1455           0 :   fd_funk_txn_t *       funk_txn  = slot_ctx->funk_txn;
    1456             : 
    1457           0 :   fd_acc_mgr_delete( acc_mgr );
    1458             : 
    1459           0 :   fd_funk_start_write( runner->funk );
    1460           0 :   fd_funk_txn_cancel( runner->funk, funk_txn, 1 );
    1461           0 :   fd_funk_end_write( runner->funk );
    1462           0 : }
    1463             : 
    1464             : static void
    1465             : _block_context_destroy( fd_exec_instr_test_runner_t * runner,
    1466             :                         fd_exec_slot_ctx_t *            slot_ctx,
    1467             :                         fd_wksp_t *                     wksp,
    1468           0 :                         fd_alloc_t *                    alloc ) {
    1469           0 :   if( !slot_ctx ) return; // This shouldn't be false either
    1470           0 :   fd_acc_mgr_t * acc_mgr = slot_ctx->acc_mgr;
    1471             : 
    1472           0 :   fd_acc_mgr_delete( acc_mgr );
    1473             : 
    1474           0 :   fd_wksp_free_laddr( fd_alloc_delete( fd_alloc_leave( alloc ) ) );
    1475           0 :   fd_wksp_detach( wksp );
    1476             : 
    1477           0 :   fd_funk_start_write( runner->funk );
    1478           0 :   fd_funk_txn_cancel_all( runner->funk, 1 );
    1479           0 :   fd_funk_end_write( runner->funk );
    1480           0 : }
    1481             : 
    1482             : static fd_sbpf_syscalls_t *
    1483             : lookup_syscall_func( fd_sbpf_syscalls_t *syscalls,
    1484             :                      const char *syscall_name,
    1485           0 :                      size_t len) {
    1486           0 :   ulong i;
    1487             : 
    1488           0 :   if (!syscall_name) return NULL;
    1489             : 
    1490           0 :   for (i = 0; i < fd_sbpf_syscalls_slot_cnt(); ++i) {
    1491           0 :     if (!fd_sbpf_syscalls_key_inval(syscalls[i].key) && syscalls[i].name && strlen(syscalls[i].name) == len) {
    1492           0 :       if (!memcmp(syscalls[i].name, syscall_name, len)) {
    1493           0 :         return syscalls + i;
    1494           0 :       }
    1495           0 :     }
    1496           0 :   }
    1497             : 
    1498           0 :   return NULL;
    1499           0 : }
    1500             : 
    1501             : ulong
    1502             : fd_exec_instr_test_run( fd_exec_instr_test_runner_t * runner,
    1503             :                         void const *                  input_,
    1504             :                         void **                       output_,
    1505             :                         void *                        output_buf,
    1506           0 :                         ulong                         output_bufsz ) {
    1507           0 :   fd_exec_test_instr_context_t const * input  = fd_type_pun_const( input_ );
    1508           0 :   fd_exec_test_instr_effects_t **      output = fd_type_pun( output_ );
    1509             : 
    1510           0 :   FD_SPAD_FRAME_BEGIN( runner->spad ) {
    1511             : 
    1512             :   /* Convert the Protobuf inputs to a fd_exec context */
    1513           0 :   fd_exec_instr_ctx_t ctx[1];
    1514           0 :   if( !fd_exec_test_instr_context_create( runner, ctx, input, false ) ) {
    1515           0 :     fd_exec_test_instr_context_destroy( runner, ctx );
    1516           0 :     return 0UL;
    1517           0 :   }
    1518             : 
    1519           0 :   fd_instr_info_t * instr = (fd_instr_info_t *) ctx->instr;
    1520             : 
    1521             :   /* Execute the test */
    1522           0 :   int exec_result = fd_execute_instr(ctx->txn_ctx, instr);
    1523             : 
    1524             :   /* Allocate space to capture outputs */
    1525             : 
    1526           0 :   ulong output_end = (ulong)output_buf + output_bufsz;
    1527           0 :   FD_SCRATCH_ALLOC_INIT( l, output_buf );
    1528             : 
    1529           0 :   fd_exec_test_instr_effects_t * effects =
    1530           0 :     FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_exec_test_instr_effects_t),
    1531           0 :                                 sizeof (fd_exec_test_instr_effects_t) );
    1532           0 :   if( FD_UNLIKELY( _l > output_end ) ) {
    1533           0 :     fd_exec_test_instr_context_destroy( runner, ctx );
    1534           0 :     return 0UL;
    1535           0 :   }
    1536           0 :   fd_memset( effects, 0, sizeof(fd_exec_test_instr_effects_t) );
    1537             : 
    1538             :   /* Capture error code */
    1539             : 
    1540           0 :   effects->result   = -exec_result;
    1541           0 :   effects->cu_avail = ctx->txn_ctx->compute_meter;
    1542             : 
    1543           0 :   if( exec_result == FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR ) {
    1544           0 :     effects->custom_err     = ctx->txn_ctx->custom_err;
    1545           0 :   }
    1546             : 
    1547             :   /* Allocate space for captured accounts */
    1548           0 :   ulong modified_acct_cnt = ctx->txn_ctx->accounts_cnt;
    1549             : 
    1550           0 :   fd_exec_test_acct_state_t * modified_accts =
    1551           0 :     FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_exec_test_acct_state_t),
    1552           0 :                                 sizeof (fd_exec_test_acct_state_t) * modified_acct_cnt );
    1553           0 :   if( FD_UNLIKELY( _l > output_end ) ) {
    1554           0 :     fd_exec_test_instr_context_destroy( runner, ctx );
    1555           0 :     return 0;
    1556           0 :   }
    1557           0 :   effects->modified_accounts       = modified_accts;
    1558           0 :   effects->modified_accounts_count = 0UL;
    1559             : 
    1560             :   /* Capture borrowed accounts */
    1561             : 
    1562           0 :   for( ulong j=0UL; j < ctx->txn_ctx->accounts_cnt; j++ ) {
    1563           0 :     fd_txn_account_t * acc = &ctx->txn_ctx->accounts[j];
    1564           0 :     if( !acc->const_meta ) {
    1565           0 :       continue;
    1566           0 :     }
    1567             : 
    1568           0 :     ulong modified_idx = effects->modified_accounts_count;
    1569           0 :     assert( modified_idx < modified_acct_cnt );
    1570             : 
    1571           0 :     fd_exec_test_acct_state_t * out_acct = &effects->modified_accounts[ modified_idx ];
    1572           0 :     memset( out_acct, 0, sizeof(fd_exec_test_acct_state_t) );
    1573             :     /* Copy over account content */
    1574             : 
    1575           0 :     memcpy( out_acct->address, acc->pubkey, sizeof(fd_pubkey_t) );
    1576           0 :     out_acct->lamports     = acc->const_meta->info.lamports;
    1577           0 :     if( acc->const_meta->dlen>0UL ) {
    1578           0 :       out_acct->data =
    1579           0 :         FD_SCRATCH_ALLOC_APPEND( l, alignof(pb_bytes_array_t),
    1580           0 :                                     PB_BYTES_ARRAY_T_ALLOCSIZE( acc->const_meta->dlen ) );
    1581           0 :       if( FD_UNLIKELY( _l > output_end ) ) {
    1582           0 :         fd_exec_test_instr_context_destroy( runner, ctx );
    1583           0 :         return 0UL;
    1584           0 :       }
    1585           0 :       out_acct->data->size = (pb_size_t)acc->const_meta->dlen;
    1586           0 :       fd_memcpy( out_acct->data->bytes, acc->const_data, acc->const_meta->dlen );
    1587           0 :     }
    1588             : 
    1589           0 :     out_acct->executable     = acc->const_meta->info.executable;
    1590           0 :     out_acct->rent_epoch     = acc->const_meta->info.rent_epoch;
    1591           0 :     memcpy( out_acct->owner, acc->const_meta->info.owner, sizeof(fd_pubkey_t) );
    1592             : 
    1593           0 :     effects->modified_accounts_count++;
    1594           0 :   }
    1595             : 
    1596             :   /* Capture return data */
    1597           0 :   fd_txn_return_data_t * return_data = &ctx->txn_ctx->return_data;
    1598           0 :   if( return_data->len>0UL ) {
    1599           0 :     effects->return_data = FD_SCRATCH_ALLOC_APPEND(l, alignof(pb_bytes_array_t),
    1600           0 :                                 PB_BYTES_ARRAY_T_ALLOCSIZE( return_data->len ) );
    1601           0 :     if( FD_UNLIKELY( _l > output_end ) ) {
    1602           0 :       fd_exec_test_instr_context_destroy( runner, ctx );
    1603           0 :       return 0UL;
    1604           0 :     }
    1605           0 :     effects->return_data->size = (pb_size_t)return_data->len;
    1606           0 :     fd_memcpy( effects->return_data->bytes, return_data->data, return_data->len );
    1607           0 :   }
    1608             : 
    1609           0 :   ulong actual_end = FD_SCRATCH_ALLOC_FINI( l, 1UL );
    1610           0 :   fd_exec_test_instr_context_destroy( runner, ctx );
    1611             : 
    1612           0 :   *output = effects;
    1613           0 :   return actual_end - (ulong)output_buf;
    1614             : 
    1615           0 :   } FD_SPAD_FRAME_END;
    1616           0 : }
    1617             : 
    1618             : ulong
    1619             : fd_exec_block_test_run( fd_exec_instr_test_runner_t * runner,
    1620             :                         void const *                  input_,
    1621             :                         void **                       output_,
    1622             :                         void *                        output_buf,
    1623           0 :                         ulong                         output_bufsz ) {
    1624           0 :   fd_exec_test_block_context_t const * input  = fd_type_pun_const( input_ );
    1625           0 :   fd_exec_test_block_effects_t **      output = fd_type_pun( output_ );
    1626             : 
    1627           0 :   FD_SPAD_FRAME_BEGIN( runner->spad ) {
    1628             :     /* Initialize memory */
    1629           0 :     fd_wksp_t *           wksp          = fd_wksp_attach( "wksp" );
    1630           0 :     fd_alloc_t *          alloc         = fd_alloc_join( fd_alloc_new( fd_wksp_alloc_laddr( wksp, fd_alloc_align(), fd_alloc_footprint(), 2 ), 2 ), 0 );
    1631           0 :     uchar *               slot_ctx_mem  = fd_spad_alloc( runner->spad, FD_EXEC_SLOT_CTX_ALIGN,  FD_EXEC_SLOT_CTX_FOOTPRINT );
    1632           0 :     fd_exec_slot_ctx_t *  slot_ctx      = fd_exec_slot_ctx_join ( fd_exec_slot_ctx_new ( slot_ctx_mem, runner->spad ) );
    1633             : 
    1634           0 :     int res = _block_context_create_and_exec( runner, slot_ctx, input );
    1635           0 :     if( res>0 ) {
    1636           0 :       _block_context_destroy( runner, slot_ctx, wksp, alloc );
    1637           0 :       return 0;
    1638           0 :     }
    1639             : 
    1640             :     /* Start saving block exec results */
    1641           0 :     FD_SCRATCH_ALLOC_INIT( l, output_buf );
    1642           0 :     ulong output_end = (ulong)output_buf + output_bufsz;
    1643             : 
    1644           0 :     fd_exec_test_block_effects_t * effects =
    1645           0 :     FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_exec_test_block_effects_t),
    1646           0 :                                   sizeof (fd_exec_test_block_effects_t) );
    1647           0 :     if( FD_UNLIKELY( _l > output_end ) ) {
    1648           0 :       abort();
    1649           0 :     }
    1650           0 :     fd_memset( effects, 0, sizeof(fd_exec_test_block_effects_t) );
    1651             : 
    1652             :     /* Capture capitalization */
    1653           0 :     effects->slot_capitalization = slot_ctx->slot_bank.capitalization;
    1654             : 
    1655             :     /* Capture hashes */
    1656           0 :     uchar out_lt_hash[32];
    1657           0 :     fd_lthash_hash( (fd_lthash_value_t const *)slot_ctx->slot_bank.lthash.lthash, out_lt_hash );
    1658           0 :     fd_memcpy( effects->bank_hash, slot_ctx->slot_bank.banks_hash.hash, sizeof(fd_hash_t) );
    1659           0 :     fd_memcpy( effects->lt_hash, out_lt_hash, sizeof(fd_hash_t) );
    1660           0 :     fd_memcpy( effects->account_delta_hash, slot_ctx->account_delta_hash.hash, sizeof(fd_hash_t) );
    1661             : 
    1662             :     /* Capture accounts. Since the only input accounts list comes from the input transactions, we have to iterate through
    1663             :        all input transactions, gather the account keys in order, and skip any duplicate accounts. */
    1664             :     // TODO: implement me
    1665             : 
    1666           0 :     ulong actual_end = FD_SCRATCH_ALLOC_FINI( l, 1UL );
    1667           0 :     _block_context_destroy( runner, slot_ctx, wksp, alloc );
    1668             : 
    1669           0 :     *output = effects;
    1670           0 :     return actual_end - (ulong)output_buf;
    1671           0 :   } FD_SPAD_FRAME_END;
    1672           0 : }
    1673             : 
    1674             : 
    1675             : ulong
    1676             : fd_exec_txn_test_run( fd_exec_instr_test_runner_t * runner, // Runner only contains funk instance, so we can borrow instr test runner
    1677             :                       void const *                  input_,
    1678             :                       void **                       output_,
    1679             :                       void *                        output_buf,
    1680           0 :                       ulong                         output_bufsz ) {
    1681           0 :   fd_exec_test_txn_context_t const * input  = fd_type_pun_const( input_ );
    1682           0 :   fd_exec_test_txn_result_t **       output = fd_type_pun( output_ );
    1683             : 
    1684           0 :   FD_SPAD_FRAME_BEGIN( runner->spad ) {
    1685             : 
    1686             :     /* Initialize memory */
    1687           0 :     uchar *               slot_ctx_mem = fd_spad_alloc( runner->spad, FD_EXEC_SLOT_CTX_ALIGN,  FD_EXEC_SLOT_CTX_FOOTPRINT  );
    1688           0 :     fd_exec_slot_ctx_t *  slot_ctx     = fd_exec_slot_ctx_join( fd_exec_slot_ctx_new( slot_ctx_mem, runner->spad ) );
    1689             : 
    1690             :     /* Create and exec transaction */
    1691           0 :     fd_execute_txn_task_info_t * task_info = _txn_context_create_and_exec( runner, slot_ctx, input );
    1692           0 :     if( task_info == NULL ) {
    1693           0 :       _txn_context_destroy( runner, slot_ctx );
    1694           0 :       return 0UL;
    1695           0 :     }
    1696           0 :     fd_exec_txn_ctx_t * txn_ctx = task_info->txn_ctx;
    1697             : 
    1698           0 :     int exec_res = task_info->exec_res;
    1699             : 
    1700             :     /* Start saving txn exec results */
    1701           0 :     FD_SCRATCH_ALLOC_INIT( l, output_buf );
    1702           0 :     ulong output_end = (ulong)output_buf + output_bufsz;
    1703             : 
    1704           0 :     fd_exec_test_txn_result_t * txn_result =
    1705           0 :     FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_exec_test_txn_result_t),
    1706           0 :                                   sizeof (fd_exec_test_txn_result_t) );
    1707           0 :     if( FD_UNLIKELY( _l > output_end ) ) {
    1708           0 :       abort();
    1709           0 :     }
    1710           0 :     fd_memset( txn_result, 0, sizeof(fd_exec_test_txn_result_t) );
    1711             : 
    1712             :     /* Capture basic results fields */
    1713           0 :     txn_result->executed                          = task_info->txn->flags & FD_TXN_P_FLAGS_EXECUTE_SUCCESS;
    1714           0 :     txn_result->sanitization_error                = !( task_info->txn->flags & FD_TXN_P_FLAGS_SANITIZE_SUCCESS );
    1715           0 :     txn_result->has_resulting_state               = false;
    1716           0 :     txn_result->resulting_state.acct_states_count = 0;
    1717           0 :     txn_result->is_ok                             = !exec_res;
    1718           0 :     txn_result->status                            = (uint32_t) -exec_res;
    1719           0 :     txn_result->instruction_error                 = 0;
    1720           0 :     txn_result->instruction_error_index           = 0;
    1721           0 :     txn_result->custom_error                      = 0;
    1722           0 :     txn_result->executed_units                    = txn_ctx->compute_unit_limit - txn_ctx->compute_meter;
    1723           0 :     txn_result->has_fee_details                   = false;
    1724             : 
    1725           0 :     if( txn_result->sanitization_error ) {
    1726           0 :       if( exec_res==FD_RUNTIME_TXN_ERR_INSTRUCTION_ERROR ) {
    1727             :       /* If exec_res was an instruction error and we have a sanitization error, it was a precompile error */
    1728           0 :         txn_result->instruction_error       = (uint32_t) -txn_ctx->exec_err;
    1729           0 :         txn_result->instruction_error_index = (uint32_t) txn_ctx->instr_err_idx;
    1730             : 
    1731             :         /*
    1732             :         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.
    1733             :         For now, only precompiles throw custom error codes, so we can ignore all custom error codes thrown in the sanitization phase. If this changes,
    1734             :         this logic will have to be revisited.
    1735             : 
    1736             :         if( task_info->txn_ctx->exec_err == FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR ) {
    1737             :           txn_result->custom_error = txn_ctx->custom_err;
    1738             :         }
    1739             :         */
    1740           0 :       }
    1741             : 
    1742           0 :       ulong actual_end = FD_SCRATCH_ALLOC_FINI( l, 1UL );
    1743           0 :       _txn_context_destroy( runner, slot_ctx );
    1744             : 
    1745           0 :       *output = txn_result;
    1746           0 :       return actual_end - (ulong)output_buf;
    1747             : 
    1748           0 :     } else {
    1749             :       /* Capture the instruction error code */
    1750           0 :       if( exec_res==FD_RUNTIME_TXN_ERR_INSTRUCTION_ERROR ) {
    1751           0 :         int instr_err_idx                   = txn_ctx->instr_err_idx;
    1752           0 :         int program_id_idx                  = txn_ctx->instr_infos[instr_err_idx].program_id;
    1753             : 
    1754           0 :         txn_result->instruction_error       = (uint32_t) -txn_ctx->exec_err;
    1755           0 :         txn_result->instruction_error_index = (uint32_t) instr_err_idx;
    1756             : 
    1757             :         /* If the exec err was a custom instr error and came from a precompile instruction, don't capture the custom error code. */
    1758           0 :         if( txn_ctx->exec_err==FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR &&
    1759           0 :             fd_executor_lookup_native_precompile_program( &txn_ctx->accounts[ program_id_idx ] )==NULL ) {
    1760           0 :           txn_result->custom_error = txn_ctx->custom_err;
    1761           0 :         }
    1762           0 :       }
    1763           0 :     }
    1764             : 
    1765           0 :     txn_result->has_fee_details                = true;
    1766           0 :     txn_result->fee_details.transaction_fee    = slot_ctx->slot_bank.collected_execution_fees;
    1767           0 :     txn_result->fee_details.prioritization_fee = slot_ctx->slot_bank.collected_priority_fees;
    1768             : 
    1769             :     /* Rent is only collected on successfully loaded transactions */
    1770           0 :     txn_result->rent                           = txn_ctx->collected_rent;
    1771             : 
    1772           0 :     if( txn_ctx->return_data.len > 0 ) {
    1773           0 :       txn_result->return_data = FD_SCRATCH_ALLOC_APPEND( l, alignof(pb_bytes_array_t),
    1774           0 :                                       PB_BYTES_ARRAY_T_ALLOCSIZE( txn_ctx->return_data.len ) );
    1775           0 :       if( FD_UNLIKELY( _l > output_end ) ) {
    1776           0 :         abort();
    1777           0 :       }
    1778             : 
    1779           0 :       txn_result->return_data->size = (pb_size_t)txn_ctx->return_data.len;
    1780           0 :       fd_memcpy( txn_result->return_data->bytes, txn_ctx->return_data.data, txn_ctx->return_data.len );
    1781           0 :     }
    1782             : 
    1783             :     /* Allocate space for captured accounts */
    1784           0 :     ulong modified_acct_cnt = txn_ctx->accounts_cnt;
    1785             : 
    1786           0 :     txn_result->has_resulting_state         = true;
    1787           0 :     txn_result->resulting_state.acct_states =
    1788           0 :       FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_exec_test_acct_state_t),
    1789           0 :                                   sizeof (fd_exec_test_acct_state_t) * modified_acct_cnt );
    1790           0 :     if( FD_UNLIKELY( _l > output_end ) ) {
    1791           0 :       abort();
    1792           0 :     }
    1793             : 
    1794             :     /* Capture borrowed accounts */
    1795           0 :     for( ulong j=0UL; j < txn_ctx->accounts_cnt; j++ ) {
    1796           0 :       fd_txn_account_t * acc = &txn_ctx->accounts[j];
    1797             : 
    1798             :       /* For fees-only transactions, only save the fee payer (and potentially the nonce) only */
    1799           0 :       if( task_info->txn->flags & FD_TXN_P_FLAGS_FEES_ONLY ) {
    1800           0 :         if( j!=FD_FEE_PAYER_TXN_IDX && j!=task_info->txn_ctx->nonce_account_idx_in_txn ) {
    1801           0 :           continue;
    1802           0 :         }
    1803           0 :       }
    1804             : 
    1805           0 :       if( !( fd_txn_account_is_writable_idx( txn_ctx, (int)j ) || j==FD_FEE_PAYER_TXN_IDX ) ) continue;
    1806           0 :       assert( acc->meta );
    1807             : 
    1808           0 :       ulong modified_idx = txn_result->resulting_state.acct_states_count;
    1809           0 :       assert( modified_idx < modified_acct_cnt );
    1810             : 
    1811           0 :       fd_exec_test_acct_state_t * out_acct = &txn_result->resulting_state.acct_states[ modified_idx ];
    1812           0 :       memset( out_acct, 0, sizeof(fd_exec_test_acct_state_t) );
    1813             :       /* Copy over account content */
    1814             : 
    1815           0 :       memcpy( out_acct->address, acc->pubkey, sizeof(fd_pubkey_t) );
    1816             : 
    1817           0 :       out_acct->lamports = acc->const_meta->info.lamports;
    1818             : 
    1819           0 :       if( acc->const_meta->dlen > 0 ) {
    1820           0 :         out_acct->data =
    1821           0 :           FD_SCRATCH_ALLOC_APPEND( l, alignof(pb_bytes_array_t),
    1822           0 :                                       PB_BYTES_ARRAY_T_ALLOCSIZE( acc->const_meta->dlen ) );
    1823           0 :         if( FD_UNLIKELY( _l > output_end ) ) {
    1824           0 :           abort();
    1825           0 :         }
    1826           0 :         out_acct->data->size = (pb_size_t)acc->const_meta->dlen;
    1827           0 :         fd_memcpy( out_acct->data->bytes, acc->const_data, acc->const_meta->dlen );
    1828           0 :       }
    1829             : 
    1830           0 :       out_acct->executable     = acc->const_meta->info.executable;
    1831           0 :       out_acct->rent_epoch     = acc->const_meta->info.rent_epoch;
    1832           0 :       memcpy( out_acct->owner, acc->const_meta->info.owner, sizeof(fd_pubkey_t) );
    1833             : 
    1834           0 :       txn_result->resulting_state.acct_states_count++;
    1835           0 :     }
    1836             : 
    1837           0 :     ulong actual_end = FD_SCRATCH_ALLOC_FINI( l, 1UL );
    1838           0 :     _txn_context_destroy( runner, slot_ctx );
    1839             : 
    1840           0 :     *output = txn_result;
    1841           0 :     return actual_end - (ulong)output_buf;
    1842           0 :   } FD_SPAD_FRAME_END;
    1843           0 : }
    1844             : 
    1845             : 
    1846             : ulong
    1847             : fd_sbpf_program_load_test_run( fd_exec_instr_test_runner_t * runner,
    1848             :                                void const *                  input_,
    1849             :                                void **                       output_,
    1850             :                                void *                        output_buf,
    1851           0 :                                ulong                         output_bufsz ) {
    1852           0 :   fd_exec_test_elf_loader_ctx_t const * input  = fd_type_pun_const( input_ );
    1853           0 :   fd_exec_test_elf_loader_effects_t **  output = fd_type_pun( output_ );
    1854             : 
    1855           0 :   fd_sbpf_elf_info_t info;
    1856           0 :   fd_valloc_t valloc = fd_spad_virtual( runner->spad );
    1857             : 
    1858           0 :   if ( FD_UNLIKELY( !input->has_elf || !input->elf.data ) ){
    1859           0 :     return 0UL;
    1860           0 :   }
    1861             : 
    1862           0 :   ulong elf_sz = input->elf_sz;
    1863           0 :   void const * _bin;
    1864             : 
    1865             :   /* elf_sz will be passed as arguments to elf loader functions.
    1866             :      pb decoder allocates memory for elf.data based on its actual size,
    1867             :      not elf_sz !.
    1868             :      If elf_sz is larger than the size of actual elf data, this may result
    1869             :      in out-of-bounds accesses which will upset ASAN (however intentional).
    1870             :      So in this case we just copy the data into a memory region of elf_sz bytes
    1871             : 
    1872             :      ! The decoupling of elf_sz and the actual binary size is intentional to test
    1873             :       underflow/overflow behavior */
    1874           0 :   if ( elf_sz > input->elf.data->size ){
    1875           0 :     void * tmp = fd_valloc_malloc( valloc, 1UL, elf_sz );
    1876           0 :     if ( FD_UNLIKELY( !tmp ) ){
    1877           0 :       return 0UL;
    1878           0 :     }
    1879           0 :     fd_memcpy( tmp, input->elf.data->bytes, input->elf.data->size );
    1880           0 :     _bin = tmp;
    1881           0 :   } else {
    1882           0 :     _bin = input->elf.data->bytes;
    1883           0 :   }
    1884             : 
    1885             :   // Allocate space for captured effects
    1886           0 :   ulong output_end = (ulong)output_buf + output_bufsz;
    1887           0 :   FD_SCRATCH_ALLOC_INIT( l, output_buf );
    1888             : 
    1889           0 :   fd_exec_test_elf_loader_effects_t * elf_effects =
    1890           0 :     FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_exec_test_elf_loader_effects_t),
    1891           0 :                                 sizeof (fd_exec_test_elf_loader_effects_t) );
    1892           0 :   if( FD_UNLIKELY( _l > output_end ) ) {
    1893             :     /* return 0 on fuzz-specific failures */
    1894           0 :     return 0UL;
    1895           0 :   }
    1896           0 :   fd_memset( elf_effects, 0, sizeof(fd_exec_test_elf_loader_effects_t) );
    1897             : 
    1898             :   /* wrap the loader code in do-while(0) block so that we can exit
    1899             :      immediately if execution fails at any point */
    1900             : 
    1901           0 :   do{
    1902             : 
    1903           0 :     if( FD_UNLIKELY( !fd_sbpf_elf_peek( &info, _bin, elf_sz, input->deploy_checks, FD_SBPF_V0, FD_SBPF_V3 ) ) ) {
    1904             :       /* return incomplete effects on execution failures */
    1905           0 :       break;
    1906           0 :     }
    1907             : 
    1908           0 :     void* rodata = fd_valloc_malloc( valloc, FD_SBPF_PROG_RODATA_ALIGN, info.rodata_footprint );
    1909           0 :     FD_TEST( rodata );
    1910             : 
    1911           0 :     fd_sbpf_program_t * prog = fd_sbpf_program_new( fd_valloc_malloc( valloc, fd_sbpf_program_align(), fd_sbpf_program_footprint( &info ) ), &info, rodata );
    1912           0 :     FD_TEST( prog );
    1913             : 
    1914           0 :     fd_sbpf_syscalls_t * syscalls = fd_sbpf_syscalls_new( fd_valloc_malloc( valloc, fd_sbpf_syscalls_align(), fd_sbpf_syscalls_footprint() ));
    1915           0 :     FD_TEST( syscalls );
    1916             : 
    1917           0 :     fd_vm_syscall_register_all( syscalls, 0 );
    1918             : 
    1919           0 :     int res = fd_sbpf_program_load( prog, _bin, elf_sz, syscalls, input->deploy_checks );
    1920           0 :     if( FD_UNLIKELY( res ) ) {
    1921           0 :       break;
    1922           0 :     }
    1923             : 
    1924           0 :     fd_memset( elf_effects, 0, sizeof(fd_exec_test_elf_loader_effects_t) );
    1925           0 :     elf_effects->rodata_sz = prog->rodata_sz;
    1926             : 
    1927             :     // Load rodata section
    1928           0 :     elf_effects->rodata = FD_SCRATCH_ALLOC_APPEND(l, 8UL, PB_BYTES_ARRAY_T_ALLOCSIZE( prog->rodata_sz ));
    1929           0 :     if( FD_UNLIKELY( _l > output_end ) ) {
    1930           0 :       return 0UL;
    1931           0 :     }
    1932           0 :     elf_effects->rodata->size = (pb_size_t) prog->rodata_sz;
    1933           0 :     fd_memcpy( &(elf_effects->rodata->bytes), prog->rodata, prog->rodata_sz );
    1934             : 
    1935           0 :     elf_effects->text_cnt = prog->text_cnt;
    1936           0 :     elf_effects->text_off = prog->text_off;
    1937             : 
    1938           0 :     elf_effects->entry_pc = prog->entry_pc;
    1939             : 
    1940             : 
    1941           0 :     pb_size_t calldests_sz = (pb_size_t) fd_sbpf_calldests_cnt( prog->calldests);
    1942           0 :     elf_effects->calldests_count = calldests_sz;
    1943           0 :     elf_effects->calldests = FD_SCRATCH_ALLOC_APPEND(l, 8UL, calldests_sz * sizeof(uint64_t));
    1944           0 :     if( FD_UNLIKELY( _l > output_end ) ) {
    1945           0 :       return 0UL;
    1946           0 :     }
    1947             : 
    1948           0 :     ulong i = 0;
    1949           0 :     for(ulong target_pc = fd_sbpf_calldests_const_iter_init(prog->calldests); !fd_sbpf_calldests_const_iter_done(target_pc);
    1950           0 :     target_pc = fd_sbpf_calldests_const_iter_next(prog->calldests, target_pc)) {
    1951           0 :       elf_effects->calldests[i] = target_pc;
    1952           0 :       ++i;
    1953           0 :     }
    1954           0 :   } while(0);
    1955             : 
    1956           0 :   ulong actual_end = FD_SCRATCH_ALLOC_FINI( l, 1UL );
    1957             : 
    1958           0 :   *output = elf_effects;
    1959           0 :   return actual_end - (ulong) output_buf;
    1960           0 : }
    1961             : 
    1962             : static fd_exec_test_instr_effects_t const * cpi_exec_effects = NULL;
    1963             : 
    1964             : ulong
    1965             : fd_exec_vm_syscall_test_run( fd_exec_instr_test_runner_t * runner,
    1966             :                              void const *                  input_,
    1967             :                              void **                       output_,
    1968             :                              void *                        output_buf,
    1969           0 :                              ulong                         output_bufsz ) {
    1970           0 :   fd_exec_test_syscall_context_t const * input =  fd_type_pun_const( input_ );
    1971           0 :   fd_exec_test_syscall_effects_t **      output = fd_type_pun( output_ );
    1972             : 
    1973             :   /* Create execution context */
    1974           0 :   const fd_exec_test_instr_context_t * input_instr_ctx = &input->instr_ctx;
    1975           0 :   fd_exec_instr_ctx_t ctx[1];
    1976             :   // Skip extra checks for non-CPI syscalls
    1977           0 :   int is_cpi            = !strncmp( (const char *)input->syscall_invocation.function_name.bytes, "sol_invoke_signed", 17 );
    1978           0 :   int skip_extra_checks = !is_cpi;
    1979             : 
    1980           0 :   if( !fd_exec_test_instr_context_create( runner, ctx, input_instr_ctx, skip_extra_checks ) )
    1981           0 :     goto error;
    1982           0 :   fd_valloc_t valloc = fd_spad_virtual( runner->spad );
    1983           0 :   fd_spad_t * spad = fd_exec_instr_test_runner_get_spad( runner );
    1984             : 
    1985           0 :   if (is_cpi) {
    1986           0 :     ctx->txn_ctx->instr_info_cnt = 1;
    1987             : 
    1988             :     /* Need to setup txn_descriptor for txn account write checks (see fd_txn_account_is_writable_idx)
    1989             :        FIXME: this could probably go in fd_exec_test_instr_context_create? */
    1990           0 :     fd_txn_t * txn_descriptor = (fd_txn_t *)fd_spad_alloc_debug( spad, fd_txn_align(), fd_txn_footprint( ctx->txn_ctx->instr_info_cnt, 0UL ) );
    1991           0 :     txn_descriptor->transaction_version = FD_TXN_V0;
    1992           0 :     txn_descriptor->acct_addr_cnt = (ushort)ctx->txn_ctx->accounts_cnt;
    1993             : 
    1994           0 :     ctx->txn_ctx->txn_descriptor = txn_descriptor;
    1995           0 :   }
    1996             : 
    1997           0 :   ctx->txn_ctx->instr_trace[0].instr_info = (fd_instr_info_t *)ctx->instr;
    1998           0 :   ctx->txn_ctx->instr_trace[0].stack_height = 1;
    1999             : 
    2000             :   /* Capture outputs */
    2001           0 :   ulong output_end = (ulong)output_buf + output_bufsz;
    2002           0 :   FD_SCRATCH_ALLOC_INIT( l, output_buf );
    2003           0 :   fd_exec_test_syscall_effects_t * effects =
    2004           0 :     FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_exec_test_syscall_effects_t),
    2005           0 :                                 sizeof (fd_exec_test_syscall_effects_t) );
    2006           0 :   if( FD_UNLIKELY( _l > output_end ) ) {
    2007           0 :     goto error;
    2008           0 :   }
    2009             : 
    2010           0 :   if (input->vm_ctx.return_data.program_id && input->vm_ctx.return_data.program_id->size == sizeof(fd_pubkey_t)) {
    2011           0 :     fd_memcpy( ctx->txn_ctx->return_data.program_id.uc, input->vm_ctx.return_data.program_id->bytes, sizeof(fd_pubkey_t) );
    2012           0 :     ctx->txn_ctx->return_data.len = input->vm_ctx.return_data.data->size;
    2013           0 :     fd_memcpy( ctx->txn_ctx->return_data.data, input->vm_ctx.return_data.data->bytes, ctx->txn_ctx->return_data.len );
    2014           0 :   }
    2015             : 
    2016           0 :   *effects = (fd_exec_test_syscall_effects_t) FD_EXEC_TEST_SYSCALL_EFFECTS_INIT_ZERO;
    2017             : 
    2018             :   /* Set up the VM instance */
    2019           0 :   fd_sha256_t _sha[1];
    2020           0 :   fd_sha256_t * sha = fd_sha256_join( fd_sha256_new( _sha ) );
    2021           0 :   fd_sbpf_syscalls_t * syscalls = fd_sbpf_syscalls_new( fd_valloc_malloc( valloc, fd_sbpf_syscalls_align(), fd_sbpf_syscalls_footprint() ) );
    2022           0 :   fd_vm_syscall_register_all( syscalls, 0 );
    2023             : 
    2024             :   /* Pull out the memory regions */
    2025           0 :   if( !input->has_vm_ctx ) {
    2026           0 :     goto error;
    2027           0 :   }
    2028           0 :   if( input->has_exec_effects ){
    2029           0 :     cpi_exec_effects = &input->exec_effects;
    2030           0 :   }
    2031             : 
    2032           0 :   ulong rodata_sz = input->vm_ctx.rodata ? input->vm_ctx.rodata->size : 0UL;
    2033           0 :   uchar * rodata = fd_valloc_malloc( valloc, 8UL, rodata_sz );
    2034           0 :   if ( input->vm_ctx.rodata != NULL ) {
    2035           0 :     fd_memcpy( rodata, input->vm_ctx.rodata->bytes, rodata_sz );
    2036           0 :   }
    2037             : 
    2038           0 :   if( input->vm_ctx.heap_max > FD_VM_HEAP_MAX ) {
    2039           0 :     goto error;
    2040           0 :   }
    2041             : 
    2042           0 :   fd_vm_t * vm = fd_vm_join( fd_vm_new( fd_valloc_malloc( valloc, fd_vm_align(), fd_vm_footprint() ) ) );
    2043           0 :   if ( !vm ) {
    2044           0 :     goto error;
    2045           0 :   }
    2046             : 
    2047             :   /* If the program ID account owner is the v1 BPF loader, then alignment is disabled (controlled by
    2048             :      the `is_deprecated` flag) */
    2049             : 
    2050           0 :   ulong                   input_sz                = 0UL;
    2051           0 :   ulong                   pre_lens[256]           = {0};
    2052           0 :   fd_vm_input_region_t    input_mem_regions[1000] = {0}; /* We can have a max of (3 * num accounts + 1) regions */
    2053           0 :   fd_vm_acc_region_meta_t acc_region_metas[256]   = {0}; /* instr acc idx to idx */
    2054           0 :   uint                    input_mem_regions_cnt   = 0U;
    2055           0 :   int                     direct_mapping          = FD_FEATURE_ACTIVE( ctx->txn_ctx->slot, ctx->txn_ctx->features, bpf_account_data_direct_mapping );
    2056             : 
    2057           0 :   uchar * input_ptr      = NULL;
    2058           0 :   uchar   program_id_idx = ctx->instr->program_id;
    2059           0 :   uchar   is_deprecated  = ( program_id_idx < ctx->txn_ctx->accounts_cnt ) &&
    2060           0 :                            ( !memcmp( ctx->txn_ctx->accounts[program_id_idx].const_meta->info.owner, fd_solana_bpf_loader_deprecated_program_id.key, sizeof(fd_pubkey_t) ) );
    2061             : 
    2062             :   /* TODO: Check for an error code. Probably unlikely during fuzzing though */
    2063           0 :   fd_bpf_loader_input_serialize_parameters( ctx,
    2064           0 :                                             &input_sz,
    2065           0 :                                             pre_lens,
    2066           0 :                                             input_mem_regions,
    2067           0 :                                             &input_mem_regions_cnt,
    2068           0 :                                             acc_region_metas,
    2069           0 :                                             direct_mapping,
    2070           0 :                                             is_deprecated,
    2071           0 :                                             &input_ptr );
    2072             : 
    2073           0 :   fd_vm_init( vm,
    2074           0 :               ctx,
    2075           0 :               input->vm_ctx.heap_max,
    2076           0 :               ctx->txn_ctx->compute_meter,
    2077           0 :               rodata,
    2078           0 :               rodata_sz,
    2079           0 :               NULL, // TODO
    2080           0 :               0, // TODO
    2081           0 :               0, // TODO
    2082           0 :               0, // TODO, text_sz
    2083           0 :               0, // TODO
    2084           0 :               NULL, // TODO
    2085           0 :               TEST_VM_DEFAULT_SBPF_VERSION,
    2086           0 :               syscalls,
    2087           0 :               NULL, // TODO
    2088           0 :               sha,
    2089           0 :               input_mem_regions,
    2090           0 :               input_mem_regions_cnt,
    2091           0 :               acc_region_metas,
    2092           0 :               is_deprecated,
    2093           0 :               FD_FEATURE_ACTIVE( ctx->txn_ctx->slot, ctx->txn_ctx->features, bpf_account_data_direct_mapping ) );
    2094             : 
    2095             :   // Override some execution state values from the syscall fuzzer input
    2096             :   // This is so we can test if the syscall mutates any of these erroneously
    2097           0 :   vm->reg[0] = input->vm_ctx.r0;
    2098           0 :   vm->reg[1] = input->vm_ctx.r1;
    2099           0 :   vm->reg[2] = input->vm_ctx.r2;
    2100           0 :   vm->reg[3] = input->vm_ctx.r3;
    2101           0 :   vm->reg[4] = input->vm_ctx.r4;
    2102           0 :   vm->reg[5] = input->vm_ctx.r5;
    2103           0 :   vm->reg[6] = input->vm_ctx.r6;
    2104           0 :   vm->reg[7] = input->vm_ctx.r7;
    2105           0 :   vm->reg[8] = input->vm_ctx.r8;
    2106           0 :   vm->reg[9] = input->vm_ctx.r9;
    2107           0 :   vm->reg[10] = input->vm_ctx.r10;
    2108           0 :   vm->reg[11] = input->vm_ctx.r11;
    2109             : 
    2110             :   // Override initial part of the heap, if specified the syscall fuzzer input
    2111           0 :   if( input->syscall_invocation.heap_prefix ) {
    2112           0 :     fd_memcpy( vm->heap, input->syscall_invocation.heap_prefix->bytes,
    2113           0 :                fd_ulong_min(input->syscall_invocation.heap_prefix->size, vm->heap_max) );
    2114           0 :   }
    2115             : 
    2116             :   // Override initial part of the stack, if specified the syscall fuzzer input
    2117           0 :   if( input->syscall_invocation.stack_prefix ) {
    2118           0 :     fd_memcpy( vm->stack, input->syscall_invocation.stack_prefix->bytes,
    2119           0 :                fd_ulong_min(input->syscall_invocation.stack_prefix->size, FD_VM_STACK_MAX) );
    2120           0 :   }
    2121             : 
    2122             :   // Look up the syscall to execute
    2123           0 :   char * syscall_name = (char *)input->syscall_invocation.function_name.bytes;
    2124           0 :   fd_sbpf_syscalls_t const * syscall = lookup_syscall_func(syscalls, syscall_name, input->syscall_invocation.function_name.size);
    2125           0 :   if( !syscall ) {
    2126           0 :     goto error;
    2127           0 :   }
    2128             : 
    2129             :   /* Actually invoke the syscall */
    2130           0 :   int stack_push_err = fd_instr_stack_push( ctx->txn_ctx, (fd_instr_info_t *)ctx->instr );
    2131           0 :   if( FD_UNLIKELY( stack_push_err ) ) {
    2132           0 :       FD_LOG_WARNING(( "instr stack push err" ));
    2133           0 :       goto error;
    2134           0 :   }
    2135             :   /* There's an instr ctx struct embedded in the txn ctx instr stack. */
    2136           0 :   fd_exec_instr_ctx_t * instr_ctx = &ctx->txn_ctx->instr_stack[ ctx->txn_ctx->instr_stack_sz - 1 ];
    2137           0 :   *instr_ctx = (fd_exec_instr_ctx_t) {
    2138           0 :     .instr     = ctx->instr,
    2139           0 :     .txn_ctx   = ctx->txn_ctx,
    2140           0 :     .acc_mgr   = ctx->acc_mgr,
    2141           0 :     .funk_txn  = ctx->funk_txn,
    2142           0 :     .parent    = NULL,
    2143           0 :     .index     = 0U,
    2144           0 :     .depth     = 0U,
    2145           0 :     .child_cnt = 0U,
    2146           0 :   };
    2147           0 :   int syscall_err = syscall->func( vm, vm->reg[1], vm->reg[2], vm->reg[3], vm->reg[4], vm->reg[5], &vm->reg[0] );
    2148           0 :   int stack_pop_err = fd_instr_stack_pop( ctx->txn_ctx, ctx->instr );
    2149           0 :   if( FD_UNLIKELY( stack_pop_err ) ) {
    2150           0 :       FD_LOG_WARNING(( "instr stack pop err" ));
    2151           0 :       goto error;
    2152           0 :   }
    2153           0 :   if( syscall_err ) {
    2154           0 :     fd_log_collector_program_failure( vm->instr_ctx );
    2155           0 :   }
    2156             : 
    2157             :   /* Capture the effects */
    2158           0 :   int exec_err = vm->instr_ctx->txn_ctx->exec_err;
    2159           0 :   effects->error = 0;
    2160           0 :   if( syscall_err ) {
    2161           0 :     if( exec_err==0 ) {
    2162           0 :       FD_LOG_WARNING(( "TODO: syscall returns error, but exec_err not set. this is probably missing a log." ));
    2163           0 :       effects->error = -1;
    2164           0 :     } else {
    2165           0 :       effects->error = (exec_err <= 0) ? -exec_err : -1;
    2166             : 
    2167             :       /* Map error kind, equivalent to:
    2168             :           effects->error_kind = (fd_exec_test_err_kind_t)(vm->instr_ctx->txn_ctx->exec_err_kind); */
    2169           0 :       switch (vm->instr_ctx->txn_ctx->exec_err_kind) {
    2170           0 :         case FD_EXECUTOR_ERR_KIND_EBPF:
    2171           0 :           effects->error_kind = FD_EXEC_TEST_ERR_KIND_EBPF;
    2172           0 :           break;
    2173           0 :         case FD_EXECUTOR_ERR_KIND_SYSCALL:
    2174           0 :           effects->error_kind = FD_EXEC_TEST_ERR_KIND_SYSCALL;
    2175           0 :           break;
    2176           0 :         case FD_EXECUTOR_ERR_KIND_INSTR:
    2177           0 :           effects->error_kind = FD_EXEC_TEST_ERR_KIND_INSTRUCTION;
    2178           0 :           break;
    2179           0 :         default:
    2180           0 :           effects->error_kind = FD_EXEC_TEST_ERR_KIND_UNSPECIFIED;
    2181           0 :           break;
    2182           0 :       }
    2183           0 :     }
    2184           0 :   }
    2185           0 :   effects->r0 = syscall_err ? 0 : vm->reg[0]; // Save only on success
    2186           0 :   effects->cu_avail = (ulong)vm->cu;
    2187             : 
    2188           0 :   if( vm->heap_max ) {
    2189           0 :     effects->heap = FD_SCRATCH_ALLOC_APPEND(
    2190           0 :       l, alignof(uint), PB_BYTES_ARRAY_T_ALLOCSIZE( vm->heap_max ) );
    2191           0 :     if( FD_UNLIKELY( _l > output_end ) ) {
    2192           0 :       goto error;
    2193           0 :     }
    2194           0 :     effects->heap->size = (uint)vm->heap_max;
    2195           0 :     fd_memcpy( effects->heap->bytes, vm->heap, vm->heap_max );
    2196           0 :   } else {
    2197           0 :     effects->heap = NULL;
    2198           0 :   }
    2199             : 
    2200           0 :   effects->stack = FD_SCRATCH_ALLOC_APPEND(
    2201           0 :     l, alignof(pb_bytes_array_t), PB_BYTES_ARRAY_T_ALLOCSIZE( FD_VM_STACK_MAX ) );
    2202           0 :     if( FD_UNLIKELY( _l > output_end ) ) {
    2203           0 :       goto error;
    2204           0 :     }
    2205           0 :   effects->stack->size = (uint)FD_VM_STACK_MAX;
    2206           0 :   fd_memcpy( effects->stack->bytes, vm->stack, FD_VM_STACK_MAX );
    2207             : 
    2208           0 :   if( vm->rodata_sz ) {
    2209           0 :     effects->rodata = FD_SCRATCH_ALLOC_APPEND(
    2210           0 :       l, alignof(pb_bytes_array_t), PB_BYTES_ARRAY_T_ALLOCSIZE( rodata_sz ) );
    2211           0 :     if( FD_UNLIKELY( _l > output_end ) ) {
    2212           0 :       goto error;
    2213           0 :     }
    2214           0 :     effects->rodata->size = (uint)rodata_sz;
    2215           0 :     fd_memcpy( effects->rodata->bytes, vm->rodata, rodata_sz );
    2216           0 :   } else {
    2217           0 :     effects->rodata = NULL;
    2218           0 :   }
    2219             : 
    2220           0 :   effects->frame_count = vm->frame_cnt;
    2221             : 
    2222           0 :   fd_log_collector_t * log = &vm->instr_ctx->txn_ctx->log_collector;
    2223             :   /* Only collect log on valid errors (i.e., != -1). Follows
    2224             :      https://github.com/firedancer-io/solfuzz-agave/blob/99758d3c4f3a342d56e2906936458d82326ae9a8/src/utils/err_map.rs#L148 */
    2225           0 :   if( effects->error != -1 && log->buf_sz ) {
    2226           0 :     effects->log = FD_SCRATCH_ALLOC_APPEND(
    2227           0 :       l, alignof(pb_bytes_array_t), PB_BYTES_ARRAY_T_ALLOCSIZE( log->buf_sz ) );
    2228           0 :     if( FD_UNLIKELY( _l > output_end ) ) {
    2229           0 :       goto error;
    2230           0 :     }
    2231           0 :     effects->log->size = (uint)fd_log_collector_debug_sprintf( log, (char *)effects->log->bytes, 0 );
    2232           0 :   } else {
    2233           0 :     effects->log = NULL;
    2234           0 :   }
    2235             : 
    2236             :   /* Capture input regions */
    2237           0 :   effects->inputdata = NULL; /* Deprecated, using input_data_regions instead */
    2238           0 :   ulong tmp_end = FD_SCRATCH_ALLOC_FINI( l, 1UL );
    2239           0 :   ulong input_regions_size = load_from_vm_input_regions( vm->input_mem_regions,
    2240           0 :                                                         vm->input_mem_regions_cnt,
    2241           0 :                                                         &effects->input_data_regions,
    2242           0 :                                                         &effects->input_data_regions_count,
    2243           0 :                                                         (void *)tmp_end,
    2244           0 :                                                         fd_ulong_sat_sub( output_end, tmp_end ) );
    2245             : 
    2246           0 :   if( !!vm->input_mem_regions_cnt && !effects->input_data_regions ) {
    2247           0 :     goto error;
    2248           0 :   }
    2249             : 
    2250             :   /* Return the effects */
    2251           0 :   ulong actual_end = tmp_end + input_regions_size;
    2252           0 :   fd_exec_test_instr_context_destroy( runner, ctx );
    2253           0 :   cpi_exec_effects = NULL;
    2254             : 
    2255           0 :   *output = effects;
    2256           0 :   return actual_end - (ulong)output_buf;
    2257             : 
    2258           0 : error:
    2259           0 :   fd_exec_test_instr_context_destroy( runner, ctx );
    2260           0 :   cpi_exec_effects = NULL;
    2261           0 :   return 0;
    2262           0 : }
    2263             : 
    2264             : /* Stubs fd_execute_instr for binaries compiled with
    2265             :    `-Xlinker --wrap=fd_execute_instr` */
    2266             : int
    2267             : __wrap_fd_execute_instr( fd_exec_txn_ctx_t * txn_ctx,
    2268             :                          fd_instr_info_t *   instr_info )
    2269           0 : {
    2270           0 :     static const pb_byte_t zero_blk[32] = {0};
    2271             : 
    2272           0 :     if( cpi_exec_effects == NULL ) {
    2273           0 :       FD_LOG_WARNING(( "fd_execute_instr is disabled" ));
    2274           0 :       return FD_EXECUTOR_INSTR_SUCCESS;
    2275           0 :     }
    2276             : 
    2277             :     // Iterate through instruction accounts
    2278           0 :     for( ushort i = 0UL; i < instr_info->acct_cnt; ++i ) {
    2279           0 :       uchar idx_in_txn = instr_info->acct_txn_idxs[i];
    2280           0 :       fd_pubkey_t * acct_pubkey = &instr_info->acct_pubkeys[i];
    2281             : 
    2282           0 :       fd_txn_account_t * acct = NULL;
    2283             :       /* Find (first) account in cpi_exec_effects->modified_accounts that matches pubkey */
    2284           0 :       for( uint j = 0UL; j < cpi_exec_effects->modified_accounts_count; ++j ) {
    2285           0 :         fd_exec_test_acct_state_t * acct_state = &cpi_exec_effects->modified_accounts[j];
    2286           0 :         if( memcmp( acct_state->address, acct_pubkey, sizeof(fd_pubkey_t) ) != 0 ) continue;
    2287             : 
    2288             :         /* Fetch borrowed account */
    2289             :         /* First check if account is read-only.
    2290             :            TODO: Once direct mapping is enabled we _technically_ don't need
    2291             :                  this check */
    2292             : 
    2293           0 :         if( fd_exec_txn_ctx_get_account_view_idx( txn_ctx, idx_in_txn, &acct ) ) {
    2294           0 :           break;
    2295           0 :         }
    2296           0 :         if( acct->meta == NULL ){
    2297           0 :           break;
    2298           0 :         }
    2299             : 
    2300             :         /* Now borrow mutably (with resize) */
    2301           0 :         int err = fd_exec_txn_ctx_get_account_modify_idx( txn_ctx,
    2302           0 :                                                           idx_in_txn,
    2303             :                                                           /* Do not reallocate if data is not going to be modified */
    2304           0 :                                                           acct_state->data ? acct_state->data->size : 0UL,
    2305           0 :                                                           &acct );
    2306           0 :         if( err ) break;
    2307             : 
    2308             :         /* Update account state */
    2309           0 :         acct->meta->info.lamports = acct_state->lamports;
    2310           0 :         acct->meta->info.executable = acct_state->executable;
    2311           0 :         acct->meta->info.rent_epoch = acct_state->rent_epoch;
    2312             : 
    2313             :         /* TODO: use lower level API (i.e., fd_borrowed_account_resize) to avoid memcpy here */
    2314           0 :         if( acct_state->data ){
    2315           0 :           fd_memcpy( acct->data, acct_state->data->bytes, acct_state->data->size );
    2316           0 :           acct->meta->dlen = acct_state->data->size;
    2317           0 :         }
    2318             : 
    2319             :         /* Follow solfuzz-agave, which skips if pubkey is malformed */
    2320           0 :         if( memcmp( acct_state->owner, zero_blk, sizeof(fd_pubkey_t) ) != 0 ) {
    2321           0 :           fd_memcpy( acct->meta->info.owner, acct_state->owner, sizeof(fd_pubkey_t) );
    2322           0 :         }
    2323             : 
    2324           0 :         break;
    2325           0 :       }
    2326           0 :     }
    2327           0 :     return FD_EXECUTOR_INSTR_SUCCESS;
    2328           0 : }

Generated by: LCOV version 1.14