LCOV - code coverage report
Current view: top level - flamenco/runtime/context - fd_exec_slot_ctx.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 24 236 10.2 %
Date: 2025-08-05 05:04:49 Functions: 4 7 57.1 %

          Line data    Source code
       1             : #include "fd_exec_slot_ctx.h"
       2             : #include "../sysvar/fd_sysvar_epoch_schedule.h"
       3             : 
       4             : #include <assert.h>
       5             : #include <time.h>
       6             : 
       7             : void *
       8           3 : fd_exec_slot_ctx_new( void * mem ) {
       9             : 
      10           3 :   if( FD_UNLIKELY( !mem ) ) {
      11           0 :     FD_LOG_WARNING(( "NULL mem" ));
      12           0 :     return NULL;
      13           0 :   }
      14             : 
      15           3 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)mem, FD_EXEC_SLOT_CTX_ALIGN ) ) ) {
      16           0 :     FD_LOG_WARNING(( "misaligned mem" ));
      17           0 :     return NULL;
      18           0 :   }
      19             : 
      20           3 :   fd_memset( mem, 0, sizeof(fd_exec_slot_ctx_t) );
      21             : 
      22           3 :   fd_exec_slot_ctx_t * self = (fd_exec_slot_ctx_t *)mem;
      23             : 
      24           3 :   FD_COMPILER_MFENCE();
      25           3 :   self->magic = FD_EXEC_SLOT_CTX_MAGIC;
      26           3 :   FD_COMPILER_MFENCE();
      27             : 
      28           3 :   return mem;
      29           3 : }
      30             : 
      31             : fd_exec_slot_ctx_t *
      32           3 : fd_exec_slot_ctx_join( void * mem ) {
      33           3 :   if( FD_UNLIKELY( !mem ) ) {
      34           0 :     FD_LOG_WARNING(( "NULL block" ));
      35           0 :     return NULL;
      36           0 :   }
      37             : 
      38           3 :   fd_exec_slot_ctx_t * ctx = (fd_exec_slot_ctx_t *) mem;
      39             : 
      40           3 :   if( FD_UNLIKELY( ctx->magic!=FD_EXEC_SLOT_CTX_MAGIC ) ) {
      41           0 :     FD_LOG_WARNING(( "bad magic" ));
      42           0 :     return NULL;
      43           0 :   }
      44             : 
      45           3 :   return ctx;
      46           3 : }
      47             : 
      48             : void *
      49           3 : fd_exec_slot_ctx_leave( fd_exec_slot_ctx_t * ctx) {
      50           3 :   if( FD_UNLIKELY( !ctx ) ) {
      51           0 :     FD_LOG_WARNING(( "NULL block" ));
      52           0 :     return NULL;
      53           0 :   }
      54             : 
      55           3 :   if( FD_UNLIKELY( ctx->magic!=FD_EXEC_SLOT_CTX_MAGIC ) ) {
      56           0 :     FD_LOG_WARNING(( "bad magic" ));
      57           0 :     return NULL;
      58           0 :   }
      59             : 
      60           3 :   return (void *) ctx;
      61           3 : }
      62             : 
      63             : void *
      64           0 : fd_exec_slot_ctx_delete( void * mem ) {
      65           0 :   if( FD_UNLIKELY( !mem ) ) {
      66           0 :     FD_LOG_WARNING(( "NULL mem" ));
      67           0 :     return NULL;
      68           0 :   }
      69             : 
      70           0 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)mem, FD_EXEC_SLOT_CTX_ALIGN) ) )  {
      71           0 :     FD_LOG_WARNING(( "misaligned mem" ));
      72           0 :     return NULL;
      73           0 :   }
      74             : 
      75           0 :   fd_exec_slot_ctx_t * hdr = (fd_exec_slot_ctx_t *)mem;
      76           0 :   if( FD_UNLIKELY( hdr->magic!=FD_EXEC_SLOT_CTX_MAGIC ) ) {
      77           0 :     FD_LOG_WARNING(( "bad magic" ));
      78           0 :     return NULL;
      79           0 :   }
      80             : 
      81           0 :   FD_COMPILER_MFENCE();
      82           0 :   FD_VOLATILE( hdr->magic ) = 0UL;
      83           0 :   FD_COMPILER_MFENCE();
      84             : 
      85           0 :   return mem;
      86           0 : }
      87             : 
      88             : fd_exec_slot_ctx_t *
      89             : fd_exec_slot_ctx_recover( fd_exec_slot_ctx_t *                slot_ctx,
      90           0 :                           fd_solana_manifest_global_t const * manifest ) {
      91           0 :   ulong stakes_sz = fd_stakes_size_global( &manifest->bank.stakes );
      92           0 :   fd_stakes_global_t * stakes = fd_bank_stakes_locking_modify( slot_ctx->bank );
      93           0 :   fd_memcpy( stakes, &manifest->bank.stakes, stakes_sz );
      94             :   /* Verify stakes */
      95             : 
      96           0 :   fd_bank_stakes_end_locking_modify( slot_ctx->bank );
      97             : 
      98             :   /* Move EpochStakes */
      99           0 :   do {
     100           0 :     ulong epoch = fd_bank_epoch_get( slot_ctx->bank );
     101             : 
     102             :     /* We need to save the vote accounts for the current epoch and the next
     103             :        epoch as it is used to calculate the leader schedule at the epoch
     104             :        boundary. */
     105             : 
     106           0 :     fd_vote_accounts_global_t * vote_accounts_curr_stakes = NULL;
     107           0 :     fd_vote_accounts_global_t * vote_accounts_next_stakes = NULL;
     108             : 
     109           0 :     fd_epoch_epoch_stakes_pair_global_t * versioned_bank_epoch_stakes = fd_versioned_bank_epoch_stakes_join( &manifest->bank );
     110           0 :     for( ulong i=0UL; i<manifest->bank.epoch_stakes_len; i++ ) {
     111           0 :       if( versioned_bank_epoch_stakes[i].key == epoch ) {
     112           0 :         vote_accounts_curr_stakes = &versioned_bank_epoch_stakes[i].value.stakes.vote_accounts;
     113           0 :       }
     114           0 :       if( versioned_bank_epoch_stakes[i].key == epoch+1UL ) {
     115           0 :         vote_accounts_next_stakes = &versioned_bank_epoch_stakes[i].value.stakes.vote_accounts;
     116           0 :       }
     117             : 
     118             :       /* When loading from a snapshot, Agave's stake caches mean that we have to special-case the epoch stakes
     119             :          that are used for the second epoch E+2 after the snapshot epoch E.
     120             : 
     121             :          If the snapshot contains the epoch stakes for E+2, we should use those.
     122             : 
     123             :          If the snapshot does not, we should use the stakes at the end of the E-1 epoch, instead of E-2 as we do for
     124             :          all other epochs. */
     125           0 :     }
     126             : 
     127           0 :     fd_versioned_epoch_stakes_pair_global_t * versioned_epoch_stakes = fd_solana_manifest_versioned_epoch_stakes_join( manifest );
     128           0 :     for( ulong i=0UL; i<manifest->versioned_epoch_stakes_len; i++ ) {
     129             : 
     130           0 :       if( versioned_epoch_stakes[i].epoch == epoch ) {
     131           0 :         vote_accounts_curr_stakes = &versioned_epoch_stakes[i].val.inner.Current.stakes.vote_accounts;
     132           0 :       }
     133           0 :       if( versioned_epoch_stakes[i].epoch == epoch+1UL ) {
     134           0 :         vote_accounts_next_stakes = &versioned_epoch_stakes[i].val.inner.Current.stakes.vote_accounts;
     135             : 
     136             :         /* Save the initial value to be used for the get_epoch_stake
     137             :            syscall.
     138             : 
     139             :            A note on Agave's indexing scheme for their epoch_stakes
     140             :            structure:
     141             : 
     142             :            https://github.com/anza-xyz/agave/blob/v2.2.14/runtime/src/bank.rs#L6175
     143             : 
     144             :            If we are loading a snapshot and replaying in the middle of
     145             :            epoch 7, the syscall is supposed to return the total stake at
     146             :            the end of epoch 6.  The epoch_stakes structure is indexed in
     147             :            Agave by the epoch number of the leader schedule that the
     148             :            stakes are meant to determine.  For instance, to get the
     149             :            stakes at the end of epoch 6, we should query by 8, because
     150             :            the leader schedule for epoch 8 is determined based on the
     151             :            stakes at the end of epoch 6.  Therefore, we save the total
     152             :            epoch stake by querying for epoch+1. */
     153           0 :         fd_bank_total_epoch_stake_set( slot_ctx->bank, versioned_epoch_stakes[i].val.inner.Current.total_stake );
     154           0 :       }
     155           0 :     }
     156             : 
     157           0 :     fd_bank_use_prev_epoch_stake_set( slot_ctx->bank, epoch + 2UL );
     158             : 
     159           0 :     fd_vote_accounts_pair_global_t_mapnode_t * vote_accounts_curr_stakes_pool = fd_vote_accounts_vote_accounts_pool_join( vote_accounts_curr_stakes );
     160           0 :     fd_vote_accounts_pair_global_t_mapnode_t * vote_accounts_curr_stakes_root = fd_vote_accounts_vote_accounts_root_join( vote_accounts_curr_stakes );
     161             : 
     162           0 :     fd_vote_accounts_pair_global_t_mapnode_t * vote_accounts_next_stakes_pool = fd_vote_accounts_vote_accounts_pool_join( vote_accounts_next_stakes );
     163           0 :     fd_vote_accounts_pair_global_t_mapnode_t * vote_accounts_next_stakes_root = fd_vote_accounts_vote_accounts_root_join( vote_accounts_next_stakes );
     164             : 
     165           0 :     if( FD_UNLIKELY( (!vote_accounts_curr_stakes_pool) | (!vote_accounts_next_stakes_pool) ) ) {
     166           0 :       FD_LOG_WARNING(( "snapshot missing EpochStakes for epochs %lu and/or %lu", epoch, epoch+1UL ));
     167           0 :       return 0;
     168           0 :     }
     169             : 
     170             :     /* Move current EpochStakes */
     171             : 
     172           0 :     fd_vote_accounts_global_t * epoch_stakes = fd_bank_epoch_stakes_locking_modify( slot_ctx->bank );
     173           0 :     uchar * epoch_stakes_pool_mem = (uchar *)fd_ulong_align_up( (ulong)epoch_stakes + sizeof(fd_vote_accounts_global_t), fd_vote_accounts_pair_global_t_map_align() );
     174           0 :     fd_vote_accounts_pair_global_t_mapnode_t * epoch_stakes_pool = fd_vote_accounts_pair_global_t_map_join( fd_vote_accounts_pair_global_t_map_new( epoch_stakes_pool_mem, 50000UL ) );
     175           0 :     fd_vote_accounts_pair_global_t_mapnode_t * epoch_stakes_root = NULL;
     176             : 
     177           0 :     uchar * acc_region_curr = (uchar *)fd_ulong_align_up( (ulong)epoch_stakes_pool + fd_vote_accounts_pair_global_t_map_footprint( 50000UL ), 8UL );
     178             : 
     179           0 :     for( fd_vote_accounts_pair_global_t_mapnode_t * n = fd_vote_accounts_pair_global_t_map_minimum(
     180           0 :           vote_accounts_curr_stakes_pool,
     181           0 :           vote_accounts_curr_stakes_root );
     182           0 :           n;
     183           0 :           n = fd_vote_accounts_pair_global_t_map_successor( vote_accounts_curr_stakes_pool, n ) ) {
     184             : 
     185           0 :       fd_vote_accounts_pair_global_t_mapnode_t * elem = fd_vote_accounts_pair_global_t_map_acquire(
     186           0 :         epoch_stakes_pool );
     187           0 :       FD_TEST( elem );
     188             : 
     189           0 :       elem->elem.stake = n->elem.stake;
     190           0 :       elem->elem.key   = n->elem.key;
     191             : 
     192           0 :       elem->elem.value.lamports    = n->elem.value.lamports;
     193           0 :       elem->elem.value.data_len    = 0UL;
     194           0 :       elem->elem.value.data_offset = 0UL;
     195           0 :       elem->elem.value.owner       = n->elem.value.owner;
     196           0 :       elem->elem.value.executable  = n->elem.value.executable;
     197           0 :       elem->elem.value.rent_epoch  = n->elem.value.rent_epoch;
     198             : 
     199           0 :       elem->elem.value.data_offset = (ulong)(acc_region_curr - (uchar *)&elem->elem.value);
     200           0 :       elem->elem.value.data_len = n->elem.value.data_len;
     201             : 
     202           0 :       uchar * manifest_data = fd_solana_account_data_join( &n->elem.value );
     203           0 :       memcpy( acc_region_curr, manifest_data, n->elem.value.data_len );
     204           0 :       acc_region_curr += n->elem.value.data_len;
     205             : 
     206           0 :       fd_vote_accounts_pair_global_t_map_insert(
     207           0 :         epoch_stakes_pool,
     208           0 :         &epoch_stakes_root,
     209           0 :         elem );
     210           0 :     }
     211             : 
     212           0 :     fd_vote_accounts_vote_accounts_pool_update( epoch_stakes, epoch_stakes_pool );
     213           0 :     fd_vote_accounts_vote_accounts_root_update( epoch_stakes, epoch_stakes_root );
     214           0 :     fd_bank_epoch_stakes_end_locking_modify( slot_ctx->bank );
     215             : 
     216             :     /* Move next EpochStakes */
     217             : 
     218           0 :     fd_vote_accounts_global_t * next_epoch_stakes = fd_bank_next_epoch_stakes_locking_modify( slot_ctx->bank );
     219           0 :     uchar * next_epoch_stakes_pool_mem = (uchar *)fd_ulong_align_up( (ulong)next_epoch_stakes + sizeof(fd_vote_accounts_global_t), fd_vote_accounts_pair_global_t_map_align() );
     220           0 :     fd_vote_accounts_pair_global_t_mapnode_t * next_epoch_stakes_pool = fd_vote_accounts_pair_global_t_map_join( fd_vote_accounts_pair_global_t_map_new( next_epoch_stakes_pool_mem, 50000UL ) );
     221           0 :     fd_vote_accounts_pair_global_t_mapnode_t * next_epoch_stakes_root = NULL;
     222             : 
     223           0 :     fd_vote_accounts_pair_global_t_mapnode_t * pool = vote_accounts_next_stakes_pool;
     224           0 :     fd_vote_accounts_pair_global_t_mapnode_t * root = vote_accounts_next_stakes_root;
     225             : 
     226           0 :     acc_region_curr = (uchar *)fd_ulong_align_up( (ulong)next_epoch_stakes_pool + fd_vote_accounts_pair_global_t_map_footprint( 50000UL ), 8UL );
     227             : 
     228           0 :     for( fd_vote_accounts_pair_global_t_mapnode_t * n = fd_vote_accounts_pair_global_t_map_minimum( pool, root );
     229           0 :          n;
     230           0 :          n = fd_vote_accounts_pair_global_t_map_successor( pool, n ) ) {
     231             : 
     232           0 :       fd_vote_accounts_pair_global_t_mapnode_t * elem = fd_vote_accounts_pair_global_t_map_acquire( next_epoch_stakes_pool );
     233           0 :       FD_TEST( elem );
     234             : 
     235           0 :       elem->elem.stake = n->elem.stake;
     236           0 :       elem->elem.key   = n->elem.key;
     237             : 
     238           0 :       elem->elem.value.lamports    = n->elem.value.lamports;
     239           0 :       elem->elem.value.data_len    = 0UL;
     240           0 :       elem->elem.value.data_offset = 0UL;
     241           0 :       elem->elem.value.owner       = n->elem.value.owner;
     242           0 :       elem->elem.value.executable  = n->elem.value.executable;
     243           0 :       elem->elem.value.rent_epoch  = n->elem.value.rent_epoch;
     244             : 
     245           0 :       elem->elem.value.data_offset = (ulong)(acc_region_curr - (uchar *)&elem->elem.value);;
     246           0 :       elem->elem.value.data_len = n->elem.value.data_len;
     247             : 
     248           0 :       uchar * manifest_data = fd_solana_account_data_join( &n->elem.value );
     249           0 :       memcpy( acc_region_curr, manifest_data, n->elem.value.data_len );
     250           0 :       acc_region_curr += n->elem.value.data_len;
     251             : 
     252           0 :       fd_vote_accounts_pair_global_t_map_insert(
     253           0 :         next_epoch_stakes_pool,
     254           0 :         &next_epoch_stakes_root,
     255           0 :         elem );
     256             : 
     257           0 :     }
     258           0 :     fd_vote_accounts_vote_accounts_pool_update( next_epoch_stakes, next_epoch_stakes_pool );
     259           0 :     fd_vote_accounts_vote_accounts_root_update( next_epoch_stakes, next_epoch_stakes_root );
     260           0 :     fd_bank_next_epoch_stakes_end_locking_modify( slot_ctx->bank );
     261             : 
     262           0 :   } while(0);
     263             : 
     264           0 :   return slot_ctx;
     265           0 : }
     266             : 
     267             : fd_exec_slot_ctx_t *
     268             : fd_exec_slot_ctx_recover_status_cache( fd_exec_slot_ctx_t *    ctx,
     269             :                                        fd_bank_slot_deltas_t * slot_deltas,
     270           0 :                                        fd_spad_t *             runtime_spad ) {
     271             : 
     272           0 :   fd_txncache_t * status_cache = ctx->status_cache;
     273           0 :   if( !status_cache ) {
     274           0 :     FD_LOG_WARNING(("No status cache in slot ctx"));
     275           0 :     return NULL;
     276           0 :   }
     277             : 
     278           0 :   FD_SPAD_FRAME_BEGIN( runtime_spad ) {
     279             : 
     280           0 :   ulong num_entries = 0;
     281           0 :   for( ulong i = 0; i < slot_deltas->slot_deltas_len; i++ ) {
     282           0 :     fd_slot_delta_t * slot_delta = &slot_deltas->slot_deltas[i];
     283           0 :     for( ulong j = 0; j < slot_delta->slot_delta_vec_len; j++ ) {
     284           0 :       num_entries += slot_delta->slot_delta_vec[j].value.statuses_len;
     285           0 :     }
     286           0 :   }
     287           0 :   fd_txncache_insert_t * insert_vals = fd_spad_alloc_check( runtime_spad, alignof(fd_txncache_insert_t), num_entries * sizeof(fd_txncache_insert_t) );
     288             : 
     289             :   /* Dumb sort for 300 slot entries to insert in order. */
     290           0 :   fd_slot_delta_t ** deltas = fd_spad_alloc_check( runtime_spad, alignof(fd_slot_delta_t*), slot_deltas->slot_deltas_len * sizeof(fd_slot_delta_t*) );
     291             : 
     292           0 :   long curr = -1;
     293           0 :   for( ulong i = 0UL; i < slot_deltas->slot_deltas_len; i++ ) {
     294           0 :     ulong curr_min     = ULONG_MAX;
     295           0 :     ulong curr_min_idx = ULONG_MAX;
     296           0 :     for( ulong j = 0; j < slot_deltas->slot_deltas_len; j++ ) {
     297           0 :       fd_slot_delta_t * slot_delta = &slot_deltas->slot_deltas[j];
     298           0 :       if( (long)slot_delta->slot <= curr ) continue;
     299             : 
     300           0 :       if( curr_min > slot_delta->slot ) {
     301           0 :         curr_min = slot_delta->slot;
     302           0 :         curr_min_idx = j;
     303           0 :       }
     304           0 :     }
     305           0 :     deltas[i] = &slot_deltas->slot_deltas[curr_min_idx];
     306           0 :     curr = (long)slot_deltas->slot_deltas[curr_min_idx].slot;
     307           0 :   }
     308             : 
     309           0 :   ulong idx = 0;
     310           0 :   for( ulong i = 0; i < slot_deltas->slot_deltas_len; i++ ) {
     311           0 :     fd_slot_delta_t * slot_delta = deltas[i];
     312           0 :     ulong slot = slot_delta->slot;
     313           0 :     if( slot_delta->is_root ) {
     314           0 :       fd_txncache_register_root_slot( ctx->status_cache, slot );
     315           0 :     }
     316           0 :     for( ulong j = 0; j < slot_delta->slot_delta_vec_len; j++ ) {
     317           0 :       fd_status_pair_t * pair = &slot_delta->slot_delta_vec[j];
     318           0 :       fd_hash_t * blockhash = &pair->hash;
     319           0 :       uchar * results = fd_spad_alloc( runtime_spad, FD_SPAD_ALIGN, pair->value.statuses_len );
     320           0 :       for( ulong k = 0; k < pair->value.statuses_len; k++ ) {
     321           0 :         fd_cache_status_t * status = &pair->value.statuses[k];
     322           0 :         uchar * result = results + k;
     323           0 :         *result = (uchar)status->result.discriminant;
     324           0 :         insert_vals[idx++] = (fd_txncache_insert_t){
     325           0 :           .blockhash = blockhash->uc,
     326           0 :           .slot = slot,
     327           0 :           .txnhash = status->key_slice,
     328           0 :           .result = result
     329           0 :         };
     330           0 :       }
     331           0 :     }
     332           0 :   }
     333           0 :   fd_txncache_insert_batch( ctx->status_cache, insert_vals, num_entries );
     334             : 
     335           0 :   for( ulong i = 0; i < slot_deltas->slot_deltas_len; i++ ) {
     336           0 :     fd_slot_delta_t * slot_delta = deltas[i];
     337           0 :     ulong slot = slot_delta->slot;
     338           0 :     for( ulong j = 0; j < slot_delta->slot_delta_vec_len; j++ ) {
     339           0 :       fd_status_pair_t * pair      = &slot_delta->slot_delta_vec[j];
     340           0 :       fd_hash_t *        blockhash = &pair->hash;
     341           0 :       fd_txncache_set_txnhash_offset( ctx->status_cache, slot, blockhash->uc, pair->value.txn_idx );
     342           0 :     }
     343           0 :   }
     344             : 
     345           0 :   } FD_SPAD_FRAME_END;
     346           0 :   return ctx;
     347           0 : }
     348             : 
     349             : ulong
     350          15 : fd_bank_epoch_get( fd_bank_t const * bank ) {
     351          15 :   fd_epoch_schedule_t epoch_schedule = fd_bank_epoch_schedule_get( bank );
     352             :   return fd_slot_to_epoch( &epoch_schedule, fd_bank_slot_get( bank ), NULL );
     353          15 : }

Generated by: LCOV version 1.14