LCOV - code coverage report
Current view: top level - discof/restore/utils - fd_ssload.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 367 0.0 %
Date: 2026-11-11 12:40:43 Functions: 0 5 0.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_RUNTIME_MANIFEST_EPOCH_STAKES_LEN==sizeof(((fd_snapshot_manifest_t *)0)->epoch_stakes)/sizeof(fd_snapshot_manifest_epoch_stakes_t), epoch_stakes_len );
      11             : FD_STATIC_ASSERT( FD_RUNTIME_MAX_VAT_VOTE_ACCOUNTS==sizeof(((fd_snapshot_manifest_epoch_stakes_t *)0)->vote_stakes)/sizeof(fd_snapshot_manifest_vote_stakes_t), epoch_vote_stakes_max );
      12             : 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 );
      13             : 
      14             : int
      15             : fd_ssload_manifest_validate( fd_snapshot_manifest_t const * manifest,
      16             :                              ulong                          max_vote_accounts,
      17           0 :                              ulong                          max_stake_accounts ) {
      18             : 
      19           0 :   if( FD_UNLIKELY( max_vote_accounts!=FD_RUNTIME_MAX_VAT_VOTE_ACCOUNTS ||
      20           0 :                    !max_stake_accounts ) ) {
      21           0 :     FD_LOG_WARNING(( "banks capacity mismatch: max_vote_accounts=%lu (expected %lu) max_stake_accounts=%lu (expected nonzero)",
      22           0 :                      max_vote_accounts,  FD_RUNTIME_MAX_VAT_VOTE_ACCOUNTS,
      23           0 :                      max_stake_accounts ));
      24           0 :     return -1;
      25           0 :   }
      26             : 
      27           0 :   if( FD_UNLIKELY( !manifest->ticks_per_slot ) ) {
      28           0 :     FD_LOG_WARNING(( "corrupt snapshot: ticks_per_slot must be non-zero" ));
      29           0 :     return -1;
      30           0 :   }
      31             : 
      32             :   /* slots_per_epoch must be at least FD_EPOCH_LEN_MIN, so that
      33             :      fd_slot_to_epoch and related functions produce valid data.
      34             :      This check must come before any epoch computation. */
      35             : 
      36           0 :   if( FD_UNLIKELY( manifest->epoch_schedule_params.slots_per_epoch<FD_EPOCH_LEN_MIN ) ) {
      37           0 :     FD_LOG_WARNING(( "corrupt snapshot: slots_per_epoch %lu below minimum %lu",
      38           0 :                      manifest->epoch_schedule_params.slots_per_epoch, FD_EPOCH_LEN_MIN ));
      39           0 :     return -1;
      40           0 :   }
      41             : 
      42           0 :   if( FD_UNLIKELY( manifest->epoch_schedule_params.slots_per_epoch>FD_RUNTIME_SLOTS_PER_EPOCH ) ) {
      43           0 :     FD_LOG_WARNING(( "unsupported snapshot: slots_per_epoch %lu above maximum %lu",
      44           0 :                      manifest->epoch_schedule_params.slots_per_epoch, FD_RUNTIME_SLOTS_PER_EPOCH ));
      45           0 :     return -1;
      46           0 :   }
      47             : 
      48           0 :   if( FD_UNLIKELY( manifest->epoch_schedule_params.warmup>1 ) ) {
      49           0 :     FD_LOG_WARNING(( "corrupt snapshot: warmup %u is not boolean", (uint)manifest->epoch_schedule_params.warmup ));
      50           0 :     return -1;
      51           0 :   }
      52             : 
      53             :   /* Validate that the manifest's first_normal_{epoch,slot} are
      54             :      consistent with the derivation from slots_per_epoch and warmup. */
      55             : 
      56           0 :   fd_epoch_schedule_t derived;
      57           0 :   if( FD_UNLIKELY( !fd_epoch_schedule_derive( &derived,
      58           0 :                                               manifest->epoch_schedule_params.slots_per_epoch,
      59           0 :                                               manifest->epoch_schedule_params.leader_schedule_slot_offset,
      60           0 :                                               manifest->epoch_schedule_params.warmup ) ) ) {
      61           0 :     FD_LOG_WARNING(( "corrupt snapshot: fd_epoch_schedule_derive failed" ));
      62           0 :     return -1;
      63           0 :   }
      64           0 :   if( FD_UNLIKELY( derived.first_normal_epoch!=manifest->epoch_schedule_params.first_normal_epoch ) ) {
      65           0 :     FD_LOG_WARNING(( "corrupt snapshot: first_normal_epoch mismatch (manifest=%lu derived=%lu)",
      66           0 :                      manifest->epoch_schedule_params.first_normal_epoch, derived.first_normal_epoch ));
      67           0 :     return -1;
      68           0 :   }
      69           0 :   if( FD_UNLIKELY( derived.first_normal_slot!=manifest->epoch_schedule_params.first_normal_slot ) ) {
      70           0 :     FD_LOG_WARNING(( "corrupt snapshot: first_normal_slot mismatch (manifest=%lu derived=%lu)",
      71           0 :                      manifest->epoch_schedule_params.first_normal_slot, derived.first_normal_slot ));
      72           0 :     return -1;
      73           0 :   }
      74             : 
      75             :   /* Blockhash queue structural validation */
      76             : 
      77           0 :   ulong const age_cnt = manifest->blockhashes_len;
      78           0 :   fd_snapshot_manifest_blockhash_t const * ages = manifest->blockhashes;
      79             : 
      80           0 :   if( FD_UNLIKELY( !age_cnt || age_cnt>FD_BLOCKHASHES_MAX ) ) {
      81           0 :     FD_LOG_WARNING(( "corrupt snapshot: invalid blockhash age count %lu (max %lu)", age_cnt, FD_BLOCKHASHES_MAX ));
      82           0 :     return -1;
      83           0 :   }
      84             : 
      85             :   /* hash_index values may skip (see fd_blockhashes.h), so the only
      86             :      structural requirements are that no two entries share a hash_index
      87             :      and that the newest index leaves room for the runtime to register
      88             :      the next one. */
      89             : 
      90           0 :   for( ulong i=0UL; i<age_cnt; i++ ) {
      91           0 :     if( FD_UNLIKELY( ages[ i ].hash_index==ULONG_MAX ) ) {
      92           0 :       FD_LOG_WARNING(( "corrupt snapshot: blockhash queue sequence number wraparound (hash_index=%lu)", ages[ i ].hash_index ));
      93           0 :       return -1;
      94           0 :     }
      95           0 :     for( ulong j=0UL; j<i; j++ ) {
      96           0 :       if( FD_UNLIKELY( ages[ i ].hash_index==ages[ j ].hash_index ) ) {
      97           0 :         FD_LOG_WARNING(( "corrupt snapshot: duplicate blockhash queue hash_index=%lu", ages[ i ].hash_index ));
      98           0 :         return -1;
      99           0 :       }
     100           0 :     }
     101           0 :   }
     102             : 
     103             :   /* Array bounds checks, reject manifests whose counts exceed the
     104             :      fixed-size arrays in fd_snapshot_manifest_t.  Validating here
     105             :      enables early recovery from malformed snapshots. */
     106             : 
     107           0 :   if( FD_UNLIKELY( manifest->hard_fork_cnt>FD_HARD_FORKS_MAX ) ) {
     108           0 :     FD_LOG_WARNING(( "corrupt snapshot: hard_fork_cnt %lu exceeds max %lu",
     109           0 :                      manifest->hard_fork_cnt, FD_HARD_FORKS_MAX ));
     110           0 :     return -1;
     111           0 :   }
     112             : 
     113             :   /* Epoch credits downcasting validation */
     114             : 
     115           0 :   for( ulong i=0UL; i<FD_RUNTIME_MANIFEST_EPOCH_STAKES_LEN; i++ ) {
     116           0 :     if( FD_UNLIKELY( manifest->epoch_stakes[i].vote_stakes_len>FD_RUNTIME_MAX_VAT_VOTE_ACCOUNTS ) ) {
     117           0 :       FD_LOG_WARNING(( "corrupt snapshot: epoch_stakes[%lu].vote_stakes_len %lu exceeds max %lu",
     118           0 :                        i, manifest->epoch_stakes[i].vote_stakes_len, FD_RUNTIME_MAX_VAT_VOTE_ACCOUNTS ));
     119           0 :       return -1;
     120           0 :     }
     121           0 :     for( ulong j=0UL; j<manifest->epoch_stakes[i].vote_stakes_len; j++ ) {
     122           0 :       fd_snapshot_manifest_vote_stakes_t const * vs = &manifest->epoch_stakes[i].vote_stakes[j];
     123           0 :       if( FD_UNLIKELY( vs->epoch_credits_history_len>FD_EPOCH_CREDITS_MAX ) ) {
     124           0 :         FD_LOG_WARNING(( "corrupt snapshot: epoch_stakes[%lu].vote_stakes[%lu].epoch_credits_history_len %lu exceeds max %lu",
     125           0 :                          i, j, vs->epoch_credits_history_len, FD_EPOCH_CREDITS_MAX ));
     126           0 :         return -1;
     127           0 :       }
     128             :       /* The Alpenglow migration marker is a special credits record that
     129             :          separates pre-Alpenglow and post-Alpenglow credits.  Note
     130             :          Alpenglow is for a slot, so the credit epoch before and after
     131             :          can be the same.  Validators who earned credits before and
     132             :          after will have two entries for the same epoch. */
     133             : 
     134           0 :       ulong ec_base     = 0UL;
     135           0 :       ulong prev        = ULONG_MAX; /* index of the previous non-marker entry */
     136           0 :       int   marker_seen = 0;
     137             : 
     138           0 :       for( ulong k=0UL; k<vs->epoch_credits_history_len; k++ ) {
     139           0 :         epoch_credits_t const * epc = &vs->epoch_credits[k];
     140           0 :         if( FD_UNLIKELY( fd_epoch_credits_is_alpenglow_marker( epc ) ) ) {
     141           0 :           if( FD_UNLIKELY( marker_seen ) ) {
     142           0 :             FD_LOG_WARNING(( "corrupt snapshot: epoch_stakes[%lu].vote_stakes[%lu] has more than one alpenglow migration marker", i, j ));
     143           0 :             return -1;
     144           0 :           }
     145           0 :           marker_seen = 1;
     146           0 :           continue;
     147           0 :         }
     148           0 :         if( FD_UNLIKELY( prev==ULONG_MAX ) ) ec_base = epc->prev_credits;
     149           0 :         if( FD_UNLIKELY( epc->prev_credits>epc->credits ) ) {
     150           0 :           FD_LOG_WARNING(( "corrupt snapshot: epoch_stakes[%lu].vote_stakes[%lu].epoch_credits[%lu].prev_credits %lu exceeds credits %lu",
     151           0 :                            i, j, k, epc->prev_credits, epc->credits ));
     152           0 :           return -1;
     153           0 :         }
     154           0 :         if( FD_UNLIKELY( prev!=ULONG_MAX && epc->epoch<vs->epoch_credits[prev].epoch ) ) {
     155           0 :           FD_LOG_WARNING(( "corrupt snapshot: epoch_stakes[%lu].vote_stakes[%lu].epoch_credits[%lu].epoch %lu is less than previous epoch %lu",
     156           0 :                            i, j, k, epc->epoch, vs->epoch_credits[prev].epoch ));
     157           0 :           return -1;
     158           0 :         }
     159           0 :         if( FD_UNLIKELY( prev!=ULONG_MAX && epc->prev_credits!=vs->epoch_credits[prev].credits ) ) {
     160           0 :           FD_LOG_WARNING(( "corrupt snapshot: epoch_stakes[%lu].vote_stakes[%lu].epoch_credits[%lu].prev_credits %lu does not equal previous credits %lu",
     161           0 :                            i, j, k, epc->prev_credits, vs->epoch_credits[prev].credits ));
     162           0 :           return -1;
     163           0 :         }
     164           0 :         if( FD_UNLIKELY( epc->epoch>(ulong)USHORT_MAX ) ) {
     165           0 :           FD_LOG_WARNING(( "corrupt snapshot: epoch_stakes[%lu].vote_stakes[%lu].epoch_credits[%lu].epoch %lu exceeds USHORT_MAX",
     166           0 :                            i, j, k, epc->epoch ));
     167           0 :           return -1;
     168           0 :         }
     169           0 :         if( FD_UNLIKELY( epc->credits<ec_base ) ) {
     170           0 :           FD_LOG_WARNING(( "corrupt snapshot: epoch_stakes[%lu].vote_stakes[%lu].epoch_credits[%lu].credits %lu is below base %lu",
     171           0 :                            i, j, k, epc->credits, ec_base ));
     172           0 :           return -1;
     173           0 :         }
     174           0 :         prev = k;
     175           0 :       }
     176           0 :     }
     177           0 :   }
     178             : 
     179             :   /* Epoch stakes index validation.  fd_slot_to_leader_schedule_epoch
     180             :      is inlined here with overflow-safe arithmetic. */
     181             : 
     182           0 :   fd_epoch_schedule_t epoch_schedule = (fd_epoch_schedule_t){
     183           0 :     .slots_per_epoch             = manifest->epoch_schedule_params.slots_per_epoch,
     184           0 :     .leader_schedule_slot_offset = manifest->epoch_schedule_params.leader_schedule_slot_offset,
     185           0 :     .warmup                      = manifest->epoch_schedule_params.warmup,
     186           0 :     .first_normal_epoch          = manifest->epoch_schedule_params.first_normal_epoch,
     187           0 :     .first_normal_slot           = manifest->epoch_schedule_params.first_normal_slot,
     188           0 :   };
     189             : 
     190           0 :   ulong epoch = fd_slot_to_epoch( &epoch_schedule, manifest->slot, NULL );
     191             : 
     192             :   /* Compute leader_schedule_epoch with overflow safety.  Mirrors
     193             :      fd_slot_to_leader_schedule_epoch but rejects overflow instead
     194             :      of silently wrapping. */
     195             : 
     196           0 :   ulong leader_schedule_epoch;
     197           0 :   if( FD_UNLIKELY( manifest->slot<epoch_schedule.first_normal_slot ) ) {
     198           0 :     if( FD_UNLIKELY( __builtin_uaddl_overflow( epoch, 1UL, &leader_schedule_epoch ) ) ) {
     199           0 :       FD_LOG_WARNING(( "corrupt snapshot: leader_schedule_epoch overflow (epoch=%lu)", epoch ));
     200           0 :       return -1;
     201           0 :     }
     202           0 :   } else {
     203           0 :     ulong delta = manifest->slot-epoch_schedule.first_normal_slot;
     204           0 :     ulong sum;
     205           0 :     if( FD_UNLIKELY( __builtin_uaddl_overflow( delta, epoch_schedule.leader_schedule_slot_offset, &sum ) ) ) {
     206           0 :       FD_LOG_WARNING(( "corrupt snapshot: leader_schedule_slot_offset overflow "
     207           0 :                        "(slot_delta=%lu leader_schedule_slot_offset=%lu)",
     208           0 :                        delta, epoch_schedule.leader_schedule_slot_offset ));
     209           0 :       return -1;
     210           0 :     }
     211           0 :     ulong n_epochs = sum/epoch_schedule.slots_per_epoch;
     212           0 :     if( FD_UNLIKELY( __builtin_uaddl_overflow( epoch_schedule.first_normal_epoch, n_epochs, &leader_schedule_epoch ) ) ) {
     213           0 :       FD_LOG_WARNING(( "corrupt snapshot: leader_schedule_epoch overflow "
     214           0 :                        "(first_normal_epoch=%lu n_epochs=%lu)",
     215           0 :                        epoch_schedule.first_normal_epoch, n_epochs ));
     216           0 :       return -1;
     217           0 :     }
     218           0 :   }
     219             : 
     220           0 :   ulong epoch_stakes_base = epoch>3UL ? epoch-3UL : 0UL;
     221             : 
     222           0 :   if( FD_UNLIKELY( leader_schedule_epoch<epoch_stakes_base ) ) {
     223           0 :     FD_LOG_WARNING(( "corrupt snapshot: leader_schedule_epoch %lu < epoch_stakes_base %lu",
     224           0 :                      leader_schedule_epoch, epoch_stakes_base ));
     225           0 :     return -1;
     226           0 :   }
     227           0 :   ulong t_1_idx = leader_schedule_epoch-epoch_stakes_base;
     228           0 :   if( FD_UNLIKELY( t_1_idx>=FD_RUNTIME_MANIFEST_EPOCH_STAKES_LEN ) ) {
     229           0 :     FD_LOG_WARNING(( "corrupt snapshot: epoch stakes index %lu out of range (max %lu)",
     230           0 :                      t_1_idx, FD_RUNTIME_MANIFEST_EPOCH_STAKES_LEN ));
     231           0 :     return -1;
     232           0 :   }
     233             : 
     234           0 :   if( FD_UNLIKELY( manifest->epoch_stakes[t_1_idx].vote_stakes_len>max_vote_accounts ) ) {
     235           0 :     FD_LOG_WARNING(( "corrupt snapshot: T-1 epoch stakes length %lu exceeds max_vote_accounts %lu",
     236           0 :                      manifest->epoch_stakes[t_1_idx].vote_stakes_len, max_vote_accounts ));
     237           0 :     return -1;
     238           0 :   }
     239             : 
     240           0 :   ulong t_2_idx = epoch-epoch_stakes_base;
     241           0 :   if( FD_UNLIKELY( manifest->epoch_stakes[t_2_idx].vote_stakes_len>max_vote_accounts ) ) {
     242           0 :     FD_LOG_WARNING(( "corrupt snapshot: T-2 epoch stakes length %lu exceeds max_vote_accounts %lu",
     243           0 :                      manifest->epoch_stakes[t_2_idx].vote_stakes_len, max_vote_accounts ));
     244           0 :     return -1;
     245           0 :   }
     246             : 
     247           0 :   for( ulong j=0UL; j<manifest->epoch_stakes[t_1_idx].vote_stakes_len; j++ ) {
     248           0 :     if( FD_UNLIKELY( !manifest->epoch_stakes[t_1_idx].vote_stakes[j].stake ) ) {
     249           0 :       FD_LOG_WARNING(( "corrupt snapshot: T-1 epoch stakes entry %lu has zero stake", j ));
     250           0 :       return -1;
     251           0 :     }
     252           0 :   }
     253             : 
     254           0 :   return 0;
     255           0 : }
     256             : 
     257             : #define SORT_NAME        fd_blockhash_sort
     258           0 : #define SORT_KEY_T       fd_blockhash_info_t
     259           0 : #define SORT_BEFORE(a,b) ((a).hash_index<(b).hash_index)
     260             : #include "../../../util/tmpl/fd_sort.c"
     261             : 
     262             : static int
     263             : blockhashes_recover( fd_blockhashes_t *                       blockhashes,
     264             :                      fd_snapshot_manifest_blockhash_t const * ages,
     265             :                      ulong                                    age_cnt,
     266           0 :                      ulong                                    seed ) {
     267             : 
     268             :   /* The caller must guarantee that fd_ssload_manifest_validate has
     269             :      already been invoked, verifying that age_cnt is in the range
     270             :      (0, FD_BLOCKHASHES_MAX] and that hash_index values are distinct. */
     271             : 
     272           0 :   if( FD_UNLIKELY( !fd_blockhashes_init( blockhashes, seed ) ) ) {
     273           0 :     FD_LOG_WARNING(( "failed to initialize blockhash queue" ));
     274           0 :     return -1;
     275           0 :   }
     276             : 
     277             :   /* The manifest lists entries in arbitrary order (Agave serializes a
     278             :      HashMap). sort the deque by hash_index, then index them in the map.
     279             : 
     280             :      The deque was just initialized, so it starts at slot 0 and the at
     281             :      most FD_BLOCKHASHES_MAX entries are contiguous at the front of its
     282             :      array without wrapping, which is what makes sorting it in place
     283             :      valid. */
     284             : 
     285           0 :   FD_TEST( fd_blockhash_deq_empty( blockhashes->d.deque ) );
     286           0 :   FD_TEST( !( blockhashes->d.start % fd_blockhash_deq_max( blockhashes->d.deque ) ) ); /* head at array slot 0 */
     287           0 :   FD_TEST( age_cnt<=fd_blockhash_deq_max( blockhashes->d.deque ) );
     288             : 
     289           0 :   for( ulong i=0UL; i<age_cnt; i++ ) {
     290           0 :     fd_snapshot_manifest_blockhash_t const * elem = &ages[ i ];
     291           0 :     fd_blockhash_info_t * info = fd_blockhash_deq_push_tail_nocopy( blockhashes->d.deque );
     292           0 :     fd_memset( info, 0, sizeof(fd_blockhash_info_t) );
     293           0 :     fd_memcpy( info->hash.uc, elem->hash, 32UL );
     294           0 :     info->lamports_per_signature = elem->lamports_per_signature;
     295           0 :     info->hash_index             = elem->hash_index;
     296           0 :   }
     297           0 :   fd_blockhash_sort_inplace( blockhashes->d.deque, age_cnt );
     298             : 
     299           0 :   for( ulong i=0UL; i<age_cnt; i++ ) {
     300           0 :     fd_blockhash_map_idx_insert( blockhashes->map, i, blockhashes->d.deque );
     301           0 :   }
     302             : 
     303           0 :   return 0;
     304           0 : }
     305             : 
     306             : int
     307             : fd_ssload_recover_validate( fd_snapshot_manifest_t const * manifest,
     308           0 :                             fd_banks_t const *             banks ) {
     309           0 :   return fd_ssload_manifest_validate( manifest, banks->max_vote_accounts, banks->max_stake_accounts );
     310           0 : }
     311             : 
     312             : int
     313             : fd_ssload_recover_apply( fd_snapshot_manifest_t * manifest,
     314             :                          fd_bank_t *              bank,
     315           0 :                          ulong                    blockhash_seed ) {
     316             : 
     317             :   /* Slot */
     318             : 
     319           0 :   bank->f.slot = manifest->slot;
     320           0 :   bank->f.parent_slot = manifest->parent_slot;
     321             : 
     322             :   /* Bank Hash */
     323             : 
     324           0 :   fd_hash_t hash;
     325           0 :   fd_memcpy( &hash.uc, manifest->bank_hash, 32UL );
     326           0 :   bank->f.bank_hash = hash;
     327             : 
     328           0 :   fd_hash_t parent_hash;
     329           0 :   fd_memcpy( &parent_hash.uc, manifest->parent_bank_hash, 32UL );
     330           0 :   bank->f.prev_bank_hash = parent_hash;
     331             : 
     332           0 :   fd_fee_rate_governor_t * fee_rate_governor = &bank->f.fee_rate_governor;
     333           0 :   fee_rate_governor->target_lamports_per_signature = manifest->fee_rate_governor.target_lamports_per_signature;
     334           0 :   fee_rate_governor->target_signatures_per_slot    = manifest->fee_rate_governor.target_signatures_per_slot;
     335           0 :   fee_rate_governor->min_lamports_per_signature    = manifest->fee_rate_governor.min_lamports_per_signature;
     336           0 :   fee_rate_governor->max_lamports_per_signature    = manifest->fee_rate_governor.max_lamports_per_signature;
     337           0 :   fee_rate_governor->burn_percent                  = manifest->fee_rate_governor.burn_percent;
     338             :   /* https://github.com/anza-xyz/agave/blob/v3.0.3/runtime/src/serde_snapshot.rs#L464-L466 */
     339           0 :   bank->f.rbh_lamports_per_sig = manifest->lamports_per_signature;
     340             : 
     341           0 :   fd_inflation_t * inflation = &bank->f.inflation;
     342           0 :   inflation->initial         = manifest->inflation_params.initial;
     343           0 :   inflation->terminal        = manifest->inflation_params.terminal;
     344           0 :   inflation->taper           = manifest->inflation_params.taper;
     345           0 :   inflation->foundation      = manifest->inflation_params.foundation;
     346           0 :   inflation->foundation_term = manifest->inflation_params.foundation_term;
     347           0 :   inflation->unused          = 0.0;
     348             : 
     349           0 :   fd_epoch_schedule_t * epoch_schedule = &bank->f.epoch_schedule;
     350           0 :   epoch_schedule->slots_per_epoch             = manifest->epoch_schedule_params.slots_per_epoch;
     351           0 :   epoch_schedule->leader_schedule_slot_offset = manifest->epoch_schedule_params.leader_schedule_slot_offset;
     352           0 :   epoch_schedule->warmup                      = manifest->epoch_schedule_params.warmup;
     353           0 :   epoch_schedule->first_normal_epoch          = manifest->epoch_schedule_params.first_normal_epoch;
     354           0 :   epoch_schedule->first_normal_slot           = manifest->epoch_schedule_params.first_normal_slot;
     355             : 
     356           0 :   ulong epoch = fd_slot_to_epoch( epoch_schedule, manifest->slot, NULL );
     357           0 :   bank->f.epoch = epoch;
     358             : 
     359           0 :   fd_rent_t * rent = &bank->f.rent;
     360           0 :   rent->lamports_per_uint8_year = manifest->rent_params.lamports_per_uint8_year;
     361           0 :   rent->exemption_threshold     = manifest->rent_params.exemption_threshold;
     362           0 :   rent->burn_percent            = manifest->rent_params.burn_percent;
     363             : 
     364             :   /* https://github.com/anza-xyz/agave/blob/v3.0.6/ledger/src/blockstore_processor.rs#L1118
     365             :      None gets treated as 0 for hash verification. */
     366           0 :   ulong restored_hashes_per_tick = manifest->has_hashes_per_tick ? manifest->hashes_per_tick : 0UL;
     367             : 
     368           0 :   fd_lthash_value_t * lthash = fd_bank_lthash_locking_modify( bank );
     369           0 :   if( FD_LIKELY( manifest->has_accounts_lthash ) ) {
     370           0 :     fd_memcpy( lthash, manifest->accounts_lthash, sizeof(fd_lthash_value_t) );
     371           0 :   } else {
     372           0 :     fd_memset( lthash, 0, sizeof(fd_lthash_value_t) );
     373           0 :   }
     374           0 :   fd_bank_lthash_end_locking_modify( bank );
     375             : 
     376           0 :   fd_blockhashes_t * blockhashes = &bank->f.block_hash_queue;
     377           0 :   if( FD_UNLIKELY( blockhashes_recover( blockhashes, manifest->blockhashes, manifest->blockhashes_len, blockhash_seed ) ) ) {
     378           0 :     FD_LOG_WARNING(( "blockhash queue recovery failed" ));
     379           0 :     return -1;
     380           0 :   }
     381             : 
     382             :   /* PoH */
     383           0 :   fd_blockhashes_t const * bhq = &bank->f.block_hash_queue;
     384           0 :   fd_hash_t const * last_hash = fd_blockhashes_peek_last_hash( bhq );
     385           0 :   if( FD_LIKELY( last_hash ) ) bank->f.poh = *last_hash;
     386             : 
     387           0 :   bank->f.capitalization                   = manifest->capitalization;
     388           0 :   bank->f.txn_count                        = manifest->transaction_count;
     389           0 :   bank->f.signature_count                  = manifest->signature_count;
     390           0 :   bank->f.tick_height                      = manifest->tick_height;
     391           0 :   bank->f.max_tick_height                  = manifest->max_tick_height;
     392           0 :   bank->f.ticks_per_slot                   = manifest->ticks_per_slot;
     393           0 :   bank->f.genesis_creation_time            = manifest->creation_time_seconds;
     394           0 :   bank->f.slot_params                      = FD_SLOT_PARAMS_400MS;
     395           0 :   bank->f.slot_params.ns_per_slot          = manifest->ns_per_slot;
     396           0 :   bank->f.slot_params.ns_per_slot_adjusted = fd_ulong_sat_sub( bank->f.slot_params.ns_per_slot, FD_TARGET_SLOT_ADJUSTMENT_NS );
     397           0 :   bank->f.slot_params.slots_per_year       = manifest->slots_per_year;
     398           0 :   bank->f.slot_params.hashes_per_tick      = restored_hashes_per_tick;
     399           0 :   bank->f.block_height                     = manifest->block_height;
     400           0 :   bank->f.execution_fees                   = manifest->collector_fees;
     401           0 :   bank->f.priority_fees                    = 0UL;
     402             : 
     403             :   /* Set the cluster type based on the genesis creation time.  This is
     404             :      later cross referenced against the genesis hash. */
     405           0 :   switch( bank->f.genesis_creation_time ) {
     406           0 :     case FD_RUNTIME_GENESIS_CREATION_TIME_TESTNET:
     407           0 :       bank->f.cluster_type = FD_CLUSTER_TESTNET;
     408           0 :       break;
     409           0 :     case FD_RUNTIME_GENESIS_CREATION_TIME_MAINNET:
     410           0 :       bank->f.cluster_type = FD_CLUSTER_MAINNET_BETA;
     411           0 :       break;
     412           0 :     case FD_RUNTIME_GENESIS_CREATION_TIME_DEVNET:
     413           0 :       bank->f.cluster_type = FD_CLUSTER_DEVNET;
     414           0 :       break;
     415           0 :     default:
     416           0 :       bank->f.cluster_type = FD_CLUSTER_UNKNOWN;
     417           0 :   }
     418             : 
     419             :   /* Restore the hard fork list. */
     420           0 :   bank->f.hard_fork_cnt = manifest->hard_fork_cnt;
     421           0 :   if( FD_LIKELY( manifest->hard_fork_cnt ) ) {
     422           0 :     for( ulong i=0UL; i<manifest->hard_fork_cnt; i++ ) {
     423           0 :       bank->f.hard_forks[ i ] = manifest->hard_forks[ i ];
     424           0 :     }
     425           0 :   }
     426             : 
     427             :   /* snapin populates the root stake delegation cache directly from the
     428             :      account stream.  The manifest's primary stake delegations are
     429             :      intentionally ignored. */
     430             : 
     431             :   /* We also want to set the total stake to be the total amount of stake
     432             :      at the end of the previous epoch. This value is used for the
     433             :      get_epoch_stake syscall.
     434             : 
     435             :      A note on Agave's indexing scheme for their epoch_stakes
     436             :      structure:
     437             : 
     438             :      https://github.com/anza-xyz/agave/blob/v2.2.14/runtime/src/bank.rs#L6175
     439             : 
     440             :      If we are loading a snapshot and replaying in the middle of
     441             :      epoch 7, the syscall is supposed to return the total stake at
     442             :      the end of epoch 6.  The epoch_stakes structure is indexed in
     443             :      Agave by the epoch number of the leader schedule that the
     444             :      stakes are meant to determine.  For instance, to get the
     445             :      stakes at the end of epoch 6, we should query by 8, because
     446             :      the leader schedule for epoch 8 is determined based on the
     447             :      stakes at the end of epoch 6.  Therefore, we save the total
     448             :      epoch stake using the leader schedule epoch below. */
     449             : 
     450           0 :   fd_vote_stakes_t * vote_stakes = fd_bank_vote_stakes( bank );
     451           0 :   fd_vote_stakes_reset( vote_stakes );
     452           0 :   bank->vote_stakes_fork_id = fd_vote_stakes_init( vote_stakes, bank->f.epoch );
     453           0 :   ushort vote_stakes_fork_id = bank->vote_stakes_fork_id;
     454             : 
     455           0 :   ulong leader_schedule_epoch = fd_slot_to_leader_schedule_epoch( epoch_schedule, manifest->slot );
     456           0 :   ulong epoch_stakes_base     = epoch > 3UL ? epoch - 3UL : 0UL;
     457           0 :   ulong t_1_idx = leader_schedule_epoch - epoch_stakes_base;
     458           0 :   ulong t_2_idx = epoch - epoch_stakes_base;
     459             : 
     460           0 :   bank->f.total_epoch_stake = manifest->epoch_stakes[t_1_idx].total_stake;
     461             : 
     462           0 :   bank->epoch_credits_fork_id = fd_epoch_credits_store_new_fork( fd_bank_epoch_credits( bank ), bank->epoch_credits_fork_id );
     463           0 :   ulong epoch_credits_len = 0UL;
     464           0 :   fd_epoch_credits_view_t epoch_credits_view[1];
     465           0 :   FD_TEST( fd_epoch_credits_view_init( epoch_credits_view, fd_bank_epoch_credits( bank ), bank->epoch_credits_fork_id ) );
     466             : 
     467             :   /* Populate the top votes for the end of the T-1 epoch if the
     468             :      snapshot is in epoch T. */
     469           0 :   for( ulong i=0UL; i<manifest->epoch_stakes[t_1_idx].vote_stakes_len; i++ ) {
     470           0 :     fd_snapshot_manifest_vote_stakes_t const * elem = &manifest->epoch_stakes[t_1_idx].vote_stakes[i];
     471             : 
     472           0 :     fd_vote_stakes_snap_insert_t_1( vote_stakes, vote_stakes_fork_id, (fd_pubkey_t *)elem->vote, (fd_pubkey_t *)elem->identity, elem->stake, elem->commission, elem->identity_bls,
     473           0 :                                     elem->commission_block_bps, elem->pending_delegator_rewards,
     474           0 :                                     (fd_pubkey_t const *)elem->commission_inflation, (fd_pubkey_t const *)elem->commission_block, &elem->location );
     475             : 
     476             :     /* Reward recalculation resolves every epoch credits entry against
     477             :        the t_1 set, so only admitted accounts may get one. */
     478           0 :     if( FD_UNLIKELY( !fd_vote_stakes_query_t_1( vote_stakes, vote_stakes_fork_id, (fd_pubkey_t const *)elem->vote, NULL ) ) ) continue;
     479             : 
     480           0 :     if( FD_UNLIKELY( epoch_credits_len>=FD_RUNTIME_MAX_VAT_VOTE_ACCOUNTS ) ) {
     481           0 :       FD_LOG_WARNING(( "corrupt snapshot: more vote accounts than the epoch credits store holds (%lu)", FD_RUNTIME_MAX_VAT_VOTE_ACCOUNTS ));
     482           0 :       fd_epoch_credits_view_fini( epoch_credits_view );
     483           0 :       return -1;
     484           0 :     }
     485           0 :     fd_epoch_credits_t * ec = &epoch_credits_view->credits[epoch_credits_len];
     486           0 :     fd_memcpy( ec->pubkey, elem->vote, 32UL );
     487             : 
     488           0 :     ulong cnt        = 0UL;
     489           0 :     ec->base_credits = 0UL;
     490           0 :     for( ulong j=0UL; j<elem->epoch_credits_history_len; j++ ) {
     491           0 :       if( FD_UNLIKELY( fd_epoch_credits_is_alpenglow_marker( &elem->epoch_credits[ j ] ) ) ) continue;
     492           0 :       if( FD_UNLIKELY( !cnt ) ) ec->base_credits = elem->epoch_credits[ j ].prev_credits;
     493           0 :       ec->epoch[ cnt ]              = (ushort)elem->epoch_credits[ j ].epoch;
     494           0 :       ec->credits_delta[ cnt ]      = elem->epoch_credits[ j ].credits      - ec->base_credits;
     495           0 :       ec->prev_credits_delta[ cnt ] = elem->epoch_credits[ j ].prev_credits - ec->base_credits;
     496           0 :       cnt++;
     497           0 :     }
     498             :     /* Manifest validation already rejects non-increasing epochs (except
     499             :        Alpenglow marker). */
     500           0 :     ec->cnt = (uchar)cnt;
     501           0 :     epoch_credits_len++;
     502           0 :   }
     503           0 :   epoch_credits_view->len = epoch_credits_len;
     504           0 :   fd_epoch_credits_view_fini( epoch_credits_view );
     505           0 :   fd_vote_stakes_finalize( vote_stakes, vote_stakes_fork_id, FD_VOTE_STAKES_ITER_T_1 );
     506             : 
     507             :   /* Populate the top votes for the end of the T-2 epoch if the
     508             :      snapshot is in epoch T. */
     509           0 :   for( ulong i=0UL; i<manifest->epoch_stakes[t_2_idx].vote_stakes_len; i++ ) {
     510           0 :     fd_snapshot_manifest_vote_stakes_t const * elem = &manifest->epoch_stakes[t_2_idx].vote_stakes[i];
     511           0 :     fd_vote_stakes_snap_insert_t_2( vote_stakes, vote_stakes_fork_id, (fd_pubkey_t *)elem->vote, (fd_pubkey_t *)elem->identity, elem->stake, elem->commission, elem->identity_bls,
     512           0 :                                     elem->commission_block_bps, elem->pending_delegator_rewards,
     513           0 :                                     (fd_pubkey_t const *)elem->commission_inflation, (fd_pubkey_t const *)elem->commission_block, &elem->location );
     514           0 :   }
     515           0 :   fd_vote_stakes_finalize( vote_stakes, vote_stakes_fork_id, FD_VOTE_STAKES_ITER_T_2 );
     516             : 
     517             :   /* Populate the top votes for the end of the T-3 through T-5 epochs
     518             :      if the snapshot is in epoch T.  Entries the snapshot does not
     519             :      carry stay non-resident. */
     520           0 :   for( ulong n=3UL; n<=5UL && epoch>=n-2UL; n++ ) {
     521           0 :     fd_snapshot_manifest_epoch_stakes_t const * epoch_stakes = &manifest->epoch_stakes[t_2_idx-(n-2UL)];
     522           0 :     for( ulong i=0UL; i<epoch_stakes->vote_stakes_len; i++ ) {
     523           0 :       fd_snapshot_manifest_vote_stakes_t const * elem = &epoch_stakes->vote_stakes[i];
     524           0 :       fd_vote_stakes_snap_insert_t_n( vote_stakes, vote_stakes_fork_id, n, (fd_pubkey_t *)elem->vote, (fd_pubkey_t *)elem->identity, elem->stake, elem->commission, elem->identity_bls,
     525           0 :                                       elem->commission_block_bps, elem->pending_delegator_rewards, NULL, NULL, &elem->location );
     526           0 :     }
     527           0 :     fd_vote_stakes_finalize( vote_stakes, vote_stakes_fork_id, FD_VOTE_STAKES_ITER_T_2+(int)(n-2UL) );
     528           0 :   }
     529             : 
     530           0 :   bank->accdb_fork_id        = (fd_accdb_fork_id_t){ .val = manifest->accdb_fork_id };
     531           0 :   bank->parent_accdb_fork_id = bank->accdb_fork_id;
     532           0 :   bank->txncache_fork_id     = (fd_txncache_fork_id_t){ .val = manifest->txncache_fork_id };
     533             : 
     534           0 :   return 0;
     535           0 : }
     536             : 
     537             : int
     538             : fd_ssload_recover( fd_snapshot_manifest_t * manifest,
     539             :                    fd_banks_t *             banks,
     540             :                    fd_bank_t *              bank,
     541           0 :                    ulong                    blockhash_seed ) {
     542             : 
     543           0 :   if( FD_UNLIKELY( fd_ssload_recover_validate( manifest, banks ) ) ) {
     544           0 :     FD_LOG_WARNING(( "snapshot manifest validation failed" ));
     545           0 :     return -1;
     546           0 :   }
     547             : 
     548           0 :   return fd_ssload_recover_apply( manifest, bank, blockhash_seed );
     549           0 : }

Generated by: LCOV version 1.14