LCOV - code coverage report
Current view: top level - flamenco/runtime - fd_runtime.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 140 2696 5.2 %
Date: 2025-01-08 12:08:44 Functions: 5 81 6.2 %

          Line data    Source code
       1             : #include "fd_runtime.h"
       2             : #include "fd_acc_mgr.h"
       3             : #include "fd_runtime_err.h"
       4             : #include "fd_runtime_init.h"
       5             : #include "fd_pubkey_utils.h"
       6             : 
       7             : #include "fd_executor.h"
       8             : #include "fd_account.h"
       9             : #include "fd_hashes.h"
      10             : #include "fd_txncache.h"
      11             : #include "sysvar/fd_sysvar_cache.h"
      12             : #include "sysvar/fd_sysvar_clock.h"
      13             : #include "sysvar/fd_sysvar_epoch_schedule.h"
      14             : #include "sysvar/fd_sysvar_recent_hashes.h"
      15             : #include "sysvar/fd_sysvar_stake_history.h"
      16             : #include "sysvar/fd_sysvar.h"
      17             : #include "../../ballet/base58/fd_base58.h"
      18             : #include "../../ballet/txn/fd_txn.h"
      19             : #include "../../ballet/bmtree/fd_bmtree.h"
      20             : #include "../../ballet/bmtree/fd_wbmtree.h"
      21             : 
      22             : #include "../stakes/fd_stakes.h"
      23             : #include "../rewards/fd_rewards.h"
      24             : 
      25             : #include "context/fd_exec_txn_ctx.h"
      26             : #include "context/fd_exec_instr_ctx.h"
      27             : #include "info/fd_microblock_batch_info.h"
      28             : #include "info/fd_microblock_info.h"
      29             : 
      30             : #include "program/fd_stake_program.h"
      31             : #include "program/fd_builtin_programs.h"
      32             : #include "program/fd_system_program.h"
      33             : #include "program/fd_vote_program.h"
      34             : #include "program/fd_bpf_program_util.h"
      35             : #include "program/fd_bpf_loader_program.h"
      36             : #include "program/fd_compute_budget_program.h"
      37             : 
      38             : #include "sysvar/fd_sysvar_clock.h"
      39             : #include "sysvar/fd_sysvar_fees.h"
      40             : #include "sysvar/fd_sysvar_last_restart_slot.h"
      41             : #include "sysvar/fd_sysvar_recent_hashes.h"
      42             : #include "sysvar/fd_sysvar_rent.h"
      43             : #include "sysvar/fd_sysvar_slot_hashes.h"
      44             : #include "sysvar/fd_sysvar_slot_history.h"
      45             : 
      46             : #include "../nanopb/pb_decode.h"
      47             : #include "../nanopb/pb_encode.h"
      48             : #include "../types/fd_solana_block.pb.h"
      49             : 
      50             : #include "fd_system_ids.h"
      51             : #include "../vm/fd_vm.h"
      52             : #include "fd_blockstore.h"
      53             : #include "../../ballet/pack/fd_pack.h"
      54             : #include "../fd_rwlock.h"
      55             : 
      56             : #include <stdio.h>
      57             : #include <ctype.h>
      58             : #include <unistd.h>
      59             : #include <sys/stat.h>
      60             : #include <sys/types.h>
      61             : #include <errno.h>
      62             : #include <fcntl.h>
      63             : 
      64             : /******************************************************************************/
      65             : /* Public Runtime Helpers                                                     */
      66             : /******************************************************************************/
      67             : 
      68             : void 
      69           0 : fd_runtime_update_leaders( fd_exec_slot_ctx_t * slot_ctx, ulong slot ) {
      70           0 :   FD_SCRATCH_SCOPE_BEGIN {
      71           0 :     fd_epoch_schedule_t schedule = slot_ctx->epoch_ctx->epoch_bank.epoch_schedule;
      72             : 
      73           0 :     FD_LOG_INFO(( "schedule->slots_per_epoch = %lu", schedule.slots_per_epoch ));
      74           0 :     FD_LOG_INFO(( "schedule->leader_schedule_slot_offset = %lu", schedule.leader_schedule_slot_offset ));
      75           0 :     FD_LOG_INFO(( "schedule->warmup = %d", schedule.warmup ));
      76           0 :     FD_LOG_INFO(( "schedule->first_normal_epoch = %lu", schedule.first_normal_epoch ));
      77           0 :     FD_LOG_INFO(( "schedule->first_normal_slot = %lu", schedule.first_normal_slot ));
      78             : 
      79           0 :     fd_vote_accounts_t const * epoch_vaccs = &slot_ctx->slot_bank.epoch_stakes;
      80             : 
      81           0 :     ulong epoch    = fd_slot_to_epoch( &schedule, slot, NULL );
      82           0 :     ulong slot0    = fd_epoch_slot0( &schedule, epoch );
      83           0 :     ulong slot_cnt = fd_epoch_slot_cnt( &schedule, epoch );
      84             : 
      85           0 :     FD_LOG_INFO(( "starting rent list init" ));
      86             : 
      87           0 :     fd_acc_mgr_set_slots_per_epoch( slot_ctx, fd_epoch_slot_cnt(&schedule, epoch) );
      88           0 :     FD_LOG_INFO(( "rent list init done" ));
      89             : 
      90           0 :     ulong               vote_acc_cnt  = fd_vote_accounts_pair_t_map_size( epoch_vaccs->vote_accounts_pool, epoch_vaccs->vote_accounts_root );
      91           0 :     fd_stake_weight_t * epoch_weights = fd_scratch_alloc( alignof(fd_stake_weight_t), vote_acc_cnt * sizeof(fd_stake_weight_t) );
      92           0 :     if( FD_UNLIKELY( !epoch_weights ) ) {
      93           0 :       FD_LOG_ERR(("fd_scratch_alloc() failed"));
      94           0 :     }
      95             : 
      96           0 :     ulong stake_weight_cnt = fd_stake_weights_by_node(epoch_vaccs, epoch_weights);
      97             : 
      98           0 :     if( FD_UNLIKELY( stake_weight_cnt == ULONG_MAX ) ) {
      99           0 :       FD_LOG_ERR(("fd_stake_weights_by_node() failed"));
     100           0 :     }
     101             : 
     102             :     /* Derive leader schedule */
     103             : 
     104           0 :     FD_LOG_INFO(( "stake_weight_cnt=%lu slot_cnt=%lu", stake_weight_cnt, slot_cnt ));
     105           0 :     ulong epoch_leaders_footprint = fd_epoch_leaders_footprint( stake_weight_cnt, slot_cnt );
     106           0 :     FD_LOG_INFO(( "epoch_leaders_footprint=%lu", epoch_leaders_footprint ));
     107           0 :     if( FD_LIKELY( epoch_leaders_footprint ) ) {
     108           0 :       FD_TEST( stake_weight_cnt <= MAX_PUB_CNT );
     109           0 :       FD_TEST( slot_cnt <= MAX_SLOTS_CNT );
     110           0 :       void *               epoch_leaders_mem = fd_exec_epoch_ctx_leaders( slot_ctx->epoch_ctx );
     111           0 :       fd_epoch_leaders_t * leaders           = fd_epoch_leaders_join( fd_epoch_leaders_new( epoch_leaders_mem, 
     112           0 :                                                                                                        epoch, 
     113           0 :                                                                                                        slot0,
     114           0 :                                                                                                        slot_cnt,
     115           0 :                                                                                                        stake_weight_cnt,
     116           0 :                                                                                                        epoch_weights,
     117           0 :                                                                                                        0UL ) );
     118           0 :       FD_TEST( leaders );
     119           0 :     }
     120           0 :   } FD_SCRATCH_SCOPE_END;
     121           0 : }
     122             : 
     123             : /* Loads the sysvar cache. Expects acc_mgr, funk_txn, valloc to be non-NULL and valid. */
     124             : int 
     125           0 : fd_runtime_sysvar_cache_load( fd_exec_slot_ctx_t * slot_ctx ) {
     126           0 :   if( FD_UNLIKELY( !slot_ctx->acc_mgr ) ) { 
     127           0 :     return -1;
     128           0 :   }
     129             : 
     130           0 :   fd_sysvar_cache_restore( slot_ctx->sysvar_cache, slot_ctx->acc_mgr, slot_ctx->funk_txn );
     131             : 
     132           0 :   return FD_RUNTIME_EXECUTE_SUCCESS;
     133           0 : }
     134             : 
     135             : /******************************************************************************/
     136             : /* Various Private Runtime Helpers                                            */
     137             : /******************************************************************************/
     138             : 
     139             : /* NOTE: Rent functions are not being cleaned up due to the fact that they will
     140             :    be entirely torn out of the codebase very soon. */
     141             : 
     142             : static void
     143           0 : fd_runtime_collect_rent_for_slot( fd_exec_slot_ctx_t * slot_ctx, ulong off, ulong epoch ) {
     144           0 :   fd_funk_txn_t * txn     = slot_ctx->funk_txn;
     145           0 :   fd_acc_mgr_t *  acc_mgr = slot_ctx->acc_mgr;
     146           0 :   fd_funk_t *     funk    = slot_ctx->acc_mgr->funk;
     147           0 :   fd_wksp_t *     wksp    = fd_funk_wksp( funk );
     148             : 
     149           0 :   fd_funk_partvec_t * partvec = fd_funk_get_partvec( funk, wksp );
     150             : 
     151           0 :   fd_funk_rec_t * rec_map = fd_funk_rec_map( funk, wksp );
     152             : 
     153           0 :   for( fd_funk_rec_t const *rec_ro = fd_funk_part_head( partvec, (uint)off, rec_map );
     154           0 :        rec_ro != NULL;
     155           0 :        rec_ro = fd_funk_part_next( rec_ro, rec_map ) ) {
     156             : 
     157           0 :     if ( FD_UNLIKELY( !fd_funk_key_is_acc( rec_ro->pair.key ) ) ) {
     158           0 :       continue;
     159           0 :     }
     160             : 
     161           0 :     fd_pubkey_t const *key = fd_type_pun_const( rec_ro->pair.key[0].uc );
     162           0 :     FD_BORROWED_ACCOUNT_DECL( rec );
     163           0 :     int err = fd_acc_mgr_view( acc_mgr, txn, key, rec );
     164             : 
     165             :     /* Account might not exist anymore in the current world */
     166           0 :     if( err==FD_ACC_MGR_ERR_UNKNOWN_ACCOUNT ) {
     167           0 :       continue;
     168           0 :     }
     169           0 :     if( FD_UNLIKELY( err != FD_ACC_MGR_SUCCESS ) ) {
     170           0 :       FD_LOG_WARNING(( "fd_runtime_collect_rent: fd_acc_mgr_view failed (%d)", err ));
     171           0 :       continue;
     172           0 :     }
     173             : 
     174             :     /* Check if latest version in this transaction */
     175           0 :     if( rec_ro!=rec->const_rec ) {
     176           0 :       continue;
     177           0 :     }
     178             : 
     179             :     /* Upgrade read-only handle to writable */
     180           0 :     err = fd_acc_mgr_modify(
     181           0 :         acc_mgr, txn, key,
     182           0 :         /* do_create   */ 0,
     183           0 :         /* min_data_sz */ 0UL,
     184           0 :         rec);
     185           0 :     if( FD_UNLIKELY( err!=FD_ACC_MGR_SUCCESS ) ) {
     186           0 :       FD_LOG_WARNING(( "fd_runtime_collect_rent_range: fd_acc_mgr_modify failed (%d)", err ));
     187           0 :       continue;
     188           0 :     }
     189             : 
     190             :     /* Actually invoke rent collection */
     191           0 :     slot_ctx->slot_bank.collected_rent += fd_runtime_collect_rent_from_account( slot_ctx, rec->meta, key, epoch );
     192           0 :   }
     193           0 : }
     194             : 
     195             : /* Yes, this is a real function that exists in Solana. Yes, I am ashamed I have had to replicate it. */
     196             : // https://github.com/firedancer-io/solana/blob/d8292b427adf8367d87068a3a88f6fd3ed8916a5/runtime/src/bank.rs#L5618
     197             : static ulong
     198           0 : fd_runtime_slot_count_in_two_day( ulong ticks_per_slot ) {
     199           0 :   return 2UL * FD_SYSVAR_CLOCK_DEFAULT_TICKS_PER_SECOND * 86400UL /* seconds per day */ / ticks_per_slot;
     200           0 : }
     201             : 
     202             : // https://github.com/firedancer-io/solana/blob/d8292b427adf8367d87068a3a88f6fd3ed8916a5/runtime/src/bank.rs#L5594
     203             : static int
     204           0 : fd_runtime_use_multi_epoch_collection( fd_exec_slot_ctx_t const * slot_ctx, ulong slot ) {
     205           0 :   fd_epoch_bank_t const * epoch_bank = fd_exec_epoch_ctx_epoch_bank( slot_ctx->epoch_ctx );
     206           0 :   fd_epoch_schedule_t const * schedule = &epoch_bank->epoch_schedule;
     207             : 
     208           0 :   ulong off;
     209           0 :   ulong epoch = fd_slot_to_epoch( schedule, slot, &off );
     210           0 :   ulong slots_per_normal_epoch = fd_epoch_slot_cnt( schedule, schedule->first_normal_epoch );
     211             : 
     212           0 :   ulong slot_count_in_two_day = fd_runtime_slot_count_in_two_day( epoch_bank->ticks_per_slot );
     213             : 
     214           0 :   int use_multi_epoch_collection = ( epoch >= schedule->first_normal_epoch )
     215           0 :       && ( slots_per_normal_epoch < slot_count_in_two_day );
     216             : 
     217           0 :   return use_multi_epoch_collection;
     218           0 : }
     219             : 
     220             : static ulong
     221           0 : fd_runtime_num_rent_partitions( fd_exec_slot_ctx_t const * slot_ctx, ulong slot ) {
     222           0 :   fd_epoch_bank_t const * epoch_bank = fd_exec_epoch_ctx_epoch_bank( slot_ctx->epoch_ctx );
     223           0 :   fd_epoch_schedule_t const * schedule = &epoch_bank->epoch_schedule;
     224             : 
     225           0 :   ulong off;
     226           0 :   ulong epoch = fd_slot_to_epoch( schedule, slot, &off );
     227           0 :   ulong slots_per_epoch = fd_epoch_slot_cnt( schedule, epoch );
     228             : 
     229           0 :   ulong slot_count_in_two_day = fd_runtime_slot_count_in_two_day( epoch_bank->ticks_per_slot );
     230             : 
     231           0 :   int use_multi_epoch_collection = fd_runtime_use_multi_epoch_collection( slot_ctx, slot );
     232             : 
     233           0 :   if( use_multi_epoch_collection ) {
     234           0 :     ulong epochs_in_cycle = slot_count_in_two_day / slots_per_epoch;
     235           0 :     return slots_per_epoch * epochs_in_cycle;
     236           0 :   } else {
     237           0 :     return slots_per_epoch;
     238           0 :   }
     239           0 : }
     240             : 
     241             : // https://github.com/anza-xyz/agave/blob/2bdcc838c18d262637524274cbb2275824eb97b8/accounts-db/src/accounts_partition.rs#L30
     242             : static ulong
     243           0 : fd_runtime_get_rent_partition( fd_exec_slot_ctx_t const * slot_ctx, ulong slot ) {
     244           0 :   int use_multi_epoch_collection = fd_runtime_use_multi_epoch_collection( slot_ctx, slot );
     245             : 
     246           0 :   fd_epoch_bank_t const * epoch_bank = fd_exec_epoch_ctx_epoch_bank( slot_ctx->epoch_ctx );
     247           0 :   fd_epoch_schedule_t const * schedule = &epoch_bank->epoch_schedule;
     248             : 
     249           0 :   ulong off;
     250           0 :   ulong epoch = fd_slot_to_epoch( schedule, slot, &off );
     251           0 :   ulong slot_count_per_epoch = fd_epoch_slot_cnt( schedule, epoch );
     252           0 :   ulong slot_count_in_two_day = fd_runtime_slot_count_in_two_day( epoch_bank->ticks_per_slot );
     253             : 
     254           0 :   ulong base_epoch;
     255           0 :   ulong epoch_count_in_cycle;
     256           0 :   if( use_multi_epoch_collection ) {
     257           0 :     base_epoch = schedule->first_normal_epoch;
     258           0 :     epoch_count_in_cycle = slot_count_in_two_day / slot_count_per_epoch;
     259           0 :   } else {
     260           0 :     base_epoch = 0;
     261           0 :     epoch_count_in_cycle = 1;
     262           0 :   }
     263             : 
     264           0 :   ulong epoch_offset = epoch - base_epoch;
     265           0 :   ulong epoch_index_in_cycle = epoch_offset % epoch_count_in_cycle;
     266           0 :   return off + ( epoch_index_in_cycle * slot_count_per_epoch );
     267           0 : }
     268             : 
     269             : static ulong 
     270             : fd_runtime_calculate_rent_burn( ulong             rent_collected,
     271           0 :                                 fd_rent_t const * rent ) {
     272           0 :   return (rent_collected * rent->burn_percent) / 100UL;
     273           0 : }
     274             : 
     275             : static void
     276           0 : fd_runtime_collect_rent( fd_exec_slot_ctx_t * slot_ctx ) {
     277             :   // Bank::collect_rent_eagerly (enter)
     278             : 
     279           0 :   fd_epoch_bank_t const * epoch_bank = fd_exec_epoch_ctx_epoch_bank( slot_ctx->epoch_ctx );
     280           0 :   fd_epoch_schedule_t const * schedule = &epoch_bank->epoch_schedule;
     281             : 
     282             :   // Bank::rent_collection_partitions              (enter)
     283             :   // Bank::variable_cycle_partitions               (enter)
     284             :   // Bank::variable_cycle_partitions_between_slots (enter)
     285             : 
     286           0 :   ulong slot0 = slot_ctx->slot_bank.prev_slot;
     287           0 :   ulong slot1 = slot_ctx->slot_bank.slot;
     288             : 
     289             :   /* For genesis, we collect rent for slot 0. */
     290           0 :   if (slot1 == 0) {
     291           0 :     ulong s = slot1;
     292           0 :     ulong off;
     293           0 :     ulong epoch = fd_slot_to_epoch(schedule, s, &off);
     294             : 
     295             :     /* FIXME: This will not necessarily support warmup_epochs */
     296           0 :     ulong num_partitions = fd_runtime_num_rent_partitions( slot_ctx, s );
     297             :     /* Reconstruct rent lists if the number of slots per epoch changes */
     298           0 :     fd_acc_mgr_set_slots_per_epoch( slot_ctx, num_partitions );
     299           0 :     fd_runtime_collect_rent_for_slot( slot_ctx, fd_runtime_get_rent_partition( slot_ctx, s ), epoch );
     300           0 :     return;
     301           0 :   }
     302             : 
     303           0 :   FD_TEST(slot0 <= slot1);
     304             : 
     305           0 :   for( ulong s = slot0 + 1; s <= slot1; ++s ) {
     306           0 :     ulong off;
     307           0 :     ulong epoch = fd_slot_to_epoch(schedule, s, &off);
     308             : 
     309             :     /* FIXME: This will not necessarily support warmup_epochs */
     310           0 :     ulong num_partitions = fd_runtime_num_rent_partitions( slot_ctx, s );
     311             :     /* Reconstruct rent lists if the number of slots per epoch changes */
     312           0 :     fd_acc_mgr_set_slots_per_epoch( slot_ctx, num_partitions );
     313           0 :     fd_runtime_collect_rent_for_slot( slot_ctx, fd_runtime_get_rent_partition( slot_ctx, s ), epoch );
     314           0 :   }
     315             : 
     316             :   // FD_LOG_DEBUG(("rent collected - lamports: %lu", slot_ctx->slot_bank.collected_rent));
     317           0 : }
     318             : 
     319             : 
     320             : /* fee to be deposited should be > 0
     321             :    Returns 0 if validation succeeds
     322             :    Returns the amount to burn(==fee) on failure */
     323             : FD_FN_PURE static ulong 
     324             : fd_runtime_validate_fee_collector( fd_exec_slot_ctx_t    const * slot_ctx,
     325             :                                    fd_borrowed_account_t const * collector,
     326           0 :                                    ulong                         fee ) {
     327           0 :   if( FD_UNLIKELY( fee<=0UL ) ) {
     328           0 :     FD_LOG_ERR(( "expected fee(%lu) to be >0UL", fee ));
     329           0 :   }
     330             : 
     331           0 :   if( FD_UNLIKELY( memcmp( collector->const_meta->info.owner, fd_solana_system_program_id.key, sizeof(collector->const_meta->info.owner) ) ) ) {
     332           0 :     FD_BASE58_ENCODE_32_BYTES( collector->pubkey->key, _out_key );
     333           0 :     FD_LOG_WARNING(( "cannot pay a non-system-program owned account (%s)", _out_key ));
     334           0 :     return fee;
     335           0 :   }
     336             : 
     337             :   /* https://github.com/anza-xyz/agave/blob/v1.18.23/runtime/src/bank/fee_distribution.rs#L111
     338             :      https://github.com/anza-xyz/agave/blob/v1.18.23/runtime/src/accounts/account_rent_state.rs#L39
     339             :      In agave's fee deposit code, rent state transition check logic is as follows:
     340             :      The transition is NOT allowed iff
     341             :      === BEGIN
     342             :      the post deposit account is rent paying AND the pre deposit account is not rent paying
     343             :      OR
     344             :      the post deposit account is rent paying AND the pre deposit account is rent paying AND !(post_data_size == pre_data_size && post_lamports <= pre_lamports)
     345             :      === END
     346             :      post_data_size == pre_data_size is always true during fee deposit.
     347             :      However, post_lamports > pre_lamports because we are paying a >0 amount.
     348             :      So, the above reduces down to
     349             :      === BEGIN
     350             :      the post deposit account is rent paying AND the pre deposit account is not rent paying
     351             :      OR
     352             :      the post deposit account is rent paying AND the pre deposit account is rent paying AND TRUE
     353             :      === END
     354             :      This is equivalent to checking that the post deposit account is rent paying.
     355             :      An account is rent paying if the post deposit balance is >0 AND it's not rent exempt.
     356             :      We already know that the post deposit balance is >0 because we are paying a >0 amount.
     357             :      So TLDR we just check if the account is rent exempt.
     358             :    */
     359           0 :   ulong minbal = fd_rent_exempt_minimum_balance( fd_sysvar_cache_rent( slot_ctx->sysvar_cache ), collector->const_meta->dlen );
     360           0 :   if( FD_UNLIKELY( collector->const_meta->info.lamports + fee < minbal ) ) {
     361           0 :     FD_BASE58_ENCODE_32_BYTES( collector->pubkey->key, _out_key );
     362           0 :     FD_LOG_WARNING(("cannot pay a rent paying account (%s)", _out_key ));
     363           0 :     return fee;
     364           0 :   }
     365             : 
     366           0 :   return 0UL;
     367           0 : }
     368             : 
     369             : struct fd_validator_stake_pair {
     370             :   fd_pubkey_t pubkey;
     371             :   ulong stake;
     372             : };
     373             : typedef struct fd_validator_stake_pair fd_validator_stake_pair_t;
     374             : 
     375             : static int
     376             : fd_validator_stake_pair_compare_before( fd_validator_stake_pair_t const * a,
     377           0 :                                         fd_validator_stake_pair_t const * b ) {
     378           0 :   if( a->stake > b->stake ) {
     379           0 :     return 1;
     380           0 :   } else if (a->stake == b->stake) {
     381           0 :     return memcmp(&a->pubkey, &b->pubkey, sizeof(fd_pubkey_t)) > 0;
     382           0 :   }
     383           0 :   else
     384           0 :   { // a->stake < b->stake
     385           0 :     return 0;
     386           0 :   }
     387           0 : }
     388             : 
     389             : #define SORT_NAME sort_validator_stake_pair
     390           0 : #define SORT_KEY_T fd_validator_stake_pair_t
     391           0 : #define SORT_BEFORE(a, b) (fd_validator_stake_pair_compare_before((fd_validator_stake_pair_t const *)&a, (fd_validator_stake_pair_t const *)&b))
     392             : #include "../../util/tmpl/fd_sort.c"
     393             : #undef SORT_NAME
     394             : #undef SORT_KEY_T
     395             : #undef SORT_BERFORE
     396             : 
     397             : static void 
     398             : fd_runtime_distribute_rent_to_validators( fd_exec_slot_ctx_t * slot_ctx,
     399           0 :                                           ulong                rent_to_be_distributed ) {
     400             : 
     401           0 :   FD_SCRATCH_SCOPE_BEGIN {
     402           0 :     ulong total_staked = 0;
     403             : 
     404           0 :     fd_epoch_bank_t * epoch_bank = fd_exec_epoch_ctx_epoch_bank( slot_ctx->epoch_ctx );
     405           0 :     fd_vote_accounts_pair_t_mapnode_t *vote_accounts_pool = epoch_bank->stakes.vote_accounts.vote_accounts_pool;
     406           0 :     fd_vote_accounts_pair_t_mapnode_t *vote_accounts_root = epoch_bank->stakes.vote_accounts.vote_accounts_root;
     407             : 
     408           0 :     ulong num_validator_stakes = fd_vote_accounts_pair_t_map_size( vote_accounts_pool, vote_accounts_root );
     409           0 :     fd_validator_stake_pair_t * validator_stakes = fd_scratch_alloc( 8UL, sizeof(fd_validator_stake_pair_t) * num_validator_stakes );
     410           0 :     ulong i = 0;
     411             : 
     412           0 :     for( fd_vote_accounts_pair_t_mapnode_t *n = fd_vote_accounts_pair_t_map_minimum( vote_accounts_pool, vote_accounts_root );
     413           0 :         n;
     414           0 :         n = fd_vote_accounts_pair_t_map_successor( vote_accounts_pool, n ), i++) {
     415             : 
     416           0 :       validator_stakes[i].pubkey = n->elem.value.node_pubkey;
     417           0 :       validator_stakes[i].stake = n->elem.stake;
     418             : 
     419           0 :       total_staked += n->elem.stake;
     420             : 
     421           0 :     }
     422             : 
     423           0 :     sort_validator_stake_pair_inplace(validator_stakes, num_validator_stakes);
     424             : 
     425           0 :     ulong validate_fee_collector_account = FD_FEATURE_ACTIVE(slot_ctx, validate_fee_collector_account);
     426             : 
     427           0 :     ulong rent_distributed_in_initial_round = 0;
     428             : 
     429             :     // We now do distribution, reusing the validator stakes array for the rent stares
     430           0 :     for( i = 0; i < num_validator_stakes; i++ ) {
     431           0 :       ulong staked = validator_stakes[i].stake;
     432           0 :       ulong rent_share = (ulong)(((uint128)staked * (uint128)rent_to_be_distributed) / (uint128)total_staked);
     433             : 
     434           0 :       validator_stakes[i].stake = rent_share;
     435           0 :       rent_distributed_in_initial_round += rent_share;
     436           0 :     }
     437             : 
     438           0 :     ulong leftover_lamports = rent_to_be_distributed - rent_distributed_in_initial_round;
     439             : 
     440           0 :     for( i = 0; i < num_validator_stakes; i++ ) {
     441           0 :       if (leftover_lamports == 0) {
     442           0 :         break;
     443           0 :       }
     444             : 
     445             :       /* Not using saturating sub because Agave doesn't.
     446             :          https://github.com/anza-xyz/agave/blob/c88e6df566c5c17d71e9574785755683a8fb033a/runtime/src/bank/fee_distribution.rs#L207
     447             :        */
     448           0 :       leftover_lamports--;
     449           0 :       validator_stakes[i].stake++;
     450           0 :     }
     451             : 
     452           0 :     for( i = 0; i < num_validator_stakes; i++ ) {
     453           0 :       ulong rent_to_be_paid = validator_stakes[i].stake;
     454             : 
     455           0 :       if( rent_to_be_paid > 0 ) {
     456           0 :         fd_pubkey_t pubkey = validator_stakes[i].pubkey;
     457             : 
     458           0 :         FD_BORROWED_ACCOUNT_DECL(rec);
     459             : 
     460           0 :         int err = fd_acc_mgr_view( slot_ctx->acc_mgr, slot_ctx->funk_txn, &pubkey, rec );
     461           0 :         if( FD_UNLIKELY(err) ) {
     462           0 :           FD_LOG_WARNING(( "cannot view pubkey %s. fd_acc_mgr_view failed (%d)", FD_BASE58_ENC_32_ALLOCA( &pubkey ), err ));
     463           0 :           leftover_lamports = fd_ulong_sat_add( leftover_lamports, rent_to_be_paid );
     464           0 :           continue;
     465           0 :         }
     466             : 
     467           0 :         if( FD_LIKELY( validate_fee_collector_account ) ) {
     468           0 :           ulong burn;
     469           0 :           if( FD_UNLIKELY( burn=fd_runtime_validate_fee_collector( slot_ctx, rec, rent_to_be_paid ) ) ) {
     470           0 :             if( FD_UNLIKELY( burn!=rent_to_be_paid ) ) {
     471           0 :               FD_LOG_ERR(( "expected burn(%lu)==rent_to_be_paid(%lu)", burn, rent_to_be_paid ));
     472           0 :             }
     473           0 :             leftover_lamports = fd_ulong_sat_add( leftover_lamports, rent_to_be_paid );
     474           0 :             continue;
     475           0 :           }
     476           0 :         }
     477             : 
     478           0 :         err = fd_acc_mgr_modify( slot_ctx->acc_mgr, slot_ctx->funk_txn, &pubkey, 0, 0UL, rec );
     479           0 :         if( FD_UNLIKELY(err) ) {
     480           0 :           FD_LOG_WARNING(( "cannot modify pubkey %s. fd_acc_mgr_modify failed (%d)", FD_BASE58_ENC_32_ALLOCA( &pubkey ), err ));
     481           0 :           leftover_lamports = fd_ulong_sat_add( leftover_lamports, rent_to_be_paid );
     482           0 :           continue;
     483           0 :         }
     484           0 :         rec->meta->info.lamports += rent_to_be_paid;
     485           0 :       }
     486           0 :     } // end of iteration over validator_stakes
     487             : 
     488           0 :     ulong old = slot_ctx->slot_bank.capitalization;
     489           0 :     slot_ctx->slot_bank.capitalization = fd_ulong_sat_sub(slot_ctx->slot_bank.capitalization, leftover_lamports);
     490           0 :     FD_LOG_DEBUG(( "fd_runtime_distribute_rent_to_validators: burn %lu, capitalization %lu->%lu ", leftover_lamports, old, slot_ctx->slot_bank.capitalization ));
     491             : 
     492           0 :   } FD_SCRATCH_SCOPE_END;
     493           0 : }
     494             : 
     495             : 
     496             : static void
     497           0 : fd_runtime_distribute_rent( fd_exec_slot_ctx_t * slot_ctx ) {
     498           0 :   ulong total_rent_collected = slot_ctx->slot_bank.collected_rent;
     499           0 :   fd_epoch_bank_t * epoch_bank = fd_exec_epoch_ctx_epoch_bank( slot_ctx->epoch_ctx );
     500           0 :   ulong burned_portion = fd_runtime_calculate_rent_burn( total_rent_collected, &epoch_bank->rent );
     501           0 :   slot_ctx->slot_bank.capitalization = fd_ulong_sat_sub( slot_ctx->slot_bank.capitalization, burned_portion );
     502           0 :   ulong rent_to_be_distributed = total_rent_collected - burned_portion;
     503             : 
     504           0 :   FD_LOG_DEBUG(( "rent distribution - slot: %lu, burned_lamports: %lu, distributed_lamports: %lu, total_rent_collected: %lu", slot_ctx->slot_bank.slot, burned_portion, rent_to_be_distributed, total_rent_collected ));
     505           0 :   if( rent_to_be_distributed == 0 ) {
     506           0 :     return;
     507           0 :   }
     508             : 
     509           0 :   fd_runtime_distribute_rent_to_validators( slot_ctx, rent_to_be_distributed );
     510           0 : }
     511             : 
     512             : static int
     513           0 : fd_runtime_run_incinerator( fd_exec_slot_ctx_t * slot_ctx ) {
     514           0 :   FD_BORROWED_ACCOUNT_DECL( rec );
     515             : 
     516           0 :   int err = fd_acc_mgr_modify( slot_ctx->acc_mgr, slot_ctx->funk_txn, &fd_sysvar_incinerator_id, 0, 0UL, rec );
     517           0 :   if( FD_UNLIKELY( err!=FD_ACC_MGR_SUCCESS ) ) {
     518             :     // TODO: not really an error! This is fine!
     519           0 :     return -1;
     520           0 :   }
     521             : 
     522           0 :   slot_ctx->slot_bank.capitalization = fd_ulong_sat_sub( slot_ctx->slot_bank.capitalization, rec->const_meta->info.lamports );
     523           0 :   rec->meta->info.lamports           = 0UL;
     524             : 
     525           0 :   return 0;
     526           0 : }
     527             : 
     528             : static void
     529           0 : fd_runtime_freeze( fd_exec_slot_ctx_t * slot_ctx ) {
     530             : 
     531             :   /* https://github.com/anza-xyz/agave/blob/ced98f1ebe73f7e9691308afa757323003ff744f/runtime/src/bank.rs#L2820-L2821 */
     532           0 :   fd_runtime_collect_rent( slot_ctx );
     533             :   // self.collect_fees();
     534             : 
     535           0 :   fd_sysvar_recent_hashes_update( slot_ctx );
     536             : 
     537           0 :   if( !FD_FEATURE_ACTIVE(slot_ctx, disable_fees_sysvar) )
     538           0 :     fd_sysvar_fees_update(slot_ctx);
     539             : 
     540           0 :   ulong fees = 0UL;
     541           0 :   ulong burn = 0UL;
     542           0 :   if ( FD_FEATURE_ACTIVE( slot_ctx, reward_full_priority_fee ) ) {
     543           0 :     ulong half_fee = slot_ctx->slot_bank.collected_execution_fees / 2;
     544           0 :     fees = fd_ulong_sat_add( slot_ctx->slot_bank.collected_priority_fees, slot_ctx->slot_bank.collected_execution_fees - half_fee );
     545           0 :     burn = half_fee;
     546           0 :   } else {
     547           0 :     ulong total_fees = fd_ulong_sat_add( slot_ctx->slot_bank.collected_execution_fees, slot_ctx->slot_bank.collected_priority_fees );
     548           0 :     ulong half_fee = total_fees / 2;
     549           0 :     fees = total_fees - half_fee;
     550           0 :     burn = half_fee;
     551           0 :   }
     552           0 :   if( FD_LIKELY( fees ) ) {
     553             :     // Look at collect_fees... I think this was where I saw the fee payout..
     554           0 :     FD_BORROWED_ACCOUNT_DECL(rec);
     555             : 
     556           0 :     do {
     557             :       /* do_create=1 because we might wanna pay fees to a leader
     558             :          account that we've purged due to 0 balance. */
     559           0 :       fd_pubkey_t const * leader = fd_epoch_leaders_get( fd_exec_epoch_ctx_leaders( slot_ctx->epoch_ctx ), slot_ctx->slot_bank.slot );
     560           0 :       int err = fd_acc_mgr_modify( slot_ctx->acc_mgr, slot_ctx->funk_txn, leader, 1, 0UL, rec );
     561           0 :       if( FD_UNLIKELY(err) ) {
     562           0 :         FD_LOG_WARNING(("fd_runtime_freeze: fd_acc_mgr_modify for leader (%s) failed (%d)", FD_BASE58_ENC_32_ALLOCA( leader ), err));
     563           0 :         burn = fd_ulong_sat_add( burn, fees );
     564           0 :         break;
     565           0 :       }
     566             : 
     567           0 :       if ( FD_LIKELY( FD_FEATURE_ACTIVE( slot_ctx, validate_fee_collector_account ) ) ) {
     568           0 :         ulong _burn;
     569           0 :         if( FD_UNLIKELY( _burn=fd_runtime_validate_fee_collector( slot_ctx, rec, fees ) ) ) {
     570           0 :           if( FD_UNLIKELY( _burn!=fees ) ) {
     571           0 :             FD_LOG_ERR(( "expected _burn(%lu)==fees(%lu)", _burn, fees ));
     572           0 :           }
     573           0 :           burn = fd_ulong_sat_add( burn, fees );
     574           0 :           FD_LOG_WARNING(("fd_runtime_freeze: burned %lu", fees ));
     575           0 :           break;
     576           0 :         }
     577           0 :       }
     578             : 
     579           0 :       rec->meta->info.lamports += fees;
     580           0 :       rec->meta->slot = slot_ctx->slot_bank.slot;
     581             : 
     582           0 :       fd_blockstore_start_write( slot_ctx->blockstore );
     583           0 :       fd_block_t * blk = slot_ctx->block;
     584           0 :       blk->rewards.collected_fees = fees;
     585           0 :       blk->rewards.post_balance = rec->meta->info.lamports;
     586           0 :       memcpy( blk->rewards.leader.uc, leader->uc, sizeof(fd_hash_t) );
     587           0 :       fd_blockstore_end_write( slot_ctx->blockstore );
     588           0 :     } while(0);
     589             : 
     590           0 :     ulong old = slot_ctx->slot_bank.capitalization;
     591           0 :     slot_ctx->slot_bank.capitalization = fd_ulong_sat_sub( slot_ctx->slot_bank.capitalization, burn);
     592           0 :     FD_LOG_DEBUG(( "fd_runtime_freeze: burn %lu, capitalization %lu->%lu ", burn, old, slot_ctx->slot_bank.capitalization));
     593             : 
     594           0 :     slot_ctx->slot_bank.collected_execution_fees = 0;
     595           0 :     slot_ctx->slot_bank.collected_priority_fees = 0;
     596           0 :   }
     597             : 
     598           0 :   fd_runtime_distribute_rent( slot_ctx );
     599           0 :   fd_runtime_run_incinerator( slot_ctx );
     600             : 
     601           0 :   FD_LOG_DEBUG(( "fd_runtime_freeze: capitalization %lu ", slot_ctx->slot_bank.capitalization));
     602           0 :   slot_ctx->slot_bank.collected_rent = 0;
     603           0 : }
     604             : 
     605        2877 : #define FD_RENT_EXEMPT (-1L)
     606             : 
     607             : static long
     608             : fd_runtime_get_rent_due( fd_exec_slot_ctx_t const * slot_ctx,
     609             :                          fd_account_meta_t *        acc,
     610        2139 :                          ulong                      epoch ) {
     611             : 
     612        2139 :   fd_epoch_bank_t     * epoch_bank     = fd_exec_epoch_ctx_epoch_bank( slot_ctx->epoch_ctx );
     613        2139 :   fd_epoch_schedule_t * schedule       = &epoch_bank->rent_epoch_schedule;
     614        2139 :   fd_rent_t           * rent           = &epoch_bank->rent;
     615        2139 :   double                slots_per_year = epoch_bank->slots_per_year;
     616             : 
     617        2139 :   fd_solana_account_meta_t *info = &acc->info;
     618             : 
     619             :   /* Nothing due if account is rent-exempt
     620             :      https://github.com/anza-xyz/agave/blob/v2.0.10/sdk/src/rent_collector.rs#L90 */
     621        2139 :   ulong min_balance = fd_rent_exempt_minimum_balance( rent, acc->dlen );
     622        2139 :   if( info->lamports>=min_balance ) {
     623        2043 :     return FD_RENT_EXEMPT;
     624        2043 :   }
     625             : 
     626             :   /* Count the number of slots that have passed since last collection. This
     627             :      inlines the agave function get_slots_in_peohc
     628             :      https://github.com/anza-xyz/agave/blob/v2.0.10/sdk/src/rent_collector.rs#L93-L98 */
     629          96 :   ulong slots_elapsed = 0UL;
     630          96 :   if( FD_UNLIKELY( info->rent_epoch<schedule->first_normal_epoch ) ) {
     631             :     /* Count the slots before the first normal epoch separately */
     632         378 :     for( ulong i=info->rent_epoch; i<schedule->first_normal_epoch && i<=epoch; i++ ) {
     633         351 :       slots_elapsed += fd_epoch_slot_cnt( schedule, i+1UL );
     634         351 :     }
     635          27 :     slots_elapsed += fd_ulong_sat_sub( epoch+1UL, schedule->first_normal_epoch ) * schedule->slots_per_epoch;
     636          27 :   }
     637             :   // slots_elapsed should remain 0 if rent_epoch is greater than epoch
     638          69 :   else if( info->rent_epoch<=epoch ) {
     639           0 :     slots_elapsed = (epoch - info->rent_epoch + 1UL) * schedule->slots_per_epoch;
     640           0 :   }
     641             :   /* Consensus-critical use of doubles :( */
     642             : 
     643          96 :   double years_elapsed;
     644          96 :   if( FD_LIKELY( slots_per_year!=0.0 ) ) {
     645          96 :     years_elapsed = (double)slots_elapsed / slots_per_year;
     646          96 :   } else {
     647           0 :     years_elapsed = 0.0;
     648           0 :   }
     649             : 
     650          96 :   ulong lamports_per_year = rent->lamports_per_uint8_year * (acc->dlen + 128UL);
     651             :   /* https://github.com/anza-xyz/agave/blob/d2124a995f89e33c54f41da76bfd5b0bd5820898/sdk/src/rent_collector.rs#L108 */
     652             :   /* https://github.com/anza-xyz/agave/blob/d2124a995f89e33c54f41da76bfd5b0bd5820898/sdk/program/src/rent.rs#L95 */
     653          96 :   return (long)fd_rust_cast_double_to_ulong(years_elapsed * (double)lamports_per_year);
     654        2139 : }
     655             : 
     656             : /* https://github.com/anza-xyz/agave/blob/v2.0.10/sdk/src/rent_collector.rs#L117-149 */
     657             : /* Collect rent from an account. Returns the amount of rent collected. */
     658             : static ulong
     659             : fd_runtime_collect_from_existing_account( fd_exec_slot_ctx_t const * slot_ctx,
     660             :                                           fd_account_meta_t *        acc,
     661             :                                           fd_pubkey_t const *        pubkey,
     662       14247 :                                           ulong                      epoch ) {
     663       14247 :   ulong collected_rent = 0UL;
     664       26820 :   #define NO_RENT_COLLECTION_NOW (-1)
     665       14247 :   #define EXEMPT                 (-2)
     666       14265 :   #define COLLECT_RENT           (-3)
     667             : 
     668             :   /* An account must be hashed regardless of if rent is collected from it. */
     669       14247 :   acc->slot = slot_ctx->slot_bank.slot;
     670             : 
     671             :   /* Inlining calculate_rent_result
     672             :      https://github.com/anza-xyz/agave/blob/v2.0.10/sdk/src/rent_collector.rs#L153-184 */
     673       14247 :   int calculate_rent_result = COLLECT_RENT;
     674             : 
     675             :   /* RentResult::NoRentCollectionNow */
     676       14247 :   if( FD_LIKELY( acc->info.rent_epoch==FD_RENT_EXEMPT_RENT_EPOCH || acc->info.rent_epoch>epoch ) ) {
     677       13410 :     calculate_rent_result = NO_RENT_COLLECTION_NOW;
     678       13410 :     goto rent_calculation;
     679       13410 :   }
     680             :   /* RentResult::Exempt */
     681             :   /* Inlining should_collect_rent() */
     682         837 :   int should_collect_rent = !( acc->info.executable ||
     683         837 :                                !memcmp( pubkey, &fd_sysvar_incinerator_id, sizeof(fd_pubkey_t) ) );
     684         837 :   if( !should_collect_rent ) {
     685           3 :     calculate_rent_result = EXEMPT;
     686           3 :     goto rent_calculation;
     687           3 :   }
     688             : 
     689             :   /* https://github.com/anza-xyz/agave/blob/v2.0.10/sdk/src/rent_collector.rs#L167-180 */
     690         834 :   long rent_due = fd_runtime_get_rent_due( slot_ctx, acc, epoch );
     691         834 :   if( rent_due==FD_RENT_EXEMPT ) {
     692         825 :     calculate_rent_result = EXEMPT;
     693         825 :   } else if( rent_due==0L ) {
     694           0 :     calculate_rent_result = NO_RENT_COLLECTION_NOW;
     695           9 :   } else {
     696           9 :     calculate_rent_result = COLLECT_RENT;
     697           9 :   }
     698             : 
     699       14247 :   rent_calculation:
     700       14247 :   switch( calculate_rent_result ) {
     701         828 :     case EXEMPT:
     702         828 :       acc->info.rent_epoch = FD_RENT_EXEMPT_RENT_EPOCH;
     703         828 :       break;
     704       13410 :     case NO_RENT_COLLECTION_NOW:
     705       13410 :       break;
     706           9 :     case COLLECT_RENT:
     707           9 :       if( FD_UNLIKELY( (ulong)rent_due>=acc->info.lamports ) ) {
     708             :         /* Reclaim account */
     709           9 :         collected_rent += (ulong)acc->info.lamports;
     710           9 :         acc->info.lamports                  = 0UL;
     711           9 :         acc->dlen                           = 0UL;
     712           9 :         fd_memset( acc->info.owner, 0, sizeof(acc->info.owner) );
     713           9 :       } else {
     714           0 :         collected_rent += (ulong)rent_due;
     715           0 :         acc->info.lamports                 -= (ulong)rent_due;
     716           0 :         acc->info.rent_epoch                = epoch+1UL;
     717           0 :       }
     718       14247 :   }
     719             : 
     720       14247 :   return collected_rent;
     721             : 
     722       14247 :   #undef NO_RENT_COLLECTION_NOW
     723       14247 :   #undef EXEMPT
     724       14247 :   #undef COLLECT_RENT
     725       14247 : }
     726             : 
     727             : 
     728             : /* fd_runtime_collect_rent_from_account performs rent collection duties.
     729             :    Although the Solana runtime prevents the creation of new accounts
     730             :    that are subject to rent, some older accounts are still undergo the
     731             :    rent collection process.  Updates the account's 'rent_epoch' if
     732             :    needed. Returns the amount of rent collected. */
     733             : /* https://github.com/anza-xyz/agave/blob/v2.0.10/svm/src/account_loader.rs#L71-96 */
     734             : ulong
     735             : fd_runtime_collect_rent_from_account( fd_exec_slot_ctx_t const * slot_ctx,
     736             :                                       fd_account_meta_t *        acc,
     737             :                                       fd_pubkey_t const *        key,
     738       18111 :                                       ulong                      epoch ) {
     739             : 
     740       18111 :   if( !FD_FEATURE_ACTIVE( slot_ctx, disable_rent_fees_collection ) ) {
     741       14247 :     return fd_runtime_collect_from_existing_account( slot_ctx, acc, key, epoch );
     742       14247 :   } else {
     743        3864 :     if( FD_UNLIKELY( acc->info.rent_epoch!=FD_RENT_EXEMPT_RENT_EPOCH &&
     744        3864 :                      fd_runtime_get_rent_due( slot_ctx, acc, epoch )==FD_RENT_EXEMPT ) ) {
     745        1218 :       acc->info.rent_epoch = ULONG_MAX;
     746        1218 :     }
     747        3864 :   }
     748        3864 :   return 0UL;
     749       18111 : }
     750             : 
     751             : #undef FD_RENT_EXEMPT
     752             : 
     753             : void
     754             : fd_runtime_write_transaction_status( fd_capture_ctx_t * capture_ctx,
     755             :                                      fd_exec_slot_ctx_t * slot_ctx,
     756             :                                      fd_exec_txn_ctx_t * txn_ctx,
     757           0 :                                      int exec_txn_err) {
     758             :   /* Look up solana-side transaction status details */
     759           0 :   fd_blockstore_t * blockstore = txn_ctx->slot_ctx->blockstore;
     760           0 :   uchar * sig = (uchar *)txn_ctx->_txn_raw->raw + txn_ctx->txn_descriptor->signature_off;
     761           0 :   fd_blockstore_start_read( blockstore );
     762           0 :   fd_txn_map_t * txn_map_entry = fd_blockstore_txn_query( blockstore, sig );
     763           0 :   if( FD_LIKELY( txn_map_entry != NULL ) ) {
     764           0 :     void * meta = fd_wksp_laddr_fast( fd_blockstore_wksp( blockstore ), txn_map_entry->meta_gaddr );
     765             : 
     766           0 :     fd_solblock_TransactionStatusMeta txn_status = {0};
     767             :     /* Need to handle case for ledgers where transaction status is not available.
     768             :         This case will be handled in fd_solcap_diff. */
     769           0 :     ulong fd_cus_consumed     = txn_ctx->compute_unit_limit - txn_ctx->compute_meter;
     770           0 :     ulong solana_cus_consumed = ULONG_MAX;
     771           0 :     ulong solana_txn_err      = ULONG_MAX;
     772           0 :     if( FD_LIKELY( meta != NULL ) ) {
     773           0 :       pb_istream_t stream = pb_istream_from_buffer( meta, txn_map_entry->meta_sz );
     774           0 :       fd_blockstore_end_read( blockstore );
     775           0 :       if ( pb_decode( &stream, fd_solblock_TransactionStatusMeta_fields, &txn_status ) == false ) {
     776           0 :         FD_LOG_WARNING(("no txn_status decoding found sig=%s (%s)", FD_BASE58_ENC_64_ALLOCA( sig ), PB_GET_ERROR(&stream)));
     777           0 :       }
     778           0 :       if ( txn_status.has_compute_units_consumed ) {
     779           0 :         solana_cus_consumed = txn_status.compute_units_consumed;
     780           0 :       }
     781           0 :       if ( txn_status.has_err ) {
     782           0 :         solana_txn_err = txn_status.err.err->bytes[0];
     783           0 :       }
     784             : 
     785           0 :       fd_solcap_Transaction txn = {
     786           0 :         .slot            = slot_ctx->slot_bank.slot,
     787           0 :         .fd_txn_err      = exec_txn_err,
     788           0 :         .fd_custom_err   = txn_ctx->custom_err,
     789           0 :         .solana_txn_err  = solana_txn_err,
     790           0 :         .fd_cus_used     = fd_cus_consumed,
     791           0 :         .solana_cus_used = solana_cus_consumed,
     792           0 :         .instr_err_idx = txn_ctx->instr_err_idx == INT_MAX ? -1 : txn_ctx->instr_err_idx,
     793           0 :       };
     794           0 :       memcpy( txn.txn_sig, sig, sizeof(fd_signature_t) );
     795             : 
     796           0 :       fd_exec_instr_ctx_t const * failed_instr = txn_ctx->failed_instr;
     797           0 :       if( failed_instr ) {
     798           0 :         assert( failed_instr->depth < 4 );
     799           0 :         txn.instr_err               = failed_instr->instr_err;
     800           0 :         txn.failed_instr_path_count = failed_instr->depth + 1;
     801           0 :         for( long j = failed_instr->depth; j>=0L; j-- ) {
     802           0 :           txn.failed_instr_path[j] = failed_instr->index;
     803           0 :           failed_instr             = failed_instr->parent;
     804           0 :         }
     805           0 :       }
     806             : 
     807           0 :       fd_solcap_write_transaction2( capture_ctx->capture, &txn );
     808           0 :     }
     809           0 :   } else {
     810           0 :     fd_blockstore_end_read( blockstore );
     811           0 :   }
     812           0 : }
     813             : 
     814             : static bool
     815           0 : encode_return_data( pb_ostream_t *stream, const pb_field_t *field, void * const *arg ) {
     816           0 :   fd_exec_txn_ctx_t * txn_ctx = (fd_exec_txn_ctx_t *)(*arg);
     817           0 :   pb_encode_tag_for_field(stream, field);
     818           0 :   pb_encode_string(stream, txn_ctx->return_data.data, txn_ctx->return_data.len );
     819           0 :   return 1;
     820           0 : }
     821             : 
     822             : static ulong
     823           0 : fd_txn_copy_meta( fd_exec_txn_ctx_t * txn_ctx, uchar * dest, ulong dest_sz ) {
     824           0 :   fd_solblock_TransactionStatusMeta txn_status = {0};
     825             : 
     826           0 :   txn_status.has_fee = 1;
     827           0 :   txn_status.fee = txn_ctx->execution_fee + txn_ctx->priority_fee;
     828             : 
     829           0 :   txn_status.has_compute_units_consumed = 1;
     830           0 :   txn_status.compute_units_consumed = txn_ctx->compute_unit_limit - txn_ctx->compute_meter;
     831             : 
     832           0 :   ulong readonly_cnt = 0;
     833           0 :   ulong writable_cnt = 0;
     834           0 :   if( txn_ctx->txn_descriptor->transaction_version == FD_TXN_V0 ) {
     835           0 :     fd_txn_acct_addr_lut_t const * addr_luts = fd_txn_get_address_tables_const( txn_ctx->txn_descriptor );
     836           0 :     for( ulong i = 0; i < txn_ctx->txn_descriptor->addr_table_lookup_cnt; i++ ) {
     837           0 :       fd_txn_acct_addr_lut_t const * addr_lut = &addr_luts[i];
     838           0 :       readonly_cnt += addr_lut->readonly_cnt;
     839           0 :       writable_cnt += addr_lut->writable_cnt;
     840           0 :     }
     841           0 :   }
     842             : 
     843           0 :   typedef PB_BYTES_ARRAY_T(32) my_ba_t;
     844           0 :   typedef union { my_ba_t my; pb_bytes_array_t normal; } union_ba_t;
     845           0 :   union_ba_t writable_ba[writable_cnt];
     846           0 :   pb_bytes_array_t * writable_baptr[writable_cnt];
     847           0 :   txn_status.loaded_writable_addresses_count = (uint)writable_cnt;
     848           0 :   txn_status.loaded_writable_addresses = writable_baptr;
     849           0 :   ulong idx2 = txn_ctx->txn_descriptor->acct_addr_cnt;
     850           0 :   for (ulong idx = 0; idx < writable_cnt; idx++) {
     851           0 :     pb_bytes_array_t * ba = writable_baptr[ idx ] = &writable_ba[ idx ].normal;
     852           0 :     ba->size = 32;
     853           0 :     fd_memcpy(ba->bytes, &txn_ctx->accounts[idx2++], 32);
     854           0 :   }
     855             : 
     856           0 :   union_ba_t readonly_ba[readonly_cnt];
     857           0 :   pb_bytes_array_t * readonly_baptr[readonly_cnt];
     858           0 :   txn_status.loaded_readonly_addresses_count = (uint)readonly_cnt;
     859           0 :   txn_status.loaded_readonly_addresses = readonly_baptr;
     860           0 :   for (ulong idx = 0; idx < readonly_cnt; idx++) {
     861           0 :     pb_bytes_array_t * ba = readonly_baptr[ idx ] = &readonly_ba[ idx ].normal;
     862           0 :     ba->size = 32;
     863           0 :     fd_memcpy(ba->bytes, &txn_ctx->accounts[idx2++], 32);
     864           0 :   }
     865           0 :   ulong acct_cnt = txn_ctx->accounts_cnt;
     866           0 :   FD_TEST(acct_cnt == idx2);
     867             : 
     868           0 :   txn_status.pre_balances_count = txn_status.post_balances_count = (pb_size_t)acct_cnt;
     869           0 :   uint64_t pre_balances[acct_cnt];
     870           0 :   txn_status.pre_balances = pre_balances;
     871           0 :   uint64_t post_balances[acct_cnt];
     872           0 :   txn_status.post_balances = post_balances;
     873             : 
     874           0 :   for (ulong idx = 0; idx < acct_cnt; idx++) {
     875           0 :     fd_borrowed_account_t const * acct = &txn_ctx->borrowed_accounts[idx];
     876           0 :     ulong pre = ( acct->starting_lamports == ULONG_MAX ? 0UL : acct->starting_lamports );
     877           0 :     pre_balances[idx] = pre;
     878           0 :     post_balances[idx] = ( acct->meta ? acct->meta->info.lamports :
     879           0 :                            ( acct->orig_meta ? acct->orig_meta->info.lamports : pre ) );
     880           0 :   }
     881             : 
     882           0 :   if( txn_ctx->return_data.len ) {
     883           0 :     txn_status.has_return_data = 1;
     884           0 :     txn_status.return_data.has_program_id = 1;
     885           0 :     fd_memcpy( txn_status.return_data.program_id, txn_ctx->return_data.program_id.uc, 32U );
     886           0 :     pb_callback_t data = { .funcs.encode = encode_return_data, .arg = txn_ctx };
     887           0 :     txn_status.return_data.data = data;
     888           0 :   }
     889             : 
     890           0 :   union {
     891           0 :     pb_bytes_array_t arr;
     892           0 :     uchar space[64];
     893           0 :   } errarr;
     894           0 :   pb_byte_t * errptr = errarr.arr.bytes;
     895           0 :   if( txn_ctx->custom_err != UINT_MAX ) {
     896           0 :     *(uint*)errptr = 8 /* Instruction error */;
     897           0 :     errptr += sizeof(uint);
     898           0 :     *errptr = (uchar)txn_ctx->instr_err_idx;
     899           0 :     errptr += 1;
     900           0 :     *(int*)errptr = FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
     901           0 :     errptr += sizeof(int);
     902           0 :     *(uint*)errptr = txn_ctx->custom_err;
     903           0 :     errptr += sizeof(uint);
     904           0 :     errarr.arr.size = (uint)(errptr - errarr.arr.bytes);
     905           0 :     txn_status.has_err = 1;
     906           0 :     txn_status.err.err = &errarr.arr;
     907           0 :   } else if( txn_ctx->exec_err ) {
     908           0 :     switch( txn_ctx->exec_err_kind ) {
     909           0 :       case FD_EXECUTOR_ERR_KIND_SYSCALL:
     910           0 :         break;
     911           0 :       case FD_EXECUTOR_ERR_KIND_INSTR:
     912           0 :         *(uint*)errptr = 8 /* Instruction error */;
     913           0 :         errptr += sizeof(uint);
     914           0 :         *errptr = (uchar)txn_ctx->instr_err_idx;
     915           0 :         errptr += 1;
     916           0 :         *(int*)errptr = txn_ctx->exec_err;
     917           0 :         errptr += sizeof(int);
     918           0 :         errarr.arr.size = (uint)(errptr - errarr.arr.bytes);
     919           0 :         txn_status.has_err = 1;
     920           0 :         txn_status.err.err = &errarr.arr;
     921           0 :         break;
     922           0 :       case FD_EXECUTOR_ERR_KIND_EBPF:
     923           0 :         break;
     924           0 :     }
     925           0 :   }
     926             : 
     927           0 :   if( dest == NULL ) {
     928           0 :     size_t sz = 0;
     929           0 :     bool r = pb_get_encoded_size( &sz, fd_solblock_TransactionStatusMeta_fields, &txn_status );
     930           0 :     if( !r ) {
     931           0 :       FD_LOG_WARNING(( "pb_get_encoded_size failed" ));
     932           0 :       return 0;
     933           0 :     }
     934           0 :     return sz + txn_ctx->log_collector.buf_sz;
     935           0 :   }
     936             : 
     937           0 :   pb_ostream_t stream = pb_ostream_from_buffer( dest, dest_sz );
     938           0 :   bool r = pb_encode( &stream, fd_solblock_TransactionStatusMeta_fields, &txn_status );
     939           0 :   if( !r ) {
     940           0 :     FD_LOG_WARNING(( "pb_encode failed" ));
     941           0 :     return 0;
     942           0 :   }
     943           0 :   pb_write( &stream, txn_ctx->log_collector.buf, txn_ctx->log_collector.buf_sz );
     944           0 :   return stream.bytes_written;
     945           0 : }
     946             : 
     947             : /* fd_runtime_finalize_txns_update_blockstore_meta() updates transaction metadata
     948             :    after execution.
     949             : 
     950             :    Execution recording is controlled by slot_ctx->enable_exec_recording, and this
     951             :    function does nothing if execution recording is off.  The following comments
     952             :    only apply when execution recording is on.
     953             : 
     954             :    Transaction metadata includes execution result (success/error), balance changes,
     955             :    transaction logs, ...  All this info is not part of consensus but can be retrieved,
     956             :    for instace, via RPC getTransaction.  Firedancer stores txn meta in the blockstore,
     957             :    in the same binary format as Agave, protobuf TransactionStatusMeta. */
     958             : static void
     959             : fd_runtime_finalize_txns_update_blockstore_meta( fd_exec_slot_ctx_t *         slot_ctx,
     960             :                                                  fd_execute_txn_task_info_t * task_info,
     961           0 :                                                  ulong                        txn_cnt ) {
     962             :   /* Nothing to do if execution recording is off */
     963           0 :   if( !slot_ctx->enable_exec_recording ) {
     964           0 :     return;
     965           0 :   }
     966             : 
     967           0 :   fd_blockstore_t * blockstore      = slot_ctx->blockstore;
     968           0 :   fd_wksp_t * blockstore_wksp       = fd_blockstore_wksp( blockstore );
     969           0 :   fd_alloc_t * blockstore_alloc     = fd_blockstore_alloc( blockstore );
     970           0 :   fd_txn_map_t * txn_map = fd_blockstore_txn_map( blockstore );
     971             : 
     972             :   /* Get the total size of all logs */
     973           0 :   ulong tot_meta_sz = 2*sizeof(ulong);
     974           0 :   for( ulong txn_idx = 0; txn_idx < txn_cnt; txn_idx++ ) {
     975             :     /* Prebalance compensation */
     976           0 :     fd_exec_txn_ctx_t * txn_ctx = task_info[txn_idx].txn_ctx;
     977           0 :     txn_ctx->borrowed_accounts[0].starting_lamports += (txn_ctx->execution_fee + txn_ctx->priority_fee);
     978             :     /* Get the size without the copy */
     979           0 :     tot_meta_sz += fd_txn_copy_meta( txn_ctx, NULL, 0 );
     980           0 :   }
     981           0 :   uchar * cur_laddr = fd_alloc_malloc( blockstore_alloc, 1, tot_meta_sz );
     982           0 :   if( cur_laddr == NULL ) {
     983           0 :     return;
     984           0 :   }
     985           0 :   uchar * const end_laddr = cur_laddr + tot_meta_sz;
     986             : 
     987           0 :   fd_blockstore_start_write( blockstore );
     988           0 :   fd_block_t * blk = slot_ctx->block;
     989             :   /* Link to previous allocation */
     990           0 :   ((ulong*)cur_laddr)[0] = blk->txns_meta_gaddr;
     991           0 :   ((ulong*)cur_laddr)[1] = blk->txns_meta_sz;
     992           0 :   blk->txns_meta_gaddr = fd_wksp_gaddr_fast( blockstore_wksp, cur_laddr );
     993           0 :   blk->txns_meta_sz    = tot_meta_sz;
     994           0 :   cur_laddr += 2*sizeof(ulong);
     995             : 
     996           0 :   for( ulong txn_idx = 0; txn_idx < txn_cnt; txn_idx++ ) {
     997           0 :     fd_exec_txn_ctx_t * txn_ctx = task_info[txn_idx].txn_ctx;
     998           0 :     ulong meta_sz = fd_txn_copy_meta( txn_ctx, cur_laddr, (size_t)(end_laddr - cur_laddr) );
     999           0 :     if( meta_sz ) {
    1000           0 :       ulong  meta_gaddr = fd_wksp_gaddr_fast( blockstore_wksp, cur_laddr );
    1001             : 
    1002             :       /* Update all the signatures */
    1003           0 :       char const * sig_p = (char const *)txn_ctx->_txn_raw->raw + txn_ctx->txn_descriptor->signature_off;
    1004           0 :       fd_txn_key_t sig;
    1005           0 :       for( uchar i=0U; i<txn_ctx->txn_descriptor->signature_cnt; i++ ) {
    1006           0 :         fd_memcpy( &sig, sig_p, sizeof(fd_txn_key_t) );
    1007           0 :         fd_txn_map_t * txn_map_entry = fd_txn_map_query( txn_map, &sig, NULL );
    1008           0 :         if( FD_LIKELY( txn_map_entry ) ) {
    1009           0 :           txn_map_entry->meta_gaddr = meta_gaddr;
    1010           0 :           txn_map_entry->meta_sz    = meta_sz;
    1011           0 :         }
    1012           0 :         sig_p += FD_ED25519_SIG_SZ;
    1013           0 :       }
    1014             : 
    1015           0 :       cur_laddr += meta_sz;
    1016           0 :     }
    1017           0 :     fd_log_collector_delete( &txn_ctx->log_collector );
    1018           0 :   }
    1019             : 
    1020           0 :   FD_TEST( cur_laddr == end_laddr );
    1021             : 
    1022           0 :   fd_blockstore_end_write( blockstore );
    1023           0 : }
    1024             : 
    1025             : /******************************************************************************/
    1026             : /* Block-Level Execution Preparation/Finalization                             */
    1027             : /******************************************************************************/
    1028             : 
    1029             : static int
    1030           0 : fd_runtime_block_sysvar_update_pre_execute( fd_exec_slot_ctx_t * slot_ctx ) {
    1031             :   // let (fee_rate_governor, fee_components_time_us) = measure_us!(
    1032             :   //     FeeRateGovernor::new_derived(&parent.fee_rate_governor, parent.signature_count())
    1033             :   // );
    1034             :   /* https://github.com/firedancer-io/solana/blob/dab3da8e7b667d7527565bddbdbecf7ec1fb868e/runtime/src/bank.rs#L1312-L1314 */
    1035           0 :   fd_sysvar_fees_new_derived( slot_ctx,
    1036           0 :                               slot_ctx->slot_bank.fee_rate_governor,
    1037           0 :                               slot_ctx->slot_bank.parent_signature_cnt );
    1038             : 
    1039             :   // TODO: move all these out to a fd_sysvar_update() call...
    1040           0 :   long clock_update_time      = -fd_log_wallclock();
    1041           0 :   fd_sysvar_clock_update( slot_ctx );
    1042           0 :   clock_update_time          += fd_log_wallclock();
    1043           0 :   double clock_update_time_ms = (double)clock_update_time * 1e-6;
    1044           0 :   FD_LOG_INFO(( "clock updated - slot: %lu, elapsed: %6.6f ms", slot_ctx->slot_bank.slot, clock_update_time_ms ));
    1045           0 :   if( !FD_FEATURE_ACTIVE(slot_ctx, disable_fees_sysvar ) ) {
    1046           0 :     fd_sysvar_fees_update(slot_ctx);
    1047           0 :   }
    1048             :   // It has to go into the current txn previous info but is not in slot 0
    1049           0 :   if( slot_ctx->slot_bank.slot != 0 ) {
    1050           0 :     fd_sysvar_slot_hashes_update( slot_ctx );
    1051           0 :   }
    1052           0 :   fd_sysvar_last_restart_slot_update( slot_ctx );
    1053             : 
    1054           0 :   return 0;
    1055           0 : }
    1056             : 
    1057             : int
    1058           0 : fd_runtime_block_execute_prepare( fd_exec_slot_ctx_t * slot_ctx ) {
    1059             :   /* Update block height */
    1060           0 :   slot_ctx->slot_bank.block_height += 1UL;
    1061           0 :   fd_blockstore_start_write( slot_ctx->blockstore );
    1062           0 :   fd_blockstore_block_height_update( slot_ctx->blockstore,
    1063           0 :                                      slot_ctx->slot_bank.slot,
    1064           0 :                                      slot_ctx->slot_bank.block_height );
    1065             : 
    1066             :   // TODO: this is not part of block execution, move it.
    1067           0 :   if( slot_ctx->slot_bank.slot != 0UL ) {
    1068           0 :     slot_ctx->block = fd_blockstore_block_query( slot_ctx->blockstore, slot_ctx->slot_bank.slot );
    1069           0 :     fd_blockstore_end_write( slot_ctx->blockstore );
    1070             : 
    1071           0 :     ulong             slot_idx;
    1072           0 :     fd_epoch_bank_t * epoch_bank = fd_exec_epoch_ctx_epoch_bank( slot_ctx->epoch_ctx );
    1073           0 :     ulong             prev_epoch = fd_slot_to_epoch( &epoch_bank->epoch_schedule, slot_ctx->slot_bank.prev_slot, &slot_idx );
    1074           0 :     ulong             new_epoch  = fd_slot_to_epoch( &epoch_bank->epoch_schedule, slot_ctx->slot_bank.slot, &slot_idx );
    1075           0 :     if( FD_UNLIKELY( slot_idx==1UL && new_epoch==0UL ) ) {
    1076             :       /* the block after genesis has a height of 1*/
    1077           0 :       slot_ctx->slot_bank.block_height = 1UL;
    1078           0 :     }
    1079             : 
    1080           0 :     if( FD_UNLIKELY( prev_epoch<new_epoch || !slot_idx ) ) {
    1081           0 :       FD_LOG_DEBUG(("Epoch boundary"));
    1082             :       /* Epoch boundary! */
    1083           0 :       fd_funk_start_write( slot_ctx->acc_mgr->funk );
    1084           0 :       fd_process_new_epoch( slot_ctx, new_epoch - 1UL );
    1085           0 :       fd_funk_end_write( slot_ctx->acc_mgr->funk );
    1086           0 :     }
    1087           0 :   } else {
    1088           0 :     fd_blockstore_end_write( slot_ctx->blockstore );
    1089           0 :   }
    1090             : 
    1091           0 :   slot_ctx->slot_bank.collected_execution_fees = 0UL;
    1092           0 :   slot_ctx->slot_bank.collected_priority_fees  = 0UL;
    1093           0 :   slot_ctx->slot_bank.collected_rent           = 0UL;
    1094           0 :   slot_ctx->signature_cnt                      = 0UL;
    1095           0 :   slot_ctx->txn_count                          = 0UL;
    1096           0 :   slot_ctx->nonvote_txn_count                  = 0UL;
    1097           0 :   slot_ctx->failed_txn_count                   = 0UL;
    1098           0 :   slot_ctx->nonvote_failed_txn_count           = 0UL;
    1099           0 :   slot_ctx->total_compute_units_used           = 0UL;
    1100             : 
    1101           0 :   if( slot_ctx->slot_bank.slot != 0UL && (
    1102           0 :       FD_FEATURE_ACTIVE( slot_ctx, enable_partitioned_epoch_reward ) ||
    1103           0 :       FD_FEATURE_ACTIVE( slot_ctx, partitioned_epoch_rewards_superfeature ) ) ) {
    1104           0 :     fd_funk_start_write( slot_ctx->acc_mgr->funk );
    1105           0 :     fd_distribute_partitioned_epoch_rewards( slot_ctx );
    1106           0 :     fd_funk_end_write( slot_ctx->acc_mgr->funk );
    1107           0 :   }
    1108             : 
    1109           0 :   fd_funk_start_write( slot_ctx->acc_mgr->funk );
    1110           0 :   int result = fd_runtime_block_sysvar_update_pre_execute( slot_ctx );
    1111           0 :   fd_funk_end_write( slot_ctx->acc_mgr->funk );
    1112           0 :   if( FD_UNLIKELY( result != 0 ) ) {
    1113           0 :     FD_LOG_WARNING(("updating sysvars failed"));
    1114           0 :     return result;
    1115           0 :   }
    1116             : 
    1117             :   /* Load sysvars into cache */
    1118           0 :   if( FD_UNLIKELY( result = fd_runtime_sysvar_cache_load( slot_ctx ) ) ) {
    1119             :     /* non-zero error */
    1120           0 :     return result;
    1121           0 :   }
    1122             : 
    1123           0 :   return FD_RUNTIME_EXECUTE_SUCCESS;
    1124           0 : }
    1125             : 
    1126             : int
    1127             : fd_runtime_block_execute_finalize_tpool( fd_exec_slot_ctx_t *    slot_ctx,
    1128             :                                          fd_capture_ctx_t *      capture_ctx,
    1129             :                                          fd_block_info_t const * block_info,
    1130           0 :                                          fd_tpool_t *            tpool ) {
    1131           0 :   fd_funk_start_write( slot_ctx->acc_mgr->funk );
    1132             : 
    1133           0 :   fd_sysvar_slot_history_update( slot_ctx );
    1134             : 
    1135             :   /* This slot is now "frozen" and can't be changed anymore. */
    1136           0 :   fd_runtime_freeze( slot_ctx );
    1137             : 
    1138           0 :   int result = fd_bpf_scan_and_create_bpf_program_cache_entry_tpool( slot_ctx, slot_ctx->funk_txn, tpool );
    1139           0 :   if( FD_UNLIKELY( result ) ) {
    1140           0 :     FD_LOG_WARNING(( "update bpf program cache failed" ));
    1141           0 :     fd_funk_end_write( slot_ctx->acc_mgr->funk );
    1142           0 :     return result;
    1143           0 :   }
    1144             : 
    1145           0 :   result = fd_update_hash_bank_tpool( slot_ctx, capture_ctx, &slot_ctx->slot_bank.banks_hash, block_info->signature_cnt, tpool );
    1146           0 :   if( FD_UNLIKELY( result!=FD_EXECUTOR_INSTR_SUCCESS ) ) {
    1147           0 :     FD_LOG_WARNING(( "hashing bank failed" ));
    1148           0 :     fd_funk_end_write( slot_ctx->acc_mgr->funk );
    1149           0 :     return result;
    1150           0 :   }
    1151             : 
    1152             :   /* We don't want to save the epoch bank at the end of every slot because it
    1153             :      should only be changing at the epoch boundary. */
    1154             : 
    1155           0 :   result = fd_runtime_save_slot_bank( slot_ctx );
    1156           0 :   if( FD_UNLIKELY( result!=FD_RUNTIME_EXECUTE_SUCCESS ) ) {
    1157           0 :     FD_LOG_WARNING(( "failed to save slot bank" ));
    1158           0 :     fd_funk_end_write( slot_ctx->acc_mgr->funk );
    1159           0 :     return result;
    1160           0 :   }
    1161             : 
    1162           0 :   fd_funk_end_write( slot_ctx->acc_mgr->funk );
    1163             : 
    1164           0 :   slot_ctx->total_compute_units_requested = 0UL;
    1165           0 :   for ( fd_account_compute_table_iter_t iter = fd_account_compute_table_iter_init( slot_ctx->account_compute_table );
    1166           0 :         !fd_account_compute_table_iter_done( slot_ctx->account_compute_table, iter );
    1167           0 :         iter = fd_account_compute_table_iter_next( slot_ctx->account_compute_table, iter ) ) {
    1168           0 :     fd_account_compute_elem_t * e = fd_account_compute_table_iter_ele( slot_ctx->account_compute_table, iter );
    1169           0 :     fd_account_compute_table_remove( slot_ctx->account_compute_table, &e->key );
    1170           0 :   }
    1171             : 
    1172           0 :   return FD_RUNTIME_EXECUTE_SUCCESS;
    1173           0 : }
    1174             : 
    1175             : /******************************************************************************/
    1176             : /* Transaction Level Execution Management                                     */
    1177             : /******************************************************************************/
    1178             : 
    1179             : /* fd_runtime_prepare_txns_start is responsible for setting up the task infos,
    1180             :    the slot_ctx, and for setting up the accessed accounts. */
    1181             : 
    1182             : int
    1183             : fd_runtime_prepare_txns_start( fd_exec_slot_ctx_t *         slot_ctx,
    1184             :                                fd_execute_txn_task_info_t * task_info,
    1185             :                                fd_txn_p_t *                 txns,
    1186       12741 :                                ulong                        txn_cnt ) {
    1187       12741 :   int res = 0;
    1188             :   /* Loop across transactions */
    1189       25482 :   for (ulong txn_idx = 0; txn_idx < txn_cnt; txn_idx++) {
    1190       12741 :     fd_txn_p_t * txn = &txns[txn_idx];
    1191             : 
    1192             :     /* Allocate/setup transaction context and task infos */
    1193       12741 :     task_info[txn_idx].txn_ctx      = fd_valloc_malloc( fd_scratch_virtual(), FD_EXEC_TXN_CTX_ALIGN, FD_EXEC_TXN_CTX_FOOTPRINT );
    1194       12741 :     fd_exec_txn_ctx_t * txn_ctx     = task_info[txn_idx].txn_ctx;
    1195       12741 :     task_info[txn_idx].exec_res     = 0;
    1196       12741 :     task_info[txn_idx].txn          = txn;
    1197       12741 :     fd_txn_t const * txn_descriptor = (fd_txn_t const *) txn->_;
    1198             : 
    1199       12741 :     fd_rawtxn_b_t raw_txn = { .raw = txn->payload, .txn_sz = (ushort)txn->payload_sz };
    1200             : 
    1201       12741 :     int err = fd_execute_txn_prepare_start( slot_ctx, txn_ctx, txn_descriptor, &raw_txn );
    1202       12741 :     if( FD_UNLIKELY( err ) ) {
    1203         264 :       task_info[txn_idx].exec_res = err;
    1204         264 :       txn->flags                  = 0U;
    1205         264 :       res |= err;
    1206         264 :     }
    1207       12741 :   }
    1208             : 
    1209       12741 :   return res;
    1210       12741 : }
    1211             : 
    1212             : /* fd_runtime_pre_execute_check is responsible for conducting many of the 
    1213             :    transaction sanitization checks. */
    1214             : 
    1215             : void
    1216       12741 : fd_runtime_pre_execute_check( fd_execute_txn_task_info_t * task_info ) {
    1217       12741 :   if( FD_UNLIKELY( !( task_info->txn->flags & FD_TXN_P_FLAGS_SANITIZE_SUCCESS ) ) ) {
    1218         264 :     return;
    1219         264 :   }
    1220             : 
    1221       12477 :   fd_exec_txn_ctx_t *          txn_ctx   = task_info->txn_ctx;
    1222             : 
    1223       12477 :   fd_funk_txn_t * parent_txn = txn_ctx->slot_ctx->funk_txn;
    1224       12477 :   txn_ctx->funk_txn          = parent_txn;
    1225       12477 :   fd_executor_setup_borrowed_accounts_for_txn( txn_ctx );
    1226             : 
    1227       12477 :   int err;
    1228             : 
    1229             :   /* https://github.com/anza-xyz/agave/blob/16de8b75ebcd57022409b422de557dd37b1de8db/sdk/src/transaction/sanitized.rs#L263-L275
    1230             :      TODO: Agave's precompile verification is done at the slot level, before batching and executing transactions. This logic should probably
    1231             :      be moved in the future. The Agave call heirarchy looks something like this:
    1232             :             process_single_slot
    1233             :                    v
    1234             :             confirm_full_slot
    1235             :                    v
    1236             :             confirm_slot_entries --------->
    1237             :                    v                      v
    1238             :             verify_transaction      process_entries
    1239             :                    v                      v
    1240             :             verify_precompiles      process_batches
    1241             :                                           v
    1242             :                                          ...
    1243             :                                           v
    1244             :                               load_and_execute_transactions
    1245             :                                           v
    1246             :                                          ...
    1247             :                                           v
    1248             :                                     load_accounts --> load_transaction_accounts
    1249             :                                           v
    1250             :                               general transaction execution
    1251             : 
    1252             :   */
    1253       12477 :   err = fd_executor_verify_precompiles( txn_ctx );
    1254       12477 :   if( FD_UNLIKELY( err!=FD_RUNTIME_EXECUTE_SUCCESS ) ) {
    1255        4113 :     task_info->txn->flags = 0U;
    1256        4113 :     task_info->exec_res   = err;
    1257        4113 :     return;
    1258        4113 :   }
    1259             : 
    1260             :   /* Post-sanitization checks. Called from `prepare_sanitized_batch()` which, for now, only is used
    1261             :      to lock the accounts and perform a couple basic validations.
    1262             :      https://github.com/anza-xyz/agave/blob/838c1952595809a31520ff1603a13f2c9123aa51/accounts-db/src/account_locks.rs#L118 */
    1263        8364 :   err = fd_executor_validate_account_locks( txn_ctx );
    1264        8364 :   if( FD_UNLIKELY( err!=FD_RUNTIME_EXECUTE_SUCCESS ) ) {
    1265         393 :     task_info->txn->flags = 0U;
    1266         393 :     task_info->exec_res   = err;
    1267         393 :     return;
    1268         393 :   }
    1269             : 
    1270             :   /* `load_and_execute_transactions()` -> `check_transactions()`
    1271             :      https://github.com/anza-xyz/agave/blob/ced98f1ebe73f7e9691308afa757323003ff744f/runtime/src/bank.rs#L3667-L3672 */
    1272        7971 :   err = fd_executor_check_transactions( txn_ctx );
    1273        7971 :   if( FD_UNLIKELY( err!=FD_RUNTIME_EXECUTE_SUCCESS ) ) {
    1274        1041 :     task_info->txn->flags = 0U;
    1275        1041 :     task_info->exec_res = err;
    1276        1041 :     return;
    1277        1041 :   }
    1278             : 
    1279             :   /* `load_and_execute_sanitized_transactions()` -> `validate_fees()` -> `validate_transaction_fee_payer()`
    1280             :      https://github.com/anza-xyz/agave/blob/ced98f1ebe73f7e9691308afa757323003ff744f/svm/src/transaction_processor.rs#L236-L249 */
    1281        6930 :   err = fd_executor_validate_transaction_fee_payer( txn_ctx );
    1282        6930 :   if( FD_UNLIKELY( err!=FD_RUNTIME_EXECUTE_SUCCESS ) ) {
    1283         204 :     task_info->txn->flags = 0U;
    1284         204 :     task_info->exec_res = err;
    1285         204 :     return;
    1286         204 :   }
    1287             : 
    1288             :   /* https://github.com/anza-xyz/agave/blob/ced98f1ebe73f7e9691308afa757323003ff744f/svm/src/transaction_processor.rs#L284-L296 */
    1289        6726 :   err = fd_executor_load_transaction_accounts( txn_ctx );
    1290        6726 :   if( FD_UNLIKELY( err!=FD_RUNTIME_EXECUTE_SUCCESS ) ) {
    1291        1536 :     task_info->txn->flags = 0U;
    1292        1536 :     task_info->exec_res   = err;
    1293        1536 :     return;
    1294        1536 :   }
    1295        6726 : }
    1296             : 
    1297             : /* fd_runtime_finalize_txn is a helper used by the non-tpool transaction
    1298             :    executor to finalize borrowed account changes back into funk. It also
    1299             :    handles txncache insertion and updates to the vote/stake cache. */
    1300             : 
    1301             : static int
    1302             : fd_runtime_finalize_txn( fd_exec_slot_ctx_t *         slot_ctx,
    1303             :                          fd_capture_ctx_t *           capture_ctx,
    1304           0 :                          fd_execute_txn_task_info_t * task_info ) {
    1305             : 
    1306           0 :   fd_exec_txn_ctx_t * txn_ctx      = task_info->txn_ctx;
    1307           0 :   int                 exec_txn_err = task_info->exec_res;
    1308             : 
    1309             :   /* Store transaction info including logs */
    1310           0 :   fd_runtime_finalize_txns_update_blockstore_meta( slot_ctx, task_info, 1UL );
    1311             : 
    1312             :   /* For ledgers that contain txn status, decode and write out for solcap */
    1313           0 :   if( capture_ctx != NULL && capture_ctx->capture && capture_ctx->capture_txns ) {
    1314             :     // TODO: probably need to get rid of this lock or special case it to not use funk's lock.
    1315           0 :     fd_funk_start_write( slot_ctx->acc_mgr->funk );
    1316           0 :     fd_runtime_write_transaction_status( capture_ctx, slot_ctx, txn_ctx, exec_txn_err );
    1317           0 :     fd_funk_end_write( slot_ctx->acc_mgr->funk );
    1318           0 :   }
    1319             : 
    1320           0 :   if( slot_ctx->status_cache ) {
    1321           0 :     fd_txncache_insert_t * status_insert = fd_scratch_alloc( alignof(fd_txncache_insert_t), sizeof(fd_txncache_insert_t) );
    1322           0 :     uchar *                results       = fd_scratch_alloc( alignof(uchar), sizeof(uchar) );
    1323             : 
    1324           0 :     results[0] = exec_txn_err == 0 ? 1 : 0;
    1325           0 :     fd_txncache_insert_t * curr_insert = &status_insert[0];
    1326           0 :     curr_insert->blockhash = ((uchar *)txn_ctx->_txn_raw->raw + txn_ctx->txn_descriptor->recent_blockhash_off);
    1327           0 :     curr_insert->slot = slot_ctx->slot_bank.slot;
    1328           0 :     fd_hash_t * hash = &txn_ctx->blake_txn_msg_hash;
    1329           0 :     curr_insert->txnhash = hash->uc;
    1330           0 :     curr_insert->result = &results[0];
    1331           0 :     if( !fd_txncache_insert_batch( slot_ctx->status_cache, status_insert, 1UL ) ) {
    1332           0 :       FD_LOG_DEBUG(("Status cache is full, this should not be possible"));
    1333           0 :     }
    1334           0 :   }
    1335             : 
    1336           0 :   if( FD_UNLIKELY( exec_txn_err ) ) {
    1337             : 
    1338             :     /* Save the fee_payer. Everything but the fee balance should be reset.
    1339             :        TODO: an optimization here could be to use a dirty flag in the
    1340             :        borrowed account. If the borrowed account data has been changed in
    1341             :        any way, then the full account can be rolled back as it is done now.
    1342             :        However, most of the time the account data is not changed, and only
    1343             :        the lamport balance has to change. */
    1344           0 :     fd_borrowed_account_t * borrowed_account = fd_borrowed_account_init( &txn_ctx->borrowed_accounts[0] );
    1345             : 
    1346           0 :     fd_acc_mgr_view( txn_ctx->acc_mgr, txn_ctx->funk_txn, &txn_ctx->accounts[0], borrowed_account );
    1347           0 :     memcpy( borrowed_account->pubkey->key, &txn_ctx->accounts[0], sizeof(fd_pubkey_t) );
    1348             : 
    1349           0 :     void * borrowed_account_data = fd_spad_alloc( txn_ctx->spad, FD_ACCOUNT_REC_ALIGN, FD_ACC_TOT_SZ_MAX );
    1350           0 :     fd_borrowed_account_make_modifiable( borrowed_account, borrowed_account_data );
    1351           0 :     borrowed_account->meta->info.lamports -= (txn_ctx->execution_fee + txn_ctx->priority_fee);
    1352             : 
    1353           0 :     fd_acc_mgr_save_non_tpool( slot_ctx->acc_mgr, slot_ctx->funk_txn, &txn_ctx->borrowed_accounts[0] );
    1354             : 
    1355           0 :     for( ulong i=1UL; i<txn_ctx->accounts_cnt; i++ ) {
    1356           0 :       if( txn_ctx->nonce_accounts[i] ) {
    1357           0 :         ushort                recent_blockhash_off = txn_ctx->txn_descriptor->recent_blockhash_off;
    1358           0 :         fd_hash_t *           recent_blockhash     = (fd_hash_t *)((uchar *)txn_ctx->_txn_raw->raw + recent_blockhash_off);
    1359           0 :         fd_block_hash_queue_t queue                = slot_ctx->slot_bank.block_hash_queue;
    1360           0 :         ulong                 queue_sz             = fd_hash_hash_age_pair_t_map_size( queue.ages_pool, queue.ages_root );
    1361           0 :         if( FD_UNLIKELY( !queue_sz ) ) {
    1362           0 :           FD_LOG_ERR(( "Blockhash queue is empty" ));
    1363           0 :         }
    1364             : 
    1365           0 :         if( !fd_executor_is_blockhash_valid_for_age( &queue, recent_blockhash, FD_RECENT_BLOCKHASHES_MAX_ENTRIES ) ) {
    1366           0 :           fd_acc_mgr_save_non_tpool( slot_ctx->acc_mgr, slot_ctx->funk_txn, &txn_ctx->borrowed_accounts[i] );
    1367           0 :         }
    1368           0 :       }
    1369           0 :     }
    1370           0 :   } else {
    1371             : 
    1372           0 :     int dirty_vote_acc  = txn_ctx->dirty_vote_acc;
    1373           0 :     int dirty_stake_acc = txn_ctx->dirty_stake_acc;
    1374             : 
    1375           0 :     for( ulong i=0UL; i<txn_ctx->accounts_cnt; i++ ) {
    1376             :       /* We are only interested in saving writable accounts and the fee
    1377             :          payer account. */
    1378           0 :       if( !fd_txn_account_is_writable_idx( txn_ctx, (int)i ) || i!=FD_FEE_PAYER_TXN_IDX ) {
    1379           0 :         continue;
    1380           0 :       }
    1381             : 
    1382           0 :       fd_borrowed_account_t * acc_rec = &txn_ctx->borrowed_accounts[i];
    1383             : 
    1384           0 :       if( dirty_vote_acc && 0==memcmp( acc_rec->const_meta->info.owner, &fd_solana_vote_program_id, sizeof(fd_pubkey_t) ) ) {
    1385             :         /* lock for inserting/modifying vote accounts in slot ctx. */
    1386           0 :         fd_funk_start_write( slot_ctx->acc_mgr->funk );
    1387           0 :         fd_vote_store_account( slot_ctx, acc_rec, txn_ctx->spad );
    1388           0 :         FD_SPAD_FRAME_BEGIN( txn_ctx->spad ) {
    1389           0 :           fd_vote_state_versioned_t vsv[1];
    1390           0 :           fd_bincode_decode_ctx_t decode_vsv =
    1391           0 :             { .data    = acc_rec->const_data,
    1392           0 :               .dataend = acc_rec->const_data + acc_rec->const_meta->dlen,
    1393           0 :               .valloc  = fd_spad_virtual( txn_ctx->spad ) };
    1394             : 
    1395           0 :           int err = fd_vote_state_versioned_decode( vsv, &decode_vsv );
    1396           0 :           if( err ) break; /* out of scratch scope */
    1397             : 
    1398           0 :           fd_vote_block_timestamp_t const * ts = NULL;
    1399           0 :           switch( vsv->discriminant ) {
    1400           0 :           case fd_vote_state_versioned_enum_v0_23_5:
    1401           0 :             ts = &vsv->inner.v0_23_5.last_timestamp;
    1402           0 :             break;
    1403           0 :           case fd_vote_state_versioned_enum_v1_14_11:
    1404           0 :             ts = &vsv->inner.v1_14_11.last_timestamp;
    1405           0 :             break;
    1406           0 :           case fd_vote_state_versioned_enum_current:
    1407           0 :             ts = &vsv->inner.current.last_timestamp;
    1408           0 :             break;
    1409           0 :           default:
    1410           0 :             __builtin_unreachable();
    1411           0 :           }
    1412             : 
    1413           0 :           fd_valloc_t valloc = fd_spad_virtual( txn_ctx->spad );
    1414           0 :           fd_vote_record_timestamp_vote_with_slot( slot_ctx, acc_rec->pubkey, ts->timestamp, ts->slot, valloc );
    1415           0 :         } FD_SPAD_FRAME_END;
    1416           0 :         fd_funk_end_write( slot_ctx->acc_mgr->funk );
    1417           0 :       }
    1418             : 
    1419           0 :       if( dirty_stake_acc && 0==memcmp( acc_rec->const_meta->info.owner, &fd_solana_stake_program_id, sizeof(fd_pubkey_t) ) ) {
    1420             :         // TODO: does this correctly handle stake account close?
    1421           0 :         fd_funk_start_write( slot_ctx->acc_mgr->funk );
    1422           0 :         fd_store_stake_delegation( slot_ctx, acc_rec );
    1423           0 :         fd_funk_end_write( slot_ctx->acc_mgr->funk );
    1424           0 :       }
    1425             : 
    1426           0 :       fd_acc_mgr_save_non_tpool( slot_ctx->acc_mgr, slot_ctx->funk_txn, &txn_ctx->borrowed_accounts[i] );
    1427           0 :     }
    1428           0 :   }
    1429             : 
    1430           0 :   FD_ATOMIC_FETCH_AND_ADD( &slot_ctx->signature_cnt, txn_ctx->txn_descriptor->signature_cnt );
    1431             : 
    1432           0 :   return 0;
    1433           0 : }
    1434             : 
    1435             : /* fd_runtime_prepare_execute_finalize_txn is responsible for processing the 
    1436             :    entire transaction end-to-end. */
    1437             : 
    1438             : static int
    1439             : fd_runtime_prepare_execute_finalize_txn( fd_exec_slot_ctx_t *         slot_ctx,
    1440             :                                          fd_spad_t *                  spad,
    1441             :                                          fd_capture_ctx_t *           capture_ctx,
    1442             :                                          fd_txn_p_t *                 txn,
    1443           0 :                                          fd_execute_txn_task_info_t * task_info ) {
    1444           0 :   FD_SPAD_FRAME_BEGIN( spad ) {
    1445           0 :   FD_SCRATCH_SCOPE_BEGIN {
    1446             : 
    1447           0 :   int res = 0;
    1448             : 
    1449           0 :   task_info->txn_ctx              = fd_valloc_malloc( fd_scratch_virtual(), FD_EXEC_TXN_CTX_ALIGN, FD_EXEC_TXN_CTX_FOOTPRINT );
    1450           0 :   fd_exec_txn_ctx_t * txn_ctx     = task_info->txn_ctx;
    1451           0 :   task_info->exec_res             = -1;
    1452           0 :   task_info->txn                  = txn;
    1453           0 :   fd_txn_t const * txn_descriptor = (fd_txn_t const *) txn->_;
    1454             : 
    1455           0 :   task_info->txn_ctx->spad = spad;
    1456             : 
    1457           0 :   fd_rawtxn_b_t raw_txn = { .raw = txn->payload, .txn_sz = (ushort)txn->payload_sz };
    1458             : 
    1459           0 :   res = fd_execute_txn_prepare_start( slot_ctx, txn_ctx, txn_descriptor, &raw_txn );
    1460           0 :   if( FD_UNLIKELY( res ) ) {
    1461           0 :     txn->flags = 0U;
    1462           0 :     return -1;
    1463           0 :   }
    1464             : 
    1465           0 :   if( FD_UNLIKELY( fd_executor_txn_verify( txn_ctx )!=0 ) ) {
    1466           0 :     FD_LOG_WARNING(( "sigverify failed: %s", FD_BASE58_ENC_64_ALLOCA( (uchar *)txn_ctx->_txn_raw->raw+txn_ctx->txn_descriptor->signature_off ) ));
    1467           0 :     task_info->txn->flags = 0U;
    1468           0 :     task_info->exec_res   = FD_RUNTIME_TXN_ERR_SIGNATURE_FAILURE;
    1469           0 :   }
    1470             : 
    1471           0 :   fd_runtime_pre_execute_check( task_info );
    1472           0 :   if( FD_UNLIKELY( !( task_info->txn->flags & FD_TXN_P_FLAGS_SANITIZE_SUCCESS ) ) ) {
    1473           0 :     res  = task_info->exec_res;
    1474           0 :     return -1;
    1475           0 :   }
    1476             : 
    1477             :   /* Execute */
    1478           0 :   task_info->txn->flags |= FD_TXN_P_FLAGS_EXECUTE_SUCCESS;
    1479           0 :   task_info->exec_res = fd_execute_txn( task_info->txn_ctx );
    1480             : 
    1481           0 :   if( task_info->exec_res==0 ) {
    1482           0 :     fd_txn_reclaim_accounts( task_info->txn_ctx );
    1483           0 :   }
    1484             : 
    1485           0 :   FD_ATOMIC_FETCH_AND_ADD( &slot_ctx->slot_bank.collected_execution_fees, task_info->txn_ctx->execution_fee );
    1486           0 :   FD_ATOMIC_FETCH_AND_ADD( &slot_ctx->slot_bank.collected_priority_fees, task_info->txn_ctx->priority_fee );
    1487           0 :   FD_ATOMIC_FETCH_AND_ADD( &slot_ctx->slot_bank.collected_rent, task_info->txn_ctx->collected_rent );
    1488             : 
    1489           0 :   fd_runtime_finalize_txn( slot_ctx, capture_ctx, task_info );
    1490             : 
    1491           0 :   return res;
    1492             : 
    1493           0 :   } FD_SCRATCH_SCOPE_END;
    1494           0 :   } FD_SPAD_FRAME_END;
    1495           0 : }
    1496             : 
    1497             : /* fd_runtime_process_txns is the entrypoint for processing a batch of txns. */
    1498             : 
    1499             : int
    1500             : fd_runtime_process_txns( fd_exec_slot_ctx_t * slot_ctx,
    1501             :                          fd_spad_t *          spad,
    1502             :                          fd_capture_ctx_t *   capture_ctx,
    1503             :                          fd_txn_p_t *         txns,
    1504           0 :                          ulong                txn_cnt ) {
    1505             : 
    1506           0 :   FD_SCRATCH_SCOPE_BEGIN {
    1507             : 
    1508           0 :     fd_execute_txn_task_info_t * task_infos = fd_scratch_alloc( 8, txn_cnt * sizeof(fd_execute_txn_task_info_t));
    1509             : 
    1510           0 :     for( ulong i=0UL; i<txn_cnt; i++ ) {
    1511           0 :       txns[i].flags = FD_TXN_P_FLAGS_SANITIZE_SUCCESS;
    1512           0 :     }
    1513             : 
    1514           0 :     for( ulong i=0UL; i<txn_cnt; i++ ) {
    1515           0 :       fd_runtime_prepare_execute_finalize_txn( slot_ctx, spad, capture_ctx, &txns[i], &task_infos[i] );
    1516           0 :     }
    1517             : 
    1518           0 :     FD_ATOMIC_FETCH_AND_ADD( &slot_ctx->slot_bank.transaction_count, txn_cnt );
    1519             : 
    1520           0 :     return 0;
    1521           0 :   } FD_SCRATCH_SCOPE_END;
    1522             : 
    1523           0 : }
    1524             : 
    1525             : /* fd_runtime_execute_txn_task is a tpool wrapper around transaction execution. */
    1526             : 
    1527             : static void FD_FN_UNUSED
    1528             : fd_runtime_execute_txn_task( void * tpool,
    1529             :                              ulong  t0 FD_PARAM_UNUSED, 
    1530             :                              ulong  t1 FD_PARAM_UNUSED,
    1531             :                              void * args FD_PARAM_UNUSED,
    1532             :                              void * reduce FD_PARAM_UNUSED, 
    1533             :                              ulong  stride FD_PARAM_UNUSED,
    1534             :                              ulong  l0 FD_PARAM_UNUSED, 
    1535             :                              ulong  l1 FD_PARAM_UNUSED,
    1536             :                              ulong  m0, 
    1537             :                              ulong  m1 FD_PARAM_UNUSED,
    1538             :                              ulong  n0 FD_PARAM_UNUSED, 
    1539           0 :                              ulong  n1 FD_PARAM_UNUSED ) {
    1540           0 : 
    1541           0 :   fd_execute_txn_task_info_t * task_info = (fd_execute_txn_task_info_t *)tpool + m0;
    1542           0 : 
    1543           0 :   if( !( task_info->txn->flags & FD_TXN_P_FLAGS_SANITIZE_SUCCESS ) ) {
    1544           0 :     task_info->exec_res = -1;
    1545           0 :     return;
    1546           0 :   }
    1547           0 : 
    1548           0 :   task_info->txn->flags |= FD_TXN_P_FLAGS_EXECUTE_SUCCESS;
    1549           0 :   task_info->exec_res    = fd_execute_txn( task_info->txn_ctx );
    1550           0 : 
    1551           0 :   if( task_info->exec_res != 0 ) {
    1552           0 :     return;
    1553           0 :   }
    1554           0 :   fd_txn_reclaim_accounts( task_info->txn_ctx );
    1555           0 : }
    1556             : 
    1557             : /* fd_txn_sigverify_task and fd_txn_pre_execute_checks_task are responisble
    1558             :    for the bulk of the pre-transaction execution checks in the runtime.
    1559             :    They aim to preserve the ordering present in the Agave client to match
    1560             :    parity in terms of error codes. Sigverify is kept seperate from the rest
    1561             :    of the transaction checks for fuzzing convenience.
    1562             : 
    1563             :    For reference this is the general code path which contains all relevant
    1564             :    pre-transactions checks in the v2.0.x Agave client from upstream
    1565             :    to downstream is as follows:
    1566             : 
    1567             :    confirm_slot_entries() which calls verify_ticks()
    1568             :    (which is currently unimplemented in firedancer) and
    1569             :    verify_transaction(). verify_transaction() calls verify_and_hash_message()
    1570             :    and verify_precompiles() which parallels fd_executor_txn_verify() and
    1571             :    fd_executor_verify_precompiles().
    1572             : 
    1573             :    process_entries() contains a duplicate account check which is part of
    1574             :    agave account lock acquiring. This is checked inline in
    1575             :    fd_txn_pre_execute_checks_task().
    1576             : 
    1577             :    load_and_execute_transactions() contains the function check_transactions().
    1578             :    This contains check_age() and check_status_cache() which is paralleled by
    1579             :    fd_check_transaction_age() and fd_executor_check_status_cache()
    1580             :    respectively.
    1581             : 
    1582             :    load_and_execute_sanitized_transactions() contains validate_fees()
    1583             :    which is responsible for executing the compute budget instructions,
    1584             :    validating the fee payer and collecting the fee. This is mirrored in
    1585             :    firedancer with fd_executor_compute_budget_program_execute_instructions()
    1586             :    and fd_executor_collect_fees(). load_and_execute_sanitized_transactions()
    1587             :    also checks the total data size of the accounts in load_accounts() and
    1588             :    validates the program accounts in load_transaction_accounts(). This
    1589             :    is paralled by fd_executor_load_transaction_accounts(). */
    1590             : 
    1591             : static void FD_FN_UNUSED
    1592             : fd_txn_sigverify_task( void *tpool,
    1593             :                                        ulong t0 FD_PARAM_UNUSED, ulong t1 FD_PARAM_UNUSED,
    1594             :                                        void *args FD_PARAM_UNUSED,
    1595             :                                        void *reduce FD_PARAM_UNUSED, ulong stride FD_PARAM_UNUSED,
    1596             :                                        ulong l0 FD_PARAM_UNUSED, ulong l1 FD_PARAM_UNUSED,
    1597             :                                        ulong m0, ulong m1 FD_PARAM_UNUSED,
    1598           0 :                                        ulong n0 FD_PARAM_UNUSED, ulong n1 FD_PARAM_UNUSED ) {
    1599           0 :   fd_execute_txn_task_info_t * task_info = (fd_execute_txn_task_info_t *)tpool + m0;
    1600             : 
    1601             :   /* the txn failed sanitize sometime earlier */
    1602           0 :   if( FD_UNLIKELY( !( task_info->txn->flags & FD_TXN_P_FLAGS_SANITIZE_SUCCESS ) ) ) {
    1603           0 :     return;
    1604           0 :   }
    1605             : 
    1606           0 :   fd_exec_txn_ctx_t * txn_ctx = task_info->txn_ctx;
    1607           0 :   if( FD_UNLIKELY( fd_executor_txn_verify( txn_ctx )!=0 ) ) {
    1608           0 :     FD_LOG_WARNING(("sigverify failed: %s", FD_BASE58_ENC_64_ALLOCA( (uchar *)txn_ctx->_txn_raw->raw+txn_ctx->txn_descriptor->signature_off ) ));
    1609           0 :     task_info->txn->flags = 0U;
    1610           0 :     task_info->exec_res   = FD_RUNTIME_TXN_ERR_SIGNATURE_FAILURE;
    1611           0 :   }
    1612             : 
    1613           0 : }
    1614             : 
    1615             : static void FD_FN_UNUSED
    1616             : fd_txn_prep_and_exec_task( void  *tpool,
    1617             :                            ulong t0 FD_PARAM_UNUSED,      ulong t1 FD_PARAM_UNUSED,
    1618             :                            void  *args FD_PARAM_UNUSED,
    1619             :                            void  *reduce FD_PARAM_UNUSED, ulong stride FD_PARAM_UNUSED,
    1620             :                            ulong l0 FD_PARAM_UNUSED,      ulong l1 FD_PARAM_UNUSED,
    1621             :                            ulong m0,                      ulong m1 FD_PARAM_UNUSED,
    1622           0 :                            ulong n0 FD_PARAM_UNUSED,      ulong n1 FD_PARAM_UNUSED ) {
    1623             : 
    1624           0 :   fd_execute_txn_task_info_t * task_info = (fd_execute_txn_task_info_t *)tpool + m0;
    1625           0 :   fd_exec_slot_ctx_t * slot_ctx = (fd_exec_slot_ctx_t *)args;
    1626             :   // fd_capture_ctx_t * capture_ctx = (fd_capture_ctx_t *)reduce;
    1627             : 
    1628             :   /* It is important to note that there is currently a 1-1 mapping between the
    1629             :      tiles and tpool threads at the time of this comment. Eventually, this will
    1630             :      change and the transaction context's spad will not be queried by tile
    1631             :      index as every tile will correspond to one CPU core. */
    1632           0 :   ulong tile_idx = fd_tile_idx();
    1633           0 :   task_info->txn_ctx->spad = task_info->spads[ tile_idx ];
    1634           0 :   if( FD_UNLIKELY( !task_info->txn_ctx->spad ) ) {
    1635           0 :     FD_LOG_ERR(("spad is NULL"));
    1636           0 :   }
    1637             : 
    1638           0 :   fd_runtime_pre_execute_check( task_info );
    1639             : 
    1640             :   /* Transaction sanitization is complete at this point. Now finish account
    1641             :     setup, execute the transaction, and reclaim dead accounts. */
    1642           0 :   if( FD_LIKELY( !task_info->exec_res ) ) {
    1643           0 :     task_info->txn->flags |= FD_TXN_P_FLAGS_EXECUTE_SUCCESS;
    1644           0 :     task_info->exec_res    = fd_execute_txn( task_info->txn_ctx );
    1645           0 :     fd_txn_reclaim_accounts( task_info->txn_ctx );
    1646           0 :   }
    1647             : 
    1648           0 :   FD_ATOMIC_FETCH_AND_ADD( &slot_ctx->slot_bank.collected_execution_fees, task_info->txn_ctx->execution_fee );
    1649           0 :   FD_ATOMIC_FETCH_AND_ADD( &slot_ctx->slot_bank.collected_priority_fees, task_info->txn_ctx->priority_fee );
    1650           0 :   FD_ATOMIC_FETCH_AND_ADD( &slot_ctx->slot_bank.collected_rent, task_info->txn_ctx->collected_rent );
    1651             : 
    1652             :   // fd_runtime_finalize_txn( slot_ctx, capture_ctx, task_info );
    1653             : 
    1654           0 : }
    1655             : 
    1656             : /* This task could be combined with the rest of the transaction checks that
    1657             :    exist in fd_runtime_prepare_txns_phase2_tpool, but creates a lot more
    1658             :    complexity to make the transaction fuzzer work. */
    1659             : static int
    1660             : fd_runtime_verify_txn_signatures_tpool( fd_execute_txn_task_info_t * task_info,
    1661             :                                         ulong txn_cnt,
    1662           0 :                                         fd_tpool_t * tpool ) {
    1663           0 :   int res = 0;
    1664           0 :   fd_tpool_exec_all_rrobin( tpool, 0, fd_tpool_worker_cnt( tpool ), fd_txn_sigverify_task, task_info, NULL, NULL, 1, 0, txn_cnt );
    1665           0 :   for( ulong txn_idx = 0; txn_idx < txn_cnt; txn_idx++ ) {
    1666           0 :     if( FD_UNLIKELY( !( task_info[txn_idx].txn->flags & FD_TXN_P_FLAGS_SANITIZE_SUCCESS ) ) ) {
    1667           0 :       task_info->exec_res = FD_RUNTIME_TXN_ERR_SIGNATURE_FAILURE;
    1668           0 :       res |= FD_RUNTIME_TXN_ERR_SIGNATURE_FAILURE;
    1669           0 :       break;
    1670           0 :     }
    1671           0 :   }
    1672           0 :   return res;
    1673           0 : }
    1674             : 
    1675             : /* This setup phase sets up the borrowed accounts in each transaction and
    1676             :    performs a series of checks on each of the transactions. */
    1677             : int
    1678             : fd_runtime_prep_and_exec_txns_tpool( fd_exec_slot_ctx_t *         slot_ctx,
    1679             :                                      fd_execute_txn_task_info_t * task_info,
    1680             :                                      ulong                        txn_cnt,
    1681           0 :                                      fd_tpool_t *                 tpool ) {
    1682           0 :   int res = 0;
    1683           0 :   FD_SCRATCH_SCOPE_BEGIN {
    1684             : 
    1685           0 :     fd_tpool_exec_all_rrobin( tpool, 0, fd_tpool_worker_cnt( tpool ), fd_txn_prep_and_exec_task, task_info, slot_ctx, task_info->txn_ctx->capture_ctx, 1, 0, txn_cnt );
    1686             : 
    1687           0 :     for( ulong txn_idx=0UL; txn_idx<txn_cnt; txn_idx++ ) {
    1688           0 :       if( FD_UNLIKELY( !( task_info[txn_idx].txn->flags & FD_TXN_P_FLAGS_SANITIZE_SUCCESS ) ) ) {
    1689           0 :         res |= task_info[txn_idx].exec_res;
    1690           0 :         continue;
    1691           0 :       }
    1692           0 :     }
    1693             : 
    1694           0 :   } FD_SCRATCH_SCOPE_END;
    1695           0 :   return res;
    1696           0 : }
    1697             : 
    1698             : /* https://github.com/anza-xyz/agave/blob/16de8b75ebcd57022409b422de557dd37b1de8db/accounts-db/src/accounts.rs#L700 */
    1699             : /* fd_runtime_finalize_txns_tpool is the tpool version of fd_runtime_finalize_txn. */
    1700             : 
    1701             : static int
    1702             : fd_runtime_finalize_txns_tpool( fd_exec_slot_ctx_t *         slot_ctx,
    1703             :                                 fd_capture_ctx_t *           capture_ctx,
    1704             :                                 fd_execute_txn_task_info_t * task_info,
    1705             :                                 ulong                        txn_cnt,
    1706           0 :                                 fd_tpool_t *                 tpool ) {
    1707           0 :   FD_SCRATCH_SCOPE_BEGIN {
    1708             : 
    1709             :     /* Store transaction metadata, including logs */
    1710           0 :     fd_runtime_finalize_txns_update_blockstore_meta( slot_ctx, task_info, txn_cnt );
    1711             : 
    1712           0 :     fd_txncache_insert_t * status_insert  = NULL;
    1713           0 :     uchar *                results        = NULL;
    1714           0 :     ulong                  num_cache_txns = 0UL;
    1715             : 
    1716             : 
    1717           0 :     if( FD_LIKELY( slot_ctx->status_cache ) ) {
    1718           0 :       status_insert = fd_scratch_alloc( alignof(fd_txncache_insert_t), txn_cnt * sizeof(fd_txncache_insert_t) );
    1719           0 :       results       = fd_scratch_alloc( alignof(uchar), txn_cnt * sizeof(uchar) );
    1720           0 :     }
    1721             : 
    1722           0 :     fd_borrowed_account_t * * accounts_to_save         = fd_scratch_alloc( 8UL, MAX_TX_ACCOUNT_LOCKS * txn_cnt * sizeof(fd_borrowed_account_t *) );
    1723           0 :     ulong                     acc_idx                  = 0UL;
    1724           0 :     ulong                     nonvote_txn_count        = 0UL;
    1725           0 :     ulong                     failed_txn_count         = 0UL;
    1726           0 :     ulong                     nonvote_failed_txn_count = 0UL;
    1727           0 :     ulong                     compute_units_used       = 0UL;
    1728             : 
    1729           0 :     for( ulong txn_idx=0UL; txn_idx<txn_cnt; txn_idx++ ) {
    1730             :       /* Transaction was skipped due to preparation failure. */
    1731           0 :       if( FD_UNLIKELY( !( task_info[txn_idx].txn->flags & FD_TXN_P_FLAGS_EXECUTE_SUCCESS ) ) ) {
    1732           0 :         continue;
    1733           0 :       }
    1734           0 :       fd_exec_txn_ctx_t * txn_ctx      = task_info[txn_idx].txn_ctx;
    1735           0 :       int                 exec_txn_err = task_info[txn_idx].exec_res;
    1736             : 
    1737             :       /* For ledgers that contain txn status, decode and write out for solcap */
    1738           0 :       if( FD_UNLIKELY( capture_ctx != NULL && capture_ctx->capture && capture_ctx->capture_txns ) ) {
    1739           0 :         fd_runtime_write_transaction_status( capture_ctx, slot_ctx, txn_ctx, exec_txn_err );
    1740           0 :       }
    1741             : 
    1742           0 :       slot_ctx->signature_cnt += txn_ctx->txn_descriptor->signature_cnt;
    1743             : 
    1744           0 :       int is_vote = fd_txn_is_simple_vote_transaction( txn_ctx->txn_descriptor,
    1745           0 :                                                        txn_ctx->_txn_raw->raw,
    1746           0 :                                                        fd_solana_vote_program_id.key );
    1747           0 :       if( is_vote ) {
    1748           0 :         if( FD_UNLIKELY( exec_txn_err ) ) {
    1749           0 :           failed_txn_count++;
    1750           0 :         }
    1751           0 :       } else {
    1752           0 :         nonvote_txn_count++;
    1753           0 :         if( FD_UNLIKELY( exec_txn_err ) ) {
    1754           0 :           nonvote_failed_txn_count++;
    1755           0 :           failed_txn_count++;
    1756           0 :         }
    1757           0 :       }
    1758           0 :       compute_units_used += txn_ctx->compute_unit_limit - txn_ctx->compute_meter;
    1759             : 
    1760           0 :       if( FD_LIKELY( slot_ctx->status_cache ) ) {
    1761           0 :         results[num_cache_txns]            = exec_txn_err == 0 ? 1 : 0;
    1762           0 :         fd_txncache_insert_t * curr_insert = &status_insert[num_cache_txns];
    1763           0 :         curr_insert->blockhash             = ((uchar *)txn_ctx->_txn_raw->raw + txn_ctx->txn_descriptor->recent_blockhash_off);
    1764           0 :         curr_insert->slot                  = slot_ctx->slot_bank.slot;
    1765           0 :         fd_hash_t *            hash        = &txn_ctx->blake_txn_msg_hash;
    1766           0 :         curr_insert->txnhash               = hash->uc;
    1767           0 :         curr_insert->result                = &results[num_cache_txns];
    1768           0 :         num_cache_txns++;
    1769           0 :       }
    1770             : 
    1771           0 :       if( FD_UNLIKELY( exec_txn_err ) ) {
    1772             :         /* Save the fee_payer. Everything but the fee balance should be reset.
    1773             :            TODO: an optimization here could be to use a dirty flag in the
    1774             :            borrowed account. If the borrowed account data has been changed in
    1775             :            any way, then the full account can be rolled back as it is done now.
    1776             :            However, most of the time the account data is not changed, and only
    1777             :            the lamport balance has to change. */
    1778           0 :         fd_borrowed_account_t * borrowed_account = fd_borrowed_account_init( &txn_ctx->borrowed_accounts[ FD_FEE_PAYER_TXN_IDX ] );
    1779             : 
    1780           0 :         fd_acc_mgr_view( txn_ctx->acc_mgr, txn_ctx->funk_txn, &txn_ctx->accounts[ FD_FEE_PAYER_TXN_IDX ], borrowed_account );
    1781           0 :         memcpy( borrowed_account->pubkey->key, &txn_ctx->accounts[ FD_FEE_PAYER_TXN_IDX ], sizeof(fd_pubkey_t) );
    1782             : 
    1783           0 :         void * borrowed_account_data = fd_spad_alloc( txn_ctx->spad, FD_ACCOUNT_REC_ALIGN, FD_ACC_TOT_SZ_MAX );
    1784           0 :         fd_borrowed_account_make_modifiable( borrowed_account, borrowed_account_data );
    1785           0 :         borrowed_account->meta->info.lamports -= (txn_ctx->execution_fee + txn_ctx->priority_fee);
    1786             : 
    1787           0 :         accounts_to_save[acc_idx++] = &txn_ctx->borrowed_accounts[ FD_FEE_PAYER_TXN_IDX ];
    1788           0 :         for( ulong i=1UL; i<txn_ctx->accounts_cnt; i++ ) {
    1789           0 :           if( txn_ctx->nonce_accounts[i] ) {
    1790           0 :             ushort                recent_blockhash_off = txn_ctx->txn_descriptor->recent_blockhash_off;
    1791           0 :             fd_hash_t *           recent_blockhash     = (fd_hash_t *)((uchar *)txn_ctx->_txn_raw->raw + recent_blockhash_off);
    1792           0 :             fd_block_hash_queue_t queue                = slot_ctx->slot_bank.block_hash_queue;
    1793           0 :             ulong                 queue_sz             = fd_hash_hash_age_pair_t_map_size( queue.ages_pool, queue.ages_root );
    1794           0 :             if( FD_UNLIKELY( !queue_sz ) ) {
    1795           0 :               FD_LOG_ERR(( "Blockhash queue is empty" ));
    1796           0 :             }
    1797             : 
    1798           0 :             if( !fd_executor_is_blockhash_valid_for_age( &queue, recent_blockhash, FD_RECENT_BLOCKHASHES_MAX_ENTRIES ) ) {
    1799           0 :               accounts_to_save[acc_idx++] = &txn_ctx->borrowed_accounts[i];
    1800           0 :             }
    1801           0 :             break;
    1802           0 :           }
    1803           0 :         }
    1804           0 :       } else {
    1805           0 :         int dirty_vote_acc  = txn_ctx->dirty_vote_acc;
    1806           0 :         int dirty_stake_acc = txn_ctx->dirty_stake_acc;
    1807             : 
    1808           0 :         for( ulong i=0UL; i<txn_ctx->accounts_cnt; i++ ) {
    1809             :           /* We are only interested in saving writable accounts and the fee
    1810             :              payer account. */
    1811           0 :           if( !fd_txn_account_is_writable_idx( txn_ctx, (int)i ) && i!=FD_FEE_PAYER_TXN_IDX ) {
    1812           0 :             continue;
    1813           0 :           }
    1814             : 
    1815           0 :           fd_borrowed_account_t * acc_rec = &txn_ctx->borrowed_accounts[i];
    1816             : 
    1817           0 :           if( dirty_vote_acc && !memcmp( acc_rec->const_meta->info.owner, &fd_solana_vote_program_id, sizeof(fd_pubkey_t) ) ) {
    1818           0 :             fd_vote_store_account( slot_ctx, acc_rec, txn_ctx->spad );
    1819           0 :             FD_SPAD_FRAME_BEGIN( txn_ctx->spad ) {
    1820           0 :               fd_vote_state_versioned_t vsv[1];
    1821           0 :               fd_bincode_decode_ctx_t decode_vsv =
    1822           0 :                 { .data    = acc_rec->const_data,
    1823           0 :                   .dataend = acc_rec->const_data + acc_rec->const_meta->dlen,
    1824           0 :                   .valloc  = fd_spad_virtual( txn_ctx->spad ) };
    1825             : 
    1826           0 :               int err = fd_vote_state_versioned_decode( vsv, &decode_vsv );
    1827           0 :               if( err ) break; /* out of scratch scope */
    1828             : 
    1829           0 :               fd_vote_block_timestamp_t const * ts = NULL;
    1830           0 :               switch( vsv->discriminant ) {
    1831           0 :               case fd_vote_state_versioned_enum_v0_23_5:
    1832           0 :                 ts = &vsv->inner.v0_23_5.last_timestamp;
    1833           0 :                 break;
    1834           0 :               case fd_vote_state_versioned_enum_v1_14_11:
    1835           0 :                 ts = &vsv->inner.v1_14_11.last_timestamp;
    1836           0 :                 break;
    1837           0 :               case fd_vote_state_versioned_enum_current:
    1838           0 :                 ts = &vsv->inner.current.last_timestamp;
    1839           0 :                 break;
    1840           0 :               default:
    1841           0 :                 __builtin_unreachable();
    1842           0 :               }
    1843             : 
    1844           0 :               fd_valloc_t valloc = fd_spad_virtual( txn_ctx->spad );
    1845           0 :               fd_vote_record_timestamp_vote_with_slot( slot_ctx, acc_rec->pubkey, ts->timestamp, ts->slot, valloc );
    1846           0 :             } FD_SPAD_FRAME_END;
    1847           0 :           }
    1848             : 
    1849           0 :           if( dirty_stake_acc && !memcmp( acc_rec->const_meta->info.owner, &fd_solana_stake_program_id, sizeof(fd_pubkey_t) ) ) {
    1850             :             // TODO: does this correctly handle stake account close?
    1851           0 :             fd_store_stake_delegation( slot_ctx, acc_rec );
    1852           0 :           }
    1853             : 
    1854           0 :           accounts_to_save[acc_idx++] = acc_rec;
    1855           0 :         }
    1856           0 :       }
    1857           0 :     }
    1858             : 
    1859             :     /* Accumulate transaction counters */
    1860             : 
    1861           0 :     FD_ATOMIC_FETCH_AND_ADD( &slot_ctx->nonvote_txn_count, nonvote_txn_count );
    1862           0 :     FD_ATOMIC_FETCH_AND_ADD( &slot_ctx->failed_txn_count, failed_txn_count );
    1863           0 :     FD_ATOMIC_FETCH_AND_ADD( &slot_ctx->nonvote_failed_txn_count, nonvote_failed_txn_count );
    1864           0 :     FD_ATOMIC_FETCH_AND_ADD( &slot_ctx->total_compute_units_used, compute_units_used );
    1865             : 
    1866             :     /* All the accounts have been accumulated and can be saved */
    1867             : 
    1868             :     // TODO: we need to use the txn ctx funk_txn, valloc, etc.
    1869           0 :     int err = fd_acc_mgr_save_many_tpool( slot_ctx->acc_mgr, slot_ctx->funk_txn, accounts_to_save, acc_idx, tpool );
    1870           0 :     if( FD_UNLIKELY( err!=FD_ACC_MGR_SUCCESS ) ) {
    1871           0 :       FD_LOG_ERR(( "failed to save edits to accounts" ));
    1872           0 :       return -1;
    1873           0 :     }
    1874             : 
    1875           0 :     if( FD_LIKELY( slot_ctx->status_cache ) ) {
    1876           0 :       if( FD_UNLIKELY( !fd_txncache_insert_batch( slot_ctx->status_cache, status_insert, num_cache_txns ) ) ) {
    1877           0 :         FD_LOG_WARNING(("Status cache is full, this should not be possible"));
    1878           0 :       }
    1879           0 :     }
    1880             : 
    1881           0 :   return 0;
    1882           0 :   } FD_SCRATCH_SCOPE_END;
    1883           0 : }
    1884             : 
    1885             : struct fd_pubkey_map_node {
    1886             :   ulong       pubkey;
    1887             :   uint        hash;
    1888             : };
    1889             : typedef struct fd_pubkey_map_node fd_pubkey_map_node_t;
    1890             : 
    1891             : #define MAP_NAME                fd_pubkey_map
    1892           0 : #define MAP_T                   fd_pubkey_map_node_t
    1893           0 : #define MAP_KEY                 pubkey
    1894           0 : #define MAP_KEY_T               ulong
    1895           0 : #define MAP_KEY_NULL            0
    1896           0 : #define MAP_KEY_INVAL( k )      k==0
    1897           0 : #define MAP_KEY_EQUAL( k0, k1 ) k0==k1
    1898             : #define MAP_KEY_EQUAL_IS_SLOW   1
    1899           0 : #define MAP_KEY_HASH( key )     ( (uint)key )
    1900             : #define MAP_MEMOIZE             1
    1901             : #include "../../util/tmpl/fd_map_dynamic.c"
    1902             : 
    1903             : /* return 0 on failure, 1 if exists, 2 if inserted */
    1904             : static uint
    1905             : fd_pubkey_map_insert_if_not_in( fd_pubkey_map_node_t * map,
    1906           0 :                                 fd_pubkey_t            pubkey ) {
    1907             :   /* Check if entry already exists */
    1908           0 :   ulong h = fd_hash( 0UL, &pubkey, sizeof( fd_pubkey_t ) );
    1909           0 :   fd_pubkey_map_node_t * entry = fd_pubkey_map_query( map, h, NULL );
    1910           0 :   if( entry )
    1911           0 :     return 1;
    1912             : 
    1913             :   /* Insert new */
    1914           0 :   entry = fd_pubkey_map_insert( map, h );
    1915           0 :   if( FD_UNLIKELY( !entry ) ) return 0;  /* check for internal map collision */
    1916             : 
    1917           0 :   return 2;
    1918           0 : }
    1919             : 
    1920             : /* fd_runtime_generate_wave is responsible for scheduling parallel transactions */
    1921             : 
    1922             : static void
    1923             : fd_runtime_generate_wave( fd_execute_txn_task_info_t * task_infos,
    1924             :                           ulong *                      prev_incomplete_txn_idxs,
    1925             :                           ulong                        prev_incomplete_txn_idxs_cnt,
    1926             :                           ulong                        prev_accounts_cnt,
    1927             :                           ulong *                      incomplete_txn_idxs,
    1928             :                           ulong *                      _incomplete_txn_idxs_cnt,
    1929             :                           ulong *                      _incomplete_accounts_cnt,
    1930             :                           fd_execute_txn_task_info_t * wave_task_infos,
    1931           0 :                           ulong *                      _wave_task_infos_cnt ) {
    1932           0 :   FD_SCRATCH_SCOPE_BEGIN {
    1933           0 :     int                    lg_slot_cnt  = fd_ulong_find_msb( prev_accounts_cnt ) + 1;
    1934           0 :     void *                 read_map_mem = fd_scratch_alloc( fd_pubkey_map_align(), fd_pubkey_map_footprint( lg_slot_cnt ) );
    1935           0 :     fd_pubkey_map_node_t * read_map     = fd_pubkey_map_join( fd_pubkey_map_new( read_map_mem, lg_slot_cnt ) );
    1936             : 
    1937           0 :     void *                 write_map_mem = fd_scratch_alloc( fd_pubkey_map_align(), fd_pubkey_map_footprint( lg_slot_cnt ) );
    1938           0 :     fd_pubkey_map_node_t * write_map     = fd_pubkey_map_join( fd_pubkey_map_new( write_map_mem, lg_slot_cnt ) );
    1939             : 
    1940           0 :     ulong incomplete_txn_idxs_cnt = 0UL;
    1941           0 :     ulong wave_task_infos_cnt     = 0UL;
    1942           0 :     ulong accounts_in_wave        = 0UL;
    1943           0 :     for( ulong i=0UL; i<prev_incomplete_txn_idxs_cnt; i++ ) {
    1944           0 :       ulong txn_idx           = prev_incomplete_txn_idxs[i];
    1945           0 :       uint  is_executable_now = 1;
    1946           0 :       fd_execute_txn_task_info_t * task_info = &task_infos[txn_idx];
    1947             : 
    1948           0 :       for( ulong j=0UL; j<task_info->txn_ctx->accounts_cnt; j++ ) {
    1949           0 :         ulong h = fd_hash( 0UL, &task_info->txn_ctx->accounts[j], sizeof( fd_pubkey_t ) );
    1950           0 :         if( fd_pubkey_map_query( write_map, h, NULL ) != NULL ) {
    1951           0 :           is_executable_now = 0;
    1952           0 :           break;
    1953           0 :         }
    1954           0 :         if( fd_txn_account_is_writable_idx( task_info->txn_ctx, (int)j ) ) {
    1955           0 :           if( fd_pubkey_map_query( read_map, h, NULL ) != NULL ) {
    1956           0 :             is_executable_now = 0;
    1957           0 :             break;
    1958           0 :           }
    1959           0 :         }
    1960           0 :       }
    1961             : 
    1962           0 :       if( !is_executable_now ) {
    1963           0 :         incomplete_txn_idxs[incomplete_txn_idxs_cnt++] = txn_idx;
    1964           0 :       } else {
    1965           0 :         wave_task_infos[wave_task_infos_cnt++] = *task_info;
    1966           0 :       }
    1967             : 
    1968             :       /* Include txn in wave */
    1969           0 :       for( ulong j=0UL; j<task_info->txn_ctx->accounts_cnt; j++ ) {
    1970           0 :         if( fd_txn_account_is_writable_idx( task_info->txn_ctx, (int)j ) ) {
    1971           0 :           uint ins_res = fd_pubkey_map_insert_if_not_in( write_map, task_info->txn_ctx->accounts[j] );
    1972           0 :           if( ins_res==2U ) {
    1973           0 :             accounts_in_wave++;
    1974           0 :           }
    1975           0 :         } else {
    1976           0 :           uint ins_res = fd_pubkey_map_insert_if_not_in( read_map, task_info->txn_ctx->accounts[j] );
    1977           0 :           if( ins_res==2UL ) {
    1978           0 :             accounts_in_wave++;
    1979           0 :           }
    1980           0 :         }
    1981           0 :       }
    1982             : 
    1983           0 :     }
    1984             : 
    1985           0 :     *_incomplete_txn_idxs_cnt = incomplete_txn_idxs_cnt;
    1986           0 :     *_incomplete_accounts_cnt = prev_accounts_cnt - accounts_in_wave;
    1987           0 :     *_wave_task_infos_cnt     = wave_task_infos_cnt;
    1988           0 :   } FD_SCRATCH_SCOPE_END;
    1989           0 : }
    1990             : 
    1991             : /* NOTE: Don't mess with this call without updating the transaction fuzzing harness appropriately!
    1992             :    fd_exec_instr_test.c:_txn_context_create_and_exec.
    1993             :    
    1994             :    This function is the tpool version of fd_runtime_process_txn. */
    1995             : int
    1996             : fd_runtime_process_txns_in_waves_tpool( fd_exec_slot_ctx_t * slot_ctx,
    1997             :                                         fd_capture_ctx_t *   capture_ctx,
    1998             :                                         fd_txn_p_t *         all_txns,
    1999             :                                         ulong                total_txn_cnt,
    2000             :                                         fd_tpool_t *         tpool,
    2001             :                                         fd_spad_t * *        spads,
    2002           0 :                                         ulong                spad_cnt ) {
    2003           0 :     int dump_txn = capture_ctx && slot_ctx->slot_bank.slot >= capture_ctx->dump_proto_start_slot && capture_ctx->dump_txn_to_pb;
    2004             : 
    2005             :     /* As a note, the batch size of 128 is a relatively arbitrary number. The
    2006             :        notion of batching here will change as the transaction execution model
    2007             :        changes with respect to transaction execution. */
    2008           0 :     #define BATCH_SIZE (128UL)
    2009           0 :     ulong batch_size = fd_ulong_min( fd_tile_cnt(), BATCH_SIZE );
    2010             : 
    2011           0 :     for( ulong i=0UL; i<total_txn_cnt; i++ ) {
    2012           0 :       all_txns[i].flags = FD_TXN_P_FLAGS_SANITIZE_SUCCESS;
    2013           0 :     }
    2014             : 
    2015           0 :     ulong num_batches = total_txn_cnt/batch_size;
    2016           0 :     ulong rem         = total_txn_cnt%batch_size;
    2017           0 :     num_batches      += rem ? 1UL : 0UL;
    2018             : 
    2019           0 :     int res = 0;
    2020           0 :     for( ulong i=0UL; i<num_batches; i++ ) {
    2021           0 :       FD_SCRATCH_SCOPE_BEGIN {
    2022             : 
    2023           0 :       fd_txn_p_t * txns    = all_txns + (batch_size * i);
    2024           0 :       ulong        txn_cnt = ((i+1UL==num_batches) && rem) ? rem : batch_size;
    2025             : 
    2026           0 :       fd_execute_txn_task_info_t * task_infos          = fd_scratch_alloc( 8UL, txn_cnt * sizeof(fd_execute_txn_task_info_t) );
    2027           0 :       fd_execute_txn_task_info_t * wave_task_infos     = fd_scratch_alloc( 8UL, txn_cnt * sizeof(fd_execute_txn_task_info_t) );
    2028           0 :       ulong                        wave_task_infos_cnt = 0UL;
    2029             : 
    2030           0 :       res = fd_runtime_prepare_txns_start( slot_ctx, task_infos, txns, txn_cnt );
    2031           0 :       if( res != 0 ) {
    2032           0 :         FD_LOG_DEBUG(("Fail prep 1"));
    2033           0 :       }
    2034             : 
    2035           0 :       ulong * incomplete_txn_idxs     = fd_scratch_alloc( 8UL, txn_cnt * sizeof(ulong) );
    2036           0 :       ulong   incomplete_txn_idxs_cnt = 0UL;
    2037           0 :       ulong   incomplete_accounts_cnt = 0UL;
    2038             : 
    2039             :       /* Setup sanitized txns as incomplete and set the capture context */
    2040           0 :       for( ulong i=0UL; i<txn_cnt; i++ ) {
    2041           0 :         if( FD_UNLIKELY( !( task_infos[i].txn->flags & FD_TXN_P_FLAGS_SANITIZE_SUCCESS ) ) ) {
    2042           0 :           continue;
    2043           0 :         }
    2044           0 :         incomplete_txn_idxs[incomplete_txn_idxs_cnt++] = i;
    2045           0 :         incomplete_accounts_cnt                       += task_infos[i].txn_ctx->accounts_cnt;
    2046           0 :         task_infos[i].txn_ctx->capture_ctx             = capture_ctx;
    2047           0 :       }
    2048             : 
    2049           0 :       ulong * next_incomplete_txn_idxs     = fd_scratch_alloc( 8UL, txn_cnt * sizeof(ulong) );
    2050           0 :       ulong   next_incomplete_txn_idxs_cnt = 0UL;
    2051           0 :       ulong   next_incomplete_accounts_cnt = 0UL;
    2052             : 
    2053           0 :       while( incomplete_txn_idxs_cnt > 0 ) {
    2054           0 :         fd_runtime_generate_wave( task_infos, incomplete_txn_idxs, incomplete_txn_idxs_cnt, incomplete_accounts_cnt,
    2055           0 :                                   next_incomplete_txn_idxs, &next_incomplete_txn_idxs_cnt, &next_incomplete_accounts_cnt,
    2056           0 :                                   wave_task_infos, &wave_task_infos_cnt );
    2057           0 :         ulong * temp_incomplete_txn_idxs = incomplete_txn_idxs;
    2058           0 :         incomplete_txn_idxs              = next_incomplete_txn_idxs;
    2059           0 :         next_incomplete_txn_idxs         = temp_incomplete_txn_idxs;
    2060           0 :         incomplete_txn_idxs_cnt          = next_incomplete_txn_idxs_cnt;
    2061             : 
    2062           0 :         for( ulong i=0UL; i<spad_cnt; i++ ) {
    2063             :           /* Borrowed accounts are allocated during prep and need to
    2064             :              persist till the end of finalize. This initial frame will
    2065             :              be holding that. */
    2066           0 :           fd_spad_push( spads[ i ] );
    2067           0 :         }
    2068             : 
    2069             :         /* Assign out spads to the transaction contexts */
    2070           0 :         for( ulong i=0UL; i<wave_task_infos_cnt; i++ ) {
    2071           0 :           wave_task_infos[i].spads = spads;
    2072           0 :         }
    2073             : 
    2074             :         // Dump txns in waves
    2075           0 :         if( dump_txn ) {
    2076           0 :           for( ulong i = 0; i < wave_task_infos_cnt; ++i ) {
    2077             :             /* Manual push/pop on the spad within the callee. */
    2078           0 :             fd_dump_txn_to_protobuf( wave_task_infos[i].txn_ctx, spads[0] );
    2079           0 :           }
    2080           0 :         }
    2081             : 
    2082           0 :         res |= fd_runtime_verify_txn_signatures_tpool( wave_task_infos, wave_task_infos_cnt, tpool );
    2083           0 :         if( FD_UNLIKELY( res ) ) {
    2084           0 :           FD_LOG_WARNING(( "Fail signature verification" ));
    2085           0 :         }
    2086             : 
    2087           0 :         res |= fd_runtime_prep_and_exec_txns_tpool( slot_ctx, wave_task_infos, wave_task_infos_cnt, tpool );
    2088           0 :         if( res != 0 ) {
    2089           0 :           FD_LOG_DEBUG(( "Fail prep and exec" ));
    2090           0 :         }
    2091             : 
    2092           0 :         int finalize_res = fd_runtime_finalize_txns_tpool( slot_ctx, capture_ctx, wave_task_infos, wave_task_infos_cnt, tpool );
    2093           0 :         if( finalize_res != 0 ) {
    2094           0 :           FD_LOG_ERR(( "Fail finalize" ));
    2095           0 :         }
    2096             : 
    2097           0 :         for( ulong i=0UL; i<spad_cnt; i++ ) {
    2098             :           /* The first frame, which holds borrowed accounts, can be
    2099             :              pretty big, and there are additional dynamic allocations
    2100             :              during finalize. */
    2101           0 :           if( FD_UNLIKELY( fd_spad_verify( spads[ i ] ) ) ) {
    2102           0 :             FD_LOG_ERR(( "spad corrupted or overflown" ));
    2103           0 :           }
    2104             :           /* We indiscriminately pushed a frame to every spad.
    2105             :              So it should be safe to indiscriminately pop here. */
    2106           0 :           fd_spad_pop( spads[ i ] );
    2107           0 :           if( FD_UNLIKELY( fd_spad_frame_used( spads[ i ] )!=0 ) ) {
    2108           0 :             FD_LOG_ERR(( "stray spad frame frame_used=%lu", fd_spad_frame_used( spads[ i ] ) ));
    2109           0 :           }
    2110           0 :         }
    2111             : 
    2112           0 :       }
    2113           0 :       } FD_SCRATCH_SCOPE_END;
    2114           0 :     }
    2115           0 :     slot_ctx->slot_bank.transaction_count += total_txn_cnt;
    2116             : 
    2117           0 :     #undef BATCH_SIZE
    2118             : 
    2119           0 :     return res;
    2120           0 : }
    2121             : 
    2122             : /******************************************************************************/
    2123             : /* Epoch Boundary                                                             */
    2124             : /******************************************************************************/
    2125             : 
    2126             : /* Update the epoch bank stakes cache with the delegated stake values from the slot bank cache.
    2127             : The slot bank cache will have been accumulating this epoch, and now we are at an epoch boundary
    2128             : we can safely update the epoch stakes cache with the latest values.
    2129             : 
    2130             : In Solana, the stakes cache is updated after every transaction
    2131             :   (https://github.com/solana-labs/solana/blob/c091fd3da8014c0ef83b626318018f238f506435/runtime/src/bank.rs#L7587).
    2132             : As delegations have to warm up, the contents of the cache will not change inter-epoch. We can therefore update
    2133             : the cache only at epoch boundaries.
    2134             : 
    2135             : https://github.com/solana-labs/solana/blob/c091fd3da8014c0ef83b626318018f238f506435/runtime/src/stakes.rs#L65 */
    2136             : static void
    2137           0 : fd_update_stake_delegations( fd_exec_slot_ctx_t * slot_ctx, fd_epoch_info_t * temp_info ) {
    2138           0 :   FD_SCRATCH_SCOPE_BEGIN {
    2139           0 :     fd_epoch_bank_t * epoch_bank = fd_exec_epoch_ctx_epoch_bank( slot_ctx->epoch_ctx );
    2140           0 :     fd_stakes_t *     stakes     = &epoch_bank->stakes;
    2141             : 
    2142             :     // TODO: is this size correct if the same stake account is in both the slot and epoch cache? Is this possible?
    2143           0 :     ulong stake_delegations_size = fd_delegation_pair_t_map_size(
    2144           0 :       stakes->stake_delegations_pool, stakes->stake_delegations_root );
    2145           0 :     stake_delegations_size += fd_stake_accounts_pair_t_map_size(
    2146           0 :       slot_ctx->slot_bank.stake_account_keys.stake_accounts_pool, slot_ctx->slot_bank.stake_account_keys.stake_accounts_root );
    2147             : 
    2148             :     // Create a new epoch stake delegations cache, which will hold the union of the slot and epoch caches.
    2149           0 :     fd_delegation_pair_t_mapnode_t * new_stake_root = NULL;
    2150           0 :     fd_delegation_pair_t_mapnode_t * new_stake_pool = fd_delegation_pair_t_map_alloc( fd_scratch_virtual(), stake_delegations_size );
    2151             : 
    2152           0 :     for ( ulong idx = 0; idx < temp_info->infos_len; idx++ ) {
    2153             :         // Fetch the delegation associated with this stake account
    2154           0 :       fd_delegation_pair_t_mapnode_t * entry = fd_delegation_pair_t_map_acquire( new_stake_pool );
    2155           0 :       fd_memcpy(&entry->elem.account, &temp_info->infos[idx].account, sizeof(fd_pubkey_t));
    2156           0 :       fd_memcpy(&entry->elem.delegation, &temp_info->infos[idx].stake.delegation, sizeof(fd_delegation_t));
    2157           0 :       fd_delegation_pair_t_map_insert( new_stake_pool, &new_stake_root, entry );
    2158           0 :     }
    2159             : 
    2160             :     // Update the epoch bank vote_accounts with the latest values from the slot bank
    2161             :     // FIXME: resize the vote_accounts_pool if necessary
    2162           0 :     ulong total_epoch_stake = 0UL;
    2163           0 :     for ( fd_vote_accounts_pair_t_mapnode_t * n = fd_vote_accounts_pair_t_map_minimum(
    2164           0 :         slot_ctx->slot_bank.vote_account_keys.vote_accounts_pool,
    2165           0 :           slot_ctx->slot_bank.vote_account_keys.vote_accounts_root );
    2166           0 :           n;
    2167           0 :           n = fd_vote_accounts_pair_t_map_successor( slot_ctx->slot_bank.vote_account_keys.vote_accounts_pool, n ) ) {
    2168             : 
    2169             :       // If the vote account is not in the epoch stakes cache, insert it
    2170           0 :       fd_vote_accounts_pair_t_mapnode_t key;
    2171           0 :       fd_memcpy( &key.elem.key, &n->elem.key, FD_PUBKEY_FOOTPRINT );
    2172           0 :       fd_vote_accounts_pair_t_mapnode_t * epoch_cache_node = fd_vote_accounts_pair_t_map_find( stakes->vote_accounts.vote_accounts_pool, stakes->vote_accounts.vote_accounts_root, &key );
    2173           0 :       if( epoch_cache_node == NULL ) {
    2174           0 :         epoch_cache_node = fd_vote_accounts_pair_t_map_acquire( stakes->vote_accounts.vote_accounts_pool );
    2175             : 
    2176           0 :         fd_memcpy(&epoch_cache_node->elem.key, &n->elem.key, sizeof(fd_pubkey_t));
    2177           0 :         fd_memcpy(&epoch_cache_node->elem.stake, &n->elem.stake, sizeof(ulong));
    2178           0 :         fd_memcpy(&epoch_cache_node->elem.value, &n->elem.value, sizeof(fd_solana_account_t));
    2179             : 
    2180           0 :         fd_vote_accounts_pair_t_map_insert( stakes->vote_accounts.vote_accounts_pool, &stakes->vote_accounts.vote_accounts_root, epoch_cache_node );
    2181           0 :       } else {
    2182           0 :         epoch_cache_node->elem.stake = n->elem.stake;
    2183           0 :       }
    2184           0 :       total_epoch_stake += epoch_cache_node->elem.stake;
    2185           0 :     }
    2186           0 :     slot_ctx->epoch_ctx->total_epoch_stake = total_epoch_stake;
    2187             : 
    2188           0 :     fd_bincode_destroy_ctx_t destroy_slot = { .valloc = slot_ctx->valloc };
    2189           0 :     fd_vote_accounts_destroy( &slot_ctx->slot_bank.vote_account_keys, &destroy_slot );
    2190           0 :     fd_stake_accounts_destroy(&slot_ctx->slot_bank.stake_account_keys, &destroy_slot );
    2191             : 
    2192             :     /* Release all nodes in tree.
    2193             :        FIXME sweep pool and ignore tree nodes might is probably faster
    2194             :        than recursive descent */
    2195           0 :     fd_delegation_pair_t_map_release_tree( stakes->stake_delegations_pool, stakes->stake_delegations_root );
    2196           0 :     stakes->stake_delegations_root = NULL;
    2197             : 
    2198           0 :     for( fd_delegation_pair_t_mapnode_t * n = fd_delegation_pair_t_map_minimum( new_stake_pool, new_stake_root ); n; n = fd_delegation_pair_t_map_successor( new_stake_pool, n ) ) {
    2199           0 :       fd_delegation_pair_t_mapnode_t * e = fd_delegation_pair_t_map_acquire( stakes->stake_delegations_pool );
    2200           0 :       if( FD_UNLIKELY( !e ) ) {
    2201           0 :         FD_LOG_CRIT(( "Stake delegation map overflowed! (capacity=%lu)", fd_delegation_pair_t_map_max( stakes->stake_delegations_pool ) ));
    2202           0 :       }
    2203           0 :       fd_memcpy( &e->elem.account, &n->elem.account, sizeof(fd_pubkey_t));
    2204           0 :       fd_memcpy( &e->elem.delegation, &n->elem.delegation, sizeof(fd_delegation_t));
    2205           0 :       fd_delegation_pair_t_map_insert( stakes->stake_delegations_pool, &stakes->stake_delegations_root, e );
    2206           0 :     }
    2207             : 
    2208           0 :     slot_ctx->slot_bank.stake_account_keys.stake_accounts_root = NULL;
    2209           0 :     slot_ctx->slot_bank.stake_account_keys.stake_accounts_pool = fd_stake_accounts_pair_t_map_alloc( slot_ctx->valloc, 100000 );
    2210             : 
    2211           0 :     slot_ctx->slot_bank.vote_account_keys.vote_accounts_root = NULL;
    2212           0 :     slot_ctx->slot_bank.vote_account_keys.vote_accounts_pool = fd_vote_accounts_pair_t_map_alloc( slot_ctx->valloc, 100000 );
    2213           0 :   } FD_SCRATCH_SCOPE_END;
    2214           0 : }
    2215             : 
    2216             : /* Replace the stakes in T-2 (slot_ctx->slot_bank.epoch_stakes) by the stakes at T-1 (epoch_bank->next_epoch_stakes) */
    2217             : static void 
    2218           0 : fd_update_epoch_stakes( fd_exec_slot_ctx_t * slot_ctx ) {
    2219           0 :   FD_SCRATCH_SCOPE_BEGIN {
    2220           0 :     fd_epoch_bank_t * epoch_bank = &slot_ctx->epoch_ctx->epoch_bank;
    2221             : 
    2222             :     /* Copy epoch_bank->next_epoch_stakes into slot_ctx->slot_bank.epoch_stakes */
    2223           0 :     fd_vote_accounts_pair_t_map_release_tree(
    2224           0 :       slot_ctx->slot_bank.epoch_stakes.vote_accounts_pool,
    2225           0 :       slot_ctx->slot_bank.epoch_stakes.vote_accounts_root );
    2226           0 :     slot_ctx->slot_bank.epoch_stakes.vote_accounts_root = NULL;
    2227             : 
    2228           0 :     for ( fd_vote_accounts_pair_t_mapnode_t * n = fd_vote_accounts_pair_t_map_minimum(
    2229           0 :       epoch_bank->next_epoch_stakes.vote_accounts_pool,
    2230           0 :       epoch_bank->next_epoch_stakes.vote_accounts_root );
    2231           0 :           n;
    2232           0 :           n = fd_vote_accounts_pair_t_map_successor( epoch_bank->next_epoch_stakes.vote_accounts_pool, n ) ) {
    2233             : 
    2234           0 :       const fd_pubkey_t null_pubkey = {{ 0 }};
    2235           0 :       if ( memcmp( &n->elem.key, &null_pubkey, FD_PUBKEY_FOOTPRINT ) == 0 ) {
    2236           0 :         continue;
    2237           0 :       }
    2238             : 
    2239           0 :       fd_vote_accounts_pair_t_mapnode_t * elem = fd_vote_accounts_pair_t_map_acquire(
    2240           0 :         slot_ctx->slot_bank.epoch_stakes.vote_accounts_pool );
    2241           0 :       if ( FD_UNLIKELY(
    2242           0 :           fd_vote_accounts_pair_t_map_free( slot_ctx->slot_bank.epoch_stakes.vote_accounts_pool ) == 0 ) ) {
    2243           0 :         FD_LOG_ERR(( "slot_ctx->slot_bank.epoch_stakes.vote_accounts_pool full" ));
    2244           0 :       }
    2245             : 
    2246           0 :       fd_memcpy( &elem->elem, &n->elem, sizeof(fd_vote_accounts_pair_t));
    2247           0 :       fd_vote_accounts_pair_t_map_insert(
    2248           0 :         slot_ctx->slot_bank.epoch_stakes.vote_accounts_pool,
    2249           0 :         &slot_ctx->slot_bank.epoch_stakes.vote_accounts_root,
    2250           0 :         elem );
    2251           0 :     }
    2252           0 :   } FD_SCRATCH_SCOPE_END;
    2253           0 : }
    2254             : 
    2255             : /* Copy epoch_bank->stakes.vote_accounts into epoch_bank->next_epoch_stakes. */
    2256           0 : static void fd_update_next_epoch_stakes( fd_exec_slot_ctx_t * slot_ctx ) {
    2257           0 :   FD_SCRATCH_SCOPE_BEGIN {
    2258           0 :     fd_epoch_bank_t * epoch_bank = &slot_ctx->epoch_ctx->epoch_bank;
    2259             : 
    2260             :     /* Copy epoch_ctx->epoch_bank->stakes.vote_accounts into epoch_bank->next_epoch_stakes */
    2261           0 :     fd_vote_accounts_pair_t_map_release_tree(
    2262           0 :       epoch_bank->next_epoch_stakes.vote_accounts_pool,
    2263           0 :       epoch_bank->next_epoch_stakes.vote_accounts_root );
    2264             : 
    2265           0 :     epoch_bank->next_epoch_stakes.vote_accounts_pool = fd_exec_epoch_ctx_next_epoch_stakes_join( slot_ctx->epoch_ctx );
    2266           0 :     epoch_bank->next_epoch_stakes.vote_accounts_root = NULL;
    2267             : 
    2268           0 :     for ( fd_vote_accounts_pair_t_mapnode_t * n = fd_vote_accounts_pair_t_map_minimum(
    2269           0 :       epoch_bank->stakes.vote_accounts.vote_accounts_pool,
    2270           0 :       epoch_bank->stakes.vote_accounts.vote_accounts_root );
    2271           0 :           n;
    2272           0 :           n = fd_vote_accounts_pair_t_map_successor( epoch_bank->stakes.vote_accounts.vote_accounts_pool, n ) ) {
    2273           0 :       fd_vote_accounts_pair_t_mapnode_t * elem = fd_vote_accounts_pair_t_map_acquire( epoch_bank->next_epoch_stakes.vote_accounts_pool );
    2274           0 :       fd_memcpy( &elem->elem, &n->elem, sizeof(fd_vote_accounts_pair_t));
    2275           0 :       fd_vote_accounts_pair_t_map_insert( epoch_bank->next_epoch_stakes.vote_accounts_pool, &epoch_bank->next_epoch_stakes.vote_accounts_root, elem );
    2276           0 :     }
    2277           0 :   } FD_SCRATCH_SCOPE_END;
    2278           0 : }
    2279             : 
    2280             : /* Mimics `bank.new_target_program_account()`. Assumes `out_rec` is a modifiable record.
    2281             : 
    2282             :    From the calling context, `out_rec` points to a native program record (e.g. Config, ALUT native programs).
    2283             :    There should be enough space in `out_rec->data` to hold at least 36 bytes (the size of a BPF upgradeable
    2284             :    program account) when calling this function. The native program account's owner is set to the BPF loader
    2285             :    upgradeable program ID, and lamports are increased / deducted to contain the rent exempt minimum balance.
    2286             : 
    2287             :    https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L79-L95 */
    2288             : static int
    2289             : fd_new_target_program_account( fd_exec_slot_ctx_t *    slot_ctx,
    2290             :                                const fd_pubkey_t *     target_program_data_address,
    2291           0 :                                fd_borrowed_account_t * out_rec ) {
    2292             :   /* https://github.com/anza-xyz/agave/blob/v2.1.0/sdk/account/src/lib.rs#L471 */
    2293           0 :   out_rec->meta->info.rent_epoch = 0UL;
    2294             : 
    2295             :   /* https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L86-L88 */
    2296           0 :   fd_bpf_upgradeable_loader_state_t state = {
    2297           0 :     .discriminant = fd_bpf_upgradeable_loader_state_enum_program,
    2298           0 :     .inner = {
    2299           0 :       .program = {
    2300           0 :         .programdata_address = *target_program_data_address,
    2301           0 :       }
    2302           0 :     }
    2303           0 :   };
    2304             : 
    2305             :   /* https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L89-L90 */
    2306           0 :   const fd_rent_t * rent = fd_sysvar_cache_rent( slot_ctx->sysvar_cache );
    2307           0 :   if( FD_UNLIKELY( rent==NULL ) ) {
    2308           0 :     return -1;
    2309           0 :   }
    2310             : 
    2311           0 :   out_rec->meta->info.lamports = fd_rent_exempt_minimum_balance( rent, SIZE_OF_PROGRAM );
    2312           0 :   fd_bincode_encode_ctx_t ctx = {
    2313           0 :     .data = out_rec->data,
    2314           0 :     .dataend = out_rec->data + SIZE_OF_PROGRAM,
    2315           0 :   };
    2316             : 
    2317             :   /* https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L91-L9 */
    2318           0 :   int err = fd_bpf_upgradeable_loader_state_encode( &state, &ctx );
    2319           0 :   if( FD_UNLIKELY( err ) ) {
    2320           0 :     return err;
    2321           0 :   }
    2322           0 :   fd_memcpy( out_rec->meta->info.owner, fd_solana_bpf_loader_upgradeable_program_id.uc, sizeof(fd_pubkey_t) );
    2323             : 
    2324             :   /* https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L93-L94 */
    2325           0 :   out_rec->meta->info.executable = 1;
    2326           0 :   return FD_RUNTIME_EXECUTE_SUCCESS;
    2327           0 : }
    2328             : 
    2329             : /* Mimics `bank.new_target_program_data_account()`. Assumes `new_target_program_data_account` is a modifiable record.
    2330             :    `config_upgrade_authority_address` may be NULL.
    2331             : 
    2332             :    This function uses an existing buffer account `buffer_acc_rec` to set the program data account data for a core
    2333             :    program BPF migration. Sets the lamports and data fields of `new_target_program_data_account` based on the
    2334             :    ELF data length, and sets the owner to the BPF loader upgradeable program ID.
    2335             : 
    2336             :    https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L97-L153 */
    2337             : static int
    2338             : fd_new_target_program_data_account( fd_exec_slot_ctx_t *    slot_ctx,
    2339             :                                     fd_pubkey_t *           config_upgrade_authority_address,
    2340             :                                     fd_borrowed_account_t * buffer_acc_rec,
    2341           0 :                                     fd_borrowed_account_t * new_target_program_data_account ) {
    2342             :   /* https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L113-L116 */
    2343           0 :   fd_bpf_upgradeable_loader_state_t state;
    2344           0 :   fd_bincode_decode_ctx_t decode_ctx = {
    2345           0 :     .data = buffer_acc_rec->const_data,
    2346           0 :     .dataend = buffer_acc_rec->const_data + buffer_acc_rec->const_meta->dlen,
    2347           0 :     .valloc = fd_scratch_virtual()
    2348           0 :   };
    2349           0 :   int err = fd_bpf_upgradeable_loader_state_decode( &state, &decode_ctx );
    2350           0 :   if( FD_UNLIKELY( err ) ) {
    2351           0 :     return err;
    2352           0 :   }
    2353           0 :   if( FD_UNLIKELY( !fd_bpf_upgradeable_loader_state_is_buffer( &state ) ) ) {
    2354           0 :     return -1;
    2355           0 :   }
    2356             : 
    2357             :   /* https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L118-L125 */
    2358           0 :   if( config_upgrade_authority_address!=NULL ) {
    2359           0 :     if( FD_UNLIKELY( state.inner.buffer.authority_address==NULL ||
    2360           0 :                      memcmp( config_upgrade_authority_address, state.inner.buffer.authority_address, sizeof(fd_pubkey_t) ) ) ) {
    2361           0 :       return -1;
    2362           0 :     }
    2363           0 :   }
    2364             : 
    2365             :   /* https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L127-L132 */
    2366           0 :   const fd_rent_t * rent = fd_sysvar_cache_rent( slot_ctx->sysvar_cache );
    2367           0 :   if( FD_UNLIKELY( rent==NULL ) ) {
    2368           0 :     return -1;
    2369           0 :   }
    2370             : 
    2371           0 :   const uchar * elf = buffer_acc_rec->const_data + BUFFER_METADATA_SIZE;
    2372           0 :   ulong space = PROGRAMDATA_METADATA_SIZE - BUFFER_METADATA_SIZE + buffer_acc_rec->const_meta->dlen;
    2373           0 :   ulong lamports = fd_rent_exempt_minimum_balance( rent, space );
    2374             : 
    2375             :   /* https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L134-L137 */
    2376           0 :   fd_bpf_upgradeable_loader_state_t programdata_metadata = {
    2377           0 :     .discriminant = fd_bpf_upgradeable_loader_state_enum_program_data,
    2378           0 :     .inner = {
    2379           0 :       .program_data = {
    2380           0 :         .slot = slot_ctx->slot_bank.slot,
    2381           0 :         .upgrade_authority_address = config_upgrade_authority_address
    2382           0 :       }
    2383           0 :     }
    2384           0 :   };
    2385             : 
    2386             :   /* https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L139-L144 */
    2387           0 :   new_target_program_data_account->meta->info.lamports = lamports;
    2388           0 :   fd_bincode_encode_ctx_t encode_ctx = {
    2389           0 :     .data = new_target_program_data_account->data,
    2390           0 :     .dataend = new_target_program_data_account->data + PROGRAMDATA_METADATA_SIZE,
    2391           0 :   };
    2392           0 :   err = fd_bpf_upgradeable_loader_state_encode( &programdata_metadata, &encode_ctx );
    2393           0 :   if( FD_UNLIKELY( err ) ) {
    2394           0 :     return err;
    2395           0 :   }
    2396           0 :   fd_memcpy( new_target_program_data_account->meta->info.owner, fd_solana_bpf_loader_upgradeable_program_id.uc, sizeof(fd_pubkey_t) );
    2397             : 
    2398             :   /* Copy the ELF data over
    2399             :      https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L145 */
    2400           0 :   fd_memcpy( new_target_program_data_account->data + PROGRAMDATA_METADATA_SIZE, elf, buffer_acc_rec->const_meta->dlen - BUFFER_METADATA_SIZE );
    2401             : 
    2402           0 :   return FD_RUNTIME_EXECUTE_SUCCESS;
    2403           0 : }
    2404             : 
    2405             : /* Mimics `migrate_builtin_to_core_bpf()`. The arguments map as follows:
    2406             :     - builtin_program_id: builtin_program_id
    2407             :     - config
    2408             :       - source_buffer_address: source_buffer_address
    2409             :       - migration_target
    2410             :         - Builtin: !stateless
    2411             :         - Stateless: stateless
    2412             :       - upgrade_authority_address: upgrade_authority_address
    2413             :   https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L235-L318 */
    2414             : static void
    2415             : fd_migrate_builtin_to_core_bpf( fd_exec_slot_ctx_t * slot_ctx,
    2416             :                                 fd_pubkey_t *        upgrade_authority_address,
    2417             :                                 const fd_pubkey_t *  builtin_program_id,
    2418             :                                 const fd_pubkey_t *  source_buffer_address,
    2419           0 :                                 uchar                stateless ) {
    2420           0 :   int err;
    2421             : 
    2422             :   /* https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L242-L243
    2423             : 
    2424             :      The below logic is used to obtain a `TargetBuiltin` account. There are three fields of `TargetBuiltin` returned:
    2425             :       - target.program_address: builtin_program_id
    2426             :       - target.program_account:
    2427             :           - if stateless: an AccountSharedData::default() (i.e. system program id, 0 lamports, 0 data, non-executable, system program owner)
    2428             :           - if NOT stateless: the existing account (for us its called `target_program_account`)
    2429             :       - target.program_data_address: `target_program_data_address` for us, derived below. */
    2430             : 
    2431             :   /* These checks will fail if the core program has already been migrated to BPF, since the account will exist + the program owner
    2432             :      will no longer be the native loader.
    2433             :      https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/target_builtin.rs#L23-L50 */
    2434           0 :   FD_BORROWED_ACCOUNT_DECL( target_program_account );
    2435           0 :   uchar program_exists = ( fd_acc_mgr_view( slot_ctx->acc_mgr, slot_ctx->funk_txn, builtin_program_id, target_program_account )==FD_ACC_MGR_SUCCESS );
    2436           0 :   if( !stateless ) {
    2437             :     /* The program account should exist.
    2438             :        https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/target_builtin.rs#L30-L33 */
    2439           0 :     if( FD_UNLIKELY( !program_exists ) ) {
    2440           0 :       FD_LOG_WARNING(( "Builtin program %s does not exist, skipping migration...", FD_BASE58_ENC_32_ALLOCA( builtin_program_id ) ));
    2441           0 :       return;
    2442           0 :     }
    2443             : 
    2444             :     /* The program account should be owned by the native loader.
    2445             :        https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/target_builtin.rs#L35-L38 */
    2446           0 :     if( FD_UNLIKELY( memcmp( target_program_account->const_meta->info.owner, fd_solana_native_loader_id.uc, sizeof(fd_pubkey_t) ) ) ) {
    2447           0 :       FD_LOG_WARNING(( "Builtin program %s is not owned by the native loader, skipping migration...", FD_BASE58_ENC_32_ALLOCA( builtin_program_id ) ));
    2448           0 :       return;
    2449           0 :     }
    2450           0 :   } else {
    2451             :     /* The program account should _not_ exist.
    2452             :        https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/target_builtin.rs#L42-L46 */
    2453           0 :     if( FD_UNLIKELY( program_exists ) ) {
    2454           0 :       FD_LOG_WARNING(( "Stateless program %s already exists, skipping migration...", FD_BASE58_ENC_32_ALLOCA( builtin_program_id ) ));
    2455           0 :       return;
    2456           0 :     }
    2457           0 :   }
    2458             : 
    2459             :   /* The program data account should not exist.
    2460             :      https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/target_builtin.rs#L52-L62 */
    2461           0 :   uint custom_err = UINT_MAX;
    2462           0 :   fd_pubkey_t target_program_data_address[ 1UL ];
    2463           0 :   uchar * seeds[ 1UL ];
    2464           0 :   seeds[ 0UL ]    = (uchar *)builtin_program_id;
    2465           0 :   ulong seed_sz   = sizeof(fd_pubkey_t);
    2466           0 :   uchar bump_seed = 0;
    2467           0 :   err = fd_pubkey_find_program_address( &fd_solana_bpf_loader_upgradeable_program_id, 1UL, seeds, &seed_sz, target_program_data_address, &bump_seed, &custom_err );
    2468           0 :   if( FD_UNLIKELY( err ) ) {
    2469             :     /* TODO: We should handle these errors more gracefully instead of just killing the client. */
    2470           0 :     FD_LOG_ERR(( "Unable to find a viable program address bump seed" )); // Solana panics, error code is undefined
    2471           0 :     return;
    2472           0 :   }
    2473           0 :   FD_BORROWED_ACCOUNT_DECL( program_data_account );
    2474           0 :   if( FD_UNLIKELY( fd_acc_mgr_view( slot_ctx->acc_mgr, slot_ctx->funk_txn, target_program_data_address, program_data_account )==FD_ACC_MGR_SUCCESS ) ) {
    2475           0 :     FD_LOG_WARNING(( "Program data account %s already exists, skipping migration...", FD_BASE58_ENC_32_ALLOCA( target_program_data_address ) ));
    2476           0 :     return;
    2477           0 :   }
    2478             : 
    2479             :   /* https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L244
    2480             : 
    2481             :      Obtains a `SourceBuffer` account. There are two fields returned:
    2482             :       - source.buffer_address: source_buffer_address
    2483             :       - source.buffer_account: the existing buffer account */
    2484             : 
    2485             :   /* The buffer account should exist.
    2486             :      https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/source_buffer.rs#L26-L29 */
    2487           0 :   FD_BORROWED_ACCOUNT_DECL( source_buffer_account );
    2488           0 :   if( FD_UNLIKELY( fd_acc_mgr_modify( slot_ctx->acc_mgr, slot_ctx->funk_txn, source_buffer_address, 0, 0UL, source_buffer_account )!=FD_ACC_MGR_SUCCESS ) ) {
    2489           0 :     FD_LOG_WARNING(( "Buffer account %s does not exist, skipping migration...", FD_BASE58_ENC_32_ALLOCA( source_buffer_address ) ));
    2490           0 :     return;
    2491           0 :   }
    2492             : 
    2493             :   /* The buffer account should be owned by the upgradeable loader.
    2494             :      https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/source_buffer.rs#L31-L34 */
    2495           0 :   if( FD_UNLIKELY( memcmp( source_buffer_account->const_meta->info.owner, fd_solana_bpf_loader_upgradeable_program_id.uc, sizeof(fd_pubkey_t) ) ) ) {
    2496           0 :     FD_LOG_WARNING(( "Buffer account %s is not owned by the upgradeable loader, skipping migration...", FD_BASE58_ENC_32_ALLOCA( source_buffer_address ) ));
    2497           0 :     return;
    2498           0 :   }
    2499             : 
    2500             :   /* The buffer account should have the correct state. We already check the buffer account state in `fd_new_target_program_data_account`,
    2501             :       so we can skip the checks here.
    2502             :      https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/source_buffer.rs#L37-L47 */
    2503             : 
    2504             :   /* This check is done a bit prematurely because we calculate the previous account state's lamports. We use 0 for starting lamports
    2505             :      for stateless accounts because they don't yet exist.
    2506             : 
    2507             :      https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L277-L280 */
    2508           0 :   ulong lamports_to_burn = ( stateless ? 0UL : target_program_account->const_meta->info.lamports ) + source_buffer_account->const_meta->info.lamports;
    2509             : 
    2510             :   /* Start a funk write txn */
    2511           0 :   fd_funk_txn_t * parent_txn = slot_ctx->funk_txn;
    2512           0 :   fd_funk_txn_xid_t migration_xid = fd_funk_generate_xid();
    2513           0 :   slot_ctx->funk_txn = fd_funk_txn_prepare( slot_ctx->acc_mgr->funk, slot_ctx->funk_txn, &migration_xid, 0UL );
    2514             : 
    2515             :   /* Attempt serialization of program account. If the program is stateless, we want to create the account. Otherwise,
    2516             :      we want a writable handle to modify the existing account.
    2517             :      https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L246-L249 */
    2518           0 :   FD_BORROWED_ACCOUNT_DECL( new_target_program_account );
    2519           0 :   err = fd_acc_mgr_modify( slot_ctx->acc_mgr, slot_ctx->funk_txn, builtin_program_id, stateless, SIZE_OF_PROGRAM, new_target_program_account );
    2520           0 :   if( FD_UNLIKELY( err ) ) {
    2521           0 :     FD_LOG_WARNING(( "Builtin program ID %s does not exist", FD_BASE58_ENC_32_ALLOCA( builtin_program_id ) ));
    2522           0 :     goto fail;
    2523           0 :   }
    2524           0 :   new_target_program_account->meta->dlen = SIZE_OF_PROGRAM;
    2525           0 :   new_target_program_account->meta->slot = slot_ctx->slot_bank.slot;
    2526             : 
    2527             :   /* Create a new target program account. This modifies the existing record. */
    2528           0 :   err = fd_new_target_program_account( slot_ctx, target_program_data_address, new_target_program_account );
    2529           0 :   if( FD_UNLIKELY( err ) ) {
    2530           0 :     FD_LOG_WARNING(( "Failed to write new program state to %s", FD_BASE58_ENC_32_ALLOCA( builtin_program_id ) ));
    2531           0 :     goto fail;
    2532           0 :   }
    2533             : 
    2534             :   /* Create a new target program data account. */
    2535           0 :   ulong new_target_program_data_account_sz = PROGRAMDATA_METADATA_SIZE - BUFFER_METADATA_SIZE + source_buffer_account->const_meta->dlen;
    2536           0 :   FD_BORROWED_ACCOUNT_DECL( new_target_program_data_account );
    2537           0 :   err = fd_acc_mgr_modify( slot_ctx->acc_mgr,
    2538           0 :                            slot_ctx->funk_txn,
    2539           0 :                            target_program_data_address,
    2540           0 :                            1,
    2541           0 :                            new_target_program_data_account_sz,
    2542           0 :                            new_target_program_data_account );
    2543           0 :   if( FD_UNLIKELY( err ) ) {
    2544           0 :     FD_LOG_WARNING(( "Failed to create new program data account to %s", FD_BASE58_ENC_32_ALLOCA( target_program_data_address ) ));
    2545           0 :     goto fail;
    2546           0 :   }
    2547           0 :   new_target_program_data_account->meta->dlen = new_target_program_data_account_sz;
    2548           0 :   new_target_program_data_account->meta->slot = slot_ctx->slot_bank.slot;
    2549             : 
    2550           0 :   err = fd_new_target_program_data_account( slot_ctx, upgrade_authority_address, source_buffer_account, new_target_program_data_account );
    2551           0 :   if( FD_UNLIKELY( err ) ) {
    2552           0 :     FD_LOG_WARNING(( "Failed to write new program data state to %s", FD_BASE58_ENC_32_ALLOCA( target_program_data_address ) ));
    2553           0 :     goto fail;
    2554           0 :   }
    2555             : 
    2556             :   /* Deploy the new target Core BPF program.
    2557             :      https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L268-L271 */
    2558           0 :   err = fd_directly_invoke_loader_v3_deploy( slot_ctx,
    2559           0 :                                              new_target_program_data_account->const_data + PROGRAMDATA_METADATA_SIZE,
    2560           0 :                                              new_target_program_data_account->const_meta->dlen - PROGRAMDATA_METADATA_SIZE );
    2561           0 :   if( FD_UNLIKELY( err ) ) {
    2562           0 :     FD_LOG_WARNING(( "Failed to deploy program %s", FD_BASE58_ENC_32_ALLOCA( builtin_program_id ) ));
    2563           0 :     goto fail;
    2564           0 :   }
    2565             : 
    2566             :   /* https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L281-L284 */
    2567           0 :   ulong lamports_to_fund = new_target_program_account->const_meta->info.lamports + new_target_program_data_account->const_meta->info.lamports;
    2568             : 
    2569             :   /* Update capitalization.
    2570             :      https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L286-L297 */
    2571           0 :   if( lamports_to_burn>lamports_to_fund ) {
    2572           0 :     slot_ctx->slot_bank.capitalization -= lamports_to_burn - lamports_to_fund;
    2573           0 :   } else {
    2574           0 :     slot_ctx->slot_bank.capitalization += lamports_to_fund - lamports_to_burn;
    2575           0 :   }
    2576             : 
    2577             :   /* Reclaim the source buffer account
    2578             :      https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L305 */
    2579           0 :   source_buffer_account->meta->info.lamports = 0;
    2580           0 :   source_buffer_account->meta->dlen = 0;
    2581           0 :   fd_memset( source_buffer_account->meta->info.owner, 0, sizeof(fd_pubkey_t) );
    2582             : 
    2583             :   /* Publish the in-preparation transaction into the parent. We should not have to create
    2584             :      a BPF cache entry here because the program is technically "delayed visibility", so the program
    2585             :      should not be invokable until the next slot. The cache entry will be created at the end of the
    2586             :      block as a part of the finalize routine. */
    2587           0 :   fd_funk_txn_publish_into_parent( slot_ctx->acc_mgr->funk, slot_ctx->funk_txn, 1 );
    2588           0 :   slot_ctx->funk_txn = parent_txn;
    2589           0 :   return;
    2590             : 
    2591           0 : fail:
    2592             :   /* Cancel the in-preparation transaction and discard any in-progress changes. */
    2593           0 :   fd_funk_txn_cancel( slot_ctx->acc_mgr->funk, slot_ctx->funk_txn, 0UL );
    2594           0 :   slot_ctx->funk_txn = parent_txn;
    2595           0 : }
    2596             : 
    2597             : static void
    2598           0 : fd_apply_builtin_program_feature_transitions( fd_exec_slot_ctx_t * slot_ctx ) {
    2599           0 :   FD_SCRATCH_SCOPE_BEGIN {
    2600             :     /* TODO: Set the upgrade authority properly from the core bpf migration config. Right now it's set to None.
    2601             : 
    2602             :        Migrate any necessary stateless builtins to core BPF. So far, the only "stateless" builtin
    2603             :        is the Feature program. Beginning checks in the `migrate_builtin_to_core_bpf` function will
    2604             :        fail if the program has already been migrated to BPF.
    2605             :        https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank.rs#L6776-L6793 */
    2606           0 :     if( FD_FEATURE_ACTIVE( slot_ctx, migrate_feature_gate_program_to_core_bpf ) ) {
    2607           0 :       fd_migrate_builtin_to_core_bpf( slot_ctx, NULL, &fd_solana_feature_program_id, &fd_solana_feature_program_buffer_address, 1 );
    2608           0 :     }
    2609             : 
    2610           0 :     if( FD_FEATURE_ACTIVE( slot_ctx, migrate_config_program_to_core_bpf ) ) {
    2611           0 :       fd_migrate_builtin_to_core_bpf( slot_ctx, NULL, &fd_solana_config_program_id, &fd_solana_config_program_buffer_address, 0 );
    2612           0 :     }
    2613             : 
    2614           0 :     if( FD_FEATURE_ACTIVE( slot_ctx, migrate_address_lookup_table_program_to_core_bpf ) ) {
    2615           0 :       fd_migrate_builtin_to_core_bpf( slot_ctx, NULL, &fd_solana_address_lookup_table_program_id, &fd_solana_address_lookup_table_program_buffer_address, 0 );
    2616           0 :     }
    2617             : 
    2618           0 :     if( FD_FEATURE_ACTIVE( slot_ctx, migrate_stake_program_to_core_bpf ) ) {
    2619           0 :       fd_migrate_builtin_to_core_bpf( slot_ctx, NULL, &fd_solana_stake_program_id, &fd_solana_stake_program_buffer_address, 0 );
    2620           0 :     }
    2621           0 :   } FD_SCRATCH_SCOPE_END;
    2622           0 : }
    2623             : 
    2624             : static void
    2625             : fd_feature_activate( fd_exec_slot_ctx_t * slot_ctx,
    2626             :                     fd_feature_id_t const * id,
    2627           0 :                     uchar const       acct[ static 32 ] ) {
    2628             : 
    2629             :   // Skip reverted features from being activated
    2630           0 :   if ( id->reverted==1 ) {
    2631           0 :     return;
    2632           0 :   }
    2633             : 
    2634           0 :   FD_BORROWED_ACCOUNT_DECL(acct_rec);
    2635           0 :   int err = fd_acc_mgr_view(slot_ctx->acc_mgr, slot_ctx->funk_txn, (fd_pubkey_t *)acct, acct_rec);
    2636           0 :   if (FD_UNLIKELY(err != FD_ACC_MGR_SUCCESS))
    2637           0 :     return;
    2638             : 
    2639           0 :   fd_feature_t feature[1];
    2640             : 
    2641           0 :   FD_SCRATCH_SCOPE_BEGIN
    2642           0 :   {
    2643             : 
    2644           0 :     fd_bincode_decode_ctx_t ctx = {
    2645           0 :         .data = acct_rec->const_data,
    2646           0 :         .dataend = acct_rec->const_data + acct_rec->const_meta->dlen,
    2647           0 :         .valloc = fd_scratch_virtual(),
    2648           0 :     };
    2649           0 :     int decode_err = fd_feature_decode(feature, &ctx);
    2650           0 :     if (FD_UNLIKELY(decode_err != FD_BINCODE_SUCCESS)) {
    2651           0 :       FD_LOG_ERR(( "Failed to decode feature account %s (%d)", FD_BASE58_ENC_32_ALLOCA( acct ), decode_err ));
    2652           0 :     }
    2653             : 
    2654           0 :     if( feature->has_activated_at ) {
    2655           0 :       FD_LOG_INFO(( "feature already activated - acc: %s, slot: %lu", FD_BASE58_ENC_32_ALLOCA( acct ), feature->activated_at ));
    2656           0 :       fd_features_set(&slot_ctx->epoch_ctx->features, id, feature->activated_at);
    2657           0 :     } else {
    2658           0 :       FD_LOG_INFO(( "Feature %s not activated at %lu, activating", FD_BASE58_ENC_32_ALLOCA( acct ), feature->activated_at ));
    2659             : 
    2660           0 :       FD_BORROWED_ACCOUNT_DECL(modify_acct_rec);
    2661           0 :       err = fd_acc_mgr_modify(slot_ctx->acc_mgr, slot_ctx->funk_txn, (fd_pubkey_t *)acct, 0, 0UL, modify_acct_rec);
    2662           0 :       if (FD_UNLIKELY(err != FD_ACC_MGR_SUCCESS)) {
    2663           0 :         return;
    2664           0 :       }
    2665             : 
    2666           0 :       feature->has_activated_at = 1;
    2667           0 :       feature->activated_at = slot_ctx->slot_bank.slot;
    2668           0 :       fd_bincode_encode_ctx_t encode_ctx = {
    2669           0 :         .data = modify_acct_rec->data,
    2670           0 :         .dataend = modify_acct_rec->data + modify_acct_rec->meta->dlen,
    2671           0 :       };
    2672           0 :       int encode_err = fd_feature_encode(feature, &encode_ctx);
    2673           0 :       if (FD_UNLIKELY(encode_err != FD_BINCODE_SUCCESS)) {
    2674           0 :         FD_LOG_ERR(( "Failed to encode feature account %s (%d)", FD_BASE58_ENC_32_ALLOCA( acct ), decode_err ));
    2675           0 :       }
    2676           0 :     }
    2677             :     /* No need to call destroy, since we are using fd_scratch allocator. */
    2678           0 :   } FD_SCRATCH_SCOPE_END;
    2679           0 : }
    2680             : 
    2681             : static void
    2682           0 : fd_features_activate( fd_exec_slot_ctx_t * slot_ctx ) {
    2683           0 :   for( fd_feature_id_t const * id = fd_feature_iter_init();
    2684           0 :                                    !fd_feature_iter_done( id );
    2685           0 :                                id = fd_feature_iter_next( id ) ) {
    2686           0 :     fd_feature_activate( slot_ctx, id, id->id.key );
    2687           0 :   }
    2688           0 : }
    2689             : 
    2690             : uint
    2691           0 : fd_runtime_is_epoch_boundary( fd_epoch_bank_t * epoch_bank, ulong curr_slot, ulong prev_slot ) {
    2692           0 :   ulong slot_idx;
    2693           0 :   ulong prev_epoch = fd_slot_to_epoch( &epoch_bank->epoch_schedule, prev_slot, &slot_idx );
    2694           0 :   ulong new_epoch  = fd_slot_to_epoch( &epoch_bank->epoch_schedule, curr_slot, &slot_idx );
    2695             : 
    2696           0 :   return ( prev_epoch < new_epoch || slot_idx == 0 );
    2697           0 : }
    2698             : 
    2699             : /* Starting a new epoch.
    2700             :   New epoch:        T
    2701             :   Just ended epoch: T-1
    2702             :   Epoch before:     T-2
    2703             : 
    2704             :   In this function:
    2705             :   - stakes in T-2 (slot_ctx->slot_bank.epoch_stakes) should be replaced by T-1 (epoch_bank->next_epoch_stakes)
    2706             :   - stakes at T-1 (epoch_bank->next_epoch_stakes) should be replaced by updated stakes at T (stakes->vote_accounts)
    2707             :   - leader schedule should be calculated using new T-2 stakes (slot_ctx->slot_bank.epoch_stakes)
    2708             : 
    2709             :   Invariant during an epoch T:
    2710             :   epoch_bank->next_epoch_stakes    holds the stakes at T-1
    2711             :   slot_ctx->slot_bank.epoch_stakes holds the stakes at T-2
    2712             :  */
    2713             : /* process for the start of a new epoch */
    2714             : 
    2715             : void fd_process_new_epoch( fd_exec_slot_ctx_t * slot_ctx,
    2716           0 :                            ulong                parent_epoch ) {
    2717           0 :   FD_LOG_NOTICE(( "fd_process_new_epoch start" ));
    2718             : 
    2719           0 :   ulong             slot;
    2720           0 :   fd_epoch_bank_t * epoch_bank = fd_exec_epoch_ctx_epoch_bank( slot_ctx->epoch_ctx );
    2721           0 :   ulong             epoch      = fd_slot_to_epoch( &epoch_bank->epoch_schedule, slot_ctx->slot_bank.slot, &slot );
    2722             : 
    2723             :   /* Activate new features
    2724             :      https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank.rs#L6587-L6598 */
    2725           0 :   fd_features_activate( slot_ctx );
    2726           0 :   fd_features_restore( slot_ctx );
    2727             : 
    2728             :   /* Apply builtin program feature transitions
    2729             :      https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank.rs#L6621-L6624 */
    2730           0 :   fd_apply_builtin_program_feature_transitions( slot_ctx );
    2731             : 
    2732             :   /* Change the speed of the poh clock
    2733             :      https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank.rs#L6627-L6649 */
    2734           0 :   if( FD_FEATURE_JUST_ACTIVATED( slot_ctx, update_hashes_per_tick6 ) ) {
    2735           0 :     epoch_bank->hashes_per_tick = UPDATED_HASHES_PER_TICK6;
    2736           0 :   } else if( FD_FEATURE_JUST_ACTIVATED(slot_ctx, update_hashes_per_tick5 ) ) {
    2737           0 :     epoch_bank->hashes_per_tick = UPDATED_HASHES_PER_TICK5;
    2738           0 :   } else if( FD_FEATURE_JUST_ACTIVATED( slot_ctx, update_hashes_per_tick4 ) ) {
    2739           0 :     epoch_bank->hashes_per_tick = UPDATED_HASHES_PER_TICK4;
    2740           0 :   } else if( FD_FEATURE_JUST_ACTIVATED(slot_ctx, update_hashes_per_tick3 ) ) {
    2741           0 :     epoch_bank->hashes_per_tick = UPDATED_HASHES_PER_TICK3;
    2742           0 :   } else if( FD_FEATURE_JUST_ACTIVATED(slot_ctx, update_hashes_per_tick2 ) ) {
    2743           0 :     epoch_bank->hashes_per_tick = UPDATED_HASHES_PER_TICK2;
    2744           0 :   }
    2745             : 
    2746             :   /* Get the new rate activation epoch */
    2747           0 :   int _err[1];
    2748           0 :   ulong * new_rate_activation_epoch = fd_scratch_alloc( alignof(ulong), sizeof(ulong) );
    2749           0 :   int     is_some                   = fd_new_warmup_cooldown_rate_epoch( slot_ctx, new_rate_activation_epoch, _err );
    2750           0 :   if( FD_UNLIKELY( !is_some ) ) {
    2751           0 :     new_rate_activation_epoch = NULL;
    2752           0 :   }
    2753             : 
    2754           0 :   FD_SCRATCH_SCOPE_BEGIN {
    2755             : 
    2756           0 :     fd_epoch_info_t temp_info = {0};
    2757           0 :     fd_epoch_info_new( &temp_info );
    2758             : 
    2759             :     /* Updates stake history sysvar accumulated values. */
    2760           0 :     fd_stakes_activate_epoch( slot_ctx, new_rate_activation_epoch, &temp_info );
    2761             : 
    2762             :     /* Update the stakes epoch value to the new epoch */
    2763           0 :     epoch_bank->stakes.epoch = epoch;
    2764             : 
    2765             :     /* If appropiate, use the stakes at T-1 to generate the leader schedule instead of T-2.
    2766             :        This is due to a subtlety in how Agave's stake caches interact when loading from snapshots.
    2767             :        See the comment in fd_exec_slot_ctx_recover_. */
    2768           0 :     if( slot_ctx->slot_bank.has_use_preceeding_epoch_stakes && slot_ctx->slot_bank.use_preceeding_epoch_stakes == epoch ) {
    2769           0 :       fd_update_epoch_stakes( slot_ctx );
    2770           0 :     }
    2771             : 
    2772             :     /* Distribute rewards */
    2773           0 :     fd_hash_t const * parent_blockhash = slot_ctx->slot_bank.block_hash_queue.last_hash;
    2774           0 :     if( ( FD_FEATURE_ACTIVE( slot_ctx, enable_partitioned_epoch_reward ) ||
    2775           0 :           FD_FEATURE_ACTIVE( slot_ctx, partitioned_epoch_rewards_superfeature ) ) ) {
    2776           0 :       FD_LOG_NOTICE(( "fd_begin_partitioned_rewards" ));
    2777           0 :       fd_begin_partitioned_rewards( slot_ctx, parent_blockhash, parent_epoch, &temp_info );
    2778           0 :     } else {
    2779           0 :       fd_update_rewards( slot_ctx, parent_blockhash, parent_epoch, &temp_info );
    2780           0 :     }
    2781             : 
    2782             :     /* Updates stakes at time T */
    2783           0 :     fd_stake_history_t const * history = fd_sysvar_cache_stake_history( slot_ctx->sysvar_cache );
    2784           0 :     if( FD_UNLIKELY( !history ) ) {
    2785           0 :       FD_LOG_ERR(( "StakeHistory sysvar is missing from sysvar cache" ));
    2786           0 :     }
    2787             : 
    2788           0 :     FD_LOG_NOTICE(( "refresh_vote_accounts" ));
    2789             : 
    2790           0 :     refresh_vote_accounts( slot_ctx, history, new_rate_activation_epoch, &temp_info );
    2791           0 :     fd_update_stake_delegations( slot_ctx, &temp_info );
    2792             : 
    2793             :     /* Replace stakes at T-2 (slot_ctx->slot_bank.epoch_stakes) by stakes at T-1 (epoch_bank->next_epoch_stakes) */
    2794           0 :     fd_update_epoch_stakes( slot_ctx );
    2795             : 
    2796             :     /* Replace stakes at T-1 (epoch_bank->next_epoch_stakes) by updated stakes at T (stakes->vote_accounts) */
    2797           0 :     fd_update_next_epoch_stakes( slot_ctx );
    2798             : 
    2799             :     /* Update current leaders using slot_ctx->slot_bank.epoch_stakes (new T-2 stakes) */
    2800           0 :     fd_runtime_update_leaders( slot_ctx, slot_ctx->slot_bank.slot );
    2801             : 
    2802           0 :     fd_calculate_epoch_accounts_hash_values( slot_ctx );
    2803           0 :   } FD_SCRATCH_SCOPE_END;
    2804             : 
    2805           0 :   FD_LOG_NOTICE(( "fd_process_new_epoch end" ));
    2806           0 : }
    2807             : 
    2808             : /******************************************************************************/
    2809             : /* Block Parsing                                                              */
    2810             : /******************************************************************************/
    2811             : 
    2812             : /* Block iteration and parsing */
    2813             : 
    2814             : /* As a note, all of the logic in this section is used by the full firedancer
    2815             :    client. The store tile uses these APIs to help parse raw (micro)blocks 
    2816             :    received from the network. */
    2817             : 
    2818             : /* Helpers */
    2819             : 
    2820             : static int 
    2821             : fd_runtime_parse_microblock_hdr( void const *          buf,
    2822             :                                  ulong                 buf_sz,
    2823             :                                  fd_microblock_hdr_t * opt_microblock_hdr,
    2824           0 :                                  ulong *               opt_microblock_hdr_size ) {
    2825             : 
    2826           0 :   if( FD_UNLIKELY( buf_sz<sizeof(fd_microblock_hdr_t) ) ) {
    2827           0 :     return -1;
    2828           0 :   }
    2829             : 
    2830           0 :   if( !!opt_microblock_hdr ) {
    2831           0 :     *opt_microblock_hdr = *(fd_microblock_hdr_t *)buf;
    2832           0 :   }
    2833             : 
    2834           0 :   if( !!opt_microblock_hdr_size ) {
    2835           0 :     *opt_microblock_hdr_size = sizeof(fd_microblock_hdr_t);
    2836           0 :   }
    2837             : 
    2838           0 :   return 0;
    2839           0 : }
    2840             : 
    2841             : /* if we are currently in the middle of a batch, batch_cnt will include the current batch.
    2842             :    if we are at the start of a batch, batch_cnt will include the current batch. */
    2843             : static fd_raw_block_txn_iter_t
    2844             : find_next_txn_in_raw_block( uchar const *                  orig_data,
    2845             :                             fd_block_entry_batch_t const * batches, /* The batch we are currently consuming. */
    2846             :                             ulong                          batch_cnt, /* Includes batch we are currently consuming. */
    2847             :                             ulong                          curr_offset,
    2848           0 :                             ulong                          num_microblocks ) {
    2849             : 
    2850             :   /* At this point, all the transactions in the current microblock have been consumed
    2851             :      by fd_raw_block_txn_iter_next */
    2852             : 
    2853             :   /* Case 1: there are microblocks remaining in the current batch */
    2854           0 :   for( ulong i=0UL; i<num_microblocks; i++ ) {
    2855           0 :     ulong microblock_hdr_size            = 0UL;
    2856           0 :     fd_microblock_info_t microblock_info = {0};
    2857           0 :     if( FD_UNLIKELY( fd_runtime_parse_microblock_hdr( orig_data + curr_offset, 
    2858           0 :                                                       batches->end_off - curr_offset, 
    2859           0 :                                                       &microblock_info.microblock_hdr, 
    2860           0 :                                                       &microblock_hdr_size ) ) ) {
    2861             :       /* TODO: improve error handling */
    2862           0 :       FD_LOG_ERR(( "premature end of batch" ));
    2863           0 :     }
    2864           0 :     curr_offset += microblock_hdr_size;
    2865             : 
    2866             :     /* If we have found a microblock with transactions in the current batch, return that */
    2867           0 :     if( FD_LIKELY( microblock_info.microblock_hdr.txn_cnt ) ) {
    2868           0 :       return (fd_raw_block_txn_iter_t){
    2869           0 :         .curr_batch            = batches,
    2870           0 :         .orig_data             = orig_data,
    2871           0 :         .remaining_batches     = batch_cnt,
    2872           0 :         .remaining_microblocks = fd_ulong_sat_sub( fd_ulong_sat_sub(num_microblocks, i), 1UL),
    2873           0 :         .remaining_txns        = microblock_info.microblock_hdr.txn_cnt,
    2874           0 :         .curr_offset           = curr_offset,
    2875           0 :         .curr_txn_sz           = ULONG_MAX
    2876           0 :       };
    2877           0 :     }
    2878           0 :   }
    2879             : 
    2880             :   /* If we have consumed the current batch, but did not find any txns, we need to move on to the next one */
    2881           0 :   curr_offset = batches->end_off;
    2882           0 :   batch_cnt   = fd_ulong_sat_sub( batch_cnt, 1UL );
    2883           0 :   batches++;
    2884             :   
    2885             :   /* Case 2: need to find the next batch with a microblock in that has a non-zero number of txns */
    2886           0 :   for( ulong i=0UL; i<batch_cnt; i++ ) {
    2887             :     /* Sanity-check that we have not over-shot the end of the batch */
    2888           0 :     ulong const batch_end_off = batches[i].end_off;
    2889           0 :     if( FD_UNLIKELY( curr_offset+sizeof(ulong)>batch_end_off ) ) {
    2890           0 :       FD_LOG_ERR(( "premature end of batch" ));
    2891           0 :     }
    2892             : 
    2893             :     /* Consume the ulong describing how many microblocks there are */
    2894           0 :     num_microblocks = FD_LOAD( ulong, orig_data + curr_offset );
    2895           0 :     curr_offset    += sizeof(ulong);
    2896             : 
    2897             :     /* Iterate over each microblock until we find one with a non-zero txn cnt */
    2898           0 :     for( ulong j=0UL; j<num_microblocks; j++ ) {
    2899           0 :       ulong microblock_hdr_size            = 0UL;
    2900           0 :       fd_microblock_info_t microblock_info = {0};
    2901           0 :       if( FD_UNLIKELY( fd_runtime_parse_microblock_hdr( orig_data + curr_offset, 
    2902           0 :                                                         batch_end_off - curr_offset, 
    2903           0 :                                                         &microblock_info.microblock_hdr, 
    2904           0 :                                                         &microblock_hdr_size ) ) ) {
    2905             :         /* TODO: improve error handling */
    2906           0 :         FD_LOG_ERR(( "premature end of batch" ));
    2907           0 :       }
    2908           0 :       curr_offset += microblock_hdr_size;
    2909             : 
    2910             :       /* If we have found a microblock with a non-zero number of transactions in, return that */
    2911           0 :       if( FD_LIKELY( microblock_info.microblock_hdr.txn_cnt ) ) {
    2912           0 :         return (fd_raw_block_txn_iter_t){
    2913           0 :           .curr_batch            = &batches[i],
    2914           0 :           .orig_data             = orig_data,
    2915           0 :           .remaining_batches     = fd_ulong_sat_sub( batch_cnt, i ),
    2916           0 :           .remaining_microblocks = fd_ulong_sat_sub( fd_ulong_sat_sub( num_microblocks, j ), 1UL ),
    2917           0 :           .remaining_txns        = microblock_info.microblock_hdr.txn_cnt,
    2918           0 :           .curr_offset           = curr_offset,
    2919           0 :           .curr_txn_sz           = ULONG_MAX
    2920           0 :         };
    2921           0 :       }
    2922           0 :     }
    2923             : 
    2924             :     /* Skip to the start of the next batch */
    2925           0 :     curr_offset = batch_end_off;
    2926           0 :   }
    2927             : 
    2928             :   /* Case 3: we didn't manage to find any microblocks with non-zero transaction counts in */
    2929           0 :   return (fd_raw_block_txn_iter_t) {
    2930           0 :     .curr_batch            = batches,
    2931           0 :     .orig_data             = orig_data,
    2932           0 :     .remaining_batches     = 0UL,
    2933           0 :     .remaining_microblocks = 0UL,
    2934           0 :     .remaining_txns        = 0UL,
    2935           0 :     .curr_offset           = curr_offset,
    2936           0 :     .curr_txn_sz           = ULONG_MAX
    2937           0 :   };
    2938           0 : }
    2939             : 
    2940             : /* Public API */
    2941             : 
    2942             : fd_raw_block_txn_iter_t
    2943             : fd_raw_block_txn_iter_init( uchar const *                  orig_data,
    2944             :                             fd_block_entry_batch_t const * batches,
    2945           0 :                             ulong                          batch_cnt ) {
    2946             :   /* In general, every read of a lower level count should lead to a
    2947             :      decrement of a higher level count.  For example, reading a count
    2948             :      of microblocks should lead to a decrement of the number of
    2949             :      remaining batches.  In some sense, the batch count is drained into
    2950             :      the microblock count. */
    2951             : 
    2952           0 :   ulong num_microblocks = FD_LOAD( ulong, orig_data );
    2953           0 :   return find_next_txn_in_raw_block( orig_data, batches, batch_cnt, sizeof(ulong), num_microblocks );
    2954           0 : }
    2955             : 
    2956             : ulong
    2957           0 : fd_raw_block_txn_iter_done( fd_raw_block_txn_iter_t iter ) {
    2958           0 :   return iter.remaining_batches==0UL && iter.remaining_microblocks==0UL && iter.remaining_txns==0UL;
    2959           0 : }
    2960             : 
    2961             : fd_raw_block_txn_iter_t
    2962           0 : fd_raw_block_txn_iter_next( fd_raw_block_txn_iter_t iter ) {
    2963           0 :   ulong const batch_end_off = iter.curr_batch->end_off;
    2964           0 :   fd_txn_p_t out_txn;
    2965           0 :   if( iter.curr_txn_sz == ULONG_MAX ) {
    2966           0 :     ulong payload_sz = 0;
    2967           0 :     ulong txn_sz = fd_txn_parse_core( iter.orig_data + iter.curr_offset, fd_ulong_min( batch_end_off - iter.curr_offset, FD_TXN_MTU), TXN(&out_txn), NULL, &payload_sz );
    2968           0 :     if( FD_UNLIKELY( !txn_sz || txn_sz>FD_TXN_MTU ) ) {
    2969           0 :       FD_LOG_ERR(("Invalid txn parse"));
    2970           0 :     }
    2971           0 :     iter.curr_offset += payload_sz;
    2972           0 :   } else {
    2973           0 :     iter.curr_offset += iter.curr_txn_sz;
    2974           0 :     iter.curr_txn_sz  = ULONG_MAX;
    2975           0 :   }
    2976             : 
    2977           0 :   if( --iter.remaining_txns ) {
    2978           0 :     return iter;
    2979           0 :   }
    2980             : 
    2981           0 :   return find_next_txn_in_raw_block( iter.orig_data,
    2982           0 :                                      iter.curr_batch,
    2983           0 :                                      iter.remaining_batches,
    2984           0 :                                      iter.curr_offset,
    2985           0 :                                      iter.remaining_microblocks );
    2986           0 : }
    2987             : 
    2988             : void
    2989           0 : fd_raw_block_txn_iter_ele( fd_raw_block_txn_iter_t iter, fd_txn_p_t * out_txn ) {
    2990           0 :   ulong const batch_end_off = iter.curr_batch->end_off;
    2991           0 :   ulong       payload_sz    = 0UL;
    2992           0 :   ulong       txn_sz        = fd_txn_parse_core( iter.orig_data + iter.curr_offset,
    2993           0 :                                                  fd_ulong_min( batch_end_off - iter.curr_offset, FD_TXN_MTU ),
    2994           0 :                                                  TXN( out_txn ), NULL, &payload_sz );
    2995             : 
    2996           0 :   if( FD_UNLIKELY( !txn_sz || txn_sz>FD_TXN_MTU ) ) {
    2997           0 :     FD_LOG_ERR(( "Invalid txn parse %lu", txn_sz ));
    2998           0 :   }
    2999           0 :   fd_memcpy( out_txn->payload, iter.orig_data + iter.curr_offset, payload_sz );
    3000           0 :   out_txn->payload_sz = (ushort)payload_sz;
    3001           0 :   iter.curr_txn_sz    = payload_sz;
    3002           0 : }
    3003             : 
    3004             : /******************************************************************************/
    3005             : /* Block Parsing logic (Only for offline replay)                              */
    3006             : /******************************************************************************/
    3007             : 
    3008             : /* The below runtime block parsing and block destroying logic is ONLY used in 
    3009             :    offline replay to simulate the block parsing/freeing that would occur in 
    3010             :    the full, live firedancer client. This is done via two APIs: 
    3011             :    fd_runtime_block_prepare and fd_runtime_block_destroy. */
    3012             : 
    3013             : /* Helpers for fd_runtime_block_prepare */
    3014             : 
    3015             : static int 
    3016             : fd_runtime_parse_microblock_txns( void const *                buf,
    3017             :                                   ulong                       buf_sz,
    3018             :                                   fd_microblock_hdr_t const * microblock_hdr,
    3019             :                                   fd_txn_p_t *                out_txns,
    3020             :                                   ulong *                     out_signature_cnt,
    3021             :                                   ulong *                     out_account_cnt,
    3022           0 :                                   ulong *                     out_microblock_txns_sz ) {
    3023             : 
    3024           0 :   ulong buf_off       = 0UL;
    3025           0 :   ulong signature_cnt = 0UL;
    3026           0 :   ulong account_cnt   = 0UL;
    3027             : 
    3028           0 :   for( ulong i=0UL; i<microblock_hdr->txn_cnt; i++ ) {
    3029           0 :     ulong payload_sz = 0UL;
    3030           0 :     ulong txn_sz     = fd_txn_parse_core( (uchar const *)buf + buf_off, 
    3031           0 :                                           fd_ulong_min( buf_sz-buf_off, FD_TXN_MTU ), 
    3032           0 :                                           TXN( &out_txns[i] ),
    3033           0 :                                           NULL, 
    3034           0 :                                           &payload_sz );
    3035           0 :     if( FD_UNLIKELY( !txn_sz || txn_sz>FD_TXN_MTU ) ) {
    3036           0 :       return -1;
    3037           0 :     }
    3038             : 
    3039           0 :     fd_memcpy( out_txns[i].payload, (uchar *)buf + buf_off, payload_sz );
    3040           0 :     out_txns[i].payload_sz = (ushort)payload_sz;
    3041             : 
    3042           0 :     signature_cnt += TXN( &out_txns[i] )->signature_cnt;
    3043           0 :     account_cnt   += fd_txn_account_cnt( TXN(&out_txns[i]), FD_TXN_ACCT_CAT_ALL );
    3044           0 :     buf_off       += payload_sz;
    3045           0 :   }
    3046             : 
    3047           0 :   *out_signature_cnt      = signature_cnt;
    3048           0 :   *out_account_cnt        = account_cnt;
    3049           0 :   *out_microblock_txns_sz = buf_off;
    3050             : 
    3051           0 :   return 0;
    3052           0 : }
    3053             : 
    3054             : static int 
    3055             : fd_runtime_microblock_prepare( void const *           buf,
    3056             :                                ulong                  buf_sz,
    3057             :                                fd_valloc_t            valloc,
    3058           0 :                                fd_microblock_info_t * out_microblock_info ) {
    3059             : 
    3060           0 :   fd_microblock_info_t microblock_info = {
    3061           0 :     .raw_microblock = buf,
    3062           0 :     .signature_cnt  = 0UL,
    3063           0 :   };
    3064           0 :   ulong buf_off = 0UL;
    3065           0 :   ulong hdr_sz  = 0UL;
    3066           0 :   if( FD_UNLIKELY( fd_runtime_parse_microblock_hdr( buf, buf_sz, &microblock_info.microblock_hdr, &hdr_sz ) ) ) {
    3067           0 :     return -1;
    3068           0 :   }
    3069           0 :   buf_off += hdr_sz;
    3070             : 
    3071           0 :   ulong txn_cnt        = microblock_info.microblock_hdr.txn_cnt;
    3072           0 :   microblock_info.txns = fd_valloc_malloc( valloc, alignof(fd_txn_p_t), txn_cnt * sizeof(fd_txn_p_t) );
    3073           0 :   ulong txns_sz        = 0UL;
    3074           0 :   if( FD_UNLIKELY( fd_runtime_parse_microblock_txns( (uchar *)buf + buf_off,
    3075           0 :                                                      buf_sz - buf_off,
    3076           0 :                                                      &microblock_info.microblock_hdr,
    3077           0 :                                                      microblock_info.txns,
    3078           0 :                                                      &microblock_info.signature_cnt,
    3079           0 :                                                      &microblock_info.account_cnt,
    3080           0 :                                                      &txns_sz ) ) ) {
    3081           0 :     fd_valloc_free( valloc, microblock_info.txns );
    3082           0 :     return -1;
    3083           0 :   }
    3084             : 
    3085           0 :   buf_off                          += txns_sz;
    3086           0 :   microblock_info.raw_microblock_sz = buf_off;
    3087           0 :   *out_microblock_info              = microblock_info;
    3088             : 
    3089           0 :   return 0;
    3090           0 : }
    3091             : 
    3092             : static int
    3093             : fd_runtime_microblock_batch_prepare( void const *                 buf,
    3094             :                                      ulong                        buf_sz,
    3095             :                                      fd_valloc_t                  valloc,
    3096           0 :                                      fd_microblock_batch_info_t * out_microblock_batch_info ) {
    3097             : 
    3098           0 :   fd_microblock_batch_info_t microblock_batch_info = {
    3099           0 :     .raw_microblock_batch = buf,
    3100           0 :     .signature_cnt        = 0UL,
    3101           0 :     .txn_cnt              = 0UL,
    3102           0 :     .account_cnt          = 0UL,
    3103           0 :   };
    3104           0 :   ulong buf_off = 0UL;
    3105             : 
    3106           0 :   if( FD_UNLIKELY( buf_sz<sizeof(ulong) ) ) {
    3107           0 :     FD_LOG_WARNING(( "microblock batch buffer too small" ));
    3108           0 :     return -1;
    3109           0 :   }
    3110           0 :   ulong microblock_cnt = FD_LOAD( ulong, buf );
    3111           0 :   buf_off             += sizeof(ulong);
    3112             : 
    3113           0 :   microblock_batch_info.microblock_cnt   = microblock_cnt;
    3114           0 :   microblock_batch_info.microblock_infos = fd_valloc_malloc( valloc, alignof(fd_microblock_info_t), microblock_cnt * sizeof(fd_microblock_info_t) );
    3115             : 
    3116           0 :   ulong signature_cnt = 0UL;
    3117           0 :   ulong txn_cnt       = 0UL;
    3118           0 :   ulong account_cnt   = 0UL;
    3119           0 :   for( ulong i=0UL; i<microblock_cnt; i++ ) {
    3120           0 :     fd_microblock_info_t * microblock_info = &microblock_batch_info.microblock_infos[i];
    3121           0 :     if( FD_UNLIKELY( fd_runtime_microblock_prepare( (uchar const *)buf + buf_off, buf_sz - buf_off, valloc, microblock_info ) ) ) {
    3122           0 :       fd_valloc_free( valloc, microblock_batch_info.microblock_infos );
    3123           0 :       return -1;
    3124           0 :     }
    3125             : 
    3126           0 :     signature_cnt += microblock_info->signature_cnt;
    3127           0 :     txn_cnt       += microblock_info->microblock_hdr.txn_cnt;
    3128           0 :     account_cnt   += microblock_info->account_cnt;
    3129           0 :     buf_off       += microblock_info->raw_microblock_sz;
    3130           0 :   }
    3131             : 
    3132           0 :   microblock_batch_info.signature_cnt           = signature_cnt;
    3133           0 :   microblock_batch_info.txn_cnt                 = txn_cnt;
    3134           0 :   microblock_batch_info.account_cnt             = account_cnt;
    3135           0 :   microblock_batch_info.raw_microblock_batch_sz = buf_off;
    3136             : 
    3137           0 :   *out_microblock_batch_info                    = microblock_batch_info;
    3138             : 
    3139           0 :   return 0;
    3140           0 : }
    3141             : 
    3142             : /* This function is used for parsing/preparing blocks during offline runtime replay. */
    3143             : static int
    3144             : fd_runtime_block_prepare( fd_blockstore_t * blockstore,
    3145             :                           fd_block_t *      block,
    3146             :                           ulong             slot,
    3147             :                           fd_valloc_t       valloc,
    3148           0 :                           fd_block_info_t * out_block_info ) {
    3149           0 :   uchar const *                  buf         = fd_blockstore_block_data_laddr( blockstore, block );
    3150           0 :   ulong const                    buf_sz      = block->data_sz;
    3151           0 :   fd_block_entry_batch_t const * batch_laddr = fd_blockstore_block_batch_laddr( blockstore, block );
    3152           0 :   ulong const                    batch_cnt   = block->batch_cnt;
    3153             : 
    3154           0 :   fd_block_info_t block_info = {
    3155           0 :       .raw_block    = buf,
    3156           0 :       .raw_block_sz = buf_sz,
    3157           0 :   };
    3158             : 
    3159           0 :   ulong microblock_batch_cnt = 0UL;
    3160           0 :   ulong microblock_cnt       = 0UL;
    3161           0 :   ulong signature_cnt        = 0UL;
    3162           0 :   ulong txn_cnt              = 0UL;
    3163           0 :   ulong account_cnt          = 0UL;
    3164           0 :   block_info.microblock_batch_infos = fd_valloc_malloc( valloc, alignof(fd_microblock_batch_info_t), block->batch_cnt * sizeof(fd_microblock_batch_info_t) );
    3165             : 
    3166           0 :   ulong buf_off = 0UL;
    3167           0 :   for( microblock_batch_cnt=0UL; microblock_batch_cnt < batch_cnt; microblock_batch_cnt++ ) {
    3168           0 :     ulong const batch_end_off = batch_laddr[ microblock_batch_cnt ].end_off;
    3169           0 :     fd_microblock_batch_info_t * microblock_batch_info = block_info.microblock_batch_infos + microblock_batch_cnt;
    3170           0 :     if( FD_UNLIKELY( fd_runtime_microblock_batch_prepare( buf + buf_off, batch_end_off - buf_off, valloc, microblock_batch_info ) ) ) {
    3171           0 :       return -1;
    3172           0 :     }
    3173             : 
    3174           0 :     signature_cnt  += microblock_batch_info->signature_cnt;
    3175           0 :     txn_cnt        += microblock_batch_info->txn_cnt;
    3176           0 :     account_cnt    += microblock_batch_info->account_cnt;
    3177           0 :     microblock_cnt += microblock_batch_info->microblock_cnt;
    3178             : 
    3179           0 :     uchar allow_trailing = 1UL;
    3180           0 :     buf_off += microblock_batch_info->raw_microblock_batch_sz;
    3181           0 :     if( FD_UNLIKELY( buf_off > batch_end_off ) ) {
    3182           0 :       FD_LOG_ERR(( "parser error: shouldn't have been allowed to read past batch boundary" ));
    3183           0 :     }
    3184           0 :     if( FD_UNLIKELY( buf_off < batch_end_off ) ) {
    3185           0 :       if( FD_LIKELY( allow_trailing ) ) {
    3186           0 :         FD_LOG_NOTICE(( "ignoring %lu trailing bytes in slot %lu batch %lu", batch_end_off-buf_off, slot, microblock_batch_cnt ));
    3187           0 :       }
    3188           0 :       if( FD_UNLIKELY( !allow_trailing ) ) {
    3189           0 :         FD_LOG_WARNING(( "%lu trailing bytes in slot %lu batch %lu", batch_end_off-buf_off, slot, microblock_batch_cnt ));
    3190           0 :         return -1;
    3191           0 :       }
    3192           0 :     }
    3193           0 :     buf_off = batch_end_off;
    3194           0 :   }
    3195             : 
    3196           0 :   block_info.microblock_batch_cnt = microblock_batch_cnt;
    3197           0 :   block_info.microblock_cnt       = microblock_cnt;
    3198           0 :   block_info.signature_cnt        = signature_cnt;
    3199           0 :   block_info.txn_cnt              = txn_cnt;
    3200           0 :   block_info.account_cnt          = account_cnt;
    3201             : 
    3202           0 :   *out_block_info = block_info;
    3203             : 
    3204           0 :   return 0;
    3205           0 : }
    3206             : 
    3207             : /* Block collecting (Only for offline replay) */
    3208             : 
    3209             : static ulong
    3210             : fd_runtime_microblock_collect_txns( fd_microblock_info_t const * microblock_info,
    3211           0 :                                     fd_txn_p_t *                 out_txns ) {
    3212           0 :   ulong txn_cnt = microblock_info->microblock_hdr.txn_cnt;
    3213           0 :   fd_memcpy( out_txns, microblock_info->txns, txn_cnt * sizeof(fd_txn_p_t) );
    3214             : 
    3215           0 :   return txn_cnt;
    3216           0 : }
    3217             : 
    3218             : static ulong
    3219             : fd_runtime_microblock_batch_collect_txns( fd_microblock_batch_info_t const * microblock_batch_info,
    3220           0 :                                           fd_txn_p_t *                       out_txns ) {
    3221           0 :   for( ulong i=0UL; i<microblock_batch_info->microblock_cnt; i++ ) {
    3222           0 :     ulong txns_collected = fd_runtime_microblock_collect_txns( &microblock_batch_info->microblock_infos[i], out_txns );
    3223           0 :     out_txns            += txns_collected;
    3224           0 :   }
    3225             : 
    3226           0 :   return microblock_batch_info->txn_cnt;
    3227           0 : }
    3228             : 
    3229             : static ulong
    3230             : fd_runtime_block_collect_txns( fd_block_info_t const * block_info,
    3231           0 :                                fd_txn_p_t *            out_txns ) {
    3232           0 :   for( ulong i=0UL; i<block_info->microblock_batch_cnt; i++ ) {
    3233           0 :     ulong txns_collected = fd_runtime_microblock_batch_collect_txns( &block_info->microblock_batch_infos[i], out_txns );
    3234           0 :     out_txns            += txns_collected;
    3235           0 :   }
    3236             : 
    3237           0 :   return block_info->txn_cnt;
    3238           0 : }
    3239             : 
    3240             : /* TODO: Move this around once the changes to it are updated. */
    3241             : 
    3242             : int
    3243             : fd_runtime_block_verify_ticks( fd_block_info_t const * block_info,
    3244             :                                ulong                   tick_height,
    3245           0 :                                ulong                   max_tick_height ) {
    3246           0 :   (void)tick_height; (void)max_tick_height;
    3247           0 :   ulong tick_count = 0UL;
    3248           0 :   for( ulong i = 0UL; i < block_info->microblock_batch_cnt; i++ ) {
    3249           0 :     fd_microblock_batch_info_t const * microblock_batch_info = &block_info->microblock_batch_infos[ i ];
    3250           0 :     for( ulong j = 0UL; j < microblock_batch_info->microblock_cnt; j++ ) {
    3251           0 :       fd_microblock_info_t const * microblock_info = &microblock_batch_info->microblock_infos[ i ];
    3252           0 :       if( microblock_info->microblock_hdr.txn_cnt == 0UL ) {
    3253             :         /* if this mblk is a tick */
    3254           0 :         tick_count++;
    3255           0 :       }
    3256           0 :     }
    3257           0 :   }
    3258           0 :   (void)tick_count;
    3259           0 :   return 0;
    3260           0 : }
    3261             : 
    3262             : /******************************************************************************/
    3263             : /* Genesis                                                                    */
    3264             : /*******************************************************************************/
    3265             : 
    3266             : static void 
    3267             : fd_runtime_init_program( fd_exec_slot_ctx_t * slot_ctx )
    3268           0 : {
    3269           0 :   fd_sysvar_recent_hashes_init( slot_ctx );
    3270           0 :   fd_sysvar_clock_init( slot_ctx );
    3271           0 :   fd_sysvar_slot_history_init( slot_ctx );
    3272           0 :   fd_sysvar_epoch_schedule_init( slot_ctx );
    3273           0 :   if( !FD_FEATURE_ACTIVE( slot_ctx, disable_fees_sysvar ) ) {
    3274           0 :     fd_sysvar_fees_init( slot_ctx );
    3275           0 :   }
    3276           0 :   fd_sysvar_rent_init( slot_ctx );
    3277           0 :   fd_sysvar_stake_history_init( slot_ctx );
    3278           0 :   fd_sysvar_last_restart_slot_init( slot_ctx );
    3279             : 
    3280           0 :   fd_builtin_programs_init( slot_ctx );
    3281           0 :   fd_stake_program_config_init( slot_ctx );
    3282           0 : }
    3283             : 
    3284             : static void
    3285             : fd_runtime_init_bank_from_genesis( fd_exec_slot_ctx_t *  slot_ctx,
    3286             :                                    fd_genesis_solana_t * genesis_block,
    3287           0 :                                    fd_hash_t const *     genesis_hash ) {
    3288           0 :   slot_ctx->slot_bank.slot = 0UL;
    3289             : 
    3290           0 :   memcpy( &slot_ctx->slot_bank.poh, genesis_hash->hash, FD_SHA256_HASH_SZ );
    3291           0 :   memset( slot_ctx->slot_bank.banks_hash.hash, 0, FD_SHA256_HASH_SZ );
    3292             : 
    3293           0 :   slot_ctx->slot_bank.fee_rate_governor      = genesis_block->fee_rate_governor;
    3294           0 :   slot_ctx->slot_bank.lamports_per_signature = 0UL;
    3295           0 :   slot_ctx->prev_lamports_per_signature      = 0UL;
    3296             : 
    3297           0 :   fd_poh_config_t *     poh        = &genesis_block->poh_config;
    3298           0 :   fd_exec_epoch_ctx_t * epoch_ctx  = slot_ctx->epoch_ctx;
    3299           0 :   fd_epoch_bank_t *     epoch_bank = fd_exec_epoch_ctx_epoch_bank( epoch_ctx );
    3300           0 :   if( poh->has_hashes_per_tick ) {
    3301           0 :     epoch_bank->hashes_per_tick = poh->hashes_per_tick;
    3302           0 :   } else {
    3303           0 :     epoch_bank->hashes_per_tick = 0UL;
    3304           0 :   }
    3305           0 :   epoch_bank->ticks_per_slot        = genesis_block->ticks_per_slot;
    3306           0 :   epoch_bank->genesis_creation_time = genesis_block->creation_time;
    3307           0 :   uint128 target_tick_duration      = ((uint128)poh->target_tick_duration.seconds * 1000000000UL + (uint128)poh->target_tick_duration.nanoseconds);
    3308           0 :   epoch_bank->ns_per_slot           = target_tick_duration * epoch_bank->ticks_per_slot;
    3309             : 
    3310           0 :   epoch_bank->slots_per_year          = SECONDS_PER_YEAR * (1000000000.0 / (double)target_tick_duration) / (double)epoch_bank->ticks_per_slot;
    3311           0 :   epoch_bank->genesis_creation_time   = genesis_block->creation_time;
    3312           0 :   slot_ctx->slot_bank.max_tick_height = epoch_bank->ticks_per_slot * (slot_ctx->slot_bank.slot + 1);
    3313           0 :   epoch_bank->epoch_schedule          = genesis_block->epoch_schedule;
    3314           0 :   epoch_bank->inflation               = genesis_block->inflation;
    3315           0 :   epoch_bank->rent                    = genesis_block->rent;
    3316           0 :   slot_ctx->slot_bank.block_height    = 0UL;
    3317             : 
    3318           0 :   fd_block_block_hash_entry_t * hashes = slot_ctx->slot_bank.recent_block_hashes.hashes =
    3319           0 :                                          deq_fd_block_block_hash_entry_t_alloc( slot_ctx->valloc, FD_SYSVAR_RECENT_HASHES_CAP );
    3320           0 :   fd_block_block_hash_entry_t * elem   = deq_fd_block_block_hash_entry_t_push_head_nocopy( hashes );
    3321           0 :   fd_block_block_hash_entry_new( elem );
    3322           0 :   fd_memcpy( elem->blockhash.hash, genesis_hash, FD_SHA256_HASH_SZ );
    3323           0 :   elem->fee_calculator.lamports_per_signature = 0UL;
    3324             : 
    3325           0 :   slot_ctx->slot_bank.block_hash_queue.ages_root = NULL;
    3326           0 :   slot_ctx->slot_bank.block_hash_queue.ages_pool = fd_hash_hash_age_pair_t_map_alloc( slot_ctx->valloc, 400 );
    3327           0 :   fd_hash_hash_age_pair_t_mapnode_t * node       = fd_hash_hash_age_pair_t_map_acquire( slot_ctx->slot_bank.block_hash_queue.ages_pool );
    3328           0 :   node->elem = (fd_hash_hash_age_pair_t){
    3329           0 :     .key = *genesis_hash,
    3330           0 :     .val = (fd_hash_age_t){ .hash_index = 0UL, .fee_calculator = (fd_fee_calculator_t){.lamports_per_signature = 0UL}, .timestamp = (ulong)fd_log_wallclock() }
    3331           0 :   };
    3332           0 :   fd_hash_hash_age_pair_t_map_insert( slot_ctx->slot_bank.block_hash_queue.ages_pool, &slot_ctx->slot_bank.block_hash_queue.ages_root, node );
    3333           0 :   slot_ctx->slot_bank.block_hash_queue.last_hash_index = 0UL;
    3334           0 :   slot_ctx->slot_bank.block_hash_queue.last_hash       = fd_valloc_malloc( slot_ctx->valloc, FD_HASH_ALIGN, FD_HASH_FOOTPRINT );
    3335           0 :   fd_memcpy( slot_ctx->slot_bank.block_hash_queue.last_hash, genesis_hash, FD_SHA256_HASH_SZ );
    3336           0 :   slot_ctx->slot_bank.block_hash_queue.max_age         = FD_BLOCKHASH_QUEUE_MAX_ENTRIES;
    3337             : 
    3338           0 :   slot_ctx->signature_cnt = 0UL;
    3339             : 
    3340             :   /* Derive epoch stakes */
    3341             : 
    3342           0 :   fd_vote_accounts_pair_t_mapnode_t * vacc_pool = fd_exec_epoch_ctx_stake_votes_join( epoch_ctx );
    3343           0 :   fd_vote_accounts_pair_t_mapnode_t * vacc_root = NULL;
    3344           0 :   FD_TEST( vacc_pool );
    3345             : 
    3346           0 :   fd_delegation_pair_t_mapnode_t * sacc_pool = fd_exec_epoch_ctx_stake_delegations_join( epoch_ctx );
    3347           0 :   fd_delegation_pair_t_mapnode_t * sacc_root = NULL;
    3348             : 
    3349           0 :   fd_acc_lamports_t capitalization = 0UL;
    3350             : 
    3351           0 :   for( ulong i=0UL; i<genesis_block->accounts_len; i++ ) {
    3352           0 :     fd_pubkey_account_pair_t const * acc = &genesis_block->accounts[i];
    3353           0 :     capitalization = fd_ulong_sat_add( capitalization, acc->account.lamports );
    3354             : 
    3355           0 :     if( !memcmp(acc->account.owner.key, fd_solana_vote_program_id.key, sizeof(fd_pubkey_t)) ) {
    3356             :       /* Vote Program Account */
    3357           0 :       fd_vote_accounts_pair_t_mapnode_t *node = fd_vote_accounts_pair_t_map_acquire(vacc_pool);
    3358           0 :       FD_TEST( node );
    3359             : 
    3360           0 :       fd_vote_block_timestamp_t last_timestamp = {0};
    3361           0 :       fd_pubkey_t               node_pubkey    = {0};
    3362           0 :       FD_SCRATCH_SCOPE_BEGIN {
    3363             :         /* Deserialize content */
    3364           0 :         fd_vote_state_versioned_t vs[1];
    3365           0 :         fd_bincode_decode_ctx_t decode =
    3366           0 :             { .data    = acc->account.data,
    3367           0 :               .dataend = acc->account.data + acc->account.data_len,
    3368           0 :               .valloc  = fd_scratch_virtual() };
    3369           0 :         int decode_err = fd_vote_state_versioned_decode( vs, &decode );
    3370           0 :         if( FD_UNLIKELY( decode_err!=FD_BINCODE_SUCCESS ) ) {
    3371           0 :           FD_LOG_WARNING(( "fd_vote_state_versioned_decode failed (%d)", decode_err ));
    3372           0 :           return;
    3373           0 :         }
    3374             : 
    3375           0 :         switch( vs->discriminant )
    3376           0 :         {
    3377           0 :         case fd_vote_state_versioned_enum_current:
    3378           0 :           last_timestamp = vs->inner.current.last_timestamp;
    3379           0 :           node_pubkey    = vs->inner.current.node_pubkey;
    3380           0 :           break;
    3381           0 :         case fd_vote_state_versioned_enum_v0_23_5:
    3382           0 :           last_timestamp = vs->inner.v0_23_5.last_timestamp;
    3383           0 :           node_pubkey    = vs->inner.v0_23_5.node_pubkey;
    3384           0 :           break;
    3385           0 :         case fd_vote_state_versioned_enum_v1_14_11:
    3386           0 :           last_timestamp = vs->inner.v1_14_11.last_timestamp;
    3387           0 :           node_pubkey    = vs->inner.v1_14_11.node_pubkey;
    3388           0 :           break;
    3389           0 :         default:
    3390           0 :           __builtin_unreachable();
    3391           0 :         }
    3392             : 
    3393           0 :       } FD_SCRATCH_SCOPE_END;
    3394             : 
    3395           0 :       fd_memcpy(node->elem.key.key, acc->key.key, sizeof(fd_pubkey_t));
    3396           0 :       node->elem.stake = acc->account.lamports;
    3397           0 :       node->elem.value = (fd_solana_vote_account_t){
    3398           0 :         .lamports = acc->account.lamports,
    3399           0 :         .node_pubkey = node_pubkey,
    3400           0 :         .last_timestamp_ts = last_timestamp.timestamp,
    3401           0 :         .last_timestamp_slot = last_timestamp.slot,
    3402           0 :         .owner = acc->account.owner,
    3403           0 :         .executable = acc->account.executable,
    3404           0 :         .rent_epoch = acc->account.rent_epoch 
    3405           0 :       };
    3406             : 
    3407           0 :       fd_vote_accounts_pair_t_map_insert( vacc_pool, &vacc_root, node );
    3408             : 
    3409           0 :       FD_LOG_INFO(( "Adding genesis vote account: key=%s stake=%lu",
    3410           0 :                     FD_BASE58_ENC_32_ALLOCA( node->elem.key.key ),
    3411           0 :                     node->elem.stake ));
    3412           0 :     } else if( !memcmp( acc->account.owner.key, fd_solana_stake_program_id.key, sizeof(fd_pubkey_t) ) ) {
    3413             :       /* stake program account */
    3414           0 :       fd_stake_state_v2_t   stake_state   = {0};
    3415           0 :       fd_account_meta_t     meta          = { .dlen = acc->account.data_len };
    3416           0 :       fd_borrowed_account_t stake_account = {
    3417           0 :         .const_data = acc->account.data,
    3418           0 :         .const_meta = &meta,
    3419           0 :         .data = acc->account.data,
    3420           0 :         .meta = &meta 
    3421           0 :       };
    3422           0 :       FD_TEST( fd_stake_get_state(&stake_account, &slot_ctx->valloc, &stake_state) == 0 );
    3423           0 :       if( !stake_state.inner.stake.stake.delegation.stake ) { 
    3424           0 :         continue;
    3425           0 :       }
    3426           0 :       fd_delegation_pair_t_mapnode_t   query_node = {0};
    3427           0 :       fd_memcpy(&query_node.elem.account, acc->key.key, sizeof(fd_pubkey_t));
    3428           0 :       fd_delegation_pair_t_mapnode_t * node = fd_delegation_pair_t_map_find( sacc_pool, sacc_root, &query_node );
    3429             : 
    3430           0 :       if( !node ) {
    3431           0 :         node = fd_delegation_pair_t_map_acquire( sacc_pool );
    3432           0 :         fd_memcpy( &node->elem.account, acc->key.key, sizeof(fd_pubkey_t) );
    3433           0 :         fd_memcpy( &node->elem.delegation, &stake_state.inner.stake.stake.delegation, sizeof(fd_delegation_t) );
    3434           0 :         fd_delegation_pair_t_map_insert( sacc_pool, &sacc_root, node );
    3435           0 :       } else {
    3436           0 :         fd_memcpy( &node->elem.account, acc->key.key, sizeof(fd_pubkey_t) );
    3437           0 :         fd_memcpy( &node->elem.delegation, &stake_state.inner.stake.stake.delegation, sizeof(fd_delegation_t) );
    3438           0 :       }
    3439           0 :     } else if( !memcmp(acc->account.owner.key, fd_solana_feature_program_id.key, sizeof(fd_pubkey_t)) ) {
    3440             :       /* Feature Account */
    3441             : 
    3442             :       /* Scan list of feature IDs to resolve address => feature offset */
    3443           0 :       fd_feature_id_t const *found = NULL;
    3444           0 :       for( fd_feature_id_t const * id = fd_feature_iter_init();
    3445           0 :            !fd_feature_iter_done( id );
    3446           0 :            id = fd_feature_iter_next( id ) ) {
    3447           0 :         if( !memcmp( acc->key.key, id->id.key, sizeof(fd_pubkey_t) ) ) {
    3448           0 :           found = id;
    3449           0 :           break;
    3450           0 :         }
    3451           0 :       }
    3452             : 
    3453           0 :       if( found ) {
    3454             :         /* Load feature activation */
    3455           0 :         FD_SCRATCH_SCOPE_BEGIN {
    3456           0 :           fd_bincode_decode_ctx_t decode = { 
    3457           0 :             .data = acc->account.data,
    3458           0 :             .dataend = acc->account.data + acc->account.data_len,
    3459           0 :             .valloc = fd_scratch_virtual()
    3460           0 :           };
    3461           0 :           fd_feature_t feature = {0};
    3462           0 :           int err = fd_feature_decode( &feature, &decode );
    3463           0 :           FD_TEST( err==FD_BINCODE_SUCCESS );
    3464           0 :           if( feature.has_activated_at ) {
    3465           0 :             FD_LOG_DEBUG(( "Feature %s activated at %lu (genesis)", FD_BASE58_ENC_32_ALLOCA( acc->key.key ), feature.activated_at ));
    3466           0 :             fd_features_set( &slot_ctx->epoch_ctx->features, found, feature.activated_at );
    3467           0 :           } else {
    3468           0 :             FD_LOG_DEBUG(( "Feature %s not activated (genesis)", FD_BASE58_ENC_32_ALLOCA( acc->key.key ) ));
    3469           0 :             fd_features_set( &slot_ctx->epoch_ctx->features, found, ULONG_MAX );
    3470           0 :           }
    3471           0 :         } FD_SCRATCH_SCOPE_END;
    3472           0 :       }
    3473           0 :     }
    3474           0 :   }
    3475             : 
    3476           0 :   slot_ctx->slot_bank.epoch_stakes.vote_accounts_pool = fd_vote_accounts_pair_t_map_alloc( slot_ctx->valloc, 100000 );  /* FIXME remove magic constant */
    3477           0 :   slot_ctx->slot_bank.epoch_stakes.vote_accounts_root = NULL;
    3478             : 
    3479           0 :   fd_vote_accounts_pair_t_mapnode_t * next_pool = fd_exec_epoch_ctx_next_epoch_stakes_join( slot_ctx->epoch_ctx );
    3480           0 :   fd_vote_accounts_pair_t_mapnode_t * next_root = NULL;
    3481             : 
    3482           0 :   for( fd_vote_accounts_pair_t_mapnode_t *n = fd_vote_accounts_pair_t_map_minimum( vacc_pool, vacc_root );
    3483           0 :        n;
    3484           0 :        n = fd_vote_accounts_pair_t_map_successor( vacc_pool, n )) {
    3485           0 :     fd_vote_accounts_pair_t_mapnode_t * e = fd_vote_accounts_pair_t_map_acquire( slot_ctx->slot_bank.epoch_stakes.vote_accounts_pool );
    3486           0 :     fd_memcpy( &e->elem, &n->elem, sizeof(fd_vote_accounts_pair_t) );
    3487           0 :     fd_vote_accounts_pair_t_map_insert( slot_ctx->slot_bank.epoch_stakes.vote_accounts_pool, &slot_ctx->slot_bank.epoch_stakes.vote_accounts_root, e );
    3488             : 
    3489           0 :     fd_vote_accounts_pair_t_mapnode_t * next_e = fd_vote_accounts_pair_t_map_acquire( next_pool );
    3490           0 :     fd_memcpy( &next_e->elem, &n->elem, sizeof(fd_vote_accounts_pair_t) );
    3491           0 :     fd_vote_accounts_pair_t_map_insert( next_pool, &next_root, next_e );
    3492           0 :   }
    3493             : 
    3494           0 :   for( fd_delegation_pair_t_mapnode_t *n = fd_delegation_pair_t_map_minimum( sacc_pool, sacc_root );
    3495           0 :        n;
    3496           0 :        n = fd_delegation_pair_t_map_successor( sacc_pool, n )) {
    3497           0 :     fd_vote_accounts_pair_t_mapnode_t query_voter  = {0};
    3498           0 :     fd_pubkey_t *                     voter_pubkey = &n->elem.delegation.voter_pubkey;
    3499           0 :     fd_memcpy( &query_voter.elem.key, voter_pubkey, sizeof(fd_pubkey_t) );
    3500             : 
    3501           0 :     fd_vote_accounts_pair_t_mapnode_t * voter = fd_vote_accounts_pair_t_map_find( vacc_pool, vacc_root, &query_voter );
    3502             : 
    3503           0 :     if( !!voter ) {
    3504           0 :       voter->elem.stake = fd_ulong_sat_add( voter->elem.stake, n->elem.delegation.stake );
    3505           0 :     }
    3506           0 :   }
    3507             : 
    3508           0 :   epoch_bank->next_epoch_stakes = (fd_vote_accounts_t){
    3509           0 :     .vote_accounts_pool = next_pool,
    3510           0 :     .vote_accounts_root = next_root,
    3511           0 :   };
    3512             : 
    3513             :   /* Initializes the stakes cache in the Bank structure. */
    3514           0 :   epoch_bank->stakes = (fd_stakes_t){
    3515           0 :       .stake_delegations_pool = sacc_pool,
    3516           0 :       .stake_delegations_root = sacc_root,
    3517           0 :       .epoch                  = 0UL,
    3518           0 :       .unused                 = 0UL,
    3519           0 :       .vote_accounts = (fd_vote_accounts_t){
    3520           0 :         .vote_accounts_pool = vacc_pool,
    3521           0 :         .vote_accounts_root = vacc_root
    3522           0 :       },
    3523           0 :       .stake_history = {0}
    3524           0 :   };
    3525             : 
    3526           0 :   slot_ctx->slot_bank.capitalization             = capitalization;
    3527           0 :   slot_ctx->slot_bank.timestamp_votes.votes_pool = fd_clock_timestamp_vote_t_map_alloc( slot_ctx->valloc, 10000 ); /* FIXME: remove magic constant */
    3528           0 :   slot_ctx->slot_bank.timestamp_votes.votes_root = NULL;
    3529             : 
    3530           0 : }
    3531             : 
    3532             : static int
    3533           0 : fd_runtime_process_genesis_block( fd_exec_slot_ctx_t * slot_ctx, fd_capture_ctx_t * capture_ctx, fd_tpool_t * tpool ) {
    3534           0 :   ulong hashcnt_per_slot = slot_ctx->epoch_ctx->epoch_bank.hashes_per_tick * slot_ctx->epoch_ctx->epoch_bank.ticks_per_slot;
    3535           0 :   while( hashcnt_per_slot-- ) {
    3536           0 :     fd_sha256_hash( slot_ctx->slot_bank.poh.uc, sizeof(fd_hash_t), slot_ctx->slot_bank.poh.uc );
    3537           0 :   }
    3538             : 
    3539           0 :   slot_ctx->slot_bank.collected_execution_fees = 0UL;
    3540           0 :   slot_ctx->slot_bank.collected_priority_fees  = 0UL;
    3541           0 :   slot_ctx->slot_bank.collected_rent           = 0UL;
    3542           0 :   slot_ctx->signature_cnt                      = 0UL;
    3543           0 :   slot_ctx->txn_count                          = 0UL;
    3544           0 :   slot_ctx->nonvote_txn_count                  = 0UL;
    3545           0 :   slot_ctx->failed_txn_count                   = 0UL;
    3546           0 :   slot_ctx->nonvote_failed_txn_count           = 0UL;
    3547           0 :   slot_ctx->total_compute_units_used           = 0UL;
    3548             : 
    3549           0 :   fd_sysvar_slot_history_update( slot_ctx );
    3550             : 
    3551           0 :   fd_runtime_freeze( slot_ctx );
    3552             : 
    3553             :   /* sort and update bank hash */
    3554           0 :   int result = fd_update_hash_bank_tpool( slot_ctx, capture_ctx, &slot_ctx->slot_bank.banks_hash, slot_ctx->signature_cnt, tpool );
    3555           0 :   if( FD_UNLIKELY( result != FD_EXECUTOR_INSTR_SUCCESS ) ) {
    3556           0 :     FD_LOG_ERR(( "Failed to update bank hash with error=%d", result ));
    3557           0 :   }
    3558             : 
    3559           0 :   FD_TEST( FD_RUNTIME_EXECUTE_SUCCESS==fd_runtime_save_epoch_bank( slot_ctx ) );
    3560             : 
    3561           0 :   FD_TEST( FD_RUNTIME_EXECUTE_SUCCESS==fd_runtime_save_slot_bank( slot_ctx ) );
    3562             : 
    3563           0 :   return FD_RUNTIME_EXECUTE_SUCCESS;
    3564           0 : }
    3565             : 
    3566             : void
    3567             : fd_runtime_read_genesis( fd_exec_slot_ctx_t * slot_ctx,
    3568             :                          char const         * genesis_filepath,
    3569             :                          uchar                is_snapshot,
    3570             :                          fd_capture_ctx_t   * capture_ctx,
    3571           0 :                          fd_tpool_t         * tpool ) {
    3572           0 :   if( strlen( genesis_filepath ) == 0 ) { 
    3573           0 :     return;
    3574           0 :   }
    3575             : 
    3576           0 :   struct stat sbuf;
    3577           0 :   if( FD_UNLIKELY( stat( genesis_filepath, &sbuf) < 0 ) ) {
    3578           0 :     FD_LOG_ERR(( "cannot open %s : %s", genesis_filepath, strerror(errno) ));
    3579           0 :   }
    3580           0 :   int fd = open( genesis_filepath, O_RDONLY );
    3581           0 :   if( FD_UNLIKELY( fd < 0 ) ) {
    3582           0 :     FD_LOG_ERR(("cannot open %s : %s", genesis_filepath, strerror(errno)));
    3583           0 :   }
    3584             : 
    3585           0 :   fd_genesis_solana_t genesis_block = {0};
    3586           0 :   fd_genesis_solana_new( &genesis_block );
    3587           0 :   fd_hash_t           genesis_hash;
    3588             : 
    3589           0 :   fd_epoch_bank_t *   epoch_bank = fd_exec_epoch_ctx_epoch_bank( slot_ctx->epoch_ctx );
    3590             : 
    3591           0 :   FD_SCRATCH_SCOPE_BEGIN {
    3592           0 :     uchar * buf = fd_scratch_alloc( alignof(ulong), (ulong)sbuf.st_size );
    3593           0 :     ssize_t n   = read( fd, buf, (ulong)sbuf.st_size );
    3594           0 :     close( fd );
    3595             : 
    3596           0 :     fd_bincode_decode_ctx_t decode_ctx = {
    3597           0 :       .data    = buf,
    3598           0 :       .dataend = buf + n,
    3599           0 :       .valloc  = slot_ctx->valloc,
    3600           0 :     };
    3601           0 :     if( FD_UNLIKELY( fd_genesis_solana_decode( &genesis_block, &decode_ctx ) ) ) {
    3602           0 :       FD_LOG_ERR(("fd_genesis_solana_decode failed"));
    3603           0 :     }
    3604             : 
    3605             :     // The hash is generated from the raw data... don't mess with this..
    3606           0 :     fd_sha256_hash( buf, (ulong)n, genesis_hash.uc );
    3607             : 
    3608           0 :   } FD_SCRATCH_SCOPE_END;
    3609             : 
    3610           0 :   fd_memcpy( epoch_bank->genesis_hash.uc, genesis_hash.uc, sizeof(fd_hash_t) );
    3611           0 :   epoch_bank->cluster_type = genesis_block.cluster_type;
    3612             : 
    3613           0 :   fd_funk_start_write( slot_ctx->acc_mgr->funk );
    3614             : 
    3615           0 :   if( !is_snapshot ) {
    3616           0 :     fd_runtime_init_bank_from_genesis( slot_ctx, &genesis_block, &genesis_hash );
    3617             : 
    3618           0 :     fd_runtime_init_program( slot_ctx );
    3619             : 
    3620           0 :     FD_LOG_DEBUG(( "start genesis accounts - count: %lu", genesis_block.accounts_len ));
    3621             : 
    3622           0 :     for( ulong i=0; i<genesis_block.accounts_len; i++ ) {
    3623           0 :       fd_pubkey_account_pair_t * a = &genesis_block.accounts[i];
    3624             : 
    3625           0 :       FD_BORROWED_ACCOUNT_DECL( rec );
    3626             : 
    3627           0 :       int err = fd_acc_mgr_modify( slot_ctx->acc_mgr, 
    3628           0 :                                    slot_ctx->funk_txn, 
    3629           0 :                                    &a->key,
    3630           0 :                                    /* do_create */ 1, 
    3631           0 :                                    a->account.data_len, 
    3632           0 :                                    rec );
    3633             : 
    3634           0 :       if( FD_UNLIKELY( err ) ) {
    3635           0 :         FD_LOG_ERR(( "fd_acc_mgr_modify failed (%d)", err ));
    3636           0 :       }
    3637             : 
    3638           0 :       rec->meta->dlen            = a->account.data_len;
    3639           0 :       rec->meta->info.lamports   = a->account.lamports;
    3640           0 :       rec->meta->info.rent_epoch = a->account.rent_epoch;
    3641           0 :       rec->meta->info.executable = a->account.executable;
    3642           0 :       memcpy( rec->meta->info.owner, a->account.owner.key, sizeof(fd_hash_t));
    3643           0 :       if( a->account.data_len ) {
    3644           0 :         memcpy( rec->data, a->account.data, a->account.data_len );
    3645           0 :       }
    3646           0 :     }
    3647             : 
    3648           0 :     FD_LOG_DEBUG(( "end genesis accounts" ));
    3649             : 
    3650           0 :     FD_LOG_DEBUG(( "native instruction processors - count: %lu", genesis_block.native_instruction_processors_len ));
    3651             : 
    3652           0 :     for( ulong i=0UL; i < genesis_block.native_instruction_processors_len; i++ ) {
    3653           0 :       fd_string_pubkey_pair_t * a = &genesis_block.native_instruction_processors[i];
    3654           0 :       fd_write_builtin_bogus_account( slot_ctx, a->pubkey.uc, (const char *) a->string, a->string_len );
    3655           0 :     }
    3656             : 
    3657           0 :     fd_features_restore( slot_ctx );
    3658             : 
    3659           0 :     slot_ctx->slot_bank.slot = 0UL;
    3660             : 
    3661           0 :     int err = fd_runtime_process_genesis_block( slot_ctx, capture_ctx, tpool );
    3662           0 :     if( FD_UNLIKELY( err ) ) {
    3663           0 :       FD_LOG_ERR(( "Genesis slot 0 execute failed with error %d", err ));
    3664           0 :     }
    3665           0 :   }
    3666             : 
    3667           0 :   slot_ctx->slot_bank.stake_account_keys.stake_accounts_root = NULL;
    3668           0 :   slot_ctx->slot_bank.stake_account_keys.stake_accounts_pool = fd_stake_accounts_pair_t_map_alloc( slot_ctx->valloc, 100000UL );
    3669             : 
    3670           0 :   slot_ctx->slot_bank.vote_account_keys.vote_accounts_root   = NULL;
    3671           0 :   slot_ctx->slot_bank.vote_account_keys.vote_accounts_pool   = fd_vote_accounts_pair_t_map_alloc( slot_ctx->valloc, 100000UL );
    3672             : 
    3673           0 :   fd_funk_end_write( slot_ctx->acc_mgr->funk );
    3674             : 
    3675           0 :   fd_bincode_destroy_ctx_t ctx2 = { .valloc = slot_ctx->valloc };
    3676           0 :   fd_genesis_solana_destroy( &genesis_block, &ctx2 );
    3677           0 : }
    3678             : 
    3679             : /******************************************************************************/
    3680             : /* Offline Replay                                                             */
    3681             : /******************************************************************************/
    3682             : 
    3683             : /* As a note, currently offline and live replay of transactions has differences
    3684             :    with regards to how the execution environment is setup. These are helpers
    3685             :    used to emulate this behavior */
    3686             : 
    3687             : struct fd_poh_verification_info {
    3688             :   fd_microblock_info_t const *microblock_info;
    3689             :   fd_hash_t const *in_poh_hash;
    3690             :   int success;
    3691             : };
    3692             : typedef struct fd_poh_verification_info fd_poh_verification_info_t;
    3693             : 
    3694             : static void 
    3695             : fd_runtime_microblock_verify_info_collect( fd_microblock_info_t const * microblock_info,
    3696             :                                            fd_hash_t            const * in_poh_hash,
    3697           0 :                                            fd_poh_verification_info_t * poh_verification_info ) {
    3698           0 :   poh_verification_info->microblock_info = microblock_info;
    3699           0 :   poh_verification_info->in_poh_hash     = in_poh_hash;
    3700           0 :   poh_verification_info->success         = 0;
    3701           0 : }
    3702             : 
    3703             : static void 
    3704             : fd_runtime_microblock_batch_verify_info_collect( fd_microblock_batch_info_t const * microblock_batch_info,
    3705             :                                                  fd_hash_t                  const * in_poh_hash,
    3706           0 :                                                  fd_poh_verification_info_t *       poh_verification_info ) {
    3707           0 :   for( ulong i=0UL; i<microblock_batch_info->microblock_cnt; i++ ) {
    3708           0 :     fd_microblock_info_t const * microblock_info = &microblock_batch_info->microblock_infos[i];
    3709           0 :     fd_runtime_microblock_verify_info_collect( microblock_info, in_poh_hash, &poh_verification_info[i] );
    3710           0 :     in_poh_hash = (fd_hash_t const *)&microblock_info->microblock_hdr.hash;
    3711           0 :   }
    3712           0 : }
    3713             : 
    3714             : static void 
    3715             : fd_runtime_block_verify_info_collect( fd_block_info_t const *      block_info,
    3716             :                                       fd_hash_t       const *      in_poh_hash,
    3717             :                                       fd_poh_verification_info_t * poh_verification_info)
    3718           0 : {
    3719           0 :   for( ulong i=0UL; i<block_info->microblock_batch_cnt; i++ ) {
    3720           0 :     fd_microblock_batch_info_t const * microblock_batch_info = &block_info->microblock_batch_infos[i];
    3721             : 
    3722           0 :     fd_runtime_microblock_batch_verify_info_collect( microblock_batch_info, in_poh_hash, poh_verification_info );
    3723           0 :     in_poh_hash            = (fd_hash_t const *)poh_verification_info[microblock_batch_info->microblock_cnt - 1].microblock_info->microblock_hdr.hash;
    3724           0 :     poh_verification_info += microblock_batch_info->microblock_cnt;
    3725           0 :   }
    3726           0 : }
    3727             : 
    3728             : static void
    3729             : fd_runtime_poh_verify_wide_task( void * tpool,
    3730             :                                  ulong  t0 FD_PARAM_UNUSED, 
    3731             :                                  ulong  t1 FD_PARAM_UNUSED,
    3732             :                                  void * args FD_PARAM_UNUSED,
    3733             :                                  void * reduce FD_PARAM_UNUSED, 
    3734             :                                  ulong  stride FD_PARAM_UNUSED,
    3735             :                                  ulong  l0 FD_PARAM_UNUSED, 
    3736             :                                  ulong  l1 FD_PARAM_UNUSED,
    3737             :                                  ulong  m0, 
    3738             :                                  ulong  m1 FD_PARAM_UNUSED,
    3739             :                                  ulong  n0 FD_PARAM_UNUSED, 
    3740           0 :                                  ulong  n1 FD_PARAM_UNUSED ) {
    3741           0 :   fd_poh_verification_info_t * poh_info = (fd_poh_verification_info_t *)tpool + m0;
    3742             : 
    3743           0 :   fd_hash_t out_poh_hash = *poh_info->in_poh_hash;
    3744             : 
    3745           0 :   fd_microblock_info_t const *microblock_info = poh_info->microblock_info;
    3746           0 :   ulong hash_cnt = microblock_info->microblock_hdr.hash_cnt;
    3747           0 :   ulong txn_cnt = microblock_info->microblock_hdr.txn_cnt;
    3748             : 
    3749           0 :   if( !txn_cnt ) {
    3750           0 :     fd_poh_append( &out_poh_hash, hash_cnt );
    3751           0 :   } else {
    3752           0 :     if( hash_cnt ) {
    3753           0 :       fd_poh_append(&out_poh_hash, hash_cnt - 1);
    3754           0 :     }
    3755             : 
    3756           0 :     ulong                 leaf_cnt = microblock_info->signature_cnt;
    3757           0 :     uchar *               commit   = fd_alloca_check( FD_WBMTREE32_ALIGN, fd_wbmtree32_footprint(leaf_cnt));
    3758           0 :     fd_wbmtree32_leaf_t * leafs    = fd_alloca_check(alignof(fd_wbmtree32_leaf_t), sizeof(fd_wbmtree32_leaf_t) * leaf_cnt);
    3759           0 :     uchar *               mbuf     = fd_alloca_check( 1UL, leaf_cnt * (sizeof(fd_ed25519_sig_t) + 1) );
    3760             : 
    3761           0 :     fd_wbmtree32_t *      tree = fd_wbmtree32_init(commit, leaf_cnt);
    3762           0 :     fd_wbmtree32_leaf_t * l    = &leafs[0];
    3763             : 
    3764             :     /* Loop across transactions */
    3765           0 :     for( ulong txn_idx=0UL; txn_idx<txn_cnt; txn_idx++ ) {
    3766           0 :       fd_txn_p_t *          txn_p      = &microblock_info->txns[txn_idx];
    3767           0 :       fd_txn_t      const * txn        = (fd_txn_t const *) txn_p->_;
    3768           0 :       fd_rawtxn_b_t const   raw_txn[1] = {{ .raw = txn_p->payload, .txn_sz = (ushort)txn_p->payload_sz } };
    3769             : 
    3770             :       /* Loop across signatures */
    3771           0 :       fd_ed25519_sig_t const * sigs = (fd_ed25519_sig_t const *)((ulong)raw_txn->raw + (ulong)txn->signature_off);
    3772           0 :       for( ulong j=0UL; j<txn->signature_cnt; j++ ) {
    3773           0 :         l->data     = (uchar *)&sigs[j];
    3774           0 :         l->data_len = sizeof(fd_ed25519_sig_t);
    3775           0 :         l++;
    3776           0 :       }
    3777           0 :     }
    3778             : 
    3779           0 :     fd_wbmtree32_append( tree, leafs, leaf_cnt, mbuf );
    3780           0 :     uchar * root = fd_wbmtree32_fini( tree );
    3781           0 :     fd_poh_mixin( &out_poh_hash, root );
    3782           0 :   }
    3783             : 
    3784           0 :   if( FD_UNLIKELY( memcmp(microblock_info->microblock_hdr.hash, out_poh_hash.hash, sizeof(fd_hash_t)) ) ) {
    3785           0 :     FD_LOG_WARNING(( "poh mismatch (bank: %s, entry: %s)", FD_BASE58_ENC_32_ALLOCA( out_poh_hash.hash ), FD_BASE58_ENC_32_ALLOCA( microblock_info->microblock_hdr.hash ) ));
    3786           0 :     poh_info->success = -1;
    3787           0 :   }
    3788           0 : }
    3789             : 
    3790             : static int
    3791             : fd_runtime_poh_verify_tpool( fd_poh_verification_info_t * poh_verification_info,
    3792             :                              ulong                        poh_verification_info_cnt,
    3793           0 :                              fd_tpool_t *                 tpool ) {
    3794           0 :   fd_tpool_exec_all_rrobin( tpool, 
    3795           0 :                             0, 
    3796           0 :                             fd_tpool_worker_cnt( tpool ), 
    3797           0 :                             fd_runtime_poh_verify_wide_task, 
    3798           0 :                             poh_verification_info, 
    3799           0 :                             NULL, 
    3800           0 :                             NULL, 
    3801           0 :                             1, 
    3802           0 :                             0,
    3803           0 :                             poh_verification_info_cnt );
    3804             : 
    3805           0 :   for( ulong i=0UL; i<poh_verification_info_cnt; i++ ) {
    3806           0 :     if( poh_verification_info[i].success ) {
    3807           0 :       return -1;
    3808           0 :     }
    3809           0 :   }
    3810             : 
    3811           0 :   return 0;
    3812           0 : }
    3813             : 
    3814             : static int
    3815             : fd_runtime_block_verify_tpool( fd_block_info_t const * block_info,
    3816             :                                fd_hash_t       const * in_poh_hash,
    3817             :                                fd_hash_t *             out_poh_hash,
    3818             :                                fd_valloc_t             valloc,
    3819           0 :                                fd_tpool_t *            tpool ) {
    3820           0 :   long block_verify_time = -fd_log_wallclock();
    3821             : 
    3822           0 :   fd_hash_t                    tmp_in_poh_hash           = *in_poh_hash;
    3823           0 :   ulong                        poh_verification_info_cnt = block_info->microblock_cnt;
    3824           0 :   fd_poh_verification_info_t * poh_verification_info     = fd_valloc_malloc( valloc,
    3825           0 :                                                                              alignof(fd_poh_verification_info_t),
    3826           0 :                                                                              poh_verification_info_cnt * sizeof(fd_poh_verification_info_t));
    3827           0 :   fd_runtime_block_verify_info_collect( block_info, &tmp_in_poh_hash, poh_verification_info );
    3828           0 :   int result = fd_runtime_poh_verify_tpool( poh_verification_info, poh_verification_info_cnt, tpool );
    3829           0 :   fd_memcpy( out_poh_hash->hash, poh_verification_info[poh_verification_info_cnt - 1].microblock_info->microblock_hdr.hash, sizeof(fd_hash_t) );
    3830           0 :   fd_valloc_free( valloc, poh_verification_info );
    3831             : 
    3832           0 :   block_verify_time          += fd_log_wallclock();
    3833           0 :   double block_verify_time_ms = (double)block_verify_time * 1e-6;
    3834             : 
    3835           0 :   FD_LOG_INFO(( "verified block successfully - elapsed: %6.6f ms", block_verify_time_ms ));
    3836             : 
    3837           0 :   return result;
    3838           0 : }
    3839             : 
    3840             : static int
    3841             : fd_runtime_publish_old_txns( fd_exec_slot_ctx_t * slot_ctx,
    3842             :                              fd_capture_ctx_t *   capture_ctx,
    3843           0 :                              fd_tpool_t *         tpool ) {        
    3844             :   /* Publish any transaction older than 31 slots */
    3845           0 :   fd_funk_t *       funk       = slot_ctx->acc_mgr->funk;
    3846           0 :   fd_funk_txn_t *   txnmap     = fd_funk_txn_map( funk, fd_funk_wksp( funk ) );
    3847           0 :   fd_epoch_bank_t * epoch_bank = fd_exec_epoch_ctx_epoch_bank( slot_ctx->epoch_ctx );
    3848             : 
    3849           0 :   uint depth = 0;
    3850           0 :   for( fd_funk_txn_t * txn = slot_ctx->funk_txn; txn; txn = fd_funk_txn_parent(txn, txnmap) ) {
    3851           0 :     if( ++depth == (FD_RUNTIME_NUM_ROOT_BLOCKS - 1 ) ) {
    3852           0 :       FD_LOG_DEBUG(("publishing %s (slot %lu)", FD_BASE58_ENC_32_ALLOCA( &txn->xid ), txn->xid.ul[0]));
    3853             : 
    3854           0 :       if( slot_ctx->status_cache && !fd_txncache_get_is_constipated( slot_ctx->status_cache ) ) {
    3855           0 :         fd_txncache_register_root_slot( slot_ctx->status_cache, txn->xid.ul[0] );
    3856           0 :       } else if( slot_ctx->status_cache ) {
    3857           0 :         fd_txncache_register_constipated_slot( slot_ctx->status_cache, txn->xid.ul[0] );
    3858           0 :       }
    3859             : 
    3860           0 :       fd_funk_start_write( funk );
    3861           0 :       if( slot_ctx->epoch_ctx->constipate_root ) {
    3862           0 :         fd_funk_txn_t * parent = fd_funk_txn_parent( txn, txnmap );
    3863           0 :         if( parent != NULL ) {
    3864           0 :           slot_ctx->root_slot = txn->xid.ul[0];
    3865             : 
    3866           0 :           if( FD_UNLIKELY( fd_funk_txn_publish_into_parent( funk, txn, 1) != FD_FUNK_SUCCESS ) ) {
    3867           0 :             FD_LOG_ERR(( "Unable to publish into the parent transaction" ));
    3868           0 :           }
    3869           0 :         }
    3870           0 :       } else {
    3871           0 :         slot_ctx->root_slot = txn->xid.ul[0];
    3872             :         /* TODO: The epoch boundary check is not correct due to skipped slots. */
    3873           0 :         if( (!(slot_ctx->root_slot % slot_ctx->snapshot_freq) || (
    3874           0 :              !(slot_ctx->root_slot % slot_ctx->incremental_freq) && slot_ctx->last_snapshot_slot)) &&
    3875           0 :              !fd_runtime_is_epoch_boundary( epoch_bank, slot_ctx->root_slot, slot_ctx->root_slot - 1UL )) {
    3876             : 
    3877           0 :           slot_ctx->last_snapshot_slot         = slot_ctx->root_slot;
    3878           0 :           slot_ctx->epoch_ctx->constipate_root = 1;
    3879           0 :           fd_txncache_set_is_constipated( slot_ctx->status_cache, 1 );
    3880           0 :         }
    3881             : 
    3882           0 :         if( FD_UNLIKELY( !fd_funk_txn_publish( funk, txn, 1 ) ) ) {
    3883           0 :           FD_LOG_ERR(( "No transactions were published" ));
    3884           0 :         }
    3885           0 :       }
    3886             : 
    3887           0 :       if( txn->xid.ul[0] >= epoch_bank->eah_start_slot ) {
    3888           0 :         fd_accounts_hash( slot_ctx->acc_mgr->funk, &slot_ctx->slot_bank, slot_ctx->valloc, tpool, &slot_ctx->slot_bank.epoch_account_hash );
    3889           0 :         epoch_bank->eah_start_slot = ULONG_MAX;
    3890           0 :       }
    3891             : 
    3892           0 :       if( capture_ctx != NULL ) {
    3893           0 :         fd_runtime_checkpt( capture_ctx, slot_ctx, txn->xid.ul[0] );
    3894           0 :       }
    3895             : 
    3896           0 :       fd_funk_end_write( funk );
    3897             : 
    3898           0 :       break;
    3899           0 :     }
    3900           0 :   }
    3901             : 
    3902           0 :   return 0;
    3903           0 : }
    3904             : 
    3905             : static int
    3906             : fd_runtime_block_execute_tpool( fd_exec_slot_ctx_t *    slot_ctx,
    3907             :                                 fd_capture_ctx_t *      capture_ctx,
    3908             :                                 fd_block_info_t const * block_info,
    3909             :                                 fd_tpool_t *            tpool,
    3910             :                                 fd_spad_t * *           spads,
    3911           0 :                                 ulong                   spad_cnt ) {
    3912           0 :   FD_SCRATCH_SCOPE_BEGIN {
    3913           0 :     if ( capture_ctx != NULL && capture_ctx->capture ) {
    3914           0 :       fd_solcap_writer_set_slot( capture_ctx->capture, slot_ctx->slot_bank.slot );
    3915           0 :     }
    3916             : 
    3917           0 :     slot_ctx->tick_count = 0UL;
    3918             : 
    3919           0 :     long block_execute_time = -fd_log_wallclock();
    3920             : 
    3921           0 :     int res = fd_runtime_block_execute_prepare( slot_ctx );
    3922           0 :     if( res != FD_RUNTIME_EXECUTE_SUCCESS ) {
    3923           0 :       return res;
    3924           0 :     }
    3925             : 
    3926           0 :     ulong txn_cnt = block_info->txn_cnt;
    3927           0 :     fd_txn_p_t * txn_ptrs = fd_scratch_alloc( alignof(fd_txn_p_t), txn_cnt * sizeof(fd_txn_p_t) );
    3928             : 
    3929             :     /* This now collects the tick entries in a block. */
    3930           0 :     fd_runtime_block_collect_txns( block_info, txn_ptrs );
    3931             : 
    3932             :     /* TODO: Currently the tick height is manually updated for each executed 
    3933             :        slot as a hack to support snapshot loading. This code should be removed 
    3934             :        once correct tick calculation is implemented. */
    3935           0 :     slot_ctx->slot_bank.tick_height     += 64UL;
    3936           0 :     slot_ctx->slot_bank.max_tick_height += 64UL;
    3937             : 
    3938           0 :     res = fd_runtime_process_txns_in_waves_tpool( slot_ctx, capture_ctx, txn_ptrs, txn_cnt, tpool, spads, spad_cnt );
    3939           0 :     if( res != FD_RUNTIME_EXECUTE_SUCCESS ) {
    3940           0 :       return res;
    3941           0 :     }
    3942             : 
    3943           0 :     long block_finalize_time = -fd_log_wallclock();
    3944           0 :     res = fd_runtime_block_execute_finalize_tpool( slot_ctx, capture_ctx, block_info, tpool );
    3945           0 :     if( res != FD_RUNTIME_EXECUTE_SUCCESS ) {
    3946           0 :       return res;
    3947           0 :     }
    3948             : 
    3949           0 :     slot_ctx->slot_bank.transaction_count += txn_cnt;
    3950             : 
    3951           0 :     block_finalize_time += fd_log_wallclock();
    3952           0 :     double block_finalize_time_ms = (double)block_finalize_time * 1e-6;
    3953           0 :     FD_LOG_INFO(( "finalized block successfully - slot: %lu, elapsed: %6.6f ms", slot_ctx->slot_bank.slot, block_finalize_time_ms ));
    3954             : 
    3955           0 :     block_execute_time += fd_log_wallclock();
    3956           0 :     double block_execute_time_ms = (double)block_execute_time * 1e-6;
    3957             : 
    3958           0 :     FD_LOG_INFO(( "executed block successfully - slot: %lu, elapsed: %6.6f ms", slot_ctx->slot_bank.slot, block_execute_time_ms ));
    3959             : 
    3960           0 :     return FD_RUNTIME_EXECUTE_SUCCESS;
    3961           0 :   } FD_SCRATCH_SCOPE_END;
    3962           0 : }
    3963             : 
    3964             : int
    3965             : fd_runtime_block_eval_tpool( fd_exec_slot_ctx_t * slot_ctx,
    3966             :                              fd_capture_ctx_t *   capture_ctx,
    3967             :                              fd_tpool_t *         tpool,
    3968             :                              ulong                scheduler,
    3969             :                              ulong *              txn_cnt,
    3970             :                              fd_spad_t * *        spads,
    3971           0 :                              ulong                spad_cnt ) {
    3972           0 :   (void)scheduler;
    3973             : 
    3974           0 :   FD_SCRATCH_SCOPE_BEGIN {
    3975             : 
    3976           0 :   int err = fd_runtime_publish_old_txns( slot_ctx, capture_ctx, tpool );
    3977           0 :   if( err != 0 ) {
    3978           0 :     return err;
    3979           0 :   }
    3980             : 
    3981           0 :   fd_funk_t * funk = slot_ctx->acc_mgr->funk;
    3982             : 
    3983           0 :   ulong slot = slot_ctx->slot_bank.slot;
    3984             : 
    3985           0 :   long block_eval_time = -fd_log_wallclock();
    3986           0 :   fd_block_info_t block_info;
    3987           0 :   int ret = fd_runtime_block_prepare( slot_ctx->blockstore, slot_ctx->block, slot, fd_scratch_virtual(), &block_info );
    3988           0 :   *txn_cnt = block_info.txn_cnt;
    3989             : 
    3990             :   /* Use the blockhash as the funk xid */
    3991           0 :   fd_funk_txn_xid_t xid;
    3992             : 
    3993           0 :   fd_blockstore_start_read( slot_ctx->blockstore );
    3994           0 :   fd_hash_t const * hash = fd_blockstore_block_hash_query( slot_ctx->blockstore, slot );
    3995           0 :   if( FD_UNLIKELY( !hash ) ) {
    3996           0 :     ret = FD_RUNTIME_EXECUTE_GENERIC_ERR;
    3997           0 :     FD_LOG_WARNING(( "missing blockhash for %lu", slot ));
    3998           0 :   } else {
    3999           0 :     fd_memcpy( xid.uc, hash->uc, sizeof(fd_funk_txn_xid_t) );
    4000           0 :     xid.ul[0] = slot_ctx->slot_bank.slot;
    4001             :     /* push a new transaction on the stack */
    4002           0 :     fd_funk_start_write( funk );
    4003           0 :     slot_ctx->funk_txn = fd_funk_txn_prepare( funk, slot_ctx->funk_txn, &xid, 1 );
    4004           0 :     fd_funk_end_write( funk );
    4005           0 :   }
    4006           0 :   fd_blockstore_end_read( slot_ctx->blockstore );
    4007             : 
    4008           0 :   if( FD_RUNTIME_EXECUTE_SUCCESS == ret ) {
    4009           0 :     ret = fd_runtime_block_verify_tpool( &block_info, &slot_ctx->slot_bank.poh, &slot_ctx->slot_bank.poh, fd_scratch_virtual(), tpool );
    4010           0 :   }
    4011           0 :   if( FD_RUNTIME_EXECUTE_SUCCESS == ret ) {
    4012           0 :     ret = fd_runtime_block_execute_tpool( slot_ctx, capture_ctx, &block_info, tpool, spads, spad_cnt );
    4013           0 :   }
    4014             : 
    4015             :   // FIXME: better way of using starting slot
    4016           0 :   if( FD_UNLIKELY( FD_RUNTIME_EXECUTE_SUCCESS != ret ) ) {
    4017           0 :     FD_LOG_WARNING(( "execution failure, code %d", ret ));
    4018             :     /* Skip over slot next time */
    4019           0 :     slot_ctx->slot_bank.slot = slot+1UL;
    4020           0 :     return 0;
    4021           0 :   }
    4022             : 
    4023           0 :   block_eval_time          += fd_log_wallclock();
    4024           0 :   double block_eval_time_ms = (double)block_eval_time * 1e-6;
    4025           0 :   double tps                = (double) block_info.txn_cnt / ((double)block_eval_time * 1e-9);
    4026           0 :   FD_LOG_INFO(( "evaluated block successfully - slot: %lu, elapsed: %6.6f ms, signatures: %lu, txns: %lu, tps: %6.6f, bank_hash: %s, leader: %s",
    4027           0 :                 slot_ctx->slot_bank.slot,
    4028           0 :                 block_eval_time_ms,
    4029           0 :                 block_info.signature_cnt,
    4030           0 :                 block_info.txn_cnt,
    4031           0 :                 tps,
    4032           0 :                 FD_BASE58_ENC_32_ALLOCA( slot_ctx->slot_bank.banks_hash.hash ),
    4033           0 :                 FD_BASE58_ENC_32_ALLOCA( fd_epoch_leaders_get( fd_exec_epoch_ctx_leaders( slot_ctx->epoch_ctx ), slot_ctx->slot_bank.slot ) ) ));
    4034             : 
    4035           0 :   slot_ctx->slot_bank.transaction_count += block_info.txn_cnt;
    4036             : 
    4037           0 :   fd_funk_start_write( slot_ctx->acc_mgr->funk );
    4038           0 :   fd_runtime_save_slot_bank( slot_ctx );
    4039           0 :   fd_funk_end_write( slot_ctx->acc_mgr->funk );
    4040             : 
    4041           0 :   slot_ctx->slot_bank.prev_slot = slot;
    4042             :   // FIXME: this shouldn't be doing this, it doesn't work with forking. punting changing it though
    4043           0 :   slot_ctx->slot_bank.slot = slot+1UL;
    4044             : 
    4045           0 :   } FD_SCRATCH_SCOPE_END;
    4046             : 
    4047           0 :   return 0;
    4048           0 : }
    4049             : 
    4050             : /******************************************************************************/
    4051             : /* Debugging Tools                                                            */
    4052             : /******************************************************************************/
    4053             : 
    4054             : static void
    4055             : fd_runtime_copy_program_data_acc_to_pruned_funk( fd_funk_t *          pruned_funk,
    4056             :                                                  fd_funk_txn_t *      prune_txn,
    4057             :                                                  fd_exec_slot_ctx_t * slot_ctx,
    4058           0 :                                                  fd_pubkey_t const *  program_pubkey ) {
    4059           0 :   /* If account corresponds to bpf_upgradeable, copy over the programdata as well.
    4060           0 :      This is necessary for executing any bpf upgradeable program. */
    4061           0 : 
    4062           0 :   fd_account_meta_t const * program_acc = fd_acc_mgr_view_raw( slot_ctx->acc_mgr, NULL,
    4063           0 :                                                                program_pubkey, NULL, NULL, NULL );
    4064           0 : 
    4065           0 :   if( memcmp( program_acc->info.owner, fd_solana_bpf_loader_upgradeable_program_id.key, sizeof(fd_pubkey_t) ) ) {
    4066           0 :     return;
    4067           0 :   }
    4068           0 : 
    4069           0 :   fd_bincode_decode_ctx_t ctx = {
    4070           0 :     .data    = (uchar *)program_acc + program_acc->hlen,
    4071           0 :     .dataend = (char *) ctx.data + program_acc->dlen,
    4072           0 :     .valloc  = slot_ctx->valloc,
    4073           0 :   };
    4074           0 : 
    4075           0 :   fd_bpf_upgradeable_loader_state_t loader_state;
    4076           0 :   if ( fd_bpf_upgradeable_loader_state_decode( &loader_state, &ctx ) ) {
    4077           0 :     FD_LOG_ERR(( "fd_bpf_upgradeable_loader_state_decode failed" ));
    4078           0 :   }
    4079           0 : 
    4080           0 :   if( !fd_bpf_upgradeable_loader_state_is_program( &loader_state ) ) {
    4081           0 :     FD_LOG_ERR(( "fd_bpf_upgradeable_loader_state_is_program failed" ));
    4082           0 :   }
    4083           0 : 
    4084           0 :   fd_pubkey_t * programdata_pubkey = (fd_pubkey_t *)&loader_state.inner.program.programdata_address;
    4085           0 :   fd_funk_rec_key_t programdata_reckey = fd_acc_funk_key( programdata_pubkey );
    4086           0 : 
    4087           0 :   /* Copy over programdata record */
    4088           0 :   fd_funk_rec_t * new_rec_pd = fd_funk_rec_write_prepare( pruned_funk, prune_txn, &programdata_reckey,
    4089           0 :                                                           0, 1, NULL, NULL );
    4090           0 :   FD_TEST(( !!new_rec_pd ));
    4091           0 : }
    4092             : 
    4093             : static void FD_FN_UNUSED
    4094             : fd_runtime_copy_accounts_to_pruned_funk( fd_funk_t *          pruned_funk,
    4095             :                                          fd_funk_txn_t *      prune_txn,
    4096             :                                          fd_exec_slot_ctx_t * slot_ctx,
    4097           0 :                                          fd_exec_txn_ctx_t *  txn_ctx ) {
    4098           0 :   /* This function is only responsible for copying over the account ids that are
    4099           0 :      modified. The account data is copied over after execution is complete. */
    4100           0 : 
    4101           0 :   /* Copy over ALUTs */
    4102           0 :   fd_txn_acct_addr_lut_t * addr_luts = fd_txn_get_address_tables( (fd_txn_t *) txn_ctx->txn_descriptor );
    4103           0 :   for( ulong i = 0; i < txn_ctx->txn_descriptor->addr_table_lookup_cnt; i++ ) {
    4104           0 :     fd_txn_acct_addr_lut_t * addr_lut = &addr_luts[i];
    4105           0 :     fd_pubkey_t const * addr_lut_acc = (fd_pubkey_t *)((uchar *)txn_ctx->_txn_raw->raw + addr_lut->addr_off);
    4106           0 :     if ( addr_lut_acc ) {
    4107           0 :       fd_funk_rec_key_t acc_lut_rec_key = fd_acc_funk_key( addr_lut_acc );
    4108           0 :       fd_funk_rec_write_prepare( pruned_funk, prune_txn, &acc_lut_rec_key, 0, 1, NULL, NULL );
    4109           0 :     }
    4110           0 :   }
    4111           0 : 
    4112           0 :   /* Get program id from top level instructions and copy over programdata */
    4113           0 :   fd_instr_info_t instrs[txn_ctx->txn_descriptor->instr_cnt];
    4114           0 :   for ( ushort i = 0; i < txn_ctx->txn_descriptor->instr_cnt; i++ ) {
    4115           0 :     fd_txn_instr_t const * txn_instr = &txn_ctx->txn_descriptor->instr[i];
    4116           0 :     fd_convert_txn_instr_to_instr( txn_ctx, txn_instr, txn_ctx->borrowed_accounts, &instrs[i] );
    4117           0 :     fd_pubkey_t program_pubkey = instrs[i].program_id_pubkey;
    4118           0 :     fd_funk_rec_key_t program_rec_key = fd_acc_funk_key( &program_pubkey );
    4119           0 :     fd_funk_rec_t *new_rec = fd_funk_rec_write_prepare(pruned_funk, prune_txn, &program_rec_key, 0, 1, NULL, NULL);
    4120           0 :     if ( !new_rec ) {
    4121           0 :       FD_LOG_NOTICE(("fd_funk_rec_write_prepare failed %s", FD_BASE58_ENC_32_ALLOCA( &program_pubkey ) ));
    4122           0 :       continue;
    4123           0 :     }
    4124           0 : 
    4125           0 :     /* If account corresponds to bpf_upgradeable, copy over the programdata as well */
    4126           0 :     fd_runtime_copy_program_data_acc_to_pruned_funk( pruned_funk, prune_txn, slot_ctx, &program_pubkey );
    4127           0 :   }
    4128           0 : 
    4129           0 :   /* Write out all accounts touched during the transaction, copy over all program data accounts for
    4130           0 :      any BPF upgradeable accounts in case they are a CPI's program account. */
    4131           0 :   for( ulong i = 0; i < txn_ctx->accounts_cnt; i++ ) {
    4132           0 :     fd_pubkey_t * acc_pubkey = (fd_pubkey_t *)&txn_ctx->accounts[i].key;
    4133           0 :     fd_funk_rec_key_t rec_key = fd_acc_funk_key( acc_pubkey );
    4134           0 :     fd_funk_rec_t * rec = fd_funk_rec_write_prepare( pruned_funk, prune_txn, &rec_key, 0, 1, NULL, NULL );
    4135           0 :     FD_TEST(( !!rec ));
    4136           0 :     fd_runtime_copy_program_data_acc_to_pruned_funk( pruned_funk, prune_txn, slot_ctx, acc_pubkey );
    4137           0 :   }
    4138           0 : }
    4139             : 
    4140             : void
    4141             : fd_runtime_checkpt( fd_capture_ctx_t *   capture_ctx,
    4142             :                     fd_exec_slot_ctx_t * slot_ctx,
    4143           0 :                     ulong                slot ) {
    4144           0 :   int is_checkpt_freq = capture_ctx != NULL && slot % capture_ctx->checkpt_freq == 0;
    4145           0 :   int is_abort_slot   = slot == ULONG_MAX;
    4146           0 :   if( !is_checkpt_freq && !is_abort_slot ) {
    4147           0 :     return;
    4148           0 :   }
    4149             : 
    4150           0 :   if( capture_ctx->checkpt_path != NULL ) {
    4151           0 :     if( !is_abort_slot ) {
    4152           0 :       FD_LOG_NOTICE(( "checkpointing at slot=%lu to file=%s", slot, capture_ctx->checkpt_path ));
    4153           0 :       fd_funk_end_write( slot_ctx->acc_mgr->funk );
    4154           0 :     } else {
    4155           0 :       FD_LOG_NOTICE(( "checkpointing after mismatch to file=%s", capture_ctx->checkpt_path ));
    4156           0 :     }
    4157             : 
    4158           0 :     unlink( capture_ctx->checkpt_path );
    4159           0 :     int err = fd_wksp_checkpt( fd_funk_wksp( slot_ctx->acc_mgr->funk ), capture_ctx->checkpt_path, 0666, 0, NULL );
    4160           0 :     if ( err ) {
    4161           0 :       FD_LOG_ERR(( "backup failed: error %d", err ));
    4162           0 :     }
    4163             : 
    4164           0 :     if( !is_abort_slot ) {
    4165           0 :       fd_funk_start_write( slot_ctx->acc_mgr->funk );
    4166           0 :     }
    4167           0 :   }
    4168             : 
    4169           0 : }
    4170             : 
    4171             : void
    4172           0 : fd_runtime_collect_rent_accounts_prune( ulong slot, fd_exec_slot_ctx_t * slot_ctx, fd_capture_ctx_t * capture_ctx ) {
    4173             :   /* TODO: Test if this works across epoch boundaries */
    4174             : 
    4175             :   /* As a note, the number of partitions are determined before execution begins.
    4176             :      The rent accounts for each slot are added to the pruned funk. The data in
    4177             :      the accounts is populated after execution is completed. */
    4178           0 :   fd_epoch_bank_t const * epoch_bank = fd_exec_epoch_ctx_epoch_bank_const( slot_ctx->epoch_ctx );
    4179           0 :   fd_epoch_schedule_t const * schedule = &epoch_bank->epoch_schedule;
    4180             : 
    4181           0 :   ulong off;
    4182             : 
    4183           0 :   fd_slot_to_epoch( schedule, slot, &off );
    4184             : 
    4185           0 :   fd_funk_t * funk = slot_ctx->acc_mgr->funk;
    4186           0 :   fd_wksp_t * wksp = fd_funk_wksp( funk );
    4187           0 :   fd_funk_partvec_t * partvec = fd_funk_get_partvec( funk, wksp );
    4188           0 :   fd_funk_rec_t * rec_map = fd_funk_rec_map( funk, wksp );
    4189             : 
    4190           0 :   for (fd_funk_rec_t const *rec_ro = fd_funk_part_head(partvec, (uint)off, rec_map);
    4191           0 :       rec_ro != NULL;
    4192           0 :       rec_ro = fd_funk_part_next(rec_ro, rec_map)) {
    4193             : 
    4194           0 :     fd_pubkey_t const * key = fd_type_pun_const(rec_ro->pair.key[0].uc);
    4195           0 :     fd_funk_rec_key_t rec_key = fd_acc_funk_key( key );
    4196             : 
    4197           0 :     fd_funk_txn_xid_t prune_xid;
    4198           0 :     fd_memset( &prune_xid, 0x42, sizeof(fd_funk_txn_xid_t));
    4199             : 
    4200           0 :     fd_funk_txn_t * txn_map = fd_funk_txn_map( capture_ctx->pruned_funk, fd_funk_wksp( capture_ctx->pruned_funk ) );
    4201           0 :     fd_funk_txn_t * prune_txn = fd_funk_txn_query( &prune_xid, txn_map );
    4202             : 
    4203           0 :     fd_funk_rec_t * rec = fd_funk_rec_write_prepare( capture_ctx->pruned_funk, prune_txn, &rec_key, 0, 1, NULL, NULL );
    4204           0 :     FD_TEST(( !!rec ));
    4205             : 
    4206           0 :     int res = fd_funk_part_set( capture_ctx->pruned_funk, rec, (uint)off );
    4207           0 :     FD_TEST(( res == 0 ));
    4208           0 :   }
    4209           0 : }
    4210             : 
    4211             : // TODO: add tracking account_state hashes so that we can verify our
    4212             : // banks hash... this has interesting threading implications since we
    4213             : // could execute the cryptography in another thread for tracking this
    4214             : // but we don't actually have anything to compare it to until we hit
    4215             : // another snapshot...  Probably we should just store the results into
    4216             : // the slot_ctx state (a slot/hash map)?
    4217             : //
    4218             : // What slots exactly do cache'd account_updates go into?  how are
    4219             : // they hashed (which slot?)?

Generated by: LCOV version 1.14