LCOV - code coverage report
Current view: top level - flamenco/runtime - fd_runtime.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 2264 0.0 %
Date: 2025-07-01 05:00:49 Functions: 0 66 0.0 %

          Line data    Source code
       1             : #include "fd_runtime.h"
       2             : #include "context/fd_capture_ctx.h"
       3             : #include "fd_acc_mgr.h"
       4             : #include "fd_bank.h"
       5             : #include "fd_runtime_err.h"
       6             : #include "fd_runtime_init.h"
       7             : #include "fd_pubkey_utils.h"
       8             : 
       9             : #include "fd_executor.h"
      10             : #include "fd_cost_tracker.h"
      11             : #include "fd_runtime_public.h"
      12             : #include "fd_txncache.h"
      13             : #include "sysvar/fd_sysvar_clock.h"
      14             : #include "sysvar/fd_sysvar_epoch_schedule.h"
      15             : #include "sysvar/fd_sysvar_recent_hashes.h"
      16             : #include "sysvar/fd_sysvar_stake_history.h"
      17             : #include "sysvar/fd_sysvar.h"
      18             : #include "../../ballet/base58/fd_base58.h"
      19             : #include "../../ballet/txn/fd_txn.h"
      20             : #include "../../ballet/bmtree/fd_bmtree.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             : #include "program/fd_address_lookup_table_program.h"
      38             : 
      39             : #include "sysvar/fd_sysvar_clock.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 "tests/fd_dump_pb.h"
      47             : 
      48             : #include "../../ballet/nanopb/pb_decode.h"
      49             : #include "../../ballet/nanopb/pb_encode.h"
      50             : #include "../types/fd_solana_block.pb.h"
      51             : 
      52             : #include "fd_system_ids.h"
      53             : #include "../vm/fd_vm.h"
      54             : #include "fd_blockstore.h"
      55             : #include "../../disco/pack/fd_pack.h"
      56             : #include "../fd_rwlock.h"
      57             : 
      58             : #include <stdio.h>
      59             : #include <ctype.h>
      60             : #include <unistd.h>
      61             : #include <sys/stat.h>
      62             : #include <sys/types.h>
      63             : #include <errno.h>
      64             : #include <fcntl.h>
      65             : 
      66             : /******************************************************************************/
      67             : /* Public Runtime Helpers                                                     */
      68             : /******************************************************************************/
      69             : 
      70             : /*
      71             :    https://github.com/anza-xyz/agave/blob/v2.1.1/runtime/src/bank.rs#L1254-L1258
      72             :    https://github.com/anza-xyz/agave/blob/v2.1.1/runtime/src/bank.rs#L1749
      73             :  */
      74             : int
      75             : fd_runtime_compute_max_tick_height( ulong   ticks_per_slot,
      76             :                                     ulong   slot,
      77           0 :                                     ulong * out_max_tick_height /* out */ ) {
      78           0 :   ulong max_tick_height = 0UL;
      79           0 :   if( FD_LIKELY( ticks_per_slot > 0UL ) ) {
      80           0 :     ulong next_slot = fd_ulong_sat_add( slot, 1UL );
      81           0 :     if( FD_UNLIKELY( next_slot == slot ) ) {
      82           0 :       FD_LOG_WARNING(( "max tick height addition overflowed slot %lu ticks_per_slot %lu", slot, ticks_per_slot ));
      83           0 :       return -1;
      84           0 :     }
      85           0 :     if( FD_UNLIKELY( ULONG_MAX / ticks_per_slot < next_slot ) ) {
      86           0 :       FD_LOG_WARNING(( "max tick height multiplication overflowed slot %lu ticks_per_slot %lu", slot, ticks_per_slot ));
      87           0 :       return -1;
      88           0 :     }
      89           0 :     max_tick_height = fd_ulong_sat_mul( next_slot, ticks_per_slot );
      90           0 :   }
      91           0 :   *out_max_tick_height = max_tick_height;
      92           0 :   return FD_RUNTIME_EXECUTE_SUCCESS;
      93           0 : }
      94             : 
      95             : void
      96             : fd_runtime_update_slots_per_epoch( fd_bank_t * bank,
      97           0 :                                    ulong       slots_per_epoch ) {
      98           0 :   if( FD_LIKELY( slots_per_epoch == fd_bank_slots_per_epoch_get( bank ) ) ) {
      99           0 :     return;
     100           0 :   }
     101             : 
     102           0 :   fd_bank_slots_per_epoch_set( bank, slots_per_epoch );
     103             : 
     104           0 :   fd_bank_part_width_set( bank, fd_rent_partition_width( slots_per_epoch ) );
     105           0 : }
     106             : 
     107             : void
     108             : fd_runtime_update_leaders( fd_bank_t * bank,
     109             :                            ulong       slot,
     110           0 :                            fd_spad_t * runtime_spad ) {
     111             : 
     112           0 :   FD_SPAD_FRAME_BEGIN( runtime_spad ) {
     113             : 
     114           0 :   fd_epoch_schedule_t const * epoch_schedule = fd_bank_epoch_schedule_query( bank );
     115             : 
     116           0 :   FD_LOG_INFO(( "schedule->slots_per_epoch = %lu", epoch_schedule->slots_per_epoch ));
     117           0 :   FD_LOG_INFO(( "schedule->leader_schedule_slot_offset = %lu", epoch_schedule->leader_schedule_slot_offset ));
     118           0 :   FD_LOG_INFO(( "schedule->warmup = %d", epoch_schedule->warmup ));
     119           0 :   FD_LOG_INFO(( "schedule->first_normal_epoch = %lu", epoch_schedule->first_normal_epoch ));
     120           0 :   FD_LOG_INFO(( "schedule->first_normal_slot = %lu", epoch_schedule->first_normal_slot ));
     121             : 
     122           0 :   fd_vote_accounts_global_t const *          epoch_vaccs   = fd_bank_epoch_stakes_locking_query( bank );
     123           0 :   fd_vote_accounts_pair_global_t_mapnode_t * vote_acc_pool = fd_vote_accounts_vote_accounts_pool_join( epoch_vaccs );
     124           0 :   fd_vote_accounts_pair_global_t_mapnode_t * vote_acc_root = fd_vote_accounts_vote_accounts_root_join( epoch_vaccs );
     125             : 
     126           0 :   ulong epoch    = fd_slot_to_epoch( epoch_schedule, slot, NULL );
     127           0 :   ulong slot0    = fd_epoch_slot0( epoch_schedule, epoch );
     128           0 :   ulong slot_cnt = fd_epoch_slot_cnt( epoch_schedule, epoch );
     129             : 
     130           0 :   fd_runtime_update_slots_per_epoch( bank, fd_epoch_slot_cnt( epoch_schedule, epoch ) );
     131             : 
     132           0 :   ulong vote_acc_cnt  = fd_vote_accounts_pair_global_t_map_size( vote_acc_pool, vote_acc_root );
     133           0 :   fd_bank_epoch_stakes_end_locking_query( bank );
     134             : 
     135           0 :   fd_stake_weight_t * epoch_weights = fd_spad_alloc_check( runtime_spad, alignof(fd_stake_weight_t), vote_acc_cnt * sizeof(fd_stake_weight_t) );
     136             : 
     137           0 :   ulong stake_weight_cnt = fd_stake_weights_by_node( epoch_vaccs, epoch_weights, runtime_spad );
     138             : 
     139           0 :   if( FD_UNLIKELY( stake_weight_cnt == ULONG_MAX ) ) {
     140           0 :     FD_LOG_ERR(( "fd_stake_weights_by_node() failed" ));
     141           0 :   }
     142             : 
     143             :   /* Derive leader schedule */
     144             : 
     145           0 :   FD_LOG_INFO(( "stake_weight_cnt=%lu slot_cnt=%lu", stake_weight_cnt, slot_cnt ));
     146           0 :   ulong epoch_leaders_footprint = fd_epoch_leaders_footprint( stake_weight_cnt, slot_cnt );
     147           0 :   FD_LOG_INFO(( "epoch_leaders_footprint=%lu", epoch_leaders_footprint ));
     148           0 :   if( FD_LIKELY( epoch_leaders_footprint ) ) {
     149           0 :     if( FD_UNLIKELY( stake_weight_cnt>MAX_PUB_CNT ) ) {
     150           0 :       FD_LOG_ERR(( "Stake weight count exceeded max" ));
     151           0 :     }
     152           0 :     if( FD_UNLIKELY( slot_cnt>MAX_SLOTS_PER_EPOCH ) ) {
     153           0 :       FD_LOG_ERR(( "Slot count exceeeded max" ));
     154           0 :     }
     155             : 
     156           0 :     void * epoch_leaders_mem = fd_bank_epoch_leaders_locking_modify( bank );
     157           0 :     fd_epoch_leaders_t * leaders = fd_epoch_leaders_join( fd_epoch_leaders_new( epoch_leaders_mem,
     158           0 :                                                                                            epoch,
     159           0 :                                                                                            slot0,
     160           0 :                                                                                            slot_cnt,
     161           0 :                                                                                            stake_weight_cnt,
     162           0 :                                                                                            epoch_weights,
     163           0 :                                                                                            0UL ) );
     164           0 :     fd_bank_epoch_leaders_end_locking_modify( bank );
     165           0 :     if( FD_UNLIKELY( !leaders ) ) {
     166           0 :       FD_LOG_ERR(( "Unable to init and join fd_epoch_leaders" ));
     167           0 :     }
     168           0 :   }
     169             : 
     170           0 :   } FD_SPAD_FRAME_END;
     171           0 : }
     172             : 
     173             : /******************************************************************************/
     174             : /* Various Private Runtime Helpers                                            */
     175             : /******************************************************************************/
     176             : 
     177             : /* fee to be deposited should be > 0
     178             :    Returns 0 if validation succeeds
     179             :    Returns the amount to burn(==fee) on failure */
     180             : static ulong
     181             : fd_runtime_validate_fee_collector( fd_bank_t *              bank,
     182             :                                    fd_txn_account_t const * collector,
     183           0 :                                    ulong                    fee ) {
     184           0 :   if( FD_UNLIKELY( fee<=0UL ) ) {
     185           0 :     FD_LOG_ERR(( "expected fee(%lu) to be >0UL", fee ));
     186           0 :   }
     187             : 
     188           0 :   if( FD_UNLIKELY( memcmp( collector->vt->get_owner( collector ), fd_solana_system_program_id.key, sizeof(fd_pubkey_t) ) ) ) {
     189           0 :     FD_BASE58_ENCODE_32_BYTES( collector->pubkey->key, _out_key );
     190           0 :     FD_LOG_WARNING(( "cannot pay a non-system-program owned account (%s)", _out_key ));
     191           0 :     return fee;
     192           0 :   }
     193             : 
     194             :   /* https://github.com/anza-xyz/agave/blob/v1.18.23/runtime/src/bank/fee_distribution.rs#L111
     195             :      https://github.com/anza-xyz/agave/blob/v1.18.23/runtime/src/accounts/account_rent_state.rs#L39
     196             :      In agave's fee deposit code, rent state transition check logic is as follows:
     197             :      The transition is NOT allowed iff
     198             :      === BEGIN
     199             :      the post deposit account is rent paying AND the pre deposit account is not rent paying
     200             :      OR
     201             :      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)
     202             :      === END
     203             :      post_data_size == pre_data_size is always true during fee deposit.
     204             :      However, post_lamports > pre_lamports because we are paying a >0 amount.
     205             :      So, the above reduces down to
     206             :      === BEGIN
     207             :      the post deposit account is rent paying AND the pre deposit account is not rent paying
     208             :      OR
     209             :      the post deposit account is rent paying AND the pre deposit account is rent paying AND TRUE
     210             :      === END
     211             :      This is equivalent to checking that the post deposit account is rent paying.
     212             :      An account is rent paying if the post deposit balance is >0 AND it's not rent exempt.
     213             :      We already know that the post deposit balance is >0 because we are paying a >0 amount.
     214             :      So TLDR we just check if the account is rent exempt.
     215             :    */
     216           0 :   fd_rent_t const * rent = fd_bank_rent_query( bank );
     217           0 :   ulong minbal = fd_rent_exempt_minimum_balance( rent, collector->vt->get_data_len( collector ) );
     218           0 :   if( FD_UNLIKELY( collector->vt->get_lamports( collector ) + fee < minbal ) ) {
     219           0 :     FD_BASE58_ENCODE_32_BYTES( collector->pubkey->key, _out_key );
     220           0 :     FD_LOG_WARNING(("cannot pay a rent paying account (%s)", _out_key ));
     221           0 :     return fee;
     222           0 :   }
     223             : 
     224           0 :   return 0UL;
     225           0 : }
     226             : 
     227             : static int
     228             : fd_runtime_run_incinerator( fd_bank_t *     bank,
     229             :                             fd_funk_t *     funk,
     230           0 :                             fd_funk_txn_t * funk_txn ) {
     231           0 :   FD_TXN_ACCOUNT_DECL( rec );
     232             : 
     233           0 :   int err = fd_txn_account_init_from_funk_mutable( rec,
     234           0 :                                                    &fd_sysvar_incinerator_id,
     235           0 :                                                    funk,
     236           0 :                                                    funk_txn,
     237           0 :                                                    0,
     238           0 :                                                    0UL );
     239           0 :   if( FD_UNLIKELY( err!=FD_ACC_MGR_SUCCESS ) ) {
     240             :     // TODO: not really an error! This is fine!
     241           0 :     return -1;
     242           0 :   }
     243             : 
     244           0 :   ulong new_capitalization = fd_ulong_sat_sub( fd_bank_capitalization_get( bank ), rec->vt->get_lamports( rec ) );
     245           0 :   fd_bank_capitalization_set( bank, new_capitalization );
     246             : 
     247           0 :   rec->vt->set_lamports( rec, 0UL );
     248           0 :   fd_txn_account_mutable_fini( rec, funk, funk_txn );
     249             : 
     250           0 :   return 0;
     251           0 : }
     252             : 
     253             : static void
     254           0 : fd_runtime_freeze( fd_exec_slot_ctx_t * slot_ctx, fd_spad_t * runtime_spad ) {
     255             : 
     256           0 :   fd_sysvar_recent_hashes_update( slot_ctx, runtime_spad );
     257             : 
     258           0 :   ulong fees = 0UL;
     259           0 :   ulong burn = 0UL;
     260             : 
     261           0 :   ulong execution_fees = fd_bank_execution_fees_get( slot_ctx->bank );
     262           0 :   ulong priority_fees  = fd_bank_priority_fees_get( slot_ctx->bank );
     263             : 
     264           0 :   if( FD_FEATURE_ACTIVE_BANK( slot_ctx->bank, reward_full_priority_fee ) ) {
     265           0 :     ulong half_fee = execution_fees / 2;
     266           0 :     fees = fd_ulong_sat_add( priority_fees, execution_fees - half_fee );
     267           0 :     burn = half_fee;
     268           0 :   } else {
     269           0 :     ulong total_fees = fd_ulong_sat_add( execution_fees, priority_fees );
     270           0 :     ulong half_fee = total_fees / 2;
     271           0 :     fees = total_fees - half_fee;
     272           0 :     burn = half_fee;
     273           0 :   }
     274           0 :   if( FD_LIKELY( fees ) ) {
     275             :     // Look at collect_fees... I think this was where I saw the fee payout..
     276           0 :     FD_TXN_ACCOUNT_DECL( rec );
     277             : 
     278           0 :     do {
     279             :       /* do_create=1 because we might wanna pay fees to a leader
     280             :          account that we've purged due to 0 balance. */
     281             : 
     282           0 :       fd_epoch_leaders_t const * leaders = fd_bank_epoch_leaders_locking_query( slot_ctx->bank );
     283           0 :       if( FD_UNLIKELY( !leaders ) ) {
     284           0 :         FD_LOG_WARNING(( "fd_runtime_freeze: leaders not found" ));
     285           0 :         break;
     286           0 :       }
     287             : 
     288           0 :       fd_pubkey_t const * leader = fd_epoch_leaders_get( leaders, slot_ctx->slot );
     289           0 :       if( FD_UNLIKELY( !leader ) ) {
     290           0 :         FD_LOG_WARNING(( "fd_runtime_freeze: leader not found" ));
     291           0 :         break;
     292           0 :       }
     293             : 
     294           0 :       int err = fd_txn_account_init_from_funk_mutable( rec, leader, slot_ctx->funk, slot_ctx->funk_txn, 1, 0UL );
     295           0 :       if( FD_UNLIKELY( err ) ) {
     296           0 :         FD_LOG_WARNING(("fd_runtime_freeze: fd_txn_account_init_from_funk_mutable for leader (%s) failed (%d)", FD_BASE58_ENC_32_ALLOCA( leader ), err));
     297           0 :         burn = fd_ulong_sat_add( burn, fees );
     298           0 :         break;
     299           0 :       }
     300             : 
     301           0 :       fd_bank_epoch_leaders_end_locking_query( slot_ctx->bank );
     302             : 
     303           0 :       if ( FD_LIKELY( FD_FEATURE_ACTIVE_BANK( slot_ctx->bank, validate_fee_collector_account ) ) ) {
     304           0 :         ulong _burn;
     305           0 :         if( FD_UNLIKELY( _burn=fd_runtime_validate_fee_collector( slot_ctx->bank, rec, fees ) ) ) {
     306           0 :           if( FD_UNLIKELY( _burn!=fees ) ) {
     307           0 :             FD_LOG_ERR(( "expected _burn(%lu)==fees(%lu)", _burn, fees ));
     308           0 :           }
     309           0 :           burn = fd_ulong_sat_add( burn, fees );
     310           0 :           FD_LOG_WARNING(("fd_runtime_freeze: burned %lu", fees ));
     311           0 :           break;
     312           0 :         }
     313           0 :       }
     314             : 
     315             :       /* TODO: is it ok to not check the overflow error here? */
     316           0 :       rec->vt->checked_add_lamports( rec, fees );
     317           0 :       rec->vt->set_slot( rec, slot_ctx->slot );
     318             : 
     319           0 :       fd_txn_account_mutable_fini( rec, slot_ctx->funk, slot_ctx->funk_txn );
     320             : 
     321           0 :     } while(0);
     322             : 
     323           0 :     ulong old = fd_bank_capitalization_get( slot_ctx->bank );
     324           0 :     fd_bank_capitalization_set( slot_ctx->bank, fd_ulong_sat_sub( old, burn ) );
     325           0 :     FD_LOG_DEBUG(( "fd_runtime_freeze: burn %lu, capitalization %lu->%lu ", burn, old, fd_bank_capitalization_get( slot_ctx->bank ) ));
     326             : 
     327           0 :     fd_bank_execution_fees_set( slot_ctx->bank, 0UL );
     328             : 
     329           0 :     fd_bank_priority_fees_set( slot_ctx->bank, 0UL );
     330           0 :   }
     331             : 
     332           0 :   fd_runtime_run_incinerator( slot_ctx->bank, slot_ctx->funk, slot_ctx->funk_txn );
     333             : 
     334           0 : }
     335             : 
     336           0 : #define FD_RENT_EXEMPT (-1L)
     337             : 
     338             : static long
     339             : fd_runtime_get_rent_due( fd_epoch_schedule_t const * schedule,
     340             :                          fd_rent_t const *           rent,
     341             :                          double                      slots_per_year,
     342             :                          fd_txn_account_t *          acc,
     343           0 :                          ulong                       epoch ) {
     344             :   /* Nothing due if account is rent-exempt
     345             :      https://github.com/anza-xyz/agave/blob/v2.0.10/sdk/src/rent_collector.rs#L90 */
     346           0 :   ulong min_balance = fd_rent_exempt_minimum_balance( rent, acc->vt->get_data_len( acc ) );
     347           0 :   if( acc->vt->get_lamports( acc )>=min_balance ) {
     348           0 :     return FD_RENT_EXEMPT;
     349           0 :   }
     350             : 
     351             :   /* Count the number of slots that have passed since last collection. This
     352             :      inlines the agave function get_slots_in_peohc
     353             :      https://github.com/anza-xyz/agave/blob/v2.0.10/sdk/src/rent_collector.rs#L93-L98 */
     354           0 :   ulong slots_elapsed = 0UL;
     355           0 :   if( FD_UNLIKELY( acc->vt->get_rent_epoch( acc )<schedule->first_normal_epoch ) ) {
     356             :     /* Count the slots before the first normal epoch separately */
     357           0 :     for( ulong i=acc->vt->get_rent_epoch( acc ); i<schedule->first_normal_epoch && i<=epoch; i++ ) {
     358           0 :       slots_elapsed += fd_epoch_slot_cnt( schedule, i+1UL );
     359           0 :     }
     360           0 :     slots_elapsed += fd_ulong_sat_sub( epoch+1UL, schedule->first_normal_epoch ) * schedule->slots_per_epoch;
     361           0 :   }
     362             :   // slots_elapsed should remain 0 if rent_epoch is greater than epoch
     363           0 :   else if( acc->vt->get_rent_epoch( acc )<=epoch ) {
     364           0 :     slots_elapsed = (epoch - acc->vt->get_rent_epoch( acc ) + 1UL) * schedule->slots_per_epoch;
     365           0 :   }
     366             :   /* Consensus-critical use of doubles :( */
     367             : 
     368           0 :   double years_elapsed;
     369           0 :   if( FD_LIKELY( slots_per_year!=0.0 ) ) {
     370           0 :     years_elapsed = (double)slots_elapsed / slots_per_year;
     371           0 :   } else {
     372           0 :     years_elapsed = 0.0;
     373           0 :   }
     374             : 
     375           0 :   ulong lamports_per_year = rent->lamports_per_uint8_year * (acc->vt->get_data_len( acc ) + 128UL);
     376             :   /* https://github.com/anza-xyz/agave/blob/d2124a995f89e33c54f41da76bfd5b0bd5820898/sdk/src/rent_collector.rs#L108 */
     377             :   /* https://github.com/anza-xyz/agave/blob/d2124a995f89e33c54f41da76bfd5b0bd5820898/sdk/program/src/rent.rs#L95 */
     378           0 :   return (long)fd_rust_cast_double_to_ulong(years_elapsed * (double)lamports_per_year);
     379           0 : }
     380             : 
     381             : /* fd_runtime_collect_rent_from_account performs rent collection duties.
     382             :    Although the Solana runtime prevents the creation of new accounts
     383             :    that are subject to rent, some older accounts are still undergo the
     384             :    rent collection process.  Updates the account's 'rent_epoch' if
     385             :    needed. Returns the amount of rent collected. */
     386             : /* https://github.com/anza-xyz/agave/blob/v2.0.10/svm/src/account_loader.rs#L71-96 */
     387             : ulong
     388             : fd_runtime_collect_rent_from_account( fd_epoch_schedule_t const * schedule,
     389             :                                       fd_rent_t const *           rent,
     390             :                                       double                      slots_per_year,
     391             :                                       fd_txn_account_t *          acc,
     392           0 :                                       ulong                       epoch ) {
     393             : 
     394           0 :   if( FD_UNLIKELY( acc->vt->get_rent_epoch( acc )!=FD_RENT_EXEMPT_RENT_EPOCH &&
     395           0 :                      fd_runtime_get_rent_due( schedule,
     396           0 :                                               rent,
     397           0 :                                               slots_per_year,
     398           0 :                                               acc,
     399           0 :                                               epoch )==FD_RENT_EXEMPT ) ) {
     400           0 :       acc->vt->set_rent_epoch( acc, FD_RENT_EXEMPT_RENT_EPOCH );
     401           0 :   }
     402           0 :   return 0UL;
     403           0 : }
     404             : 
     405             : #undef FD_RENT_EXEMPT
     406             : 
     407             : /******************************************************************************/
     408             : /* Block-Level Execution Preparation/Finalization                             */
     409             : /******************************************************************************/
     410             : 
     411             : /*
     412             : https://github.com/firedancer-io/solana/blob/dab3da8e7b667d7527565bddbdbecf7ec1fb868e/sdk/program/src/fee_calculator.rs#L105-L165
     413             : */
     414             : static void
     415             : fd_runtime_new_fee_rate_governor_derived( fd_bank_t * bank,
     416           0 :                                           ulong       latest_singatures_per_slot ) {
     417             : 
     418           0 :   fd_fee_rate_governor_t const * base_fee_rate_governor = fd_bank_fee_rate_governor_query( bank );
     419             : 
     420           0 :   ulong old_lamports_per_signature = fd_bank_lamports_per_signature_get( bank );
     421             : 
     422           0 :   fd_fee_rate_governor_t me = {
     423           0 :     .target_signatures_per_slot    = base_fee_rate_governor->target_signatures_per_slot,
     424           0 :     .target_lamports_per_signature = base_fee_rate_governor->target_lamports_per_signature,
     425           0 :     .max_lamports_per_signature    = base_fee_rate_governor->max_lamports_per_signature,
     426           0 :     .min_lamports_per_signature    = base_fee_rate_governor->min_lamports_per_signature,
     427           0 :     .burn_percent                  = base_fee_rate_governor->burn_percent
     428           0 :   };
     429             : 
     430           0 :   ulong new_lamports_per_signature = 0;
     431           0 :   if( me.target_signatures_per_slot > 0 ) {
     432           0 :     me.min_lamports_per_signature = fd_ulong_max( 1UL, (ulong)(me.target_lamports_per_signature / 2) );
     433           0 :     me.max_lamports_per_signature = me.target_lamports_per_signature * 10;
     434           0 :     ulong desired_lamports_per_signature = fd_ulong_min(
     435           0 :       me.max_lamports_per_signature,
     436           0 :       fd_ulong_max(
     437           0 :         me.min_lamports_per_signature,
     438           0 :         me.target_lamports_per_signature
     439           0 :         * fd_ulong_min(latest_singatures_per_slot, (ulong)UINT_MAX)
     440           0 :         / me.target_signatures_per_slot
     441           0 :       )
     442           0 :     );
     443           0 :     long gap = (long)desired_lamports_per_signature - (long)old_lamports_per_signature;
     444           0 :     if ( gap == 0 ) {
     445           0 :       new_lamports_per_signature = desired_lamports_per_signature;
     446           0 :     } else {
     447           0 :       long gap_adjust = (long)(fd_ulong_max( 1UL, (ulong)(me.target_lamports_per_signature / 20) ))
     448           0 :         * (gap != 0)
     449           0 :         * (gap > 0 ? 1 : -1);
     450           0 :       new_lamports_per_signature = fd_ulong_min(
     451           0 :         me.max_lamports_per_signature,
     452           0 :         fd_ulong_max(
     453           0 :           me.min_lamports_per_signature,
     454           0 :           (ulong)((long)old_lamports_per_signature + gap_adjust)
     455           0 :         )
     456           0 :       );
     457           0 :     }
     458           0 :   } else {
     459           0 :     new_lamports_per_signature = base_fee_rate_governor->target_lamports_per_signature;
     460           0 :     me.min_lamports_per_signature = me.target_lamports_per_signature;
     461           0 :     me.max_lamports_per_signature = me.target_lamports_per_signature;
     462           0 :   }
     463             : 
     464           0 :   if( FD_UNLIKELY( old_lamports_per_signature==0UL ) ) {
     465           0 :     fd_bank_prev_lamports_per_signature_set( bank, new_lamports_per_signature );
     466           0 :   } else {
     467           0 :     fd_bank_prev_lamports_per_signature_set( bank, old_lamports_per_signature );
     468           0 :   }
     469             : 
     470           0 :   fd_bank_fee_rate_governor_set( bank, me );
     471             : 
     472           0 :   fd_bank_lamports_per_signature_set( bank, new_lamports_per_signature );
     473           0 : }
     474             : 
     475             : static int
     476             : fd_runtime_block_sysvar_update_pre_execute( fd_exec_slot_ctx_t * slot_ctx,
     477           0 :                                             fd_spad_t *          runtime_spad ) {
     478             :   // let (fee_rate_governor, fee_components_time_us) = measure_us!(
     479             :   //     FeeRateGovernor::new_derived(&parent.fee_rate_governor, parent.signature_count())
     480             :   // );
     481             :   /* https://github.com/firedancer-io/solana/blob/dab3da8e7b667d7527565bddbdbecf7ec1fb868e/runtime/src/bank.rs#L1312-L1314 */
     482             : 
     483           0 :   fd_runtime_new_fee_rate_governor_derived( slot_ctx->bank, fd_bank_parent_signature_cnt_get( slot_ctx->bank ) );
     484             : 
     485             :   // TODO: move all these out to a fd_sysvar_update() call...
     486           0 :   long clock_update_time      = -fd_log_wallclock();
     487           0 :   fd_sysvar_clock_update( slot_ctx->bank, slot_ctx->funk, slot_ctx->funk_txn, runtime_spad );
     488           0 :   clock_update_time          += fd_log_wallclock();
     489           0 :   double clock_update_time_ms = (double)clock_update_time * 1e-6;
     490           0 :   FD_LOG_INFO(( "clock updated - slot: %lu, elapsed: %6.6f ms", slot_ctx->slot, clock_update_time_ms ));
     491             : 
     492             :   // It has to go into the current txn previous info but is not in slot 0
     493           0 :   if( slot_ctx->slot != 0 ) {
     494           0 :     fd_sysvar_slot_hashes_update( slot_ctx, runtime_spad );
     495           0 :   }
     496           0 :   fd_sysvar_last_restart_slot_update( slot_ctx, runtime_spad );
     497             : 
     498           0 :   return 0;
     499           0 : }
     500             : 
     501             : int
     502             : fd_runtime_microblock_verify_ticks( fd_blockstore_t *           blockstore,
     503             :                                     ulong                       slot,
     504             :                                     fd_microblock_hdr_t const * hdr,
     505             :                                     bool               slot_complete,
     506             :                                     ulong              tick_height,
     507             :                                     ulong              max_tick_height,
     508           0 :                                     ulong              hashes_per_tick ) {
     509           0 :   ulong invalid_tick_hash_count = 0UL;
     510           0 :   ulong has_trailing_entry      = 0UL;
     511             : 
     512             :   /*
     513             :     In order to mimic the order of checks in Agave,
     514             :     we cache the results of some checks but do not immediately return
     515             :     an error.
     516             :   */
     517           0 :   fd_block_map_query_t quer[1];
     518           0 :   int err = fd_block_map_prepare( blockstore->block_map, &slot, NULL, quer, FD_MAP_FLAG_BLOCKING );
     519           0 :   fd_block_info_t * query = fd_block_map_query_ele( quer );
     520           0 :   if( FD_UNLIKELY( err || query->slot != slot ) ) {
     521           0 :     FD_LOG_ERR(( "fd_runtime_microblock_verify_ticks: fd_block_map_prepare on %lu failed", slot ));
     522           0 :   }
     523             : 
     524           0 :   query->tick_hash_count_accum = fd_ulong_sat_add( query->tick_hash_count_accum, hdr->hash_cnt );
     525           0 :   if( hdr->txn_cnt == 0UL ) {
     526           0 :     query->ticks_consumed++;
     527           0 :     if( FD_LIKELY( hashes_per_tick > 1UL ) ) {
     528           0 :       if( FD_UNLIKELY( query->tick_hash_count_accum != hashes_per_tick ) ) {
     529           0 :         FD_LOG_WARNING(( "tick_hash_count %lu hashes_per_tick %lu tick_count %lu", query->tick_hash_count_accum, hashes_per_tick, query->ticks_consumed ));
     530           0 :         invalid_tick_hash_count = 1U;
     531           0 :       }
     532           0 :     }
     533           0 :     query->tick_hash_count_accum = 0UL;
     534           0 :   } else {
     535             :     /* This wasn't a tick entry, but it's the last entry. */
     536           0 :     if( FD_UNLIKELY( slot_complete ) ) {
     537           0 :       FD_LOG_WARNING(( "last has %lu transactions expects 0", hdr->txn_cnt ));
     538           0 :       has_trailing_entry = 1U;
     539           0 :     }
     540           0 :   }
     541             : 
     542           0 :   ulong next_tick_height = tick_height + query->ticks_consumed;
     543           0 :   fd_block_map_publish( quer );
     544             : 
     545           0 :   if( FD_UNLIKELY( next_tick_height > max_tick_height ) ) {
     546           0 :     FD_LOG_WARNING(( "Too many ticks tick_height %lu max_tick_height %lu hashes_per_tick %lu tick_count %lu", tick_height, max_tick_height, hashes_per_tick, query->ticks_consumed ));
     547           0 :     return FD_BLOCK_ERR_TOO_MANY_TICKS;
     548           0 :   }
     549           0 :   if( FD_UNLIKELY( slot_complete && next_tick_height < max_tick_height ) ) {
     550           0 :     FD_LOG_WARNING(( "Too few ticks" ));
     551           0 :     return FD_BLOCK_ERR_TOO_FEW_TICKS;
     552           0 :   }
     553           0 :   if( FD_UNLIKELY( slot_complete && has_trailing_entry ) ) {
     554           0 :     FD_LOG_WARNING(( "Did not end with a tick" ));
     555           0 :     return FD_BLOCK_ERR_TRAILING_ENTRY;
     556           0 :   }
     557             : 
     558             :   /* Not returning FD_BLOCK_ERR_INVALID_LAST_TICK because we assume the
     559             :      slot is full. */
     560             : 
     561             :   /* Don't care about low power hashing or no hashing. */
     562           0 :   if( FD_LIKELY( hashes_per_tick > 1UL ) ) {
     563           0 :     if( FD_UNLIKELY( invalid_tick_hash_count ) ) {
     564           0 :       FD_LOG_WARNING(( "Tick with invalid number of hashes found" ));
     565           0 :       return FD_BLOCK_ERR_INVALID_TICK_HASH_COUNT;
     566           0 :     }
     567           0 :   }
     568           0 :   return FD_BLOCK_OK;
     569           0 : }
     570             : 
     571             : /* A streaming version of this by batch is implemented in batch_verify_ticks.
     572             :    This block_verify_ticks should only used for offline replay. */
     573             : ulong
     574             : fd_runtime_block_verify_ticks( fd_blockstore_t * blockstore,
     575             :                                ulong             slot,
     576             :                                uchar *           block_data,
     577             :                                ulong             block_data_sz,
     578             :                                ulong             tick_height,
     579             :                                ulong             max_tick_height,
     580           0 :                                ulong             hashes_per_tick ) {
     581           0 :   ulong tick_count              = 0UL;
     582           0 :   ulong tick_hash_count         = 0UL;
     583           0 :   ulong has_trailing_entry      = 0UL;
     584           0 :   uchar invalid_tick_hash_count = 0U;
     585             :   /*
     586             :     Iterate over microblocks/entries to
     587             :     (1) count the number of ticks
     588             :     (2) check whether the last entry is a tick
     589             :     (3) check whether ticks align with hashes per tick
     590             : 
     591             :     This precomputes everything we need in a single loop over the array.
     592             :     In order to mimic the order of checks in Agave,
     593             :     we cache the results of some checks but do not immediately return
     594             :     an error.
     595             :    */
     596           0 :   ulong slot_complete_idx = FD_SHRED_IDX_NULL;
     597           0 :   fd_block_set_t data_complete_idxs[FD_SHRED_BLK_MAX / sizeof(ulong)];
     598           0 :   int err = FD_MAP_ERR_AGAIN;
     599           0 :   while( err == FD_MAP_ERR_AGAIN ) {
     600           0 :     fd_block_map_query_t quer[1] = {0};
     601           0 :     err = fd_block_map_query_try( blockstore->block_map, &slot, NULL, quer, 0 );
     602           0 :     fd_block_info_t * query = fd_block_map_query_ele( quer );
     603           0 :     if( FD_UNLIKELY( err == FD_MAP_ERR_AGAIN ) )continue;
     604           0 :     if( FD_UNLIKELY( err == FD_MAP_ERR_KEY ) ) FD_LOG_ERR(( "fd_runtime_block_verify_ticks: fd_block_map_query_try failed" ));
     605           0 :     slot_complete_idx = query->slot_complete_idx;
     606           0 :     fd_memcpy( data_complete_idxs, query->data_complete_idxs, sizeof(data_complete_idxs) );
     607           0 :     err = fd_block_map_query_test( quer );
     608           0 :   }
     609             : 
     610           0 :   uint   batch_cnt = 0;
     611           0 :   ulong  batch_idx = 0;
     612           0 :   while ( batch_idx <= slot_complete_idx ) {
     613           0 :     batch_cnt++;
     614           0 :     ulong batch_sz = 0;
     615           0 :     uint  end_idx  = (uint)fd_block_set_const_iter_next( data_complete_idxs, batch_idx - 1 );
     616           0 :     FD_TEST( fd_blockstore_slice_query( blockstore, slot, (uint) batch_idx, end_idx, block_data_sz, block_data, &batch_sz ) == FD_BLOCKSTORE_SUCCESS );
     617           0 :     ulong micro_cnt = FD_LOAD( ulong, block_data );
     618           0 :     ulong off       = sizeof(ulong);
     619           0 :     for( ulong i = 0UL; i < micro_cnt; i++ ){
     620           0 :       fd_microblock_hdr_t const * hdr = fd_type_pun_const( ( block_data + off ) );
     621           0 :       off += sizeof(fd_microblock_hdr_t);
     622           0 :       tick_hash_count = fd_ulong_sat_add( tick_hash_count, hdr->hash_cnt );
     623           0 :       if( hdr->txn_cnt == 0UL ){
     624           0 :         tick_count++;
     625           0 :         if( FD_LIKELY( hashes_per_tick > 1UL ) ) {
     626           0 :           if( FD_UNLIKELY( tick_hash_count != hashes_per_tick ) ) {
     627           0 :             FD_LOG_WARNING(( "tick_hash_count %lu hashes_per_tick %lu tick_count %lu i %lu micro_cnt %lu", tick_hash_count, hashes_per_tick, tick_count, i, micro_cnt ));
     628           0 :             invalid_tick_hash_count = 1U;
     629           0 :           }
     630           0 :         }
     631           0 :         tick_hash_count = 0UL;
     632           0 :         continue;
     633           0 :       }
     634             :       /* This wasn't a tick entry, but it's the last entry. */
     635           0 :       if( FD_UNLIKELY( i == micro_cnt - 1UL ) ) {
     636           0 :         has_trailing_entry = batch_cnt;
     637           0 :       }
     638             : 
     639             :       /* seek past txns */
     640           0 :       uchar txn[FD_TXN_MAX_SZ];
     641           0 :       for( ulong j = 0; j < hdr->txn_cnt; j++ ) {
     642           0 :         ulong pay_sz = 0;
     643           0 :         ulong txn_sz = fd_txn_parse_core( block_data + off, fd_ulong_min( batch_sz - off, FD_TXN_MTU ), txn, NULL, &pay_sz );
     644           0 :         if( FD_UNLIKELY( !pay_sz ) ) FD_LOG_ERR(( "failed to parse transaction %lu in microblock %lu in slot %lu", j, i, slot ) );
     645           0 :         if( FD_UNLIKELY( !txn_sz || txn_sz > FD_TXN_MTU )) FD_LOG_ERR(( "failed to parse transaction %lu in microblock %lu in slot %lu. txn size: %lu", j, i, slot, txn_sz ));
     646           0 :         off += pay_sz;
     647           0 :       }
     648           0 :     }
     649             :     /* advance batch iterator */
     650           0 :     if( FD_UNLIKELY( batch_cnt == 1 ) ){ /* first batch */
     651           0 :       batch_idx = fd_block_set_const_iter_init( data_complete_idxs ) + 1;
     652           0 :     } else {
     653           0 :       batch_idx = fd_block_set_const_iter_next( data_complete_idxs, batch_idx - 1 ) + 1;
     654           0 :     }
     655           0 :   }
     656             : 
     657           0 :   ulong next_tick_height = tick_height + tick_count;
     658           0 :   if( FD_UNLIKELY( next_tick_height > max_tick_height ) ) {
     659           0 :     FD_LOG_WARNING(( "Too many ticks tick_height %lu max_tick_height %lu hashes_per_tick %lu tick_count %lu", tick_height, max_tick_height, hashes_per_tick, tick_count ));
     660           0 :     FD_LOG_WARNING(( "Too many ticks" ));
     661           0 :     return FD_BLOCK_ERR_TOO_MANY_TICKS;
     662           0 :   }
     663           0 :   if( FD_UNLIKELY( next_tick_height < max_tick_height ) ) {
     664           0 :     FD_LOG_WARNING(( "Too few ticks" ));
     665           0 :     return FD_BLOCK_ERR_TOO_FEW_TICKS;
     666           0 :   }
     667           0 :   if( FD_UNLIKELY( has_trailing_entry == batch_cnt ) ) {
     668           0 :     FD_LOG_WARNING(( "Did not end with a tick" ));
     669           0 :     return FD_BLOCK_ERR_TRAILING_ENTRY;
     670           0 :   }
     671             : 
     672             :   /* Not returning FD_BLOCK_ERR_INVALID_LAST_TICK because we assume the
     673             :      slot is full. */
     674             : 
     675             :   /* Don't care about low power hashing or no hashing. */
     676           0 :   if( FD_LIKELY( hashes_per_tick > 1UL ) ) {
     677           0 :     if( FD_UNLIKELY( invalid_tick_hash_count ) ) {
     678           0 :       FD_LOG_WARNING(( "Tick with invalid number of hashes found" ));
     679           0 :       return FD_BLOCK_ERR_INVALID_TICK_HASH_COUNT;
     680           0 :     }
     681           0 :   }
     682             : 
     683           0 :   return FD_BLOCK_OK;
     684           0 : }
     685             : 
     686             : int
     687             : fd_runtime_load_txn_address_lookup_tables( fd_txn_t const * txn,
     688             :                                            uchar const *    payload,
     689             :                                            fd_funk_t *      funk,
     690             :                                            fd_funk_txn_t *  funk_txn,
     691             :                                            ulong            slot,
     692             :                                            fd_slot_hash_t * hashes,
     693           0 :                                            fd_acct_addr_t * out_accts_alt ) {
     694             : 
     695           0 :   if( FD_LIKELY( txn->transaction_version!=FD_TXN_V0 ) ) return FD_RUNTIME_EXECUTE_SUCCESS;
     696             : 
     697           0 :   ulong            readonly_lut_accs_cnt = 0UL;
     698           0 :   ulong            writable_lut_accs_cnt = 0UL;
     699           0 :   fd_acct_addr_t * readonly_lut_accs     = out_accts_alt+txn->addr_table_adtl_writable_cnt;
     700           0 :   fd_txn_acct_addr_lut_t const * addr_luts = fd_txn_get_address_tables_const( txn );
     701           0 :   for( ulong i = 0UL; i < txn->addr_table_lookup_cnt; i++ ) {
     702           0 :     fd_txn_acct_addr_lut_t const * addr_lut  = &addr_luts[i];
     703           0 :     fd_pubkey_t const * addr_lut_acc = (fd_pubkey_t *)(payload + addr_lut->addr_off);
     704             : 
     705             :     /* https://github.com/anza-xyz/agave/blob/368ea563c423b0a85cc317891187e15c9a321521/accounts-db/src/accounts.rs#L90-L94 */
     706           0 :     FD_TXN_ACCOUNT_DECL( addr_lut_rec );
     707           0 :     int err = fd_txn_account_init_from_funk_readonly( addr_lut_rec,
     708           0 :                                                       addr_lut_acc,
     709           0 :                                                       funk,
     710           0 :                                                       funk_txn );
     711           0 :     if( FD_UNLIKELY( err != FD_ACC_MGR_SUCCESS ) ) {
     712           0 :       return FD_RUNTIME_TXN_ERR_ADDRESS_LOOKUP_TABLE_NOT_FOUND;
     713           0 :     }
     714             : 
     715             :     /* https://github.com/anza-xyz/agave/blob/368ea563c423b0a85cc317891187e15c9a321521/accounts-db/src/accounts.rs#L96-L114 */
     716           0 :     if( FD_UNLIKELY( memcmp( addr_lut_rec->vt->get_owner( addr_lut_rec ), fd_solana_address_lookup_table_program_id.key, sizeof(fd_pubkey_t) ) ) ) {
     717           0 :       return FD_RUNTIME_TXN_ERR_INVALID_ADDRESS_LOOKUP_TABLE_OWNER;
     718           0 :     }
     719             : 
     720             :     /* Realistically impossible case, but need to make sure we don't cause an OOB data access
     721             :        https://github.com/anza-xyz/agave/blob/368ea563c423b0a85cc317891187e15c9a321521/sdk/program/src/address_lookup_table/state.rs#L205-L209 */
     722           0 :     if( FD_UNLIKELY( addr_lut_rec->vt->get_data_len( addr_lut_rec ) < FD_LOOKUP_TABLE_META_SIZE ) ) {
     723           0 :       return FD_RUNTIME_TXN_ERR_INVALID_ADDRESS_LOOKUP_TABLE_DATA;
     724           0 :     }
     725             : 
     726             :     /* https://github.com/anza-xyz/agave/blob/574bae8fefc0ed256b55340b9d87b7689bcdf222/accounts-db/src/accounts.rs#L141-L142 */
     727           0 :     fd_bincode_decode_ctx_t decode_ctx = {
     728           0 :       .data    = addr_lut_rec->vt->get_data( addr_lut_rec ),
     729           0 :       .dataend = &addr_lut_rec->vt->get_data( addr_lut_rec )[FD_LOOKUP_TABLE_META_SIZE]
     730           0 :     };
     731             : 
     732           0 :     ulong total_sz = 0UL;
     733           0 :     err = fd_address_lookup_table_state_decode_footprint( &decode_ctx, &total_sz );
     734           0 :     if( FD_UNLIKELY( err ) ) {
     735           0 :       return FD_RUNTIME_TXN_ERR_INVALID_ADDRESS_LOOKUP_TABLE_DATA;
     736           0 :     }
     737             : 
     738             :     /* https://github.com/anza-xyz/agave/blob/368ea563c423b0a85cc317891187e15c9a321521/sdk/program/src/address_lookup_table/state.rs#L197-L214 */
     739           0 :     fd_address_lookup_table_state_t table[1];
     740           0 :     fd_address_lookup_table_state_t * addr_lookup_table_state = fd_address_lookup_table_state_decode( table, &decode_ctx );
     741             : 
     742             :     /* https://github.com/anza-xyz/agave/blob/368ea563c423b0a85cc317891187e15c9a321521/sdk/program/src/address_lookup_table/state.rs#L200-L203 */
     743           0 :     if( FD_UNLIKELY( addr_lookup_table_state->discriminant != fd_address_lookup_table_state_enum_lookup_table ) ) {
     744           0 :       return FD_RUNTIME_TXN_ERR_INVALID_ADDRESS_LOOKUP_TABLE_DATA;
     745           0 :     }
     746             : 
     747             :     /* Again probably an impossible case, but the ALUT data needs to be 32-byte aligned
     748             :        https://github.com/anza-xyz/agave/blob/368ea563c423b0a85cc317891187e15c9a321521/sdk/program/src/address_lookup_table/state.rs#L210-L214 */
     749           0 :     if( FD_UNLIKELY( (addr_lut_rec->vt->get_data_len( addr_lut_rec ) - FD_LOOKUP_TABLE_META_SIZE) & 0x1fUL ) ) {
     750           0 :       return FD_RUNTIME_TXN_ERR_INVALID_ADDRESS_LOOKUP_TABLE_DATA;
     751           0 :     }
     752             : 
     753             :     /* https://github.com/anza-xyz/agave/blob/368ea563c423b0a85cc317891187e15c9a321521/accounts-db/src/accounts.rs#L101-L112 */
     754           0 :     fd_acct_addr_t * lookup_addrs  = (fd_acct_addr_t *)&addr_lut_rec->vt->get_data( addr_lut_rec )[FD_LOOKUP_TABLE_META_SIZE];
     755           0 :     ulong         lookup_addrs_cnt = (addr_lut_rec->vt->get_data_len( addr_lut_rec ) - FD_LOOKUP_TABLE_META_SIZE) >> 5UL; // = (dlen - 56) / 32
     756             : 
     757             :     /* https://github.com/anza-xyz/agave/blob/368ea563c423b0a85cc317891187e15c9a321521/sdk/program/src/address_lookup_table/state.rs#L175-L176 */
     758           0 :     ulong active_addresses_len;
     759           0 :     err = fd_get_active_addresses_len( &addr_lookup_table_state->inner.lookup_table,
     760           0 :                                        slot,
     761           0 :                                        hashes,
     762           0 :                                        lookup_addrs_cnt,
     763           0 :                                        &active_addresses_len );
     764           0 :     if( FD_UNLIKELY( err ) ) {
     765           0 :       return err;
     766           0 :     }
     767             : 
     768             :     /* https://github.com/anza-xyz/agave/blob/368ea563c423b0a85cc317891187e15c9a321521/sdk/program/src/address_lookup_table/state.rs#L169-L182 */
     769           0 :     uchar * writable_lut_idxs = (uchar *)payload + addr_lut->writable_off;
     770           0 :     for( ulong j=0; j<addr_lut->writable_cnt; j++ ) {
     771             :       /* https://github.com/anza-xyz/agave/blob/368ea563c423b0a85cc317891187e15c9a321521/sdk/program/src/address_lookup_table/state.rs#L177-L181 */
     772           0 :       if( writable_lut_idxs[j] >= active_addresses_len ) {
     773           0 :         return FD_RUNTIME_TXN_ERR_INVALID_ADDRESS_LOOKUP_TABLE_INDEX;
     774           0 :       }
     775           0 :       out_accts_alt[writable_lut_accs_cnt++] = lookup_addrs[writable_lut_idxs[j]];
     776           0 :     }
     777             : 
     778           0 :     uchar * readonly_lut_idxs = (uchar *)payload + addr_lut->readonly_off;
     779           0 :     for( ulong j = 0; j < addr_lut->readonly_cnt; j++ ) {
     780             :       /* https://github.com/anza-xyz/agave/blob/368ea563c423b0a85cc317891187e15c9a321521/sdk/program/src/address_lookup_table/state.rs#L177-L181 */
     781           0 :       if( readonly_lut_idxs[j] >= active_addresses_len ) {
     782           0 :         return FD_RUNTIME_TXN_ERR_INVALID_ADDRESS_LOOKUP_TABLE_INDEX;
     783           0 :       }
     784           0 :       readonly_lut_accs[readonly_lut_accs_cnt++] = lookup_addrs[readonly_lut_idxs[j]];
     785           0 :     }
     786           0 :   }
     787             : 
     788           0 :   return FD_RUNTIME_EXECUTE_SUCCESS;
     789           0 : }
     790             : 
     791             : int
     792             : fd_runtime_microblock_verify_read_write_conflicts( fd_txn_p_t *               txns,
     793             :                                                    ulong                      txn_cnt,
     794             :                                                    fd_conflict_detect_ele_t * acct_map,
     795             :                                                    fd_acct_addr_t *           acct_arr,
     796             :                                                    fd_funk_t *                funk,
     797             :                                                    fd_funk_txn_t *            funk_txn,
     798             :                                                    ulong                      slot,
     799             :                                                    fd_slot_hash_t *           slot_hashes,
     800             :                                                    fd_features_t *            features,
     801             :                                                    int *                      out_conflict_detected,
     802           0 :                                                    fd_acct_addr_t *           out_conflict_addr_opt ) {
     803           0 :   *out_conflict_detected=FD_RUNTIME_NO_CONFLICT_DETECTED;
     804           0 : #define NO_CONFLICT ( *out_conflict_detected==FD_RUNTIME_NO_CONFLICT_DETECTED )
     805             : 
     806           0 : #define UPDATE_CONFLICT(cond1, cond2, acct) \
     807           0 : if( FD_UNLIKELY( cond1 ) ) { \
     808           0 :   if( FD_LIKELY( out_conflict_addr_opt ) ) *out_conflict_addr_opt = acct; \
     809           0 :   *out_conflict_detected=FD_RUNTIME_WRITE_WRITE_CONFLICT_DETECTED; \
     810           0 : } else if( FD_UNLIKELY( cond2 ) ) { \
     811           0 :   if( FD_LIKELY( out_conflict_addr_opt ) ) *out_conflict_addr_opt = acct; \
     812           0 :   *out_conflict_detected=FD_RUNTIME_READ_WRITE_CONFLICT_DETECTED; \
     813           0 : }
     814             : 
     815           0 :   ulong curr_idx            = 0;
     816           0 :   ulong sentinel_is_read    = 0;
     817           0 :   ulong sentinel_is_written = 0;
     818           0 :   int runtime_err           = FD_RUNTIME_EXECUTE_SUCCESS;
     819           0 :   for( ulong i=0; i<txn_cnt && NO_CONFLICT; i++ ) {
     820           0 :     fd_txn_p_t *           txn = txns+i;
     821           0 :     fd_acct_addr_t * txn_accts = acct_arr+curr_idx;
     822             : 
     823             :     /* Put the immediate & ALT accounts at txn_accts */
     824           0 :     const fd_acct_addr_t * accts_imm = fd_txn_get_acct_addrs( TXN(txn), txn->payload );
     825           0 :     ulong              accts_imm_cnt = fd_txn_account_cnt( TXN(txn), FD_TXN_ACCT_CAT_IMM );
     826           0 :     fd_memcpy( txn_accts, accts_imm, accts_imm_cnt*sizeof(fd_acct_addr_t) );
     827           0 :     runtime_err = fd_runtime_load_txn_address_lookup_tables( TXN(txn),
     828           0 :                                                              txn->payload,
     829           0 :                                                              funk,
     830           0 :                                                              funk_txn,
     831           0 :                                                              slot,
     832           0 :                                                              slot_hashes,
     833           0 :                                                              txn_accts+accts_imm_cnt );
     834           0 :     if( FD_UNLIKELY( runtime_err!=FD_RUNTIME_EXECUTE_SUCCESS ) ) break;
     835             : 
     836           0 :     ulong accounts_cnt   = fd_txn_account_cnt( TXN(txn), FD_TXN_ACCT_CAT_ALL );
     837           0 :     curr_idx            +=accounts_cnt;
     838           0 :     uint bpf_upgradeable = fd_txn_account_has_bpf_loader_upgradeable( fd_type_pun( txn_accts ), accounts_cnt );
     839             : 
     840             :     /* Iterate all writable accounts and detect W-W/R-W conflicts */
     841           0 :     for( fd_txn_acct_iter_t iter=fd_txn_acct_iter_init( TXN(txn), FD_TXN_ACCT_CAT_WRITABLE );
     842           0 :          iter!=fd_txn_acct_iter_end() && NO_CONFLICT;
     843           0 :          iter=fd_txn_acct_iter_next( iter ) ) {
     844           0 :       ushort idx                     = (ushort)fd_txn_acct_iter_idx( iter );
     845           0 :       fd_acct_addr_t writable_acc = txn_accts[ idx ];
     846             : 
     847             :       /* Check whether writable_acc is demoted to a read-only account */
     848           0 :       if( FD_UNLIKELY( !fd_exec_txn_account_is_writable_idx_flat( slot,
     849           0 :                                                                   idx,
     850           0 :                                                                   fd_type_pun( &txn_accts[ idx ] ),
     851           0 :                                                                   TXN(txn),
     852           0 :                                                                   features,
     853           0 :                                                                   bpf_upgradeable ) ) ) {
     854           0 :         continue;
     855           0 :       }
     856             : 
     857             :       /* writable_acc is the sentinel (fd_acct_addr_null) */
     858           0 :       if( FD_UNLIKELY( fd_conflict_detect_map_key_inval( writable_acc ) ) ) {
     859           0 :         UPDATE_CONFLICT( sentinel_is_written, sentinel_is_read, writable_acc );
     860           0 :         sentinel_is_written = 1;
     861           0 :         continue;
     862           0 :       }
     863             : 
     864             :       /* writable_acc is not the sentinel (fd_acct_addr_null) */
     865           0 :       fd_conflict_detect_ele_t * found = fd_conflict_detect_map_query( acct_map, writable_acc, NULL );
     866           0 :       if( FD_UNLIKELY( found ) ) {
     867           0 :         UPDATE_CONFLICT( found->writable, !found->writable, writable_acc );
     868           0 :       } else {
     869           0 :         fd_conflict_detect_ele_t * entry = fd_conflict_detect_map_insert( acct_map, writable_acc );
     870           0 :         entry->writable                  = 1;
     871           0 :       }
     872           0 :     }
     873             : 
     874             :     /* Iterate all readonly accounts and detect R-W conflicts */
     875           0 :     for( fd_txn_acct_iter_t iter=fd_txn_acct_iter_init( TXN(txn), FD_TXN_ACCT_CAT_READONLY );
     876           0 :          iter!=fd_txn_acct_iter_end() && NO_CONFLICT;
     877           0 :          iter=fd_txn_acct_iter_next( iter ) ) {
     878           0 :       fd_acct_addr_t readonly_acc = txn_accts[ fd_txn_acct_iter_idx( iter ) ];
     879             : 
     880             :       /* readonly_acc is the sentinel (fd_acct_addr_null) */
     881           0 :       if( FD_UNLIKELY( fd_conflict_detect_map_key_inval( readonly_acc ) ) ) {
     882           0 :         UPDATE_CONFLICT( 0, sentinel_is_written, readonly_acc );
     883           0 :         sentinel_is_read = 1;
     884           0 :         continue;
     885           0 :       }
     886             : 
     887             :       /* readonly_acc is not the sentinel (fd_acct_addr_null) */
     888           0 :       fd_conflict_detect_ele_t * found = fd_conflict_detect_map_query( acct_map, readonly_acc, NULL );
     889           0 :       if( FD_UNLIKELY( found ) ) {
     890           0 :         UPDATE_CONFLICT( 0, found->writable, readonly_acc );
     891           0 :       } else {
     892           0 :         fd_conflict_detect_ele_t * entry = fd_conflict_detect_map_insert( acct_map, readonly_acc );
     893           0 :         entry->writable                  = 0;
     894           0 :       }
     895           0 :     }
     896           0 :   }
     897             : 
     898             :   /* Clear all the entries inserted into acct_map */
     899           0 :   for( ulong i=0; i<curr_idx; i++ ) {
     900           0 :     if( FD_UNLIKELY( fd_conflict_detect_map_key_inval( acct_arr[i] ) ) ) continue;
     901           0 :     fd_conflict_detect_ele_t * found = fd_conflict_detect_map_query( acct_map, acct_arr[i], NULL );
     902           0 :     if( FD_LIKELY( found ) ) fd_conflict_detect_map_remove( acct_map, found );
     903           0 :   }
     904             : 
     905           0 :   if( FD_UNLIKELY( runtime_err!=FD_RUNTIME_EXECUTE_SUCCESS ) ) {
     906           0 :     return runtime_err;
     907           0 :   } else {
     908             :     /* https://github.com/anza-xyz/agave/blob/v2.2.3/accounts-db/src/account_locks.rs#L31 */
     909             :     /* https://github.com/anza-xyz/agave/blob/v2.2.3/accounts-db/src/account_locks.rs#L34 */
     910           0 :     return NO_CONFLICT? FD_RUNTIME_EXECUTE_SUCCESS : FD_RUNTIME_TXN_ERR_ACCOUNT_IN_USE;
     911           0 :   }
     912           0 : }
     913             : 
     914             : void
     915           0 : fd_runtime_poh_verify( fd_poh_verifier_t * poh_info ) {
     916             : 
     917           0 :   fd_hash_t working_hash = *(poh_info->in_poh_hash);
     918           0 :   fd_hash_t init_hash    = working_hash;
     919             : 
     920           0 :   fd_microblock_hdr_t const * hdr = poh_info->microblock.hdr;
     921           0 :   ulong               microblk_sz = poh_info->microblk_max_sz;
     922             : 
     923           0 :   if( !hdr->txn_cnt ){
     924           0 :     fd_poh_append( &working_hash, hdr->hash_cnt );
     925           0 :   } else { /* not a tick, regular microblock */
     926           0 :     if( hdr->hash_cnt ){
     927           0 :       fd_poh_append( &working_hash, hdr->hash_cnt - 1 );
     928           0 :     }
     929             : 
     930           0 :     ulong leaf_cnt_max = FD_TXN_ACTUAL_SIG_MAX * hdr->txn_cnt;
     931             : 
     932           0 :     FD_SPAD_FRAME_BEGIN( poh_info->spad ) {
     933           0 :       uchar *               commit = fd_spad_alloc( poh_info->spad, FD_WBMTREE32_ALIGN, fd_wbmtree32_footprint(leaf_cnt_max) );
     934           0 :       fd_wbmtree32_leaf_t * leafs  = fd_spad_alloc( poh_info->spad, alignof(fd_wbmtree32_leaf_t), sizeof(fd_wbmtree32_leaf_t) * leaf_cnt_max );
     935           0 :       fd_wbmtree32_t *      tree   = fd_wbmtree32_init( commit, leaf_cnt_max );
     936           0 :       fd_wbmtree32_leaf_t * l      = &leafs[0];
     937             : 
     938             :       /* Loop across transactions */
     939           0 :       ulong leaf_cnt = 0UL;
     940           0 :       ulong off      = sizeof(fd_microblock_hdr_t);
     941           0 :       for( ulong txn_idx=0UL; txn_idx<hdr->txn_cnt; txn_idx++ ) {
     942           0 :         fd_txn_p_t txn_p;
     943           0 :         ulong pay_sz = 0UL;
     944           0 :         ulong txn_sz = fd_txn_parse_core( poh_info->microblock.raw + off,
     945           0 :                                           fd_ulong_min( FD_TXN_MTU, microblk_sz - off ),
     946           0 :                                           TXN(&txn_p),
     947           0 :                                           NULL,
     948           0 :                                           &pay_sz );
     949           0 :         if( FD_UNLIKELY( !pay_sz || !txn_sz || txn_sz > FD_TXN_MTU )  ) {
     950           0 :           FD_LOG_ERR(( "failed to parse transaction %lu in replay", txn_idx ));
     951           0 :         }
     952             : 
     953             :         /* Loop across signatures */
     954           0 :         fd_txn_t const *         txn  = (fd_txn_t const *) txn_p._;
     955           0 :         fd_ed25519_sig_t const * sigs = (fd_ed25519_sig_t const *)fd_type_pun((poh_info->microblock.raw + off) + (ulong)txn->signature_off);
     956           0 :         for( ulong j=0UL; j<txn->signature_cnt; j++ ) {
     957           0 :           l->data     = (uchar *)&sigs[j];
     958           0 :           l->data_len = sizeof(fd_ed25519_sig_t);
     959           0 :           l++;
     960           0 :           leaf_cnt++;
     961           0 :         }
     962           0 :         off += pay_sz;
     963           0 :       }
     964             : 
     965           0 :       uchar * mbuf = fd_spad_alloc( poh_info->spad, 1UL, leaf_cnt * (sizeof(fd_ed25519_sig_t) + 1) );
     966           0 :       fd_wbmtree32_append( tree, leafs, leaf_cnt, mbuf );
     967           0 :       uchar * root = fd_wbmtree32_fini( tree );
     968           0 :       fd_poh_mixin( &working_hash, root );
     969           0 :     } FD_SPAD_FRAME_END;
     970           0 :   }
     971             : 
     972           0 :   if( FD_UNLIKELY( memcmp(hdr->hash, working_hash.hash, sizeof(fd_hash_t)) ) ) {
     973           0 :     FD_LOG_WARNING(( "poh mismatch (bank: %s, entry: %s, INIT: %s )", FD_BASE58_ENC_32_ALLOCA( working_hash.hash ), FD_BASE58_ENC_32_ALLOCA( hdr->hash ), FD_BASE58_ENC_32_ALLOCA( init_hash.hash ) ));
     974           0 :     poh_info->success = -1;
     975           0 :   }
     976           0 : }
     977             : 
     978             : int
     979             : fd_runtime_block_execute_prepare( fd_exec_slot_ctx_t * slot_ctx,
     980             :                                   fd_blockstore_t *    blockstore,
     981           0 :                                   fd_spad_t *          runtime_spad ) {
     982             : 
     983             : 
     984           0 :   if( blockstore && slot_ctx->slot != 0UL ) {
     985           0 :     fd_blockstore_block_height_update( blockstore,
     986           0 :                                        slot_ctx->slot,
     987           0 :                                        fd_bank_block_height_get( slot_ctx->bank ) );
     988           0 :   }
     989             : 
     990           0 :   fd_bank_execution_fees_set( slot_ctx->bank, 0UL );
     991             : 
     992           0 :   fd_bank_priority_fees_set( slot_ctx->bank, 0UL );
     993             : 
     994           0 :   fd_bank_signature_count_set( slot_ctx->bank, 0UL );
     995             : 
     996           0 :   fd_bank_txn_count_set( slot_ctx->bank, 0UL );
     997             : 
     998           0 :   fd_bank_nonvote_txn_count_set( slot_ctx->bank, 0UL );
     999             : 
    1000           0 :   fd_bank_failed_txn_count_set( slot_ctx->bank, 0UL );
    1001             : 
    1002           0 :   fd_bank_nonvote_failed_txn_count_set( slot_ctx->bank, 0UL );
    1003             : 
    1004           0 :   fd_bank_total_compute_units_used_set( slot_ctx->bank, 0UL );
    1005             : 
    1006           0 :   int result = fd_runtime_block_sysvar_update_pre_execute( slot_ctx, runtime_spad );
    1007           0 :   if( FD_UNLIKELY( result != 0 ) ) {
    1008           0 :     FD_LOG_WARNING(("updating sysvars failed"));
    1009           0 :     return result;
    1010           0 :   }
    1011             : 
    1012           0 :   return FD_RUNTIME_EXECUTE_SUCCESS;
    1013           0 : }
    1014             : 
    1015             : void
    1016             : fd_runtime_block_execute_finalize_start( fd_exec_slot_ctx_t *             slot_ctx,
    1017             :                                          fd_spad_t *                      runtime_spad,
    1018             :                                          fd_accounts_hash_task_data_t * * task_data,
    1019           0 :                                          ulong                            lt_hash_cnt ) {
    1020             : 
    1021           0 :   fd_sysvar_slot_history_update( slot_ctx, runtime_spad );
    1022             : 
    1023             :   /* This slot is now "frozen" and can't be changed anymore. */
    1024           0 :   fd_runtime_freeze( slot_ctx, runtime_spad );
    1025             : 
    1026           0 :   int result = fd_bpf_scan_and_create_bpf_program_cache_entry( slot_ctx, runtime_spad );
    1027           0 :   if( FD_UNLIKELY( result ) ) {
    1028           0 :     FD_LOG_WARNING(( "update bpf program cache failed" ));
    1029           0 :     return;
    1030           0 :   }
    1031             : 
    1032             :   /* Collect list of changed accounts to be added to bank hash */
    1033           0 :   *task_data = fd_spad_alloc( runtime_spad,
    1034           0 :                               alignof(fd_accounts_hash_task_data_t),
    1035           0 :                               sizeof(fd_accounts_hash_task_data_t) );
    1036           0 :   (*task_data)->lthash_values = fd_spad_alloc_check(
    1037           0 :       runtime_spad, alignof(fd_lthash_value_t), lt_hash_cnt * sizeof(fd_lthash_value_t) );
    1038             : 
    1039           0 :   for( ulong i=0UL; i<lt_hash_cnt; i++ ) {
    1040           0 :     fd_lthash_zero( &((*task_data)->lthash_values)[i] );
    1041           0 :   }
    1042             : 
    1043           0 :   fd_collect_modified_accounts( slot_ctx, *task_data, runtime_spad );
    1044           0 : }
    1045             : 
    1046             : int
    1047             : fd_runtime_block_execute_finalize_finish( fd_exec_slot_ctx_t *             slot_ctx,
    1048             :                                           fd_capture_ctx_t *               capture_ctx,
    1049             :                                           fd_runtime_block_info_t const *  block_info,
    1050             :                                           fd_spad_t *                      runtime_spad,
    1051             :                                           fd_accounts_hash_task_data_t *   task_data,
    1052           0 :                                           ulong                            lt_hash_cnt ) {
    1053             : 
    1054           0 :   fd_hash_t * bank_hash = fd_bank_bank_hash_modify( slot_ctx->bank );
    1055           0 :   int err = fd_update_hash_bank_exec_hash( slot_ctx,
    1056           0 :                                            bank_hash,
    1057           0 :                                            capture_ctx,
    1058           0 :                                            task_data,
    1059           0 :                                            1UL,
    1060           0 :                                            task_data->lthash_values,
    1061           0 :                                            lt_hash_cnt,
    1062           0 :                                            block_info->signature_cnt,
    1063           0 :                                            runtime_spad );
    1064             : 
    1065           0 :   if( FD_UNLIKELY( err ) ) {
    1066           0 :     FD_LOG_ERR(( "Unable to hash at end of slot" ));
    1067           0 :   }
    1068             : 
    1069           0 :   return FD_RUNTIME_EXECUTE_SUCCESS;
    1070             : 
    1071           0 : }
    1072             : 
    1073             : void
    1074             : block_finalize_tpool_wrapper( void * para_arg_1,
    1075             :                               void * para_arg_2 FD_PARAM_UNUSED,
    1076             :                               void * arg_1,
    1077             :                               void * arg_2,
    1078             :                               void * arg_3,
    1079           0 :                               void * arg_4 FD_PARAM_UNUSED ) {
    1080           0 :   fd_tpool_t *                   tpool      = (fd_tpool_t *)para_arg_1;
    1081           0 :   fd_accounts_hash_task_data_t * task_data  = (fd_accounts_hash_task_data_t *)arg_1;
    1082           0 :   ulong                          worker_cnt = (ulong)arg_2;
    1083           0 :   fd_exec_slot_ctx_t *           slot_ctx   = (fd_exec_slot_ctx_t *)arg_3;
    1084             : 
    1085           0 :   ulong cnt_per_worker = (worker_cnt>1) ? (task_data->info_sz / (worker_cnt-1UL)) + 1UL : task_data->info_sz;
    1086           0 :   for( ulong worker_idx=1UL; worker_idx<worker_cnt; worker_idx++ ) {
    1087           0 :     ulong start_idx = (worker_idx-1UL) * cnt_per_worker;
    1088           0 :     if( start_idx >= task_data->info_sz ) {
    1089           0 :       worker_cnt = worker_idx;
    1090           0 :       break;
    1091           0 :     }
    1092           0 :     ulong end_idx = fd_ulong_sat_sub((worker_idx) * cnt_per_worker, 1UL);
    1093           0 :     if( end_idx >= task_data->info_sz )
    1094           0 :       end_idx = fd_ulong_sat_sub( task_data->info_sz, 1UL );;
    1095           0 :     fd_tpool_exec( tpool, worker_idx, fd_account_hash_task,
    1096           0 :                    task_data, start_idx, end_idx,
    1097           0 :                    &task_data->lthash_values[worker_idx], slot_ctx, 0UL,
    1098           0 :                    0UL, 0UL, worker_idx, 0UL, 0UL, 0UL );
    1099           0 :   }
    1100             : 
    1101           0 :   for( ulong worker_idx=1UL; worker_idx<worker_cnt; worker_idx++ ) {
    1102           0 :     fd_tpool_wait( tpool, worker_idx );
    1103           0 :   }
    1104           0 : }
    1105             : 
    1106             : int
    1107             : fd_runtime_block_execute_finalize_para( fd_exec_slot_ctx_t *             slot_ctx,
    1108             :                                         fd_capture_ctx_t *               capture_ctx,
    1109             :                                         fd_runtime_block_info_t const *  block_info,
    1110             :                                         ulong                            worker_cnt,
    1111             :                                         fd_spad_t *                      runtime_spad,
    1112           0 :                                         fd_exec_para_cb_ctx_t *          exec_para_ctx ) {
    1113             : 
    1114           0 :   fd_accounts_hash_task_data_t * task_data = NULL;
    1115             : 
    1116           0 :   fd_runtime_block_execute_finalize_start( slot_ctx, runtime_spad, &task_data, worker_cnt );
    1117             : 
    1118           0 :   exec_para_ctx->fn_arg_1 = (void*)task_data;
    1119           0 :   exec_para_ctx->fn_arg_2 = (void*)worker_cnt;
    1120           0 :   exec_para_ctx->fn_arg_3 = (void*)slot_ctx;
    1121           0 :   fd_exec_para_call_func( exec_para_ctx );
    1122             : 
    1123           0 :   fd_runtime_block_execute_finalize_finish( slot_ctx, capture_ctx, block_info, runtime_spad, task_data, worker_cnt );
    1124             : 
    1125           0 :   return 0;
    1126           0 : }
    1127             : 
    1128             : /******************************************************************************/
    1129             : /* Transaction Level Execution Management                                     */
    1130             : /******************************************************************************/
    1131             : 
    1132             : /* fd_runtime_prepare_txns_start is responsible for setting up the task infos,
    1133             :    the slot_ctx, and for setting up the accessed accounts. */
    1134             : 
    1135             : int
    1136             : fd_runtime_prepare_txns_start( fd_exec_slot_ctx_t *         slot_ctx,
    1137             :                                fd_execute_txn_task_info_t * task_info,
    1138             :                                fd_txn_p_t *                 txns,
    1139             :                                ulong                        txn_cnt,
    1140           0 :                                fd_spad_t *                  runtime_spad ) {
    1141           0 :   int res = 0;
    1142             :   /* Loop across transactions */
    1143           0 :   for( ulong txn_idx = 0UL; txn_idx < txn_cnt; txn_idx++ ) {
    1144           0 :     fd_txn_p_t * txn = &txns[txn_idx];
    1145             : 
    1146             :     /* Allocate/setup transaction context and task infos */
    1147           0 :     task_info[txn_idx].txn_ctx      = fd_spad_alloc( runtime_spad, FD_EXEC_TXN_CTX_ALIGN, FD_EXEC_TXN_CTX_FOOTPRINT );
    1148           0 :     fd_exec_txn_ctx_t * txn_ctx     = task_info[txn_idx].txn_ctx;
    1149           0 :     task_info[txn_idx].exec_res     = 0;
    1150           0 :     task_info[txn_idx].txn          = txn;
    1151           0 :     fd_txn_t const * txn_descriptor = (fd_txn_t const *) txn->_;
    1152             : 
    1153           0 :     fd_rawtxn_b_t raw_txn = { .raw = txn->payload, .txn_sz = (ushort)txn->payload_sz };
    1154             : 
    1155           0 :     task_info[txn_idx].txn_ctx->spad      = runtime_spad;
    1156           0 :     task_info[txn_idx].txn_ctx->spad_wksp = fd_wksp_containing( runtime_spad );
    1157           0 :     int err = fd_execute_txn_prepare_start( slot_ctx,
    1158           0 :                                             txn_ctx,
    1159           0 :                                             txn_descriptor,
    1160           0 :                                             &raw_txn );
    1161           0 :     if( FD_UNLIKELY( err ) ) {
    1162           0 :       task_info[txn_idx].exec_res = err;
    1163           0 :       txn->flags                  = 0U;
    1164           0 :       res |= err;
    1165           0 :     }
    1166           0 :   }
    1167             : 
    1168           0 :   return res;
    1169           0 : }
    1170             : 
    1171             : /* fd_runtime_pre_execute_check is responsible for conducting many of the
    1172             :    transaction sanitization checks. */
    1173             : 
    1174             : void
    1175             : fd_runtime_pre_execute_check( fd_execute_txn_task_info_t * task_info,
    1176           0 :                               uchar                        dump_txn ) {
    1177           0 :   if( FD_UNLIKELY( !( task_info->txn->flags & FD_TXN_P_FLAGS_SANITIZE_SUCCESS ) ) ) {
    1178           0 :     return;
    1179           0 :   }
    1180             : 
    1181           0 :   fd_exec_txn_ctx_t * txn_ctx = task_info->txn_ctx;
    1182           0 :   fd_executor_setup_accounts_for_txn( txn_ctx );
    1183             : 
    1184             :   /* Dump transaction to protobuf */
    1185           0 :   if( FD_UNLIKELY( dump_txn ) ) {
    1186           0 :     fd_dump_txn_to_protobuf( task_info->txn_ctx, task_info->txn_ctx->spad );
    1187           0 :   }
    1188             : 
    1189           0 :   int err;
    1190             : 
    1191             :   /* https://github.com/anza-xyz/agave/blob/16de8b75ebcd57022409b422de557dd37b1de8db/sdk/src/transaction/sanitized.rs#L263-L275
    1192             :      TODO: Agave's precompile verification is done at the slot level, before batching and executing transactions. This logic should probably
    1193             :      be moved in the future. The Agave call heirarchy looks something like this:
    1194             :             process_single_slot
    1195             :                    v
    1196             :             confirm_full_slot
    1197             :                    v
    1198             :             confirm_slot_entries --------->
    1199             :                    v                      v
    1200             :             verify_transaction      process_entries
    1201             :                    v                      v
    1202             :             verify_precompiles      process_batches
    1203             :                                           v
    1204             :                                          ...
    1205             :                                           v
    1206             :                               load_and_execute_transactions
    1207             :                                           v
    1208             :                                          ...
    1209             :                                           v
    1210             :                                     load_accounts --> load_transaction_accounts
    1211             :                                           v
    1212             :                               general transaction execution
    1213             : 
    1214             :   */
    1215             : 
    1216           0 :   if( !FD_FEATURE_ACTIVE( txn_ctx->slot, &txn_ctx->features, move_precompile_verification_to_svm ) ) {
    1217           0 :     err = fd_executor_verify_precompiles( txn_ctx );
    1218           0 :     if( FD_UNLIKELY( err!=FD_RUNTIME_EXECUTE_SUCCESS ) ) {
    1219           0 :       task_info->txn->flags = 0U;
    1220           0 :       task_info->exec_res   = err;
    1221           0 :       return;
    1222           0 :     }
    1223           0 :   }
    1224             : 
    1225             :   /* Post-sanitization checks. Called from `prepare_sanitized_batch()` which, for now, only is used
    1226             :      to lock the accounts and perform a couple basic validations.
    1227             :      https://github.com/anza-xyz/agave/blob/838c1952595809a31520ff1603a13f2c9123aa51/accounts-db/src/account_locks.rs#L118 */
    1228           0 :   err = fd_executor_validate_account_locks( txn_ctx );
    1229           0 :   if( FD_UNLIKELY( err!=FD_RUNTIME_EXECUTE_SUCCESS ) ) {
    1230           0 :     task_info->txn->flags = 0U;
    1231           0 :     task_info->exec_res   = err;
    1232           0 :     return;
    1233           0 :   }
    1234             : 
    1235             :   /* `load_and_execute_transactions()` -> `check_transactions()`
    1236             :      https://github.com/anza-xyz/agave/blob/ced98f1ebe73f7e9691308afa757323003ff744f/runtime/src/bank.rs#L3667-L3672 */
    1237           0 :   err = fd_executor_check_transactions( txn_ctx );
    1238           0 :   if( FD_UNLIKELY( err!=FD_RUNTIME_EXECUTE_SUCCESS ) ) {
    1239           0 :     task_info->txn->flags = 0U;
    1240           0 :     task_info->exec_res   = err;
    1241           0 :     return;
    1242           0 :   }
    1243             : 
    1244             :   /* `load_and_execute_sanitized_transactions()` -> `validate_fees()` -> `validate_transaction_fee_payer()`
    1245             :      https://github.com/anza-xyz/agave/blob/ced98f1ebe73f7e9691308afa757323003ff744f/svm/src/transaction_processor.rs#L236-L249 */
    1246           0 :   err = fd_executor_validate_transaction_fee_payer( txn_ctx );
    1247           0 :   if( FD_UNLIKELY( err!=FD_RUNTIME_EXECUTE_SUCCESS ) ) {
    1248           0 :     task_info->txn->flags = 0U;
    1249           0 :     task_info->exec_res   = err;
    1250           0 :     return;
    1251           0 :   }
    1252             : 
    1253             :   /* https://github.com/anza-xyz/agave/blob/ced98f1ebe73f7e9691308afa757323003ff744f/svm/src/transaction_processor.rs#L284-L296 */
    1254           0 :   err = fd_executor_load_transaction_accounts( txn_ctx );
    1255           0 :   if( FD_UNLIKELY( err!=FD_RUNTIME_EXECUTE_SUCCESS ) ) {
    1256             :     /* Regardless of whether transaction accounts were loaded successfully, the transaction is
    1257             :         included in the block and transaction fees are collected.
    1258             :         https://github.com/anza-xyz/agave/blob/v2.1.6/svm/src/transaction_processor.rs#L341-L357 */
    1259           0 :     task_info->txn->flags |= FD_TXN_P_FLAGS_FEES_ONLY;
    1260           0 :     task_info->exec_res    = err;
    1261             : 
    1262             :     /* If the transaction fails to load, the "rollback" accounts will include one of the following:
    1263             :         1. Nonce account only
    1264             :         2. Fee payer only
    1265             :         3. Nonce account + fee payer
    1266             : 
    1267             :         Because the cost tracker uses the loaded account data size in block cost calculations, we need to
    1268             :         make sure our calculated loaded accounts data size is conformant with Agave's.
    1269             :         https://github.com/anza-xyz/agave/blob/v2.1.14/runtime/src/bank.rs#L4116
    1270             : 
    1271             :         In any case, we should always add the dlen of the fee payer. */
    1272           0 :     task_info->txn_ctx->loaded_accounts_data_size = task_info->txn_ctx->accounts[FD_FEE_PAYER_TXN_IDX].vt->get_data_len( &task_info->txn_ctx->accounts[FD_FEE_PAYER_TXN_IDX] );
    1273             : 
    1274             :     /* Special case handling for if a nonce account is present in the transaction. */
    1275           0 :     if( task_info->txn_ctx->nonce_account_idx_in_txn!=ULONG_MAX ) {
    1276             :       /* If the nonce account is not the fee payer, then we separately add the dlen of the nonce account. Otherwise, we would
    1277             :           be double counting the dlen of the fee payer. */
    1278           0 :       if( task_info->txn_ctx->nonce_account_idx_in_txn!=FD_FEE_PAYER_TXN_IDX ) {
    1279           0 :         task_info->txn_ctx->loaded_accounts_data_size += task_info->txn_ctx->rollback_nonce_account->vt->get_data_len( task_info->txn_ctx->rollback_nonce_account );
    1280           0 :       }
    1281           0 :     }
    1282           0 :   }
    1283             : 
    1284             :   /*
    1285             :      The fee payer and the nonce account will be stored and hashed so
    1286             :      long as the transaction landed on chain, or, in Agave terminology,
    1287             :      the transaction was processed.
    1288             :      https://github.com/anza-xyz/agave/blob/v2.1.1/runtime/src/account_saver.rs#L72
    1289             : 
    1290             :      A transaction lands on chain in one of two ways:
    1291             :      (1) Passed fee validation and loaded accounts.
    1292             :      (2) Passed fee validation and failed to load accounts and the enable_transaction_loading_failure_fees feature is enabled as per
    1293             :          SIMD-0082 https://github.com/anza-xyz/feature-gate-tracker/issues/52
    1294             : 
    1295             :      So, at this point, the transaction is committable.
    1296             :    */
    1297           0 : }
    1298             : 
    1299             : /* fd_runtime_finalize_txn is a helper used by the non-tpool transaction
    1300             :    executor to finalize borrowed account changes back into funk. It also
    1301             :    handles txncache insertion and updates to the vote/stake cache.
    1302             :    TODO: This function should probably be moved to fd_executor.c. */
    1303             : 
    1304             : void
    1305             : fd_runtime_finalize_txn( fd_funk_t *                  funk,
    1306             :                          fd_funk_txn_t *              funk_txn,
    1307             :                          fd_execute_txn_task_info_t * task_info,
    1308             :                          fd_spad_t *                  finalize_spad,
    1309           0 :                          fd_bank_t *                  bank ) {
    1310             : 
    1311             :   /* for all accounts, if account->is_verified==true, propagate update
    1312             :      to cache entry. */
    1313             : 
    1314             :   /* Store transaction info including logs */
    1315             :   // fd_runtime_finalize_txns_update_blockstore_meta( slot_ctx, task_info, 1UL );
    1316             : 
    1317             :   /* Collect fees */
    1318             : 
    1319           0 :   FD_ATOMIC_FETCH_AND_ADD( fd_bank_execution_fees_modify( bank ), task_info->txn_ctx->execution_fee );
    1320           0 :   FD_ATOMIC_FETCH_AND_ADD( fd_bank_priority_fees_modify( bank ), task_info->txn_ctx->priority_fee );
    1321             : 
    1322           0 :   fd_exec_txn_ctx_t * txn_ctx      = task_info->txn_ctx;
    1323           0 :   int                 exec_txn_err = task_info->exec_res;
    1324             : 
    1325           0 :   FD_ATOMIC_FETCH_AND_ADD( fd_bank_signature_count_modify( bank ), txn_ctx->txn_descriptor->signature_cnt );
    1326             : 
    1327           0 :   if( FD_UNLIKELY( exec_txn_err ) ) {
    1328             : 
    1329             :     /* Save the fee_payer. Everything but the fee balance should be reset.
    1330             :        TODO: an optimization here could be to use a dirty flag in the
    1331             :        borrowed account. If the borrowed account data has been changed in
    1332             :        any way, then the full account can be rolled back as it is done now.
    1333             :        However, most of the time the account data is not changed, and only
    1334             :        the lamport balance has to change. */
    1335             : 
    1336             :     /* With nonce account rollbacks, there are three cases:
    1337             :        1. No nonce account in the transaction
    1338             :        2. Nonce account is the fee payer
    1339             :        3. Nonce account is not the fee payer
    1340             : 
    1341             :        We should always rollback the nonce account first. Note that the nonce account may be the fee payer (case 2). */
    1342           0 :     if( txn_ctx->nonce_account_idx_in_txn!=ULONG_MAX ) {
    1343           0 :       fd_txn_account_save( txn_ctx->rollback_nonce_account, funk, funk_txn, txn_ctx->spad_wksp );
    1344           0 :     }
    1345             : 
    1346             :     /* Now, we must only save the fee payer if the nonce account was not the fee payer (because that was already saved above) */
    1347           0 :     if( FD_LIKELY( txn_ctx->nonce_account_idx_in_txn!=FD_FEE_PAYER_TXN_IDX ) ) {
    1348           0 :       fd_txn_account_save( txn_ctx->rollback_fee_payer_account, funk, funk_txn, txn_ctx->spad_wksp );
    1349           0 :     }
    1350           0 :   } else {
    1351             : 
    1352           0 :     int dirty_vote_acc  = txn_ctx->dirty_vote_acc;
    1353           0 :     int dirty_stake_acc = txn_ctx->dirty_stake_acc;
    1354             : 
    1355           0 :     for( ushort i=0; i<txn_ctx->accounts_cnt; i++ ) {
    1356             :       /* We are only interested in saving writable accounts and the fee
    1357             :          payer account. */
    1358           0 :       if( !fd_exec_txn_ctx_account_is_writable_idx( txn_ctx, i ) && i!=FD_FEE_PAYER_TXN_IDX ) {
    1359           0 :         continue;
    1360           0 :       }
    1361             : 
    1362           0 :       fd_txn_account_t * acc_rec = &txn_ctx->accounts[i];
    1363             : 
    1364           0 :       if( dirty_vote_acc && 0==memcmp( acc_rec->vt->get_owner( acc_rec ), &fd_solana_vote_program_id, sizeof(fd_pubkey_t) ) ) {
    1365           0 :         fd_vote_store_account( acc_rec, bank );
    1366           0 :         FD_SPAD_FRAME_BEGIN( finalize_spad ) {
    1367           0 :           int err;
    1368           0 :           fd_vote_state_versioned_t * vsv = fd_bincode_decode_spad(
    1369           0 :               vote_state_versioned, finalize_spad,
    1370           0 :               acc_rec->vt->get_data( acc_rec ),
    1371           0 :               acc_rec->vt->get_data_len( acc_rec ),
    1372           0 :               &err );
    1373           0 :           if( FD_UNLIKELY( err ) ) {
    1374           0 :             FD_LOG_WARNING(( "failed to decode vote state versioned" ));
    1375           0 :             continue;
    1376           0 :           }
    1377             : 
    1378           0 :           fd_vote_block_timestamp_t const * ts = NULL;
    1379           0 :           switch( vsv->discriminant ) {
    1380           0 :           case fd_vote_state_versioned_enum_v0_23_5:
    1381           0 :             ts = &vsv->inner.v0_23_5.last_timestamp;
    1382           0 :             break;
    1383           0 :           case fd_vote_state_versioned_enum_v1_14_11:
    1384           0 :             ts = &vsv->inner.v1_14_11.last_timestamp;
    1385           0 :             break;
    1386           0 :           case fd_vote_state_versioned_enum_current:
    1387           0 :             ts = &vsv->inner.current.last_timestamp;
    1388           0 :             break;
    1389           0 :           default:
    1390           0 :             __builtin_unreachable();
    1391           0 :           }
    1392             : 
    1393           0 :           fd_vote_record_timestamp_vote_with_slot( acc_rec->pubkey,
    1394           0 :                                                    ts->timestamp,
    1395           0 :                                                    ts->slot,
    1396           0 :                                                    bank );
    1397           0 :         } FD_SPAD_FRAME_END;
    1398           0 :       }
    1399             : 
    1400           0 :       if( dirty_stake_acc && 0==memcmp( acc_rec->vt->get_owner( acc_rec ), &fd_solana_stake_program_id, sizeof(fd_pubkey_t) ) ) {
    1401             :         // TODO: does this correctly handle stake account close?
    1402           0 :         fd_store_stake_delegation( acc_rec, bank );
    1403           0 :       }
    1404             : 
    1405           0 :       fd_txn_account_save( &txn_ctx->accounts[i], funk, funk_txn, txn_ctx->spad_wksp );
    1406           0 :     }
    1407           0 :   }
    1408             : 
    1409           0 :   int is_vote = fd_txn_is_simple_vote_transaction( txn_ctx->txn_descriptor, txn_ctx->_txn_raw->raw );
    1410           0 :   if( !is_vote ){
    1411           0 :     ulong * nonvote_txn_count = fd_bank_nonvote_txn_count_modify( bank );
    1412           0 :     FD_ATOMIC_FETCH_AND_ADD(nonvote_txn_count, 1);
    1413             : 
    1414           0 :     if( FD_UNLIKELY( exec_txn_err ) ){
    1415           0 :       ulong * nonvote_failed_txn_count = fd_bank_nonvote_failed_txn_count_modify( bank );
    1416           0 :       FD_ATOMIC_FETCH_AND_ADD( nonvote_failed_txn_count, 1 );
    1417           0 :     }
    1418           0 :   } else {
    1419           0 :     if( FD_UNLIKELY( exec_txn_err ) ){
    1420           0 :       ulong * failed_txn_count = fd_bank_failed_txn_count_modify( bank );
    1421           0 :       FD_ATOMIC_FETCH_AND_ADD( failed_txn_count, 1 );
    1422           0 :     }
    1423           0 :   }
    1424             : 
    1425           0 :   ulong * total_compute_units_used = fd_bank_total_compute_units_used_modify( bank );
    1426           0 :   FD_ATOMIC_FETCH_AND_ADD( total_compute_units_used, txn_ctx->compute_unit_limit - txn_ctx->compute_meter );
    1427             : 
    1428           0 : }
    1429             : 
    1430             : /* fd_runtime_prepare_and_execute_txn is the main entrypoint into the executor
    1431             :    tile. At this point, the slot and epoch context should NOT be changed.
    1432             :    NOTE: The executor tile doesn't exist yet. */
    1433             : 
    1434             : static int
    1435             : fd_runtime_prepare_and_execute_txn( fd_exec_slot_ctx_t const *   slot_ctx,
    1436             :                                     fd_txn_p_t *                 txn,
    1437             :                                     fd_execute_txn_task_info_t * task_info,
    1438             :                                     fd_spad_t *                  exec_spad,
    1439           0 :                                     fd_capture_ctx_t *           capture_ctx ) {
    1440             : 
    1441           0 :   uchar dump_txn = !!( capture_ctx && slot_ctx->slot >= capture_ctx->dump_proto_start_slot && capture_ctx->dump_txn_to_pb );
    1442           0 :   int res = 0;
    1443             : 
    1444           0 :   fd_exec_txn_ctx_t * txn_ctx     = task_info->txn_ctx;
    1445           0 :   task_info->exec_res             = -1;
    1446           0 :   task_info->txn                  = txn;
    1447           0 :   fd_txn_t const * txn_descriptor = (fd_txn_t const *) txn->_;
    1448           0 :   task_info->txn_ctx->spad        = exec_spad;
    1449           0 :   task_info->txn_ctx->spad_wksp   = fd_wksp_containing( exec_spad );
    1450             : 
    1451           0 :   fd_rawtxn_b_t raw_txn = { .raw = txn->payload, .txn_sz = (ushort)txn->payload_sz };
    1452             : 
    1453           0 :   res = fd_execute_txn_prepare_start( slot_ctx, txn_ctx, txn_descriptor, &raw_txn );
    1454           0 :   if( FD_UNLIKELY( res ) ) {
    1455           0 :     txn->flags = 0U;
    1456           0 :     return -1;
    1457           0 :   }
    1458             : 
    1459           0 :   task_info->txn_ctx->capture_ctx = capture_ctx;
    1460             : 
    1461           0 :   if( FD_UNLIKELY( fd_executor_txn_verify( txn_ctx )!=0 ) ) {
    1462           0 :     FD_LOG_WARNING(( "sigverify failed: %s", FD_BASE58_ENC_64_ALLOCA( (uchar *)txn_ctx->_txn_raw->raw+txn_ctx->txn_descriptor->signature_off ) ));
    1463           0 :     task_info->txn->flags = 0U;
    1464           0 :     task_info->exec_res   = FD_RUNTIME_TXN_ERR_SIGNATURE_FAILURE;
    1465           0 :   }
    1466             : 
    1467           0 :   fd_runtime_pre_execute_check( task_info, dump_txn ); /* TODO: check if this will be called from executor tile or replay tile */
    1468           0 :   if( FD_UNLIKELY( !( task_info->txn->flags & FD_TXN_P_FLAGS_SANITIZE_SUCCESS ) ) ) {
    1469           0 :     res  = task_info->exec_res;
    1470           0 :     return -1;
    1471           0 :   }
    1472             : 
    1473             :   /* Execute */
    1474           0 :   task_info->txn->flags |= FD_TXN_P_FLAGS_EXECUTE_SUCCESS;
    1475           0 :   task_info->exec_res    = fd_execute_txn( task_info );
    1476             : 
    1477           0 :   if( task_info->exec_res==0 ) {
    1478           0 :     fd_txn_reclaim_accounts( task_info->txn_ctx );
    1479           0 :   }
    1480             : 
    1481           0 :   return res;
    1482             : 
    1483           0 : }
    1484             : 
    1485             : static void
    1486             : fd_runtime_prepare_execute_finalize_txn_task( void * tpool,
    1487             :                                               ulong  t0,
    1488             :                                               ulong  t1,
    1489             :                                               void * args,
    1490             :                                               void * reduce,
    1491             :                                               ulong  stride FD_PARAM_UNUSED,
    1492             :                                               ulong  l0     FD_PARAM_UNUSED,
    1493             :                                               ulong  l1     FD_PARAM_UNUSED,
    1494             :                                               ulong  m0     FD_PARAM_UNUSED,
    1495             :                                               ulong  m1     FD_PARAM_UNUSED,
    1496             :                                               ulong  n0     FD_PARAM_UNUSED,
    1497           0 :                                               ulong  n1     FD_PARAM_UNUSED ) {
    1498             : 
    1499           0 :   fd_exec_slot_ctx_t *         slot_ctx     = (fd_exec_slot_ctx_t *)tpool;
    1500           0 :   fd_capture_ctx_t *           capture_ctx  = (fd_capture_ctx_t *)t0;
    1501           0 :   fd_txn_p_t *                 txn          = (fd_txn_p_t *)t1;
    1502           0 :   fd_execute_txn_task_info_t * task_info    = (fd_execute_txn_task_info_t *)args;
    1503           0 :   fd_spad_t *                  exec_spad    = (fd_spad_t *)reduce;
    1504             : 
    1505           0 :   fd_runtime_prepare_and_execute_txn( slot_ctx,
    1506           0 :                                       txn,
    1507           0 :                                       task_info,
    1508           0 :                                       exec_spad,
    1509           0 :                                       capture_ctx );
    1510             : 
    1511           0 :   if( FD_UNLIKELY( !( task_info->txn->flags & FD_TXN_P_FLAGS_EXECUTE_SUCCESS ) ) ) {
    1512           0 :     return;
    1513           0 :   }
    1514             : 
    1515           0 :   fd_runtime_finalize_txn( slot_ctx->funk, slot_ctx->funk_txn, task_info, task_info->txn_ctx->spad, slot_ctx->bank );
    1516           0 : }
    1517             : 
    1518             : /* fd_executor_txn_verify and fd_runtime_pre_execute_check are responisble
    1519             :    for the bulk of the pre-transaction execution checks in the runtime.
    1520             :    They aim to preserve the ordering present in the Agave client to match
    1521             :    parity in terms of error codes. Sigverify is kept separate from the rest
    1522             :    of the transaction checks for fuzzing convenience.
    1523             : 
    1524             :    For reference this is the general code path which contains all relevant
    1525             :    pre-transactions checks in the v2.0.x Agave client from upstream
    1526             :    to downstream is as follows:
    1527             : 
    1528             :    confirm_slot_entries() which calls verify_ticks() and
    1529             :    verify_transaction(). verify_transaction() calls verify_and_hash_message()
    1530             :    and verify_precompiles() which parallels fd_executor_txn_verify() and
    1531             :    fd_executor_verify_precompiles().
    1532             : 
    1533             :    process_entries() contains a duplicate account check which is part of
    1534             :    agave account lock acquiring. This is checked inline in
    1535             :    fd_runtime_pre_execute_check().
    1536             : 
    1537             :    load_and_execute_transactions() contains the function check_transactions().
    1538             :    This contains check_age() and check_status_cache() which is paralleled by
    1539             :    fd_check_transaction_age() and fd_executor_check_status_cache()
    1540             :    respectively.
    1541             : 
    1542             :    load_and_execute_sanitized_transactions() contains validate_fees()
    1543             :    which is responsible for executing the compute budget instructions,
    1544             :    validating the fee payer and collecting the fee. This is mirrored in
    1545             :    firedancer with fd_executor_compute_budget_program_execute_instructions()
    1546             :    and fd_executor_collect_fees(). load_and_execute_sanitized_transactions()
    1547             :    also checks the total data size of the accounts in load_accounts() and
    1548             :    validates the program accounts in load_transaction_accounts(). This
    1549             :    is paralled by fd_executor_load_transaction_accounts(). */
    1550             : 
    1551             : int
    1552             : fd_runtime_process_txns_in_microblock_stream( fd_exec_slot_ctx_t * slot_ctx,
    1553             :                                               fd_capture_ctx_t *   capture_ctx,
    1554             :                                               fd_txn_p_t *         txns,
    1555             :                                               ulong                txn_cnt,
    1556             :                                               fd_tpool_t *         tpool,
    1557             :                                               fd_spad_t * *        exec_spads,
    1558             :                                               ulong                exec_spad_cnt,
    1559             :                                               fd_spad_t *          runtime_spad,
    1560           0 :                                               fd_cost_tracker_t *  cost_tracker_opt ) {
    1561             : 
    1562           0 :   int res = 0;
    1563             : 
    1564           0 :   for( ulong i=0UL; i<txn_cnt; i++ ) {
    1565           0 :     txns[i].flags = FD_TXN_P_FLAGS_SANITIZE_SUCCESS;
    1566           0 :   }
    1567             : 
    1568           0 :   fd_execute_txn_task_info_t * task_infos = fd_spad_alloc( runtime_spad,
    1569           0 :                                                            alignof(fd_execute_txn_task_info_t),
    1570           0 :                                                            txn_cnt * sizeof(fd_execute_txn_task_info_t) );
    1571             : 
    1572           0 :   ulong curr_exec_idx = 0UL;
    1573           0 :   while( curr_exec_idx<txn_cnt ) {
    1574           0 :     ulong exec_idx_start = curr_exec_idx;
    1575             : 
    1576             :     // Push a new spad frame for each exec spad
    1577           0 :     for( ulong worker_idx=1UL; worker_idx<exec_spad_cnt; worker_idx++ ) {
    1578           0 :       fd_spad_push( exec_spads[ worker_idx ] );
    1579           0 :     }
    1580             : 
    1581           0 :     for( ulong worker_idx=1UL; worker_idx<exec_spad_cnt; worker_idx++ ) {
    1582           0 :       if( curr_exec_idx>=txn_cnt ) {
    1583           0 :         break;
    1584           0 :       }
    1585           0 :       if( !fd_tpool_worker_idle( tpool, worker_idx ) ) {
    1586           0 :         continue;
    1587           0 :       }
    1588             : 
    1589           0 :       task_infos[ curr_exec_idx ].spad    = exec_spads[ worker_idx ];
    1590           0 :       task_infos[ curr_exec_idx ].txn     = &txns[ curr_exec_idx ];
    1591           0 :       task_infos[ curr_exec_idx ].txn_ctx = fd_spad_alloc( task_infos[ curr_exec_idx ].spad,
    1592           0 :                                                            FD_EXEC_TXN_CTX_ALIGN,
    1593           0 :                                                            FD_EXEC_TXN_CTX_FOOTPRINT );
    1594           0 :       if( FD_UNLIKELY( !task_infos[ curr_exec_idx ].txn_ctx ) ) {
    1595           0 :         FD_LOG_ERR(( "failed to allocate txn ctx" ));
    1596           0 :       }
    1597             : 
    1598           0 :       fd_tpool_exec( tpool, worker_idx, fd_runtime_prepare_execute_finalize_txn_task,
    1599           0 :                      slot_ctx, (ulong)capture_ctx, (ulong)task_infos[curr_exec_idx].txn,
    1600           0 :                      &task_infos[ curr_exec_idx ], exec_spads[ worker_idx ], 0UL,
    1601           0 :                      0UL, 0UL, 0UL, 0UL, 0UL, 0UL );
    1602             : 
    1603           0 :       curr_exec_idx++;
    1604           0 :     }
    1605             : 
    1606             :     /* Wait for the workers to finish before we try to dispatch them a new task */
    1607           0 :     for( ulong worker_idx=1UL; worker_idx<exec_spad_cnt; worker_idx++ ) {
    1608           0 :       fd_tpool_wait( tpool, worker_idx );
    1609           0 :     }
    1610             : 
    1611             :     /* Verify cost tracker limits (only for offline replay)
    1612             :        https://github.com/anza-xyz/agave/blob/v2.2.0/ledger/src/blockstore_processor.rs#L284-L299 */
    1613           0 :     if( cost_tracker_opt!=NULL && FD_FEATURE_ACTIVE_BANK( slot_ctx->bank, apply_cost_tracker_during_replay ) ) {
    1614           0 :       for( ulong i=exec_idx_start; i<curr_exec_idx; i++ ) {
    1615             : 
    1616             :         /* Skip any transactions that were not processed */
    1617           0 :         fd_execute_txn_task_info_t const * task_info = &task_infos[ i ];
    1618           0 :         if( FD_UNLIKELY( !( task_info->txn->flags & FD_TXN_P_FLAGS_EXECUTE_SUCCESS ) ) ) continue;
    1619             : 
    1620           0 :         fd_exec_txn_ctx_t const * txn_ctx          = task_info->txn_ctx;
    1621           0 :         fd_transaction_cost_t     transaction_cost = fd_calculate_cost_for_executed_transaction( task_info->txn_ctx,
    1622           0 :                                                                                                  runtime_spad );
    1623             : 
    1624             :         /* https://github.com/anza-xyz/agave/blob/v2.2.0/ledger/src/blockstore_processor.rs#L302-L307 */
    1625           0 :         res = fd_cost_tracker_try_add( cost_tracker_opt, txn_ctx, &transaction_cost );
    1626           0 :         if( FD_UNLIKELY( res ) ) {
    1627           0 :           FD_LOG_WARNING(( "Block cost limits exceeded for slot %lu", slot_ctx->slot ));
    1628           0 :           break;
    1629           0 :         }
    1630           0 :       }
    1631           0 :     }
    1632             : 
    1633             :     // Pop the spad frame
    1634           0 :     for( ulong worker_idx=1UL; worker_idx<exec_spad_cnt; worker_idx++ ) {
    1635           0 :       fd_spad_pop( exec_spads[ worker_idx ] );
    1636           0 :     }
    1637             : 
    1638             :     /* If there was a error with cost tracker calculations, return the error */
    1639           0 :     if( FD_UNLIKELY( res ) ) return res;
    1640           0 :   }
    1641             : 
    1642           0 :   return 0;
    1643             : 
    1644           0 : }
    1645             : 
    1646             : /******************************************************************************/
    1647             : /* Epoch Boundary                                                             */
    1648             : /******************************************************************************/
    1649             : 
    1650             : /* Update the epoch bank stakes cache with the delegated stake values from the slot bank cache.
    1651             : The slot bank cache will have been accumulating this epoch, and now we are at an epoch boundary
    1652             : we can safely update the epoch stakes cache with the latest values.
    1653             : 
    1654             : In Solana, the stakes cache is updated after every transaction
    1655             :   (https://github.com/solana-labs/solana/blob/c091fd3da8014c0ef83b626318018f238f506435/runtime/src/bank.rs#L7587).
    1656             : As delegations have to warm up, the contents of the cache will not change inter-epoch. We can therefore update
    1657             : the cache only at epoch boundaries.
    1658             : 
    1659             : https://github.com/solana-labs/solana/blob/c091fd3da8014c0ef83b626318018f238f506435/runtime/src/stakes.rs#L65 */
    1660             : static void
    1661             : fd_update_stake_delegations( fd_exec_slot_ctx_t * slot_ctx,
    1662           0 :                              fd_epoch_info_t *    temp_info ) {
    1663             : 
    1664           0 :   fd_stakes_global_t * stakes = fd_bank_stakes_locking_modify( slot_ctx->bank );
    1665           0 :   fd_delegation_pair_t_mapnode_t * stake_delegations_pool = fd_stakes_stake_delegations_pool_join( stakes );
    1666           0 :   fd_delegation_pair_t_mapnode_t * stake_delegations_root = fd_stakes_stake_delegations_root_join( stakes );
    1667             : 
    1668             :   /* In one pass, iterate over all the new stake infos and insert the updated values into the epoch stakes cache
    1669             :       This assumes that there is enough memory pre-allocated for the stakes cache. */
    1670           0 :   for( ulong idx=temp_info->stake_infos_new_keys_start_idx; idx<temp_info->stake_infos_len; idx++ ) {
    1671             :     // Fetch and store the delegation associated with this stake account
    1672           0 :     fd_delegation_pair_t_mapnode_t key;
    1673           0 :     key.elem.account = temp_info->stake_infos[idx].account;
    1674           0 :     fd_delegation_pair_t_mapnode_t * entry = fd_delegation_pair_t_map_find( stake_delegations_pool, stake_delegations_root, &key );
    1675           0 :     if( FD_LIKELY( entry==NULL ) ) {
    1676           0 :       entry = fd_delegation_pair_t_map_acquire( stake_delegations_pool );
    1677           0 :       if( FD_UNLIKELY( !entry ) ) {
    1678           0 :         FD_TEST( 0 == fd_delegation_pair_t_map_verify( stake_delegations_pool, stake_delegations_root ) );
    1679           0 :         FD_LOG_CRIT(( "stake_delegations_pool full %lu", fd_delegation_pair_t_map_size( stake_delegations_pool, stake_delegations_root ) ));
    1680           0 :       }
    1681           0 :       entry->elem.account    = temp_info->stake_infos[idx].account;
    1682           0 :       entry->elem.delegation = temp_info->stake_infos[idx].stake.delegation;
    1683           0 :       fd_delegation_pair_t_map_insert( stake_delegations_pool, &stake_delegations_root, entry );
    1684           0 :     }
    1685           0 :   }
    1686             : 
    1687           0 :   fd_stakes_stake_delegations_pool_update( stakes, stake_delegations_pool );
    1688           0 :   fd_stakes_stake_delegations_root_update( stakes, stake_delegations_root );
    1689           0 :   fd_bank_stakes_end_locking_modify( slot_ctx->bank );
    1690             : 
    1691             :   /* At the epoch boundary, release all of the stake account keys
    1692             :      because at this point all of the changes have been applied to the
    1693             :      stakes. */
    1694           0 :   fd_account_keys_global_t * stake_account_keys = fd_bank_stake_account_keys_locking_modify( slot_ctx->bank );
    1695           0 :   fd_account_keys_pair_t_mapnode_t * account_keys_pool = fd_account_keys_account_keys_pool_join( stake_account_keys );
    1696           0 :   fd_account_keys_pair_t_mapnode_t * account_keys_root = fd_account_keys_account_keys_root_join( stake_account_keys );
    1697             : 
    1698           0 :   fd_account_keys_pair_t_map_release_tree( account_keys_pool, account_keys_root );
    1699           0 :   account_keys_root = NULL;
    1700             : 
    1701           0 :   fd_account_keys_account_keys_pool_update( stake_account_keys, account_keys_pool );
    1702           0 :   fd_account_keys_account_keys_root_update( stake_account_keys, account_keys_root );
    1703           0 :   fd_bank_stake_account_keys_end_locking_modify( slot_ctx->bank );
    1704           0 : }
    1705             : 
    1706             : /* Replace the stakes in T-2 (slot_ctx->slot_bank.epoch_stakes) by the stakes at T-1 (epoch_bank->next_epoch_stakes) */
    1707             : static void
    1708           0 : fd_update_epoch_stakes( fd_exec_slot_ctx_t * slot_ctx ) {
    1709             : 
    1710             :   /* Copy epoch_bank->next_epoch_stakes into slot_ctx->slot_bank.epoch_stakes */
    1711             : 
    1712           0 :   ulong total_sz = sizeof(fd_vote_accounts_global_t) +
    1713           0 :                    fd_vote_accounts_pair_global_t_map_footprint( 50000UL ) +
    1714           0 :                    4000 * 50000UL;
    1715             : 
    1716           0 :   fd_vote_accounts_global_t const * next_epoch_stakes = fd_bank_next_epoch_stakes_locking_query( slot_ctx->bank );
    1717             : 
    1718           0 :   fd_vote_accounts_global_t * epoch_stakes = fd_bank_epoch_stakes_locking_modify( slot_ctx->bank );
    1719           0 :   fd_memcpy( epoch_stakes, next_epoch_stakes, total_sz );
    1720           0 :   fd_bank_epoch_stakes_end_locking_modify( slot_ctx->bank );
    1721             : 
    1722           0 :   fd_bank_next_epoch_stakes_end_locking_query( slot_ctx->bank );
    1723             : 
    1724           0 : }
    1725             : 
    1726             : /* Copy epoch_bank->stakes.vote_accounts into epoch_bank->next_epoch_stakes. */
    1727             : static void
    1728           0 : fd_update_next_epoch_stakes( fd_exec_slot_ctx_t * slot_ctx ) {
    1729             : 
    1730             :   /* FIXME: This is technically not correct, since the vote accounts
    1731             :      could be laid out after the stake delegations from fd_stakes.
    1732             :      The correct solution is to split out the stake delgations from the
    1733             :      vote accounts in fd_stakes. */
    1734             : 
    1735             :   /* Copy epoch_ctx->epoch_bank->stakes.vote_accounts into epoch_bank->next_epoch_stakes */
    1736             : 
    1737           0 :   ulong total_sz = sizeof(fd_vote_accounts_global_t) +
    1738           0 :                    fd_vote_accounts_pair_global_t_map_footprint( 50000UL ) +
    1739           0 :                    4000 * 50000UL;
    1740             : 
    1741             : 
    1742           0 :   fd_stakes_global_t const *        stakes = fd_bank_stakes_locking_query( slot_ctx->bank );
    1743           0 :   fd_vote_accounts_global_t const * vote_stakes = &stakes->vote_accounts;
    1744             : 
    1745             : 
    1746           0 :   fd_vote_accounts_global_t * next_epoch_stakes = fd_bank_next_epoch_stakes_locking_modify( slot_ctx->bank );
    1747           0 :   fd_memcpy( next_epoch_stakes, vote_stakes, total_sz );
    1748           0 :   fd_bank_next_epoch_stakes_end_locking_modify( slot_ctx->bank );
    1749             : 
    1750           0 :   fd_bank_stakes_end_locking_query( slot_ctx->bank );
    1751           0 : }
    1752             : 
    1753             : /* Mimics `bank.new_target_program_account()`. Assumes `out_rec` is a modifiable record.
    1754             : 
    1755             :    From the calling context, `out_rec` points to a native program record (e.g. Config, ALUT native programs).
    1756             :    There should be enough space in `out_rec->data` to hold at least 36 bytes (the size of a BPF upgradeable
    1757             :    program account) when calling this function. The native program account's owner is set to the BPF loader
    1758             :    upgradeable program ID, and lamports are increased / deducted to contain the rent exempt minimum balance.
    1759             : 
    1760             :    https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L79-L95 */
    1761             : static int
    1762             : fd_new_target_program_account( fd_exec_slot_ctx_t * slot_ctx,
    1763             :                                fd_pubkey_t const *  target_program_data_address,
    1764           0 :                                fd_txn_account_t *   out_rec ) {
    1765             :   /* https://github.com/anza-xyz/agave/blob/v2.1.0/sdk/account/src/lib.rs#L471 */
    1766           0 :   out_rec->vt->set_rent_epoch( out_rec, 0UL );
    1767             : 
    1768             :   /* https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L86-L88 */
    1769           0 :   fd_bpf_upgradeable_loader_state_t state = {
    1770           0 :     .discriminant = fd_bpf_upgradeable_loader_state_enum_program,
    1771           0 :     .inner = {
    1772           0 :       .program = {
    1773           0 :         .programdata_address = *target_program_data_address,
    1774           0 :       }
    1775           0 :     }
    1776           0 :   };
    1777             : 
    1778             :   /* https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L89-L90 */
    1779           0 :   fd_rent_t const * rent = fd_bank_rent_query( slot_ctx->bank );
    1780           0 :   if( FD_UNLIKELY( rent==NULL ) ) {
    1781           0 :     return -1;
    1782           0 :   }
    1783             : 
    1784           0 :   out_rec->vt->set_lamports( out_rec, fd_rent_exempt_minimum_balance( rent, SIZE_OF_PROGRAM ) );
    1785           0 :   fd_bincode_encode_ctx_t ctx = {
    1786           0 :     .data    = out_rec->vt->get_data_mut( out_rec ),
    1787           0 :     .dataend = out_rec->vt->get_data_mut( out_rec ) + SIZE_OF_PROGRAM,
    1788           0 :   };
    1789             : 
    1790             :   /* https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L91-L9 */
    1791           0 :   int err = fd_bpf_upgradeable_loader_state_encode( &state, &ctx );
    1792           0 :   if( FD_UNLIKELY( err ) ) {
    1793           0 :     return err;
    1794           0 :   }
    1795           0 :   out_rec->vt->set_owner( out_rec, &fd_solana_bpf_loader_upgradeable_program_id );
    1796             : 
    1797             :   /* https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L93-L94 */
    1798           0 :   out_rec->vt->set_executable( out_rec, 1 );
    1799           0 :   return FD_RUNTIME_EXECUTE_SUCCESS;
    1800           0 : }
    1801             : 
    1802             : /* Mimics `bank.new_target_program_data_account()`. Assumes `new_target_program_data_account` is a modifiable record.
    1803             :    `config_upgrade_authority_address` may be NULL.
    1804             : 
    1805             :    This function uses an existing buffer account `buffer_acc_rec` to set the program data account data for a core
    1806             :    program BPF migration. Sets the lamports and data fields of `new_target_program_data_account` based on the
    1807             :    ELF data length, and sets the owner to the BPF loader upgradeable program ID.
    1808             : 
    1809             :    https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L97-L153 */
    1810             : static int
    1811             : fd_new_target_program_data_account( fd_exec_slot_ctx_t * slot_ctx,
    1812             :                                     fd_pubkey_t *        config_upgrade_authority_address,
    1813             :                                     fd_txn_account_t *   buffer_acc_rec,
    1814             :                                     fd_txn_account_t *   new_target_program_data_account,
    1815           0 :                                     fd_spad_t *          runtime_spad ) {
    1816             : 
    1817           0 :   FD_SPAD_FRAME_BEGIN( runtime_spad ) {
    1818             : 
    1819             :   /* https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L113-L116 */
    1820           0 :   int err;
    1821           0 :   fd_bpf_upgradeable_loader_state_t * state = fd_bincode_decode_spad(
    1822           0 :       bpf_upgradeable_loader_state, runtime_spad,
    1823           0 :       buffer_acc_rec->vt->get_data( buffer_acc_rec ),
    1824           0 :       buffer_acc_rec->vt->get_data_len( buffer_acc_rec ),
    1825           0 :       &err );
    1826           0 :   if( FD_UNLIKELY( err ) ) return err;
    1827             : 
    1828           0 :   if( FD_UNLIKELY( !fd_bpf_upgradeable_loader_state_is_buffer( state ) ) ) {
    1829           0 :     return -1;
    1830           0 :   }
    1831             : 
    1832             :   /* https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L118-L125 */
    1833           0 :   if( config_upgrade_authority_address!=NULL ) {
    1834           0 :     if( FD_UNLIKELY( state->inner.buffer.authority_address==NULL ||
    1835           0 :                      memcmp( config_upgrade_authority_address, state->inner.buffer.authority_address, sizeof(fd_pubkey_t) ) ) ) {
    1836           0 :       return -1;
    1837           0 :     }
    1838           0 :   }
    1839             : 
    1840             :   /* https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L127-L132 */
    1841           0 :   fd_rent_t const * rent = fd_bank_rent_query( slot_ctx->bank );
    1842           0 :   if( FD_UNLIKELY( rent==NULL ) ) {
    1843           0 :     return -1;
    1844           0 :   }
    1845             : 
    1846           0 :   const uchar * elf = buffer_acc_rec->vt->get_data( buffer_acc_rec ) + BUFFER_METADATA_SIZE;
    1847           0 :   ulong space = PROGRAMDATA_METADATA_SIZE - BUFFER_METADATA_SIZE + buffer_acc_rec->vt->get_data_len( buffer_acc_rec );
    1848           0 :   ulong lamports = fd_rent_exempt_minimum_balance( rent, space );
    1849             : 
    1850             :   /* https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L134-L137 */
    1851           0 :   fd_bpf_upgradeable_loader_state_t programdata_metadata = {
    1852           0 :     .discriminant = fd_bpf_upgradeable_loader_state_enum_program_data,
    1853           0 :     .inner = {
    1854           0 :       .program_data = {
    1855           0 :         .slot = slot_ctx->slot,
    1856           0 :         .upgrade_authority_address = config_upgrade_authority_address
    1857           0 :       }
    1858           0 :     }
    1859           0 :   };
    1860             : 
    1861             :   /* https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L139-L144 */
    1862           0 :   new_target_program_data_account->vt->set_lamports( new_target_program_data_account, lamports );
    1863           0 :   fd_bincode_encode_ctx_t encode_ctx = {
    1864           0 :     .data    = new_target_program_data_account->vt->get_data_mut( new_target_program_data_account ),
    1865           0 :     .dataend = new_target_program_data_account->vt->get_data_mut( new_target_program_data_account ) + PROGRAMDATA_METADATA_SIZE,
    1866           0 :   };
    1867           0 :   err = fd_bpf_upgradeable_loader_state_encode( &programdata_metadata, &encode_ctx );
    1868           0 :   if( FD_UNLIKELY( err ) ) {
    1869           0 :     return err;
    1870           0 :   }
    1871           0 :   new_target_program_data_account->vt->set_owner( new_target_program_data_account, &fd_solana_bpf_loader_upgradeable_program_id );
    1872             : 
    1873             :   /* Copy the ELF data over
    1874             :      https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L145 */
    1875           0 :   fd_memcpy( new_target_program_data_account->vt->get_data_mut( new_target_program_data_account ) + PROGRAMDATA_METADATA_SIZE, elf, buffer_acc_rec->vt->get_data_len( buffer_acc_rec ) - BUFFER_METADATA_SIZE );
    1876             : 
    1877           0 :   return FD_RUNTIME_EXECUTE_SUCCESS;
    1878             : 
    1879           0 :   } FD_SPAD_FRAME_END;
    1880           0 : }
    1881             : 
    1882             : /* Mimics `migrate_builtin_to_core_bpf()`. The arguments map as follows:
    1883             :     - builtin_program_id: builtin_program_id
    1884             :     - config
    1885             :       - source_buffer_address: source_buffer_address
    1886             :       - migration_target
    1887             :         - Builtin: !stateless
    1888             :         - Stateless: stateless
    1889             :       - upgrade_authority_address: upgrade_authority_address
    1890             :   https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L235-L318 */
    1891             : static void
    1892             : fd_migrate_builtin_to_core_bpf( fd_exec_slot_ctx_t * slot_ctx,
    1893             :                                 fd_pubkey_t *        upgrade_authority_address,
    1894             :                                 fd_pubkey_t const *  builtin_program_id,
    1895             :                                 fd_pubkey_t const *  source_buffer_address,
    1896             :                                 uchar                stateless,
    1897           0 :                                 fd_spad_t *          runtime_spad ) {
    1898           0 :   int err;
    1899             : 
    1900             :   /* https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L242-L243
    1901             : 
    1902             :      The below logic is used to obtain a `TargetBuiltin` account. There are three fields of `TargetBuiltin` returned:
    1903             :       - target.program_address: builtin_program_id
    1904             :       - target.program_account:
    1905             :           - if stateless: an AccountSharedData::default() (i.e. system program id, 0 lamports, 0 data, non-executable, system program owner)
    1906             :           - if NOT stateless: the existing account (for us its called `target_program_account`)
    1907             :       - target.program_data_address: `target_program_data_address` for us, derived below. */
    1908             : 
    1909             :   /* These checks will fail if the core program has already been migrated to BPF, since the account will exist + the program owner
    1910             :      will no longer be the native loader.
    1911             :      https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/target_builtin.rs#L23-L50 */
    1912           0 :   FD_TXN_ACCOUNT_DECL( target_program_account );
    1913           0 :   uchar program_exists = ( fd_txn_account_init_from_funk_readonly( target_program_account, builtin_program_id, slot_ctx->funk, slot_ctx->funk_txn )==FD_ACC_MGR_SUCCESS );
    1914           0 :   if( !stateless ) {
    1915             :     /* The program account should exist.
    1916             :        https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/target_builtin.rs#L30-L33 */
    1917           0 :     if( FD_UNLIKELY( !program_exists ) ) {
    1918           0 :       FD_LOG_WARNING(( "Builtin program %s does not exist, skipping migration...", FD_BASE58_ENC_32_ALLOCA( builtin_program_id ) ));
    1919           0 :       return;
    1920           0 :     }
    1921             : 
    1922             :     /* The program account should be owned by the native loader.
    1923             :        https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/target_builtin.rs#L35-L38 */
    1924           0 :     if( FD_UNLIKELY( memcmp( target_program_account->vt->get_owner( target_program_account ), fd_solana_native_loader_id.uc, sizeof(fd_pubkey_t) ) ) ) {
    1925           0 :       FD_LOG_WARNING(( "Builtin program %s is not owned by the native loader, skipping migration...", FD_BASE58_ENC_32_ALLOCA( builtin_program_id ) ));
    1926           0 :       return;
    1927           0 :     }
    1928           0 :   } else {
    1929             :     /* The program account should _not_ exist.
    1930             :        https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/target_builtin.rs#L42-L46 */
    1931           0 :     if( FD_UNLIKELY( program_exists ) ) {
    1932           0 :       FD_LOG_WARNING(( "Stateless program %s already exists, skipping migration...", FD_BASE58_ENC_32_ALLOCA( builtin_program_id ) ));
    1933           0 :       return;
    1934           0 :     }
    1935           0 :   }
    1936             : 
    1937             :   /* The program data account should not exist.
    1938             :      https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/target_builtin.rs#L52-L62 */
    1939           0 :   uint custom_err = UINT_MAX;
    1940           0 :   fd_pubkey_t target_program_data_address[ 1UL ];
    1941           0 :   uchar * seeds[ 1UL ];
    1942           0 :   seeds[ 0UL ]    = (uchar *)builtin_program_id;
    1943           0 :   ulong seed_sz   = sizeof(fd_pubkey_t);
    1944           0 :   uchar bump_seed = 0;
    1945           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 );
    1946           0 :   if( FD_UNLIKELY( err ) ) {
    1947             :     /* TODO: We should handle these errors more gracefully instead of just killing the client. */
    1948           0 :     FD_LOG_ERR(( "Unable to find a viable program address bump seed" )); // Solana panics, error code is undefined
    1949           0 :     return;
    1950           0 :   }
    1951           0 :   FD_TXN_ACCOUNT_DECL( program_data_account );
    1952           0 :   if( FD_UNLIKELY( fd_txn_account_init_from_funk_readonly( program_data_account, target_program_data_address, slot_ctx->funk, slot_ctx->funk_txn )==FD_ACC_MGR_SUCCESS ) ) {
    1953           0 :     FD_LOG_WARNING(( "Program data account %s already exists, skipping migration...", FD_BASE58_ENC_32_ALLOCA( target_program_data_address ) ));
    1954           0 :     return;
    1955           0 :   }
    1956             : 
    1957             :   /* https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L244
    1958             : 
    1959             :      Obtains a `SourceBuffer` account. There are two fields returned:
    1960             :       - source.buffer_address: source_buffer_address
    1961             :       - source.buffer_account: the existing buffer account */
    1962             : 
    1963             :   /* The buffer account should exist.
    1964             :      https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/source_buffer.rs#L26-L29 */
    1965           0 :   FD_TXN_ACCOUNT_DECL( source_buffer_account );
    1966           0 :   if( FD_UNLIKELY( fd_txn_account_init_from_funk_mutable( source_buffer_account, source_buffer_address, slot_ctx->funk, slot_ctx->funk_txn, 0, 0UL )!=FD_ACC_MGR_SUCCESS ) ) {
    1967           0 :     FD_LOG_WARNING(( "Buffer account %s does not exist, skipping migration...", FD_BASE58_ENC_32_ALLOCA( source_buffer_address ) ));
    1968           0 :     return;
    1969           0 :   }
    1970             : 
    1971             :   /* The buffer account should be owned by the upgradeable loader.
    1972             :      https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/source_buffer.rs#L31-L34 */
    1973           0 :   if( FD_UNLIKELY( memcmp( source_buffer_account->vt->get_owner( source_buffer_account ), fd_solana_bpf_loader_upgradeable_program_id.uc, sizeof(fd_pubkey_t) ) ) ) {
    1974           0 :     FD_LOG_WARNING(( "Buffer account %s is not owned by the upgradeable loader, skipping migration...", FD_BASE58_ENC_32_ALLOCA( source_buffer_address ) ));
    1975           0 :     return;
    1976           0 :   }
    1977             : 
    1978             :   /* The buffer account should have the correct state. We already check the buffer account state in `fd_new_target_program_data_account`,
    1979             :       so we can skip the checks here.
    1980             :      https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/source_buffer.rs#L37-L47 */
    1981             : 
    1982             :   /* This check is done a bit prematurely because we calculate the previous account state's lamports. We use 0 for starting lamports
    1983             :      for stateless accounts because they don't yet exist.
    1984             : 
    1985             :      https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L277-L280 */
    1986           0 :   ulong lamports_to_burn = ( stateless ? 0UL : target_program_account->vt->get_lamports( target_program_account ) ) + source_buffer_account->vt->get_lamports( source_buffer_account );
    1987             : 
    1988             :   /* Start a funk write txn */
    1989           0 :   fd_funk_txn_t * parent_txn = slot_ctx->funk_txn;
    1990           0 :   fd_funk_txn_xid_t migration_xid = fd_funk_generate_xid();
    1991           0 :   fd_funk_txn_start_write( slot_ctx->funk );
    1992           0 :   slot_ctx->funk_txn = fd_funk_txn_prepare( slot_ctx->funk, slot_ctx->funk_txn, &migration_xid, 0UL );
    1993           0 :   fd_funk_txn_end_write( slot_ctx->funk );
    1994             : 
    1995             :   /* Attempt serialization of program account. If the program is stateless, we want to create the account. Otherwise,
    1996             :      we want a writable handle to modify the existing account.
    1997             :      https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L246-L249 */
    1998           0 :   FD_TXN_ACCOUNT_DECL( new_target_program_account );
    1999           0 :   err = fd_txn_account_init_from_funk_mutable( new_target_program_account, builtin_program_id, slot_ctx->funk, slot_ctx->funk_txn, stateless, SIZE_OF_PROGRAM );
    2000           0 :   if( FD_UNLIKELY( err ) ) {
    2001           0 :     FD_LOG_WARNING(( "Builtin program ID %s does not exist", FD_BASE58_ENC_32_ALLOCA( builtin_program_id ) ));
    2002           0 :     goto fail;
    2003           0 :   }
    2004           0 :   new_target_program_account->vt->set_data_len( new_target_program_account, SIZE_OF_PROGRAM );
    2005           0 :   new_target_program_account->vt->set_slot( new_target_program_account, slot_ctx->slot );
    2006             : 
    2007             :   /* Create a new target program account. This modifies the existing record. */
    2008           0 :   err = fd_new_target_program_account( slot_ctx, target_program_data_address, new_target_program_account );
    2009           0 :   if( FD_UNLIKELY( err ) ) {
    2010           0 :     FD_LOG_WARNING(( "Failed to write new program state to %s", FD_BASE58_ENC_32_ALLOCA( builtin_program_id ) ));
    2011           0 :     goto fail;
    2012           0 :   }
    2013             : 
    2014           0 :   fd_txn_account_mutable_fini( new_target_program_account, slot_ctx->funk, slot_ctx->funk_txn );
    2015             : 
    2016             :   /* Create a new target program data account. */
    2017           0 :   ulong new_target_program_data_account_sz = PROGRAMDATA_METADATA_SIZE - BUFFER_METADATA_SIZE + source_buffer_account->vt->get_data_len( source_buffer_account );
    2018           0 :   FD_TXN_ACCOUNT_DECL( new_target_program_data_account );
    2019           0 :   err = fd_txn_account_init_from_funk_mutable( new_target_program_data_account,
    2020           0 :                                                target_program_data_address,
    2021           0 :                                                slot_ctx->funk,
    2022           0 :                                                slot_ctx->funk_txn,
    2023           0 :                                                1,
    2024           0 :                                                new_target_program_data_account_sz );
    2025           0 :   if( FD_UNLIKELY( err ) ) {
    2026           0 :     FD_LOG_WARNING(( "Failed to create new program data account to %s", FD_BASE58_ENC_32_ALLOCA( target_program_data_address ) ));
    2027           0 :     goto fail;
    2028           0 :   }
    2029           0 :   new_target_program_data_account->vt->set_data_len( new_target_program_data_account, new_target_program_data_account_sz );
    2030           0 :   new_target_program_data_account->vt->set_slot( new_target_program_data_account, slot_ctx->slot );
    2031             : 
    2032           0 :   err = fd_new_target_program_data_account( slot_ctx,
    2033           0 :                                             upgrade_authority_address,
    2034           0 :                                             source_buffer_account,
    2035           0 :                                             new_target_program_data_account,
    2036           0 :                                             runtime_spad );
    2037           0 :   if( FD_UNLIKELY( err ) ) {
    2038           0 :     FD_LOG_WARNING(( "Failed to write new program data state to %s", FD_BASE58_ENC_32_ALLOCA( target_program_data_address ) ));
    2039           0 :     goto fail;
    2040           0 :   }
    2041             : 
    2042           0 :   fd_txn_account_mutable_fini( new_target_program_data_account, slot_ctx->funk, slot_ctx->funk_txn );
    2043             : 
    2044             :   /* Deploy the new target Core BPF program.
    2045             :      https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L268-L271 */
    2046           0 :   err = fd_directly_invoke_loader_v3_deploy( slot_ctx,
    2047           0 :                                              new_target_program_data_account->vt->get_data( new_target_program_data_account ) + PROGRAMDATA_METADATA_SIZE,
    2048           0 :                                              new_target_program_data_account->vt->get_data_len( new_target_program_data_account ) - PROGRAMDATA_METADATA_SIZE,
    2049           0 :                                              runtime_spad );
    2050           0 :   if( FD_UNLIKELY( err ) ) {
    2051           0 :     FD_LOG_WARNING(( "Failed to deploy program %s", FD_BASE58_ENC_32_ALLOCA( builtin_program_id ) ));
    2052           0 :     goto fail;
    2053           0 :   }
    2054             : 
    2055             :   /* https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L281-L284 */
    2056           0 :   ulong lamports_to_fund = new_target_program_account->vt->get_lamports( new_target_program_account ) + new_target_program_data_account->vt->get_lamports( new_target_program_data_account );
    2057             : 
    2058             :   /* Update capitalization.
    2059             :      https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L286-L297 */
    2060           0 :   if( lamports_to_burn>lamports_to_fund ) {
    2061           0 :     fd_bank_capitalization_set( slot_ctx->bank, fd_bank_capitalization_get( slot_ctx->bank ) - ( lamports_to_burn - lamports_to_fund ) );
    2062           0 :   } else {
    2063           0 :     fd_bank_capitalization_set( slot_ctx->bank, fd_bank_capitalization_get( slot_ctx->bank ) + ( lamports_to_fund - lamports_to_burn ) );
    2064           0 :   }
    2065             : 
    2066             :   /* Reclaim the source buffer account
    2067             :      https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L305 */
    2068           0 :   source_buffer_account->vt->set_lamports( source_buffer_account, 0 );
    2069           0 :   source_buffer_account->vt->set_data_len( source_buffer_account, 0 );
    2070           0 :   source_buffer_account->vt->clear_owner( source_buffer_account );
    2071             : 
    2072           0 :   fd_txn_account_mutable_fini( source_buffer_account, slot_ctx->funk, slot_ctx->funk_txn );
    2073             : 
    2074             :   /* Publish the in-preparation transaction into the parent. We should not have to create
    2075             :      a BPF cache entry here because the program is technically "delayed visibility", so the program
    2076             :      should not be invokable until the next slot. The cache entry will be created at the end of the
    2077             :      block as a part of the finalize routine. */
    2078           0 :   fd_funk_txn_start_write( slot_ctx->funk );
    2079           0 :   fd_funk_txn_publish_into_parent( slot_ctx->funk, slot_ctx->funk_txn, 1 );
    2080           0 :   fd_funk_txn_end_write( slot_ctx->funk );
    2081           0 :   slot_ctx->funk_txn = parent_txn;
    2082           0 :   return;
    2083             : 
    2084           0 : fail:
    2085             :   /* Cancel the in-preparation transaction and discard any in-progress changes. */
    2086           0 :   fd_funk_txn_start_write( slot_ctx->funk );
    2087           0 :   fd_funk_txn_cancel( slot_ctx->funk, slot_ctx->funk_txn, 0UL );
    2088           0 :   fd_funk_txn_end_write( slot_ctx->funk );
    2089           0 :   slot_ctx->funk_txn = parent_txn;
    2090           0 : }
    2091             : 
    2092             : /* https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank.rs#L6704 */
    2093             : static void
    2094             : fd_apply_builtin_program_feature_transitions( fd_exec_slot_ctx_t * slot_ctx,
    2095           0 :                                               fd_spad_t *          runtime_spad ) {
    2096             :   /* TODO: Set the upgrade authority properly from the core bpf migration config. Right now it's set to None.
    2097             : 
    2098             :      Migrate any necessary stateless builtins to core BPF. So far, the only "stateless" builtin
    2099             :      is the Feature program. Beginning checks in the `migrate_builtin_to_core_bpf` function will
    2100             :      fail if the program has already been migrated to BPF. */
    2101             : 
    2102           0 :   FD_SPAD_FRAME_BEGIN( runtime_spad ) {
    2103             : 
    2104           0 :   fd_builtin_program_t const * builtins = fd_builtins();
    2105           0 :   for( ulong i=0UL; i<fd_num_builtins(); i++ ) {
    2106             :     /* https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank.rs#L6732-L6751 */
    2107           0 :     if( builtins[i].core_bpf_migration_config && FD_FEATURE_ACTIVE_OFFSET( slot_ctx->slot, fd_bank_features_get( slot_ctx->bank ), builtins[i].core_bpf_migration_config->enable_feature_offset ) ) {
    2108           0 :       FD_LOG_NOTICE(( "Migrating builtin program %s to core BPF", FD_BASE58_ENC_32_ALLOCA( builtins[i].pubkey->key ) ));
    2109           0 :       fd_migrate_builtin_to_core_bpf( slot_ctx,
    2110           0 :                                       builtins[i].core_bpf_migration_config->upgrade_authority_address,
    2111           0 :                                       builtins[i].core_bpf_migration_config->builtin_program_id,
    2112           0 :                                       builtins[i].core_bpf_migration_config->source_buffer_address,
    2113           0 :                                       0,
    2114           0 :                                       runtime_spad );
    2115           0 :     }
    2116             :     /* https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank.rs#L6753-L6774 */
    2117           0 :     if( builtins[i].enable_feature_offset!=NO_ENABLE_FEATURE_ID && FD_FEATURE_JUST_ACTIVATED_OFFSET( slot_ctx, builtins[i].enable_feature_offset ) ) {
    2118           0 :       FD_LOG_NOTICE(( "Enabling builtin program %s", FD_BASE58_ENC_32_ALLOCA( builtins[i].pubkey->key ) ));
    2119           0 :       fd_write_builtin_account( slot_ctx, *builtins[i].pubkey, builtins[i].data,strlen(builtins[i].data) );
    2120           0 :     }
    2121           0 :   }
    2122             : 
    2123             :   /* https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank.rs#L6776-L6793 */
    2124           0 :   fd_stateless_builtin_program_t const * stateless_builtins = fd_stateless_builtins();
    2125           0 :   for( ulong i=0UL; i<fd_num_stateless_builtins(); i++ ) {
    2126           0 :     if( stateless_builtins[i].core_bpf_migration_config && FD_FEATURE_ACTIVE_OFFSET( slot_ctx->slot, fd_bank_features_get( slot_ctx->bank ), stateless_builtins[i].core_bpf_migration_config->enable_feature_offset ) ) {
    2127           0 :       FD_LOG_NOTICE(( "Migrating stateless builtin program %s to core BPF", FD_BASE58_ENC_32_ALLOCA( stateless_builtins[i].pubkey->key ) ));
    2128           0 :       fd_migrate_builtin_to_core_bpf( slot_ctx,
    2129           0 :                                       stateless_builtins[i].core_bpf_migration_config->upgrade_authority_address,
    2130           0 :                                       stateless_builtins[i].core_bpf_migration_config->builtin_program_id,
    2131           0 :                                       stateless_builtins[i].core_bpf_migration_config->source_buffer_address,
    2132           0 :                                       1,
    2133           0 :                                       runtime_spad );
    2134           0 :     }
    2135           0 :   }
    2136             : 
    2137             :   /* https://github.com/anza-xyz/agave/blob/c1080de464cfb578c301e975f498964b5d5313db/runtime/src/bank.rs#L6795-L6805 */
    2138           0 :   fd_precompile_program_t const * precompiles = fd_precompiles();
    2139           0 :   for( ulong i=0UL; i<fd_num_precompiles(); i++ ) {
    2140           0 :     if( FD_FEATURE_JUST_ACTIVATED_OFFSET( slot_ctx, precompiles[i].feature_offset ) ) {
    2141           0 :       fd_write_builtin_account( slot_ctx, *precompiles[i].pubkey, "", 0 );
    2142           0 :     }
    2143           0 :   }
    2144             : 
    2145           0 :   } FD_SPAD_FRAME_END;
    2146           0 : }
    2147             : 
    2148             : static void
    2149             : fd_feature_activate( fd_features_t *         features,
    2150             :                      fd_exec_slot_ctx_t *    slot_ctx,
    2151             :                      fd_feature_id_t const * id,
    2152             :                      uchar const             acct[ static 32 ],
    2153           0 :                      fd_spad_t *             runtime_spad ) {
    2154             : 
    2155             :   // Skip reverted features from being activated
    2156           0 :   if( id->reverted==1 ) {
    2157           0 :     return;
    2158           0 :   }
    2159             : 
    2160           0 :   FD_TXN_ACCOUNT_DECL( acct_rec );
    2161           0 :   int err = fd_txn_account_init_from_funk_readonly( acct_rec, (fd_pubkey_t*)acct, slot_ctx->funk, slot_ctx->funk_txn );
    2162           0 :   if( FD_UNLIKELY( err != FD_ACC_MGR_SUCCESS ) ) {
    2163           0 :     return;
    2164           0 :   }
    2165             : 
    2166           0 :   FD_SPAD_FRAME_BEGIN( runtime_spad ) {
    2167             : 
    2168           0 :   int decode_err = 0;
    2169           0 :   fd_feature_t * feature = fd_bincode_decode_spad(
    2170           0 :       feature, runtime_spad,
    2171           0 :       acct_rec->vt->get_data( acct_rec ),
    2172           0 :       acct_rec->vt->get_data_len( acct_rec ),
    2173           0 :       &decode_err );
    2174           0 :   if( FD_UNLIKELY( decode_err ) ) {
    2175           0 :     FD_LOG_WARNING(( "Failed to decode feature account %s (%d)", FD_BASE58_ENC_32_ALLOCA( acct ), decode_err ));
    2176           0 :     return;
    2177           0 :   }
    2178             : 
    2179           0 :   if( feature->has_activated_at ) {
    2180           0 :     FD_LOG_INFO(( "feature already activated - acc: %s, slot: %lu", FD_BASE58_ENC_32_ALLOCA( acct ), feature->activated_at ));
    2181           0 :     fd_features_set( features, id, feature->activated_at);
    2182           0 :   } else {
    2183           0 :     FD_LOG_INFO(( "Feature %s not activated at %lu, activating", FD_BASE58_ENC_32_ALLOCA( acct ), feature->activated_at ));
    2184             : 
    2185           0 :     FD_TXN_ACCOUNT_DECL( modify_acct_rec );
    2186           0 :     err = fd_txn_account_init_from_funk_mutable( modify_acct_rec, (fd_pubkey_t *)acct, slot_ctx->funk, slot_ctx->funk_txn, 0, 0UL );
    2187           0 :     if( FD_UNLIKELY( err != FD_ACC_MGR_SUCCESS ) ) {
    2188           0 :       return;
    2189           0 :     }
    2190             : 
    2191           0 :     feature->has_activated_at = 1;
    2192           0 :     feature->activated_at     = slot_ctx->slot;
    2193           0 :     fd_bincode_encode_ctx_t encode_ctx = {
    2194           0 :       .data    = modify_acct_rec->vt->get_data_mut( modify_acct_rec ),
    2195           0 :       .dataend = modify_acct_rec->vt->get_data_mut( modify_acct_rec ) + modify_acct_rec->vt->get_data_len( modify_acct_rec ),
    2196           0 :     };
    2197           0 :     int encode_err = fd_feature_encode( feature, &encode_ctx );
    2198           0 :     if( FD_UNLIKELY( encode_err != FD_BINCODE_SUCCESS ) ) {
    2199           0 :       FD_LOG_ERR(( "Failed to encode feature account %s (%d)", FD_BASE58_ENC_32_ALLOCA( acct ), decode_err ));
    2200           0 :     }
    2201             : 
    2202           0 :     fd_txn_account_mutable_fini( modify_acct_rec, slot_ctx->funk, slot_ctx->funk_txn );
    2203           0 :   }
    2204             : 
    2205           0 :   } FD_SPAD_FRAME_END;
    2206           0 : }
    2207             : 
    2208             : static void
    2209           0 : fd_features_activate( fd_exec_slot_ctx_t * slot_ctx, fd_spad_t * runtime_spad ) {
    2210           0 :   fd_features_t * features = fd_bank_features_modify( slot_ctx->bank );
    2211           0 :   for( fd_feature_id_t const * id = fd_feature_iter_init();
    2212           0 :                                    !fd_feature_iter_done( id );
    2213           0 :                                id = fd_feature_iter_next( id ) ) {
    2214           0 :     fd_feature_activate( features, slot_ctx, id, id->id.key, runtime_spad );
    2215           0 :   }
    2216           0 : }
    2217             : 
    2218             : uint
    2219           0 : fd_runtime_is_epoch_boundary( fd_exec_slot_ctx_t * slot_ctx, ulong curr_slot, ulong prev_slot ) {
    2220           0 :   ulong slot_idx;
    2221           0 :   fd_epoch_schedule_t const * schedule = fd_bank_epoch_schedule_query( slot_ctx->bank );
    2222           0 :   ulong prev_epoch = fd_slot_to_epoch( schedule, prev_slot, &slot_idx );
    2223           0 :   ulong new_epoch  = fd_slot_to_epoch( schedule, curr_slot, &slot_idx );
    2224             : 
    2225           0 :   return ( prev_epoch < new_epoch || slot_idx == 0 );
    2226           0 : }
    2227             : 
    2228             : /* Starting a new epoch.
    2229             :   New epoch:        T
    2230             :   Just ended epoch: T-1
    2231             :   Epoch before:     T-2
    2232             : 
    2233             :   In this function:
    2234             :   - stakes in T-2 (slot_ctx->slot_bank.epoch_stakes) should be replaced by T-1 (epoch_bank->next_epoch_stakes)
    2235             :   - stakes at T-1 (epoch_bank->next_epoch_stakes) should be replaced by updated stakes at T (stakes->vote_accounts)
    2236             :   - leader schedule should be calculated using new T-2 stakes (slot_ctx->slot_bank.epoch_stakes)
    2237             : 
    2238             :   Invariant during an epoch T:
    2239             :   epoch_bank->next_epoch_stakes    holds the stakes at T-1
    2240             :   slot_ctx->slot_bank.epoch_stakes holds the stakes at T-2
    2241             :  */
    2242             : /* process for the start of a new epoch */
    2243             : static void
    2244             : fd_runtime_process_new_epoch( fd_exec_slot_ctx_t * slot_ctx,
    2245             :                               ulong                parent_epoch,
    2246             :                               fd_tpool_t *         tpool,
    2247             :                               fd_spad_t * *        exec_spads,
    2248             :                               ulong                exec_spad_cnt,
    2249           0 :                               fd_spad_t *          runtime_spad ) {
    2250           0 :   FD_LOG_NOTICE(( "fd_process_new_epoch start" ));
    2251             : 
    2252           0 :   long start = fd_log_wallclock();
    2253             : 
    2254           0 :   ulong                       slot;
    2255           0 :   fd_epoch_schedule_t const * epoch_schedule = fd_bank_epoch_schedule_query( slot_ctx->bank );
    2256           0 :   ulong                       epoch          = fd_slot_to_epoch( epoch_schedule, slot_ctx->slot, &slot );
    2257             : 
    2258             :   /* Activate new features
    2259             :      https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank.rs#L6587-L6598 */
    2260           0 :   fd_features_activate( slot_ctx, runtime_spad );
    2261           0 :   fd_features_restore( slot_ctx, runtime_spad );
    2262             : 
    2263             :   /* Apply builtin program feature transitions
    2264             :      https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank.rs#L6621-L6624 */
    2265           0 :   fd_apply_builtin_program_feature_transitions( slot_ctx, runtime_spad );
    2266             : 
    2267             :   /* Change the speed of the poh clock
    2268             :      https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank.rs#L6627-L6649 */
    2269             : 
    2270           0 :   if( FD_FEATURE_JUST_ACTIVATED( slot_ctx, update_hashes_per_tick6 ) ) {
    2271           0 :     fd_bank_hashes_per_tick_set( slot_ctx->bank, UPDATED_HASHES_PER_TICK6 );
    2272           0 :   } else if( FD_FEATURE_JUST_ACTIVATED( slot_ctx, update_hashes_per_tick5 ) ) {
    2273           0 :     fd_bank_hashes_per_tick_set( slot_ctx->bank, UPDATED_HASHES_PER_TICK5 );
    2274           0 :   } else if( FD_FEATURE_JUST_ACTIVATED( slot_ctx, update_hashes_per_tick4 ) ) {
    2275           0 :     fd_bank_hashes_per_tick_set( slot_ctx->bank, UPDATED_HASHES_PER_TICK4 );
    2276           0 :   } else if( FD_FEATURE_JUST_ACTIVATED( slot_ctx, update_hashes_per_tick3 ) ) {
    2277           0 :     fd_bank_hashes_per_tick_set( slot_ctx->bank, UPDATED_HASHES_PER_TICK3 );
    2278           0 :   } else if( FD_FEATURE_JUST_ACTIVATED( slot_ctx, update_hashes_per_tick2 ) ) {
    2279           0 :     fd_bank_hashes_per_tick_set( slot_ctx->bank, UPDATED_HASHES_PER_TICK2 );
    2280           0 :   }
    2281             : 
    2282             :   /* Get the new rate activation epoch */
    2283           0 :   int _err[1];
    2284           0 :   ulong   new_rate_activation_epoch_val = 0UL;
    2285           0 :   ulong * new_rate_activation_epoch     = &new_rate_activation_epoch_val;
    2286           0 :   int     is_some                       = fd_new_warmup_cooldown_rate_epoch( slot_ctx->slot,
    2287           0 :                                                                              slot_ctx->funk,
    2288           0 :                                                                              slot_ctx->funk_txn,
    2289           0 :                                                                              runtime_spad,
    2290           0 :                                                                              fd_bank_features_query( slot_ctx->bank ),
    2291           0 :                                                                              new_rate_activation_epoch,
    2292           0 :                                                                              _err );
    2293           0 :   if( FD_UNLIKELY( !is_some ) ) {
    2294           0 :     new_rate_activation_epoch = NULL;
    2295           0 :   }
    2296             : 
    2297           0 :   fd_epoch_info_t temp_info = {0};
    2298           0 :   fd_epoch_info_new( &temp_info );
    2299             : 
    2300             :   /* If appropiate, use the stakes at T-1 to generate the leader schedule instead of T-2.
    2301             :       This is due to a subtlety in how Agave's stake caches interact when loading from snapshots.
    2302             :       See the comment in fd_exec_slot_ctx_recover_. */
    2303             : 
    2304           0 :   if( fd_bank_use_prev_epoch_stake_get( slot_ctx->bank ) == epoch ) {
    2305           0 :     fd_update_epoch_stakes( slot_ctx );
    2306           0 :   }
    2307             : 
    2308             :   /* Updates stake history sysvar accumulated values. */
    2309           0 :   fd_stakes_activate_epoch( slot_ctx,
    2310           0 :                             new_rate_activation_epoch,
    2311           0 :                             &temp_info,
    2312           0 :                             tpool,
    2313           0 :                             exec_spads,
    2314           0 :                             exec_spad_cnt,
    2315           0 :                             runtime_spad );
    2316             : 
    2317             :   /* Update the stakes epoch value to the new epoch */
    2318           0 :   fd_stakes_global_t * stakes = fd_bank_stakes_locking_modify( slot_ctx->bank );
    2319           0 :   stakes->epoch = epoch;
    2320           0 :   fd_bank_stakes_end_locking_modify( slot_ctx->bank );
    2321             : 
    2322           0 :   fd_update_stake_delegations( slot_ctx, &temp_info );
    2323             : 
    2324             :   /* Refresh vote accounts in stakes cache using updated stake weights, and merges slot bank vote accounts with the epoch bank vote accounts.
    2325             :     https://github.com/anza-xyz/agave/blob/v2.1.6/runtime/src/stakes.rs#L363-L370 */
    2326           0 :   fd_stake_history_t const * history = fd_sysvar_stake_history_read( slot_ctx->funk, slot_ctx->funk_txn, runtime_spad );
    2327           0 :   if( FD_UNLIKELY( !history ) ) {
    2328           0 :     FD_LOG_ERR(( "StakeHistory sysvar could not be read and decoded" ));
    2329           0 :   }
    2330             : 
    2331             :   /* In order to correctly handle the lifetimes of allocations for partitioned
    2332             :      epoch rewards, we will push a spad frame when rewards partitioning starts.
    2333             :      We will only pop this frame when all of the rewards for the epoch have
    2334             :      been distributed. As a note, this is technically not the most optimal use
    2335             :      of memory as some data structures used can be freed when this function
    2336             :      exits, but this is okay since the additional allocations are on the order
    2337             :      of a few megabytes and are freed after a few thousand slots. */
    2338             : 
    2339           0 :   fd_spad_push( runtime_spad );
    2340             : 
    2341           0 :   fd_refresh_vote_accounts( slot_ctx,
    2342           0 :                             history,
    2343           0 :                             new_rate_activation_epoch,
    2344           0 :                             &temp_info,
    2345           0 :                             tpool,
    2346           0 :                             exec_spads,
    2347           0 :                             exec_spad_cnt,
    2348           0 :                             runtime_spad );
    2349             : 
    2350             :   /* Distribute rewards */
    2351             : 
    2352           0 :   fd_block_hash_queue_global_t const * bhq              = (fd_block_hash_queue_global_t *)&slot_ctx->bank->block_hash_queue[0];
    2353           0 :   fd_hash_t const *                    parent_blockhash = fd_block_hash_queue_last_hash_join( bhq );
    2354             : 
    2355           0 :   fd_begin_partitioned_rewards( slot_ctx,
    2356           0 :                                 parent_blockhash,
    2357           0 :                                 parent_epoch,
    2358           0 :                                 &temp_info,
    2359           0 :                                 tpool,
    2360           0 :                                 exec_spads,
    2361           0 :                                 exec_spad_cnt,
    2362           0 :                                 runtime_spad );
    2363             : 
    2364             :   /* Replace stakes at T-2 (slot_ctx->slot_bank.epoch_stakes) by stakes at T-1 (epoch_bank->next_epoch_stakes) */
    2365           0 :   fd_update_epoch_stakes( slot_ctx );
    2366             : 
    2367             :   /* Replace stakes at T-1 (epoch_bank->next_epoch_stakes) by updated stakes at T (stakes->vote_accounts) */
    2368           0 :   fd_update_next_epoch_stakes( slot_ctx );
    2369             : 
    2370             :   /* Update current leaders using slot_ctx->slot_bank.epoch_stakes (new T-2 stakes) */
    2371           0 :   fd_runtime_update_leaders( slot_ctx->bank, slot_ctx->slot, runtime_spad );
    2372             : 
    2373           0 :   fd_calculate_epoch_accounts_hash_values( slot_ctx );
    2374             : 
    2375           0 :   FD_LOG_NOTICE(( "fd_process_new_epoch end" ));
    2376             : 
    2377           0 :   long end = fd_log_wallclock();
    2378           0 :   FD_LOG_NOTICE(("fd_process_new_epoch took %ld ns", end - start));
    2379           0 : }
    2380             : 
    2381             : /******************************************************************************/
    2382             : /* Block Parsing                                                              */
    2383             : /******************************************************************************/
    2384             : 
    2385             : /* Block iteration and parsing */
    2386             : 
    2387             : /* As a note, all of the logic in this section is used by the full firedancer
    2388             :    client. The store tile uses these APIs to help parse raw (micro)blocks
    2389             :    received from the network. */
    2390             : 
    2391             : /* Helpers */
    2392             : 
    2393             : static int
    2394             : fd_runtime_parse_microblock_hdr( void const *          buf FD_PARAM_UNUSED,
    2395           0 :                                  ulong                 buf_sz ) {
    2396             : 
    2397           0 :   if( FD_UNLIKELY( buf_sz<sizeof(fd_microblock_hdr_t) ) ) {
    2398           0 :     return -1;
    2399           0 :   }
    2400           0 :   return 0;
    2401           0 : }
    2402             : 
    2403             : void
    2404             : fd_runtime_update_program_cache( fd_exec_slot_ctx_t * slot_ctx,
    2405             :                                  fd_txn_p_t const *   txn_p,
    2406           0 :                                  fd_spad_t *          runtime_spad ) {
    2407           0 :   fd_txn_t const * txn_descriptor = TXN( txn_p );
    2408             : 
    2409             :   /* Iterate over account keys referenced directly in the transaction first */
    2410           0 :   fd_acct_addr_t const * acc_addrs = fd_txn_get_acct_addrs( txn_descriptor, txn_p );
    2411           0 :   for( ushort acc_idx=0; acc_idx<txn_descriptor->acct_addr_cnt; acc_idx++ ) {
    2412           0 :     fd_pubkey_t const * account = fd_type_pun_const( &acc_addrs[acc_idx] );
    2413           0 :     fd_bpf_program_update_program_cache( slot_ctx, account, runtime_spad );
    2414           0 :   }
    2415             : 
    2416           0 :   if( txn_descriptor->transaction_version==FD_TXN_V0 ) {
    2417             : 
    2418             :     /* Iterate over account keys referenced in ALUTs */
    2419           0 :     fd_acct_addr_t alut_accounts[256];
    2420           0 :     fd_slot_hashes_global_t const * slot_hashes_global = fd_sysvar_slot_hashes_read( slot_ctx->funk, slot_ctx->funk_txn, runtime_spad );
    2421           0 :     if( FD_UNLIKELY( !slot_hashes_global ) ) {
    2422           0 :       return;
    2423           0 :     }
    2424             : 
    2425           0 :     fd_slot_hash_t * slot_hash = deq_fd_slot_hash_t_join( (uchar *)slot_hashes_global + slot_hashes_global->hashes_offset );
    2426             : 
    2427           0 :     if( FD_UNLIKELY( fd_runtime_load_txn_address_lookup_tables( txn_descriptor,
    2428           0 :                          txn_p->payload,
    2429           0 :                          slot_ctx->funk,
    2430           0 :                          slot_ctx->funk_txn,
    2431           0 :                          slot_ctx->slot,
    2432           0 :                          slot_hash,
    2433           0 :                          alut_accounts ) ) ) {
    2434           0 :       return;
    2435           0 :     }
    2436             : 
    2437           0 :     for( ushort alut_idx=0; alut_idx<txn_descriptor->addr_table_adtl_cnt; alut_idx++ ) {
    2438           0 :       fd_pubkey_t const * account = fd_type_pun_const( &alut_accounts[alut_idx] );
    2439           0 :       fd_bpf_program_update_program_cache( slot_ctx, account, runtime_spad );
    2440           0 :     }
    2441           0 :   }
    2442           0 : }
    2443             : 
    2444             : /* if we are currently in the middle of a batch, batch_cnt will include the current batch.
    2445             :    if we are at the start of a batch, batch_cnt will include the current batch. */
    2446             : static fd_raw_block_txn_iter_t
    2447             : find_next_txn_in_raw_block( uchar const *                  orig_data,
    2448             :                             fd_block_entry_batch_t const * batches, /* The batch we are currently consuming. */
    2449             :                             ulong                          batch_cnt, /* Includes batch we are currently consuming. */
    2450             :                             ulong                          curr_offset,
    2451           0 :                             ulong                          num_microblocks ) {
    2452             : 
    2453             :   /* At this point, all the transactions in the current microblock have been consumed
    2454             :      by fd_raw_block_txn_iter_next */
    2455             : 
    2456             :   /* Case 1: there are microblocks remaining in the current batch */
    2457           0 :   for( ulong i=0UL; i<num_microblocks; i++ ) {
    2458           0 :     ulong microblock_hdr_size            = sizeof(fd_microblock_hdr_t);
    2459           0 :     fd_microblock_info_t microblock_info = {0};
    2460           0 :     if( FD_UNLIKELY( fd_runtime_parse_microblock_hdr( orig_data + curr_offset,
    2461           0 :                                                       batches->end_off - curr_offset ) ) ) {
    2462             :       /* TODO: improve error handling */
    2463           0 :       FD_LOG_ERR(( "premature end of batch" ));
    2464           0 :     }
    2465           0 :     microblock_info.microblock.hdr = (fd_microblock_hdr_t const * )(orig_data + curr_offset);
    2466           0 :     curr_offset += microblock_hdr_size;
    2467             : 
    2468             :     /* If we have found a microblock with transactions in the current batch, return that */
    2469           0 :     if( FD_LIKELY( microblock_info.microblock.hdr->txn_cnt ) ) {
    2470           0 :       return (fd_raw_block_txn_iter_t){
    2471           0 :         .curr_batch            = batches,
    2472           0 :         .orig_data             = orig_data,
    2473           0 :         .remaining_batches     = batch_cnt,
    2474           0 :         .remaining_microblocks = fd_ulong_sat_sub( fd_ulong_sat_sub(num_microblocks, i), 1UL),
    2475           0 :         .remaining_txns        = microblock_info.microblock.hdr->txn_cnt,
    2476           0 :         .curr_offset           = curr_offset,
    2477           0 :         .curr_txn_sz           = ULONG_MAX
    2478           0 :       };
    2479           0 :     }
    2480           0 :   }
    2481             : 
    2482             :   /* If we have consumed the current batch, but did not find any txns, we need to move on to the next one */
    2483           0 :   curr_offset = batches->end_off;
    2484           0 :   batch_cnt   = fd_ulong_sat_sub( batch_cnt, 1UL );
    2485           0 :   batches++;
    2486             : 
    2487             :   /* Case 2: need to find the next batch with a microblock in that has a non-zero number of txns */
    2488           0 :   for( ulong i=0UL; i<batch_cnt; i++ ) {
    2489             :     /* Sanity-check that we have not over-shot the end of the batch */
    2490           0 :     ulong const batch_end_off = batches[i].end_off;
    2491           0 :     if( FD_UNLIKELY( curr_offset+sizeof(ulong)>batch_end_off ) ) {
    2492           0 :       FD_LOG_ERR(( "premature end of batch" ));
    2493           0 :     }
    2494             : 
    2495             :     /* Consume the ulong describing how many microblocks there are */
    2496           0 :     num_microblocks = FD_LOAD( ulong, orig_data + curr_offset );
    2497           0 :     curr_offset    += sizeof(ulong);
    2498             : 
    2499             :     /* Iterate over each microblock until we find one with a non-zero txn cnt */
    2500           0 :     for( ulong j=0UL; j<num_microblocks; j++ ) {
    2501           0 :       ulong microblock_hdr_size            = sizeof(fd_microblock_hdr_t);
    2502           0 :       fd_microblock_info_t microblock_info = {0};
    2503           0 :       if( FD_UNLIKELY( fd_runtime_parse_microblock_hdr( orig_data + curr_offset,
    2504           0 :                                                         batch_end_off - curr_offset ) ) ) {
    2505             :         /* TODO: improve error handling */
    2506           0 :         FD_LOG_ERR(( "premature end of batch" ));
    2507           0 :       }
    2508           0 :       microblock_info.microblock.hdr = (fd_microblock_hdr_t const * )(orig_data + curr_offset);
    2509           0 :       curr_offset += microblock_hdr_size;
    2510             : 
    2511             :       /* If we have found a microblock with a non-zero number of transactions in, return that */
    2512           0 :       if( FD_LIKELY( microblock_info.microblock.hdr->txn_cnt ) ) {
    2513           0 :         return (fd_raw_block_txn_iter_t){
    2514           0 :           .curr_batch            = &batches[i],
    2515           0 :           .orig_data             = orig_data,
    2516           0 :           .remaining_batches     = fd_ulong_sat_sub( batch_cnt, i ),
    2517           0 :           .remaining_microblocks = fd_ulong_sat_sub( fd_ulong_sat_sub( num_microblocks, j ), 1UL ),
    2518           0 :           .remaining_txns        = microblock_info.microblock.hdr->txn_cnt,
    2519           0 :           .curr_offset           = curr_offset,
    2520           0 :           .curr_txn_sz           = ULONG_MAX
    2521           0 :         };
    2522           0 :       }
    2523           0 :     }
    2524             : 
    2525             :     /* Skip to the start of the next batch */
    2526           0 :     curr_offset = batch_end_off;
    2527           0 :   }
    2528             : 
    2529             :   /* Case 3: we didn't manage to find any microblocks with non-zero transaction counts in */
    2530           0 :   return (fd_raw_block_txn_iter_t) {
    2531           0 :     .curr_batch            = batches,
    2532           0 :     .orig_data             = orig_data,
    2533           0 :     .remaining_batches     = 0UL,
    2534           0 :     .remaining_microblocks = 0UL,
    2535           0 :     .remaining_txns        = 0UL,
    2536           0 :     .curr_offset           = curr_offset,
    2537           0 :     .curr_txn_sz           = ULONG_MAX
    2538           0 :   };
    2539           0 : }
    2540             : 
    2541             : /* Public API */
    2542             : 
    2543             : fd_raw_block_txn_iter_t
    2544             : fd_raw_block_txn_iter_init( uchar const *                  orig_data,
    2545             :                             fd_block_entry_batch_t const * batches,
    2546           0 :                             ulong                          batch_cnt ) {
    2547             :   /* In general, every read of a lower level count should lead to a
    2548             :      decrement of a higher level count.  For example, reading a count
    2549             :      of microblocks should lead to a decrement of the number of
    2550             :      remaining batches.  In some sense, the batch count is drained into
    2551             :      the microblock count. */
    2552             : 
    2553           0 :   ulong num_microblocks = FD_LOAD( ulong, orig_data );
    2554           0 :   return find_next_txn_in_raw_block( orig_data, batches, batch_cnt, sizeof(ulong), num_microblocks );
    2555           0 : }
    2556             : 
    2557             : ulong
    2558           0 : fd_raw_block_txn_iter_done( fd_raw_block_txn_iter_t iter ) {
    2559           0 :   return iter.remaining_batches==0UL && iter.remaining_microblocks==0UL && iter.remaining_txns==0UL;
    2560           0 : }
    2561             : 
    2562             : fd_raw_block_txn_iter_t
    2563           0 : fd_raw_block_txn_iter_next( fd_raw_block_txn_iter_t iter ) {
    2564           0 :   ulong const batch_end_off = iter.curr_batch->end_off;
    2565           0 :   fd_txn_p_t out_txn;
    2566           0 :   if( iter.curr_txn_sz == ULONG_MAX ) {
    2567           0 :     ulong payload_sz = 0;
    2568           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 );
    2569           0 :     if( FD_UNLIKELY( !txn_sz || txn_sz>FD_TXN_MTU ) ) {
    2570           0 :       FD_LOG_ERR(("Invalid txn parse"));
    2571           0 :     }
    2572           0 :     iter.curr_offset += payload_sz;
    2573           0 :   } else {
    2574           0 :     iter.curr_offset += iter.curr_txn_sz;
    2575           0 :     iter.curr_txn_sz  = ULONG_MAX;
    2576           0 :   }
    2577             : 
    2578           0 :   if( --iter.remaining_txns ) {
    2579           0 :     return iter;
    2580           0 :   }
    2581             : 
    2582           0 :   return find_next_txn_in_raw_block( iter.orig_data,
    2583           0 :                                      iter.curr_batch,
    2584           0 :                                      iter.remaining_batches,
    2585           0 :                                      iter.curr_offset,
    2586           0 :                                      iter.remaining_microblocks );
    2587           0 : }
    2588             : 
    2589             : void
    2590           0 : fd_raw_block_txn_iter_ele( fd_raw_block_txn_iter_t iter, fd_txn_p_t * out_txn ) {
    2591           0 :   ulong const batch_end_off = iter.curr_batch->end_off;
    2592           0 :   ulong       payload_sz    = 0UL;
    2593           0 :   ulong       txn_sz        = fd_txn_parse_core( iter.orig_data + iter.curr_offset,
    2594           0 :                                                  fd_ulong_min( batch_end_off - iter.curr_offset, FD_TXN_MTU ),
    2595           0 :                                                  TXN( out_txn ), NULL, &payload_sz );
    2596             : 
    2597           0 :   if( FD_UNLIKELY( !txn_sz || txn_sz>FD_TXN_MTU ) ) {
    2598           0 :     FD_LOG_ERR(( "Invalid txn parse %lu", txn_sz ));
    2599           0 :   }
    2600           0 :   fd_memcpy( out_txn->payload, iter.orig_data + iter.curr_offset, payload_sz );
    2601           0 :   out_txn->payload_sz = (ushort)payload_sz;
    2602           0 :   iter.curr_txn_sz    = payload_sz;
    2603           0 : }
    2604             : 
    2605             : /******************************************************************************/
    2606             : /* Block Parsing logic (Only for offline replay)                              */
    2607             : /******************************************************************************/
    2608             : 
    2609             : /* The below runtime block parsing and block destroying logic is ONLY used in
    2610             :    offline replay to simulate the block parsing/freeing that would occur in
    2611             :    the full, live firedancer client. This is done via two APIs:
    2612             :    fd_runtime_block_prepare and fd_runtime_block_destroy. */
    2613             : 
    2614             : /* Helpers for fd_runtime_block_prepare */
    2615             : 
    2616             : static int
    2617             : fd_runtime_parse_microblock_txns( void const *                buf,
    2618             :                                   ulong                       buf_sz,
    2619             :                                   fd_microblock_hdr_t const * microblock_hdr,
    2620             :                                   fd_txn_p_t *                out_txns,
    2621             :                                   ulong *                     out_signature_cnt,
    2622             :                                   ulong *                     out_account_cnt,
    2623           0 :                                   ulong *                     out_microblock_txns_sz ) {
    2624             : 
    2625           0 :   ulong buf_off       = 0UL;
    2626           0 :   ulong signature_cnt = 0UL;
    2627           0 :   ulong account_cnt   = 0UL;
    2628             : 
    2629           0 :   for( ulong i=0UL; i<microblock_hdr->txn_cnt; i++ ) {
    2630           0 :     ulong payload_sz = 0UL;
    2631           0 :     ulong txn_sz     = fd_txn_parse_core( (uchar const *)buf + buf_off,
    2632           0 :                                           fd_ulong_min( buf_sz-buf_off, FD_TXN_MTU ),
    2633           0 :                                           TXN( &out_txns[i] ),
    2634           0 :                                           NULL,
    2635           0 :                                           &payload_sz );
    2636           0 :     if( FD_UNLIKELY( !txn_sz || txn_sz>FD_TXN_MTU || !payload_sz  ) ) {
    2637           0 :       return -1;
    2638           0 :     }
    2639             : 
    2640           0 :     fd_memcpy( out_txns[i].payload, (uchar *)buf + buf_off, payload_sz );
    2641           0 :     out_txns[i].payload_sz = (ushort)payload_sz;
    2642             : 
    2643           0 :     signature_cnt += TXN( &out_txns[i] )->signature_cnt;
    2644           0 :     account_cnt   += fd_txn_account_cnt( TXN(&out_txns[i]), FD_TXN_ACCT_CAT_ALL );
    2645           0 :     buf_off       += payload_sz;
    2646           0 :   }
    2647             : 
    2648           0 :   *out_signature_cnt      = signature_cnt;
    2649           0 :   *out_account_cnt        = account_cnt;
    2650           0 :   *out_microblock_txns_sz = buf_off;
    2651             : 
    2652           0 :   return 0;
    2653           0 : }
    2654             : 
    2655             : static int
    2656             : fd_runtime_microblock_prepare( void const *           buf,
    2657             :                                ulong                  buf_sz,
    2658             :                                fd_spad_t *            runtime_spad,
    2659           0 :                                fd_microblock_info_t * out_microblock_info ) {
    2660             : 
    2661           0 :   fd_microblock_info_t microblock_info = {
    2662           0 :     .signature_cnt  = 0UL,
    2663           0 :   };
    2664           0 :   ulong buf_off = 0UL;
    2665           0 :   ulong hdr_sz  = sizeof(fd_microblock_hdr_t);
    2666           0 :   if( FD_UNLIKELY( fd_runtime_parse_microblock_hdr( buf, buf_sz ) ) ) {
    2667           0 :     return -1;
    2668           0 :   }
    2669           0 :   microblock_info.microblock.hdr = (fd_microblock_hdr_t const *)buf;
    2670           0 :   buf_off += hdr_sz;
    2671             : 
    2672           0 :   ulong txn_cnt        = microblock_info.microblock.hdr->txn_cnt;
    2673           0 :   microblock_info.txns = fd_spad_alloc( runtime_spad, alignof(fd_txn_p_t), txn_cnt * sizeof(fd_txn_p_t) );
    2674           0 :   ulong txns_sz        = 0UL;
    2675           0 :   if( FD_UNLIKELY( fd_runtime_parse_microblock_txns( (uchar *)buf + buf_off,
    2676           0 :                                                      buf_sz - buf_off,
    2677           0 :                                                      microblock_info.microblock.hdr,
    2678           0 :                                                      microblock_info.txns,
    2679           0 :                                                      &microblock_info.signature_cnt,
    2680           0 :                                                      &microblock_info.account_cnt,
    2681           0 :                                                      &txns_sz ) ) ) {
    2682           0 :     return -1;
    2683           0 :   }
    2684             : 
    2685           0 :   buf_off                          += txns_sz;
    2686           0 :   microblock_info.raw_microblock_sz = buf_off;
    2687           0 :   *out_microblock_info              = microblock_info;
    2688             : 
    2689           0 :   return 0;
    2690           0 : }
    2691             : 
    2692             : static int
    2693             : fd_runtime_microblock_batch_prepare( void const *                 buf,
    2694             :                                      ulong                        buf_sz,
    2695             :                                      fd_spad_t *                  runtime_spad,
    2696           0 :                                      fd_microblock_batch_info_t * out_microblock_batch_info ) {
    2697             : 
    2698           0 :   fd_microblock_batch_info_t microblock_batch_info = {
    2699           0 :     .raw_microblock_batch = buf,
    2700           0 :     .signature_cnt        = 0UL,
    2701           0 :     .txn_cnt              = 0UL,
    2702           0 :     .account_cnt          = 0UL,
    2703           0 :   };
    2704           0 :   ulong buf_off = 0UL;
    2705             : 
    2706           0 :   if( FD_UNLIKELY( buf_sz<sizeof(ulong) ) ) {
    2707           0 :     FD_LOG_WARNING(( "microblock batch buffer too small" ));
    2708           0 :     return -1;
    2709           0 :   }
    2710           0 :   ulong microblock_cnt = FD_LOAD( ulong, buf );
    2711           0 :   buf_off             += sizeof(ulong);
    2712             : 
    2713           0 :   microblock_batch_info.microblock_cnt   = microblock_cnt;
    2714           0 :   microblock_batch_info.microblock_infos = fd_spad_alloc( runtime_spad, alignof(fd_microblock_info_t), microblock_cnt * sizeof(fd_microblock_info_t) );
    2715             : 
    2716           0 :   ulong signature_cnt = 0UL;
    2717           0 :   ulong txn_cnt       = 0UL;
    2718           0 :   ulong account_cnt   = 0UL;
    2719           0 :   for( ulong i=0UL; i<microblock_cnt; i++ ) {
    2720           0 :     fd_microblock_info_t * microblock_info = &microblock_batch_info.microblock_infos[i];
    2721           0 :     if( FD_UNLIKELY( fd_runtime_microblock_prepare( (uchar const *)buf + buf_off, buf_sz - buf_off, runtime_spad, microblock_info ) ) ) {
    2722           0 :       return -1;
    2723           0 :     }
    2724             : 
    2725           0 :     signature_cnt += microblock_info->signature_cnt;
    2726           0 :     txn_cnt       += microblock_info->microblock.hdr->txn_cnt;
    2727           0 :     account_cnt   += microblock_info->account_cnt;
    2728           0 :     buf_off       += microblock_info->raw_microblock_sz;
    2729           0 :   }
    2730             : 
    2731           0 :   microblock_batch_info.signature_cnt           = signature_cnt;
    2732           0 :   microblock_batch_info.txn_cnt                 = txn_cnt;
    2733           0 :   microblock_batch_info.account_cnt             = account_cnt;
    2734           0 :   microblock_batch_info.raw_microblock_batch_sz = buf_off;
    2735             : 
    2736           0 :   *out_microblock_batch_info                    = microblock_batch_info;
    2737             : 
    2738           0 :   return 0;
    2739           0 : }
    2740             : 
    2741             : /* This function is used for parsing/preparing blocks during offline runtime replay. */
    2742             : static int
    2743             : fd_runtime_block_prepare( fd_blockstore_t         * blockstore,
    2744             :                           fd_block_t              * block,
    2745             :                           ulong                     slot,
    2746             :                           fd_spad_t               * runtime_spad,
    2747           0 :                           fd_runtime_block_info_t * out_block_info ) {
    2748           0 :   uchar const *                  buf         = fd_blockstore_block_data_laddr( blockstore, block );
    2749           0 :   ulong const                    buf_sz      = block->data_sz;
    2750           0 :   fd_block_entry_batch_t const * batch_laddr = fd_blockstore_block_batch_laddr( blockstore, block );
    2751           0 :   ulong const                    batch_cnt   = block->batch_cnt;
    2752             : 
    2753           0 :   fd_runtime_block_info_t block_info = {
    2754           0 :       .raw_block    = buf,
    2755           0 :       .raw_block_sz = buf_sz,
    2756           0 :   };
    2757             : 
    2758           0 :   ulong microblock_batch_cnt        = 0UL;
    2759           0 :   ulong microblock_cnt              = 0UL;
    2760           0 :   ulong signature_cnt               = 0UL;
    2761           0 :   ulong txn_cnt                     = 0UL;
    2762           0 :   ulong account_cnt                 = 0UL;
    2763           0 :   block_info.microblock_batch_infos = fd_spad_alloc( runtime_spad, alignof(fd_microblock_batch_info_t), block->batch_cnt * sizeof(fd_microblock_batch_info_t) );
    2764             : 
    2765           0 :   ulong buf_off = 0UL;
    2766           0 :   for( microblock_batch_cnt=0UL; microblock_batch_cnt < batch_cnt; microblock_batch_cnt++ ) {
    2767           0 :     ulong const batch_end_off = batch_laddr[ microblock_batch_cnt ].end_off;
    2768           0 :     fd_microblock_batch_info_t * microblock_batch_info = block_info.microblock_batch_infos + microblock_batch_cnt;
    2769           0 :     if( FD_UNLIKELY( fd_runtime_microblock_batch_prepare( buf + buf_off, batch_end_off - buf_off, runtime_spad, microblock_batch_info ) ) ) {
    2770           0 :       return -1;
    2771           0 :     }
    2772             : 
    2773           0 :     signature_cnt  += microblock_batch_info->signature_cnt;
    2774           0 :     txn_cnt        += microblock_batch_info->txn_cnt;
    2775           0 :     account_cnt    += microblock_batch_info->account_cnt;
    2776           0 :     microblock_cnt += microblock_batch_info->microblock_cnt;
    2777             : 
    2778           0 :     uchar allow_trailing = 1UL;
    2779           0 :     buf_off += microblock_batch_info->raw_microblock_batch_sz;
    2780           0 :     if( FD_UNLIKELY( buf_off > batch_end_off ) ) {
    2781           0 :       FD_LOG_ERR(( "parser error: shouldn't have been allowed to read past batch boundary" ));
    2782           0 :     }
    2783           0 :     if( FD_UNLIKELY( buf_off < batch_end_off ) ) {
    2784           0 :       if( FD_LIKELY( allow_trailing ) ) {
    2785           0 :         FD_LOG_NOTICE(( "ignoring %lu trailing bytes in slot %lu batch %lu", batch_end_off-buf_off, slot, microblock_batch_cnt ));
    2786           0 :       }
    2787           0 :       if( FD_UNLIKELY( !allow_trailing ) ) {
    2788           0 :         FD_LOG_WARNING(( "%lu trailing bytes in slot %lu batch %lu", batch_end_off-buf_off, slot, microblock_batch_cnt ));
    2789           0 :         return -1;
    2790           0 :       }
    2791           0 :     }
    2792           0 :     buf_off = batch_end_off;
    2793           0 :   }
    2794             : 
    2795           0 :   block_info.microblock_batch_cnt = microblock_batch_cnt;
    2796           0 :   block_info.microblock_cnt       = microblock_cnt;
    2797           0 :   block_info.signature_cnt        = signature_cnt;
    2798           0 :   block_info.txn_cnt              = txn_cnt;
    2799           0 :   block_info.account_cnt          = account_cnt;
    2800             : 
    2801           0 :   *out_block_info = block_info;
    2802             : 
    2803           0 :   return 0;
    2804           0 : }
    2805             : 
    2806             : /* Block collecting (Only for offline replay) */
    2807             : 
    2808             : static ulong
    2809             : fd_runtime_microblock_collect_txns( fd_microblock_info_t const * microblock_info,
    2810           0 :                                     fd_txn_p_t *                 out_txns ) {
    2811           0 :   ulong txn_cnt = microblock_info->microblock.hdr->txn_cnt;
    2812           0 :   fd_memcpy( out_txns, microblock_info->txns, txn_cnt * sizeof(fd_txn_p_t) );
    2813           0 :   return txn_cnt;
    2814           0 : }
    2815             : 
    2816             : static ulong
    2817             : fd_runtime_microblock_batch_collect_txns( fd_microblock_batch_info_t const * microblock_batch_info,
    2818           0 :                                           fd_txn_p_t *                       out_txns ) {
    2819           0 :   for( ulong i=0UL; i<microblock_batch_info->microblock_cnt; i++ ) {
    2820           0 :     ulong txns_collected = fd_runtime_microblock_collect_txns( &microblock_batch_info->microblock_infos[i], out_txns );
    2821           0 :     out_txns            += txns_collected;
    2822           0 :   }
    2823             : 
    2824           0 :   return microblock_batch_info->txn_cnt;
    2825           0 : }
    2826             : 
    2827             : static ulong
    2828             : fd_runtime_block_collect_txns( fd_runtime_block_info_t const * block_info,
    2829           0 :                                fd_txn_p_t *            out_txns ) {
    2830           0 :   for( ulong i=0UL; i<block_info->microblock_batch_cnt; i++ ) {
    2831           0 :     ulong txns_collected = fd_runtime_microblock_batch_collect_txns( &block_info->microblock_batch_infos[i], out_txns );
    2832           0 :     out_txns            += txns_collected;
    2833           0 :   }
    2834             : 
    2835           0 :   return block_info->txn_cnt;
    2836           0 : }
    2837             : 
    2838             : /******************************************************************************/
    2839             : /* Genesis                                                                    */
    2840             : /*******************************************************************************/
    2841             : 
    2842             : static void
    2843             : fd_runtime_init_program( fd_exec_slot_ctx_t * slot_ctx,
    2844           0 :                          fd_spad_t *          runtime_spad ) {
    2845           0 :   fd_sysvar_recent_hashes_init( slot_ctx, runtime_spad );
    2846           0 :   fd_sysvar_clock_init( slot_ctx->bank, slot_ctx->funk, slot_ctx->funk_txn );
    2847           0 :   fd_sysvar_slot_history_init( slot_ctx, runtime_spad );
    2848           0 :   fd_sysvar_slot_hashes_init( slot_ctx, runtime_spad );
    2849           0 :   fd_sysvar_epoch_schedule_init( slot_ctx );
    2850           0 :   fd_sysvar_rent_init( slot_ctx );
    2851           0 :   fd_sysvar_stake_history_init( slot_ctx );
    2852           0 :   fd_sysvar_last_restart_slot_init( slot_ctx );
    2853             : 
    2854           0 :   fd_builtin_programs_init( slot_ctx );
    2855           0 :   fd_stake_program_config_init( slot_ctx );
    2856           0 : }
    2857             : 
    2858             : static void
    2859             : fd_runtime_init_bank_from_genesis( fd_exec_slot_ctx_t *        slot_ctx,
    2860             :                                    fd_genesis_solana_t const * genesis_block,
    2861             :                                    fd_hash_t const *           genesis_hash,
    2862           0 :                                    fd_spad_t *                 runtime_spad ) {
    2863           0 :   slot_ctx->slot = 0UL;
    2864             : 
    2865           0 :   fd_bank_poh_set( slot_ctx->bank, *genesis_hash );
    2866             : 
    2867           0 :   fd_hash_t * bank_hash = fd_bank_bank_hash_modify( slot_ctx->bank );
    2868           0 :   memset( bank_hash->hash, 0, FD_SHA256_HASH_SZ );
    2869             : 
    2870           0 :   fd_poh_config_t const * poh  = &genesis_block->poh_config;
    2871           0 :   uint128 target_tick_duration = ((uint128)poh->target_tick_duration.seconds * 1000000000UL + (uint128)poh->target_tick_duration.nanoseconds);
    2872             : 
    2873           0 :   fd_bank_epoch_schedule_set( slot_ctx->bank, genesis_block->epoch_schedule );
    2874             : 
    2875           0 :   fd_bank_rent_set( slot_ctx->bank, genesis_block->rent );
    2876             : 
    2877           0 :   fd_bank_block_height_set( slot_ctx->bank, 0UL );
    2878             : 
    2879           0 :   fd_bank_inflation_set( slot_ctx->bank, genesis_block->inflation );
    2880             : 
    2881           0 :   fd_block_hash_queue_global_t *      block_hash_queue = (fd_block_hash_queue_global_t *)&slot_ctx->bank->block_hash_queue[0];
    2882           0 :   uchar *                             last_hash_mem    = (uchar *)fd_ulong_align_up( (ulong)block_hash_queue + sizeof(fd_block_hash_queue_global_t), alignof(fd_hash_t) );
    2883           0 :   uchar *                             ages_pool_mem    = (uchar *)fd_ulong_align_up( (ulong)last_hash_mem + sizeof(fd_hash_t), fd_hash_hash_age_pair_t_map_align() );
    2884           0 :   fd_hash_hash_age_pair_t_mapnode_t * ages_pool        = fd_hash_hash_age_pair_t_map_join( fd_hash_hash_age_pair_t_map_new( ages_pool_mem, 400 ) );
    2885           0 :   fd_hash_hash_age_pair_t_mapnode_t * ages_root        = NULL;
    2886             : 
    2887           0 :   fd_hash_hash_age_pair_t_mapnode_t * node = fd_hash_hash_age_pair_t_map_acquire( ages_pool );
    2888           0 :   node->elem = (fd_hash_hash_age_pair_t){
    2889           0 :     .key = *genesis_hash,
    2890           0 :     .val = (fd_hash_age_t){ .hash_index = 0UL, .fee_calculator = (fd_fee_calculator_t){ .lamports_per_signature = 0UL }, .timestamp = (ulong)fd_log_wallclock() }
    2891           0 :   };
    2892           0 :   fd_hash_hash_age_pair_t_map_insert( ages_pool, &ages_root, node );
    2893           0 :   fd_memcpy( last_hash_mem, genesis_hash, FD_HASH_FOOTPRINT );
    2894             : 
    2895           0 :   block_hash_queue->last_hash_index  = 0UL;
    2896           0 :   block_hash_queue->last_hash_offset = (ulong)last_hash_mem - (ulong)block_hash_queue;
    2897           0 :   block_hash_queue->ages_pool_offset = (ulong)fd_hash_hash_age_pair_t_map_leave( ages_pool ) - (ulong)block_hash_queue;
    2898           0 :   block_hash_queue->ages_root_offset = (ulong)ages_root - (ulong)block_hash_queue;
    2899           0 :   block_hash_queue->max_age          = FD_BLOCKHASH_QUEUE_MAX_ENTRIES;
    2900             : 
    2901           0 :   fd_bank_fee_rate_governor_set( slot_ctx->bank, genesis_block->fee_rate_governor );
    2902             : 
    2903           0 :   fd_bank_lamports_per_signature_set( slot_ctx->bank, 0UL );
    2904             : 
    2905           0 :   fd_bank_prev_lamports_per_signature_set( slot_ctx->bank, 0UL );
    2906             : 
    2907           0 :   fd_bank_max_tick_height_set( slot_ctx->bank, genesis_block->ticks_per_slot * (slot_ctx->slot + 1) );
    2908             : 
    2909           0 :   fd_bank_hashes_per_tick_set( slot_ctx->bank, !!poh->hashes_per_tick ? poh->hashes_per_tick : 0UL );
    2910             : 
    2911           0 :   fd_bank_ns_per_slot_set( slot_ctx->bank, target_tick_duration * genesis_block->ticks_per_slot );
    2912             : 
    2913           0 :   fd_bank_ticks_per_slot_set( slot_ctx->bank, genesis_block->ticks_per_slot );
    2914             : 
    2915           0 :   fd_bank_genesis_creation_time_set( slot_ctx->bank, genesis_block->creation_time );
    2916             : 
    2917           0 :   fd_bank_slots_per_year_set( slot_ctx->bank, SECONDS_PER_YEAR * (1000000000.0 / (double)target_tick_duration) / (double)genesis_block->ticks_per_slot );
    2918             : 
    2919           0 :   fd_bank_signature_count_set( slot_ctx->bank, 0UL );
    2920             : 
    2921             :   /* Derive epoch stakes */
    2922             : 
    2923           0 :   fd_vote_accounts_pair_t_mapnode_t * vacc_pool = NULL;
    2924           0 :   fd_vote_accounts_pair_t_mapnode_t * vacc_root = NULL;
    2925           0 :   FD_TEST( vacc_pool );
    2926             : 
    2927           0 :   fd_delegation_pair_t_mapnode_t * sacc_pool = NULL;
    2928           0 :   fd_delegation_pair_t_mapnode_t * sacc_root = NULL;
    2929             : 
    2930           0 :   fd_acc_lamports_t capitalization = 0UL;
    2931             : 
    2932           0 :   fd_features_t * features = fd_bank_features_modify( slot_ctx->bank );
    2933           0 :   FD_FEATURE_SET_ACTIVE(features, accounts_lt_hash, 0);
    2934           0 :   FD_FEATURE_SET_ACTIVE(features, remove_accounts_delta_hash, 0);
    2935             : 
    2936           0 :   for( ulong i=0UL; i<genesis_block->accounts_len; i++ ) {
    2937           0 :     fd_pubkey_account_pair_t const * acc = &genesis_block->accounts[i];
    2938           0 :     capitalization = fd_ulong_sat_add( capitalization, acc->account.lamports );
    2939             : 
    2940           0 :     if( !memcmp(acc->account.owner.key, fd_solana_vote_program_id.key, sizeof(fd_pubkey_t)) ) {
    2941             :       /* Vote Program Account */
    2942           0 :       fd_vote_accounts_pair_t_mapnode_t *node = fd_vote_accounts_pair_t_map_acquire(vacc_pool);
    2943           0 :       FD_TEST( node );
    2944             : 
    2945             :       /* FIXME: Reimplement when we try to fix genesis. */
    2946             :       // fd_vote_block_timestamp_t last_timestamp = {0};
    2947             :       // fd_pubkey_t               node_pubkey    = {0};
    2948             :       // FD_SPAD_FRAME_BEGIN( runtime_spad ) {
    2949             :       //   /* Deserialize content */
    2950             :       //   fd_vote_state_versioned_t vs[1];
    2951             :       //   fd_bincode_decode_ctx_t decode = {
    2952             :       //     .data    = acc->account.data,
    2953             :       //     .dataend = acc->account.data + acc->account.data_len,
    2954             :       //     .valloc  = fd_spad_virtual( runtime_spad )
    2955             :       //   };
    2956             :       //   int decode_err = fd_vote_state_versioned_decode( vs, &decode );
    2957             :       //   if( FD_UNLIKELY( decode_err!=FD_BINCODE_SUCCESS ) ) {
    2958             :       //     FD_LOG_WARNING(( "fd_vote_state_versioned_decode failed (%d)", decode_err ));
    2959             :       //     return;
    2960             :       //   }
    2961             : 
    2962             :       //   switch( vs->discriminant )
    2963             :       //   {
    2964             :       //   case fd_vote_state_versioned_enum_current:
    2965             :       //     last_timestamp = vs->inner.current.last_timestamp;
    2966             :       //     node_pubkey    = vs->inner.current.node_pubkey;
    2967             :       //     break;
    2968             :       //   case fd_vote_state_versioned_enum_v0_23_5:
    2969             :       //     last_timestamp = vs->inner.v0_23_5.last_timestamp;
    2970             :       //     node_pubkey    = vs->inner.v0_23_5.node_pubkey;
    2971             :       //     break;
    2972             :       //   case fd_vote_state_versioned_enum_v1_14_11:
    2973             :       //     last_timestamp = vs->inner.v1_14_11.last_timestamp;
    2974             :       //     node_pubkey    = vs->inner.v1_14_11.node_pubkey;
    2975             :       //     break;
    2976             :       //   default:
    2977             :       //     __builtin_unreachable();
    2978             :       //   }
    2979             : 
    2980             :       // } FD_SPAD_FRAME_END;
    2981             : 
    2982             :       // fd_memcpy(node->elem.key.key, acc->key.key, sizeof(fd_pubkey_t));
    2983             :       // node->elem.stake = acc->account.lamports;
    2984             :       // node->elem.value = (fd_solana_vote_account_t){
    2985             :       //   .lamports = acc->account.lamports,
    2986             :       //   .node_pubkey = node_pubkey,
    2987             :       //   .last_timestamp_ts = last_timestamp.timestamp,
    2988             :       //   .last_timestamp_slot = last_timestamp.slot,
    2989             :       //   .owner = acc->account.owner,
    2990             :       //   .executable = acc->account.executable,
    2991             :       //   .rent_epoch = acc->account.rent_epoch
    2992             :       // };
    2993             : 
    2994           0 :       fd_vote_accounts_pair_t_map_insert( vacc_pool, &vacc_root, node );
    2995             : 
    2996           0 :       FD_LOG_INFO(( "Adding genesis vote account: key=%s stake=%lu",
    2997           0 :                     FD_BASE58_ENC_32_ALLOCA( node->elem.key.key ),
    2998           0 :                     node->elem.stake ));
    2999           0 :     } else if( !memcmp( acc->account.owner.key, fd_solana_stake_program_id.key, sizeof(fd_pubkey_t) ) ) {
    3000             :       /* stake program account */
    3001           0 :       fd_stake_state_v2_t   stake_state   = {0};
    3002           0 :       fd_account_meta_t     meta          = { .dlen = acc->account.data_len };
    3003           0 :       FD_TXN_ACCOUNT_DECL( stake_account );
    3004           0 :       fd_txn_account_init_from_meta_and_data_mutable( stake_account, &meta, acc->account.data );
    3005           0 :       FD_TEST( fd_stake_get_state( stake_account, &stake_state ) == 0 );
    3006           0 :       if( !stake_state.inner.stake.stake.delegation.stake ) {
    3007           0 :         continue;
    3008           0 :       }
    3009           0 :       fd_delegation_pair_t_mapnode_t   query_node = {0};
    3010           0 :       fd_memcpy(&query_node.elem.account, acc->key.key, sizeof(fd_pubkey_t));
    3011           0 :       fd_delegation_pair_t_mapnode_t * node = fd_delegation_pair_t_map_find( sacc_pool, sacc_root, &query_node );
    3012             : 
    3013           0 :       if( !node ) {
    3014           0 :         node = fd_delegation_pair_t_map_acquire( sacc_pool );
    3015           0 :         fd_memcpy( &node->elem.account, acc->key.key, sizeof(fd_pubkey_t) );
    3016           0 :         node->elem.delegation = stake_state.inner.stake.stake.delegation;
    3017           0 :         fd_delegation_pair_t_map_insert( sacc_pool, &sacc_root, node );
    3018           0 :       } else {
    3019           0 :         fd_memcpy( &node->elem.account, acc->key.key, sizeof(fd_pubkey_t) );
    3020           0 :         node->elem.delegation = stake_state.inner.stake.stake.delegation;
    3021           0 :       }
    3022           0 :     } else if( !memcmp(acc->account.owner.key, fd_solana_feature_program_id.key, sizeof(fd_pubkey_t)) ) {
    3023             :       /* Feature Account */
    3024             : 
    3025             :       /* Scan list of feature IDs to resolve address => feature offset */
    3026           0 :       fd_feature_id_t const *found = NULL;
    3027           0 :       for( fd_feature_id_t const * id = fd_feature_iter_init();
    3028           0 :            !fd_feature_iter_done( id );
    3029           0 :            id = fd_feature_iter_next( id ) ) {
    3030           0 :         if( !memcmp( acc->key.key, id->id.key, sizeof(fd_pubkey_t) ) ) {
    3031           0 :           found = id;
    3032           0 :           break;
    3033           0 :         }
    3034           0 :       }
    3035             : 
    3036           0 :       if( found ) {
    3037             :         /* Load feature activation */
    3038           0 :         FD_SPAD_FRAME_BEGIN( runtime_spad ) {
    3039           0 :           int err;
    3040           0 :           fd_feature_t * feature = fd_bincode_decode_spad(
    3041           0 :               feature, runtime_spad,
    3042           0 :               acc->account.data,
    3043           0 :               acc->account.data_len,
    3044           0 :               &err );
    3045           0 :           FD_TEST( err==FD_BINCODE_SUCCESS );
    3046             : 
    3047           0 :           fd_features_t * features = fd_bank_features_modify( slot_ctx->bank );
    3048           0 :           if( feature->has_activated_at ) {
    3049           0 :             FD_LOG_DEBUG(( "Feature %s activated at %lu (genesis)", FD_BASE58_ENC_32_ALLOCA( acc->key.key ), feature->activated_at ));
    3050           0 :             fd_features_set( features, found, feature->activated_at );
    3051           0 :           } else {
    3052           0 :             FD_LOG_DEBUG(( "Feature %s not activated (genesis)", FD_BASE58_ENC_32_ALLOCA( acc->key.key ) ));
    3053           0 :             fd_features_set( features, found, ULONG_MAX );
    3054           0 :           }
    3055           0 :         } FD_SPAD_FRAME_END;
    3056           0 :       }
    3057           0 :     }
    3058           0 :   }
    3059             : 
    3060             :   // uchar * pool_mem = fd_spad_alloc( runtime_spad, fd_vote_accounts_pair_t_map_align(), fd_vote_accounts_pair_t_map_footprint( FD_HASH_FOOTPRINT * 400 ) );
    3061             : 
    3062             :   // slot_ctx->slot_bank.epoch_stakes.vote_accounts_pool = fd_vote_accounts_pair_t_map_join( fd_vote_accounts_pair_t_map_new( pool_mem, FD_HASH_FOOTPRINT * 400 ) );
    3063             :   // slot_ctx->slot_bank.epoch_stakes.vote_accounts_root = NULL;
    3064             : 
    3065             :   // fd_vote_accounts_pair_t_mapnode_t * next_pool = fd_exec_epoch_ctx_next_epoch_stakes_join( slot_ctx->epoch_ctx );
    3066             :   // fd_vote_accounts_pair_t_mapnode_t * next_root = NULL;
    3067             : 
    3068             :   // for( fd_vote_accounts_pair_t_mapnode_t *n = fd_vote_accounts_pair_t_map_minimum( vacc_pool, vacc_root );
    3069             :   //      n;
    3070             :   //      n = fd_vote_accounts_pair_t_map_successor( vacc_pool, n )) {
    3071             :   //   fd_vote_accounts_pair_t_mapnode_t * e = fd_vote_accounts_pair_t_map_acquire( slot_ctx->slot_bank.epoch_stakes.vote_accounts_pool );
    3072             :   //   e->elem = n->elem;
    3073             :   //   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 );
    3074             : 
    3075             :   //   fd_vote_accounts_pair_t_mapnode_t * next_e = fd_vote_accounts_pair_t_map_acquire( next_pool );
    3076             :   //   next_e->elem = n->elem;
    3077             :   //   fd_vote_accounts_pair_t_map_insert( next_pool, &next_root, next_e );
    3078             :   // }
    3079             : 
    3080           0 :   for( fd_delegation_pair_t_mapnode_t *n = fd_delegation_pair_t_map_minimum( sacc_pool, sacc_root );
    3081           0 :        n;
    3082           0 :        n = fd_delegation_pair_t_map_successor( sacc_pool, n )) {
    3083           0 :     fd_vote_accounts_pair_t_mapnode_t query_voter = {0};
    3084           0 :     query_voter.elem.key = n->elem.delegation.voter_pubkey;
    3085             : 
    3086           0 :     fd_vote_accounts_pair_t_mapnode_t * voter = fd_vote_accounts_pair_t_map_find( vacc_pool, vacc_root, &query_voter );
    3087             : 
    3088           0 :     if( !!voter ) {
    3089           0 :       voter->elem.stake = fd_ulong_sat_add( voter->elem.stake, n->elem.delegation.stake );
    3090           0 :     }
    3091           0 :   }
    3092             : 
    3093             :   // epoch_bank->next_epoch_stakes = (fd_vote_accounts_t){
    3094             :   //   .vote_accounts_pool = next_pool,
    3095             :   //   .vote_accounts_root = next_root,
    3096             :   // };
    3097             : 
    3098             :   /* Initializes the stakes cache in the Bank structure. */
    3099             :   // epoch_bank->stakes = (fd_stakes_t){
    3100             :   //     .stake_delegations_pool = sacc_pool,
    3101             :   //     .stake_delegations_root = sacc_root,
    3102             :   //     .epoch                  = 0UL,
    3103             :   //     .unused                 = 0UL,
    3104             :   //     .vote_accounts = (fd_vote_accounts_t){
    3105             :   //       .vote_accounts_pool = vacc_pool,
    3106             :   //       .vote_accounts_root = vacc_root
    3107             :   //     },
    3108             :   //     .stake_history = {0}
    3109             :   // };
    3110             : 
    3111           0 :   fd_bank_capitalization_set( slot_ctx->bank, capitalization );
    3112             : 
    3113           0 :   fd_clock_timestamp_votes_global_t * clock_timestamp_votes = fd_bank_clock_timestamp_votes_locking_modify( slot_ctx->bank );
    3114           0 :   uchar * clock_pool_mem = (uchar *)fd_ulong_align_up( (ulong)clock_timestamp_votes + sizeof(fd_clock_timestamp_votes_global_t), fd_clock_timestamp_vote_t_map_align() );
    3115           0 :   fd_clock_timestamp_vote_t_mapnode_t * clock_pool = fd_clock_timestamp_vote_t_map_join( fd_clock_timestamp_vote_t_map_new(clock_pool_mem, 30000UL ) );
    3116           0 :   clock_timestamp_votes->votes_pool_offset = (ulong)fd_clock_timestamp_vote_t_map_leave( clock_pool) - (ulong)clock_timestamp_votes;
    3117           0 :   clock_timestamp_votes->votes_root_offset = 0UL;
    3118           0 :   fd_bank_clock_timestamp_votes_end_locking_modify( slot_ctx->bank );
    3119           0 : }
    3120             : 
    3121             : static int
    3122             : fd_runtime_process_genesis_block( fd_exec_slot_ctx_t * slot_ctx,
    3123             :                                   fd_capture_ctx_t *   capture_ctx,
    3124           0 :                                   fd_spad_t *          runtime_spad ) {
    3125             : 
    3126             : 
    3127           0 :   fd_hash_t * poh = fd_bank_poh_modify( slot_ctx->bank );
    3128           0 :   ulong hashcnt_per_slot = fd_bank_hashes_per_tick_get( slot_ctx->bank ) * fd_bank_ticks_per_slot_get( slot_ctx->bank );
    3129           0 :   while( hashcnt_per_slot-- ) {
    3130           0 :     fd_sha256_hash( poh->hash, sizeof(fd_hash_t), poh->hash );
    3131           0 :   }
    3132             : 
    3133           0 :   fd_bank_execution_fees_set( slot_ctx->bank, 0UL );
    3134             : 
    3135           0 :   fd_bank_priority_fees_set( slot_ctx->bank, 0UL );
    3136             : 
    3137           0 :   fd_bank_signature_count_set( slot_ctx->bank, 0UL );
    3138             : 
    3139           0 :   fd_bank_txn_count_set( slot_ctx->bank, 0UL );
    3140             : 
    3141           0 :   fd_bank_failed_txn_count_set( slot_ctx->bank, 0UL );
    3142             : 
    3143           0 :   fd_bank_nonvote_failed_txn_count_set( slot_ctx->bank, 0UL );
    3144             : 
    3145           0 :   fd_bank_total_compute_units_used_set( slot_ctx->bank, 0UL );
    3146             : 
    3147           0 :   fd_sysvar_slot_history_update( slot_ctx, runtime_spad );
    3148             : 
    3149           0 :   fd_runtime_update_leaders( slot_ctx->bank, 0, runtime_spad );
    3150             : 
    3151           0 :   fd_runtime_freeze( slot_ctx, runtime_spad );
    3152             : 
    3153             :   /* sort and update bank hash */
    3154           0 :   fd_hash_t * bank_hash = fd_bank_bank_hash_modify( slot_ctx->bank );
    3155           0 :   int result = fd_update_hash_bank_tpool( slot_ctx,
    3156           0 :                                           capture_ctx,
    3157           0 :                                           bank_hash,
    3158           0 :                                           0UL,
    3159           0 :                                           NULL,
    3160           0 :                                           runtime_spad );
    3161             : 
    3162           0 :   if( FD_UNLIKELY( result != FD_EXECUTOR_INSTR_SUCCESS ) ) {
    3163           0 :     FD_LOG_ERR(( "Failed to update bank hash with error=%d", result ));
    3164           0 :   }
    3165             : 
    3166           0 :   return FD_RUNTIME_EXECUTE_SUCCESS;
    3167           0 : }
    3168             : 
    3169             : void
    3170             : fd_runtime_read_genesis( fd_exec_slot_ctx_t * slot_ctx,
    3171             :                          char const *         genesis_filepath,
    3172             :                          uchar                is_snapshot,
    3173             :                          fd_capture_ctx_t *   capture_ctx,
    3174           0 :                          fd_spad_t *          runtime_spad ) {
    3175             : 
    3176           0 :   if( strlen( genesis_filepath ) == 0 ) {
    3177           0 :     return;
    3178           0 :   }
    3179             : 
    3180           0 :   struct stat sbuf;
    3181           0 :   if( FD_UNLIKELY( stat( genesis_filepath, &sbuf) < 0 ) ) {
    3182           0 :     FD_LOG_ERR(( "cannot open %s : %s", genesis_filepath, strerror(errno) ));
    3183           0 :   }
    3184           0 :   int fd = open( genesis_filepath, O_RDONLY );
    3185           0 :   if( FD_UNLIKELY( fd < 0 ) ) {
    3186           0 :     FD_LOG_ERR(("cannot open %s : %s", genesis_filepath, strerror(errno)));
    3187           0 :   }
    3188             : 
    3189           0 :   fd_genesis_solana_t * genesis_block;
    3190           0 :   fd_hash_t             genesis_hash;
    3191             : 
    3192             :   /* NOTE: These genesis decode spad allocs persist through the lifetime of fd_runtime,
    3193             :      even though they aren't used outside of this function. This is because
    3194             :      fd_runtime_init_bank_from_genesis, which depends on the genesis_block, initializes
    3195             :      a bunch of structures on spad that need to persist throughout fd_runtime. Using a bump
    3196             :      allocator does not let us free memory lower in the stack without freeing everything
    3197             :      above it (in a meaningful way).
    3198             : 
    3199             :      FIXME: Use spad frames here once the fd_runtime structures initialized here are no
    3200             :      longer spad-backed. */
    3201             : 
    3202           0 :   uchar * buf = fd_spad_alloc( runtime_spad, alignof(ulong), (ulong)sbuf.st_size );
    3203           0 :   ulong sz    = 0UL;
    3204           0 :   int res     = fd_io_read( fd, buf, (ulong)sbuf.st_size, (ulong)sbuf.st_size, &sz );
    3205           0 :   FD_TEST( res==0 );
    3206           0 :   FD_TEST( sz==(ulong)sbuf.st_size );
    3207           0 :   close( fd );
    3208             : 
    3209           0 :     int err;
    3210           0 :     genesis_block = fd_bincode_decode_spad(
    3211           0 :         genesis_solana, runtime_spad, buf, sz, &err );
    3212           0 :     if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) {
    3213           0 :       FD_LOG_ERR(( "fd_genesis_solana_decode_footprint failed (%d)", err ));
    3214           0 :     }
    3215             : 
    3216             :   // The hash is generated from the raw data... don't mess with this..
    3217           0 :   fd_sha256_hash( buf, sz, genesis_hash.uc );
    3218             : 
    3219           0 :   fd_hash_t * genesis_hash_bm = fd_bank_genesis_hash_modify( slot_ctx->bank );
    3220           0 :   fd_memcpy( genesis_hash_bm, buf, sizeof(fd_hash_t) );
    3221             : 
    3222           0 :   if( !is_snapshot ) {
    3223           0 :     fd_runtime_init_bank_from_genesis( slot_ctx,
    3224           0 :                                         genesis_block,
    3225           0 :                                         &genesis_hash,
    3226           0 :                                         runtime_spad );
    3227             : 
    3228           0 :     fd_runtime_init_program( slot_ctx, runtime_spad );
    3229             : 
    3230           0 :     FD_LOG_DEBUG(( "start genesis accounts - count: %lu", genesis_block->accounts_len ));
    3231             : 
    3232           0 :     for( ulong i=0; i<genesis_block->accounts_len; i++ ) {
    3233           0 :       fd_pubkey_account_pair_t * a = &genesis_block->accounts[i];
    3234             : 
    3235           0 :       FD_TXN_ACCOUNT_DECL( rec );
    3236             : 
    3237           0 :       int err = fd_txn_account_init_from_funk_mutable( rec,
    3238           0 :                                                       &a->key,
    3239           0 :                                                       slot_ctx->funk,
    3240           0 :                                                       slot_ctx->funk_txn,
    3241           0 :                                                       1, /* do_create */
    3242           0 :                                                       a->account.data_len );
    3243             : 
    3244           0 :       if( FD_UNLIKELY( err ) ) {
    3245           0 :         FD_LOG_ERR(( "fd_txn_account_init_from_funk_mutable failed (%d)", err ));
    3246           0 :       }
    3247             : 
    3248           0 :       rec->vt->set_data( rec, a->account.data, a->account.data_len );
    3249           0 :       rec->vt->set_lamports( rec, a->account.lamports );
    3250           0 :       rec->vt->set_rent_epoch( rec, a->account.rent_epoch );
    3251           0 :       rec->vt->set_executable( rec, a->account.executable );
    3252           0 :       rec->vt->set_owner( rec, &a->account.owner );
    3253             : 
    3254           0 :       fd_txn_account_mutable_fini( rec, slot_ctx->funk, slot_ctx->funk_txn );
    3255           0 :     }
    3256             : 
    3257           0 :     FD_LOG_DEBUG(( "end genesis accounts" ));
    3258             : 
    3259           0 :     FD_LOG_DEBUG(( "native instruction processors - count: %lu", genesis_block->native_instruction_processors_len ));
    3260             : 
    3261           0 :     for( ulong i=0UL; i < genesis_block->native_instruction_processors_len; i++ ) {
    3262           0 :       fd_string_pubkey_pair_t * a = &genesis_block->native_instruction_processors[i];
    3263           0 :       fd_write_builtin_account( slot_ctx, a->pubkey, (const char *) a->string, a->string_len );
    3264           0 :     }
    3265             : 
    3266           0 :     fd_features_restore( slot_ctx, runtime_spad );
    3267             : 
    3268           0 :     slot_ctx->slot = 0UL;
    3269             : 
    3270           0 :     int err = fd_runtime_process_genesis_block( slot_ctx, capture_ctx, runtime_spad );
    3271           0 :     if( FD_UNLIKELY( err ) ) {
    3272           0 :       FD_LOG_ERR(( "Genesis slot 0 execute failed with error %d", err ));
    3273           0 :     }
    3274           0 :   }
    3275             : 
    3276             : 
    3277           0 :   fd_account_keys_global_t *         stake_account_keys      = fd_bank_stake_account_keys_locking_modify( slot_ctx->bank );
    3278           0 :   uchar *                            pool_mem                = (uchar *)fd_ulong_align_up( (ulong)stake_account_keys + sizeof(fd_account_keys_global_t), fd_account_keys_pair_t_map_align() );
    3279           0 :   fd_account_keys_pair_t_mapnode_t * stake_account_keys_pool = fd_account_keys_pair_t_map_join( fd_account_keys_pair_t_map_new( pool_mem, 100000UL ) );
    3280           0 :   fd_account_keys_pair_t_mapnode_t * stake_account_keys_root = NULL;
    3281             : 
    3282           0 :   fd_account_keys_account_keys_pool_update( stake_account_keys, stake_account_keys_pool );
    3283           0 :   fd_account_keys_account_keys_root_update( stake_account_keys, stake_account_keys_root );
    3284           0 :   fd_bank_stake_account_keys_end_locking_modify( slot_ctx->bank );
    3285             : 
    3286           0 :   fd_account_keys_global_t *         vote_account_keys      = fd_bank_vote_account_keys_locking_modify( slot_ctx->bank );
    3287           0 :                                      pool_mem               = (uchar *)fd_ulong_align_up( (ulong)vote_account_keys + sizeof(fd_account_keys_global_t), fd_account_keys_pair_t_map_align() );
    3288           0 :   fd_account_keys_pair_t_mapnode_t * vote_account_keys_pool = fd_account_keys_pair_t_map_join( fd_account_keys_pair_t_map_new( pool_mem, 100000UL ) );
    3289           0 :   fd_account_keys_pair_t_mapnode_t * vote_account_keys_root = NULL;
    3290             : 
    3291           0 :   fd_account_keys_account_keys_pool_update( vote_account_keys, vote_account_keys_pool );
    3292           0 :   fd_account_keys_account_keys_root_update( vote_account_keys, vote_account_keys_root );
    3293             : 
    3294           0 :   fd_bank_vote_account_keys_end_locking_modify( slot_ctx->bank );
    3295           0 : }
    3296             : 
    3297             : /******************************************************************************/
    3298             : /* Offline Replay                                                             */
    3299             : /******************************************************************************/
    3300             : 
    3301             : /* As a note, currently offline and live replay of transactions has differences
    3302             :    with regards to how the execution environment is setup. These are helpers
    3303             :    used to emulate this behavior */
    3304             : 
    3305             : struct fd_poh_verification_info {
    3306             :   fd_microblock_info_t const * microblock_info;
    3307             :   fd_hash_t const            * in_poh_hash;
    3308             :   int success;
    3309             : };
    3310             : typedef struct fd_poh_verification_info fd_poh_verification_info_t;
    3311             : 
    3312             : static void
    3313             : fd_runtime_microblock_verify_info_collect( fd_microblock_info_t const * microblock_info,
    3314             :                                            fd_hash_t            const * in_poh_hash,
    3315           0 :                                            fd_poh_verification_info_t * poh_verification_info ) {
    3316           0 :   poh_verification_info->microblock_info = microblock_info;
    3317           0 :   poh_verification_info->in_poh_hash     = in_poh_hash;
    3318           0 :   poh_verification_info->success         = 0;
    3319           0 : }
    3320             : 
    3321             : static void
    3322             : fd_runtime_microblock_batch_verify_info_collect( fd_microblock_batch_info_t const * microblock_batch_info,
    3323             :                                                  fd_hash_t                  const * in_poh_hash,
    3324           0 :                                                  fd_poh_verification_info_t *       poh_verification_info ) {
    3325           0 :   for( ulong i=0UL; i<microblock_batch_info->microblock_cnt; i++ ) {
    3326           0 :     fd_microblock_info_t const * microblock_info = &microblock_batch_info->microblock_infos[i];
    3327           0 :     fd_runtime_microblock_verify_info_collect( microblock_info, in_poh_hash, &poh_verification_info[i] );
    3328           0 :     in_poh_hash = (fd_hash_t const *)&microblock_info->microblock.hdr->hash;
    3329           0 :   }
    3330           0 : }
    3331             : 
    3332             : static void
    3333             : fd_runtime_block_verify_info_collect( fd_runtime_block_info_t const *      block_info,
    3334             :                                       fd_hash_t       const *      in_poh_hash,
    3335             :                                       fd_poh_verification_info_t * poh_verification_info)
    3336           0 : {
    3337           0 :   for( ulong i=0UL; i<block_info->microblock_batch_cnt; i++ ) {
    3338           0 :     fd_microblock_batch_info_t const * microblock_batch_info = &block_info->microblock_batch_infos[i];
    3339             : 
    3340           0 :     fd_runtime_microblock_batch_verify_info_collect( microblock_batch_info, in_poh_hash, poh_verification_info );
    3341           0 :     in_poh_hash            = (fd_hash_t const *)poh_verification_info[microblock_batch_info->microblock_cnt - 1].microblock_info->microblock.hdr->hash;
    3342           0 :     poh_verification_info += microblock_batch_info->microblock_cnt;
    3343           0 :   }
    3344           0 : }
    3345             : 
    3346             : static void
    3347             : fd_runtime_poh_verify_wide_task( void * tpool,
    3348             :                                  ulong  t0 FD_PARAM_UNUSED,
    3349             :                                  ulong  t1 FD_PARAM_UNUSED,
    3350             :                                  void * args FD_PARAM_UNUSED,
    3351             :                                  void * reduce FD_PARAM_UNUSED,
    3352             :                                  ulong  stride FD_PARAM_UNUSED,
    3353             :                                  ulong  l0 FD_PARAM_UNUSED,
    3354             :                                  ulong  l1 FD_PARAM_UNUSED,
    3355             :                                  ulong  m0,
    3356             :                                  ulong  m1 FD_PARAM_UNUSED,
    3357             :                                  ulong  n0 FD_PARAM_UNUSED,
    3358           0 :                                  ulong  n1 FD_PARAM_UNUSED ) {
    3359           0 :   fd_poh_verification_info_t * poh_info = (fd_poh_verification_info_t *)tpool + m0;
    3360             : 
    3361           0 :   fd_hash_t out_poh_hash = *poh_info->in_poh_hash;
    3362           0 :   fd_hash_t init_poh_hash_cpy = *poh_info->in_poh_hash;
    3363             : 
    3364           0 :   fd_microblock_info_t const *microblock_info = poh_info->microblock_info;
    3365           0 :   ulong hash_cnt = microblock_info->microblock.hdr->hash_cnt;
    3366           0 :   ulong txn_cnt = microblock_info->microblock.hdr->txn_cnt;
    3367             : 
    3368           0 :   if( !txn_cnt ) { /* microblock is a tick */
    3369           0 :     fd_poh_append( &out_poh_hash, hash_cnt );
    3370           0 :   } else {
    3371           0 :     if( hash_cnt ) {
    3372           0 :       fd_poh_append(&out_poh_hash, hash_cnt - 1);
    3373           0 :     }
    3374             : 
    3375           0 :     ulong                 leaf_cnt = microblock_info->signature_cnt;
    3376           0 :     uchar *               commit   = fd_alloca_check( FD_WBMTREE32_ALIGN, fd_wbmtree32_footprint(leaf_cnt));
    3377           0 :     fd_wbmtree32_leaf_t * leafs    = fd_alloca_check(alignof(fd_wbmtree32_leaf_t), sizeof(fd_wbmtree32_leaf_t) * leaf_cnt);
    3378           0 :     uchar *               mbuf     = fd_alloca_check( 1UL, leaf_cnt * (sizeof(fd_ed25519_sig_t) + 1) );
    3379             : 
    3380           0 :     fd_wbmtree32_t *      tree = fd_wbmtree32_init(commit, leaf_cnt);
    3381           0 :     fd_wbmtree32_leaf_t * l    = &leafs[0];
    3382             : 
    3383             :     /* Loop across transactions */
    3384           0 :     for( ulong txn_idx=0UL; txn_idx<txn_cnt; txn_idx++ ) {
    3385           0 :       fd_txn_p_t          * txn_p      = &microblock_info->txns[txn_idx];
    3386           0 :       fd_txn_t      const * txn        = (fd_txn_t const *) txn_p->_;
    3387           0 :       fd_rawtxn_b_t const   raw_txn[1] = {{ .raw = txn_p->payload, .txn_sz = (ushort)txn_p->payload_sz } };
    3388             : 
    3389             :       /* Loop across signatures */
    3390           0 :       fd_ed25519_sig_t const * sigs = (fd_ed25519_sig_t const *)((ulong)raw_txn->raw + (ulong)txn->signature_off);
    3391           0 :       for( ulong j=0UL; j<txn->signature_cnt; j++ ) {
    3392           0 :         l->data     = (uchar *)&sigs[j];
    3393           0 :         l->data_len = sizeof(fd_ed25519_sig_t);
    3394           0 :         l++;
    3395           0 :       }
    3396           0 :     }
    3397             : 
    3398           0 :     fd_wbmtree32_append( tree, leafs, leaf_cnt, mbuf );
    3399           0 :     uchar * root = fd_wbmtree32_fini( tree );
    3400           0 :     fd_poh_mixin( &out_poh_hash, root );
    3401           0 :   }
    3402             : 
    3403           0 :   if( FD_UNLIKELY( memcmp(microblock_info->microblock.hdr->hash, out_poh_hash.hash, sizeof(fd_hash_t)) ) ) {
    3404           0 :     FD_LOG_WARNING(( "poh mismatch (bank: %s, entry: %s. INIT: %s)",
    3405           0 :         FD_BASE58_ENC_32_ALLOCA( out_poh_hash.hash ),
    3406           0 :         FD_BASE58_ENC_32_ALLOCA( microblock_info->microblock.hdr->hash ),
    3407           0 :         FD_BASE58_ENC_32_ALLOCA(&init_poh_hash_cpy) ));
    3408           0 :     poh_info->success = -1;
    3409           0 :   }
    3410           0 : }
    3411             : 
    3412             : static int
    3413             : fd_runtime_poh_verify_tpool( fd_poh_verification_info_t * poh_verification_info,
    3414             :                              ulong                        poh_verification_info_cnt,
    3415           0 :                              fd_tpool_t *                 tpool ) {
    3416           0 :   fd_tpool_exec_all_rrobin( tpool,
    3417           0 :                             0,
    3418           0 :                             fd_tpool_worker_cnt( tpool ),
    3419           0 :                             fd_runtime_poh_verify_wide_task,
    3420           0 :                             poh_verification_info,
    3421           0 :                             NULL,
    3422           0 :                             NULL,
    3423           0 :                             1,
    3424           0 :                             0,
    3425           0 :                             poh_verification_info_cnt );
    3426             : 
    3427           0 :   for( ulong i=0UL; i<poh_verification_info_cnt; i++ ) {
    3428           0 :     if( poh_verification_info[i].success ) {
    3429           0 :       return -1;
    3430           0 :     }
    3431           0 :   }
    3432             : 
    3433           0 :   return 0;
    3434           0 : }
    3435             : 
    3436             : static int
    3437             : fd_runtime_block_verify_tpool( fd_exec_slot_ctx_t *            slot_ctx,
    3438             :                                fd_blockstore_t *               blockstore,
    3439             :                                fd_runtime_block_info_t const * block_info,
    3440             :                                fd_hash_t const *               in_poh_hash,
    3441             :                                fd_hash_t *                     out_poh_hash,
    3442             :                                fd_tpool_t *                    tpool,
    3443           0 :                                fd_spad_t *                     runtime_spad ) {
    3444             : 
    3445           0 :   FD_SPAD_FRAME_BEGIN( runtime_spad ) {
    3446             : 
    3447           0 :   long block_verify_time = -fd_log_wallclock();
    3448             : 
    3449           0 :   fd_hash_t                    tmp_in_poh_hash           = *in_poh_hash;
    3450           0 :   ulong                        poh_verification_info_cnt = block_info->microblock_cnt;
    3451           0 :   fd_poh_verification_info_t * poh_verification_info     = fd_spad_alloc( runtime_spad,
    3452           0 :                                                                           alignof(fd_poh_verification_info_t),
    3453           0 :                                                                           poh_verification_info_cnt * sizeof(fd_poh_verification_info_t) );
    3454           0 :   fd_runtime_block_verify_info_collect( block_info, &tmp_in_poh_hash, poh_verification_info );
    3455             : 
    3456           0 :   uchar * block_data = fd_spad_alloc( runtime_spad, 128UL, FD_SHRED_DATA_PAYLOAD_MAX_PER_SLOT );
    3457           0 :   ulong   tick_res   = fd_runtime_block_verify_ticks( blockstore,
    3458           0 :                                                       slot_ctx->slot,
    3459           0 :                                                       block_data,
    3460           0 :                                                       FD_SHRED_DATA_PAYLOAD_MAX_PER_SLOT,
    3461           0 :                                                       fd_bank_tick_height_get( slot_ctx->bank ),
    3462           0 :                                                       fd_bank_max_tick_height_get( slot_ctx->bank ),
    3463           0 :                                                       fd_bank_hashes_per_tick_get( slot_ctx->bank ) );
    3464             : 
    3465           0 :   if( FD_UNLIKELY( tick_res != FD_BLOCK_OK ) ) {
    3466           0 :     FD_LOG_WARNING(( "failed to verify ticks res %lu slot %lu", tick_res, slot_ctx->slot ));
    3467           0 :     return -1;
    3468           0 :   }
    3469             : 
    3470             :   /* poh_verification_info is now in order information of all the microblocks */
    3471             : 
    3472           0 :   int result = fd_runtime_poh_verify_tpool( poh_verification_info, poh_verification_info_cnt, tpool );
    3473           0 :   fd_memcpy( out_poh_hash->hash, poh_verification_info[poh_verification_info_cnt - 1].microblock_info->microblock.hdr->hash, sizeof(fd_hash_t) );
    3474             : 
    3475           0 :   block_verify_time          += fd_log_wallclock();
    3476           0 :   double block_verify_time_ms = (double)block_verify_time * 1e-6;
    3477             : 
    3478           0 :   FD_LOG_INFO(( "verified block successfully - elapsed: %6.6f ms", block_verify_time_ms ));
    3479             : 
    3480           0 :   return result;
    3481             : 
    3482           0 :   } FD_SPAD_FRAME_END;
    3483           0 : }
    3484             : 
    3485             : /* Should only be called in offline replay */
    3486             : static int
    3487             : fd_runtime_publish_old_txns( fd_exec_slot_ctx_t * slot_ctx,
    3488             :                              fd_capture_ctx_t *   capture_ctx,
    3489             :                              fd_tpool_t *         tpool,
    3490           0 :                              fd_spad_t *          runtime_spad ) {
    3491             :   /* Publish any transaction older than 31 slots */
    3492           0 :   fd_funk_txn_start_write( slot_ctx->funk );
    3493           0 :   fd_funk_t *          funk       = slot_ctx->funk;
    3494           0 :   fd_funk_txn_pool_t * txnpool    = fd_funk_txn_pool( funk );
    3495             : 
    3496           0 :   if( capture_ctx != NULL ) {
    3497           0 :     fd_runtime_checkpt( capture_ctx, slot_ctx, slot_ctx->slot );
    3498           0 :   }
    3499             : 
    3500           0 :   int do_eah = 0;
    3501             : 
    3502           0 :   uint depth = 0;
    3503           0 :   for( fd_funk_txn_t * txn = slot_ctx->funk_txn; txn; txn = fd_funk_txn_parent(txn, txnpool) ) {
    3504           0 :     if( ++depth == (FD_RUNTIME_OFFLINE_NUM_ROOT_BLOCKS - 1 ) ) {
    3505           0 :       FD_LOG_DEBUG(("publishing %s (slot %lu)", FD_BASE58_ENC_32_ALLOCA( &txn->xid ), txn->xid.ul[0]));
    3506             : 
    3507           0 :       if( FD_UNLIKELY( !fd_funk_txn_publish( funk, txn, 1 ) ) ) {
    3508           0 :         FD_LOG_ERR(( "No transactions were published" ));
    3509           0 :       }
    3510             : 
    3511             :       /* Also publish the bank */
    3512           0 :       ulong slot = txn->xid.ul[0];
    3513           0 :       fd_banks_publish( slot_ctx->banks, slot );
    3514             : 
    3515           0 :       if( txn->xid.ul[0] >= fd_bank_eah_start_slot_get( slot_ctx->bank ) ) {
    3516           0 :         if( !FD_FEATURE_ACTIVE_BANK( slot_ctx->bank, accounts_lt_hash ) ) {
    3517           0 :           do_eah = 1;
    3518           0 :         }
    3519           0 :         fd_bank_eah_start_slot_set( slot_ctx->bank, ULONG_MAX );
    3520           0 :       }
    3521             : 
    3522           0 :       break;
    3523           0 :     }
    3524           0 :   }
    3525             : 
    3526           0 :   fd_funk_txn_end_write( slot_ctx->funk );
    3527             : 
    3528             :   /* Do the EAH calculation after we have released the Funk lock, to avoid a deadlock */
    3529           0 :   if( FD_UNLIKELY( do_eah ) ) {
    3530           0 :     fd_exec_para_cb_ctx_t exec_para_ctx = {
    3531           0 :       .func       = fd_accounts_hash_counter_and_gather_tpool_cb,
    3532           0 :       .para_arg_1 = tpool
    3533           0 :     };
    3534             : 
    3535             : 
    3536           0 :     fd_hash_t * epoch_account_hash = fd_bank_epoch_account_hash_modify( slot_ctx->bank );
    3537           0 :     fd_accounts_hash( slot_ctx->funk,
    3538           0 :                       slot_ctx->slot,
    3539           0 :                       epoch_account_hash,
    3540           0 :                       runtime_spad,
    3541           0 :                       fd_bank_features_query( slot_ctx->bank ),
    3542           0 :                       &exec_para_ctx,
    3543           0 :                       NULL );
    3544           0 :   }
    3545             : 
    3546           0 :   return 0;
    3547           0 : }
    3548             : 
    3549             : int
    3550             : fd_runtime_block_execute_tpool( fd_exec_slot_ctx_t *            slot_ctx,
    3551             :                                 fd_blockstore_t *               blockstore,
    3552             :                                 fd_capture_ctx_t *              capture_ctx,
    3553             :                                 fd_runtime_block_info_t const * block_info,
    3554             :                                 fd_tpool_t *                    tpool,
    3555             :                                 fd_spad_t * *                   exec_spads,
    3556             :                                 ulong                           exec_spad_cnt,
    3557           0 :                                 fd_spad_t *                     runtime_spad ) {
    3558             : 
    3559           0 :   if ( capture_ctx != NULL && capture_ctx->capture && slot_ctx->slot>=capture_ctx->solcap_start_slot ) {
    3560           0 :     fd_solcap_writer_set_slot( capture_ctx->capture, slot_ctx->slot );
    3561           0 :   }
    3562             : 
    3563           0 :   long block_execute_time = -fd_log_wallclock();
    3564             : 
    3565           0 :   int res = fd_runtime_block_execute_prepare( slot_ctx, blockstore, runtime_spad );
    3566           0 :   if( res != FD_RUNTIME_EXECUTE_SUCCESS ) {
    3567           0 :     return res;
    3568           0 :   }
    3569             : 
    3570           0 :   ulong        txn_cnt  = block_info->txn_cnt;
    3571           0 :   fd_txn_p_t * txn_ptrs = fd_spad_alloc( runtime_spad, alignof(fd_txn_p_t), txn_cnt * sizeof(fd_txn_p_t) );
    3572             : 
    3573           0 :   fd_runtime_block_collect_txns( block_info, txn_ptrs );
    3574             : 
    3575             :   /* Initialize the cost tracker when the feature is active */
    3576           0 :   fd_cost_tracker_t * cost_tracker = fd_spad_alloc( runtime_spad, FD_COST_TRACKER_ALIGN, sizeof(fd_cost_tracker_t) );
    3577           0 :   if( FD_FEATURE_ACTIVE_BANK( slot_ctx->bank, apply_cost_tracker_during_replay ) ) {
    3578           0 :     fd_cost_tracker_init( cost_tracker, slot_ctx, runtime_spad );
    3579           0 :   }
    3580             : 
    3581             :   /* We want to emulate microblock-by-microblock execution */
    3582           0 :   ulong to_exec_idx = 0UL;
    3583           0 :   for( ulong i=0UL; i<block_info->microblock_batch_cnt; i++ ) {
    3584           0 :     for( ulong j=0UL; j<block_info->microblock_batch_infos[i].microblock_cnt; j++ ) {
    3585           0 :       ulong txn_cnt = block_info->microblock_batch_infos[i].microblock_infos[j].microblock.hdr->txn_cnt;
    3586           0 :       fd_txn_p_t * mblock_txn_ptrs = &txn_ptrs[ to_exec_idx ];
    3587           0 :       ulong        mblock_txn_cnt  = txn_cnt;
    3588           0 :       to_exec_idx += txn_cnt;
    3589             : 
    3590             :       /* UPDATE */
    3591             : 
    3592           0 :       if( !mblock_txn_cnt ) continue;
    3593             : 
    3594             :       /* Reverify programs for this epoch if needed */
    3595           0 :       for( ulong txn_idx=0UL; txn_idx<mblock_txn_cnt; txn_idx++ ) {
    3596           0 :         fd_runtime_update_program_cache( slot_ctx, &mblock_txn_ptrs[txn_idx], runtime_spad );
    3597           0 :       }
    3598             : 
    3599           0 :       res = fd_runtime_process_txns_in_microblock_stream( slot_ctx,
    3600           0 :                                                           capture_ctx,
    3601           0 :                                                           mblock_txn_ptrs,
    3602           0 :                                                           mblock_txn_cnt,
    3603           0 :                                                           tpool,
    3604           0 :                                                           exec_spads,
    3605           0 :                                                           exec_spad_cnt,
    3606           0 :                                                           runtime_spad,
    3607           0 :                                                           cost_tracker );
    3608           0 :       if( FD_UNLIKELY( res!=FD_RUNTIME_EXECUTE_SUCCESS ) ) {
    3609           0 :         return res;
    3610           0 :       }
    3611           0 :     }
    3612           0 :   }
    3613             : 
    3614           0 :   long block_finalize_time = -fd_log_wallclock();
    3615             : 
    3616           0 :   fd_exec_para_cb_ctx_t exec_para_ctx = {
    3617           0 :     .func       = block_finalize_tpool_wrapper,
    3618           0 :     .para_arg_1 = tpool
    3619           0 :   };
    3620             : 
    3621           0 :   res = fd_runtime_block_execute_finalize_para( slot_ctx,
    3622           0 :                                                 capture_ctx,
    3623           0 :                                                 block_info,
    3624           0 :                                                 fd_tpool_worker_cnt( tpool ),
    3625           0 :                                                 runtime_spad,
    3626           0 :                                                 &exec_para_ctx );
    3627           0 :   if( FD_UNLIKELY( res!=FD_RUNTIME_EXECUTE_SUCCESS ) ) {
    3628           0 :     return res;
    3629           0 :   }
    3630             : 
    3631           0 :   block_finalize_time += fd_log_wallclock();
    3632           0 :   double block_finalize_time_ms = (double)block_finalize_time * 1e-6;
    3633           0 :   FD_LOG_INFO(( "finalized block successfully - slot: %lu, elapsed: %6.6f ms", slot_ctx->slot, block_finalize_time_ms ));
    3634             : 
    3635           0 :   block_execute_time += fd_log_wallclock();
    3636           0 :   double block_execute_time_ms = (double)block_execute_time * 1e-6;
    3637             : 
    3638           0 :   FD_LOG_INFO(( "executed block successfully - slot: %lu, elapsed: %6.6f ms", slot_ctx->slot, block_execute_time_ms ));
    3639             : 
    3640           0 :   return FD_RUNTIME_EXECUTE_SUCCESS;
    3641           0 : }
    3642             : 
    3643             : void
    3644             : fd_runtime_block_pre_execute_process_new_epoch( fd_exec_slot_ctx_t * slot_ctx,
    3645             :                                                 fd_tpool_t *         tpool,
    3646             :                                                 fd_spad_t * *        exec_spads,
    3647             :                                                 ulong                exec_spad_cnt,
    3648             :                                                 fd_spad_t *          runtime_spad,
    3649           0 :                                                 int *                is_epoch_boundary ) {
    3650             : 
    3651             :   /* Update block height. */
    3652           0 :   fd_bank_block_height_set( slot_ctx->bank, fd_bank_block_height_get( slot_ctx->bank ) + 1UL );
    3653             : 
    3654           0 :   if( slot_ctx->slot != 0UL ) {
    3655           0 :     fd_epoch_schedule_t const * epoch_schedule = fd_bank_epoch_schedule_query( slot_ctx->bank );
    3656             : 
    3657           0 :     ulong             slot_idx;
    3658           0 :     ulong             prev_epoch = fd_slot_to_epoch( epoch_schedule, fd_bank_prev_slot_get( slot_ctx->bank ), &slot_idx );
    3659           0 :     ulong             new_epoch  = fd_slot_to_epoch( epoch_schedule, slot_ctx->slot, &slot_idx );
    3660           0 :     if( FD_UNLIKELY( slot_idx==1UL && new_epoch==0UL ) ) {
    3661             :       /* The block after genesis has a height of 1. */
    3662           0 :       fd_bank_block_height_set( slot_ctx->bank, 1UL );
    3663           0 :     }
    3664             : 
    3665           0 :     if( FD_UNLIKELY( prev_epoch<new_epoch || !slot_idx ) ) {
    3666           0 :       FD_LOG_DEBUG(( "Epoch boundary" ));
    3667             :       /* Epoch boundary! */
    3668           0 :       fd_runtime_process_new_epoch( slot_ctx,
    3669           0 :                                     new_epoch - 1UL,
    3670           0 :                                     tpool,
    3671           0 :                                     exec_spads,
    3672           0 :                                     exec_spad_cnt,
    3673           0 :                                     runtime_spad );
    3674           0 :       *is_epoch_boundary = 1;
    3675           0 :     }
    3676           0 :   } else {
    3677           0 :     *is_epoch_boundary = 0;
    3678           0 :   }
    3679             : 
    3680           0 :   if( FD_LIKELY( slot_ctx->slot!=0UL ) ) {
    3681           0 :     fd_distribute_partitioned_epoch_rewards( slot_ctx,
    3682           0 :                                              tpool,
    3683           0 :                                              exec_spads,
    3684           0 :                                              exec_spad_cnt,
    3685           0 :                                              runtime_spad );
    3686           0 :   }
    3687           0 : }
    3688             : 
    3689             : int
    3690             : fd_runtime_block_eval_tpool( fd_exec_slot_ctx_t * slot_ctx,
    3691             :                              ulong                slot,
    3692             :                              fd_block_t *         block,
    3693             :                              fd_capture_ctx_t *   capture_ctx,
    3694             :                              fd_tpool_t *         tpool,
    3695             :                              ulong                scheduler,
    3696             :                              ulong *              txn_cnt,
    3697             :                              fd_spad_t * *        exec_spads,
    3698             :                              ulong                exec_spad_cnt,
    3699             :                              fd_spad_t *          runtime_spad,
    3700           0 :                              fd_blockstore_t *    blockstore ) {
    3701             : 
    3702             :   /* offline replay */
    3703           0 :   (void)scheduler;
    3704             : 
    3705           0 :   int err = fd_runtime_publish_old_txns( slot_ctx, capture_ctx, tpool, runtime_spad );
    3706           0 :   if( err != 0 ) {
    3707           0 :     return err;
    3708           0 :   }
    3709             : 
    3710           0 :   fd_funk_t * funk = slot_ctx->funk;
    3711             : 
    3712           0 :   long block_eval_time = -fd_log_wallclock();
    3713           0 :   fd_runtime_block_info_t block_info;
    3714           0 :   int ret = FD_RUNTIME_EXECUTE_SUCCESS;
    3715           0 :   do {
    3716             : 
    3717             :     /* Start a new funk txn. */
    3718             : 
    3719           0 :     fd_funk_txn_xid_t xid = { .ul = { slot, slot } };
    3720           0 :     fd_funk_txn_start_write( funk );
    3721           0 :     slot_ctx->funk_txn = fd_funk_txn_prepare( funk, slot_ctx->funk_txn, &xid, 1 );
    3722           0 :     fd_funk_txn_end_write( funk );
    3723             : 
    3724           0 :     slot_ctx->slot = slot;
    3725             : 
    3726             :     /* Capturing block-agnostic state in preparation for the epoch boundary */
    3727           0 :     uchar dump_block = capture_ctx && slot >= capture_ctx->dump_proto_start_slot && capture_ctx->dump_block_to_pb;
    3728           0 :     fd_exec_test_block_context_t * block_ctx = NULL;
    3729           0 :     if( FD_UNLIKELY( dump_block ) ) {
    3730             :       /* TODO: This probably should get allocated from a separate spad for the capture ctx */
    3731           0 :       block_ctx = fd_spad_alloc( runtime_spad, alignof(fd_exec_test_block_context_t), sizeof(fd_exec_test_block_context_t) );
    3732           0 :       fd_memset( block_ctx, 0, sizeof(fd_exec_test_block_context_t) );
    3733           0 :       fd_dump_block_to_protobuf( slot_ctx, capture_ctx, runtime_spad, block_ctx );
    3734           0 :     }
    3735             : 
    3736           0 :     int is_epoch_boundary = 0;
    3737           0 :     fd_runtime_block_pre_execute_process_new_epoch( slot_ctx,
    3738           0 :                                                     tpool,
    3739           0 :                                                     exec_spads,
    3740           0 :                                                     exec_spad_cnt,
    3741           0 :                                                     runtime_spad,
    3742           0 :                                                     &is_epoch_boundary );
    3743             : 
    3744             :     /* All runtime allocations here are scoped to the end of a block. */
    3745           0 :     FD_SPAD_FRAME_BEGIN( runtime_spad ) {
    3746             : 
    3747           0 :     if( FD_UNLIKELY( (ret = fd_runtime_block_prepare( blockstore,
    3748           0 :                                                       block,
    3749           0 :                                                       slot,
    3750           0 :                                                       runtime_spad,
    3751           0 :                                                       &block_info )) != FD_RUNTIME_EXECUTE_SUCCESS ) ) {
    3752           0 :       break;
    3753           0 :     }
    3754           0 :     *txn_cnt = block_info.txn_cnt;
    3755             : 
    3756           0 :     fd_hash_t poh_out = {0};
    3757           0 :     fd_hash_t poh_in = fd_bank_poh_get( slot_ctx->bank );
    3758           0 :     if( FD_UNLIKELY( (ret = fd_runtime_block_verify_tpool( slot_ctx, blockstore, &block_info, &poh_in, &poh_out, tpool, runtime_spad )) != FD_RUNTIME_EXECUTE_SUCCESS ) ) {
    3759           0 :       break;
    3760           0 :     }
    3761             : 
    3762           0 :     fd_bank_poh_set( slot_ctx->bank, poh_out );
    3763             : 
    3764             :     /* Dump the remainder of the block after preparation, POH verification, etc */
    3765           0 :     if( FD_UNLIKELY( dump_block ) ) {
    3766           0 :       fd_dump_block_to_protobuf_tx_only( &block_info, slot_ctx, capture_ctx, runtime_spad, block_ctx );
    3767           0 :     }
    3768             : 
    3769           0 :     if( FD_UNLIKELY( (ret = fd_runtime_block_execute_tpool( slot_ctx,
    3770           0 :                                                             blockstore,
    3771           0 :                                                             capture_ctx,
    3772           0 :                                                             &block_info,
    3773           0 :                                                             tpool,
    3774           0 :                                                             exec_spads,
    3775           0 :                                                             exec_spad_cnt,
    3776           0 :                                                             runtime_spad )) != FD_RUNTIME_EXECUTE_SUCCESS ) ) {
    3777           0 :       break;
    3778           0 :     }
    3779             : 
    3780           0 :     } FD_SPAD_FRAME_END;
    3781             : 
    3782           0 :   } while( 0 );
    3783             : 
    3784             :   // FIXME: better way of using starting slot
    3785           0 :   if( FD_UNLIKELY( FD_RUNTIME_EXECUTE_SUCCESS != ret ) ) {
    3786           0 :     FD_LOG_WARNING(( "execution failure, code %d", ret ));
    3787             :     /* Skip over slot next time */
    3788             :     // slot_ctx->slot = slot+1UL;
    3789           0 :     return ret;
    3790           0 :   }
    3791             : 
    3792           0 :   block_eval_time          += fd_log_wallclock();
    3793           0 :   double block_eval_time_ms = (double)block_eval_time * 1e-6;
    3794           0 :   double tps                = (double) block_info.txn_cnt / ((double)block_eval_time * 1e-9);
    3795           0 :   fd_epoch_leaders_t const * leaders = fd_bank_epoch_leaders_locking_query( slot_ctx->bank );
    3796           0 :   fd_pubkey_t const *        leader  = fd_epoch_leaders_get( leaders, slot );
    3797           0 :   FD_LOG_INFO(( "evaluated block successfully - slot: %lu, elapsed: %6.6f ms, signatures: %lu, txns: %lu, tps: %6.6f, leader: %s",
    3798           0 :                 slot,
    3799           0 :                 block_eval_time_ms,
    3800           0 :                 block_info.signature_cnt,
    3801           0 :                 block_info.txn_cnt,
    3802           0 :                 tps,
    3803           0 :                 FD_BASE58_ENC_32_ALLOCA( leader ) ));
    3804           0 :   fd_bank_epoch_leaders_end_locking_query( slot_ctx->bank );
    3805             : 
    3806           0 :   fd_bank_transaction_count_set( slot_ctx->bank, fd_bank_transaction_count_get( slot_ctx->bank ) + block_info.txn_cnt );
    3807             : 
    3808           0 :   fd_bank_prev_slot_set( slot_ctx->bank, slot );
    3809             :   // FIXME: this shouldn't be doing this, it doesn't work with forking. punting changing it though
    3810             :   // slot_ctx->slot = slot+1UL;
    3811             : 
    3812           0 :   return 0;
    3813           0 : }
    3814             : 
    3815             : /******************************************************************************/
    3816             : /* Debugging Tools                                                            */
    3817             : /******************************************************************************/
    3818             : 
    3819             : void
    3820             : fd_runtime_checkpt( fd_capture_ctx_t *   capture_ctx,
    3821             :                     fd_exec_slot_ctx_t * slot_ctx,
    3822           0 :                     ulong                slot ) {
    3823           0 :   int is_checkpt_freq = capture_ctx != NULL && slot % capture_ctx->checkpt_freq == 0;
    3824           0 :   int is_abort_slot   = slot == ULONG_MAX;
    3825           0 :   if( !is_checkpt_freq && !is_abort_slot ) {
    3826           0 :     return;
    3827           0 :   }
    3828             : 
    3829           0 :   if( capture_ctx->checkpt_path != NULL ) {
    3830           0 :     if( !is_abort_slot ) {
    3831           0 :       FD_LOG_NOTICE(( "checkpointing at slot=%lu to file=%s", slot, capture_ctx->checkpt_path ));
    3832           0 :     } else {
    3833           0 :       FD_LOG_NOTICE(( "checkpointing after mismatch to file=%s", capture_ctx->checkpt_path ));
    3834           0 :     }
    3835             : 
    3836           0 :     unlink( capture_ctx->checkpt_path );
    3837           0 :     int err = fd_wksp_checkpt( fd_funk_wksp( slot_ctx->funk ), capture_ctx->checkpt_path, 0666, 0, NULL );
    3838           0 :     if ( err ) {
    3839           0 :       FD_LOG_ERR(( "backup failed: error %d", err ));
    3840           0 :     }
    3841           0 :   }
    3842           0 : }

Generated by: LCOV version 1.14