Line data Source code
1 : #include "fd_ssload.h"
2 :
3 : #include "../../../flamenco/runtime/context/fd_exec_slot_ctx.h"
4 : #include "../../../flamenco/runtime/program/fd_vote_program.h"
5 : #include "../../../flamenco/runtime/sysvar/fd_sysvar_epoch_schedule.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 0 : fd_exec_slot_ctx_t * slot_ctx ) {
64 :
65 : /* Slot */
66 :
67 0 : fd_eslot_t old_eslot = fd_bank_eslot_get( slot_ctx->bank );
68 0 : fd_eslot_t eslot = fd_eslot( manifest->slot, 0UL );
69 0 : fd_banks_rekey_bank( slot_ctx->banks, old_eslot, eslot );
70 :
71 : /* Bank Hash */
72 :
73 0 : fd_hash_t hash;
74 0 : fd_memcpy( &hash.uc, manifest->bank_hash, 32UL );
75 0 : fd_bank_bank_hash_set( slot_ctx->bank, hash );
76 :
77 0 : fd_hash_t parent_hash;
78 0 : fd_memcpy( &parent_hash.uc, manifest->parent_bank_hash, 32UL );
79 0 : fd_bank_prev_bank_hash_set( slot_ctx->bank, parent_hash );
80 :
81 0 : fd_fee_rate_governor_t * fee_rate_governor = fd_bank_fee_rate_governor_modify( slot_ctx->bank );
82 0 : fee_rate_governor->target_lamports_per_signature = manifest->fee_rate_governor.target_lamports_per_signature;
83 0 : fee_rate_governor->target_signatures_per_slot = manifest->fee_rate_governor.target_signatures_per_slot;
84 0 : fee_rate_governor->min_lamports_per_signature = manifest->fee_rate_governor.min_lamports_per_signature;
85 0 : fee_rate_governor->max_lamports_per_signature = manifest->fee_rate_governor.max_lamports_per_signature;
86 0 : fee_rate_governor->burn_percent = manifest->fee_rate_governor.burn_percent;
87 :
88 0 : fd_inflation_t * inflation = fd_bank_inflation_modify( slot_ctx->bank );
89 0 : inflation->initial = manifest->inflation_params.initial;
90 0 : inflation->terminal = manifest->inflation_params.terminal;
91 0 : inflation->taper = manifest->inflation_params.taper;
92 0 : inflation->foundation = manifest->inflation_params.foundation;
93 0 : inflation->foundation_term = manifest->inflation_params.foundation_term;
94 0 : inflation->unused = 0.0;
95 :
96 0 : fd_epoch_schedule_t * epoch_schedule = fd_bank_epoch_schedule_modify( slot_ctx->bank );
97 0 : epoch_schedule->slots_per_epoch = manifest->epoch_schedule_params.slots_per_epoch;
98 0 : epoch_schedule->leader_schedule_slot_offset = manifest->epoch_schedule_params.leader_schedule_slot_offset;
99 0 : epoch_schedule->warmup = manifest->epoch_schedule_params.warmup;
100 0 : epoch_schedule->first_normal_epoch = manifest->epoch_schedule_params.first_normal_epoch;
101 0 : epoch_schedule->first_normal_slot = manifest->epoch_schedule_params.first_normal_slot;
102 :
103 0 : ulong epoch = fd_slot_to_epoch( epoch_schedule, manifest->slot, NULL );
104 0 : fd_bank_epoch_set( slot_ctx->bank, epoch );
105 :
106 0 : fd_rent_t * rent = fd_bank_rent_modify( slot_ctx->bank );
107 0 : rent->lamports_per_uint8_year = manifest->rent_params.lamports_per_uint8_year;
108 0 : rent->exemption_threshold = manifest->rent_params.exemption_threshold;
109 0 : rent->burn_percent = manifest->rent_params.burn_percent;
110 :
111 0 : if( FD_LIKELY( manifest->has_hashes_per_tick ) ) fd_bank_hashes_per_tick_set( slot_ctx->bank, manifest->hashes_per_tick );
112 0 : else fd_bank_hashes_per_tick_set( slot_ctx->bank, 0UL );
113 :
114 0 : if( FD_LIKELY( manifest->has_accounts_lthash ) ) {
115 0 : fd_lthash_value_t lthash;
116 0 : fd_memcpy( lthash.bytes, manifest->accounts_lthash, 2048UL );
117 0 : fd_bank_lthash_set( slot_ctx->bank, lthash );
118 0 : } else {
119 0 : fd_lthash_value_t lthash = {0};
120 0 : fd_bank_lthash_set( slot_ctx->bank, lthash );
121 0 : }
122 :
123 0 : fd_blockhashes_t * blockhashes = fd_bank_block_hash_queue_modify( slot_ctx->bank );
124 0 : blockhashes_recover( blockhashes, manifest->blockhashes, manifest->blockhashes_len, 42UL /* TODO */ );
125 :
126 : /* PoH */
127 0 : fd_blockhashes_t const * bhq = fd_bank_block_hash_queue_query( slot_ctx->bank );
128 0 : fd_hash_t const * last_hash = fd_blockhashes_peek_last( bhq );
129 0 : if( FD_LIKELY( last_hash ) ) fd_bank_poh_set( slot_ctx->bank, *last_hash );
130 :
131 0 : fd_bank_capitalization_set( slot_ctx->bank, manifest->capitalization );
132 0 : fd_bank_lamports_per_signature_set( slot_ctx->bank, manifest->lamports_per_signature );
133 0 : fd_bank_prev_lamports_per_signature_set( slot_ctx->bank, manifest->lamports_per_signature );
134 0 : fd_bank_transaction_count_set( slot_ctx->bank, manifest->transaction_count );
135 0 : fd_bank_parent_signature_cnt_set( slot_ctx->bank, manifest->signature_count );
136 0 : fd_bank_tick_height_set( slot_ctx->bank, manifest->tick_height );
137 0 : fd_bank_max_tick_height_set( slot_ctx->bank, manifest->max_tick_height );
138 0 : fd_bank_ns_per_slot_set( slot_ctx->bank, manifest->ns_per_slot );
139 0 : fd_bank_ticks_per_slot_set( slot_ctx->bank, manifest->ticks_per_slot );
140 0 : fd_bank_genesis_creation_time_set( slot_ctx->bank, manifest->creation_time_millis );
141 0 : fd_bank_slots_per_year_set( slot_ctx->bank, manifest->slots_per_year );
142 0 : fd_bank_block_height_set( slot_ctx->bank, manifest->block_height );
143 0 : fd_bank_parent_eslot_set( slot_ctx->bank, fd_eslot( manifest->parent_slot, 0UL ) );
144 0 : fd_bank_execution_fees_set( slot_ctx->bank, manifest->collector_fees );
145 0 : fd_bank_priority_fees_set( slot_ctx->bank, 0UL );
146 :
147 : /* Update last restart slot
148 : https://github.com/solana-labs/solana/blob/30531d7a5b74f914dde53bfbb0bc2144f2ac92bb/runtime/src/bank.rs#L2152
149 :
150 : old_bank->hard_forks is sorted ascending by slot number.
151 : To find the last restart slot, take the highest hard fork slot
152 : number that is less or equal than the current slot number.
153 : (There might be some hard forks in the future, ignore these)
154 :
155 : SIMD-0047: The first restart slot should be `0` */
156 0 : fd_sol_sysvar_last_restart_slot_t * last_restart_slot = fd_bank_last_restart_slot_modify( slot_ctx->bank );
157 0 : last_restart_slot->slot = 0UL;
158 0 : if( FD_LIKELY( manifest->hard_forks_len ) ) {
159 0 : for( ulong i=0UL; i<manifest->hard_forks_len; i++ ) {
160 0 : ulong slot = manifest->hard_forks[ manifest->hard_forks_len-1UL-i ];
161 0 : if( FD_LIKELY( slot<=manifest->slot ) ) {
162 0 : last_restart_slot->slot = slot;
163 0 : break;
164 0 : }
165 0 : }
166 0 : }
167 :
168 : /* Stake delegations for the current epoch. */
169 0 : fd_stake_delegations_t * stake_delegations = fd_banks_stake_delegations_root_query( slot_ctx->banks );
170 0 : for( ulong i=0UL; i<manifest->stake_delegations_len; i++ ) {
171 0 : fd_snapshot_manifest_stake_delegation_t const * elem = &manifest->stake_delegations[ i ];
172 0 : fd_stake_delegations_update(
173 0 : stake_delegations,
174 0 : (fd_pubkey_t *)elem->stake_pubkey,
175 0 : (fd_pubkey_t *)elem->vote_pubkey,
176 0 : elem->stake_delegation,
177 0 : elem->activation_epoch,
178 0 : elem->deactivation_epoch,
179 0 : elem->credits_observed,
180 0 : elem->warmup_cooldown_rate
181 0 : );
182 0 : }
183 :
184 : /* Vote states for the current epoch. */
185 0 : fd_vote_states_t * vote_states = fd_vote_states_join( fd_vote_states_new( fd_bank_vote_states_locking_modify( slot_ctx->bank ), FD_RUNTIME_MAX_VOTE_ACCOUNTS, 999UL ) );
186 0 : for( ulong i=0UL; i<manifest->vote_accounts_len; i++ ) {
187 0 : fd_snapshot_manifest_vote_account_t const * elem = &manifest->vote_accounts[ i ];
188 : /* First convert the epoch credits to the format expected by the
189 : vote states. */
190 0 : ushort epoch_credits_epoch[ EPOCH_CREDITS_MAX ];
191 0 : ulong epoch_credits_credits[ EPOCH_CREDITS_MAX ];
192 0 : ulong epoch_credits_prev_credits[ EPOCH_CREDITS_MAX ];
193 0 : for( ulong j=0UL; j<elem->epoch_credits_history_len; j++ ) {
194 0 : epoch_credits_epoch[ j ] = (ushort)elem->epoch_credits[ j ].epoch;
195 0 : epoch_credits_credits[ j ] = elem->epoch_credits[ j ].credits;
196 0 : epoch_credits_prev_credits[ j ] = elem->epoch_credits[ j ].prev_credits;
197 0 : }
198 :
199 0 : fd_vote_states_update(
200 0 : vote_states,
201 0 : (fd_pubkey_t *)elem->vote_account_pubkey,
202 0 : (fd_pubkey_t *)elem->node_account_pubkey,
203 0 : elem->commission,
204 0 : elem->last_timestamp,
205 0 : elem->last_slot,
206 0 : elem->epoch_credits_history_len,
207 0 : epoch_credits_epoch,
208 0 : epoch_credits_credits,
209 0 : epoch_credits_prev_credits );
210 0 : fd_vote_states_update_stake( vote_states, (fd_pubkey_t *)elem->vote_account_pubkey, elem->stake );
211 0 : }
212 0 : fd_bank_vote_states_end_locking_modify( slot_ctx->bank );
213 :
214 : /* Vote stakes for the previous epoch (E-1). */
215 0 : fd_vote_states_t * vote_stakes_prev = fd_vote_states_join( fd_vote_states_new( fd_bank_vote_states_prev_locking_modify( slot_ctx->bank ), FD_RUNTIME_MAX_VOTE_ACCOUNTS, 999UL ) );
216 0 : for( ulong i=0UL; i<manifest->epoch_stakes[1].vote_stakes_len; i++ ) {
217 0 : fd_snapshot_manifest_vote_stakes_t const * elem = &manifest->epoch_stakes[1].vote_stakes[i];
218 : /* First convert the epoch credits to the format expected by the
219 : vote states. */
220 0 : ushort epoch_credits_epoch[ EPOCH_CREDITS_MAX ];
221 0 : ulong epoch_credits_credits[ EPOCH_CREDITS_MAX ];
222 0 : ulong epoch_credits_prev_credits[ EPOCH_CREDITS_MAX ];
223 0 : for( ulong j=0UL; j<elem->epoch_credits_history_len; j++ ) {
224 0 : epoch_credits_epoch[ j ] = (ushort)elem->epoch_credits[ j ].epoch;
225 0 : epoch_credits_credits[ j ] = elem->epoch_credits[ j ].credits;
226 0 : epoch_credits_prev_credits[ j ] = elem->epoch_credits[ j ].prev_credits;
227 0 : }
228 :
229 0 : fd_vote_states_update(
230 0 : vote_stakes_prev,
231 0 : (fd_pubkey_t *)elem->vote,
232 0 : (fd_pubkey_t *)elem->identity,
233 0 : elem->commission,
234 0 : elem->timestamp,
235 0 : elem->slot,
236 0 : elem->epoch_credits_history_len,
237 0 : epoch_credits_epoch,
238 0 : epoch_credits_credits,
239 0 : epoch_credits_prev_credits );
240 0 : if( elem->stake ) {
241 0 : fd_vote_states_update_stake( vote_stakes_prev, (fd_pubkey_t *)elem->vote, elem->stake );
242 0 : } else {
243 0 : fd_vote_states_remove( vote_stakes_prev, (fd_pubkey_t *)elem->vote );
244 0 : }
245 0 : }
246 :
247 0 : fd_bank_vote_states_prev_end_locking_modify( slot_ctx->bank );
248 :
249 : /* We also want to set the total stake to be the total amout of stake
250 : at the end of the previous epoch. This value is used for the
251 : get_epoch_stake syscall.
252 :
253 : FIXME: This needs to be updated at the epoch boundary and this
254 : currently does NOT happen.
255 :
256 : A note on Agave's indexing scheme for their epoch_stakes
257 : structure:
258 :
259 : https://github.com/anza-xyz/agave/blob/v2.2.14/runtime/src/bank.rs#L6175
260 :
261 : If we are loading a snapshot and replaying in the middle of
262 : epoch 7, the syscall is supposed to return the total stake at
263 : the end of epoch 6. The epoch_stakes structure is indexed in
264 : Agave by the epoch number of the leader schedule that the
265 : stakes are meant to determine. For instance, to get the
266 : stakes at the end of epoch 6, we should query by 8, because
267 : the leader schedule for epoch 8 is determined based on the
268 : stakes at the end of epoch 6. Therefore, we save the total
269 : epoch stake by querying for epoch+1. This logic is encapsulated
270 : in fd_ssmanifest_parser.c. */
271 0 : fd_bank_total_epoch_stake_set( slot_ctx->bank, manifest->epoch_stakes[1].total_stake );
272 :
273 : /* Vote stakes for the previous epoch (E-2) */
274 0 : fd_vote_states_t * vote_stakes_prev_prev = fd_vote_states_join( fd_vote_states_new( fd_bank_vote_states_prev_prev_locking_modify( slot_ctx->bank ), FD_RUNTIME_MAX_VOTE_ACCOUNTS, 999UL ) );
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 : /* First convert the epoch credits to the format expected by the
278 : vote states. */
279 0 : ushort epoch_credits_epoch[ EPOCH_CREDITS_MAX ];
280 0 : ulong epoch_credits_credits[ EPOCH_CREDITS_MAX ];
281 0 : ulong epoch_credits_prev_credits[ EPOCH_CREDITS_MAX ];
282 0 : for( ulong j=0UL; j<elem->epoch_credits_history_len; j++ ) {
283 0 : epoch_credits_epoch[ j ] = (ushort)elem->epoch_credits[ j ].epoch;
284 0 : epoch_credits_credits[ j ] = elem->epoch_credits[ j ].credits;
285 0 : epoch_credits_prev_credits[ j ] = elem->epoch_credits[ j ].prev_credits;
286 0 : }
287 0 : fd_vote_states_update(
288 0 : vote_stakes_prev_prev,
289 0 : (fd_pubkey_t *)elem->vote,
290 0 : (fd_pubkey_t *)elem->identity,
291 0 : elem->commission,
292 0 : elem->timestamp,
293 0 : elem->slot,
294 0 : elem->epoch_credits_history_len,
295 0 : epoch_credits_epoch,
296 0 : epoch_credits_credits,
297 0 : epoch_credits_prev_credits );
298 0 : if( elem->stake ) {
299 0 : fd_vote_states_update_stake( vote_stakes_prev_prev, (fd_pubkey_t *)elem->vote, elem->stake );
300 0 : } else {
301 0 : fd_vote_states_remove( vote_stakes_prev_prev, (fd_pubkey_t *)elem->vote );
302 0 : }
303 0 : }
304 0 : fd_bank_vote_states_prev_prev_end_locking_modify( slot_ctx->bank );
305 :
306 0 : }
|