LCOV - code coverage report
Current view: top level - discof/restore/utils - fd_ssload.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 326 413 78.9 %
Date: 2026-06-29 05:51:35 Functions: 3 5 60.0 %

          Line data    Source code
       1             : #include "fd_ssload.h"
       2             : 
       3             : #include "../../../disco/genesis/fd_genesis_cluster.h"
       4             : #include "../../../flamenco/runtime/fd_runtime_const.h"
       5             : #include "../../../flamenco/runtime/sysvar/fd_sysvar_epoch_schedule.h"
       6             : #include "fd_ssmsg.h"
       7             : 
       8             : FD_STATIC_ASSERT( FD_HARD_FORKS_MAX==sizeof(((fd_snapshot_manifest_t *)0)->hard_forks)/sizeof(fd_hard_fork_t), hard_forks_max );
       9             : FD_STATIC_ASSERT( FD_BLOCKHASHES_MAX==sizeof(((fd_snapshot_manifest_t *)0)->blockhashes)/sizeof(fd_snapshot_manifest_blockhash_t), blockhashes_max );
      10             : FD_STATIC_ASSERT( FD_VOTE_ACCOUNTS_MAX==sizeof(((fd_snapshot_manifest_t *)0)->vote_accounts)/sizeof(fd_snapshot_manifest_vote_account_t), vote_accounts_max );
      11             : FD_STATIC_ASSERT( FD_STAKE_DELEGATIONS_MAX==sizeof(((fd_snapshot_manifest_t *)0)->stake_delegations)/sizeof(fd_snapshot_manifest_stake_delegation_t), stake_delegations_max );
      12             : FD_STATIC_ASSERT( FD_EPOCH_STAKES_LEN==sizeof(((fd_snapshot_manifest_t *)0)->epoch_stakes)/sizeof(fd_snapshot_manifest_epoch_stakes_t), epoch_stakes_len );
      13             : FD_STATIC_ASSERT( FD_EPOCH_VOTE_STAKES_MAX==sizeof(((fd_snapshot_manifest_epoch_stakes_t *)0)->vote_stakes)/sizeof(fd_snapshot_manifest_vote_stakes_t), epoch_vote_stakes_max );
      14             : FD_STATIC_ASSERT( FD_EPOCH_CREDITS_MAX==sizeof(((fd_snapshot_manifest_vote_account_t *)0)->epoch_credits)/sizeof(epoch_credits_t), vote_account_epoch_credits_max );
      15             : FD_STATIC_ASSERT( FD_EPOCH_CREDITS_MAX==sizeof(((fd_snapshot_manifest_vote_stakes_t *)0)->epoch_credits)/sizeof(epoch_credits_t), vote_stakes_epoch_credits_max );
      16             : 
      17             : int
      18             : fd_ssload_manifest_validate( fd_snapshot_manifest_t const * manifest,
      19             :                              ulong                          max_vote_accounts,
      20         162 :                              ulong                          max_stake_accounts ) {
      21             : 
      22         162 :   if( FD_UNLIKELY( max_vote_accounts!=FD_RUNTIME_MAX_VOTE_ACCOUNTS ||
      23         162 :                    max_stake_accounts!=FD_RUNTIME_MAX_STAKE_ACCOUNTS ) ) {
      24           6 :     FD_LOG_WARNING(( "banks capacity mismatch: max_vote_accounts=%lu (expected %lu) max_stake_accounts=%lu (expected %lu)",
      25           6 :                      max_vote_accounts,  FD_RUNTIME_MAX_VOTE_ACCOUNTS,
      26           6 :                      max_stake_accounts, FD_RUNTIME_MAX_STAKE_ACCOUNTS ));
      27           6 :     return -1;
      28           6 :   }
      29             : 
      30             :   /* slots_per_epoch must be at least FD_EPOCH_LEN_MIN, so that
      31             :      fd_slot_to_epoch and related functions produce valid data.
      32             :      This check must come before any epoch computation. */
      33             : 
      34         156 :   if( FD_UNLIKELY( manifest->epoch_schedule_params.slots_per_epoch<FD_EPOCH_LEN_MIN ) ) {
      35           6 :     FD_LOG_WARNING(( "corrupt snapshot: slots_per_epoch %lu below minimum %lu",
      36           6 :                      manifest->epoch_schedule_params.slots_per_epoch, FD_EPOCH_LEN_MIN ));
      37           6 :     return -1;
      38           6 :   }
      39             : 
      40         150 :   if( FD_UNLIKELY( manifest->epoch_schedule_params.warmup>1 ) ) {
      41           3 :     FD_LOG_WARNING(( "corrupt snapshot: warmup %u is not boolean", (uint)manifest->epoch_schedule_params.warmup ));
      42           3 :     return -1;
      43           3 :   }
      44             : 
      45             :   /* Validate that the manifest's first_normal_{epoch,slot} are
      46             :      consistent with the derivation from slots_per_epoch and warmup. */
      47             : 
      48         147 :   fd_epoch_schedule_t derived;
      49         147 :   if( FD_UNLIKELY( !fd_epoch_schedule_derive( &derived,
      50         147 :                                               manifest->epoch_schedule_params.slots_per_epoch,
      51         147 :                                               manifest->epoch_schedule_params.leader_schedule_slot_offset,
      52         147 :                                               manifest->epoch_schedule_params.warmup ) ) ) {
      53           3 :     FD_LOG_WARNING(( "corrupt snapshot: fd_epoch_schedule_derive failed" ));
      54           3 :     return -1;
      55           3 :   }
      56         144 :   if( FD_UNLIKELY( derived.first_normal_epoch!=manifest->epoch_schedule_params.first_normal_epoch ) ) {
      57           9 :     FD_LOG_WARNING(( "corrupt snapshot: first_normal_epoch mismatch (manifest=%lu derived=%lu)",
      58           9 :                      manifest->epoch_schedule_params.first_normal_epoch, derived.first_normal_epoch ));
      59           9 :     return -1;
      60           9 :   }
      61         135 :   if( FD_UNLIKELY( derived.first_normal_slot!=manifest->epoch_schedule_params.first_normal_slot ) ) {
      62           0 :     FD_LOG_WARNING(( "corrupt snapshot: first_normal_slot mismatch (manifest=%lu derived=%lu)",
      63           0 :                      manifest->epoch_schedule_params.first_normal_slot, derived.first_normal_slot ));
      64           0 :     return -1;
      65           0 :   }
      66             : 
      67             :   /* Blockhash queue structural validation */
      68             : 
      69         135 :   ulong const age_cnt = manifest->blockhashes_len;
      70         135 :   fd_snapshot_manifest_blockhash_t const * ages = manifest->blockhashes;
      71             : 
      72         135 :   if( FD_UNLIKELY( !age_cnt || age_cnt>FD_BLOCKHASHES_MAX ) ) {
      73           6 :     FD_LOG_WARNING(( "corrupt snapshot: invalid blockhash age count %lu (max %lu)", age_cnt, FD_BLOCKHASHES_MAX ));
      74           6 :     return -1;
      75           6 :   }
      76             : 
      77         129 :   ulong seq_min = ULONG_MAX;
      78        1185 :   for( ulong i=0UL; i<age_cnt; i++ ) {
      79        1056 :     seq_min = fd_ulong_min( seq_min, ages[ i ].hash_index );
      80        1056 :   }
      81         129 :   ulong seq_max;
      82         129 :   if( FD_UNLIKELY( __builtin_uaddl_overflow( seq_min, age_cnt, &seq_max ) ) ) {
      83           6 :     FD_LOG_WARNING(( "corrupt snapshot: blockhash queue sequence number wraparound (seq_min=%lu age_cnt=%lu)", seq_min, age_cnt ));
      84           6 :     return -1;
      85           6 :   }
      86             : 
      87             :   /* Check for gaps and duplicates using a bitset (max 301 entries). */
      88             : 
      89         123 :   ulong seen[ (FD_BLOCKHASHES_MAX+63UL)/64UL ];
      90         123 :   fd_memset( seen, 0, sizeof(seen) );
      91        1164 :   for( ulong i=0UL; i<age_cnt; i++ ) {
      92        1047 :     ulong idx;
      93        1047 :     if( FD_UNLIKELY( __builtin_usubl_overflow( ages[ i ].hash_index, seq_min, &idx ) || idx>=age_cnt ) ) {
      94           3 :       FD_LOG_WARNING(( "corrupt snapshot: gap in blockhash queue (seq=[%lu,%lu) hash_index=%lu)",
      95           3 :                        seq_min, seq_max, ages[ i ].hash_index ));
      96           3 :       return -1;
      97           3 :     }
      98        1044 :     ulong word = idx/64UL;
      99        1044 :     ulong bit  = idx%64UL;
     100        1044 :     if( FD_UNLIKELY( seen[ word ] & (1UL<<bit) ) ) {
     101           3 :       FD_LOG_WARNING(( "corrupt snapshot: duplicate blockhash queue hash_index=%lu (relative_idx=%lu seq_min=%lu)",
     102           3 :                        ages[ i ].hash_index, idx, seq_min ));
     103           3 :       return -1;
     104           3 :     }
     105        1041 :     seen[ word ] |= (1UL<<bit);
     106        1041 :   }
     107             : 
     108             :   /* Array bounds checks, reject manifests whose counts exceed the
     109             :      fixed-size arrays in fd_snapshot_manifest_t.  Validating here
     110             :      enables early recovery from malformed snapshots. */
     111             : 
     112         117 :   if( FD_UNLIKELY( manifest->hard_fork_cnt>FD_HARD_FORKS_MAX ) ) {
     113           3 :     FD_LOG_WARNING(( "corrupt snapshot: hard_fork_cnt %lu exceeds max %lu",
     114           3 :                      manifest->hard_fork_cnt, FD_HARD_FORKS_MAX ));
     115           3 :     return -1;
     116           3 :   }
     117             : 
     118         114 :   if( FD_UNLIKELY( manifest->stake_delegations_len>FD_STAKE_DELEGATIONS_MAX ) ) {
     119           6 :     FD_LOG_WARNING(( "corrupt snapshot: stake_delegations_len %lu exceeds max %lu",
     120           6 :                      manifest->stake_delegations_len, FD_STAKE_DELEGATIONS_MAX ));
     121           6 :     return -1;
     122           6 :   }
     123             : 
     124         108 :   if( FD_UNLIKELY( manifest->stake_delegations_len>max_stake_accounts ) ) {
     125           0 :     FD_LOG_WARNING(( "corrupt snapshot: stake_delegations_len %lu exceeds max_stake_accounts %lu",
     126           0 :                      manifest->stake_delegations_len, max_stake_accounts ));
     127           0 :     return -1;
     128           0 :   }
     129             : 
     130         108 :   if( FD_UNLIKELY( manifest->vote_accounts_len>FD_VOTE_ACCOUNTS_MAX ) ) {
     131           6 :     FD_LOG_WARNING(( "corrupt snapshot: vote_accounts_len %lu exceeds max %lu",
     132           6 :                      manifest->vote_accounts_len, FD_VOTE_ACCOUNTS_MAX ));
     133           6 :     return -1;
     134           6 :   }
     135             : 
     136         102 :   if( FD_UNLIKELY( manifest->vote_accounts_len>max_vote_accounts ) ) {
     137           0 :     FD_LOG_WARNING(( "corrupt snapshot: vote_accounts_len %lu exceeds max_vote_accounts %lu",
     138           0 :                      manifest->vote_accounts_len, max_vote_accounts ));
     139           0 :     return -1;
     140           0 :   }
     141             : 
     142             :   /* Epoch credits downcasting only happens on epoch_stakes entries,
     143             :      not vote_accounts.  Validated here for consistency. */
     144             : 
     145         117 :   for( ulong i=0UL; i<manifest->vote_accounts_len; i++ ) {
     146          30 :     if( FD_UNLIKELY( manifest->vote_accounts[i].epoch_credits_history_len>FD_EPOCH_CREDITS_MAX ) ) {
     147           3 :       FD_LOG_WARNING(( "corrupt snapshot: vote_accounts[%lu].epoch_credits_history_len %lu exceeds max %lu",
     148           3 :                        i, manifest->vote_accounts[i].epoch_credits_history_len, FD_EPOCH_CREDITS_MAX ));
     149           3 :       return -1;
     150           3 :     }
     151          27 :     ulong ec_base = manifest->vote_accounts[i].epoch_credits_history_len>0UL
     152          27 :                   ? manifest->vote_accounts[i].epoch_credits[0].prev_credits : 0UL;
     153          45 :     for( ulong j=0UL; j<manifest->vote_accounts[i].epoch_credits_history_len; j++ ) {
     154          30 :       epoch_credits_t const * epc = &manifest->vote_accounts[i].epoch_credits[j];
     155          30 :       if( FD_UNLIKELY( epc->epoch>(ulong)USHORT_MAX ) ) {
     156           3 :         FD_LOG_WARNING(( "corrupt snapshot: vote_accounts[%lu].epoch_credits[%lu].epoch %lu exceeds USHORT_MAX",
     157           3 :                          i, j, epc->epoch ));
     158           3 :         return -1;
     159           3 :       }
     160          27 :       if( FD_UNLIKELY( epc->credits<ec_base || epc->credits-ec_base>(ulong)UINT_MAX ) ) {
     161           6 :         FD_LOG_WARNING(( "corrupt snapshot: vote_accounts[%lu].epoch_credits[%lu].credits %lu out of range (base %lu)",
     162           6 :                          i, j, epc->credits, ec_base ));
     163           6 :         return -1;
     164           6 :       }
     165          21 :       if( FD_UNLIKELY( epc->prev_credits<ec_base || epc->prev_credits-ec_base>(ulong)UINT_MAX ) ) {
     166           3 :         FD_LOG_WARNING(( "corrupt snapshot: vote_accounts[%lu].epoch_credits[%lu].prev_credits %lu out of range (base %lu)",
     167           3 :                          i, j, epc->prev_credits, ec_base ));
     168           3 :         return -1;
     169           3 :       }
     170          21 :     }
     171          27 :   }
     172             : 
     173             :   /* Epoch credits downcasting validation */
     174             : 
     175         294 :   for( ulong i=0UL; i<FD_EPOCH_STAKES_LEN; i++ ) {
     176         225 :     if( FD_UNLIKELY( manifest->epoch_stakes[i].vote_stakes_len>FD_EPOCH_VOTE_STAKES_MAX ) ) {
     177           6 :       FD_LOG_WARNING(( "corrupt snapshot: epoch_stakes[%lu].vote_stakes_len %lu exceeds max %lu",
     178           6 :                        i, manifest->epoch_stakes[i].vote_stakes_len, FD_EPOCH_VOTE_STAKES_MAX ));
     179           6 :       return -1;
     180           6 :     }
     181         219 :     if( FD_UNLIKELY( manifest->epoch_stakes[i].vote_stakes_len>max_vote_accounts ) ) {
     182           0 :       FD_LOG_WARNING(( "corrupt snapshot: epoch_stakes[%lu].vote_stakes_len %lu exceeds max_vote_accounts %lu",
     183           0 :                        i, manifest->epoch_stakes[i].vote_stakes_len, max_vote_accounts ));
     184           0 :       return -1;
     185           0 :     }
     186         234 :     for( ulong j=0UL; j<manifest->epoch_stakes[i].vote_stakes_len; j++ ) {
     187          27 :       fd_snapshot_manifest_vote_stakes_t const * vs = &manifest->epoch_stakes[i].vote_stakes[j];
     188          27 :       if( FD_UNLIKELY( vs->epoch_credits_history_len>FD_EPOCH_CREDITS_MAX ) ) {
     189           3 :         FD_LOG_WARNING(( "corrupt snapshot: epoch_stakes[%lu].vote_stakes[%lu].epoch_credits_history_len %lu exceeds max %lu",
     190           3 :                          i, j, vs->epoch_credits_history_len, FD_EPOCH_CREDITS_MAX ));
     191           3 :         return -1;
     192           3 :       }
     193          24 :       ulong ec_base = vs->epoch_credits_history_len>0UL ? vs->epoch_credits[0].prev_credits : 0UL;
     194          39 :       for( ulong k=0UL; k<vs->epoch_credits_history_len; k++ ) {
     195          24 :         epoch_credits_t const * epc = &vs->epoch_credits[k];
     196          24 :         if( FD_UNLIKELY( epc->epoch>(ulong)USHORT_MAX ) ) {
     197           3 :           FD_LOG_WARNING(( "corrupt snapshot: epoch_stakes[%lu].vote_stakes[%lu].epoch_credits[%lu].epoch %lu exceeds USHORT_MAX",
     198           3 :                            i, j, k, epc->epoch ));
     199           3 :           return -1;
     200           3 :         }
     201          21 :         if( FD_UNLIKELY( epc->credits<ec_base || epc->credits-ec_base>(ulong)UINT_MAX ) ) {
     202           3 :           FD_LOG_WARNING(( "corrupt snapshot: epoch_stakes[%lu].vote_stakes[%lu].epoch_credits[%lu].credits %lu out of range (base %lu)",
     203           3 :                            i, j, k, epc->credits, ec_base ));
     204           3 :           return -1;
     205           3 :         }
     206          18 :         if( FD_UNLIKELY( epc->prev_credits<ec_base || epc->prev_credits-ec_base>(ulong)UINT_MAX ) ) {
     207           3 :           FD_LOG_WARNING(( "corrupt snapshot: epoch_stakes[%lu].vote_stakes[%lu].epoch_credits[%lu].prev_credits %lu out of range (base %lu)",
     208           3 :                            i, j, k, epc->prev_credits, ec_base ));
     209           3 :           return -1;
     210           3 :         }
     211          18 :       }
     212          24 :     }
     213         219 :   }
     214             : 
     215             :   /* Epoch stakes index validation.  fd_slot_to_leader_schedule_epoch
     216             :      is inlined here with overflow-safe arithmetic. */
     217             : 
     218          69 :   fd_epoch_schedule_t epoch_schedule = (fd_epoch_schedule_t){
     219          69 :     .slots_per_epoch             = manifest->epoch_schedule_params.slots_per_epoch,
     220          69 :     .leader_schedule_slot_offset = manifest->epoch_schedule_params.leader_schedule_slot_offset,
     221          69 :     .warmup                      = manifest->epoch_schedule_params.warmup,
     222          69 :     .first_normal_epoch          = manifest->epoch_schedule_params.first_normal_epoch,
     223          69 :     .first_normal_slot           = manifest->epoch_schedule_params.first_normal_slot,
     224          69 :   };
     225             : 
     226          69 :   ulong epoch = fd_slot_to_epoch( &epoch_schedule, manifest->slot, NULL );
     227             : 
     228             :   /* Compute leader_schedule_epoch with overflow safety.  Mirrors
     229             :      fd_slot_to_leader_schedule_epoch but rejects overflow instead
     230             :      of silently wrapping. */
     231             : 
     232          69 :   ulong leader_schedule_epoch;
     233          69 :   if( FD_UNLIKELY( manifest->slot<epoch_schedule.first_normal_slot ) ) {
     234           9 :     if( FD_UNLIKELY( __builtin_uaddl_overflow( epoch, 1UL, &leader_schedule_epoch ) ) ) {
     235           0 :       FD_LOG_WARNING(( "corrupt snapshot: leader_schedule_epoch overflow (epoch=%lu)", epoch ));
     236           0 :       return -1;
     237           0 :     }
     238          60 :   } else {
     239          60 :     ulong delta = manifest->slot-epoch_schedule.first_normal_slot;
     240          60 :     ulong sum;
     241          60 :     if( FD_UNLIKELY( __builtin_uaddl_overflow( delta, epoch_schedule.leader_schedule_slot_offset, &sum ) ) ) {
     242           3 :       FD_LOG_WARNING(( "corrupt snapshot: leader_schedule_slot_offset overflow "
     243           3 :                        "(slot_delta=%lu leader_schedule_slot_offset=%lu)",
     244           3 :                        delta, epoch_schedule.leader_schedule_slot_offset ));
     245           3 :       return -1;
     246           3 :     }
     247          57 :     ulong n_epochs = sum/epoch_schedule.slots_per_epoch;
     248          57 :     if( FD_UNLIKELY( __builtin_uaddl_overflow( epoch_schedule.first_normal_epoch, n_epochs, &leader_schedule_epoch ) ) ) {
     249           0 :       FD_LOG_WARNING(( "corrupt snapshot: leader_schedule_epoch overflow "
     250           0 :                        "(first_normal_epoch=%lu n_epochs=%lu)",
     251           0 :                        epoch_schedule.first_normal_epoch, n_epochs ));
     252           0 :       return -1;
     253           0 :     }
     254          57 :   }
     255             : 
     256          66 :   ulong epoch_stakes_base = epoch>0UL ? epoch-1UL : 0UL;
     257             : 
     258          66 :   if( FD_UNLIKELY( leader_schedule_epoch<epoch_stakes_base ) ) {
     259           0 :     FD_LOG_WARNING(( "corrupt snapshot: leader_schedule_epoch %lu < epoch_stakes_base %lu",
     260           0 :                      leader_schedule_epoch, epoch_stakes_base ));
     261           0 :     return -1;
     262           0 :   }
     263          66 :   ulong t_1_idx = leader_schedule_epoch-epoch_stakes_base;
     264          66 :   if( FD_UNLIKELY( t_1_idx>=FD_EPOCH_STAKES_LEN ) ) {
     265           6 :     FD_LOG_WARNING(( "corrupt snapshot: epoch stakes index %lu out of range (max %lu)",
     266           6 :                      t_1_idx, FD_EPOCH_STAKES_LEN ));
     267           6 :     return -1;
     268           6 :   }
     269             : 
     270          60 :   return 0;
     271          66 : }
     272             : 
     273             : static int
     274             : blockhashes_recover( fd_blockhashes_t *                       blockhashes,
     275             :                      fd_snapshot_manifest_blockhash_t const * ages,
     276             :                      ulong                                    age_cnt,
     277           6 :                      ulong                                    seed ) {
     278             : 
     279             :   /* The caller must guarantee that fd_ssload_manifest_validate has
     280             :      already been invoked, verifying that age_cnt is in the range
     281             :      (0, FD_BLOCKHASHES_MAX], that there are no gaps or duplicates in
     282             :      the sequence numbers, and that seq_min+age_cnt does not overflow. */
     283             : 
     284           6 :   if( FD_UNLIKELY( !fd_blockhashes_init( blockhashes, seed ) ) ) {
     285           0 :     FD_LOG_WARNING(( "failed to initialize blockhash queue" ));
     286           0 :     return -1;
     287           0 :   }
     288             : 
     289           6 :   ulong seq_min = ULONG_MAX;
     290          12 :   for( ulong i=0UL; i<age_cnt; i++ ) {
     291           6 :     seq_min = fd_ulong_min( seq_min, ages[ i ].hash_index );
     292           6 :   }
     293             : 
     294             :   /* Reset */
     295             : 
     296          12 :   for( ulong i=0UL; i<age_cnt; i++ ) {
     297           6 :     fd_blockhash_info_t * ele = fd_blockhash_deq_push_tail_nocopy( blockhashes->d.deque );
     298           6 :     fd_memset( ele, 0, sizeof(fd_blockhash_info_t) );
     299           6 :   }
     300             : 
     301             :   /* Load hashes */
     302             : 
     303          12 :   for( ulong i=0UL; i<age_cnt; i++ ) {
     304           6 :     fd_snapshot_manifest_blockhash_t const * elem = &ages[ i ];
     305           6 :     ulong idx = elem->hash_index - seq_min;
     306           6 :     fd_blockhash_info_t * info = &blockhashes->d.deque[ idx ];
     307           6 :     info->exists = 1;
     308           6 :     fd_memcpy( info->hash.uc, elem->hash, 32UL );
     309           6 :     info->lamports_per_signature = elem->lamports_per_signature;
     310           6 :     fd_blockhash_map_idx_insert( blockhashes->map, idx, blockhashes->d.deque );
     311           6 :   }
     312             : 
     313           6 :   return 0;
     314           6 : }
     315             : 
     316             : int
     317             : fd_ssload_recover_validate( fd_snapshot_manifest_t const * manifest,
     318           0 :                             fd_banks_t const *             banks ) {
     319           0 :   return fd_ssload_manifest_validate( manifest, banks->max_vote_accounts, banks->max_stake_accounts );
     320           0 : }
     321             : 
     322             : int
     323             : fd_ssload_recover_apply( fd_snapshot_manifest_t * manifest,
     324             :                          fd_banks_t *             banks,
     325             :                          fd_bank_t *              bank,
     326           6 :                          ulong                    blockhash_seed ) {
     327             : 
     328             :   /* Slot */
     329             : 
     330           6 :   bank->f.slot = manifest->slot;
     331           6 :   bank->f.parent_slot = manifest->parent_slot;
     332             : 
     333             :   /* Bank Hash */
     334             : 
     335           6 :   fd_hash_t hash;
     336           6 :   fd_memcpy( &hash.uc, manifest->bank_hash, 32UL );
     337           6 :   bank->f.bank_hash = hash;
     338             : 
     339           6 :   fd_hash_t parent_hash;
     340           6 :   fd_memcpy( &parent_hash.uc, manifest->parent_bank_hash, 32UL );
     341           6 :   bank->f.prev_bank_hash = parent_hash;
     342             : 
     343           6 :   fd_fee_rate_governor_t * fee_rate_governor = &bank->f.fee_rate_governor;
     344           6 :   fee_rate_governor->target_lamports_per_signature = manifest->fee_rate_governor.target_lamports_per_signature;
     345           6 :   fee_rate_governor->target_signatures_per_slot    = manifest->fee_rate_governor.target_signatures_per_slot;
     346           6 :   fee_rate_governor->min_lamports_per_signature    = manifest->fee_rate_governor.min_lamports_per_signature;
     347           6 :   fee_rate_governor->max_lamports_per_signature    = manifest->fee_rate_governor.max_lamports_per_signature;
     348           6 :   fee_rate_governor->burn_percent                  = manifest->fee_rate_governor.burn_percent;
     349             :   /* https://github.com/anza-xyz/agave/blob/v3.0.3/runtime/src/serde_snapshot.rs#L464-L466 */
     350           6 :   bank->f.rbh_lamports_per_sig = manifest->lamports_per_signature;
     351             : 
     352           6 :   fd_inflation_t * inflation = &bank->f.inflation;
     353           6 :   inflation->initial         = manifest->inflation_params.initial;
     354           6 :   inflation->terminal        = manifest->inflation_params.terminal;
     355           6 :   inflation->taper           = manifest->inflation_params.taper;
     356           6 :   inflation->foundation      = manifest->inflation_params.foundation;
     357           6 :   inflation->foundation_term = manifest->inflation_params.foundation_term;
     358           6 :   inflation->unused          = 0.0;
     359             : 
     360           6 :   fd_epoch_schedule_t * epoch_schedule = &bank->f.epoch_schedule;
     361           6 :   epoch_schedule->slots_per_epoch             = manifest->epoch_schedule_params.slots_per_epoch;
     362           6 :   epoch_schedule->leader_schedule_slot_offset = manifest->epoch_schedule_params.leader_schedule_slot_offset;
     363           6 :   epoch_schedule->warmup                      = manifest->epoch_schedule_params.warmup;
     364           6 :   epoch_schedule->first_normal_epoch          = manifest->epoch_schedule_params.first_normal_epoch;
     365           6 :   epoch_schedule->first_normal_slot           = manifest->epoch_schedule_params.first_normal_slot;
     366             : 
     367           6 :   ulong epoch = fd_slot_to_epoch( epoch_schedule, manifest->slot, NULL );
     368           6 :   bank->f.epoch = epoch;
     369             : 
     370           6 :   fd_rent_t * rent = &bank->f.rent;
     371           6 :   rent->lamports_per_uint8_year = manifest->rent_params.lamports_per_uint8_year;
     372           6 :   rent->exemption_threshold     = manifest->rent_params.exemption_threshold;
     373           6 :   rent->burn_percent            = manifest->rent_params.burn_percent;
     374             : 
     375             :   /* https://github.com/anza-xyz/agave/blob/v3.0.6/ledger/src/blockstore_processor.rs#L1118
     376             :      None gets treated as 0 for hash verification. */
     377           6 :   if( FD_LIKELY( manifest->has_hashes_per_tick ) ) bank->f.hashes_per_tick = manifest->hashes_per_tick;
     378           6 :   else                                             bank->f.hashes_per_tick = 0UL;
     379             : 
     380           6 :   fd_lthash_value_t * lthash = fd_bank_lthash_locking_modify( bank );
     381           6 :   if( FD_LIKELY( manifest->has_accounts_lthash ) ) {
     382           0 :     fd_memcpy( lthash, manifest->accounts_lthash, sizeof(fd_lthash_value_t) );
     383           6 :   } else {
     384           6 :     fd_memset( lthash, 0, sizeof(fd_lthash_value_t) );
     385           6 :   }
     386           6 :   fd_bank_lthash_end_locking_modify( bank );
     387             : 
     388           6 :   fd_blockhashes_t * blockhashes = &bank->f.block_hash_queue;
     389           6 :   if( FD_UNLIKELY( blockhashes_recover( blockhashes, manifest->blockhashes, manifest->blockhashes_len, blockhash_seed ) ) ) {
     390           0 :     FD_LOG_WARNING(( "blockhash queue recovery failed" ));
     391           0 :     return -1;
     392           0 :   }
     393             : 
     394             :   /* PoH */
     395           6 :   fd_blockhashes_t const * bhq = &bank->f.block_hash_queue;
     396           6 :   fd_hash_t const * last_hash = fd_blockhashes_peek_last_hash( bhq );
     397           6 :   if( FD_LIKELY( last_hash ) ) bank->f.poh = *last_hash;
     398             : 
     399           6 :   bank->f.capitalization = manifest->capitalization;
     400           6 :   bank->f.txn_count = manifest->transaction_count;
     401           6 :   bank->f.signature_count = manifest->signature_count;
     402           6 :   bank->f.tick_height = manifest->tick_height;
     403           6 :   bank->f.max_tick_height = manifest->max_tick_height;
     404           6 :   bank->f.ns_per_slot = (fd_w_u128_t) { .ul={ manifest->ns_per_slot, 0UL } };
     405           6 :   bank->f.ticks_per_slot = manifest->ticks_per_slot;
     406           6 :   bank->f.genesis_creation_time = manifest->creation_time_seconds;
     407           6 :   bank->f.slots_per_year = manifest->slots_per_year;
     408           6 :   bank->f.block_height = manifest->block_height;
     409           6 :   bank->f.execution_fees = manifest->collector_fees;
     410           6 :   bank->f.priority_fees = 0UL;
     411             : 
     412             :   /* Set the cluster type based on the genesis creation time.  This is
     413             :      later cross referenced against the genesis hash. */
     414           6 :   switch( bank->f.genesis_creation_time ) {
     415           0 :     case FD_RUNTIME_GENESIS_CREATION_TIME_TESTNET:
     416           0 :       bank->f.cluster_type = FD_CLUSTER_TESTNET;
     417           0 :       break;
     418           0 :     case FD_RUNTIME_GENESIS_CREATION_TIME_MAINNET:
     419           0 :       bank->f.cluster_type = FD_CLUSTER_MAINNET_BETA;
     420           0 :       break;
     421           0 :     case FD_RUNTIME_GENESIS_CREATION_TIME_DEVNET:
     422           0 :       bank->f.cluster_type = FD_CLUSTER_DEVNET;
     423           0 :       break;
     424           6 :     default:
     425           6 :       bank->f.cluster_type = FD_CLUSTER_UNKNOWN;
     426           6 :   }
     427             : 
     428             :   /* Update last restart slot
     429             :      https://github.com/solana-labs/solana/blob/30531d7a5b74f914dde53bfbb0bc2144f2ac92bb/runtime/src/bank.rs#L2152
     430             : 
     431             :      old_bank->hard_forks is sorted ascending by slot number.
     432             :      To find the last restart slot, take the highest hard fork slot
     433             :      number that is less or equal than the current slot number.
     434             :      (There might be some hard forks in the future, ignore these)
     435             : 
     436             :      SIMD-0047: The first restart slot should be `0` */
     437           6 :   bank->f.hard_fork_cnt = manifest->hard_fork_cnt;
     438           6 :   if( FD_LIKELY( manifest->hard_fork_cnt ) ) {
     439           0 :     for( ulong i=0UL; i<manifest->hard_fork_cnt; i++ ) {
     440           0 :       bank->f.hard_forks[ i ] = manifest->hard_forks[ i ];
     441           0 :     }
     442             : 
     443           0 :     for( ulong i=0UL; i<manifest->hard_fork_cnt; i++ ) {
     444           0 :       ulong slot = manifest->hard_forks[ manifest->hard_fork_cnt-1UL-i ].slot;
     445           0 :       if( FD_LIKELY( slot<=manifest->slot ) ) {
     446           0 :         break;
     447           0 :       }
     448           0 :     }
     449           0 :   }
     450             : 
     451             :   /* Stake delegations for the current epoch. */
     452           6 :   fd_stake_delegations_t * stake_delegations = fd_banks_stake_delegations_root_query( banks );
     453           6 :   fd_stake_delegations_reset( stake_delegations );
     454          12 :   for( ulong i=0UL; i<manifest->stake_delegations_len; i++ ) {
     455           6 :     fd_snapshot_manifest_stake_delegation_t const * elem = &manifest->stake_delegations[ i ];
     456           6 :     if( FD_UNLIKELY( elem->stake_delegation==0UL ) ) {
     457           0 :       continue;
     458           0 :     }
     459           6 :     fd_stake_delegations_root_update(
     460           6 :         stake_delegations,
     461           6 :         (fd_pubkey_t *)elem->stake_pubkey,
     462           6 :         (fd_pubkey_t *)elem->vote_pubkey,
     463           6 :         elem->stake_delegation,
     464           6 :         elem->activation_epoch,
     465           6 :         elem->deactivation_epoch,
     466           6 :         elem->credits_observed,
     467           6 :         FD_STAKE_DELEGATIONS_WARMUP_COOLDOWN_RATE_ENUM_025
     468           6 :     );
     469           6 :   }
     470             : 
     471           6 :   fd_new_votes_t * new_votes = fd_bank_new_votes( bank );
     472           6 :   fd_new_votes_reset_root( new_votes );
     473          12 :   for( ulong i=0UL; i<manifest->vote_accounts_len; i++ ) {
     474           6 :     fd_snapshot_manifest_vote_account_t const * elem = &manifest->vote_accounts[ i ];
     475           6 :     if( FD_UNLIKELY( elem->stake==0UL ) ) fd_new_votes_root_insert( new_votes, (fd_pubkey_t *)elem->vote_account_pubkey );
     476           6 :   }
     477             : 
     478             :   /* We also want to set the total stake to be the total amount of stake
     479             :      at the end of the previous epoch. This value is used for the
     480             :      get_epoch_stake syscall.
     481             : 
     482             :      A note on Agave's indexing scheme for their epoch_stakes
     483             :      structure:
     484             : 
     485             :      https://github.com/anza-xyz/agave/blob/v2.2.14/runtime/src/bank.rs#L6175
     486             : 
     487             :      If we are loading a snapshot and replaying in the middle of
     488             :      epoch 7, the syscall is supposed to return the total stake at
     489             :      the end of epoch 6.  The epoch_stakes structure is indexed in
     490             :      Agave by the epoch number of the leader schedule that the
     491             :      stakes are meant to determine.  For instance, to get the
     492             :      stakes at the end of epoch 6, we should query by 8, because
     493             :      the leader schedule for epoch 8 is determined based on the
     494             :      stakes at the end of epoch 6.  Therefore, we save the total
     495             :      epoch stake by querying for epoch+1.  This logic is encapsulated
     496             :      in fd_ssmanifest_parser.c. */
     497             : 
     498           6 :   fd_vote_stakes_t * vote_stakes = fd_bank_vote_stakes( bank );
     499           6 :   fd_vote_stakes_reset( vote_stakes );
     500             : 
     501           6 :   fd_top_votes_t * top_votes_t_1 = fd_bank_top_votes_t_1_modify( bank );
     502           6 :   fd_top_votes_t * top_votes_t_2 = fd_bank_top_votes_t_2_modify( bank );
     503           6 :   fd_top_votes_init( top_votes_t_1 );
     504           6 :   fd_top_votes_init( top_votes_t_2 );
     505             : 
     506           6 :   ulong leader_schedule_epoch = fd_slot_to_leader_schedule_epoch( epoch_schedule, manifest->slot );
     507           6 :   ulong epoch_stakes_base     = epoch > 0UL ? epoch - 1UL : 0UL;
     508           6 :   ulong t_1_idx = leader_schedule_epoch - epoch_stakes_base;
     509             : 
     510           6 :   int   has_t_2 = (t_1_idx > 0UL);
     511           6 :   ulong t_2_idx = has_t_2 ? t_1_idx - 1UL : 0UL;
     512             : 
     513           6 :   bank->f.total_epoch_stake = manifest->epoch_stakes[t_1_idx].total_stake;
     514             : 
     515           6 :   ulong epoch_credits_len = 0UL;
     516             : 
     517             :   /* Populate the vote stakes for the end of the T-1 epoch if the
     518             :      snapshot is in epoch T. */
     519          12 :   for( ulong i=0UL; i<manifest->epoch_stakes[t_1_idx].vote_stakes_len; i++ ) {
     520           6 :     fd_snapshot_manifest_vote_stakes_t const * elem = &manifest->epoch_stakes[t_1_idx].vote_stakes[i];
     521             : 
     522           6 :     fd_vote_stakes_root_insert_key(
     523           6 :         vote_stakes,
     524           6 :         (fd_pubkey_t *)elem->vote,
     525           6 :         (fd_pubkey_t *)elem->identity,
     526           6 :         elem->stake,
     527           6 :         elem->commission,
     528           6 :         bank->f.epoch );
     529             : 
     530           6 :     fd_top_votes_insert( top_votes_t_1, (fd_pubkey_t *)elem->vote, (fd_pubkey_t *)elem->identity, elem->stake, elem->commission );
     531             : 
     532           6 :     fd_epoch_credits_t * ec = &fd_bank_epoch_credits( bank )[epoch_credits_len];
     533           6 :     fd_memcpy( ec->pubkey, elem->vote, 32UL );
     534           6 :     ec->cnt          = elem->epoch_credits_history_len;
     535           6 :     ec->base_credits = ec->cnt > 0UL ? elem->epoch_credits[0].prev_credits : 0UL;
     536           6 :     for( ulong j=0UL; j<elem->epoch_credits_history_len; j++ ) {
     537           0 :       ec->epoch[ j ]              = (ushort)elem->epoch_credits[ j ].epoch;
     538           0 :       ec->credits_delta[ j ]      = (uint)( elem->epoch_credits[ j ].credits      - ec->base_credits );
     539           0 :       ec->prev_credits_delta[ j ] = (uint)( elem->epoch_credits[ j ].prev_credits - ec->base_credits );
     540           0 :     }
     541           6 :     epoch_credits_len++;
     542           6 :   }
     543           6 :   *fd_bank_epoch_credits_len( bank ) = epoch_credits_len;
     544             : 
     545             :   /* Populate the vote stakes for the end of the T-2 epoch if the
     546             :      snapshot is in epoch T. */
     547           6 :   if( has_t_2 ) {
     548           6 :     for( ulong i=0UL; i<manifest->epoch_stakes[t_2_idx].vote_stakes_len; i++ ) {
     549           0 :       fd_snapshot_manifest_vote_stakes_t const * elem = &manifest->epoch_stakes[t_2_idx].vote_stakes[i];
     550             : 
     551           0 :       fd_top_votes_insert( top_votes_t_2, (fd_pubkey_t *)elem->vote, (fd_pubkey_t *)elem->identity, elem->stake, elem->commission );
     552           0 :       fd_vote_stakes_root_update_meta(
     553           0 :           vote_stakes,
     554           0 :           (fd_pubkey_t *)elem->vote,
     555           0 :           (fd_pubkey_t *)elem->identity,
     556           0 :           elem->stake,
     557           0 :           elem->commission,
     558           0 :           bank->f.epoch );
     559           0 :     }
     560           6 :   }
     561             : 
     562             :   /* Store commissions in the banks for the end of the T-3 epoch if the
     563             :      snapshot is in epoch T. */
     564           6 :   if( manifest->epoch_stakes[0].vote_stakes_len > 0UL ) {
     565           0 :     *fd_bank_snapshot_commission_t_3_len( bank ) = manifest->epoch_stakes[0].vote_stakes_len;
     566           0 :     fd_stashed_commission_t * snapshot_commission = fd_bank_snapshot_commission_t_3( bank );
     567           0 :     for( ulong i=0UL; i<manifest->epoch_stakes[0].vote_stakes_len; i++ ) {
     568           0 :       fd_snapshot_manifest_vote_stakes_t const * elem = &manifest->epoch_stakes[0].vote_stakes[i];
     569           0 :       fd_memcpy( snapshot_commission[i].pubkey, elem->vote, 32UL );
     570           0 :       snapshot_commission[i].commission = elem->commission;
     571           0 :     }
     572           6 :   } else {
     573           6 :     *fd_bank_snapshot_commission_t_3_len( bank ) = 0UL;
     574           6 :   }
     575             : 
     576           6 :   bank->txncache_fork_id = (fd_txncache_fork_id_t){ .val = manifest->txncache_fork_id };
     577             : 
     578           6 :   return 0;
     579           6 : }
     580             : 
     581             : int
     582             : fd_ssload_recover( fd_snapshot_manifest_t * manifest,
     583             :                    fd_banks_t *             banks,
     584             :                    fd_bank_t *              bank,
     585           0 :                    ulong                    blockhash_seed ) {
     586             : 
     587           0 :   if( FD_UNLIKELY( fd_ssload_recover_validate( manifest, banks ) ) ) {
     588           0 :     FD_LOG_WARNING(( "snapshot manifest validation failed" ));
     589           0 :     return -1;
     590           0 :   }
     591             : 
     592           0 :   return fd_ssload_recover_apply( manifest, banks, bank, blockhash_seed );
     593           0 : }

Generated by: LCOV version 1.14