LCOV - code coverage report
Current view: top level - discof/restore/utils - fd_ssload.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 207 0.0 %
Date: 2025-10-13 04:42:14 Functions: 0 2 0.0 %

          Line data    Source code
       1             : #include "fd_ssload.h"
       2             : 
       3             : #include "../../../flamenco/runtime/sysvar/fd_sysvar_epoch_schedule.h"
       4             : #include "fd_ssmsg.h"
       5             : 
       6             : void
       7             : blockhashes_recover( fd_blockhashes_t *                       blockhashes,
       8             :                      fd_snapshot_manifest_blockhash_t const * ages,
       9             :                      ulong                                    age_cnt,
      10           0 :                      ulong                                    seed ) {
      11           0 :   FD_TEST( fd_blockhashes_init( blockhashes, seed ) );
      12           0 :   FD_TEST( age_cnt && age_cnt<=FD_BLOCKHASHES_MAX );
      13             : 
      14             :   /* For depressing reasons, the ages array is not sorted when ingested
      15             :      from a snapshot.  The hash_index field is also not validated.
      16             :      Firedancer assumes that the sequence of hash_index numbers is
      17             :      gapless and does not wrap around. */
      18             : 
      19           0 :   ulong seq_min = ULONG_MAX-1;
      20           0 :   for( ulong i=0UL; i<age_cnt; i++ ) {
      21           0 :     seq_min = fd_ulong_min( seq_min, ages[ i ].hash_index );
      22           0 :   }
      23           0 :   ulong seq_max;
      24           0 :   if( FD_UNLIKELY( __builtin_uaddl_overflow( seq_min, age_cnt, &seq_max ) ) ) {
      25             :     /* TODO: Move to snapin validations so we can retry */
      26           0 :     FD_LOG_ERR(( "Corrupt snapshot: blockhash queue sequence number wraparound (seq_min=%lu age_cnt=%lu)", seq_min, age_cnt ));
      27           0 :   }
      28             : 
      29             :   /* Reset */
      30             : 
      31           0 :   for( ulong i=0UL; i<age_cnt; i++ ) {
      32           0 :     fd_blockhash_info_t * ele = fd_blockhash_deq_push_tail_nocopy( blockhashes->d.deque );
      33           0 :     memset( ele, 0, sizeof(fd_blockhash_info_t) );
      34           0 :   }
      35             : 
      36             :   /* Load hashes */
      37             : 
      38           0 :   for( ulong i=0UL; i<age_cnt; i++ ) {
      39           0 :     fd_snapshot_manifest_blockhash_t const * elem = &ages[ i ];
      40           0 :     ulong idx;
      41           0 :     if( FD_UNLIKELY( __builtin_usubl_overflow( elem->hash_index, seq_min, &idx ) ) ) {
      42             :       /* TODO: Move to snapin validations so we can retry */
      43           0 :       FD_LOG_ERR(( "Corrupt snapshot: gap in blockhash queue (seq=[%lu,%lu) idx=%lu)",
      44           0 :                    seq_min, seq_max, elem->hash_index ));
      45           0 :     }
      46           0 :     fd_blockhash_info_t * info = &blockhashes->d.deque[ idx ];
      47           0 :     if( FD_UNLIKELY( info->exists ) ) {
      48             :       /* TODO: Move to snapin validations so we can retry */
      49           0 :       FD_LOG_HEXDUMP_NOTICE(( "info", info, sizeof(fd_blockhash_info_t) ));
      50           0 :       FD_LOG_ERR(( "Corrupt snapshot: duplicate blockhash queue index %lu", idx ));
      51           0 :     }
      52           0 :     info->exists         = 1;
      53           0 :     fd_memcpy( info->hash.uc, elem->hash, 32UL );
      54           0 :     info->fee_calculator.lamports_per_signature = elem->lamports_per_signature;
      55           0 :     fd_blockhash_map_idx_insert( blockhashes->map, idx, blockhashes->d.deque );
      56           0 :   }
      57           0 : }
      58             : 
      59             : void
      60             : fd_ssload_recover( fd_snapshot_manifest_t * manifest,
      61             :                    fd_banks_t *             banks,
      62           0 :                    fd_bank_t *              bank ) {
      63             : 
      64             :   /* Slot */
      65             : 
      66           0 :   fd_bank_slot_set( bank, manifest->slot );
      67           0 :   fd_bank_parent_slot_set( bank, manifest->parent_slot );
      68             : 
      69             :   /* Bank Hash */
      70             : 
      71           0 :   fd_hash_t hash;
      72           0 :   fd_memcpy( &hash.uc, manifest->bank_hash, 32UL );
      73           0 :   fd_bank_bank_hash_set( bank, hash );
      74             : 
      75           0 :   fd_hash_t parent_hash;
      76           0 :   fd_memcpy( &parent_hash.uc, manifest->parent_bank_hash, 32UL );
      77           0 :   fd_bank_prev_bank_hash_set( bank, parent_hash );
      78             : 
      79           0 :   fd_fee_rate_governor_t * fee_rate_governor = fd_bank_fee_rate_governor_modify( bank );
      80           0 :   fee_rate_governor->target_lamports_per_signature = manifest->fee_rate_governor.target_lamports_per_signature;
      81           0 :   fee_rate_governor->target_signatures_per_slot    = manifest->fee_rate_governor.target_signatures_per_slot;
      82           0 :   fee_rate_governor->min_lamports_per_signature    = manifest->fee_rate_governor.min_lamports_per_signature;
      83           0 :   fee_rate_governor->max_lamports_per_signature    = manifest->fee_rate_governor.max_lamports_per_signature;
      84           0 :   fee_rate_governor->burn_percent                  = manifest->fee_rate_governor.burn_percent;
      85             : 
      86           0 :   fd_inflation_t * inflation = fd_bank_inflation_modify( bank );
      87           0 :   inflation->initial         = manifest->inflation_params.initial;
      88           0 :   inflation->terminal        = manifest->inflation_params.terminal;
      89           0 :   inflation->taper           = manifest->inflation_params.taper;
      90           0 :   inflation->foundation      = manifest->inflation_params.foundation;
      91           0 :   inflation->foundation_term = manifest->inflation_params.foundation_term;
      92           0 :   inflation->unused          = 0.0;
      93             : 
      94           0 :   fd_epoch_schedule_t * epoch_schedule = fd_bank_epoch_schedule_modify( bank );
      95           0 :   epoch_schedule->slots_per_epoch             = manifest->epoch_schedule_params.slots_per_epoch;
      96           0 :   epoch_schedule->leader_schedule_slot_offset = manifest->epoch_schedule_params.leader_schedule_slot_offset;
      97           0 :   epoch_schedule->warmup                      = manifest->epoch_schedule_params.warmup;
      98           0 :   epoch_schedule->first_normal_epoch          = manifest->epoch_schedule_params.first_normal_epoch;
      99           0 :   epoch_schedule->first_normal_slot           = manifest->epoch_schedule_params.first_normal_slot;
     100             : 
     101           0 :   ulong epoch = fd_slot_to_epoch( epoch_schedule, manifest->slot, NULL );
     102           0 :   fd_bank_epoch_set( bank, epoch );
     103             : 
     104           0 :   fd_rent_t * rent = fd_bank_rent_modify( bank );
     105           0 :   rent->lamports_per_uint8_year = manifest->rent_params.lamports_per_uint8_year;
     106           0 :   rent->exemption_threshold     = manifest->rent_params.exemption_threshold;
     107           0 :   rent->burn_percent            = manifest->rent_params.burn_percent;
     108             : 
     109           0 :   if( FD_LIKELY( manifest->has_hashes_per_tick ) ) fd_bank_hashes_per_tick_set( bank, manifest->hashes_per_tick );
     110           0 :   else                                             fd_bank_hashes_per_tick_set( bank, DEFAULT_HASHES_PER_TICK );
     111             : 
     112           0 :   if( FD_LIKELY( manifest->has_accounts_lthash ) ) {
     113           0 :     fd_lthash_value_t lthash;
     114           0 :     fd_memcpy( lthash.bytes, manifest->accounts_lthash, 2048UL );
     115           0 :     fd_bank_lthash_set( bank, lthash );
     116           0 :   } else {
     117           0 :     fd_lthash_value_t lthash = {0};
     118           0 :     fd_bank_lthash_set( bank, lthash );
     119           0 :   }
     120             : 
     121           0 :   fd_blockhashes_t * blockhashes = fd_bank_block_hash_queue_modify( bank );
     122           0 :   blockhashes_recover( blockhashes, manifest->blockhashes, manifest->blockhashes_len, 42UL /* TODO */ );
     123             : 
     124             :   /* PoH */
     125           0 :   fd_blockhashes_t const * bhq = fd_bank_block_hash_queue_query( bank );
     126           0 :   fd_hash_t const * last_hash = fd_blockhashes_peek_last( bhq );
     127           0 :   if( FD_LIKELY( last_hash ) ) fd_bank_poh_set( bank, *last_hash );
     128             : 
     129           0 :   fd_bank_capitalization_set( bank, manifest->capitalization );
     130           0 :   fd_bank_lamports_per_signature_set( bank, manifest->lamports_per_signature );
     131           0 :   fd_bank_prev_lamports_per_signature_set( bank, manifest->lamports_per_signature );
     132           0 :   fd_bank_transaction_count_set( bank, manifest->transaction_count );
     133           0 :   fd_bank_parent_signature_cnt_set( bank, manifest->signature_count );
     134           0 :   fd_bank_tick_height_set( bank, manifest->tick_height );
     135           0 :   fd_bank_max_tick_height_set( bank, manifest->max_tick_height );
     136           0 :   fd_bank_ns_per_slot_set( bank, manifest->ns_per_slot );
     137           0 :   fd_bank_ticks_per_slot_set( bank, manifest->ticks_per_slot );
     138           0 :   fd_bank_genesis_creation_time_set( bank, manifest->creation_time_millis );
     139           0 :   fd_bank_slots_per_year_set( bank, manifest->slots_per_year );
     140           0 :   fd_bank_block_height_set( bank, manifest->block_height );
     141           0 :   fd_bank_execution_fees_set( bank, manifest->collector_fees );
     142           0 :   fd_bank_priority_fees_set( bank, 0UL );
     143             : 
     144             :   /* Update last restart slot
     145             :      https://github.com/solana-labs/solana/blob/30531d7a5b74f914dde53bfbb0bc2144f2ac92bb/runtime/src/bank.rs#L2152
     146             : 
     147             :      old_bank->hard_forks is sorted ascending by slot number.
     148             :      To find the last restart slot, take the highest hard fork slot
     149             :      number that is less or equal than the current slot number.
     150             :      (There might be some hard forks in the future, ignore these)
     151             : 
     152             :      SIMD-0047: The first restart slot should be `0` */
     153           0 :   fd_sol_sysvar_last_restart_slot_t * last_restart_slot = fd_bank_last_restart_slot_modify( bank );
     154           0 :   last_restart_slot->slot = 0UL;
     155           0 :   if( FD_LIKELY( manifest->hard_forks_len ) ) {
     156           0 :     for( ulong i=0UL; i<manifest->hard_forks_len; i++ ) {
     157           0 :       ulong slot = manifest->hard_forks[ manifest->hard_forks_len-1UL-i ];
     158           0 :       if( FD_LIKELY( slot<=manifest->slot ) ) {
     159           0 :         last_restart_slot->slot = slot;
     160           0 :         break;
     161           0 :       }
     162           0 :     }
     163           0 :   }
     164             : 
     165             :   /* Stake delegations for the current epoch. */
     166           0 :   fd_stake_delegations_t * stake_delegations = fd_banks_stake_delegations_root_query( banks );
     167           0 :   for( ulong i=0UL; i<manifest->stake_delegations_len; i++ ) {
     168           0 :     fd_snapshot_manifest_stake_delegation_t const * elem = &manifest->stake_delegations[ i ];
     169           0 :     fd_stake_delegations_update(
     170           0 :         stake_delegations,
     171           0 :         (fd_pubkey_t *)elem->stake_pubkey,
     172           0 :         (fd_pubkey_t *)elem->vote_pubkey,
     173           0 :         elem->stake_delegation,
     174           0 :         elem->activation_epoch,
     175           0 :         elem->deactivation_epoch,
     176           0 :         elem->credits_observed,
     177           0 :         elem->warmup_cooldown_rate
     178           0 :     );
     179           0 :   }
     180             : 
     181             :   /* Vote states for the current epoch. */
     182           0 :   fd_vote_states_t * vote_states = fd_vote_states_join( fd_vote_states_new( fd_bank_vote_states_locking_modify( bank ), FD_RUNTIME_MAX_VOTE_ACCOUNTS, 999UL ) );
     183           0 :   for( ulong i=0UL; i<manifest->vote_accounts_len; i++ ) {
     184           0 :     fd_snapshot_manifest_vote_account_t const * elem = &manifest->vote_accounts[ i ];
     185             :     /* First convert the epoch credits to the format expected by the
     186             :        vote states. */
     187           0 :     ushort epoch_credits_epoch[ EPOCH_CREDITS_MAX ];
     188           0 :     ulong  epoch_credits_credits[ EPOCH_CREDITS_MAX ];
     189           0 :     ulong  epoch_credits_prev_credits[ EPOCH_CREDITS_MAX ];
     190           0 :     for( ulong j=0UL; j<elem->epoch_credits_history_len; j++ ) {
     191           0 :       epoch_credits_epoch[ j ]        = (ushort)elem->epoch_credits[ j ].epoch;
     192           0 :       epoch_credits_credits[ j ]      = elem->epoch_credits[ j ].credits;
     193           0 :       epoch_credits_prev_credits[ j ] = elem->epoch_credits[ j ].prev_credits;
     194           0 :     }
     195             : 
     196           0 :     fd_vote_states_update(
     197           0 :         vote_states,
     198           0 :         (fd_pubkey_t *)elem->vote_account_pubkey,
     199           0 :         (fd_pubkey_t *)elem->node_account_pubkey,
     200           0 :         elem->commission,
     201           0 :         elem->last_timestamp,
     202           0 :         elem->last_slot,
     203           0 :         elem->epoch_credits_history_len,
     204           0 :         epoch_credits_epoch,
     205           0 :         epoch_credits_credits,
     206           0 :         epoch_credits_prev_credits );
     207           0 :     fd_vote_states_update_stake( vote_states, (fd_pubkey_t *)elem->vote_account_pubkey, elem->stake );
     208           0 :   }
     209           0 :   fd_bank_vote_states_end_locking_modify( bank );
     210             : 
     211             :   /* Vote stakes for the previous epoch (E-1). */
     212           0 :   fd_vote_states_t * vote_stakes_prev = fd_vote_states_join( fd_vote_states_new( fd_bank_vote_states_prev_locking_modify( bank ), FD_RUNTIME_MAX_VOTE_ACCOUNTS, 999UL ) );
     213           0 :   for( ulong i=0UL; i<manifest->epoch_stakes[1].vote_stakes_len; i++ ) {
     214           0 :     fd_snapshot_manifest_vote_stakes_t const * elem = &manifest->epoch_stakes[1].vote_stakes[i];
     215             :     /* First convert the epoch credits to the format expected by the
     216             :        vote states. */
     217           0 :     ushort epoch_credits_epoch[ EPOCH_CREDITS_MAX ];
     218           0 :     ulong  epoch_credits_credits[ EPOCH_CREDITS_MAX ];
     219           0 :     ulong  epoch_credits_prev_credits[ EPOCH_CREDITS_MAX ];
     220           0 :     for( ulong j=0UL; j<elem->epoch_credits_history_len; j++ ) {
     221           0 :       epoch_credits_epoch[ j ]        = (ushort)elem->epoch_credits[ j ].epoch;
     222           0 :       epoch_credits_credits[ j ]      = elem->epoch_credits[ j ].credits;
     223           0 :       epoch_credits_prev_credits[ j ] = elem->epoch_credits[ j ].prev_credits;
     224           0 :     }
     225             : 
     226           0 :     fd_vote_states_update(
     227           0 :         vote_stakes_prev,
     228           0 :         (fd_pubkey_t *)elem->vote,
     229           0 :         (fd_pubkey_t *)elem->identity,
     230           0 :         elem->commission,
     231           0 :         elem->timestamp,
     232           0 :         elem->slot,
     233           0 :         elem->epoch_credits_history_len,
     234           0 :         epoch_credits_epoch,
     235           0 :         epoch_credits_credits,
     236           0 :         epoch_credits_prev_credits );
     237           0 :     if( elem->stake ) {
     238           0 :       fd_vote_states_update_stake( vote_stakes_prev, (fd_pubkey_t *)elem->vote, elem->stake );
     239           0 :     } else {
     240           0 :       fd_vote_states_remove( vote_stakes_prev, (fd_pubkey_t *)elem->vote );
     241           0 :     }
     242           0 :   }
     243             : 
     244           0 :   fd_bank_vote_states_prev_end_locking_modify( bank );
     245             : 
     246             :   /* We also want to set the total stake to be the total amout of stake
     247             :      at the end of the previous epoch. This value is used for the
     248             :      get_epoch_stake syscall.
     249             : 
     250             :      FIXME: This needs to be updated at the epoch boundary and this
     251             :      currently does NOT happen.
     252             : 
     253             :      A note on Agave's indexing scheme for their epoch_stakes
     254             :      structure:
     255             : 
     256             :      https://github.com/anza-xyz/agave/blob/v2.2.14/runtime/src/bank.rs#L6175
     257             : 
     258             :      If we are loading a snapshot and replaying in the middle of
     259             :      epoch 7, the syscall is supposed to return the total stake at
     260             :      the end of epoch 6.  The epoch_stakes structure is indexed in
     261             :      Agave by the epoch number of the leader schedule that the
     262             :      stakes are meant to determine.  For instance, to get the
     263             :      stakes at the end of epoch 6, we should query by 8, because
     264             :      the leader schedule for epoch 8 is determined based on the
     265             :      stakes at the end of epoch 6.  Therefore, we save the total
     266             :      epoch stake by querying for epoch+1. This logic is encapsulated
     267             :      in fd_ssmanifest_parser.c. */
     268           0 :   fd_bank_total_epoch_stake_set( bank, manifest->epoch_stakes[1].total_stake );
     269             : 
     270             :   /* Vote stakes for the previous epoch (E-2) */
     271           0 :   fd_vote_states_t * vote_stakes_prev_prev = fd_vote_states_join( fd_vote_states_new( fd_bank_vote_states_prev_prev_locking_modify( bank ), FD_RUNTIME_MAX_VOTE_ACCOUNTS, 999UL ) );
     272           0 :   for( ulong i=0UL; i<manifest->epoch_stakes[0].vote_stakes_len; i++ ) {
     273           0 :     fd_snapshot_manifest_vote_stakes_t const * elem = &manifest->epoch_stakes[0].vote_stakes[i];
     274             :     /* First convert the epoch credits to the format expected by the
     275             :        vote states. */
     276           0 :     ushort epoch_credits_epoch[ EPOCH_CREDITS_MAX ];
     277           0 :     ulong  epoch_credits_credits[ EPOCH_CREDITS_MAX ];
     278           0 :     ulong  epoch_credits_prev_credits[ EPOCH_CREDITS_MAX ];
     279           0 :     for( ulong j=0UL; j<elem->epoch_credits_history_len; j++ ) {
     280           0 :       epoch_credits_epoch[ j ]        = (ushort)elem->epoch_credits[ j ].epoch;
     281           0 :       epoch_credits_credits[ j ]      = elem->epoch_credits[ j ].credits;
     282           0 :       epoch_credits_prev_credits[ j ] = elem->epoch_credits[ j ].prev_credits;
     283           0 :     }
     284           0 :     fd_vote_states_update(
     285           0 :         vote_stakes_prev_prev,
     286           0 :         (fd_pubkey_t *)elem->vote,
     287           0 :         (fd_pubkey_t *)elem->identity,
     288           0 :         elem->commission,
     289           0 :         elem->timestamp,
     290           0 :         elem->slot,
     291           0 :         elem->epoch_credits_history_len,
     292           0 :         epoch_credits_epoch,
     293           0 :         epoch_credits_credits,
     294           0 :         epoch_credits_prev_credits );
     295           0 :     if( elem->stake ) {
     296           0 :       fd_vote_states_update_stake( vote_stakes_prev_prev, (fd_pubkey_t *)elem->vote, elem->stake );
     297           0 :     } else {
     298           0 :       fd_vote_states_remove( vote_stakes_prev_prev, (fd_pubkey_t *)elem->vote );
     299           0 :     }
     300           0 :   }
     301           0 :   fd_bank_vote_states_prev_prev_end_locking_modify( bank );
     302           0 :   bank->txncache_fork_id = (fd_txncache_fork_id_t){ .val = manifest->txncache_fork_id };
     303           0 : }

Generated by: LCOV version 1.14