LCOV - code coverage report
Current view: top level - flamenco/runtime - fd_runtime.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 1649 0.0 %
Date: 2025-08-05 05:04:49 Functions: 0 46 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 "sysvar/fd_sysvar_cache.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             : 
      18             : #include "../stakes/fd_stakes.h"
      19             : #include "../rewards/fd_rewards.h"
      20             : 
      21             : #include "context/fd_exec_txn_ctx.h"
      22             : #include "info/fd_microblock_batch_info.h"
      23             : #include "info/fd_microblock_info.h"
      24             : 
      25             : #include "program/fd_stake_program.h"
      26             : #include "program/fd_builtin_programs.h"
      27             : #include "program/fd_vote_program.h"
      28             : #include "program/fd_program_cache.h"
      29             : #include "program/fd_bpf_loader_program.h"
      30             : #include "program/fd_address_lookup_table_program.h"
      31             : 
      32             : #include "sysvar/fd_sysvar_clock.h"
      33             : #include "sysvar/fd_sysvar_last_restart_slot.h"
      34             : #include "sysvar/fd_sysvar_recent_hashes.h"
      35             : #include "sysvar/fd_sysvar_rent.h"
      36             : #include "sysvar/fd_sysvar_slot_hashes.h"
      37             : #include "sysvar/fd_sysvar_slot_history.h"
      38             : 
      39             : #include "tests/fd_dump_pb.h"
      40             : 
      41             : #include "fd_system_ids.h"
      42             : #include "../vm/fd_vm.h"
      43             : #include "../../disco/pack/fd_pack.h"
      44             : 
      45             : #include <unistd.h>
      46             : #include <sys/stat.h>
      47             : #include <sys/types.h>
      48             : #include <errno.h>
      49             : #include <fcntl.h>
      50             : 
      51             : /******************************************************************************/
      52             : /* Public Runtime Helpers                                                     */
      53             : /******************************************************************************/
      54             : 
      55             : int
      56           0 : fd_runtime_should_use_vote_keyed_leader_schedule( fd_bank_t * bank ) {
      57             :   /* Agave uses an option type for their `effective_epoch` value. We represent None
      58             :      as ULONG_MAX and Some(value) as the value.
      59             :      https://github.com/anza-xyz/agave/blob/v2.3.1/runtime/src/bank.rs#L6149-L6165 */
      60           0 :   if( FD_FEATURE_ACTIVE_BANK( bank, enable_vote_address_leader_schedule ) ) {
      61             :     /* Return the first epoch if activated at genesis
      62             :        https://github.com/anza-xyz/agave/blob/v2.3.1/runtime/src/bank.rs#L6153-L6157 */
      63           0 :     ulong activation_slot = fd_bank_features_query( bank )->enable_vote_address_leader_schedule;
      64           0 :     if( activation_slot==0UL ) return 0;
      65             : 
      66             :     /* Calculate the epoch that the feature became activated in
      67             :        https://github.com/anza-xyz/agave/blob/v2.3.1/runtime/src/bank.rs#L6159-L6160 */
      68           0 :     fd_epoch_schedule_t const * epoch_schedule = fd_bank_epoch_schedule_query( bank );
      69           0 :     ulong activation_epoch = fd_slot_to_epoch( epoch_schedule, activation_slot, NULL );
      70             : 
      71             :     /* The effective epoch is the epoch immediately after the activation epoch
      72             :        https://github.com/anza-xyz/agave/blob/v2.3.1/runtime/src/bank.rs#L6162-L6164 */
      73           0 :     ulong effective_epoch = activation_epoch + 1UL;
      74           0 :     ulong current_epoch   = fd_bank_epoch_get( bank );
      75             : 
      76             :     /* https://github.com/anza-xyz/agave/blob/v2.3.1/runtime/src/bank.rs#L6167-L6170 */
      77           0 :     return !!( current_epoch >= effective_epoch );
      78           0 :   }
      79             : 
      80             :   /* ...The rest of the logic in this function either returns `None` or `Some(false)` so we
      81             :      will just return 0 by default. */
      82           0 :   return 0;
      83           0 : }
      84             : 
      85             : /*
      86             :    https://github.com/anza-xyz/agave/blob/v2.1.1/runtime/src/bank.rs#L1254-L1258
      87             :    https://github.com/anza-xyz/agave/blob/v2.1.1/runtime/src/bank.rs#L1749
      88             :  */
      89             : int
      90             : fd_runtime_compute_max_tick_height( ulong   ticks_per_slot,
      91             :                                     ulong   slot,
      92           0 :                                     ulong * out_max_tick_height /* out */ ) {
      93           0 :   ulong max_tick_height = 0UL;
      94           0 :   if( FD_LIKELY( ticks_per_slot > 0UL ) ) {
      95           0 :     ulong next_slot = fd_ulong_sat_add( slot, 1UL );
      96           0 :     if( FD_UNLIKELY( next_slot == slot ) ) {
      97           0 :       FD_LOG_WARNING(( "max tick height addition overflowed slot %lu ticks_per_slot %lu", slot, ticks_per_slot ));
      98           0 :       return -1;
      99           0 :     }
     100           0 :     if( FD_UNLIKELY( ULONG_MAX / ticks_per_slot < next_slot ) ) {
     101           0 :       FD_LOG_WARNING(( "max tick height multiplication overflowed slot %lu ticks_per_slot %lu", slot, ticks_per_slot ));
     102           0 :       return -1;
     103           0 :     }
     104           0 :     max_tick_height = fd_ulong_sat_mul( next_slot, ticks_per_slot );
     105           0 :   }
     106           0 :   *out_max_tick_height = max_tick_height;
     107           0 :   return FD_RUNTIME_EXECUTE_SUCCESS;
     108           0 : }
     109             : 
     110             : void
     111             : fd_runtime_update_slots_per_epoch( fd_bank_t * bank,
     112           0 :                                    ulong       slots_per_epoch ) {
     113           0 :   if( FD_LIKELY( slots_per_epoch == fd_bank_slots_per_epoch_get( bank ) ) ) {
     114           0 :     return;
     115           0 :   }
     116             : 
     117           0 :   fd_bank_slots_per_epoch_set( bank, slots_per_epoch );
     118             : 
     119           0 :   fd_bank_part_width_set( bank, fd_rent_partition_width( slots_per_epoch ) );
     120           0 : }
     121             : 
     122             : void
     123             : fd_runtime_update_leaders( fd_bank_t * bank,
     124             :                            ulong       slot,
     125           0 :                            fd_spad_t * runtime_spad ) {
     126             : 
     127           0 :   FD_SPAD_FRAME_BEGIN( runtime_spad ) {
     128             : 
     129           0 :   fd_epoch_schedule_t const * epoch_schedule = fd_bank_epoch_schedule_query( bank );
     130             : 
     131           0 :   fd_vote_accounts_global_t const *          epoch_vaccs   = fd_bank_epoch_stakes_locking_query( bank );
     132           0 :   fd_vote_accounts_pair_global_t_mapnode_t * vote_acc_pool = fd_vote_accounts_vote_accounts_pool_join( epoch_vaccs );
     133           0 :   fd_vote_accounts_pair_global_t_mapnode_t * vote_acc_root = fd_vote_accounts_vote_accounts_root_join( epoch_vaccs );
     134             : 
     135           0 :   ulong epoch    = fd_slot_to_epoch ( epoch_schedule, slot, NULL );
     136           0 :   ulong slot0    = fd_epoch_slot0   ( epoch_schedule, epoch );
     137           0 :   ulong slot_cnt = fd_epoch_slot_cnt( epoch_schedule, epoch );
     138             : 
     139           0 :   fd_runtime_update_slots_per_epoch( bank, fd_epoch_slot_cnt( epoch_schedule, epoch ) );
     140             : 
     141           0 :   ulong vote_acc_cnt  = fd_vote_accounts_pair_global_t_map_size( vote_acc_pool, vote_acc_root );
     142           0 :   fd_bank_epoch_stakes_end_locking_query( bank );
     143             : 
     144           0 :   fd_vote_stake_weight_t * epoch_weights = fd_spad_alloc_check( runtime_spad, alignof(fd_vote_stake_weight_t), vote_acc_cnt * sizeof(fd_vote_stake_weight_t) );
     145           0 :   ulong stake_weight_cnt = fd_stake_weights_by_node( epoch_vaccs, epoch_weights );
     146             : 
     147           0 :   if( FD_UNLIKELY( stake_weight_cnt == ULONG_MAX ) ) {
     148           0 :     FD_LOG_ERR(( "fd_stake_weights_by_node() failed" ));
     149           0 :   }
     150             : 
     151             :   /* Derive leader schedule */
     152             : 
     153           0 :   FD_LOG_INFO(( "stake_weight_cnt=%lu slot_cnt=%lu", stake_weight_cnt, slot_cnt ));
     154           0 :   ulong epoch_leaders_footprint = fd_epoch_leaders_footprint( stake_weight_cnt, slot_cnt );
     155           0 :   FD_LOG_INFO(( "epoch_leaders_footprint=%lu", epoch_leaders_footprint ));
     156           0 :   if( FD_LIKELY( epoch_leaders_footprint ) ) {
     157           0 :     if( FD_UNLIKELY( stake_weight_cnt>MAX_PUB_CNT ) ) {
     158           0 :       FD_LOG_ERR(( "Stake weight count exceeded max" ));
     159           0 :     }
     160           0 :     if( FD_UNLIKELY( slot_cnt>MAX_SLOTS_PER_EPOCH ) ) {
     161           0 :       FD_LOG_ERR(( "Slot count exceeeded max" ));
     162           0 :     }
     163             : 
     164           0 :     ulong vote_keyed_lsched = (ulong)fd_runtime_should_use_vote_keyed_leader_schedule( bank );
     165           0 :     void * epoch_leaders_mem = fd_bank_epoch_leaders_locking_modify( bank );
     166           0 :     fd_epoch_leaders_t * leaders = fd_epoch_leaders_join( fd_epoch_leaders_new( epoch_leaders_mem,
     167           0 :                                                                                            epoch,
     168           0 :                                                                                            slot0,
     169           0 :                                                                                            slot_cnt,
     170           0 :                                                                                            stake_weight_cnt,
     171           0 :                                                                                            epoch_weights,
     172           0 :                                                                                            0UL,
     173           0 :                                                                                            vote_keyed_lsched ) );
     174           0 :     fd_bank_epoch_leaders_end_locking_modify( bank );
     175           0 :     if( FD_UNLIKELY( !leaders ) ) {
     176           0 :       FD_LOG_ERR(( "Unable to init and join fd_epoch_leaders" ));
     177           0 :     }
     178           0 :   }
     179             : 
     180           0 :   } FD_SPAD_FRAME_END;
     181           0 : }
     182             : 
     183             : /******************************************************************************/
     184             : /* Various Private Runtime Helpers                                            */
     185             : /******************************************************************************/
     186             : 
     187             : /* fee to be deposited should be > 0
     188             :    Returns 0 if validation succeeds
     189             :    Returns the amount to burn(==fee) on failure */
     190             : static ulong
     191             : fd_runtime_validate_fee_collector( fd_bank_t *              bank,
     192             :                                    fd_txn_account_t const * collector,
     193           0 :                                    ulong                    fee ) {
     194           0 :   if( FD_UNLIKELY( fee<=0UL ) ) {
     195           0 :     FD_LOG_ERR(( "expected fee(%lu) to be >0UL", fee ));
     196           0 :   }
     197             : 
     198           0 :   if( FD_UNLIKELY( memcmp( collector->vt->get_owner( collector ), fd_solana_system_program_id.key, sizeof(fd_pubkey_t) ) ) ) {
     199           0 :     FD_BASE58_ENCODE_32_BYTES( collector->pubkey->key, _out_key );
     200           0 :     FD_LOG_WARNING(( "cannot pay a non-system-program owned account (%s)", _out_key ));
     201           0 :     return fee;
     202           0 :   }
     203             : 
     204             :   /* https://github.com/anza-xyz/agave/blob/v1.18.23/runtime/src/bank/fee_distribution.rs#L111
     205             :      https://github.com/anza-xyz/agave/blob/v1.18.23/runtime/src/accounts/account_rent_state.rs#L39
     206             :      In agave's fee deposit code, rent state transition check logic is as follows:
     207             :      The transition is NOT allowed iff
     208             :      === BEGIN
     209             :      the post deposit account is rent paying AND the pre deposit account is not rent paying
     210             :      OR
     211             :      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)
     212             :      === END
     213             :      post_data_size == pre_data_size is always true during fee deposit.
     214             :      However, post_lamports > pre_lamports because we are paying a >0 amount.
     215             :      So, the above reduces down to
     216             :      === BEGIN
     217             :      the post deposit account is rent paying AND the pre deposit account is not rent paying
     218             :      OR
     219             :      the post deposit account is rent paying AND the pre deposit account is rent paying AND TRUE
     220             :      === END
     221             :      This is equivalent to checking that the post deposit account is rent paying.
     222             :      An account is rent paying if the post deposit balance is >0 AND it's not rent exempt.
     223             :      We already know that the post deposit balance is >0 because we are paying a >0 amount.
     224             :      So TLDR we just check if the account is rent exempt.
     225             :    */
     226           0 :   fd_rent_t const * rent = fd_bank_rent_query( bank );
     227           0 :   ulong minbal = fd_rent_exempt_minimum_balance( rent, collector->vt->get_data_len( collector ) );
     228           0 :   if( FD_UNLIKELY( collector->vt->get_lamports( collector ) + fee < minbal ) ) {
     229           0 :     FD_BASE58_ENCODE_32_BYTES( collector->pubkey->key, _out_key );
     230           0 :     FD_LOG_WARNING(("cannot pay a rent paying account (%s)", _out_key ));
     231           0 :     return fee;
     232           0 :   }
     233             : 
     234           0 :   return 0UL;
     235           0 : }
     236             : 
     237             : static int
     238             : fd_runtime_run_incinerator( fd_bank_t *     bank,
     239             :                             fd_funk_t *     funk,
     240           0 :                             fd_funk_txn_t * funk_txn ) {
     241           0 :   FD_TXN_ACCOUNT_DECL( rec );
     242             : 
     243           0 :   int err = fd_txn_account_init_from_funk_mutable( rec,
     244           0 :                                                    &fd_sysvar_incinerator_id,
     245           0 :                                                    funk,
     246           0 :                                                    funk_txn,
     247           0 :                                                    0,
     248           0 :                                                    0UL );
     249           0 :   if( FD_UNLIKELY( err!=FD_ACC_MGR_SUCCESS ) ) {
     250             :     // TODO: not really an error! This is fine!
     251           0 :     return -1;
     252           0 :   }
     253             : 
     254           0 :   ulong new_capitalization = fd_ulong_sat_sub( fd_bank_capitalization_get( bank ), rec->vt->get_lamports( rec ) );
     255           0 :   fd_bank_capitalization_set( bank, new_capitalization );
     256             : 
     257           0 :   rec->vt->set_lamports( rec, 0UL );
     258           0 :   fd_txn_account_mutable_fini( rec, funk, funk_txn );
     259             : 
     260           0 :   return 0;
     261           0 : }
     262             : 
     263             : static void
     264           0 : fd_runtime_freeze( fd_exec_slot_ctx_t * slot_ctx ) {
     265             : 
     266           0 :   fd_sysvar_recent_hashes_update( slot_ctx );
     267             : 
     268           0 :   ulong execution_fees = fd_bank_execution_fees_get( slot_ctx->bank );
     269           0 :   ulong priority_fees  = fd_bank_priority_fees_get( slot_ctx->bank );
     270             : 
     271           0 :   ulong burn = execution_fees / 2;
     272           0 :   ulong fees = fd_ulong_sat_add( priority_fees, execution_fees - burn );
     273             : 
     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 :         fd_bank_epoch_leaders_end_locking_query( slot_ctx->bank );
     286           0 :         break;
     287           0 :       }
     288             : 
     289           0 :       fd_pubkey_t const * leader = fd_epoch_leaders_get( leaders, fd_bank_slot_get( slot_ctx->bank ) );
     290           0 :       if( FD_UNLIKELY( !leader ) ) {
     291           0 :         FD_LOG_WARNING(( "fd_runtime_freeze: leader not found" ));
     292           0 :         fd_bank_epoch_leaders_end_locking_query( slot_ctx->bank );
     293           0 :         break;
     294           0 :       }
     295             : 
     296           0 :       int err = fd_txn_account_init_from_funk_mutable( rec, leader, slot_ctx->funk, slot_ctx->funk_txn, 1, 0UL );
     297           0 :       if( FD_UNLIKELY( err ) ) {
     298           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));
     299           0 :         burn = fd_ulong_sat_add( burn, fees );
     300           0 :         fd_bank_epoch_leaders_end_locking_query( slot_ctx->bank );
     301           0 :         break;
     302           0 :       }
     303             : 
     304           0 :       fd_bank_epoch_leaders_end_locking_query( slot_ctx->bank );
     305             : 
     306           0 :       if ( FD_LIKELY( FD_FEATURE_ACTIVE_BANK( slot_ctx->bank, validate_fee_collector_account ) ) ) {
     307           0 :         ulong _burn;
     308           0 :         if( FD_UNLIKELY( _burn=fd_runtime_validate_fee_collector( slot_ctx->bank, rec, fees ) ) ) {
     309           0 :           if( FD_UNLIKELY( _burn!=fees ) ) {
     310           0 :             FD_LOG_ERR(( "expected _burn(%lu)==fees(%lu)", _burn, fees ));
     311           0 :           }
     312           0 :           burn = fd_ulong_sat_add( burn, fees );
     313           0 :           FD_LOG_WARNING(("fd_runtime_freeze: burned %lu", fees ));
     314           0 :           break;
     315           0 :         }
     316           0 :       }
     317             : 
     318             :       /* TODO: is it ok to not check the overflow error here? */
     319           0 :       rec->vt->checked_add_lamports( rec, fees );
     320           0 :       rec->vt->set_slot( rec, fd_bank_slot_get( slot_ctx->bank ) );
     321             : 
     322           0 :       fd_txn_account_mutable_fini( rec, slot_ctx->funk, slot_ctx->funk_txn );
     323             : 
     324           0 :     } while(0);
     325             : 
     326           0 :     ulong old = fd_bank_capitalization_get( slot_ctx->bank );
     327           0 :     fd_bank_capitalization_set( slot_ctx->bank, fd_ulong_sat_sub( old, burn ) );
     328           0 :     FD_LOG_DEBUG(( "fd_runtime_freeze: burn %lu, capitalization %lu->%lu ", burn, old, fd_bank_capitalization_get( slot_ctx->bank ) ));
     329             : 
     330           0 :     fd_bank_execution_fees_set( slot_ctx->bank, 0UL );
     331             : 
     332           0 :     fd_bank_priority_fees_set( slot_ctx->bank, 0UL );
     333           0 :   }
     334             : 
     335           0 :   fd_runtime_run_incinerator( slot_ctx->bank, slot_ctx->funk, slot_ctx->funk_txn );
     336             : 
     337           0 : }
     338             : 
     339           0 : #define FD_RENT_EXEMPT (-1L)
     340             : 
     341             : static long
     342             : fd_runtime_get_rent_due( fd_epoch_schedule_t const * schedule,
     343             :                          fd_rent_t const *           rent,
     344             :                          double                      slots_per_year,
     345             :                          fd_txn_account_t *          acc,
     346           0 :                          ulong                       epoch ) {
     347             :   /* Nothing due if account is rent-exempt
     348             :      https://github.com/anza-xyz/agave/blob/v2.0.10/sdk/src/rent_collector.rs#L90 */
     349           0 :   ulong min_balance = fd_rent_exempt_minimum_balance( rent, acc->vt->get_data_len( acc ) );
     350           0 :   if( acc->vt->get_lamports( acc )>=min_balance ) {
     351           0 :     return FD_RENT_EXEMPT;
     352           0 :   }
     353             : 
     354             :   /* Count the number of slots that have passed since last collection. This
     355             :      inlines the agave function get_slots_in_peohc
     356             :      https://github.com/anza-xyz/agave/blob/v2.0.10/sdk/src/rent_collector.rs#L93-L98 */
     357           0 :   ulong slots_elapsed = 0UL;
     358           0 :   if( FD_UNLIKELY( acc->vt->get_rent_epoch( acc )<schedule->first_normal_epoch ) ) {
     359             :     /* Count the slots before the first normal epoch separately */
     360           0 :     for( ulong i=acc->vt->get_rent_epoch( acc ); i<schedule->first_normal_epoch && i<=epoch; i++ ) {
     361           0 :       slots_elapsed += fd_epoch_slot_cnt( schedule, i+1UL );
     362           0 :     }
     363           0 :     slots_elapsed += fd_ulong_sat_sub( epoch+1UL, schedule->first_normal_epoch ) * schedule->slots_per_epoch;
     364           0 :   }
     365             :   // slots_elapsed should remain 0 if rent_epoch is greater than epoch
     366           0 :   else if( acc->vt->get_rent_epoch( acc )<=epoch ) {
     367           0 :     slots_elapsed = (epoch - acc->vt->get_rent_epoch( acc ) + 1UL) * schedule->slots_per_epoch;
     368           0 :   }
     369             :   /* Consensus-critical use of doubles :( */
     370             : 
     371           0 :   double years_elapsed;
     372           0 :   if( FD_LIKELY( slots_per_year!=0.0 ) ) {
     373           0 :     years_elapsed = (double)slots_elapsed / slots_per_year;
     374           0 :   } else {
     375           0 :     years_elapsed = 0.0;
     376           0 :   }
     377             : 
     378           0 :   ulong lamports_per_year = rent->lamports_per_uint8_year * (acc->vt->get_data_len( acc ) + 128UL);
     379             :   /* https://github.com/anza-xyz/agave/blob/d2124a995f89e33c54f41da76bfd5b0bd5820898/sdk/src/rent_collector.rs#L108 */
     380             :   /* https://github.com/anza-xyz/agave/blob/d2124a995f89e33c54f41da76bfd5b0bd5820898/sdk/program/src/rent.rs#L95 */
     381           0 :   return (long)fd_rust_cast_double_to_ulong(years_elapsed * (double)lamports_per_year);
     382           0 : }
     383             : 
     384             : /* fd_runtime_collect_rent_from_account performs rent collection duties.
     385             :    Although the Solana runtime prevents the creation of new accounts
     386             :    that are subject to rent, some older accounts are still undergo the
     387             :    rent collection process.  Updates the account's 'rent_epoch' if
     388             :    needed. Returns the amount of rent collected. */
     389             : /* https://github.com/anza-xyz/agave/blob/v2.0.10/svm/src/account_loader.rs#L71-96 */
     390             : ulong
     391             : fd_runtime_collect_rent_from_account( fd_epoch_schedule_t const * schedule,
     392             :                                       fd_rent_t const *           rent,
     393             :                                       double                      slots_per_year,
     394             :                                       fd_txn_account_t *          acc,
     395           0 :                                       ulong                       epoch ) {
     396             : 
     397           0 :   if( FD_UNLIKELY( acc->vt->get_rent_epoch( acc )!=FD_RENT_EXEMPT_RENT_EPOCH &&
     398           0 :                      fd_runtime_get_rent_due( schedule,
     399           0 :                                               rent,
     400           0 :                                               slots_per_year,
     401           0 :                                               acc,
     402           0 :                                               epoch )==FD_RENT_EXEMPT ) ) {
     403           0 :       acc->vt->set_rent_epoch( acc, FD_RENT_EXEMPT_RENT_EPOCH );
     404           0 :   }
     405           0 :   return 0UL;
     406           0 : }
     407             : 
     408             : #undef FD_RENT_EXEMPT
     409             : 
     410             : /******************************************************************************/
     411             : /* Block-Level Execution Preparation/Finalization                             */
     412             : /******************************************************************************/
     413             : 
     414             : /*
     415             : https://github.com/firedancer-io/solana/blob/dab3da8e7b667d7527565bddbdbecf7ec1fb868e/sdk/program/src/fee_calculator.rs#L105-L165
     416             : */
     417             : static void
     418             : fd_runtime_new_fee_rate_governor_derived( fd_bank_t * bank,
     419           0 :                                           ulong       latest_signatures_per_slot ) {
     420             : 
     421           0 :   fd_fee_rate_governor_t const * base_fee_rate_governor = fd_bank_fee_rate_governor_query( bank );
     422             : 
     423           0 :   ulong old_lamports_per_signature = fd_bank_lamports_per_signature_get( bank );
     424             : 
     425           0 :   fd_fee_rate_governor_t me = {
     426           0 :     .target_signatures_per_slot    = base_fee_rate_governor->target_signatures_per_slot,
     427           0 :     .target_lamports_per_signature = base_fee_rate_governor->target_lamports_per_signature,
     428           0 :     .max_lamports_per_signature    = base_fee_rate_governor->max_lamports_per_signature,
     429           0 :     .min_lamports_per_signature    = base_fee_rate_governor->min_lamports_per_signature,
     430           0 :     .burn_percent                  = base_fee_rate_governor->burn_percent
     431           0 :   };
     432             : 
     433           0 :   ulong new_lamports_per_signature = 0;
     434           0 :   if( me.target_signatures_per_slot > 0 ) {
     435           0 :     me.min_lamports_per_signature = fd_ulong_max( 1UL, (ulong)(me.target_lamports_per_signature / 2) );
     436           0 :     me.max_lamports_per_signature = me.target_lamports_per_signature * 10;
     437           0 :     ulong desired_lamports_per_signature = fd_ulong_min(
     438           0 :       me.max_lamports_per_signature,
     439           0 :       fd_ulong_max(
     440           0 :         me.min_lamports_per_signature,
     441           0 :         me.target_lamports_per_signature
     442           0 :         * fd_ulong_min(latest_signatures_per_slot, (ulong)UINT_MAX)
     443           0 :         / me.target_signatures_per_slot
     444           0 :       )
     445           0 :     );
     446           0 :     long gap = (long)desired_lamports_per_signature - (long)old_lamports_per_signature;
     447           0 :     if ( gap == 0 ) {
     448           0 :       new_lamports_per_signature = desired_lamports_per_signature;
     449           0 :     } else {
     450           0 :       long gap_adjust = (long)(fd_ulong_max( 1UL, (ulong)(me.target_lamports_per_signature / 20) ))
     451           0 :         * (gap != 0)
     452           0 :         * (gap > 0 ? 1 : -1);
     453           0 :       new_lamports_per_signature = fd_ulong_min(
     454           0 :         me.max_lamports_per_signature,
     455           0 :         fd_ulong_max(
     456           0 :           me.min_lamports_per_signature,
     457           0 :           (ulong)((long)old_lamports_per_signature + gap_adjust)
     458           0 :         )
     459           0 :       );
     460           0 :     }
     461           0 :   } else {
     462           0 :     new_lamports_per_signature = base_fee_rate_governor->target_lamports_per_signature;
     463           0 :     me.min_lamports_per_signature = me.target_lamports_per_signature;
     464           0 :     me.max_lamports_per_signature = me.target_lamports_per_signature;
     465           0 :   }
     466             : 
     467           0 :   if( FD_UNLIKELY( old_lamports_per_signature==0UL ) ) {
     468           0 :     fd_bank_prev_lamports_per_signature_set( bank, new_lamports_per_signature );
     469           0 :   } else {
     470           0 :     fd_bank_prev_lamports_per_signature_set( bank, old_lamports_per_signature );
     471           0 :   }
     472             : 
     473           0 :   fd_bank_fee_rate_governor_set( bank, me );
     474             : 
     475           0 :   fd_bank_lamports_per_signature_set( bank, new_lamports_per_signature );
     476           0 : }
     477             : 
     478             : static int
     479             : fd_runtime_block_sysvar_update_pre_execute( fd_exec_slot_ctx_t * slot_ctx,
     480           0 :                                             fd_spad_t *          runtime_spad ) {
     481             :   // let (fee_rate_governor, fee_components_time_us) = measure_us!(
     482             :   //     FeeRateGovernor::new_derived(&parent.fee_rate_governor, parent.signature_count())
     483             :   // );
     484             :   /* https://github.com/firedancer-io/solana/blob/dab3da8e7b667d7527565bddbdbecf7ec1fb868e/runtime/src/bank.rs#L1312-L1314 */
     485             : 
     486           0 :   FD_SPAD_FRAME_BEGIN( runtime_spad ) {
     487             : 
     488           0 :   fd_runtime_new_fee_rate_governor_derived( slot_ctx->bank, fd_bank_parent_signature_cnt_get( slot_ctx->bank ) );
     489             : 
     490             :   // TODO: move all these out to a fd_sysvar_update() call...
     491           0 :   long clock_update_time = -fd_log_wallclock();
     492           0 :   fd_sysvar_clock_update( slot_ctx, runtime_spad );
     493           0 :   clock_update_time     += fd_log_wallclock();
     494           0 :   double clock_update_time_ms = (double)clock_update_time * 1e-6;
     495           0 :   FD_LOG_INFO(( "clock updated - slot: %lu, elapsed: %6.6f ms", fd_bank_slot_get( slot_ctx->bank ), clock_update_time_ms ));
     496             : 
     497             :   // It has to go into the current txn previous info but is not in slot 0
     498           0 :   if( fd_bank_slot_get( slot_ctx->bank ) != 0 ) {
     499           0 :     fd_sysvar_slot_hashes_update( slot_ctx, runtime_spad );
     500           0 :   }
     501           0 :   fd_sysvar_last_restart_slot_update( slot_ctx, fd_bank_last_restart_slot_get( slot_ctx->bank ).slot );
     502             : 
     503           0 :   } FD_SPAD_FRAME_END;
     504             : 
     505           0 :   return 0;
     506           0 : }
     507             : 
     508             : // int
     509             : // fd_runtime_microblock_verify_ticks( fd_blockstore_t *           blockstore,
     510             : //                                     ulong                       slot,
     511             : //                                     fd_microblock_hdr_t const * hdr,
     512             : //                                     bool               slot_complete,
     513             : //                                     ulong              tick_height,
     514             : //                                     ulong              max_tick_height,
     515             : //                                     ulong              hashes_per_tick ) {
     516             : //   ulong invalid_tick_hash_count = 0UL;
     517             : //   ulong has_trailing_entry      = 0UL;
     518             : 
     519             : //   /*
     520             : //     In order to mimic the order of checks in Agave,
     521             : //     we cache the results of some checks but do not immediately return
     522             : //     an error.
     523             : //   */
     524             : //   fd_block_map_query_t quer[1];
     525             : //   int err = fd_block_map_prepare( blockstore->block_map, &slot, NULL, quer, FD_MAP_FLAG_BLOCKING );
     526             : //   fd_block_info_t * query = fd_block_map_query_ele( quer );
     527             : //   if( FD_UNLIKELY( err || query->slot != slot ) ) {
     528             : //     FD_LOG_ERR(( "fd_runtime_microblock_verify_ticks: fd_block_map_prepare on %lu failed", slot ));
     529             : //   }
     530             : 
     531             : //   query->tick_hash_count_accum = fd_ulong_sat_add( query->tick_hash_count_accum, hdr->hash_cnt );
     532             : //   if( hdr->txn_cnt == 0UL ) {
     533             : //     query->ticks_consumed++;
     534             : //     if( FD_LIKELY( hashes_per_tick > 1UL ) ) {
     535             : //       if( FD_UNLIKELY( query->tick_hash_count_accum != hashes_per_tick ) ) {
     536             : //         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 ));
     537             : //         invalid_tick_hash_count = 1U;
     538             : //       }
     539             : //     }
     540             : //     query->tick_hash_count_accum = 0UL;
     541             : //   } else {
     542             : //     /* This wasn't a tick entry, but it's the last entry. */
     543             : //     if( FD_UNLIKELY( slot_complete ) ) {
     544             : //       FD_LOG_WARNING(( "last has %lu transactions expects 0", hdr->txn_cnt ));
     545             : //       has_trailing_entry = 1U;
     546             : //     }
     547             : //   }
     548             : 
     549             : //   ulong next_tick_height = tick_height + query->ticks_consumed;
     550             : //   fd_block_map_publish( quer );
     551             : 
     552             : //   if( FD_UNLIKELY( next_tick_height > max_tick_height ) ) {
     553             : //     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 ));
     554             : //     return FD_BLOCK_ERR_TOO_MANY_TICKS;
     555             : //   }
     556             : //   if( FD_UNLIKELY( slot_complete && next_tick_height < max_tick_height ) ) {
     557             : //     FD_LOG_WARNING(( "Too few ticks" ));
     558             : //     return FD_BLOCK_ERR_TOO_FEW_TICKS;
     559             : //   }
     560             : //   if( FD_UNLIKELY( slot_complete && has_trailing_entry ) ) {
     561             : //     FD_LOG_WARNING(( "Did not end with a tick" ));
     562             : //     return FD_BLOCK_ERR_TRAILING_ENTRY;
     563             : //   }
     564             : 
     565             : //   /* Not returning FD_BLOCK_ERR_INVALID_LAST_TICK because we assume the
     566             : //      slot is full. */
     567             : 
     568             : //   /* Don't care about low power hashing or no hashing. */
     569             : //   if( FD_LIKELY( hashes_per_tick > 1UL ) ) {
     570             : //     if( FD_UNLIKELY( invalid_tick_hash_count ) ) {
     571             : //       FD_LOG_WARNING(( "Tick with invalid number of hashes found" ));
     572             : //       return FD_BLOCK_ERR_INVALID_TICK_HASH_COUNT;
     573             : //     }
     574             : //   }
     575             : //   return FD_BLOCK_OK;
     576             : // }
     577             : 
     578             : // /* A streaming version of this by batch is implemented in batch_verify_ticks.
     579             : //    This block_verify_ticks should only used for offline replay. */
     580             : // ulong
     581             : // fd_runtime_block_verify_ticks( fd_blockstore_t * blockstore,
     582             : //                                ulong             slot,
     583             : //                                uchar *           block_data,
     584             : //                                ulong             block_data_sz,
     585             : //                                ulong             tick_height,
     586             : //                                ulong             max_tick_height,
     587             : //                                ulong             hashes_per_tick ) {
     588             : //   ulong tick_count              = 0UL;
     589             : //   ulong tick_hash_count         = 0UL;
     590             : //   ulong has_trailing_entry      = 0UL;
     591             : //   uchar invalid_tick_hash_count = 0U;
     592             : //   /*
     593             : //     Iterate over microblocks/entries to
     594             : //     (1) count the number of ticks
     595             : //     (2) check whether the last entry is a tick
     596             : //     (3) check whether ticks align with hashes per tick
     597             : 
     598             : //     This precomputes everything we need in a single loop over the array.
     599             : //     In order to mimic the order of checks in Agave,
     600             : //     we cache the results of some checks but do not immediately return
     601             : //     an error.
     602             : //    */
     603             : //   ulong slot_complete_idx = FD_SHRED_IDX_NULL;
     604             : //   fd_block_set_t data_complete_idxs[FD_SHRED_BLK_MAX / sizeof(ulong)];
     605             : //   int err = FD_MAP_ERR_AGAIN;
     606             : //   while( err == FD_MAP_ERR_AGAIN ) {
     607             : //     fd_block_map_query_t quer[1] = {0};
     608             : //     err = fd_block_map_query_try( blockstore->block_map, &slot, NULL, quer, 0 );
     609             : //     fd_block_info_t * query = fd_block_map_query_ele( quer );
     610             : //     if( FD_UNLIKELY( err == FD_MAP_ERR_AGAIN ) )continue;
     611             : //     if( FD_UNLIKELY( err == FD_MAP_ERR_KEY ) ) FD_LOG_ERR(( "fd_runtime_block_verify_ticks: fd_block_map_query_try failed" ));
     612             : //     slot_complete_idx = query->slot_complete_idx;
     613             : //     fd_memcpy( data_complete_idxs, query->data_complete_idxs, sizeof(data_complete_idxs) );
     614             : //     err = fd_block_map_query_test( quer );
     615             : //   }
     616             : 
     617             : //   uint   batch_cnt = 0;
     618             : //   ulong  batch_idx = 0;
     619             : //   while ( batch_idx <= slot_complete_idx ) {
     620             : //     batch_cnt++;
     621             : //     ulong batch_sz = 0;
     622             : //     uint  end_idx  = (uint)fd_block_set_const_iter_next( data_complete_idxs, batch_idx - 1 );
     623             : //     FD_TEST( fd_blockstore_slice_query( blockstore, slot, (uint) batch_idx, end_idx, block_data_sz, block_data, &batch_sz ) == FD_BLOCKSTORE_SUCCESS );
     624             : //     ulong micro_cnt = FD_LOAD( ulong, block_data );
     625             : //     ulong off       = sizeof(ulong);
     626             : //     for( ulong i = 0UL; i < micro_cnt; i++ ){
     627             : //       fd_microblock_hdr_t const * hdr = fd_type_pun_const( ( block_data + off ) );
     628             : //       off += sizeof(fd_microblock_hdr_t);
     629             : //       tick_hash_count = fd_ulong_sat_add( tick_hash_count, hdr->hash_cnt );
     630             : //       if( hdr->txn_cnt == 0UL ){
     631             : //         tick_count++;
     632             : //         if( FD_LIKELY( hashes_per_tick > 1UL ) ) {
     633             : //           if( FD_UNLIKELY( tick_hash_count != hashes_per_tick ) ) {
     634             : //             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 ));
     635             : //             invalid_tick_hash_count = 1U;
     636             : //           }
     637             : //         }
     638             : //         tick_hash_count = 0UL;
     639             : //         continue;
     640             : //       }
     641             : //       /* This wasn't a tick entry, but it's the last entry. */
     642             : //       if( FD_UNLIKELY( i == micro_cnt - 1UL ) ) {
     643             : //         has_trailing_entry = batch_cnt;
     644             : //       }
     645             : 
     646             : //       /* seek past txns */
     647             : //       uchar txn[FD_TXN_MAX_SZ];
     648             : //       for( ulong j = 0; j < hdr->txn_cnt; j++ ) {
     649             : //         ulong pay_sz = 0;
     650             : //         ulong txn_sz = fd_txn_parse_core( block_data + off, fd_ulong_min( batch_sz - off, FD_TXN_MTU ), txn, NULL, &pay_sz );
     651             : //         if( FD_UNLIKELY( !pay_sz ) ) FD_LOG_ERR(( "failed to parse transaction %lu in microblock %lu in slot %lu", j, i, slot ) );
     652             : //         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 ));
     653             : //         off += pay_sz;
     654             : //       }
     655             : //     }
     656             : //     /* advance batch iterator */
     657             : //     if( FD_UNLIKELY( batch_cnt == 1 ) ){ /* first batch */
     658             : //       batch_idx = fd_block_set_const_iter_init( data_complete_idxs ) + 1;
     659             : //     } else {
     660             : //       batch_idx = fd_block_set_const_iter_next( data_complete_idxs, batch_idx - 1 ) + 1;
     661             : //     }
     662             : //   }
     663             : 
     664             : //   ulong next_tick_height = tick_height + tick_count;
     665             : //   if( FD_UNLIKELY( next_tick_height > max_tick_height ) ) {
     666             : //     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 ));
     667             : //     FD_LOG_WARNING(( "Too many ticks" ));
     668             : //     return FD_BLOCK_ERR_TOO_MANY_TICKS;
     669             : //   }
     670             : //   if( FD_UNLIKELY( next_tick_height < max_tick_height ) ) {
     671             : //     FD_LOG_WARNING(( "Too few ticks" ));
     672             : //     return FD_BLOCK_ERR_TOO_FEW_TICKS;
     673             : //   }
     674             : //   if( FD_UNLIKELY( has_trailing_entry == batch_cnt ) ) {
     675             : //     FD_LOG_WARNING(( "Did not end with a tick" ));
     676             : //     return FD_BLOCK_ERR_TRAILING_ENTRY;
     677             : //   }
     678             : 
     679             : //   /* Not returning FD_BLOCK_ERR_INVALID_LAST_TICK because we assume the
     680             : //      slot is full. */
     681             : 
     682             : //   /* Don't care about low power hashing or no hashing. */
     683             : //   if( FD_LIKELY( hashes_per_tick > 1UL ) ) {
     684             : //     if( FD_UNLIKELY( invalid_tick_hash_count ) ) {
     685             : //       FD_LOG_WARNING(( "Tick with invalid number of hashes found" ));
     686             : //       return FD_BLOCK_ERR_INVALID_TICK_HASH_COUNT;
     687             : //     }
     688             : //   }
     689             : 
     690             : //   return FD_BLOCK_OK;
     691             : // }
     692             : 
     693             : int
     694             : fd_runtime_load_txn_address_lookup_tables(
     695             :     fd_txn_t const *       txn,
     696             :     uchar const *          payload,
     697             :     fd_funk_t *            funk,
     698             :     fd_funk_txn_t *        funk_txn,
     699             :     ulong                  slot,
     700             :     fd_slot_hash_t const * hashes, /* deque */
     701             :     fd_acct_addr_t *       out_accts_alt
     702           0 : ) {
     703             : 
     704           0 :   if( FD_LIKELY( txn->transaction_version!=FD_TXN_V0 ) ) return FD_RUNTIME_EXECUTE_SUCCESS;
     705             : 
     706           0 :   ulong            readonly_lut_accs_cnt = 0UL;
     707           0 :   ulong            writable_lut_accs_cnt = 0UL;
     708           0 :   fd_acct_addr_t * readonly_lut_accs     = out_accts_alt+txn->addr_table_adtl_writable_cnt;
     709           0 :   fd_txn_acct_addr_lut_t const * addr_luts = fd_txn_get_address_tables_const( txn );
     710           0 :   for( ulong i = 0UL; i < txn->addr_table_lookup_cnt; i++ ) {
     711           0 :     fd_txn_acct_addr_lut_t const * addr_lut  = &addr_luts[i];
     712           0 :     fd_pubkey_t const * addr_lut_acc = (fd_pubkey_t *)(payload + addr_lut->addr_off);
     713             : 
     714             :     /* https://github.com/anza-xyz/agave/blob/368ea563c423b0a85cc317891187e15c9a321521/accounts-db/src/accounts.rs#L90-L94 */
     715           0 :     FD_TXN_ACCOUNT_DECL( addr_lut_rec );
     716           0 :     int err = fd_txn_account_init_from_funk_readonly( addr_lut_rec,
     717           0 :                                                       addr_lut_acc,
     718           0 :                                                       funk,
     719           0 :                                                       funk_txn );
     720           0 :     if( FD_UNLIKELY( err != FD_ACC_MGR_SUCCESS ) ) {
     721           0 :       return FD_RUNTIME_TXN_ERR_ADDRESS_LOOKUP_TABLE_NOT_FOUND;
     722           0 :     }
     723             : 
     724             :     /* https://github.com/anza-xyz/agave/blob/368ea563c423b0a85cc317891187e15c9a321521/accounts-db/src/accounts.rs#L96-L114 */
     725           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) ) ) ) {
     726           0 :       return FD_RUNTIME_TXN_ERR_INVALID_ADDRESS_LOOKUP_TABLE_OWNER;
     727           0 :     }
     728             : 
     729             :     /* Realistically impossible case, but need to make sure we don't cause an OOB data access
     730             :        https://github.com/anza-xyz/agave/blob/368ea563c423b0a85cc317891187e15c9a321521/sdk/program/src/address_lookup_table/state.rs#L205-L209 */
     731           0 :     if( FD_UNLIKELY( addr_lut_rec->vt->get_data_len( addr_lut_rec ) < FD_LOOKUP_TABLE_META_SIZE ) ) {
     732           0 :       return FD_RUNTIME_TXN_ERR_INVALID_ADDRESS_LOOKUP_TABLE_DATA;
     733           0 :     }
     734             : 
     735             :     /* https://github.com/anza-xyz/agave/blob/574bae8fefc0ed256b55340b9d87b7689bcdf222/accounts-db/src/accounts.rs#L141-L142 */
     736           0 :     fd_bincode_decode_ctx_t decode_ctx = {
     737           0 :       .data    = addr_lut_rec->vt->get_data( addr_lut_rec ),
     738           0 :       .dataend = &addr_lut_rec->vt->get_data( addr_lut_rec )[FD_LOOKUP_TABLE_META_SIZE]
     739           0 :     };
     740             : 
     741           0 :     ulong total_sz = 0UL;
     742           0 :     err = fd_address_lookup_table_state_decode_footprint( &decode_ctx, &total_sz );
     743           0 :     if( FD_UNLIKELY( err ) ) {
     744           0 :       return FD_RUNTIME_TXN_ERR_INVALID_ADDRESS_LOOKUP_TABLE_DATA;
     745           0 :     }
     746             : 
     747             :     /* https://github.com/anza-xyz/agave/blob/368ea563c423b0a85cc317891187e15c9a321521/sdk/program/src/address_lookup_table/state.rs#L197-L214 */
     748           0 :     fd_address_lookup_table_state_t table[1];
     749           0 :     fd_address_lookup_table_state_t * addr_lookup_table_state = fd_address_lookup_table_state_decode( table, &decode_ctx );
     750             : 
     751             :     /* https://github.com/anza-xyz/agave/blob/368ea563c423b0a85cc317891187e15c9a321521/sdk/program/src/address_lookup_table/state.rs#L200-L203 */
     752           0 :     if( FD_UNLIKELY( addr_lookup_table_state->discriminant != fd_address_lookup_table_state_enum_lookup_table ) ) {
     753           0 :       return FD_RUNTIME_TXN_ERR_INVALID_ADDRESS_LOOKUP_TABLE_DATA;
     754           0 :     }
     755             : 
     756             :     /* Again probably an impossible case, but the ALUT data needs to be 32-byte aligned
     757             :        https://github.com/anza-xyz/agave/blob/368ea563c423b0a85cc317891187e15c9a321521/sdk/program/src/address_lookup_table/state.rs#L210-L214 */
     758           0 :     if( FD_UNLIKELY( (addr_lut_rec->vt->get_data_len( addr_lut_rec ) - FD_LOOKUP_TABLE_META_SIZE) & 0x1fUL ) ) {
     759           0 :       return FD_RUNTIME_TXN_ERR_INVALID_ADDRESS_LOOKUP_TABLE_DATA;
     760           0 :     }
     761             : 
     762             :     /* https://github.com/anza-xyz/agave/blob/368ea563c423b0a85cc317891187e15c9a321521/accounts-db/src/accounts.rs#L101-L112 */
     763           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];
     764           0 :     ulong         lookup_addrs_cnt = (addr_lut_rec->vt->get_data_len( addr_lut_rec ) - FD_LOOKUP_TABLE_META_SIZE) >> 5UL; // = (dlen - 56) / 32
     765             : 
     766             :     /* https://github.com/anza-xyz/agave/blob/368ea563c423b0a85cc317891187e15c9a321521/sdk/program/src/address_lookup_table/state.rs#L175-L176 */
     767           0 :     ulong active_addresses_len;
     768           0 :     err = fd_get_active_addresses_len( &addr_lookup_table_state->inner.lookup_table,
     769           0 :                                        slot,
     770           0 :                                        hashes,
     771           0 :                                        lookup_addrs_cnt,
     772           0 :                                        &active_addresses_len );
     773           0 :     if( FD_UNLIKELY( err ) ) {
     774           0 :       return err;
     775           0 :     }
     776             : 
     777             :     /* https://github.com/anza-xyz/agave/blob/368ea563c423b0a85cc317891187e15c9a321521/sdk/program/src/address_lookup_table/state.rs#L169-L182 */
     778           0 :     uchar * writable_lut_idxs = (uchar *)payload + addr_lut->writable_off;
     779           0 :     for( ulong j=0; j<addr_lut->writable_cnt; j++ ) {
     780             :       /* https://github.com/anza-xyz/agave/blob/368ea563c423b0a85cc317891187e15c9a321521/sdk/program/src/address_lookup_table/state.rs#L177-L181 */
     781           0 :       if( writable_lut_idxs[j] >= active_addresses_len ) {
     782           0 :         return FD_RUNTIME_TXN_ERR_INVALID_ADDRESS_LOOKUP_TABLE_INDEX;
     783           0 :       }
     784           0 :       out_accts_alt[writable_lut_accs_cnt++] = lookup_addrs[writable_lut_idxs[j]];
     785           0 :     }
     786             : 
     787           0 :     uchar * readonly_lut_idxs = (uchar *)payload + addr_lut->readonly_off;
     788           0 :     for( ulong j = 0; j < addr_lut->readonly_cnt; j++ ) {
     789             :       /* https://github.com/anza-xyz/agave/blob/368ea563c423b0a85cc317891187e15c9a321521/sdk/program/src/address_lookup_table/state.rs#L177-L181 */
     790           0 :       if( readonly_lut_idxs[j] >= active_addresses_len ) {
     791           0 :         return FD_RUNTIME_TXN_ERR_INVALID_ADDRESS_LOOKUP_TABLE_INDEX;
     792           0 :       }
     793           0 :       readonly_lut_accs[readonly_lut_accs_cnt++] = lookup_addrs[readonly_lut_idxs[j]];
     794           0 :     }
     795           0 :   }
     796             : 
     797           0 :   return FD_RUNTIME_EXECUTE_SUCCESS;
     798           0 : }
     799             : 
     800             : int
     801             : fd_runtime_microblock_verify_read_write_conflicts( fd_txn_p_t *               txns,
     802             :                                                    ulong                      txn_cnt,
     803             :                                                    fd_conflict_detect_ele_t * acct_map,
     804             :                                                    fd_acct_addr_t *           acct_arr,
     805             :                                                    fd_funk_t *                funk,
     806             :                                                    fd_funk_txn_t *            funk_txn,
     807             :                                                    ulong                      slot,
     808             :                                                    fd_slot_hash_t *           slot_hashes,
     809             :                                                    fd_features_t *            features,
     810             :                                                    int *                      out_conflict_detected,
     811           0 :                                                    fd_acct_addr_t *           out_conflict_addr_opt ) {
     812           0 :   *out_conflict_detected=FD_RUNTIME_NO_CONFLICT_DETECTED;
     813           0 : #define NO_CONFLICT ( *out_conflict_detected==FD_RUNTIME_NO_CONFLICT_DETECTED )
     814             : 
     815           0 : #define UPDATE_CONFLICT(cond1, cond2, acct) \
     816           0 : if( FD_UNLIKELY( cond1 ) ) { \
     817           0 :   if( FD_LIKELY( out_conflict_addr_opt ) ) *out_conflict_addr_opt = acct; \
     818           0 :   *out_conflict_detected=FD_RUNTIME_WRITE_WRITE_CONFLICT_DETECTED; \
     819           0 : } else if( FD_UNLIKELY( cond2 ) ) { \
     820           0 :   if( FD_LIKELY( out_conflict_addr_opt ) ) *out_conflict_addr_opt = acct; \
     821           0 :   *out_conflict_detected=FD_RUNTIME_READ_WRITE_CONFLICT_DETECTED; \
     822           0 : }
     823             : 
     824           0 :   ulong curr_idx            = 0;
     825           0 :   ulong sentinel_is_read    = 0;
     826           0 :   ulong sentinel_is_written = 0;
     827           0 :   int runtime_err           = FD_RUNTIME_EXECUTE_SUCCESS;
     828           0 :   for( ulong i=0; i<txn_cnt && NO_CONFLICT; i++ ) {
     829           0 :     fd_txn_p_t *           txn = txns+i;
     830           0 :     fd_acct_addr_t * txn_accts = acct_arr+curr_idx;
     831             : 
     832             :     /* Put the immediate & ALT accounts at txn_accts */
     833           0 :     const fd_acct_addr_t * accts_imm = fd_txn_get_acct_addrs( TXN(txn), txn->payload );
     834           0 :     ulong              accts_imm_cnt = fd_txn_account_cnt( TXN(txn), FD_TXN_ACCT_CAT_IMM );
     835           0 :     fd_memcpy( txn_accts, accts_imm, accts_imm_cnt*sizeof(fd_acct_addr_t) );
     836           0 :     runtime_err = fd_runtime_load_txn_address_lookup_tables( TXN(txn),
     837           0 :                                                              txn->payload,
     838           0 :                                                              funk,
     839           0 :                                                              funk_txn,
     840           0 :                                                              slot,
     841           0 :                                                              slot_hashes,
     842           0 :                                                              txn_accts+accts_imm_cnt );
     843           0 :     if( FD_UNLIKELY( runtime_err!=FD_RUNTIME_EXECUTE_SUCCESS ) ) break;
     844             : 
     845           0 :     ulong accounts_cnt   = fd_txn_account_cnt( TXN(txn), FD_TXN_ACCT_CAT_ALL );
     846           0 :     curr_idx            +=accounts_cnt;
     847           0 :     uint bpf_upgradeable = fd_txn_account_has_bpf_loader_upgradeable( fd_type_pun( txn_accts ), accounts_cnt );
     848             : 
     849             :     /* Iterate all writable accounts and detect W-W/R-W conflicts */
     850           0 :     for( fd_txn_acct_iter_t iter=fd_txn_acct_iter_init( TXN(txn), FD_TXN_ACCT_CAT_WRITABLE );
     851           0 :          iter!=fd_txn_acct_iter_end() && NO_CONFLICT;
     852           0 :          iter=fd_txn_acct_iter_next( iter ) ) {
     853           0 :       ushort idx                     = (ushort)fd_txn_acct_iter_idx( iter );
     854           0 :       fd_acct_addr_t writable_acc = txn_accts[ idx ];
     855             : 
     856             :       /* Check whether writable_acc is demoted to a read-only account */
     857           0 :       if( FD_UNLIKELY( !fd_exec_txn_account_is_writable_idx_flat( slot,
     858           0 :                                                                   idx,
     859           0 :                                                                   fd_type_pun( &txn_accts[ idx ] ),
     860           0 :                                                                   TXN(txn),
     861           0 :                                                                   features,
     862           0 :                                                                   bpf_upgradeable ) ) ) {
     863           0 :         continue;
     864           0 :       }
     865             : 
     866             :       /* writable_acc is the sentinel (fd_acct_addr_null) */
     867           0 :       if( FD_UNLIKELY( fd_conflict_detect_map_key_inval( writable_acc ) ) ) {
     868           0 :         UPDATE_CONFLICT( sentinel_is_written, sentinel_is_read, writable_acc );
     869           0 :         sentinel_is_written = 1;
     870           0 :         continue;
     871           0 :       }
     872             : 
     873             :       /* writable_acc is not the sentinel (fd_acct_addr_null) */
     874           0 :       fd_conflict_detect_ele_t * found = fd_conflict_detect_map_query( acct_map, writable_acc, NULL );
     875           0 :       if( FD_UNLIKELY( found ) ) {
     876           0 :         UPDATE_CONFLICT( found->writable, !found->writable, writable_acc );
     877           0 :       } else {
     878           0 :         fd_conflict_detect_ele_t * entry = fd_conflict_detect_map_insert( acct_map, writable_acc );
     879           0 :         entry->writable                  = 1;
     880           0 :       }
     881           0 :     }
     882             : 
     883             :     /* Iterate all readonly accounts and detect R-W conflicts */
     884           0 :     for( fd_txn_acct_iter_t iter=fd_txn_acct_iter_init( TXN(txn), FD_TXN_ACCT_CAT_READONLY );
     885           0 :          iter!=fd_txn_acct_iter_end() && NO_CONFLICT;
     886           0 :          iter=fd_txn_acct_iter_next( iter ) ) {
     887           0 :       fd_acct_addr_t readonly_acc = txn_accts[ fd_txn_acct_iter_idx( iter ) ];
     888             : 
     889             :       /* readonly_acc is the sentinel (fd_acct_addr_null) */
     890           0 :       if( FD_UNLIKELY( fd_conflict_detect_map_key_inval( readonly_acc ) ) ) {
     891           0 :         UPDATE_CONFLICT( 0, sentinel_is_written, readonly_acc );
     892           0 :         sentinel_is_read = 1;
     893           0 :         continue;
     894           0 :       }
     895             : 
     896             :       /* readonly_acc is not the sentinel (fd_acct_addr_null) */
     897           0 :       fd_conflict_detect_ele_t * found = fd_conflict_detect_map_query( acct_map, readonly_acc, NULL );
     898           0 :       if( FD_UNLIKELY( found ) ) {
     899           0 :         UPDATE_CONFLICT( 0, found->writable, readonly_acc );
     900           0 :       } else {
     901           0 :         fd_conflict_detect_ele_t * entry = fd_conflict_detect_map_insert( acct_map, readonly_acc );
     902           0 :         entry->writable                  = 0;
     903           0 :       }
     904           0 :     }
     905           0 :   }
     906             : 
     907             :   /* Clear all the entries inserted into acct_map */
     908           0 :   for( ulong i=0; i<curr_idx; i++ ) {
     909           0 :     if( FD_UNLIKELY( fd_conflict_detect_map_key_inval( acct_arr[i] ) ) ) continue;
     910           0 :     fd_conflict_detect_ele_t * found = fd_conflict_detect_map_query( acct_map, acct_arr[i], NULL );
     911           0 :     if( FD_LIKELY( found ) ) fd_conflict_detect_map_remove( acct_map, found );
     912           0 :   }
     913             : 
     914           0 :   if( FD_UNLIKELY( runtime_err!=FD_RUNTIME_EXECUTE_SUCCESS ) ) {
     915           0 :     return runtime_err;
     916           0 :   } else {
     917             :     /* https://github.com/anza-xyz/agave/blob/v2.2.3/accounts-db/src/account_locks.rs#L31 */
     918             :     /* https://github.com/anza-xyz/agave/blob/v2.2.3/accounts-db/src/account_locks.rs#L34 */
     919           0 :     return NO_CONFLICT? FD_RUNTIME_EXECUTE_SUCCESS : FD_RUNTIME_TXN_ERR_ACCOUNT_IN_USE;
     920           0 :   }
     921           0 : }
     922             : 
     923             : void
     924           0 : fd_runtime_poh_verify( fd_poh_verifier_t * poh_info ) {
     925             : 
     926           0 :   fd_hash_t working_hash = *(poh_info->in_poh_hash);
     927           0 :   fd_hash_t init_hash    = working_hash;
     928             : 
     929           0 :   fd_microblock_hdr_t const * hdr = poh_info->microblock.hdr;
     930           0 :   ulong               microblk_sz = poh_info->microblk_max_sz;
     931             : 
     932           0 :   if( !hdr->txn_cnt ){
     933           0 :     fd_poh_append( &working_hash, hdr->hash_cnt );
     934           0 :   } else { /* not a tick, regular microblock */
     935           0 :     if( hdr->hash_cnt ){
     936           0 :       fd_poh_append( &working_hash, hdr->hash_cnt - 1 );
     937           0 :     }
     938             : 
     939           0 :     ulong leaf_cnt_max = FD_TXN_ACTUAL_SIG_MAX * hdr->txn_cnt;
     940             : 
     941           0 :     FD_SPAD_FRAME_BEGIN( poh_info->spad ) {
     942           0 :       uchar *               commit = fd_spad_alloc( poh_info->spad, FD_WBMTREE32_ALIGN, fd_wbmtree32_footprint(leaf_cnt_max) );
     943           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 );
     944           0 :       fd_wbmtree32_t *      tree   = fd_wbmtree32_init( commit, leaf_cnt_max );
     945           0 :       fd_wbmtree32_leaf_t * l      = &leafs[0];
     946             : 
     947             :       /* Loop across transactions */
     948           0 :       ulong leaf_cnt = 0UL;
     949           0 :       ulong off      = sizeof(fd_microblock_hdr_t);
     950           0 :       for( ulong txn_idx=0UL; txn_idx<hdr->txn_cnt; txn_idx++ ) {
     951           0 :         fd_txn_p_t txn_p;
     952           0 :         ulong pay_sz = 0UL;
     953           0 :         ulong txn_sz = fd_txn_parse_core( poh_info->microblock.raw + off,
     954           0 :                                           fd_ulong_min( FD_TXN_MTU, microblk_sz - off ),
     955           0 :                                           TXN(&txn_p),
     956           0 :                                           NULL,
     957           0 :                                           &pay_sz );
     958           0 :         if( FD_UNLIKELY( !pay_sz || !txn_sz || txn_sz > FD_TXN_MTU )  ) {
     959           0 :           FD_LOG_ERR(( "failed to parse transaction %lu in replay", txn_idx ));
     960           0 :         }
     961             : 
     962             :         /* Loop across signatures */
     963           0 :         fd_txn_t const *         txn  = (fd_txn_t const *) txn_p._;
     964           0 :         fd_ed25519_sig_t const * sigs = (fd_ed25519_sig_t const *)fd_type_pun((poh_info->microblock.raw + off) + (ulong)txn->signature_off);
     965           0 :         for( ulong j=0UL; j<txn->signature_cnt; j++ ) {
     966           0 :           l->data     = (uchar *)&sigs[j];
     967           0 :           l->data_len = sizeof(fd_ed25519_sig_t);
     968           0 :           l++;
     969           0 :           leaf_cnt++;
     970           0 :         }
     971           0 :         off += pay_sz;
     972           0 :       }
     973             : 
     974           0 :       uchar * mbuf = fd_spad_alloc( poh_info->spad, 1UL, leaf_cnt * (sizeof(fd_ed25519_sig_t) + 1) );
     975           0 :       fd_wbmtree32_append( tree, leafs, leaf_cnt, mbuf );
     976           0 :       uchar * root = fd_wbmtree32_fini( tree );
     977           0 :       fd_poh_mixin( &working_hash, root );
     978           0 :     } FD_SPAD_FRAME_END;
     979           0 :   }
     980             : 
     981           0 :   if( FD_UNLIKELY( memcmp(hdr->hash, working_hash.hash, sizeof(fd_hash_t)) ) ) {
     982           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 ) ));
     983           0 :     poh_info->success = -1;
     984           0 :   }
     985           0 : }
     986             : 
     987             : int
     988             : fd_runtime_block_execute_prepare( fd_exec_slot_ctx_t * slot_ctx,
     989           0 :                                   fd_spad_t *          runtime_spad ) {
     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 :   if( FD_UNLIKELY( !fd_sysvar_cache_restore( slot_ctx ) ) ) {
    1013           0 :     FD_LOG_ERR(( "Failed to restore sysvar cache" ));
    1014           0 :   }
    1015             : 
    1016           0 :   return FD_RUNTIME_EXECUTE_SUCCESS;
    1017           0 : }
    1018             : 
    1019             : void
    1020             : fd_runtime_block_execute_finalize_start( fd_exec_slot_ctx_t *             slot_ctx,
    1021             :                                          fd_spad_t *                      runtime_spad,
    1022             :                                          fd_accounts_hash_task_data_t * * task_data,
    1023           0 :                                          ulong                            lt_hash_cnt ) {
    1024             : 
    1025           0 :   fd_sysvar_slot_history_update( slot_ctx, runtime_spad );
    1026             : 
    1027             :   /* This slot is now "frozen" and can't be changed anymore. */
    1028           0 :   fd_runtime_freeze( slot_ctx );
    1029             : 
    1030             :   /* Collect list of changed accounts to be added to bank hash */
    1031           0 :   *task_data = fd_spad_alloc( runtime_spad,
    1032           0 :                               alignof(fd_accounts_hash_task_data_t),
    1033           0 :                               sizeof(fd_accounts_hash_task_data_t) );
    1034           0 :   (*task_data)->lthash_values = fd_spad_alloc_check(
    1035           0 :       runtime_spad, alignof(fd_lthash_value_t), lt_hash_cnt * sizeof(fd_lthash_value_t) );
    1036             : 
    1037           0 :   for( ulong i=0UL; i<lt_hash_cnt; i++ ) {
    1038           0 :     fd_lthash_zero( &((*task_data)->lthash_values)[i] );
    1039           0 :   }
    1040             : 
    1041           0 :   fd_collect_modified_accounts( slot_ctx, *task_data, runtime_spad );
    1042           0 : }
    1043             : 
    1044             : int
    1045             : fd_runtime_block_execute_finalize_finish( fd_exec_slot_ctx_t *             slot_ctx,
    1046             :                                           fd_capture_ctx_t *               capture_ctx,
    1047             :                                           fd_runtime_block_info_t const *  block_info,
    1048             :                                           fd_spad_t *                      runtime_spad,
    1049             :                                           fd_accounts_hash_task_data_t *   task_data,
    1050           0 :                                           ulong                            lt_hash_cnt ) {
    1051             : 
    1052           0 :   fd_hash_t * bank_hash = fd_bank_bank_hash_modify( slot_ctx->bank );
    1053           0 :   int err = fd_update_hash_bank_exec_hash( slot_ctx,
    1054           0 :                                            bank_hash,
    1055           0 :                                            capture_ctx,
    1056           0 :                                            task_data,
    1057           0 :                                            1UL,
    1058           0 :                                            task_data->lthash_values,
    1059           0 :                                            lt_hash_cnt,
    1060           0 :                                            block_info->signature_cnt,
    1061           0 :                                            runtime_spad );
    1062             : 
    1063           0 :   if( FD_UNLIKELY( err ) ) {
    1064           0 :     FD_LOG_ERR(( "Unable to hash at end of slot" ));
    1065           0 :   }
    1066             : 
    1067           0 :   return FD_RUNTIME_EXECUTE_SUCCESS;
    1068             : 
    1069           0 : }
    1070             : 
    1071             : int
    1072             : fd_runtime_block_execute_finalize_para( fd_exec_slot_ctx_t *             slot_ctx,
    1073             :                                         fd_capture_ctx_t *               capture_ctx,
    1074             :                                         fd_runtime_block_info_t const *  block_info,
    1075             :                                         ulong                            worker_cnt,
    1076             :                                         fd_spad_t *                      runtime_spad,
    1077           0 :                                         fd_exec_para_cb_ctx_t *          exec_para_ctx ) {
    1078             : 
    1079           0 :   FD_SPAD_FRAME_BEGIN( runtime_spad ) {
    1080             : 
    1081           0 :   fd_accounts_hash_task_data_t * task_data = NULL;
    1082             : 
    1083           0 :   fd_runtime_block_execute_finalize_start( slot_ctx, runtime_spad, &task_data, worker_cnt );
    1084             : 
    1085           0 :   exec_para_ctx->fn_arg_1 = (void*)task_data;
    1086           0 :   exec_para_ctx->fn_arg_2 = (void*)worker_cnt;
    1087           0 :   exec_para_ctx->fn_arg_3 = (void*)slot_ctx;
    1088           0 :   fd_exec_para_call_func( exec_para_ctx );
    1089             : 
    1090           0 :   fd_runtime_block_execute_finalize_finish( slot_ctx, capture_ctx, block_info, runtime_spad, task_data, worker_cnt );
    1091             : 
    1092           0 :   } FD_SPAD_FRAME_END;
    1093             : 
    1094           0 :   return 0;
    1095           0 : }
    1096             : 
    1097             : /******************************************************************************/
    1098             : /* Transaction Level Execution Management                                     */
    1099             : /******************************************************************************/
    1100             : 
    1101             : /* fd_runtime_prepare_txns_start is responsible for setting up the task infos,
    1102             :    the slot_ctx, and for setting up the accessed accounts. */
    1103             : 
    1104             : int
    1105             : fd_runtime_prepare_txns_start( fd_exec_slot_ctx_t *         slot_ctx,
    1106             :                                fd_execute_txn_task_info_t * task_info,
    1107             :                                fd_txn_p_t *                 txns,
    1108             :                                ulong                        txn_cnt,
    1109           0 :                                fd_spad_t *                  runtime_spad ) {
    1110           0 :   int res = 0;
    1111             :   /* Loop across transactions */
    1112           0 :   for( ulong txn_idx = 0UL; txn_idx < txn_cnt; txn_idx++ ) {
    1113           0 :     fd_txn_p_t * txn = &txns[txn_idx];
    1114             : 
    1115             :     /* Allocate/setup transaction context and task infos */
    1116           0 :     task_info[txn_idx].txn_ctx      = fd_spad_alloc( runtime_spad, FD_EXEC_TXN_CTX_ALIGN, FD_EXEC_TXN_CTX_FOOTPRINT );
    1117           0 :     fd_exec_txn_ctx_t * txn_ctx     = task_info[txn_idx].txn_ctx;
    1118           0 :     task_info[txn_idx].exec_res     = 0;
    1119           0 :     task_info[txn_idx].txn          = txn;
    1120           0 :     fd_txn_t const * txn_descriptor = (fd_txn_t const *) txn->_;
    1121             : 
    1122           0 :     fd_rawtxn_b_t raw_txn = { .raw = txn->payload, .txn_sz = (ushort)txn->payload_sz };
    1123             : 
    1124           0 :     task_info[txn_idx].txn_ctx->spad      = runtime_spad;
    1125           0 :     task_info[txn_idx].txn_ctx->spad_wksp = fd_wksp_containing( runtime_spad );
    1126           0 :     int err = fd_execute_txn_prepare_start( slot_ctx,
    1127           0 :                                             txn_ctx,
    1128           0 :                                             txn_descriptor,
    1129           0 :                                             &raw_txn );
    1130           0 :     if( FD_UNLIKELY( err ) ) {
    1131           0 :       task_info[txn_idx].exec_res = err;
    1132           0 :       txn->flags                  = 0U;
    1133           0 :       res |= err;
    1134           0 :     }
    1135           0 :   }
    1136             : 
    1137           0 :   return res;
    1138           0 : }
    1139             : 
    1140             : /* fd_runtime_pre_execute_check is responsible for conducting many of the
    1141             :    transaction sanitization checks. */
    1142             : 
    1143             : void
    1144           0 : fd_runtime_pre_execute_check( fd_execute_txn_task_info_t * task_info ) {
    1145           0 :   if( FD_UNLIKELY( !( task_info->txn->flags & FD_TXN_P_FLAGS_SANITIZE_SUCCESS ) ) ) {
    1146           0 :     return;
    1147           0 :   }
    1148             : 
    1149           0 :   int err;
    1150             : 
    1151             :   /* https://github.com/anza-xyz/agave/blob/16de8b75ebcd57022409b422de557dd37b1de8db/sdk/src/transaction/sanitized.rs#L263-L275
    1152             :      TODO: Agave's precompile verification is done at the slot level, before batching and executing transactions. This logic should probably
    1153             :      be moved in the future. The Agave call heirarchy looks something like this:
    1154             :             process_single_slot
    1155             :                    v
    1156             :             confirm_full_slot
    1157             :                    v
    1158             :             confirm_slot_entries --------------------------------------------------->
    1159             :                    v                               v                                v
    1160             :             verify_transaction    ComputeBudget::process_instruction         process_entries
    1161             :                    v                                                                v
    1162             :             verify_precompiles                                                process_batches
    1163             :                                                                                     v
    1164             :                                                                                    ...
    1165             :                                                                                     v
    1166             :                                                                         load_and_execute_transactions
    1167             :                                                                                     v
    1168             :                                                                                    ...
    1169             :                                                                                     v
    1170             :                                                                               load_accounts --> load_transaction_accounts
    1171             :                                                                                     v
    1172             :                                                                        general transaction execution
    1173             : 
    1174             :   */
    1175             : 
    1176           0 :   uchar dump_txn = !!( task_info->txn_ctx->capture_ctx &&
    1177           0 :                        task_info->txn_ctx->slot >= task_info->txn_ctx->capture_ctx->dump_proto_start_slot &&
    1178           0 :                        task_info->txn_ctx->capture_ctx->dump_txn_to_pb );
    1179           0 :   if( FD_UNLIKELY( dump_txn ) ) {
    1180           0 :     fd_dump_txn_to_protobuf( task_info->txn_ctx, task_info->txn_ctx->spad );
    1181           0 :   }
    1182             : 
    1183             :   /* Verify the transaction. For now, this step only involves processing
    1184             :      the compute budget instructions. */
    1185           0 :   err = fd_executor_verify_transaction( task_info->txn_ctx );
    1186           0 :   if( FD_UNLIKELY( err!=FD_RUNTIME_EXECUTE_SUCCESS ) ) {
    1187           0 :     task_info->txn->flags = 0U;
    1188           0 :     task_info->exec_res   = err;
    1189           0 :     return;
    1190           0 :   }
    1191             : 
    1192             :   /* Resolve and verify ALUT-referenced account keys, if applicable */
    1193           0 :   err = fd_executor_setup_txn_alut_account_keys( task_info->txn_ctx );
    1194           0 :   if( FD_UNLIKELY( err!=FD_RUNTIME_EXECUTE_SUCCESS ) ) {
    1195           0 :     task_info->txn->flags = 0U;
    1196           0 :     task_info->exec_res   = err;
    1197           0 :     return;
    1198           0 :   }
    1199             : 
    1200             :   /* Set up the transaction accounts and other txn ctx metadata */
    1201           0 :   fd_exec_txn_ctx_t * txn_ctx = task_info->txn_ctx;
    1202           0 :   fd_executor_setup_accounts_for_txn( txn_ctx );
    1203             : 
    1204             :   /* Post-sanitization checks. Called from `prepare_sanitized_batch()` which, for now, only is used
    1205             :      to lock the accounts and perform a couple basic validations.
    1206             :      https://github.com/anza-xyz/agave/blob/838c1952595809a31520ff1603a13f2c9123aa51/accounts-db/src/account_locks.rs#L118 */
    1207           0 :   err = fd_executor_validate_account_locks( txn_ctx );
    1208           0 :   if( FD_UNLIKELY( err!=FD_RUNTIME_EXECUTE_SUCCESS ) ) {
    1209           0 :     task_info->txn->flags = 0U;
    1210           0 :     task_info->exec_res   = err;
    1211           0 :     return;
    1212           0 :   }
    1213             : 
    1214             :   /* `load_and_execute_transactions()` -> `check_transactions()`
    1215             :      https://github.com/anza-xyz/agave/blob/ced98f1ebe73f7e9691308afa757323003ff744f/runtime/src/bank.rs#L3667-L3672 */
    1216           0 :   err = fd_executor_check_transactions( txn_ctx );
    1217           0 :   if( FD_UNLIKELY( err!=FD_RUNTIME_EXECUTE_SUCCESS ) ) {
    1218           0 :     task_info->txn->flags = 0U;
    1219           0 :     task_info->exec_res   = err;
    1220           0 :     return;
    1221           0 :   }
    1222             : 
    1223             :   /* `load_and_execute_sanitized_transactions()` -> `validate_fees()` -> `validate_transaction_fee_payer()`
    1224             :      https://github.com/anza-xyz/agave/blob/ced98f1ebe73f7e9691308afa757323003ff744f/svm/src/transaction_processor.rs#L236-L249 */
    1225           0 :   err = fd_executor_validate_transaction_fee_payer( txn_ctx );
    1226           0 :   if( FD_UNLIKELY( err!=FD_RUNTIME_EXECUTE_SUCCESS ) ) {
    1227           0 :     task_info->txn->flags = 0U;
    1228           0 :     task_info->exec_res   = err;
    1229           0 :     return;
    1230           0 :   }
    1231             : 
    1232             :   /* https://github.com/anza-xyz/agave/blob/ced98f1ebe73f7e9691308afa757323003ff744f/svm/src/transaction_processor.rs#L284-L296 */
    1233           0 :   err = fd_executor_load_transaction_accounts( txn_ctx );
    1234           0 :   if( FD_UNLIKELY( err!=FD_RUNTIME_EXECUTE_SUCCESS ) ) {
    1235             :     /* Regardless of whether transaction accounts were loaded successfully, the transaction is
    1236             :         included in the block and transaction fees are collected.
    1237             :         https://github.com/anza-xyz/agave/blob/v2.1.6/svm/src/transaction_processor.rs#L341-L357 */
    1238           0 :     task_info->txn->flags |= FD_TXN_P_FLAGS_FEES_ONLY;
    1239           0 :     task_info->exec_res    = err;
    1240             : 
    1241             :     /* If the transaction fails to load, the "rollback" accounts will include one of the following:
    1242             :         1. Nonce account only
    1243             :         2. Fee payer only
    1244             :         3. Nonce account + fee payer
    1245             : 
    1246             :         Because the cost tracker uses the loaded account data size in block cost calculations, we need to
    1247             :         make sure our calculated loaded accounts data size is conformant with Agave's.
    1248             :         https://github.com/anza-xyz/agave/blob/v2.1.14/runtime/src/bank.rs#L4116
    1249             : 
    1250             :         In any case, we should always add the dlen of the fee payer. */
    1251           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] );
    1252             : 
    1253             :     /* Special case handling for if a nonce account is present in the transaction. */
    1254           0 :     if( task_info->txn_ctx->nonce_account_idx_in_txn!=ULONG_MAX ) {
    1255             :       /* If the nonce account is not the fee payer, then we separately add the dlen of the nonce account. Otherwise, we would
    1256             :           be double counting the dlen of the fee payer. */
    1257           0 :       if( task_info->txn_ctx->nonce_account_idx_in_txn!=FD_FEE_PAYER_TXN_IDX ) {
    1258           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 );
    1259           0 :       }
    1260           0 :     }
    1261           0 :   }
    1262             : 
    1263             :   /*
    1264             :      The fee payer and the nonce account will be stored and hashed so
    1265             :      long as the transaction landed on chain, or, in Agave terminology,
    1266             :      the transaction was processed.
    1267             :      https://github.com/anza-xyz/agave/blob/v2.1.1/runtime/src/account_saver.rs#L72
    1268             : 
    1269             :      A transaction lands on chain in one of two ways:
    1270             :      (1) Passed fee validation and loaded accounts.
    1271             :      (2) Passed fee validation and failed to load accounts and the enable_transaction_loading_failure_fees feature is enabled as per
    1272             :          SIMD-0082 https://github.com/anza-xyz/feature-gate-tracker/issues/52
    1273             : 
    1274             :      So, at this point, the transaction is committable.
    1275             :    */
    1276           0 : }
    1277             : 
    1278             : /* fd_runtime_finalize_txn is a helper used by the non-tpool transaction
    1279             :    executor to finalize borrowed account changes back into funk. It also
    1280             :    handles txncache insertion and updates to the vote/stake cache.
    1281             :    TODO: This function should probably be moved to fd_executor.c. */
    1282             : 
    1283             : void
    1284             : fd_runtime_finalize_txn( fd_funk_t *                  funk,
    1285             :                          fd_funk_txn_t *              funk_txn,
    1286             :                          fd_execute_txn_task_info_t * task_info,
    1287             :                          fd_spad_t *                  finalize_spad,
    1288           0 :                          fd_bank_t *                  bank ) {
    1289             : 
    1290             :   /* for all accounts, if account->is_verified==true, propagate update
    1291             :      to cache entry. */
    1292             : 
    1293             :   /* Store transaction info including logs */
    1294             :   // fd_runtime_finalize_txns_update_blockstore_meta( slot_ctx, task_info, 1UL );
    1295             : 
    1296             :   /* Collect fees */
    1297             : 
    1298           0 :   FD_ATOMIC_FETCH_AND_ADD( fd_bank_txn_count_modify( bank ), 1UL );
    1299           0 :   FD_ATOMIC_FETCH_AND_ADD( fd_bank_execution_fees_modify( bank ), task_info->txn_ctx->execution_fee );
    1300           0 :   FD_ATOMIC_FETCH_AND_ADD( fd_bank_priority_fees_modify( bank ), task_info->txn_ctx->priority_fee );
    1301             : 
    1302           0 :   fd_exec_txn_ctx_t * txn_ctx      = task_info->txn_ctx;
    1303           0 :   int                 exec_txn_err = task_info->exec_res;
    1304             : 
    1305           0 :   FD_ATOMIC_FETCH_AND_ADD( fd_bank_signature_count_modify( bank ), txn_ctx->txn_descriptor->signature_cnt );
    1306             : 
    1307           0 :   if( FD_UNLIKELY( exec_txn_err ) ) {
    1308             : 
    1309             :     /* Save the fee_payer. Everything but the fee balance should be reset.
    1310             :        TODO: an optimization here could be to use a dirty flag in the
    1311             :        borrowed account. If the borrowed account data has been changed in
    1312             :        any way, then the full account can be rolled back as it is done now.
    1313             :        However, most of the time the account data is not changed, and only
    1314             :        the lamport balance has to change. */
    1315             : 
    1316             :     /* With nonce account rollbacks, there are three cases:
    1317             :        1. No nonce account in the transaction
    1318             :        2. Nonce account is the fee payer
    1319             :        3. Nonce account is not the fee payer
    1320             : 
    1321             :        We should always rollback the nonce account first. Note that the nonce account may be the fee payer (case 2). */
    1322           0 :     if( txn_ctx->nonce_account_idx_in_txn!=ULONG_MAX ) {
    1323           0 :       fd_txn_account_save( txn_ctx->rollback_nonce_account, funk, funk_txn, txn_ctx->spad_wksp );
    1324           0 :     }
    1325             : 
    1326             :     /* Now, we must only save the fee payer if the nonce account was not the fee payer (because that was already saved above) */
    1327           0 :     if( FD_LIKELY( txn_ctx->nonce_account_idx_in_txn!=FD_FEE_PAYER_TXN_IDX ) ) {
    1328           0 :       fd_txn_account_save( txn_ctx->rollback_fee_payer_account, funk, funk_txn, txn_ctx->spad_wksp );
    1329           0 :     }
    1330           0 :   } else {
    1331             : 
    1332           0 :     int dirty_vote_acc  = txn_ctx->dirty_vote_acc;
    1333           0 :     int dirty_stake_acc = txn_ctx->dirty_stake_acc;
    1334             : 
    1335           0 :     for( ushort i=0; i<txn_ctx->accounts_cnt; i++ ) {
    1336             :       /* We are only interested in saving writable accounts and the fee
    1337             :          payer account. */
    1338           0 :       if( !fd_exec_txn_ctx_account_is_writable_idx( txn_ctx, i ) && i!=FD_FEE_PAYER_TXN_IDX ) {
    1339           0 :         continue;
    1340           0 :       }
    1341             : 
    1342           0 :       fd_txn_account_t * acc_rec = &txn_ctx->accounts[i];
    1343             : 
    1344           0 :       if( dirty_vote_acc && 0==memcmp( acc_rec->vt->get_owner( acc_rec ), &fd_solana_vote_program_id, sizeof(fd_pubkey_t) ) ) {
    1345           0 :         fd_vote_store_account( acc_rec, bank );
    1346           0 :         FD_SPAD_FRAME_BEGIN( finalize_spad ) {
    1347           0 :           int err;
    1348           0 :           fd_vote_state_versioned_t * vsv = fd_bincode_decode_spad(
    1349           0 :               vote_state_versioned, finalize_spad,
    1350           0 :               acc_rec->vt->get_data( acc_rec ),
    1351           0 :               acc_rec->vt->get_data_len( acc_rec ),
    1352           0 :               &err );
    1353           0 :           if( FD_UNLIKELY( err ) ) {
    1354           0 :             FD_LOG_WARNING(( "failed to decode vote state versioned" ));
    1355           0 :             continue;
    1356           0 :           }
    1357             : 
    1358           0 :           fd_vote_block_timestamp_t const * ts = NULL;
    1359           0 :           switch( vsv->discriminant ) {
    1360           0 :           case fd_vote_state_versioned_enum_v0_23_5:
    1361           0 :             ts = &vsv->inner.v0_23_5.last_timestamp;
    1362           0 :             break;
    1363           0 :           case fd_vote_state_versioned_enum_v1_14_11:
    1364           0 :             ts = &vsv->inner.v1_14_11.last_timestamp;
    1365           0 :             break;
    1366           0 :           case fd_vote_state_versioned_enum_current:
    1367           0 :             ts = &vsv->inner.current.last_timestamp;
    1368           0 :             break;
    1369           0 :           default:
    1370           0 :             __builtin_unreachable();
    1371           0 :           }
    1372             : 
    1373           0 :           fd_vote_record_timestamp_vote_with_slot( acc_rec->pubkey,
    1374           0 :                                                    ts->timestamp,
    1375           0 :                                                    ts->slot,
    1376           0 :                                                    bank );
    1377           0 :         } FD_SPAD_FRAME_END;
    1378           0 :       }
    1379             : 
    1380           0 :       if( dirty_stake_acc && 0==memcmp( acc_rec->vt->get_owner( acc_rec ), &fd_solana_stake_program_id, sizeof(fd_pubkey_t) ) ) {
    1381             :         // TODO: does this correctly handle stake account close?
    1382           0 :         fd_store_stake_delegation( acc_rec, bank );
    1383           0 :       }
    1384             : 
    1385           0 :       fd_txn_account_save( &txn_ctx->accounts[i], funk, funk_txn, txn_ctx->spad_wksp );
    1386           0 :     }
    1387             : 
    1388             :     /* We need to queue any existing program accounts that may have
    1389             :        been deployed / upgraded for reverification in the program
    1390             :        cache since their programdata may have changed. ELF / sBPF
    1391             :        metadata will need to be updated. */
    1392           0 :       ulong current_slot = fd_bank_slot_get( bank );
    1393           0 :       for( uchar i=0; i<txn_ctx->programs_to_reverify_cnt; i++ ) {
    1394           0 :         fd_pubkey_t const * program_key = &txn_ctx->programs_to_reverify[i];
    1395           0 :         fd_program_cache_queue_program_for_reverification( funk, funk_txn, program_key, current_slot );
    1396           0 :       }
    1397           0 :   }
    1398             : 
    1399           0 :   int is_vote = fd_txn_is_simple_vote_transaction( txn_ctx->txn_descriptor, txn_ctx->_txn_raw->raw );
    1400           0 :   if( !is_vote ){
    1401           0 :     ulong * nonvote_txn_count = fd_bank_nonvote_txn_count_modify( bank );
    1402           0 :     FD_ATOMIC_FETCH_AND_ADD(nonvote_txn_count, 1);
    1403             : 
    1404           0 :     if( FD_UNLIKELY( exec_txn_err ) ){
    1405           0 :       ulong * nonvote_failed_txn_count = fd_bank_nonvote_failed_txn_count_modify( bank );
    1406           0 :       FD_ATOMIC_FETCH_AND_ADD( nonvote_failed_txn_count, 1 );
    1407           0 :     }
    1408           0 :   } else {
    1409           0 :     if( FD_UNLIKELY( exec_txn_err ) ){
    1410           0 :       ulong * failed_txn_count = fd_bank_failed_txn_count_modify( bank );
    1411           0 :       FD_ATOMIC_FETCH_AND_ADD( failed_txn_count, 1 );
    1412           0 :     }
    1413           0 :   }
    1414             : 
    1415           0 :   ulong * total_compute_units_used = fd_bank_total_compute_units_used_modify( bank );
    1416           0 :   FD_ATOMIC_FETCH_AND_ADD( total_compute_units_used, txn_ctx->compute_budget_details.compute_unit_limit - txn_ctx->compute_budget_details.compute_meter );
    1417             : 
    1418           0 : }
    1419             : 
    1420             : /* fd_runtime_prepare_and_execute_txn is the main entrypoint into the executor
    1421             :    tile. At this point, the slot and epoch context should NOT be changed.
    1422             :    NOTE: The executor tile doesn't exist yet. */
    1423             : 
    1424             : static int
    1425             : fd_runtime_prepare_and_execute_txn( fd_exec_slot_ctx_t const *   slot_ctx,
    1426             :                                     fd_txn_p_t *                 txn,
    1427             :                                     fd_execute_txn_task_info_t * task_info,
    1428             :                                     fd_spad_t *                  exec_spad,
    1429           0 :                                     fd_capture_ctx_t *           capture_ctx ) {
    1430             : 
    1431           0 :   int res = 0;
    1432             : 
    1433           0 :   fd_exec_txn_ctx_t * txn_ctx     = task_info->txn_ctx;
    1434           0 :   task_info->exec_res             = -1;
    1435           0 :   task_info->txn                  = txn;
    1436           0 :   fd_txn_t const * txn_descriptor = (fd_txn_t const *) txn->_;
    1437           0 :   task_info->txn_ctx->spad        = exec_spad;
    1438           0 :   task_info->txn_ctx->spad_wksp   = fd_wksp_containing( exec_spad );
    1439             : 
    1440           0 :   fd_rawtxn_b_t raw_txn = { .raw = txn->payload, .txn_sz = (ushort)txn->payload_sz };
    1441             : 
    1442           0 :   res = fd_execute_txn_prepare_start( slot_ctx, txn_ctx, txn_descriptor, &raw_txn );
    1443           0 :   if( FD_UNLIKELY( res ) ) {
    1444           0 :     txn->flags = 0U;
    1445           0 :     return -1;
    1446           0 :   }
    1447             : 
    1448           0 :   task_info->txn_ctx->capture_ctx = capture_ctx;
    1449             : 
    1450           0 :   if( FD_UNLIKELY( fd_executor_txn_verify( txn_ctx )!=0 ) ) {
    1451           0 :     FD_LOG_WARNING(( "sigverify failed: %s", FD_BASE58_ENC_64_ALLOCA( (uchar *)txn_ctx->_txn_raw->raw+txn_ctx->txn_descriptor->signature_off ) ));
    1452           0 :     task_info->txn->flags = 0U;
    1453           0 :     task_info->exec_res   = FD_RUNTIME_TXN_ERR_SIGNATURE_FAILURE;
    1454           0 :   }
    1455             : 
    1456           0 :   fd_runtime_pre_execute_check( task_info ); /* TODO: check if this will be called from executor tile or replay tile */
    1457           0 :   if( FD_UNLIKELY( !( task_info->txn->flags & FD_TXN_P_FLAGS_SANITIZE_SUCCESS ) ) ) {
    1458           0 :     res  = task_info->exec_res;
    1459           0 :     return -1;
    1460           0 :   }
    1461             : 
    1462             :   /* Execute */
    1463           0 :   task_info->txn->flags |= FD_TXN_P_FLAGS_EXECUTE_SUCCESS;
    1464           0 :   task_info->exec_res    = fd_execute_txn( task_info );
    1465             : 
    1466           0 :   if( task_info->exec_res==0 ) {
    1467           0 :     fd_txn_reclaim_accounts( task_info->txn_ctx );
    1468           0 :   }
    1469             : 
    1470           0 :   return res;
    1471             : 
    1472           0 : }
    1473             : 
    1474             : /* fd_executor_txn_verify and fd_runtime_pre_execute_check are responisble
    1475             :    for the bulk of the pre-transaction execution checks in the runtime.
    1476             :    They aim to preserve the ordering present in the Agave client to match
    1477             :    parity in terms of error codes. Sigverify is kept separate from the rest
    1478             :    of the transaction checks for fuzzing convenience.
    1479             : 
    1480             :    For reference this is the general code path which contains all relevant
    1481             :    pre-transactions checks in the v2.0.x Agave client from upstream
    1482             :    to downstream is as follows:
    1483             : 
    1484             :    confirm_slot_entries() which calls verify_ticks() and
    1485             :    verify_transaction(). verify_transaction() calls verify_and_hash_message()
    1486             :    and verify_precompiles() which parallels fd_executor_txn_verify() and
    1487             :    fd_executor_verify_transaction().
    1488             : 
    1489             :    process_entries() contains a duplicate account check which is part of
    1490             :    agave account lock acquiring. This is checked inline in
    1491             :    fd_runtime_pre_execute_check().
    1492             : 
    1493             :    load_and_execute_transactions() contains the function check_transactions().
    1494             :    This contains check_age() and check_status_cache() which is paralleled by
    1495             :    fd_executor_check_transaction_age_and_compute_budget_limits() and
    1496             :    fd_executor_check_status_cache() respectively.
    1497             : 
    1498             :    load_and_execute_sanitized_transactions() contains validate_fees()
    1499             :    which is responsible for executing the compute budget instructions,
    1500             :    validating the fee payer and collecting the fee. This is mirrored in
    1501             :    firedancer with fd_executor_compute_budget_program_execute_instructions()
    1502             :    and fd_executor_collect_fees(). load_and_execute_sanitized_transactions()
    1503             :    also checks the total data size of the accounts in load_accounts() and
    1504             :    validates the program accounts in load_transaction_accounts(). This
    1505             :    is paralled by fd_executor_load_transaction_accounts(). */
    1506             : 
    1507             : /******************************************************************************/
    1508             : /* Epoch Boundary                                                             */
    1509             : /******************************************************************************/
    1510             : 
    1511             : /* Update the epoch bank stakes cache with the delegated stake values from the slot bank cache.
    1512             : The slot bank cache will have been accumulating this epoch, and now we are at an epoch boundary
    1513             : we can safely update the epoch stakes cache with the latest values.
    1514             : 
    1515             : In Solana, the stakes cache is updated after every transaction
    1516             :   (https://github.com/solana-labs/solana/blob/c091fd3da8014c0ef83b626318018f238f506435/runtime/src/bank.rs#L7587).
    1517             : As delegations have to warm up, the contents of the cache will not change inter-epoch. We can therefore update
    1518             : the cache only at epoch boundaries.
    1519             : 
    1520             : https://github.com/solana-labs/solana/blob/c091fd3da8014c0ef83b626318018f238f506435/runtime/src/stakes.rs#L65 */
    1521             : static void
    1522             : fd_update_stake_delegations( fd_exec_slot_ctx_t * slot_ctx,
    1523           0 :                              fd_epoch_info_t *    temp_info ) {
    1524             : 
    1525           0 :   fd_stakes_global_t * stakes = fd_bank_stakes_locking_modify( slot_ctx->bank );
    1526           0 :   fd_delegation_pair_t_mapnode_t * stake_delegations_pool = fd_stakes_stake_delegations_pool_join( stakes );
    1527           0 :   fd_delegation_pair_t_mapnode_t * stake_delegations_root = fd_stakes_stake_delegations_root_join( stakes );
    1528             : 
    1529             :   /* In one pass, iterate over all the new stake infos and insert the updated values into the epoch stakes cache
    1530             :       This assumes that there is enough memory pre-allocated for the stakes cache. */
    1531           0 :   for( ulong idx=temp_info->stake_infos_new_keys_start_idx; idx<temp_info->stake_infos_len; idx++ ) {
    1532             :     // Fetch and store the delegation associated with this stake account
    1533           0 :     fd_delegation_pair_t_mapnode_t key;
    1534           0 :     key.elem.account = temp_info->stake_infos[idx].account;
    1535           0 :     fd_delegation_pair_t_mapnode_t * entry = fd_delegation_pair_t_map_find( stake_delegations_pool, stake_delegations_root, &key );
    1536           0 :     if( FD_LIKELY( entry==NULL ) ) {
    1537           0 :       entry = fd_delegation_pair_t_map_acquire( stake_delegations_pool );
    1538           0 :       if( FD_UNLIKELY( !entry ) ) {
    1539           0 :         FD_TEST( 0 == fd_delegation_pair_t_map_verify( stake_delegations_pool, stake_delegations_root ) );
    1540           0 :         FD_LOG_CRIT(( "stake_delegations_pool full %lu", fd_delegation_pair_t_map_size( stake_delegations_pool, stake_delegations_root ) ));
    1541           0 :       }
    1542           0 :       entry->elem.account    = temp_info->stake_infos[idx].account;
    1543           0 :       entry->elem.delegation = temp_info->stake_infos[idx].stake.delegation;
    1544           0 :       fd_delegation_pair_t_map_insert( stake_delegations_pool, &stake_delegations_root, entry );
    1545           0 :     }
    1546           0 :   }
    1547             : 
    1548           0 :   fd_stakes_stake_delegations_pool_update( stakes, stake_delegations_pool );
    1549           0 :   fd_stakes_stake_delegations_root_update( stakes, stake_delegations_root );
    1550           0 :   fd_bank_stakes_end_locking_modify( slot_ctx->bank );
    1551             : 
    1552             :   /* At the epoch boundary, release all of the stake account keys
    1553             :      because at this point all of the changes have been applied to the
    1554             :      stakes. */
    1555           0 :   fd_account_keys_global_t * stake_account_keys = fd_bank_stake_account_keys_locking_modify( slot_ctx->bank );
    1556           0 :   fd_account_keys_pair_t_mapnode_t * account_keys_pool = fd_account_keys_account_keys_pool_join( stake_account_keys );
    1557           0 :   fd_account_keys_pair_t_mapnode_t * account_keys_root = fd_account_keys_account_keys_root_join( stake_account_keys );
    1558             : 
    1559           0 :   fd_account_keys_pair_t_map_release_tree( account_keys_pool, account_keys_root );
    1560           0 :   account_keys_root = NULL;
    1561             : 
    1562           0 :   fd_account_keys_account_keys_pool_update( stake_account_keys, account_keys_pool );
    1563           0 :   fd_account_keys_account_keys_root_update( stake_account_keys, account_keys_root );
    1564           0 :   fd_bank_stake_account_keys_end_locking_modify( slot_ctx->bank );
    1565           0 : }
    1566             : 
    1567             : /* Replace the stakes in T-2 (epoch_stakes) by the stakes at T-1 (next_epoch_stakes) */
    1568             : static void
    1569           0 : fd_update_epoch_stakes( fd_exec_slot_ctx_t * slot_ctx ) {
    1570             : 
    1571             :   /* Copy epoch_bank->next_epoch_stakes into fd_bank_slot_get( slot_ctx->bank )_bank.epoch_stakes */
    1572           0 :   fd_vote_accounts_global_t const * next_epoch_stakes = fd_bank_next_epoch_stakes_locking_query( slot_ctx->bank );
    1573             : 
    1574           0 :   fd_vote_accounts_global_t * epoch_stakes = fd_bank_epoch_stakes_locking_modify( slot_ctx->bank );
    1575           0 :   fd_memcpy( epoch_stakes, next_epoch_stakes, fd_bank_epoch_stakes_footprint );
    1576           0 :   fd_bank_epoch_stakes_end_locking_modify( slot_ctx->bank );
    1577             : 
    1578           0 :   fd_bank_next_epoch_stakes_end_locking_query( slot_ctx->bank );
    1579             : 
    1580           0 : }
    1581             : 
    1582             : /* Copy stakes->vote_accounts into next_epoch_stakes. */
    1583             : static void
    1584           0 : fd_update_next_epoch_stakes( fd_exec_slot_ctx_t * slot_ctx ) {
    1585             : 
    1586             :   /* FIXME: This is technically not correct, since the vote accounts
    1587             :      could be laid out after the stake delegations from fd_stakes.
    1588             :      The correct solution is to split out the stake delgations from the
    1589             :      vote accounts in fd_stakes. */
    1590             : 
    1591             :   /* Copy stakes->vote_accounts into next_epoch_stakes */
    1592           0 :   fd_stakes_global_t const *        stakes = fd_bank_stakes_locking_query( slot_ctx->bank );
    1593           0 :   fd_vote_accounts_global_t const * vote_stakes = &stakes->vote_accounts;
    1594             : 
    1595           0 :   fd_vote_accounts_global_t * next_epoch_stakes = fd_bank_next_epoch_stakes_locking_modify( slot_ctx->bank );
    1596           0 :   fd_memcpy( next_epoch_stakes, vote_stakes, fd_bank_next_epoch_stakes_footprint );
    1597           0 :   fd_bank_next_epoch_stakes_end_locking_modify( slot_ctx->bank );
    1598             : 
    1599           0 :   fd_bank_stakes_end_locking_query( slot_ctx->bank );
    1600           0 : }
    1601             : 
    1602             : /* Mimics `bank.new_target_program_account()`. Assumes `out_rec` is a modifiable record.
    1603             : 
    1604             :    From the calling context, `out_rec` points to a native program record (e.g. Config, ALUT native programs).
    1605             :    There should be enough space in `out_rec->data` to hold at least 36 bytes (the size of a BPF upgradeable
    1606             :    program account) when calling this function. The native program account's owner is set to the BPF loader
    1607             :    upgradeable program ID, and lamports are increased / deducted to contain the rent exempt minimum balance.
    1608             : 
    1609             :    https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L79-L95 */
    1610             : static int
    1611             : fd_new_target_program_account( fd_exec_slot_ctx_t * slot_ctx,
    1612             :                                fd_pubkey_t const *  target_program_data_address,
    1613           0 :                                fd_txn_account_t *   out_rec ) {
    1614             :   /* https://github.com/anza-xyz/agave/blob/v2.1.0/sdk/account/src/lib.rs#L471 */
    1615           0 :   out_rec->vt->set_rent_epoch( out_rec, 0UL );
    1616             : 
    1617             :   /* https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L86-L88 */
    1618           0 :   fd_bpf_upgradeable_loader_state_t state = {
    1619           0 :     .discriminant = fd_bpf_upgradeable_loader_state_enum_program,
    1620           0 :     .inner = {
    1621           0 :       .program = {
    1622           0 :         .programdata_address = *target_program_data_address,
    1623           0 :       }
    1624           0 :     }
    1625           0 :   };
    1626             : 
    1627             :   /* https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L89-L90 */
    1628           0 :   fd_rent_t const * rent = fd_bank_rent_query( slot_ctx->bank );
    1629           0 :   if( FD_UNLIKELY( rent==NULL ) ) {
    1630           0 :     return -1;
    1631           0 :   }
    1632             : 
    1633           0 :   out_rec->vt->set_lamports( out_rec, fd_rent_exempt_minimum_balance( rent, SIZE_OF_PROGRAM ) );
    1634           0 :   fd_bincode_encode_ctx_t ctx = {
    1635           0 :     .data    = out_rec->vt->get_data_mut( out_rec ),
    1636           0 :     .dataend = out_rec->vt->get_data_mut( out_rec ) + SIZE_OF_PROGRAM,
    1637           0 :   };
    1638             : 
    1639             :   /* https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L91-L9 */
    1640           0 :   int err = fd_bpf_upgradeable_loader_state_encode( &state, &ctx );
    1641           0 :   if( FD_UNLIKELY( err ) ) {
    1642           0 :     return err;
    1643           0 :   }
    1644           0 :   out_rec->vt->set_owner( out_rec, &fd_solana_bpf_loader_upgradeable_program_id );
    1645             : 
    1646             :   /* https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L93-L94 */
    1647           0 :   out_rec->vt->set_executable( out_rec, 1 );
    1648           0 :   return FD_RUNTIME_EXECUTE_SUCCESS;
    1649           0 : }
    1650             : 
    1651             : /* Mimics `bank.new_target_program_data_account()`. Assumes `new_target_program_data_account` is a modifiable record.
    1652             :    `config_upgrade_authority_address` may be NULL.
    1653             : 
    1654             :    This function uses an existing buffer account `buffer_acc_rec` to set the program data account data for a core
    1655             :    program BPF migration. Sets the lamports and data fields of `new_target_program_data_account` based on the
    1656             :    ELF data length, and sets the owner to the BPF loader upgradeable program ID.
    1657             : 
    1658             :    https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L97-L153 */
    1659             : static int
    1660             : fd_new_target_program_data_account( fd_exec_slot_ctx_t * slot_ctx,
    1661             :                                     fd_pubkey_t *        config_upgrade_authority_address,
    1662             :                                     fd_txn_account_t *   buffer_acc_rec,
    1663             :                                     fd_txn_account_t *   new_target_program_data_account,
    1664           0 :                                     fd_spad_t *          runtime_spad ) {
    1665             : 
    1666           0 :   FD_SPAD_FRAME_BEGIN( runtime_spad ) {
    1667             : 
    1668             :   /* https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L113-L116 */
    1669           0 :   int err;
    1670           0 :   fd_bpf_upgradeable_loader_state_t * state = fd_bincode_decode_spad(
    1671           0 :       bpf_upgradeable_loader_state, runtime_spad,
    1672           0 :       buffer_acc_rec->vt->get_data( buffer_acc_rec ),
    1673           0 :       buffer_acc_rec->vt->get_data_len( buffer_acc_rec ),
    1674           0 :       &err );
    1675           0 :   if( FD_UNLIKELY( err ) ) return err;
    1676             : 
    1677           0 :   if( FD_UNLIKELY( !fd_bpf_upgradeable_loader_state_is_buffer( state ) ) ) {
    1678           0 :     return -1;
    1679           0 :   }
    1680             : 
    1681             :   /* https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L118-L125 */
    1682           0 :   if( config_upgrade_authority_address!=NULL ) {
    1683           0 :     if( FD_UNLIKELY( state->inner.buffer.authority_address==NULL ||
    1684           0 :                      memcmp( config_upgrade_authority_address, state->inner.buffer.authority_address, sizeof(fd_pubkey_t) ) ) ) {
    1685           0 :       return -1;
    1686           0 :     }
    1687           0 :   }
    1688             : 
    1689             :   /* https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L127-L132 */
    1690           0 :   fd_rent_t const * rent = fd_bank_rent_query( slot_ctx->bank );
    1691           0 :   if( FD_UNLIKELY( rent==NULL ) ) {
    1692           0 :     return -1;
    1693           0 :   }
    1694             : 
    1695           0 :   const uchar * elf = buffer_acc_rec->vt->get_data( buffer_acc_rec ) + BUFFER_METADATA_SIZE;
    1696           0 :   ulong space = PROGRAMDATA_METADATA_SIZE - BUFFER_METADATA_SIZE + buffer_acc_rec->vt->get_data_len( buffer_acc_rec );
    1697           0 :   ulong lamports = fd_rent_exempt_minimum_balance( rent, space );
    1698             : 
    1699             :   /* https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L134-L137 */
    1700           0 :   fd_bpf_upgradeable_loader_state_t programdata_metadata = {
    1701           0 :     .discriminant = fd_bpf_upgradeable_loader_state_enum_program_data,
    1702           0 :     .inner = {
    1703           0 :       .program_data = {
    1704           0 :         .slot = fd_bank_slot_get( slot_ctx->bank ),
    1705           0 :         .upgrade_authority_address = config_upgrade_authority_address
    1706           0 :       }
    1707           0 :     }
    1708           0 :   };
    1709             : 
    1710             :   /* https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L139-L144 */
    1711           0 :   new_target_program_data_account->vt->set_lamports( new_target_program_data_account, lamports );
    1712           0 :   fd_bincode_encode_ctx_t encode_ctx = {
    1713           0 :     .data    = new_target_program_data_account->vt->get_data_mut( new_target_program_data_account ),
    1714           0 :     .dataend = new_target_program_data_account->vt->get_data_mut( new_target_program_data_account ) + PROGRAMDATA_METADATA_SIZE,
    1715           0 :   };
    1716           0 :   err = fd_bpf_upgradeable_loader_state_encode( &programdata_metadata, &encode_ctx );
    1717           0 :   if( FD_UNLIKELY( err ) ) {
    1718           0 :     return err;
    1719           0 :   }
    1720           0 :   new_target_program_data_account->vt->set_owner( new_target_program_data_account, &fd_solana_bpf_loader_upgradeable_program_id );
    1721             : 
    1722             :   /* Copy the ELF data over
    1723             :      https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L145 */
    1724           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 );
    1725             : 
    1726           0 :   return FD_RUNTIME_EXECUTE_SUCCESS;
    1727             : 
    1728           0 :   } FD_SPAD_FRAME_END;
    1729           0 : }
    1730             : 
    1731             : /* Mimics `migrate_builtin_to_core_bpf()`. The arguments map as follows:
    1732             :     - builtin_program_id: builtin_program_id
    1733             :     - config
    1734             :       - source_buffer_address: source_buffer_address
    1735             :       - migration_target
    1736             :         - Builtin: !stateless
    1737             :         - Stateless: stateless
    1738             :       - upgrade_authority_address: upgrade_authority_address
    1739             :   https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L235-L318 */
    1740             : static void
    1741             : fd_migrate_builtin_to_core_bpf( fd_exec_slot_ctx_t * slot_ctx,
    1742             :                                 fd_pubkey_t *        upgrade_authority_address,
    1743             :                                 fd_pubkey_t const *  builtin_program_id,
    1744             :                                 fd_pubkey_t const *  source_buffer_address,
    1745             :                                 uchar                stateless,
    1746           0 :                                 fd_spad_t *          runtime_spad ) {
    1747           0 :   int err;
    1748             : 
    1749             :   /* https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L242-L243
    1750             : 
    1751             :      The below logic is used to obtain a `TargetBuiltin` account. There are three fields of `TargetBuiltin` returned:
    1752             :       - target.program_address: builtin_program_id
    1753             :       - target.program_account:
    1754             :           - if stateless: an AccountSharedData::default() (i.e. system program id, 0 lamports, 0 data, non-executable, system program owner)
    1755             :           - if NOT stateless: the existing account (for us its called `target_program_account`)
    1756             :       - target.program_data_address: `target_program_data_address` for us, derived below. */
    1757             : 
    1758             :   /* These checks will fail if the core program has already been migrated to BPF, since the account will exist + the program owner
    1759             :      will no longer be the native loader.
    1760             :      https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/target_builtin.rs#L23-L50 */
    1761           0 :   FD_TXN_ACCOUNT_DECL( target_program_account );
    1762           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 );
    1763           0 :   if( !stateless ) {
    1764             :     /* The program account should exist.
    1765             :        https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/target_builtin.rs#L30-L33 */
    1766           0 :     if( FD_UNLIKELY( !program_exists ) ) {
    1767           0 :       FD_LOG_WARNING(( "Builtin program %s does not exist, skipping migration...", FD_BASE58_ENC_32_ALLOCA( builtin_program_id ) ));
    1768           0 :       return;
    1769           0 :     }
    1770             : 
    1771             :     /* The program account should be owned by the native loader.
    1772             :        https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/target_builtin.rs#L35-L38 */
    1773           0 :     if( FD_UNLIKELY( memcmp( target_program_account->vt->get_owner( target_program_account ), fd_solana_native_loader_id.uc, sizeof(fd_pubkey_t) ) ) ) {
    1774           0 :       FD_LOG_WARNING(( "Builtin program %s is not owned by the native loader, skipping migration...", FD_BASE58_ENC_32_ALLOCA( builtin_program_id ) ));
    1775           0 :       return;
    1776           0 :     }
    1777           0 :   } else {
    1778             :     /* The program account should _not_ exist.
    1779             :        https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/target_builtin.rs#L42-L46 */
    1780           0 :     if( FD_UNLIKELY( program_exists ) ) {
    1781           0 :       FD_LOG_WARNING(( "Stateless program %s already exists, skipping migration...", FD_BASE58_ENC_32_ALLOCA( builtin_program_id ) ));
    1782           0 :       return;
    1783           0 :     }
    1784           0 :   }
    1785             : 
    1786             :   /* The program data account should not exist.
    1787             :      https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/target_builtin.rs#L52-L62 */
    1788           0 :   uint custom_err = UINT_MAX;
    1789           0 :   fd_pubkey_t target_program_data_address[ 1UL ];
    1790           0 :   uchar * seeds[ 1UL ];
    1791           0 :   seeds[ 0UL ]    = (uchar *)builtin_program_id;
    1792           0 :   ulong seed_sz   = sizeof(fd_pubkey_t);
    1793           0 :   uchar bump_seed = 0;
    1794           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 );
    1795           0 :   if( FD_UNLIKELY( err ) ) {
    1796             :     /* TODO: We should handle these errors more gracefully instead of just killing the client. */
    1797           0 :     FD_LOG_ERR(( "Unable to find a viable program address bump seed" )); // Solana panics, error code is undefined
    1798           0 :     return;
    1799           0 :   }
    1800           0 :   FD_TXN_ACCOUNT_DECL( program_data_account );
    1801           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 ) ) {
    1802           0 :     FD_LOG_WARNING(( "Program data account %s already exists, skipping migration...", FD_BASE58_ENC_32_ALLOCA( target_program_data_address ) ));
    1803           0 :     return;
    1804           0 :   }
    1805             : 
    1806             :   /* https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L244
    1807             : 
    1808             :      Obtains a `SourceBuffer` account. There are two fields returned:
    1809             :       - source.buffer_address: source_buffer_address
    1810             :       - source.buffer_account: the existing buffer account */
    1811             : 
    1812             :   /* The buffer account should exist.
    1813             :      https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/source_buffer.rs#L26-L29 */
    1814           0 :   FD_TXN_ACCOUNT_DECL( source_buffer_account );
    1815           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 ) ) {
    1816           0 :     FD_LOG_WARNING(( "Buffer account %s does not exist, skipping migration...", FD_BASE58_ENC_32_ALLOCA( source_buffer_address ) ));
    1817           0 :     return;
    1818           0 :   }
    1819             : 
    1820             :   /* The buffer account should be owned by the upgradeable loader.
    1821             :      https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/source_buffer.rs#L31-L34 */
    1822           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) ) ) ) {
    1823           0 :     FD_LOG_WARNING(( "Buffer account %s is not owned by the upgradeable loader, skipping migration...", FD_BASE58_ENC_32_ALLOCA( source_buffer_address ) ));
    1824           0 :     return;
    1825           0 :   }
    1826             : 
    1827             :   /* The buffer account should have the correct state. We already check the buffer account state in `fd_new_target_program_data_account`,
    1828             :       so we can skip the checks here.
    1829             :      https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/source_buffer.rs#L37-L47 */
    1830             : 
    1831             :   /* This check is done a bit prematurely because we calculate the previous account state's lamports. We use 0 for starting lamports
    1832             :      for stateless accounts because they don't yet exist.
    1833             : 
    1834             :      https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L277-L280 */
    1835           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 );
    1836             : 
    1837             :   /* Start a funk write txn */
    1838           0 :   fd_funk_txn_t * parent_txn = slot_ctx->funk_txn;
    1839           0 :   fd_funk_txn_xid_t migration_xid = fd_funk_generate_xid();
    1840           0 :   fd_funk_txn_start_write( slot_ctx->funk );
    1841           0 :   slot_ctx->funk_txn = fd_funk_txn_prepare( slot_ctx->funk, slot_ctx->funk_txn, &migration_xid, 0UL );
    1842           0 :   fd_funk_txn_end_write( slot_ctx->funk );
    1843             : 
    1844             :   /* Attempt serialization of program account. If the program is stateless, we want to create the account. Otherwise,
    1845             :      we want a writable handle to modify the existing account.
    1846             :      https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L246-L249 */
    1847           0 :   FD_TXN_ACCOUNT_DECL( new_target_program_account );
    1848           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 );
    1849           0 :   if( FD_UNLIKELY( err ) ) {
    1850           0 :     FD_LOG_WARNING(( "Builtin program ID %s does not exist", FD_BASE58_ENC_32_ALLOCA( builtin_program_id ) ));
    1851           0 :     goto fail;
    1852           0 :   }
    1853           0 :   new_target_program_account->vt->set_data_len( new_target_program_account, SIZE_OF_PROGRAM );
    1854           0 :   new_target_program_account->vt->set_slot( new_target_program_account, fd_bank_slot_get( slot_ctx->bank ) );
    1855             : 
    1856             :   /* Create a new target program account. This modifies the existing record. */
    1857           0 :   err = fd_new_target_program_account( slot_ctx, target_program_data_address, new_target_program_account );
    1858           0 :   if( FD_UNLIKELY( err ) ) {
    1859           0 :     FD_LOG_WARNING(( "Failed to write new program state to %s", FD_BASE58_ENC_32_ALLOCA( builtin_program_id ) ));
    1860           0 :     goto fail;
    1861           0 :   }
    1862             : 
    1863           0 :   fd_txn_account_mutable_fini( new_target_program_account, slot_ctx->funk, slot_ctx->funk_txn );
    1864             : 
    1865             :   /* Create a new target program data account. */
    1866           0 :   ulong new_target_program_data_account_sz = PROGRAMDATA_METADATA_SIZE - BUFFER_METADATA_SIZE + source_buffer_account->vt->get_data_len( source_buffer_account );
    1867           0 :   FD_TXN_ACCOUNT_DECL( new_target_program_data_account );
    1868           0 :   err = fd_txn_account_init_from_funk_mutable( new_target_program_data_account,
    1869           0 :                                                target_program_data_address,
    1870           0 :                                                slot_ctx->funk,
    1871           0 :                                                slot_ctx->funk_txn,
    1872           0 :                                                1,
    1873           0 :                                                new_target_program_data_account_sz );
    1874           0 :   if( FD_UNLIKELY( err ) ) {
    1875           0 :     FD_LOG_WARNING(( "Failed to create new program data account to %s", FD_BASE58_ENC_32_ALLOCA( target_program_data_address ) ));
    1876           0 :     goto fail;
    1877           0 :   }
    1878           0 :   new_target_program_data_account->vt->set_data_len( new_target_program_data_account, new_target_program_data_account_sz );
    1879           0 :   new_target_program_data_account->vt->set_slot( new_target_program_data_account, fd_bank_slot_get( slot_ctx->bank ) );
    1880             : 
    1881           0 :   err = fd_new_target_program_data_account( slot_ctx,
    1882           0 :                                             upgrade_authority_address,
    1883           0 :                                             source_buffer_account,
    1884           0 :                                             new_target_program_data_account,
    1885           0 :                                             runtime_spad );
    1886           0 :   if( FD_UNLIKELY( err ) ) {
    1887           0 :     FD_LOG_WARNING(( "Failed to write new program data state to %s", FD_BASE58_ENC_32_ALLOCA( target_program_data_address ) ));
    1888           0 :     goto fail;
    1889           0 :   }
    1890             : 
    1891           0 :   fd_txn_account_mutable_fini( new_target_program_data_account, slot_ctx->funk, slot_ctx->funk_txn );
    1892             : 
    1893             :   /* Deploy the new target Core BPF program.
    1894             :      https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L268-L271 */
    1895           0 :   err = fd_directly_invoke_loader_v3_deploy( slot_ctx,
    1896           0 :                                              builtin_program_id,
    1897           0 :                                              new_target_program_data_account->vt->get_data( new_target_program_data_account ) + PROGRAMDATA_METADATA_SIZE,
    1898           0 :                                              new_target_program_data_account->vt->get_data_len( new_target_program_data_account ) - PROGRAMDATA_METADATA_SIZE,
    1899           0 :                                              runtime_spad );
    1900           0 :   if( FD_UNLIKELY( err ) ) {
    1901           0 :     FD_LOG_WARNING(( "Failed to deploy program %s", FD_BASE58_ENC_32_ALLOCA( builtin_program_id ) ));
    1902           0 :     goto fail;
    1903           0 :   }
    1904             : 
    1905             :   /* https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L281-L284 */
    1906           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 );
    1907             : 
    1908             :   /* Update capitalization.
    1909             :      https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L286-L297 */
    1910           0 :   if( lamports_to_burn>lamports_to_fund ) {
    1911           0 :     fd_bank_capitalization_set( slot_ctx->bank, fd_bank_capitalization_get( slot_ctx->bank ) - ( lamports_to_burn - lamports_to_fund ) );
    1912           0 :   } else {
    1913           0 :     fd_bank_capitalization_set( slot_ctx->bank, fd_bank_capitalization_get( slot_ctx->bank ) + ( lamports_to_fund - lamports_to_burn ) );
    1914           0 :   }
    1915             : 
    1916             :   /* Reclaim the source buffer account
    1917             :      https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L305 */
    1918           0 :   source_buffer_account->vt->set_lamports( source_buffer_account, 0 );
    1919           0 :   source_buffer_account->vt->set_data_len( source_buffer_account, 0 );
    1920           0 :   source_buffer_account->vt->clear_owner( source_buffer_account );
    1921             : 
    1922           0 :   fd_txn_account_mutable_fini( source_buffer_account, slot_ctx->funk, slot_ctx->funk_txn );
    1923             : 
    1924             :   /* Publish the in-preparation transaction into the parent. We should not have to create
    1925             :      a BPF cache entry here because the program is technically "delayed visibility", so the program
    1926             :      should not be invokable until the next slot. The cache entry will be created at the end of the
    1927             :      block as a part of the finalize routine. */
    1928           0 :   fd_funk_txn_start_write( slot_ctx->funk );
    1929           0 :   fd_funk_txn_publish_into_parent( slot_ctx->funk, slot_ctx->funk_txn, 1 );
    1930           0 :   fd_funk_txn_end_write( slot_ctx->funk );
    1931           0 :   slot_ctx->funk_txn = parent_txn;
    1932           0 :   return;
    1933             : 
    1934           0 : fail:
    1935             :   /* Cancel the in-preparation transaction and discard any in-progress changes. */
    1936           0 :   fd_funk_txn_start_write( slot_ctx->funk );
    1937           0 :   fd_funk_txn_cancel( slot_ctx->funk, slot_ctx->funk_txn, 0UL );
    1938           0 :   fd_funk_txn_end_write( slot_ctx->funk );
    1939           0 :   slot_ctx->funk_txn = parent_txn;
    1940           0 : }
    1941             : 
    1942             : /* https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank.rs#L6704 */
    1943             : static void
    1944             : fd_apply_builtin_program_feature_transitions( fd_exec_slot_ctx_t * slot_ctx,
    1945           0 :                                               fd_spad_t *          runtime_spad ) {
    1946             :   /* TODO: Set the upgrade authority properly from the core bpf migration config. Right now it's set to None.
    1947             : 
    1948             :      Migrate any necessary stateless builtins to core BPF. So far, the only "stateless" builtin
    1949             :      is the Feature program. Beginning checks in the `migrate_builtin_to_core_bpf` function will
    1950             :      fail if the program has already been migrated to BPF. */
    1951             : 
    1952           0 :   FD_SPAD_FRAME_BEGIN( runtime_spad ) {
    1953             : 
    1954           0 :   fd_builtin_program_t const * builtins = fd_builtins();
    1955           0 :   for( ulong i=0UL; i<fd_num_builtins(); i++ ) {
    1956             :     /* https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank.rs#L6732-L6751 */
    1957           0 :     if( builtins[i].core_bpf_migration_config && FD_FEATURE_ACTIVE_OFFSET( fd_bank_slot_get( slot_ctx->bank ), fd_bank_features_get( slot_ctx->bank ), builtins[i].core_bpf_migration_config->enable_feature_offset ) ) {
    1958           0 :       FD_LOG_NOTICE(( "Migrating builtin program %s to core BPF", FD_BASE58_ENC_32_ALLOCA( builtins[i].pubkey->key ) ));
    1959           0 :       fd_migrate_builtin_to_core_bpf( slot_ctx,
    1960           0 :                                       builtins[i].core_bpf_migration_config->upgrade_authority_address,
    1961           0 :                                       builtins[i].core_bpf_migration_config->builtin_program_id,
    1962           0 :                                       builtins[i].core_bpf_migration_config->source_buffer_address,
    1963           0 :                                       0,
    1964           0 :                                       runtime_spad );
    1965           0 :     }
    1966             :     /* https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank.rs#L6753-L6774 */
    1967           0 :     if( builtins[i].enable_feature_offset!=NO_ENABLE_FEATURE_ID && FD_FEATURE_JUST_ACTIVATED_OFFSET( slot_ctx, builtins[i].enable_feature_offset ) ) {
    1968           0 :       FD_LOG_NOTICE(( "Enabling builtin program %s", FD_BASE58_ENC_32_ALLOCA( builtins[i].pubkey->key ) ));
    1969           0 :       fd_write_builtin_account( slot_ctx, *builtins[i].pubkey, builtins[i].data,strlen(builtins[i].data) );
    1970           0 :     }
    1971           0 :   }
    1972             : 
    1973             :   /* https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank.rs#L6776-L6793 */
    1974           0 :   fd_stateless_builtin_program_t const * stateless_builtins = fd_stateless_builtins();
    1975           0 :   for( ulong i=0UL; i<fd_num_stateless_builtins(); i++ ) {
    1976           0 :     if( stateless_builtins[i].core_bpf_migration_config && FD_FEATURE_ACTIVE_OFFSET( fd_bank_slot_get( slot_ctx->bank ), fd_bank_features_get( slot_ctx->bank ), stateless_builtins[i].core_bpf_migration_config->enable_feature_offset ) ) {
    1977           0 :       FD_LOG_NOTICE(( "Migrating stateless builtin program %s to core BPF", FD_BASE58_ENC_32_ALLOCA( stateless_builtins[i].pubkey->key ) ));
    1978           0 :       fd_migrate_builtin_to_core_bpf( slot_ctx,
    1979           0 :                                       stateless_builtins[i].core_bpf_migration_config->upgrade_authority_address,
    1980           0 :                                       stateless_builtins[i].core_bpf_migration_config->builtin_program_id,
    1981           0 :                                       stateless_builtins[i].core_bpf_migration_config->source_buffer_address,
    1982           0 :                                       1,
    1983           0 :                                       runtime_spad );
    1984           0 :     }
    1985           0 :   }
    1986             : 
    1987             :   /* https://github.com/anza-xyz/agave/blob/c1080de464cfb578c301e975f498964b5d5313db/runtime/src/bank.rs#L6795-L6805 */
    1988           0 :   fd_precompile_program_t const * precompiles = fd_precompiles();
    1989           0 :   for( ulong i=0UL; i<fd_num_precompiles(); i++ ) {
    1990           0 :     if( precompiles[i].feature_offset != NO_ENABLE_FEATURE_ID && FD_FEATURE_JUST_ACTIVATED_OFFSET( slot_ctx, precompiles[i].feature_offset ) ) {
    1991           0 :       fd_write_builtin_account( slot_ctx, *precompiles[i].pubkey, "", 0 );
    1992           0 :     }
    1993           0 :   }
    1994             : 
    1995           0 :   } FD_SPAD_FRAME_END;
    1996           0 : }
    1997             : 
    1998             : static void
    1999             : fd_feature_activate( fd_features_t *         features,
    2000             :                      fd_exec_slot_ctx_t *    slot_ctx,
    2001             :                      fd_feature_id_t const * id,
    2002             :                      uchar const             acct[ static 32 ],
    2003           0 :                      fd_spad_t *             runtime_spad ) {
    2004             : 
    2005             :   // Skip reverted features from being activated
    2006           0 :   if( id->reverted==1 ) {
    2007           0 :     return;
    2008           0 :   }
    2009             : 
    2010           0 :   FD_TXN_ACCOUNT_DECL( acct_rec );
    2011           0 :   int err = fd_txn_account_init_from_funk_readonly( acct_rec, (fd_pubkey_t*)acct, slot_ctx->funk, slot_ctx->funk_txn );
    2012           0 :   if( FD_UNLIKELY( err != FD_ACC_MGR_SUCCESS ) ) {
    2013           0 :     return;
    2014           0 :   }
    2015             : 
    2016           0 :   FD_SPAD_FRAME_BEGIN( runtime_spad ) {
    2017             : 
    2018           0 :   int decode_err = 0;
    2019           0 :   fd_feature_t * feature = fd_bincode_decode_spad(
    2020           0 :       feature, runtime_spad,
    2021           0 :       acct_rec->vt->get_data( acct_rec ),
    2022           0 :       acct_rec->vt->get_data_len( acct_rec ),
    2023           0 :       &decode_err );
    2024           0 :   if( FD_UNLIKELY( decode_err ) ) {
    2025           0 :     FD_LOG_WARNING(( "Failed to decode feature account %s (%d)", FD_BASE58_ENC_32_ALLOCA( acct ), decode_err ));
    2026           0 :     return;
    2027           0 :   }
    2028             : 
    2029           0 :   if( feature->has_activated_at ) {
    2030           0 :     FD_LOG_INFO(( "feature already activated - acc: %s, slot: %lu", FD_BASE58_ENC_32_ALLOCA( acct ), feature->activated_at ));
    2031           0 :     fd_features_set( features, id, feature->activated_at);
    2032           0 :   } else {
    2033           0 :     FD_LOG_INFO(( "Feature %s not activated at %lu, activating", FD_BASE58_ENC_32_ALLOCA( acct ), feature->activated_at ));
    2034             : 
    2035           0 :     FD_TXN_ACCOUNT_DECL( modify_acct_rec );
    2036           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 );
    2037           0 :     if( FD_UNLIKELY( err != FD_ACC_MGR_SUCCESS ) ) {
    2038           0 :       return;
    2039           0 :     }
    2040             : 
    2041           0 :     feature->has_activated_at = 1;
    2042           0 :     feature->activated_at     = fd_bank_slot_get( slot_ctx->bank );
    2043           0 :     fd_bincode_encode_ctx_t encode_ctx = {
    2044           0 :       .data    = modify_acct_rec->vt->get_data_mut( modify_acct_rec ),
    2045           0 :       .dataend = modify_acct_rec->vt->get_data_mut( modify_acct_rec ) + modify_acct_rec->vt->get_data_len( modify_acct_rec ),
    2046           0 :     };
    2047           0 :     int encode_err = fd_feature_encode( feature, &encode_ctx );
    2048           0 :     if( FD_UNLIKELY( encode_err != FD_BINCODE_SUCCESS ) ) {
    2049           0 :       FD_LOG_ERR(( "Failed to encode feature account %s (%d)", FD_BASE58_ENC_32_ALLOCA( acct ), decode_err ));
    2050           0 :     }
    2051             : 
    2052           0 :     fd_txn_account_mutable_fini( modify_acct_rec, slot_ctx->funk, slot_ctx->funk_txn );
    2053           0 :   }
    2054             : 
    2055           0 :   } FD_SPAD_FRAME_END;
    2056           0 : }
    2057             : 
    2058             : static void
    2059           0 : fd_features_activate( fd_exec_slot_ctx_t * slot_ctx, fd_spad_t * runtime_spad ) {
    2060           0 :   fd_features_t * features = fd_bank_features_modify( slot_ctx->bank );
    2061           0 :   for( fd_feature_id_t const * id = fd_feature_iter_init();
    2062           0 :                                    !fd_feature_iter_done( id );
    2063           0 :                                id = fd_feature_iter_next( id ) ) {
    2064           0 :     fd_feature_activate( features, slot_ctx, id, id->id.key, runtime_spad );
    2065           0 :   }
    2066           0 : }
    2067             : 
    2068             : uint
    2069             : fd_runtime_is_epoch_boundary( fd_exec_slot_ctx_t * slot_ctx,
    2070             :                               ulong                curr_slot,
    2071           0 :                               ulong                prev_slot ) {
    2072           0 :   ulong slot_idx;
    2073           0 :   fd_epoch_schedule_t const * schedule = fd_bank_epoch_schedule_query( slot_ctx->bank );
    2074           0 :   ulong prev_epoch = fd_slot_to_epoch( schedule, prev_slot, &slot_idx );
    2075           0 :   ulong new_epoch  = fd_slot_to_epoch( schedule, curr_slot, &slot_idx );
    2076             : 
    2077           0 :   return ( prev_epoch < new_epoch || slot_idx == 0 );
    2078           0 : }
    2079             : 
    2080             : /* Starting a new epoch.
    2081             :   New epoch:        T
    2082             :   Just ended epoch: T-1
    2083             :   Epoch before:     T-2
    2084             : 
    2085             :   In this function:
    2086             :   - stakes in T-2 (epoch_stakes) should be replaced by T-1 (next_epoch_stakes)
    2087             :   - stakes at T-1 (next_epoch_stakes) should be replaced by updated stakes at T (stakes->vote_accounts)
    2088             :   - leader schedule should be calculated using new T-2 stakes (epoch_stakes)
    2089             : 
    2090             :   Invariant during an epoch T:
    2091             :   next_epoch_stakes holds the stakes at T-1
    2092             :   epoch_stakes holds the stakes at T-2
    2093             :  */
    2094             : /* process for the start of a new epoch */
    2095             : static void
    2096             : fd_runtime_process_new_epoch( fd_exec_slot_ctx_t * slot_ctx,
    2097             :                               fd_capture_ctx_t *   capture_ctx,
    2098             :                               ulong                parent_epoch,
    2099           0 :                               fd_spad_t *          runtime_spad ) {
    2100           0 :   FD_LOG_NOTICE(( "fd_process_new_epoch start" ));
    2101             : 
    2102           0 :   FD_SPAD_FRAME_BEGIN( runtime_spad ) {
    2103             : 
    2104           0 :   long start = fd_log_wallclock();
    2105             : 
    2106           0 :   ulong const slot  = fd_bank_slot_get ( slot_ctx->bank );
    2107           0 :   ulong const epoch = fd_bank_epoch_get( slot_ctx->bank );
    2108             : 
    2109             :   /* Activate new features
    2110             :      https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank.rs#L6587-L6598 */
    2111           0 :   fd_features_activate( slot_ctx, runtime_spad );
    2112           0 :   fd_features_restore( slot_ctx, runtime_spad );
    2113             : 
    2114             :   /* Apply builtin program feature transitions
    2115             :      https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank.rs#L6621-L6624 */
    2116           0 :   fd_apply_builtin_program_feature_transitions( slot_ctx, runtime_spad );
    2117             : 
    2118             :   /* Get the new rate activation epoch */
    2119           0 :   int _err[1];
    2120           0 :   ulong   new_rate_activation_epoch_val = 0UL;
    2121           0 :   ulong * new_rate_activation_epoch     = &new_rate_activation_epoch_val;
    2122           0 :   int is_some = fd_new_warmup_cooldown_rate_epoch(
    2123           0 :       fd_bank_epoch_schedule_query( slot_ctx->bank ),
    2124           0 :       fd_bank_features_query( slot_ctx->bank ),
    2125           0 :       slot,
    2126           0 :       new_rate_activation_epoch,
    2127           0 :       _err );
    2128           0 :   if( FD_UNLIKELY( !is_some ) ) {
    2129           0 :     new_rate_activation_epoch = NULL;
    2130           0 :   }
    2131             : 
    2132           0 :   fd_epoch_info_t temp_info = {0};
    2133           0 :   fd_epoch_info_new( &temp_info );
    2134             : 
    2135             :   /* If appropiate, use the stakes at T-1 to generate the leader schedule instead of T-2.
    2136             :       This is due to a subtlety in how Agave's stake caches interact when loading from snapshots.
    2137             :       See the comment in fd_exec_slot_ctx_recover_. */
    2138             : 
    2139           0 :   if( fd_bank_use_prev_epoch_stake_get( slot_ctx->bank ) == epoch ) {
    2140           0 :     fd_update_epoch_stakes( slot_ctx );
    2141           0 :   }
    2142             : 
    2143             :   /* Updates stake history sysvar accumulated values. */
    2144           0 :   fd_stakes_activate_epoch( slot_ctx,
    2145           0 :                             new_rate_activation_epoch,
    2146           0 :                             &temp_info,
    2147           0 :                             runtime_spad );
    2148             : 
    2149             :   /* Update the stakes epoch value to the new epoch */
    2150           0 :   fd_stakes_global_t * stakes = fd_bank_stakes_locking_modify( slot_ctx->bank );
    2151           0 :   stakes->epoch = epoch;
    2152           0 :   fd_bank_stakes_end_locking_modify( slot_ctx->bank );
    2153             : 
    2154           0 :   fd_update_stake_delegations( slot_ctx, &temp_info );
    2155             : 
    2156             :   /* Refresh vote accounts in stakes cache using updated stake weights, and merges slot bank vote accounts with the epoch bank vote accounts.
    2157             :     https://github.com/anza-xyz/agave/blob/v2.1.6/runtime/src/stakes.rs#L363-L370 */
    2158           0 :   fd_stake_history_t const * history = fd_sysvar_stake_history_read( slot_ctx->funk, slot_ctx->funk_txn, runtime_spad );
    2159           0 :   if( FD_UNLIKELY( !history ) ) {
    2160           0 :     FD_LOG_ERR(( "StakeHistory sysvar could not be read and decoded" ));
    2161           0 :   }
    2162             : 
    2163             :   /* FIXME: There are allocations made in here that are persisted. */
    2164           0 :   fd_refresh_vote_accounts( slot_ctx,
    2165           0 :                             history,
    2166           0 :                             new_rate_activation_epoch,
    2167           0 :                             &temp_info,
    2168           0 :                             runtime_spad );
    2169             : 
    2170             :   /* Distribute rewards */
    2171             : 
    2172           0 :   fd_hash_t parent_blockhash = {0};
    2173           0 :   {
    2174           0 :     fd_blockhashes_t const * bhq = fd_bank_block_hash_queue_query( slot_ctx->bank );
    2175           0 :     fd_hash_t const * bhq_last = fd_blockhashes_peek_last( bhq );
    2176           0 :     FD_TEST( bhq_last );
    2177           0 :     parent_blockhash = *bhq_last;
    2178           0 :   }
    2179             : 
    2180           0 :   fd_begin_partitioned_rewards( slot_ctx,
    2181           0 :                                 capture_ctx,
    2182           0 :                                 &parent_blockhash,
    2183           0 :                                 parent_epoch,
    2184           0 :                                 &temp_info,
    2185           0 :                                 runtime_spad );
    2186             : 
    2187             :   /* Replace stakes at T-2 (epoch_stakes) by stakes at T-1 (next_epoch_stakes) */
    2188           0 :   fd_update_epoch_stakes( slot_ctx );
    2189             : 
    2190             :   /* Replace stakes at T-1 (next_epoch_stakes) by updated stakes at T (stakes->vote_accounts) */
    2191           0 :   fd_update_next_epoch_stakes( slot_ctx );
    2192             : 
    2193             :   /* Update current leaders using epoch_stakes (new T-2 stakes) */
    2194           0 :   fd_runtime_update_leaders( slot_ctx->bank, fd_bank_slot_get( slot_ctx->bank ), runtime_spad );
    2195             : 
    2196           0 :   fd_calculate_epoch_accounts_hash_values( slot_ctx );
    2197             : 
    2198           0 :   FD_LOG_NOTICE(( "fd_process_new_epoch end" ));
    2199             : 
    2200           0 :   long end = fd_log_wallclock();
    2201           0 :   FD_LOG_NOTICE(("fd_process_new_epoch took %ld ns", end - start));
    2202             : 
    2203           0 :   } FD_SPAD_FRAME_END;
    2204           0 : }
    2205             : 
    2206             : /******************************************************************************/
    2207             : /* Block Parsing                                                              */
    2208             : /******************************************************************************/
    2209             : 
    2210             : /* Block iteration and parsing */
    2211             : 
    2212             : /* As a note, all of the logic in this section is used by the full firedancer
    2213             :    client. The store tile uses these APIs to help parse raw (micro)blocks
    2214             :    received from the network. */
    2215             : 
    2216             : /* Helpers */
    2217             : 
    2218             : void
    2219             : fd_runtime_update_program_cache( fd_exec_slot_ctx_t * slot_ctx,
    2220             :                                  fd_txn_p_t const *   txn_p,
    2221           0 :                                  fd_spad_t *          runtime_spad ) {
    2222           0 :   fd_txn_t const * txn_descriptor = TXN( txn_p );
    2223             : 
    2224           0 :   FD_SPAD_FRAME_BEGIN( runtime_spad ) {
    2225             : 
    2226             :   /* Iterate over account keys referenced directly in the transaction first */
    2227           0 :   fd_acct_addr_t const * acc_addrs = fd_txn_get_acct_addrs( txn_descriptor, txn_p );
    2228           0 :   for( ushort acc_idx=0; acc_idx<txn_descriptor->acct_addr_cnt; acc_idx++ ) {
    2229           0 :     fd_pubkey_t const * account = fd_type_pun_const( &acc_addrs[acc_idx] );
    2230           0 :     fd_program_cache_update_program( slot_ctx, account, runtime_spad );
    2231           0 :   }
    2232             : 
    2233           0 :   if( txn_descriptor->transaction_version==FD_TXN_V0 ) {
    2234             : 
    2235             :     /* Iterate over account keys referenced in ALUTs */
    2236           0 :     fd_acct_addr_t alut_accounts[256];
    2237           0 :     fd_slot_hashes_global_t const * slot_hashes_global = fd_sysvar_slot_hashes_read( slot_ctx->funk, slot_ctx->funk_txn, runtime_spad );
    2238           0 :     if( FD_UNLIKELY( !slot_hashes_global ) ) {
    2239           0 :       return;
    2240           0 :     }
    2241             : 
    2242           0 :     fd_slot_hash_t * slot_hash = deq_fd_slot_hash_t_join( (uchar *)slot_hashes_global + slot_hashes_global->hashes_offset );
    2243             : 
    2244             :     /* TODO: This is done twice, once in the replay tile and once in the
    2245             :        exec tile. We should consolidate the account resolution into a
    2246             :        single place, but also keep in mind from a conformance
    2247             :        perspective that these ALUT resolution checks happen after some
    2248             :        things like compute budget instruction parsing */
    2249           0 :     if( FD_UNLIKELY( fd_runtime_load_txn_address_lookup_tables(
    2250           0 :           txn_descriptor,
    2251           0 :           txn_p->payload,
    2252           0 :           slot_ctx->funk,
    2253           0 :           slot_ctx->funk_txn,
    2254           0 :           fd_bank_slot_get( slot_ctx->bank ),
    2255           0 :           slot_hash,
    2256           0 :           alut_accounts ) ) ) {
    2257           0 :       return;
    2258           0 :     }
    2259             : 
    2260           0 :     for( ushort alut_idx=0; alut_idx<txn_descriptor->addr_table_adtl_cnt; alut_idx++ ) {
    2261           0 :       fd_pubkey_t const * account = fd_type_pun_const( &alut_accounts[alut_idx] );
    2262           0 :       fd_program_cache_update_program( slot_ctx, account, runtime_spad );
    2263           0 :     }
    2264           0 :   }
    2265             : 
    2266           0 :   } FD_SPAD_FRAME_END;
    2267           0 : }
    2268             : 
    2269             : /* Public API */
    2270             : 
    2271             : /* Block collecting (Only for offline replay) */
    2272             : 
    2273             : static ulong
    2274             : fd_runtime_microblock_collect_txns( fd_microblock_info_t const * microblock_info,
    2275           0 :                                     fd_txn_p_t *                 out_txns ) {
    2276           0 :   ulong txn_cnt = microblock_info->microblock.hdr->txn_cnt;
    2277           0 :   fd_memcpy( out_txns, microblock_info->txns, txn_cnt * sizeof(fd_txn_p_t) );
    2278           0 :   return txn_cnt;
    2279           0 : }
    2280             : 
    2281             : static ulong
    2282             : fd_runtime_microblock_batch_collect_txns( fd_microblock_batch_info_t const * microblock_batch_info,
    2283           0 :                                           fd_txn_p_t *                       out_txns ) {
    2284           0 :   for( ulong i=0UL; i<microblock_batch_info->microblock_cnt; i++ ) {
    2285           0 :     ulong txns_collected = fd_runtime_microblock_collect_txns( &microblock_batch_info->microblock_infos[i], out_txns );
    2286           0 :     out_txns            += txns_collected;
    2287           0 :   }
    2288             : 
    2289           0 :   return microblock_batch_info->txn_cnt;
    2290           0 : }
    2291             : 
    2292             : static ulong
    2293             : fd_runtime_block_collect_txns( fd_runtime_block_info_t const * block_info,
    2294           0 :                                fd_txn_p_t *            out_txns ) {
    2295           0 :   for( ulong i=0UL; i<block_info->microblock_batch_cnt; i++ ) {
    2296           0 :     ulong txns_collected = fd_runtime_microblock_batch_collect_txns( &block_info->microblock_batch_infos[i], out_txns );
    2297           0 :     out_txns            += txns_collected;
    2298           0 :   }
    2299             : 
    2300           0 :   return block_info->txn_cnt;
    2301           0 : }
    2302             : 
    2303             : /******************************************************************************/
    2304             : /* Genesis                                                                    */
    2305             : /*******************************************************************************/
    2306             : 
    2307             : static void
    2308             : fd_runtime_init_program( fd_exec_slot_ctx_t * slot_ctx,
    2309           0 :                          fd_spad_t *          runtime_spad ) {
    2310           0 :   fd_sysvar_recent_hashes_init( slot_ctx );
    2311           0 :   fd_sysvar_clock_init( slot_ctx );
    2312           0 :   fd_sysvar_slot_history_init( slot_ctx, runtime_spad );
    2313           0 :   fd_sysvar_slot_hashes_init( slot_ctx, runtime_spad );
    2314           0 :   fd_sysvar_epoch_schedule_init( slot_ctx );
    2315           0 :   fd_sysvar_rent_init( slot_ctx );
    2316           0 :   fd_sysvar_stake_history_init( slot_ctx );
    2317           0 :   fd_sysvar_last_restart_slot_init( slot_ctx );
    2318             : 
    2319           0 :   fd_builtin_programs_init( slot_ctx );
    2320           0 :   fd_stake_program_config_init( slot_ctx );
    2321           0 : }
    2322             : 
    2323             : static void
    2324             : fd_runtime_init_bank_from_genesis( fd_exec_slot_ctx_t *        slot_ctx,
    2325             :                                    fd_genesis_solana_t const * genesis_block,
    2326             :                                    fd_hash_t const *           genesis_hash,
    2327           0 :                                    fd_spad_t *                 runtime_spad ) {
    2328             : 
    2329           0 :   fd_bank_poh_set( slot_ctx->bank, *genesis_hash );
    2330             : 
    2331           0 :   fd_hash_t * bank_hash = fd_bank_bank_hash_modify( slot_ctx->bank );
    2332           0 :   memset( bank_hash->hash, 0, FD_SHA256_HASH_SZ );
    2333             : 
    2334           0 :   fd_poh_config_t const * poh  = &genesis_block->poh_config;
    2335           0 :   uint128 target_tick_duration = ((uint128)poh->target_tick_duration.seconds * 1000000000UL + (uint128)poh->target_tick_duration.nanoseconds);
    2336             : 
    2337           0 :   fd_bank_epoch_schedule_set( slot_ctx->bank, genesis_block->epoch_schedule );
    2338             : 
    2339           0 :   fd_bank_rent_set( slot_ctx->bank, genesis_block->rent );
    2340             : 
    2341           0 :   fd_bank_block_height_set( slot_ctx->bank, 0UL );
    2342             : 
    2343           0 :   fd_bank_inflation_set( slot_ctx->bank, genesis_block->inflation );
    2344             : 
    2345           0 :   {
    2346             :     /* FIXME Why is there a previous blockhash at genesis?  Why is the
    2347             :              last_hash field an option type in Agave, if even the first
    2348             :              real block has a previous blockhash? */
    2349           0 :     ulong seed; FD_TEST( fd_rng_secure( &seed, sizeof(ulong) ) );
    2350           0 :     fd_blockhashes_t *    bhq  = fd_blockhashes_init( fd_bank_block_hash_queue_modify( slot_ctx->bank ), seed );
    2351           0 :     fd_blockhash_info_t * info = fd_blockhashes_push_new( bhq, genesis_hash );
    2352           0 :     info->fee_calculator.lamports_per_signature = 0UL;
    2353           0 :   }
    2354             : 
    2355           0 :   fd_bank_fee_rate_governor_set( slot_ctx->bank, genesis_block->fee_rate_governor );
    2356             : 
    2357           0 :   fd_bank_lamports_per_signature_set( slot_ctx->bank, 0UL );
    2358             : 
    2359           0 :   fd_bank_prev_lamports_per_signature_set( slot_ctx->bank, 0UL );
    2360             : 
    2361           0 :   fd_bank_max_tick_height_set( slot_ctx->bank, genesis_block->ticks_per_slot * (fd_bank_slot_get( slot_ctx->bank ) + 1) );
    2362             : 
    2363           0 :   fd_bank_hashes_per_tick_set( slot_ctx->bank, !!poh->hashes_per_tick ? poh->hashes_per_tick : 0UL );
    2364             : 
    2365           0 :   fd_bank_ns_per_slot_set( slot_ctx->bank, target_tick_duration * genesis_block->ticks_per_slot );
    2366             : 
    2367           0 :   fd_bank_ticks_per_slot_set( slot_ctx->bank, genesis_block->ticks_per_slot );
    2368             : 
    2369           0 :   fd_bank_genesis_creation_time_set( slot_ctx->bank, genesis_block->creation_time );
    2370             : 
    2371           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 );
    2372             : 
    2373           0 :   fd_bank_signature_count_set( slot_ctx->bank, 0UL );
    2374             : 
    2375             :   /* Derive epoch stakes */
    2376             : 
    2377           0 :   fd_stakes_global_t * stakes_global = fd_bank_stakes_locking_modify( slot_ctx->bank );
    2378             : 
    2379           0 :   uchar * vacc_pool_mem = (uchar *)fd_ulong_align_up( (ulong)stakes_global + sizeof(fd_stakes_global_t), fd_vote_accounts_pair_global_t_map_align() );
    2380           0 :   fd_vote_accounts_pair_global_t_mapnode_t * vacc_pool = fd_vote_accounts_pair_global_t_map_join( fd_vote_accounts_pair_global_t_map_new( vacc_pool_mem, 5000UL ) );
    2381           0 :   fd_vote_accounts_pair_global_t_mapnode_t * vacc_root = NULL;
    2382             : 
    2383             : 
    2384           0 :   uchar * sacc_pool_mem = (uchar *)fd_ulong_align_up( (ulong)vacc_pool + fd_vote_accounts_pair_global_t_map_footprint( 5000UL ), fd_delegation_pair_t_map_align() );
    2385           0 :   fd_delegation_pair_t_mapnode_t * sacc_pool = fd_delegation_pair_t_map_join( fd_delegation_pair_t_map_new( sacc_pool_mem, 5000UL ) );
    2386           0 :   fd_delegation_pair_t_mapnode_t * sacc_root = NULL;
    2387             : 
    2388           0 :   fd_acc_lamports_t capitalization = 0UL;
    2389             : 
    2390           0 :   fd_features_t * features = fd_bank_features_modify( slot_ctx->bank );
    2391           0 :   FD_FEATURE_SET_ACTIVE(features, accounts_lt_hash, 0);
    2392           0 :   FD_FEATURE_SET_ACTIVE(features, remove_accounts_delta_hash, 0);
    2393             : 
    2394           0 :   for( ulong i=0UL; i<genesis_block->accounts_len; i++ ) {
    2395           0 :     fd_pubkey_account_pair_t const * acc = &genesis_block->accounts[i];
    2396           0 :     capitalization = fd_ulong_sat_add( capitalization, acc->account.lamports );
    2397             : 
    2398           0 :     if( !memcmp(acc->account.owner.key, fd_solana_vote_program_id.key, sizeof(fd_pubkey_t)) ) {
    2399             :       /* Vote Program Account */
    2400           0 :       fd_vote_accounts_pair_global_t_mapnode_t * node = fd_vote_accounts_pair_global_t_map_acquire(vacc_pool);
    2401           0 :       FD_TEST( node );
    2402             : 
    2403           0 :       fd_memcpy(node->elem.key.key, acc->key.key, sizeof(fd_pubkey_t));
    2404           0 :       node->elem.stake = acc->account.lamports;
    2405           0 :       node->elem.value = (fd_solana_account_global_t){
    2406           0 :         .lamports = acc->account.lamports,
    2407           0 :         .data_len = acc->account.data_len,
    2408           0 :         .data_offset = 0UL, /* FIXME: remove this field from the cache altogether. */
    2409           0 :         .owner = acc->account.owner,
    2410           0 :         .executable = acc->account.executable,
    2411           0 :         .rent_epoch = acc->account.rent_epoch
    2412           0 :       };
    2413           0 :       fd_solana_account_data_update( &node->elem.value, acc->account.data );
    2414             : 
    2415           0 :       fd_vote_accounts_pair_global_t_map_insert( vacc_pool, &vacc_root, node );
    2416             : 
    2417           0 :       FD_LOG_INFO(( "Adding genesis vote account: key=%s stake=%lu",
    2418           0 :                     FD_BASE58_ENC_32_ALLOCA( node->elem.key.key ),
    2419           0 :                     node->elem.stake ));
    2420           0 :     } else if( !memcmp( acc->account.owner.key, fd_solana_stake_program_id.key, sizeof(fd_pubkey_t) ) ) {
    2421             :       /* stake program account */
    2422           0 :       fd_stake_state_v2_t   stake_state   = {0};
    2423           0 :       fd_account_meta_t     meta          = { .dlen = acc->account.data_len };
    2424           0 :       FD_TXN_ACCOUNT_DECL( stake_account );
    2425           0 :       fd_txn_account_init_from_meta_and_data_mutable( stake_account, &meta, acc->account.data );
    2426           0 :       FD_TEST( fd_stake_get_state( stake_account, &stake_state ) == 0 );
    2427           0 :       if( !stake_state.inner.stake.stake.delegation.stake ) {
    2428           0 :         continue;
    2429           0 :       }
    2430           0 :       fd_delegation_pair_t_mapnode_t   query_node = {0};
    2431           0 :       fd_memcpy(&query_node.elem.account, acc->key.key, sizeof(fd_pubkey_t));
    2432           0 :       fd_delegation_pair_t_mapnode_t * node = fd_delegation_pair_t_map_find( sacc_pool, sacc_root, &query_node );
    2433             : 
    2434           0 :       if( !node ) {
    2435           0 :         node = fd_delegation_pair_t_map_acquire( sacc_pool );
    2436           0 :         fd_memcpy( &node->elem.account, acc->key.key, sizeof(fd_pubkey_t) );
    2437           0 :         node->elem.delegation = stake_state.inner.stake.stake.delegation;
    2438           0 :         fd_delegation_pair_t_map_insert( sacc_pool, &sacc_root, node );
    2439           0 :       } else {
    2440           0 :         fd_memcpy( &node->elem.account, acc->key.key, sizeof(fd_pubkey_t) );
    2441           0 :         node->elem.delegation = stake_state.inner.stake.stake.delegation;
    2442           0 :       }
    2443           0 :     } else if( !memcmp(acc->account.owner.key, fd_solana_feature_program_id.key, sizeof(fd_pubkey_t)) ) {
    2444             :       /* Feature Account */
    2445             : 
    2446             :       /* Scan list of feature IDs to resolve address => feature offset */
    2447           0 :       fd_feature_id_t const *found = NULL;
    2448           0 :       for( fd_feature_id_t const * id = fd_feature_iter_init();
    2449           0 :            !fd_feature_iter_done( id );
    2450           0 :            id = fd_feature_iter_next( id ) ) {
    2451           0 :         if( !memcmp( acc->key.key, id->id.key, sizeof(fd_pubkey_t) ) ) {
    2452           0 :           found = id;
    2453           0 :           break;
    2454           0 :         }
    2455           0 :       }
    2456             : 
    2457           0 :       if( found ) {
    2458             :         /* Load feature activation */
    2459           0 :         FD_SPAD_FRAME_BEGIN( runtime_spad ) {
    2460           0 :           int err;
    2461           0 :           fd_feature_t * feature = fd_bincode_decode_spad(
    2462           0 :               feature, runtime_spad,
    2463           0 :               acc->account.data,
    2464           0 :               acc->account.data_len,
    2465           0 :               &err );
    2466           0 :           FD_TEST( err==FD_BINCODE_SUCCESS );
    2467             : 
    2468           0 :           fd_features_t * features = fd_bank_features_modify( slot_ctx->bank );
    2469           0 :           if( feature->has_activated_at ) {
    2470           0 :             FD_LOG_DEBUG(( "Feature %s activated at %lu (genesis)", FD_BASE58_ENC_32_ALLOCA( acc->key.key ), feature->activated_at ));
    2471           0 :             fd_features_set( features, found, feature->activated_at );
    2472           0 :           } else {
    2473           0 :             FD_LOG_DEBUG(( "Feature %s not activated (genesis)", FD_BASE58_ENC_32_ALLOCA( acc->key.key ) ));
    2474           0 :             fd_features_set( features, found, ULONG_MAX );
    2475           0 :           }
    2476           0 :         } FD_SPAD_FRAME_END;
    2477           0 :       }
    2478           0 :     }
    2479           0 :   }
    2480             : 
    2481           0 :   fd_vote_accounts_global_t * epoch_stakes = fd_bank_epoch_stakes_locking_modify( slot_ctx->bank );
    2482           0 :   uchar * pool_mem = (uchar *)fd_ulong_align_up( (ulong)epoch_stakes + sizeof(fd_vote_accounts_global_t), fd_vote_accounts_pair_t_map_align() );
    2483           0 :   fd_vote_accounts_pair_global_t_mapnode_t * vote_accounts_pool = fd_vote_accounts_pair_global_t_map_join( fd_vote_accounts_pair_global_t_map_new( pool_mem, 50000UL ) );
    2484           0 :   fd_vote_accounts_pair_global_t_mapnode_t * vote_accounts_root = NULL;
    2485             : 
    2486           0 :   uchar * epoch_stakes_vote_acc_region_curr = (uchar *)fd_ulong_align_up( (ulong)vote_accounts_pool + fd_vote_accounts_pair_global_t_map_footprint( 50000UL ), 8UL );
    2487             : 
    2488           0 :   fd_vote_accounts_global_t * next_epoch_stakes = fd_bank_next_epoch_stakes_locking_modify( slot_ctx->bank );
    2489           0 :   uchar * next_pool_mem = (uchar *)fd_ulong_align_up( (ulong)next_epoch_stakes + sizeof(fd_vote_accounts_global_t), fd_vote_accounts_pair_t_map_align() );
    2490           0 :   fd_vote_accounts_pair_global_t_mapnode_t * next_pool = fd_vote_accounts_pair_global_t_map_join( fd_vote_accounts_pair_global_t_map_new( next_pool_mem, 50000UL ) );
    2491           0 :   fd_vote_accounts_pair_global_t_mapnode_t * next_root = NULL;
    2492             : 
    2493           0 :   uchar * next_epoch_stakes_acc_region_curr = (uchar *)fd_ulong_align_up( (ulong)next_pool + fd_vote_accounts_pair_global_t_map_footprint( 50000UL ), 8UL );
    2494             : 
    2495           0 :   for( ulong i=0UL; i<genesis_block->accounts_len; i++ ) {
    2496           0 :     fd_pubkey_account_pair_t const * acc = &genesis_block->accounts[i];
    2497             : 
    2498           0 :     if( !memcmp( acc->account.owner.key, fd_solana_vote_program_id.key, sizeof(fd_pubkey_t) ) ) {
    2499             : 
    2500             :       /* Insert into the epoch_stakes vote accounts map */
    2501           0 :       fd_vote_accounts_pair_global_t_mapnode_t * e = fd_vote_accounts_pair_global_t_map_acquire( vote_accounts_pool );
    2502           0 :       FD_TEST( e );
    2503           0 :       e->elem.key = acc->key;
    2504           0 :       e->elem.stake = acc->account.lamports;
    2505           0 :       e->elem.value = (fd_solana_account_global_t){
    2506           0 :         .lamports = acc->account.lamports,
    2507           0 :         .data_len = acc->account.data_len,
    2508           0 :         .data_offset = 0UL, /* FIXME: remove this field from the cache altogether. */
    2509           0 :         .owner = acc->account.owner,
    2510           0 :         .executable = acc->account.executable,
    2511           0 :         .rent_epoch = acc->account.rent_epoch
    2512           0 :       };
    2513             : 
    2514           0 :       memcpy( epoch_stakes_vote_acc_region_curr, acc->account.data, acc->account.data_len );
    2515           0 :       e->elem.value.data_offset = (ulong)(epoch_stakes_vote_acc_region_curr - (uchar *)&e->elem.value);
    2516           0 :       epoch_stakes_vote_acc_region_curr += acc->account.data_len;
    2517             : 
    2518           0 :       fd_vote_accounts_pair_global_t_map_insert( vote_accounts_pool, &vote_accounts_root, e );
    2519             : 
    2520             :       /* Insert into the next_epoch_stakes vote accounts map */
    2521             :       /* FIXME: is this correct? */
    2522           0 :       fd_vote_accounts_pair_global_t_mapnode_t * next_e = fd_vote_accounts_pair_global_t_map_acquire( next_pool );
    2523           0 :       FD_TEST( next_e );
    2524           0 :       next_e->elem.key = acc->key;
    2525           0 :       next_e->elem.stake = acc->account.lamports;
    2526           0 :       next_e->elem.value = (fd_solana_account_global_t){
    2527           0 :         .lamports = acc->account.lamports,
    2528           0 :         .data_len = acc->account.data_len,
    2529           0 :         .data_offset = 0UL, /* FIXME: remove this field from the cache altogether. */
    2530           0 :         .owner = acc->account.owner,
    2531           0 :         .executable = acc->account.executable,
    2532           0 :         .rent_epoch = acc->account.rent_epoch
    2533           0 :       };
    2534             : 
    2535           0 :       memcpy( next_epoch_stakes_acc_region_curr, acc->account.data, acc->account.data_len );
    2536           0 :       next_e->elem.value.data_offset = (ulong)(next_epoch_stakes_acc_region_curr - (uchar *)&next_e->elem.value);
    2537           0 :       next_epoch_stakes_acc_region_curr += acc->account.data_len;
    2538             : 
    2539           0 :       fd_vote_accounts_pair_global_t_map_insert( next_pool, &next_root, next_e );
    2540           0 :     }
    2541             : 
    2542           0 :   }
    2543             : 
    2544           0 :   for( fd_delegation_pair_t_mapnode_t *n = fd_delegation_pair_t_map_minimum( sacc_pool, sacc_root );
    2545           0 :        n;
    2546           0 :        n = fd_delegation_pair_t_map_successor( sacc_pool, n )) {
    2547           0 :     fd_vote_accounts_pair_global_t_mapnode_t query_voter = {0};
    2548           0 :     query_voter.elem.key = n->elem.delegation.voter_pubkey;
    2549             : 
    2550           0 :     fd_vote_accounts_pair_global_t_mapnode_t * voter = fd_vote_accounts_pair_global_t_map_find( vacc_pool, vacc_root, &query_voter );
    2551             : 
    2552           0 :     if( !!voter ) {
    2553           0 :       voter->elem.stake = fd_ulong_sat_add( voter->elem.stake, n->elem.delegation.stake );
    2554           0 :     }
    2555           0 :   }
    2556             : 
    2557           0 :   fd_vote_accounts_vote_accounts_pool_update( epoch_stakes, vote_accounts_pool );
    2558           0 :   fd_vote_accounts_vote_accounts_root_update( epoch_stakes, vote_accounts_root );
    2559             : 
    2560             : 
    2561           0 :   fd_vote_accounts_vote_accounts_pool_update( next_epoch_stakes, next_pool );
    2562           0 :   fd_vote_accounts_vote_accounts_root_update( next_epoch_stakes, next_root );
    2563             : 
    2564           0 :   fd_bank_epoch_stakes_end_locking_modify( slot_ctx->bank );
    2565             : 
    2566           0 :   fd_bank_next_epoch_stakes_end_locking_modify( slot_ctx->bank );
    2567             : 
    2568             : 
    2569             : 
    2570           0 :   stakes_global->epoch  = 0UL;
    2571           0 :   stakes_global->unused = 0UL;
    2572             : 
    2573           0 :   fd_vote_accounts_vote_accounts_pool_update( &stakes_global->vote_accounts, vacc_pool );
    2574           0 :   fd_vote_accounts_vote_accounts_root_update( &stakes_global->vote_accounts, vacc_root );
    2575           0 :   fd_stakes_stake_delegations_pool_update( stakes_global, sacc_pool );
    2576           0 :   fd_stakes_stake_delegations_root_update( stakes_global, sacc_root );
    2577           0 :   fd_bank_stakes_end_locking_modify( slot_ctx->bank );
    2578             : 
    2579           0 :   fd_bank_capitalization_set( slot_ctx->bank, capitalization );
    2580             : 
    2581           0 :   fd_clock_timestamp_votes_global_t * clock_timestamp_votes = fd_bank_clock_timestamp_votes_locking_modify( slot_ctx->bank );
    2582           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() );
    2583           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 ) );
    2584           0 :   clock_timestamp_votes->votes_pool_offset = (ulong)fd_clock_timestamp_vote_t_map_leave( clock_pool) - (ulong)clock_timestamp_votes;
    2585           0 :   clock_timestamp_votes->votes_root_offset = 0UL;
    2586           0 :   fd_bank_clock_timestamp_votes_end_locking_modify( slot_ctx->bank );
    2587           0 : }
    2588             : 
    2589             : static int
    2590             : fd_runtime_process_genesis_block( fd_exec_slot_ctx_t * slot_ctx,
    2591             :                                   fd_capture_ctx_t *   capture_ctx,
    2592           0 :                                   fd_spad_t *          runtime_spad ) {
    2593             : 
    2594             : 
    2595           0 :   fd_hash_t * poh = fd_bank_poh_modify( slot_ctx->bank );
    2596           0 :   ulong hashcnt_per_slot = fd_bank_hashes_per_tick_get( slot_ctx->bank ) * fd_bank_ticks_per_slot_get( slot_ctx->bank );
    2597           0 :   while( hashcnt_per_slot-- ) {
    2598           0 :     fd_sha256_hash( poh->hash, sizeof(fd_hash_t), poh->hash );
    2599           0 :   }
    2600             : 
    2601           0 :   fd_bank_execution_fees_set( slot_ctx->bank, 0UL );
    2602             : 
    2603           0 :   fd_bank_priority_fees_set( slot_ctx->bank, 0UL );
    2604             : 
    2605           0 :   fd_bank_signature_count_set( slot_ctx->bank, 0UL );
    2606             : 
    2607           0 :   fd_bank_txn_count_set( slot_ctx->bank, 0UL );
    2608             : 
    2609           0 :   fd_bank_failed_txn_count_set( slot_ctx->bank, 0UL );
    2610             : 
    2611           0 :   fd_bank_nonvote_failed_txn_count_set( slot_ctx->bank, 0UL );
    2612             : 
    2613           0 :   fd_bank_total_compute_units_used_set( slot_ctx->bank, 0UL );
    2614             : 
    2615           0 :   fd_runtime_init_program( slot_ctx, runtime_spad );
    2616             : 
    2617           0 :   fd_sysvar_slot_history_update( slot_ctx, runtime_spad );
    2618             : 
    2619           0 :   fd_runtime_update_leaders( slot_ctx->bank, 0, runtime_spad );
    2620             : 
    2621           0 :   fd_runtime_freeze( slot_ctx );
    2622             : 
    2623             :   /* sort and update bank hash */
    2624           0 :   fd_hash_t * bank_hash = fd_bank_bank_hash_modify( slot_ctx->bank );
    2625           0 :   int result = fd_update_hash_bank_tpool( slot_ctx,
    2626           0 :                                           capture_ctx,
    2627           0 :                                           bank_hash,
    2628           0 :                                           0UL,
    2629           0 :                                           NULL,
    2630           0 :                                           runtime_spad );
    2631             : 
    2632           0 :   if( FD_UNLIKELY( result != FD_EXECUTOR_INSTR_SUCCESS ) ) {
    2633           0 :     FD_LOG_ERR(( "Failed to update bank hash with error=%d", result ));
    2634           0 :   }
    2635             : 
    2636           0 :   return FD_RUNTIME_EXECUTE_SUCCESS;
    2637           0 : }
    2638             : 
    2639             : void
    2640             : fd_runtime_read_genesis( fd_exec_slot_ctx_t * slot_ctx,
    2641             :                          char const *         genesis_filepath,
    2642             :                          uchar                is_snapshot,
    2643             :                          fd_capture_ctx_t *   capture_ctx,
    2644           0 :                          fd_spad_t *          runtime_spad ) {
    2645             : 
    2646           0 :   if( strlen( genesis_filepath ) == 0 ) {
    2647           0 :     return;
    2648           0 :   }
    2649             : 
    2650           0 :   struct stat sbuf;
    2651           0 :   if( FD_UNLIKELY( stat( genesis_filepath, &sbuf) < 0 ) ) {
    2652           0 :     FD_LOG_ERR(( "cannot open %s : %s", genesis_filepath, strerror(errno) ));
    2653           0 :   }
    2654           0 :   int fd = open( genesis_filepath, O_RDONLY );
    2655           0 :   if( FD_UNLIKELY( fd < 0 ) ) {
    2656           0 :     FD_LOG_ERR(("cannot open %s : %s", genesis_filepath, strerror(errno)));
    2657           0 :   }
    2658             : 
    2659           0 :   fd_genesis_solana_t * genesis_block;
    2660           0 :   fd_hash_t             genesis_hash;
    2661             : 
    2662             :   /* NOTE: These genesis decode spad allocs persist through the lifetime of fd_runtime,
    2663             :      even though they aren't used outside of this function. This is because
    2664             :      fd_runtime_init_bank_from_genesis, which depends on the genesis_block, initializes
    2665             :      a bunch of structures on spad that need to persist throughout fd_runtime. Using a bump
    2666             :      allocator does not let us free memory lower in the stack without freeing everything
    2667             :      above it (in a meaningful way).
    2668             : 
    2669             :      FIXME: Use spad frames here once the fd_runtime structures initialized here are no
    2670             :      longer spad-backed. */
    2671             : 
    2672           0 :   uchar * buf = fd_spad_alloc( runtime_spad, alignof(ulong), (ulong)sbuf.st_size );
    2673           0 :   ulong sz    = 0UL;
    2674           0 :   int res     = fd_io_read( fd, buf, (ulong)sbuf.st_size, (ulong)sbuf.st_size, &sz );
    2675           0 :   FD_TEST( res==0 );
    2676           0 :   FD_TEST( sz==(ulong)sbuf.st_size );
    2677           0 :   close( fd );
    2678             : 
    2679           0 :     int err;
    2680           0 :     genesis_block = fd_bincode_decode_spad(
    2681           0 :         genesis_solana, runtime_spad, buf, sz, &err );
    2682           0 :     if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) {
    2683           0 :       FD_LOG_ERR(( "fd_genesis_solana_decode_footprint failed (%d)", err ));
    2684           0 :     }
    2685             : 
    2686             :   // The hash is generated from the raw data... don't mess with this..
    2687           0 :   fd_sha256_hash( buf, sz, genesis_hash.uc );
    2688             : 
    2689           0 :   fd_hash_t * genesis_hash_bm = fd_bank_genesis_hash_modify( slot_ctx->bank );
    2690           0 :   fd_memcpy( genesis_hash_bm, buf, sizeof(fd_hash_t) );
    2691             : 
    2692           0 :   if( !is_snapshot ) {
    2693             :     /* Create a new Funk transaction for slot 0 */
    2694           0 :     fd_funk_txn_start_write( slot_ctx->funk );
    2695           0 :     fd_funk_txn_xid_t xid = { 0 };
    2696           0 :     xid.ul[1] = 0UL;
    2697           0 :     xid.ul[0] = 0UL;
    2698           0 :     slot_ctx->funk_txn = fd_funk_txn_prepare( slot_ctx->funk, NULL, &xid, 1 );
    2699           0 :     fd_funk_txn_end_write( slot_ctx->funk );
    2700             : 
    2701           0 :     fd_runtime_init_bank_from_genesis( slot_ctx,
    2702           0 :                                         genesis_block,
    2703           0 :                                         &genesis_hash,
    2704           0 :                                         runtime_spad );
    2705             : 
    2706           0 :     FD_LOG_DEBUG(( "start genesis accounts - count: %lu", genesis_block->accounts_len ));
    2707             : 
    2708           0 :     for( ulong i=0; i<genesis_block->accounts_len; i++ ) {
    2709           0 :       fd_pubkey_account_pair_t * a = &genesis_block->accounts[i];
    2710             : 
    2711           0 :       FD_TXN_ACCOUNT_DECL( rec );
    2712             : 
    2713           0 :       int err = fd_txn_account_init_from_funk_mutable( rec,
    2714           0 :                                                       &a->key,
    2715           0 :                                                       slot_ctx->funk,
    2716           0 :                                                       slot_ctx->funk_txn,
    2717           0 :                                                       1, /* do_create */
    2718           0 :                                                       a->account.data_len );
    2719             : 
    2720           0 :       if( FD_UNLIKELY( err ) ) {
    2721           0 :         FD_LOG_ERR(( "fd_txn_account_init_from_funk_mutable failed (%d)", err ));
    2722           0 :       }
    2723             : 
    2724           0 :       rec->vt->set_data( rec, a->account.data, a->account.data_len );
    2725           0 :       rec->vt->set_lamports( rec, a->account.lamports );
    2726           0 :       rec->vt->set_rent_epoch( rec, a->account.rent_epoch );
    2727           0 :       rec->vt->set_executable( rec, a->account.executable );
    2728           0 :       rec->vt->set_owner( rec, &a->account.owner );
    2729             : 
    2730           0 :       fd_txn_account_mutable_fini( rec, slot_ctx->funk, slot_ctx->funk_txn );
    2731           0 :     }
    2732             : 
    2733           0 :     FD_LOG_DEBUG(( "end genesis accounts" ));
    2734             : 
    2735           0 :     FD_LOG_DEBUG(( "native instruction processors - count: %lu", genesis_block->native_instruction_processors_len ));
    2736             : 
    2737           0 :     for( ulong i=0UL; i < genesis_block->native_instruction_processors_len; i++ ) {
    2738           0 :       fd_string_pubkey_pair_t * a = &genesis_block->native_instruction_processors[i];
    2739           0 :       fd_write_builtin_account( slot_ctx, a->pubkey, (const char *) a->string, a->string_len );
    2740           0 :     }
    2741             : 
    2742           0 :     fd_features_restore( slot_ctx, runtime_spad );
    2743             : 
    2744           0 :     int err = fd_runtime_process_genesis_block( slot_ctx, capture_ctx, runtime_spad );
    2745           0 :     if( FD_UNLIKELY( err ) ) {
    2746           0 :       FD_LOG_ERR(( "Genesis slot 0 execute failed with error %d", err ));
    2747           0 :     }
    2748           0 :   }
    2749             : 
    2750             : 
    2751           0 :   fd_account_keys_global_t *         stake_account_keys      = fd_bank_stake_account_keys_locking_modify( slot_ctx->bank );
    2752           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() );
    2753           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 ) );
    2754           0 :   fd_account_keys_pair_t_mapnode_t * stake_account_keys_root = NULL;
    2755             : 
    2756           0 :   fd_account_keys_account_keys_pool_update( stake_account_keys, stake_account_keys_pool );
    2757           0 :   fd_account_keys_account_keys_root_update( stake_account_keys, stake_account_keys_root );
    2758           0 :   fd_bank_stake_account_keys_end_locking_modify( slot_ctx->bank );
    2759             : 
    2760           0 :   fd_account_keys_global_t *         vote_account_keys      = fd_bank_vote_account_keys_locking_modify( slot_ctx->bank );
    2761           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() );
    2762           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 ) );
    2763           0 :   fd_account_keys_pair_t_mapnode_t * vote_account_keys_root = NULL;
    2764             : 
    2765           0 :   fd_account_keys_account_keys_pool_update( vote_account_keys, vote_account_keys_pool );
    2766           0 :   fd_account_keys_account_keys_root_update( vote_account_keys, vote_account_keys_root );
    2767             : 
    2768           0 :   fd_bank_vote_account_keys_end_locking_modify( slot_ctx->bank );
    2769           0 : }
    2770             : 
    2771             : /******************************************************************************/
    2772             : /* Offline Replay                                                             */
    2773             : /******************************************************************************/
    2774             : 
    2775             : /* As a note, currently offline and live replay of transactions has differences
    2776             :    with regards to how the execution environment is setup. These are helpers
    2777             :    used to emulate this behavior */
    2778             : 
    2779             : struct fd_poh_verification_info {
    2780             :   fd_microblock_info_t const * microblock_info;
    2781             :   fd_hash_t const            * in_poh_hash;
    2782             :   int success;
    2783             : };
    2784             : typedef struct fd_poh_verification_info fd_poh_verification_info_t;
    2785             : 
    2786             : int
    2787             : fd_runtime_block_execute( fd_exec_slot_ctx_t *            slot_ctx,
    2788             :                           fd_capture_ctx_t *              capture_ctx,
    2789             :                           fd_runtime_block_info_t const * block_info,
    2790           0 :                           fd_spad_t *                     runtime_spad ) {
    2791             : 
    2792           0 :   if ( capture_ctx != NULL && capture_ctx->capture && fd_bank_slot_get( slot_ctx->bank )>=capture_ctx->solcap_start_slot ) {
    2793           0 :     fd_solcap_writer_set_slot( capture_ctx->capture, fd_bank_slot_get( slot_ctx->bank ) );
    2794           0 :   }
    2795             : 
    2796           0 :   long block_execute_time = -fd_log_wallclock();
    2797             : 
    2798           0 :   int res = fd_runtime_block_execute_prepare( slot_ctx, runtime_spad );
    2799           0 :   if( res != FD_RUNTIME_EXECUTE_SUCCESS ) {
    2800           0 :     return res;
    2801           0 :   }
    2802             : 
    2803           0 :   ulong        txn_cnt  = block_info->txn_cnt;
    2804           0 :   fd_txn_p_t * txn_ptrs = fd_spad_alloc( runtime_spad, alignof(fd_txn_p_t), txn_cnt * sizeof(fd_txn_p_t) );
    2805             : 
    2806           0 :   fd_runtime_block_collect_txns( block_info, txn_ptrs );
    2807             : 
    2808             :   /* Initialize the cost tracker when the feature is active */
    2809           0 :   fd_cost_tracker_t * cost_tracker = fd_spad_alloc( runtime_spad, FD_COST_TRACKER_ALIGN, sizeof(fd_cost_tracker_t) );
    2810           0 :   fd_cost_tracker_init( cost_tracker, runtime_spad );
    2811             : 
    2812             :   /* We want to emulate microblock-by-microblock execution */
    2813           0 :   ulong to_exec_idx = 0UL;
    2814           0 :   for( ulong i=0UL; i<block_info->microblock_batch_cnt; i++ ) {
    2815           0 :     for( ulong j=0UL; j<block_info->microblock_batch_infos[i].microblock_cnt; j++ ) {
    2816           0 :       ulong txn_cnt = block_info->microblock_batch_infos[i].microblock_infos[j].microblock.hdr->txn_cnt;
    2817           0 :       fd_txn_p_t * mblock_txn_ptrs = &txn_ptrs[ to_exec_idx ];
    2818           0 :       ulong        mblock_txn_cnt  = txn_cnt;
    2819           0 :       to_exec_idx += txn_cnt;
    2820             : 
    2821             :       /* UPDATE */
    2822             : 
    2823           0 :       if( !mblock_txn_cnt ) continue;
    2824             : 
    2825             :       /* Reverify programs for this epoch if needed */
    2826           0 :       for( ulong txn_idx=0UL; txn_idx<mblock_txn_cnt; txn_idx++ ) {
    2827           0 :         fd_runtime_update_program_cache( slot_ctx, &mblock_txn_ptrs[txn_idx], runtime_spad );
    2828           0 :       }
    2829             : 
    2830           0 :       res = fd_runtime_process_txns_in_microblock_stream_sequential( slot_ctx,
    2831           0 :                                                                      capture_ctx,
    2832           0 :                                                                      mblock_txn_ptrs,
    2833           0 :                                                                      mblock_txn_cnt,
    2834           0 :                                                                      runtime_spad,
    2835           0 :                                                                      cost_tracker );
    2836           0 :       if( FD_UNLIKELY( res!=FD_RUNTIME_EXECUTE_SUCCESS ) ) {
    2837           0 :         return res;
    2838           0 :       }
    2839           0 :     }
    2840           0 :   }
    2841             : 
    2842           0 :   long block_finalize_time = -fd_log_wallclock();
    2843             : 
    2844           0 :   res = fd_runtime_block_execute_finalize_sequential( slot_ctx,
    2845           0 :                                                       capture_ctx,
    2846           0 :                                                       block_info,
    2847           0 :                                                       runtime_spad );
    2848           0 :   if( FD_UNLIKELY( res!=FD_RUNTIME_EXECUTE_SUCCESS ) ) {
    2849           0 :     return res;
    2850           0 :   }
    2851             : 
    2852           0 :   block_finalize_time += fd_log_wallclock();
    2853           0 :   double block_finalize_time_ms = (double)block_finalize_time * 1e-6;
    2854           0 :   FD_LOG_INFO(( "finalized block successfully - slot: %lu, elapsed: %6.6f ms", fd_bank_slot_get( slot_ctx->bank ), block_finalize_time_ms ));
    2855             : 
    2856           0 :   block_execute_time += fd_log_wallclock();
    2857           0 :   double block_execute_time_ms = (double)block_execute_time * 1e-6;
    2858             : 
    2859           0 :   FD_LOG_INFO(( "executed block successfully - slot: %lu, elapsed: %6.6f ms", fd_bank_slot_get( slot_ctx->bank ), block_execute_time_ms ));
    2860             : 
    2861           0 :   return FD_RUNTIME_EXECUTE_SUCCESS;
    2862           0 : }
    2863             : 
    2864             : void
    2865             : fd_runtime_block_pre_execute_process_new_epoch( fd_exec_slot_ctx_t * slot_ctx,
    2866             :                                                 fd_capture_ctx_t *   capture_ctx,
    2867             :                                                 fd_spad_t *          runtime_spad,
    2868           0 :                                                 int *                is_epoch_boundary ) {
    2869             : 
    2870             :   /* Update block height. */
    2871           0 :   fd_bank_block_height_set( slot_ctx->bank, fd_bank_block_height_get( slot_ctx->bank ) + 1UL );
    2872             : 
    2873           0 :   ulong const slot = fd_bank_slot_get( slot_ctx->bank );
    2874           0 :   if( slot != 0UL ) {
    2875           0 :     fd_epoch_schedule_t const * epoch_schedule = fd_bank_epoch_schedule_query( slot_ctx->bank );
    2876             : 
    2877           0 :     ulong prev_epoch = fd_slot_to_epoch( epoch_schedule, fd_bank_parent_slot_get( slot_ctx->bank ), NULL );
    2878           0 :     ulong slot_idx;
    2879           0 :     ulong new_epoch  = fd_slot_to_epoch( epoch_schedule, slot, &slot_idx );
    2880           0 :     if( FD_UNLIKELY( slot_idx==1UL && new_epoch==0UL ) ) {
    2881             :       /* The block after genesis has a height of 1. */
    2882           0 :       fd_bank_block_height_set( slot_ctx->bank, 1UL );
    2883           0 :     }
    2884             : 
    2885           0 :     if( FD_UNLIKELY( prev_epoch<new_epoch || !slot_idx ) ) {
    2886           0 :       FD_LOG_DEBUG(( "Epoch boundary" ));
    2887             :       /* Epoch boundary! */
    2888           0 :       fd_runtime_process_new_epoch( slot_ctx,
    2889           0 :                                     capture_ctx,
    2890           0 :                                     new_epoch - 1UL,
    2891           0 :                                     runtime_spad );
    2892           0 :       *is_epoch_boundary = 1;
    2893           0 :     }
    2894           0 :   } else {
    2895           0 :     *is_epoch_boundary = 0;
    2896           0 :   }
    2897             : 
    2898           0 :   if( FD_LIKELY( fd_bank_slot_get( slot_ctx->bank )!=0UL ) ) {
    2899           0 :     fd_distribute_partitioned_epoch_rewards( slot_ctx, capture_ctx );
    2900           0 :   }
    2901           0 : }
    2902             : 
    2903             : /******************************************************************************/
    2904             : /* Debugging Tools                                                            */
    2905             : /******************************************************************************/
    2906             : 
    2907             : void
    2908             : fd_runtime_checkpt( fd_capture_ctx_t *   capture_ctx,
    2909             :                     fd_exec_slot_ctx_t * slot_ctx,
    2910           0 :                     ulong                slot ) {
    2911           0 :   int is_checkpt_freq = capture_ctx != NULL && slot % capture_ctx->checkpt_freq == 0;
    2912           0 :   int is_abort_slot   = slot == ULONG_MAX;
    2913           0 :   if( !is_checkpt_freq && !is_abort_slot ) {
    2914           0 :     return;
    2915           0 :   }
    2916             : 
    2917           0 :   if( capture_ctx->checkpt_path != NULL ) {
    2918           0 :     if( !is_abort_slot ) {
    2919           0 :       FD_LOG_NOTICE(( "checkpointing at slot=%lu to file=%s", slot, capture_ctx->checkpt_path ));
    2920           0 :     } else {
    2921           0 :       FD_LOG_NOTICE(( "checkpointing after mismatch to file=%s", capture_ctx->checkpt_path ));
    2922           0 :     }
    2923             : 
    2924           0 :     unlink( capture_ctx->checkpt_path );
    2925           0 :     int err = fd_wksp_checkpt( fd_funk_wksp( slot_ctx->funk ), capture_ctx->checkpt_path, 0666, 0, NULL );
    2926           0 :     if ( err ) {
    2927           0 :       FD_LOG_ERR(( "backup failed: error %d", err ));
    2928           0 :     }
    2929           0 :   }
    2930           0 : }
    2931             : 
    2932             : int
    2933             : fd_runtime_process_txns_in_microblock_stream_sequential( fd_exec_slot_ctx_t * slot_ctx,
    2934             :                                                          fd_capture_ctx_t *   capture_ctx,
    2935             :                                                          fd_txn_p_t *         txns,
    2936             :                                                          ulong                txn_cnt,
    2937             :                                                          fd_spad_t *          runtime_spad,
    2938           0 :                                                          fd_cost_tracker_t *  cost_tracker_opt ) {
    2939             : 
    2940           0 :   int res = 0;
    2941             : 
    2942           0 :   for( ulong i=0UL; i<txn_cnt; i++ ) {
    2943           0 :     txns[i].flags = FD_TXN_P_FLAGS_SANITIZE_SUCCESS;
    2944           0 :   }
    2945             : 
    2946           0 :   fd_execute_txn_task_info_t * task_infos = fd_spad_alloc( runtime_spad,
    2947           0 :                                                            alignof(fd_execute_txn_task_info_t),
    2948           0 :                                                            txn_cnt * sizeof(fd_execute_txn_task_info_t) );
    2949             : 
    2950           0 :   for( ulong i=0UL; i<txn_cnt; i++ ) {
    2951           0 :     task_infos[ i ].spad    = runtime_spad;
    2952           0 :     task_infos[ i ].txn     = &txns[ i ];
    2953           0 :     task_infos[ i ].txn_ctx = fd_spad_alloc( task_infos[ i ].spad,
    2954           0 :                                              FD_EXEC_TXN_CTX_ALIGN,
    2955           0 :                                              FD_EXEC_TXN_CTX_FOOTPRINT );
    2956           0 :     if( FD_UNLIKELY( !task_infos[ i ].txn_ctx ) ) {
    2957           0 :       FD_LOG_ERR(( "failed to allocate txn ctx" ));
    2958           0 :     }
    2959             : 
    2960           0 :     fd_runtime_prepare_and_execute_txn( slot_ctx,
    2961           0 :                                         &txns[ i ],
    2962           0 :                                         &task_infos[ i ],
    2963           0 :                                         runtime_spad,
    2964           0 :                                         capture_ctx );
    2965             : 
    2966           0 :     if( FD_UNLIKELY( !( task_infos[ i ].txn->flags & FD_TXN_P_FLAGS_EXECUTE_SUCCESS ) ) ) {
    2967           0 :       continue;
    2968           0 :     }
    2969             : 
    2970           0 :     fd_runtime_finalize_txn( slot_ctx->funk, slot_ctx->funk_txn, &task_infos[ i ], task_infos[ i ].txn_ctx->spad, slot_ctx->bank );
    2971             : 
    2972           0 :     if( cost_tracker_opt!=NULL ) {
    2973           0 :       fd_execute_txn_task_info_t const * task_info = &task_infos[ i ];
    2974           0 :       if( FD_UNLIKELY( !( task_info->txn->flags & FD_TXN_P_FLAGS_EXECUTE_SUCCESS ) ) ) continue;
    2975             : 
    2976           0 :       fd_exec_txn_ctx_t const * txn_ctx          = task_info->txn_ctx;
    2977           0 :       fd_transaction_cost_t     transaction_cost = fd_calculate_cost_for_executed_transaction( task_info->txn_ctx,
    2978           0 :                                                                                                runtime_spad );
    2979             : 
    2980           0 :       res = fd_cost_tracker_try_add( cost_tracker_opt, txn_ctx, &transaction_cost );
    2981           0 :       if( FD_UNLIKELY( res ) ) {
    2982           0 :         FD_LOG_WARNING(( "Block cost limits exceeded for slot %lu", fd_bank_slot_get( slot_ctx->bank ) ));
    2983           0 :         break;
    2984           0 :       }
    2985           0 :     }
    2986           0 :   }
    2987             : 
    2988           0 :   return res;
    2989           0 : }
    2990             : 
    2991             : int
    2992             : fd_runtime_block_execute_finalize_sequential( fd_exec_slot_ctx_t *             slot_ctx,
    2993             :                                               fd_capture_ctx_t *               capture_ctx,
    2994             :                                               fd_runtime_block_info_t const *  block_info,
    2995           0 :                                               fd_spad_t *                      runtime_spad ) {
    2996             : 
    2997           0 :   FD_SPAD_FRAME_BEGIN( runtime_spad ) {
    2998             : 
    2999           0 :   fd_accounts_hash_task_data_t * task_data = NULL;
    3000             : 
    3001           0 :   fd_runtime_block_execute_finalize_start( slot_ctx, runtime_spad, &task_data, 1UL );
    3002             : 
    3003           0 :   if( task_data && task_data->info_sz > 0UL ) {
    3004           0 :     for( ulong i=0UL; i<task_data->info_sz; i++ ) {
    3005           0 :       fd_account_hash_task( task_data, i, i, &task_data->lthash_values[0], slot_ctx, 0UL,
    3006           0 :                            0UL, 0UL, 0UL, 0UL, 0UL, 0UL );
    3007           0 :     }
    3008           0 :   }
    3009             : 
    3010           0 :   fd_runtime_block_execute_finalize_finish( slot_ctx, capture_ctx, block_info, runtime_spad, task_data, 1UL );
    3011             : 
    3012           0 :   } FD_SPAD_FRAME_END;
    3013             : 
    3014           0 :   return 0;
    3015           0 : }

Generated by: LCOV version 1.14