LCOV - code coverage report
Current view: top level - flamenco/runtime - fd_runtime.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 208 2869 7.2 %
Date: 2024-11-13 11:58:15 Functions: 8 106 7.5 %

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

Generated by: LCOV version 1.14