Line data Source code
1 : #include "fd_ssload.h"
2 :
3 : #include "../../../disco/genesis/fd_genesis_cluster.h"
4 : #include "../../../flamenco/runtime/sysvar/fd_sysvar_epoch_schedule.h"
5 : #include "../../../flamenco/runtime/fd_runtime_stack.h"
6 : #include "fd_ssmsg.h"
7 :
8 : void
9 : blockhashes_recover( fd_blockhashes_t * blockhashes,
10 : fd_snapshot_manifest_blockhash_t const * ages,
11 : ulong age_cnt,
12 0 : ulong seed ) {
13 0 : FD_TEST( fd_blockhashes_init( blockhashes, seed ) );
14 0 : FD_TEST( age_cnt && age_cnt<=FD_BLOCKHASHES_MAX );
15 :
16 : /* For depressing reasons, the ages array is not sorted when ingested
17 : from a snapshot. The hash_index field is also not validated.
18 : Firedancer assumes that the sequence of hash_index numbers is
19 : gapless and does not wrap around. */
20 :
21 0 : ulong seq_min = ULONG_MAX-1;
22 0 : for( ulong i=0UL; i<age_cnt; i++ ) {
23 0 : seq_min = fd_ulong_min( seq_min, ages[ i ].hash_index );
24 0 : }
25 0 : ulong seq_max;
26 0 : if( FD_UNLIKELY( __builtin_uaddl_overflow( seq_min, age_cnt, &seq_max ) ) ) {
27 : /* TODO: Move to snapin validations so we can retry */
28 0 : FD_LOG_ERR(( "Corrupt snapshot: blockhash queue sequence number wraparound (seq_min=%lu age_cnt=%lu)", seq_min, age_cnt ));
29 0 : }
30 :
31 : /* Reset */
32 :
33 0 : for( ulong i=0UL; i<age_cnt; i++ ) {
34 0 : fd_blockhash_info_t * ele = fd_blockhash_deq_push_tail_nocopy( blockhashes->d.deque );
35 0 : memset( ele, 0, sizeof(fd_blockhash_info_t) );
36 0 : }
37 :
38 : /* Load hashes */
39 :
40 0 : for( ulong i=0UL; i<age_cnt; i++ ) {
41 0 : fd_snapshot_manifest_blockhash_t const * elem = &ages[ i ];
42 0 : ulong idx;
43 0 : if( FD_UNLIKELY( __builtin_usubl_overflow( elem->hash_index, seq_min, &idx ) ) ) {
44 : /* TODO: Move to snapin validations so we can retry */
45 0 : FD_LOG_ERR(( "Corrupt snapshot: gap in blockhash queue (seq=[%lu,%lu) idx=%lu)",
46 0 : seq_min, seq_max, elem->hash_index ));
47 0 : }
48 0 : fd_blockhash_info_t * info = &blockhashes->d.deque[ idx ];
49 0 : if( FD_UNLIKELY( info->exists ) ) {
50 : /* TODO: Move to snapin validations so we can retry */
51 0 : FD_LOG_HEXDUMP_NOTICE(( "info", info, sizeof(fd_blockhash_info_t) ));
52 0 : FD_LOG_ERR(( "Corrupt snapshot: duplicate blockhash queue index %lu", idx ));
53 0 : }
54 0 : info->exists = 1;
55 0 : fd_memcpy( info->hash.uc, elem->hash, 32UL );
56 0 : info->fee_calculator.lamports_per_signature = elem->lamports_per_signature;
57 0 : fd_blockhash_map_idx_insert( blockhashes->map, idx, blockhashes->d.deque );
58 0 : }
59 0 : }
60 :
61 : void
62 : fd_ssload_recover( fd_snapshot_manifest_t * manifest,
63 : fd_banks_t * banks,
64 : fd_bank_t * bank,
65 : fd_runtime_stack_t * runtime_stack,
66 0 : int is_incremental ) {
67 : /* Slot */
68 :
69 0 : bank->f.slot = manifest->slot;
70 0 : bank->f.parent_slot = manifest->parent_slot;
71 :
72 : /* Bank Hash */
73 :
74 0 : fd_hash_t hash;
75 0 : fd_memcpy( &hash.uc, manifest->bank_hash, 32UL );
76 0 : bank->f.bank_hash = hash;
77 :
78 0 : fd_hash_t parent_hash;
79 0 : fd_memcpy( &parent_hash.uc, manifest->parent_bank_hash, 32UL );
80 0 : bank->f.prev_bank_hash = parent_hash;
81 :
82 0 : fd_fee_rate_governor_t * fee_rate_governor = &bank->f.fee_rate_governor;
83 0 : fee_rate_governor->target_lamports_per_signature = manifest->fee_rate_governor.target_lamports_per_signature;
84 0 : fee_rate_governor->target_signatures_per_slot = manifest->fee_rate_governor.target_signatures_per_slot;
85 0 : fee_rate_governor->min_lamports_per_signature = manifest->fee_rate_governor.min_lamports_per_signature;
86 0 : fee_rate_governor->max_lamports_per_signature = manifest->fee_rate_governor.max_lamports_per_signature;
87 0 : fee_rate_governor->burn_percent = manifest->fee_rate_governor.burn_percent;
88 : /* https://github.com/anza-xyz/agave/blob/v3.0.3/runtime/src/serde_snapshot.rs#L464-L466 */
89 0 : bank->f.rbh_lamports_per_sig = manifest->lamports_per_signature;
90 :
91 0 : fd_inflation_t * inflation = &bank->f.inflation;
92 0 : inflation->initial = manifest->inflation_params.initial;
93 0 : inflation->terminal = manifest->inflation_params.terminal;
94 0 : inflation->taper = manifest->inflation_params.taper;
95 0 : inflation->foundation = manifest->inflation_params.foundation;
96 0 : inflation->foundation_term = manifest->inflation_params.foundation_term;
97 0 : inflation->unused = 0.0;
98 :
99 0 : fd_epoch_schedule_t * epoch_schedule = &bank->f.epoch_schedule;
100 0 : epoch_schedule->slots_per_epoch = manifest->epoch_schedule_params.slots_per_epoch;
101 0 : epoch_schedule->leader_schedule_slot_offset = manifest->epoch_schedule_params.leader_schedule_slot_offset;
102 0 : epoch_schedule->warmup = manifest->epoch_schedule_params.warmup;
103 0 : epoch_schedule->first_normal_epoch = manifest->epoch_schedule_params.first_normal_epoch;
104 0 : epoch_schedule->first_normal_slot = manifest->epoch_schedule_params.first_normal_slot;
105 :
106 0 : ulong epoch = fd_slot_to_epoch( epoch_schedule, manifest->slot, NULL );
107 0 : bank->f.epoch = epoch;
108 :
109 0 : fd_rent_t * rent = &bank->f.rent;
110 0 : rent->lamports_per_uint8_year = manifest->rent_params.lamports_per_uint8_year;
111 0 : rent->exemption_threshold = manifest->rent_params.exemption_threshold;
112 0 : rent->burn_percent = manifest->rent_params.burn_percent;
113 :
114 : /* https://github.com/anza-xyz/agave/blob/v3.0.6/ledger/src/blockstore_processor.rs#L1118
115 : None gets treated as 0 for hash verification. */
116 0 : if( FD_LIKELY( manifest->has_hashes_per_tick ) ) bank->f.hashes_per_tick = manifest->hashes_per_tick;
117 0 : else bank->f.hashes_per_tick = 0UL;
118 :
119 0 : fd_lthash_value_t * lthash = fd_bank_lthash_locking_modify( bank );
120 0 : if( FD_LIKELY( manifest->has_accounts_lthash ) ) {
121 0 : fd_memcpy( lthash, manifest->accounts_lthash, sizeof(fd_lthash_value_t) );
122 0 : } else {
123 0 : fd_memset( lthash, 0, sizeof(fd_lthash_value_t) );
124 0 : }
125 0 : fd_bank_lthash_end_locking_modify( bank );
126 :
127 0 : fd_blockhashes_t * blockhashes = &bank->f.block_hash_queue;
128 0 : blockhashes_recover( blockhashes, manifest->blockhashes, manifest->blockhashes_len, 42UL /* TODO */ );
129 :
130 : /* PoH */
131 0 : fd_blockhashes_t const * bhq = &bank->f.block_hash_queue;
132 0 : fd_hash_t const * last_hash = fd_blockhashes_peek_last_hash( bhq );
133 0 : if( FD_LIKELY( last_hash ) ) bank->f.poh = *last_hash;
134 :
135 0 : bank->f.capitalization = manifest->capitalization;
136 0 : bank->f.txn_count = manifest->transaction_count;
137 0 : bank->f.parent_signature_cnt = manifest->signature_count;
138 0 : bank->f.tick_height = manifest->tick_height;
139 0 : bank->f.max_tick_height = manifest->max_tick_height;
140 0 : bank->f.ns_per_slot = (fd_w_u128_t) { .ul={ manifest->ns_per_slot, 0UL } };
141 0 : bank->f.ticks_per_slot = manifest->ticks_per_slot;
142 0 : bank->f.genesis_creation_time = manifest->creation_time_seconds;
143 0 : bank->f.slots_per_year = manifest->slots_per_year;
144 0 : bank->f.block_height = manifest->block_height;
145 0 : bank->f.execution_fees = manifest->collector_fees;
146 0 : bank->f.priority_fees = 0UL;
147 :
148 : /* Set the cluster type based on the genesis creation time. This is
149 : later cross referenced against the genesis hash. */
150 0 : switch( bank->f.genesis_creation_time ) {
151 0 : case FD_RUNTIME_GENESIS_CREATION_TIME_TESTNET:
152 0 : bank->f.cluster_type = FD_CLUSTER_TESTNET;
153 0 : break;
154 0 : case FD_RUNTIME_GENESIS_CREATION_TIME_MAINNET:
155 0 : bank->f.cluster_type = FD_CLUSTER_MAINNET_BETA;
156 0 : break;
157 0 : case FD_RUNTIME_GENESIS_CREATION_TIME_DEVNET:
158 0 : bank->f.cluster_type = FD_CLUSTER_DEVNET;
159 0 : break;
160 0 : default:
161 0 : bank->f.cluster_type = FD_CLUSTER_UNKNOWN;
162 0 : }
163 :
164 : /* Update last restart slot
165 : https://github.com/solana-labs/solana/blob/30531d7a5b74f914dde53bfbb0bc2144f2ac92bb/runtime/src/bank.rs#L2152
166 :
167 : old_bank->hard_forks is sorted ascending by slot number.
168 : To find the last restart slot, take the highest hard fork slot
169 : number that is less or equal than the current slot number.
170 : (There might be some hard forks in the future, ignore these)
171 :
172 : SIMD-0047: The first restart slot should be `0` */
173 0 : fd_sol_sysvar_last_restart_slot_t * last_restart_slot = &bank->f.last_restart_slot;
174 0 : last_restart_slot->slot = 0UL;
175 0 : if( FD_LIKELY( manifest->hard_forks_len ) ) {
176 0 : for( ulong i=0UL; i<manifest->hard_forks_len; i++ ) {
177 0 : ulong slot = manifest->hard_forks[ manifest->hard_forks_len-1UL-i ];
178 0 : if( FD_LIKELY( slot<=manifest->slot ) ) {
179 0 : last_restart_slot->slot = slot;
180 0 : break;
181 0 : }
182 0 : }
183 0 : }
184 :
185 : /* Stake delegations for the current epoch. */
186 0 : fd_stake_delegations_t * stake_delegations = fd_banks_stake_delegations_root_query( banks );
187 0 : if( is_incremental ) fd_stake_delegations_init( stake_delegations );
188 0 : for( ulong i=0UL; i<manifest->stake_delegations_len; i++ ) {
189 0 : fd_snapshot_manifest_stake_delegation_t const * elem = &manifest->stake_delegations[ i ];
190 0 : if( FD_UNLIKELY( elem->stake_delegation==0UL ) ) {
191 0 : continue;
192 0 : }
193 0 : fd_stake_delegations_root_update(
194 0 : stake_delegations,
195 0 : (fd_pubkey_t *)elem->stake_pubkey,
196 0 : (fd_pubkey_t *)elem->vote_pubkey,
197 0 : elem->stake_delegation,
198 0 : elem->activation_epoch,
199 0 : elem->deactivation_epoch,
200 0 : elem->credits_observed,
201 0 : elem->warmup_cooldown_rate
202 0 : );
203 0 : }
204 :
205 : /* We also want to set the total stake to be the total amount of stake
206 : at the end of the previous epoch. This value is used for the
207 : get_epoch_stake syscall.
208 :
209 : A note on Agave's indexing scheme for their epoch_stakes
210 : structure:
211 :
212 : https://github.com/anza-xyz/agave/blob/v2.2.14/runtime/src/bank.rs#L6175
213 :
214 : If we are loading a snapshot and replaying in the middle of
215 : epoch 7, the syscall is supposed to return the total stake at
216 : the end of epoch 6. The epoch_stakes structure is indexed in
217 : Agave by the epoch number of the leader schedule that the
218 : stakes are meant to determine. For instance, to get the
219 : stakes at the end of epoch 6, we should query by 8, because
220 : the leader schedule for epoch 8 is determined based on the
221 : stakes at the end of epoch 6. Therefore, we save the total
222 : epoch stake by querying for epoch+1. This logic is encapsulated
223 : in fd_ssmanifest_parser.c. */
224 0 : bank->f.total_epoch_stake = manifest->epoch_stakes[1].total_stake;
225 :
226 0 : fd_vote_stakes_t * vote_stakes = fd_bank_vote_stakes( bank );
227 0 : if( is_incremental ) fd_vote_stakes_reset( vote_stakes );
228 :
229 0 : fd_vote_rewards_map_t * vote_ele_map = runtime_stack->stakes.vote_map;
230 0 : fd_vote_rewards_map_reset( vote_ele_map );
231 :
232 0 : fd_top_votes_t * top_votes_t_1 = fd_bank_top_votes_t_1_modify( bank );
233 0 : fd_top_votes_t * top_votes_t_2 = fd_bank_top_votes_t_2_modify( bank );
234 0 : fd_top_votes_init( top_votes_t_1 );
235 0 : fd_top_votes_init( top_votes_t_2 );
236 :
237 : /* Vote stakes for the previous epoch (E-1). */
238 0 : for( ulong i=0UL; i<manifest->epoch_stakes[1].vote_stakes_len; i++ ) {
239 0 : fd_snapshot_manifest_vote_stakes_t const * elem = &manifest->epoch_stakes[1].vote_stakes[i];
240 : /* First convert the epoch credits to the format expected by the
241 : vote states. We need to do this because we may need the vote
242 : state credits from the end of the previous epoch in case we need
243 : to recalculate the stake reward partitions. */
244 0 : fd_vote_rewards_t * vote_ele = &runtime_stack->stakes.vote_ele[i];
245 0 : fd_memcpy( vote_ele->pubkey.uc, elem->vote, 32UL );
246 0 : vote_ele->commission_t_2 = vote_ele->commission_t_1 = (uchar)elem->commission;
247 0 : fd_vote_rewards_map_idx_insert( vote_ele_map, i, runtime_stack->stakes.vote_ele );
248 0 : fd_vote_stakes_root_insert_key(
249 0 : vote_stakes,
250 0 : (fd_pubkey_t *)elem->vote,
251 0 : (fd_pubkey_t *)elem->identity,
252 0 : elem->stake,
253 0 : vote_ele->commission_t_1,
254 0 : bank->f.epoch );
255 :
256 0 : if( FD_FEATURE_ACTIVE_BANK( bank, validator_admission_ticket ) ) {
257 0 : if( FD_UNLIKELY( !elem->has_identity_bls ) ) continue;
258 0 : }
259 :
260 0 : fd_top_votes_insert( top_votes_t_1, (fd_pubkey_t *)elem->vote, (fd_pubkey_t *)elem->identity, elem->stake, (uchar)elem->commission );
261 :
262 0 : if( i<runtime_stack->expected_vote_accounts ) {
263 0 : runtime_stack->stakes.epoch_credits[i].cnt = elem->epoch_credits_history_len;
264 0 : for( ulong j=0UL; j<elem->epoch_credits_history_len; j++ ) {
265 0 : runtime_stack->stakes.epoch_credits[ i ].epoch[ j ] = (ushort)elem->epoch_credits[ j ].epoch;
266 0 : runtime_stack->stakes.epoch_credits[ i ].credits[ j ] = elem->epoch_credits[ j ].credits;
267 0 : runtime_stack->stakes.epoch_credits[ i ].prev_credits[ j ] = elem->epoch_credits[ j ].prev_credits;
268 0 : }
269 0 : } else {
270 0 : FD_LOG_ERR(( "snapshot loading currently does not support more than %lu vote accounts, %lu", runtime_stack->expected_vote_accounts, manifest->epoch_stakes[1].vote_stakes_len ));
271 0 : }
272 0 : }
273 :
274 : /* Vote stakes for the previous epoch (E-2) */
275 0 : for( ulong i=0UL; i<manifest->epoch_stakes[0].vote_stakes_len; i++ ) {
276 0 : fd_snapshot_manifest_vote_stakes_t const * elem = &manifest->epoch_stakes[0].vote_stakes[i];
277 :
278 0 : fd_vote_rewards_t * vote_ele = fd_vote_rewards_map_ele_query( vote_ele_map, (const fd_pubkey_t *)elem->vote, NULL, runtime_stack->stakes.vote_ele );
279 0 : if( FD_LIKELY( vote_ele ) ) vote_ele->commission_t_2 = (uchar)elem->commission;
280 :
281 0 : if( FD_FEATURE_ACTIVE_BANK( bank, validator_admission_ticket ) ) {
282 0 : if( FD_UNLIKELY( !elem->has_identity_bls ) ) continue;
283 0 : }
284 0 : fd_top_votes_insert( top_votes_t_2, (fd_pubkey_t *)elem->vote, (fd_pubkey_t *)elem->identity, elem->stake, (uchar)elem->commission );
285 0 : fd_vote_stakes_root_update_meta(
286 0 : vote_stakes,
287 0 : (fd_pubkey_t *)elem->vote,
288 0 : (fd_pubkey_t *)elem->identity,
289 0 : elem->stake,
290 0 : (uchar)elem->commission,
291 0 : bank->f.epoch );
292 0 : }
293 :
294 0 : bank->txncache_fork_id = (fd_txncache_fork_id_t){ .val = manifest->txncache_fork_id };
295 0 : }
|