Line data Source code
1 : #include "fd_exec_slot_ctx.h"
2 : #include "../sysvar/fd_sysvar_epoch_schedule.h"
3 :
4 : #include <assert.h>
5 : #include <time.h>
6 :
7 : void *
8 3 : fd_exec_slot_ctx_new( void * mem ) {
9 :
10 3 : if( FD_UNLIKELY( !mem ) ) {
11 0 : FD_LOG_WARNING(( "NULL mem" ));
12 0 : return NULL;
13 0 : }
14 :
15 3 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)mem, FD_EXEC_SLOT_CTX_ALIGN ) ) ) {
16 0 : FD_LOG_WARNING(( "misaligned mem" ));
17 0 : return NULL;
18 0 : }
19 :
20 3 : fd_memset( mem, 0, sizeof(fd_exec_slot_ctx_t) );
21 :
22 3 : fd_exec_slot_ctx_t * self = (fd_exec_slot_ctx_t *)mem;
23 :
24 3 : FD_COMPILER_MFENCE();
25 3 : self->magic = FD_EXEC_SLOT_CTX_MAGIC;
26 3 : FD_COMPILER_MFENCE();
27 :
28 3 : return mem;
29 3 : }
30 :
31 : fd_exec_slot_ctx_t *
32 3 : fd_exec_slot_ctx_join( void * mem ) {
33 3 : if( FD_UNLIKELY( !mem ) ) {
34 0 : FD_LOG_WARNING(( "NULL block" ));
35 0 : return NULL;
36 0 : }
37 :
38 3 : fd_exec_slot_ctx_t * ctx = (fd_exec_slot_ctx_t *) mem;
39 :
40 3 : if( FD_UNLIKELY( ctx->magic!=FD_EXEC_SLOT_CTX_MAGIC ) ) {
41 0 : FD_LOG_WARNING(( "bad magic" ));
42 0 : return NULL;
43 0 : }
44 :
45 3 : return ctx;
46 3 : }
47 :
48 : void *
49 3 : fd_exec_slot_ctx_leave( fd_exec_slot_ctx_t * ctx) {
50 3 : if( FD_UNLIKELY( !ctx ) ) {
51 0 : FD_LOG_WARNING(( "NULL block" ));
52 0 : return NULL;
53 0 : }
54 :
55 3 : if( FD_UNLIKELY( ctx->magic!=FD_EXEC_SLOT_CTX_MAGIC ) ) {
56 0 : FD_LOG_WARNING(( "bad magic" ));
57 0 : return NULL;
58 0 : }
59 :
60 3 : return (void *) ctx;
61 3 : }
62 :
63 : void *
64 0 : fd_exec_slot_ctx_delete( void * mem ) {
65 0 : if( FD_UNLIKELY( !mem ) ) {
66 0 : FD_LOG_WARNING(( "NULL mem" ));
67 0 : return NULL;
68 0 : }
69 :
70 0 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)mem, FD_EXEC_SLOT_CTX_ALIGN) ) ) {
71 0 : FD_LOG_WARNING(( "misaligned mem" ));
72 0 : return NULL;
73 0 : }
74 :
75 0 : fd_exec_slot_ctx_t * hdr = (fd_exec_slot_ctx_t *)mem;
76 0 : if( FD_UNLIKELY( hdr->magic!=FD_EXEC_SLOT_CTX_MAGIC ) ) {
77 0 : FD_LOG_WARNING(( "bad magic" ));
78 0 : return NULL;
79 0 : }
80 :
81 0 : FD_COMPILER_MFENCE();
82 0 : FD_VOLATILE( hdr->magic ) = 0UL;
83 0 : FD_COMPILER_MFENCE();
84 :
85 0 : return mem;
86 0 : }
87 :
88 : fd_exec_slot_ctx_t *
89 : fd_exec_slot_ctx_recover( fd_exec_slot_ctx_t * slot_ctx,
90 0 : fd_solana_manifest_global_t const * manifest ) {
91 0 : ulong stakes_sz = fd_stakes_size_global( &manifest->bank.stakes );
92 0 : fd_stakes_global_t * stakes = fd_bank_stakes_locking_modify( slot_ctx->bank );
93 0 : fd_memcpy( stakes, &manifest->bank.stakes, stakes_sz );
94 : /* Verify stakes */
95 :
96 0 : fd_bank_stakes_end_locking_modify( slot_ctx->bank );
97 :
98 : /* Move EpochStakes */
99 0 : do {
100 0 : ulong epoch = fd_bank_epoch_get( slot_ctx->bank );
101 :
102 : /* We need to save the vote accounts for the current epoch and the next
103 : epoch as it is used to calculate the leader schedule at the epoch
104 : boundary. */
105 :
106 0 : fd_vote_accounts_global_t * vote_accounts_curr_stakes = NULL;
107 0 : fd_vote_accounts_global_t * vote_accounts_next_stakes = NULL;
108 :
109 0 : fd_epoch_epoch_stakes_pair_global_t * versioned_bank_epoch_stakes = fd_versioned_bank_epoch_stakes_join( &manifest->bank );
110 0 : for( ulong i=0UL; i<manifest->bank.epoch_stakes_len; i++ ) {
111 0 : if( versioned_bank_epoch_stakes[i].key == epoch ) {
112 0 : vote_accounts_curr_stakes = &versioned_bank_epoch_stakes[i].value.stakes.vote_accounts;
113 0 : }
114 0 : if( versioned_bank_epoch_stakes[i].key == epoch+1UL ) {
115 0 : vote_accounts_next_stakes = &versioned_bank_epoch_stakes[i].value.stakes.vote_accounts;
116 0 : }
117 :
118 : /* When loading from a snapshot, Agave's stake caches mean that we have to special-case the epoch stakes
119 : that are used for the second epoch E+2 after the snapshot epoch E.
120 :
121 : If the snapshot contains the epoch stakes for E+2, we should use those.
122 :
123 : If the snapshot does not, we should use the stakes at the end of the E-1 epoch, instead of E-2 as we do for
124 : all other epochs. */
125 0 : }
126 :
127 0 : fd_versioned_epoch_stakes_pair_global_t * versioned_epoch_stakes = fd_solana_manifest_versioned_epoch_stakes_join( manifest );
128 0 : for( ulong i=0UL; i<manifest->versioned_epoch_stakes_len; i++ ) {
129 :
130 0 : if( versioned_epoch_stakes[i].epoch == epoch ) {
131 0 : vote_accounts_curr_stakes = &versioned_epoch_stakes[i].val.inner.Current.stakes.vote_accounts;
132 0 : }
133 0 : if( versioned_epoch_stakes[i].epoch == epoch+1UL ) {
134 0 : vote_accounts_next_stakes = &versioned_epoch_stakes[i].val.inner.Current.stakes.vote_accounts;
135 :
136 : /* Save the initial value to be used for the get_epoch_stake
137 : syscall.
138 :
139 : A note on Agave's indexing scheme for their epoch_stakes
140 : structure:
141 :
142 : https://github.com/anza-xyz/agave/blob/v2.2.14/runtime/src/bank.rs#L6175
143 :
144 : If we are loading a snapshot and replaying in the middle of
145 : epoch 7, the syscall is supposed to return the total stake at
146 : the end of epoch 6. The epoch_stakes structure is indexed in
147 : Agave by the epoch number of the leader schedule that the
148 : stakes are meant to determine. For instance, to get the
149 : stakes at the end of epoch 6, we should query by 8, because
150 : the leader schedule for epoch 8 is determined based on the
151 : stakes at the end of epoch 6. Therefore, we save the total
152 : epoch stake by querying for epoch+1. */
153 0 : fd_bank_total_epoch_stake_set( slot_ctx->bank, versioned_epoch_stakes[i].val.inner.Current.total_stake );
154 0 : }
155 0 : }
156 :
157 0 : fd_bank_use_prev_epoch_stake_set( slot_ctx->bank, epoch + 2UL );
158 :
159 0 : fd_vote_accounts_pair_global_t_mapnode_t * vote_accounts_curr_stakes_pool = fd_vote_accounts_vote_accounts_pool_join( vote_accounts_curr_stakes );
160 0 : fd_vote_accounts_pair_global_t_mapnode_t * vote_accounts_curr_stakes_root = fd_vote_accounts_vote_accounts_root_join( vote_accounts_curr_stakes );
161 :
162 0 : fd_vote_accounts_pair_global_t_mapnode_t * vote_accounts_next_stakes_pool = fd_vote_accounts_vote_accounts_pool_join( vote_accounts_next_stakes );
163 0 : fd_vote_accounts_pair_global_t_mapnode_t * vote_accounts_next_stakes_root = fd_vote_accounts_vote_accounts_root_join( vote_accounts_next_stakes );
164 :
165 0 : if( FD_UNLIKELY( (!vote_accounts_curr_stakes_pool) | (!vote_accounts_next_stakes_pool) ) ) {
166 0 : FD_LOG_WARNING(( "snapshot missing EpochStakes for epochs %lu and/or %lu", epoch, epoch+1UL ));
167 0 : return 0;
168 0 : }
169 :
170 : /* Move current EpochStakes */
171 :
172 0 : fd_vote_accounts_global_t * epoch_stakes = fd_bank_epoch_stakes_locking_modify( slot_ctx->bank );
173 0 : uchar * epoch_stakes_pool_mem = (uchar *)fd_ulong_align_up( (ulong)epoch_stakes + sizeof(fd_vote_accounts_global_t), fd_vote_accounts_pair_global_t_map_align() );
174 0 : fd_vote_accounts_pair_global_t_mapnode_t * epoch_stakes_pool = fd_vote_accounts_pair_global_t_map_join( fd_vote_accounts_pair_global_t_map_new( epoch_stakes_pool_mem, 50000UL ) );
175 0 : fd_vote_accounts_pair_global_t_mapnode_t * epoch_stakes_root = NULL;
176 :
177 0 : uchar * acc_region_curr = (uchar *)fd_ulong_align_up( (ulong)epoch_stakes_pool + fd_vote_accounts_pair_global_t_map_footprint( 50000UL ), 8UL );
178 :
179 0 : for( fd_vote_accounts_pair_global_t_mapnode_t * n = fd_vote_accounts_pair_global_t_map_minimum(
180 0 : vote_accounts_curr_stakes_pool,
181 0 : vote_accounts_curr_stakes_root );
182 0 : n;
183 0 : n = fd_vote_accounts_pair_global_t_map_successor( vote_accounts_curr_stakes_pool, n ) ) {
184 :
185 0 : fd_vote_accounts_pair_global_t_mapnode_t * elem = fd_vote_accounts_pair_global_t_map_acquire(
186 0 : epoch_stakes_pool );
187 0 : FD_TEST( elem );
188 :
189 0 : elem->elem.stake = n->elem.stake;
190 0 : elem->elem.key = n->elem.key;
191 :
192 0 : elem->elem.value.lamports = n->elem.value.lamports;
193 0 : elem->elem.value.data_len = 0UL;
194 0 : elem->elem.value.data_offset = 0UL;
195 0 : elem->elem.value.owner = n->elem.value.owner;
196 0 : elem->elem.value.executable = n->elem.value.executable;
197 0 : elem->elem.value.rent_epoch = n->elem.value.rent_epoch;
198 :
199 0 : elem->elem.value.data_offset = (ulong)(acc_region_curr - (uchar *)&elem->elem.value);
200 0 : elem->elem.value.data_len = n->elem.value.data_len;
201 :
202 0 : uchar * manifest_data = fd_solana_account_data_join( &n->elem.value );
203 0 : memcpy( acc_region_curr, manifest_data, n->elem.value.data_len );
204 0 : acc_region_curr += n->elem.value.data_len;
205 :
206 0 : fd_vote_accounts_pair_global_t_map_insert(
207 0 : epoch_stakes_pool,
208 0 : &epoch_stakes_root,
209 0 : elem );
210 0 : }
211 :
212 0 : fd_vote_accounts_vote_accounts_pool_update( epoch_stakes, epoch_stakes_pool );
213 0 : fd_vote_accounts_vote_accounts_root_update( epoch_stakes, epoch_stakes_root );
214 0 : fd_bank_epoch_stakes_end_locking_modify( slot_ctx->bank );
215 :
216 : /* Move next EpochStakes */
217 :
218 0 : fd_vote_accounts_global_t * next_epoch_stakes = fd_bank_next_epoch_stakes_locking_modify( slot_ctx->bank );
219 0 : uchar * next_epoch_stakes_pool_mem = (uchar *)fd_ulong_align_up( (ulong)next_epoch_stakes + sizeof(fd_vote_accounts_global_t), fd_vote_accounts_pair_global_t_map_align() );
220 0 : fd_vote_accounts_pair_global_t_mapnode_t * next_epoch_stakes_pool = fd_vote_accounts_pair_global_t_map_join( fd_vote_accounts_pair_global_t_map_new( next_epoch_stakes_pool_mem, 50000UL ) );
221 0 : fd_vote_accounts_pair_global_t_mapnode_t * next_epoch_stakes_root = NULL;
222 :
223 0 : fd_vote_accounts_pair_global_t_mapnode_t * pool = vote_accounts_next_stakes_pool;
224 0 : fd_vote_accounts_pair_global_t_mapnode_t * root = vote_accounts_next_stakes_root;
225 :
226 0 : acc_region_curr = (uchar *)fd_ulong_align_up( (ulong)next_epoch_stakes_pool + fd_vote_accounts_pair_global_t_map_footprint( 50000UL ), 8UL );
227 :
228 0 : for( fd_vote_accounts_pair_global_t_mapnode_t * n = fd_vote_accounts_pair_global_t_map_minimum( pool, root );
229 0 : n;
230 0 : n = fd_vote_accounts_pair_global_t_map_successor( pool, n ) ) {
231 :
232 0 : fd_vote_accounts_pair_global_t_mapnode_t * elem = fd_vote_accounts_pair_global_t_map_acquire( next_epoch_stakes_pool );
233 0 : FD_TEST( elem );
234 :
235 0 : elem->elem.stake = n->elem.stake;
236 0 : elem->elem.key = n->elem.key;
237 :
238 0 : elem->elem.value.lamports = n->elem.value.lamports;
239 0 : elem->elem.value.data_len = 0UL;
240 0 : elem->elem.value.data_offset = 0UL;
241 0 : elem->elem.value.owner = n->elem.value.owner;
242 0 : elem->elem.value.executable = n->elem.value.executable;
243 0 : elem->elem.value.rent_epoch = n->elem.value.rent_epoch;
244 :
245 0 : elem->elem.value.data_offset = (ulong)(acc_region_curr - (uchar *)&elem->elem.value);;
246 0 : elem->elem.value.data_len = n->elem.value.data_len;
247 :
248 0 : uchar * manifest_data = fd_solana_account_data_join( &n->elem.value );
249 0 : memcpy( acc_region_curr, manifest_data, n->elem.value.data_len );
250 0 : acc_region_curr += n->elem.value.data_len;
251 :
252 0 : fd_vote_accounts_pair_global_t_map_insert(
253 0 : next_epoch_stakes_pool,
254 0 : &next_epoch_stakes_root,
255 0 : elem );
256 :
257 0 : }
258 0 : fd_vote_accounts_vote_accounts_pool_update( next_epoch_stakes, next_epoch_stakes_pool );
259 0 : fd_vote_accounts_vote_accounts_root_update( next_epoch_stakes, next_epoch_stakes_root );
260 0 : fd_bank_next_epoch_stakes_end_locking_modify( slot_ctx->bank );
261 :
262 0 : } while(0);
263 :
264 0 : return slot_ctx;
265 0 : }
266 :
267 : fd_exec_slot_ctx_t *
268 : fd_exec_slot_ctx_recover_status_cache( fd_exec_slot_ctx_t * ctx,
269 : fd_bank_slot_deltas_t * slot_deltas,
270 0 : fd_spad_t * runtime_spad ) {
271 :
272 0 : fd_txncache_t * status_cache = ctx->status_cache;
273 0 : if( !status_cache ) {
274 0 : FD_LOG_WARNING(("No status cache in slot ctx"));
275 0 : return NULL;
276 0 : }
277 :
278 0 : FD_SPAD_FRAME_BEGIN( runtime_spad ) {
279 :
280 0 : ulong num_entries = 0;
281 0 : for( ulong i = 0; i < slot_deltas->slot_deltas_len; i++ ) {
282 0 : fd_slot_delta_t * slot_delta = &slot_deltas->slot_deltas[i];
283 0 : for( ulong j = 0; j < slot_delta->slot_delta_vec_len; j++ ) {
284 0 : num_entries += slot_delta->slot_delta_vec[j].value.statuses_len;
285 0 : }
286 0 : }
287 0 : fd_txncache_insert_t * insert_vals = fd_spad_alloc_check( runtime_spad, alignof(fd_txncache_insert_t), num_entries * sizeof(fd_txncache_insert_t) );
288 :
289 : /* Dumb sort for 300 slot entries to insert in order. */
290 0 : fd_slot_delta_t ** deltas = fd_spad_alloc_check( runtime_spad, alignof(fd_slot_delta_t*), slot_deltas->slot_deltas_len * sizeof(fd_slot_delta_t*) );
291 :
292 0 : long curr = -1;
293 0 : for( ulong i = 0UL; i < slot_deltas->slot_deltas_len; i++ ) {
294 0 : ulong curr_min = ULONG_MAX;
295 0 : ulong curr_min_idx = ULONG_MAX;
296 0 : for( ulong j = 0; j < slot_deltas->slot_deltas_len; j++ ) {
297 0 : fd_slot_delta_t * slot_delta = &slot_deltas->slot_deltas[j];
298 0 : if( (long)slot_delta->slot <= curr ) continue;
299 :
300 0 : if( curr_min > slot_delta->slot ) {
301 0 : curr_min = slot_delta->slot;
302 0 : curr_min_idx = j;
303 0 : }
304 0 : }
305 0 : deltas[i] = &slot_deltas->slot_deltas[curr_min_idx];
306 0 : curr = (long)slot_deltas->slot_deltas[curr_min_idx].slot;
307 0 : }
308 :
309 0 : ulong idx = 0;
310 0 : for( ulong i = 0; i < slot_deltas->slot_deltas_len; i++ ) {
311 0 : fd_slot_delta_t * slot_delta = deltas[i];
312 0 : ulong slot = slot_delta->slot;
313 0 : if( slot_delta->is_root ) {
314 0 : fd_txncache_register_root_slot( ctx->status_cache, slot );
315 0 : }
316 0 : for( ulong j = 0; j < slot_delta->slot_delta_vec_len; j++ ) {
317 0 : fd_status_pair_t * pair = &slot_delta->slot_delta_vec[j];
318 0 : fd_hash_t * blockhash = &pair->hash;
319 0 : uchar * results = fd_spad_alloc( runtime_spad, FD_SPAD_ALIGN, pair->value.statuses_len );
320 0 : for( ulong k = 0; k < pair->value.statuses_len; k++ ) {
321 0 : fd_cache_status_t * status = &pair->value.statuses[k];
322 0 : uchar * result = results + k;
323 0 : *result = (uchar)status->result.discriminant;
324 0 : insert_vals[idx++] = (fd_txncache_insert_t){
325 0 : .blockhash = blockhash->uc,
326 0 : .slot = slot,
327 0 : .txnhash = status->key_slice,
328 0 : .result = result
329 0 : };
330 0 : }
331 0 : }
332 0 : }
333 0 : fd_txncache_insert_batch( ctx->status_cache, insert_vals, num_entries );
334 :
335 0 : for( ulong i = 0; i < slot_deltas->slot_deltas_len; i++ ) {
336 0 : fd_slot_delta_t * slot_delta = deltas[i];
337 0 : ulong slot = slot_delta->slot;
338 0 : for( ulong j = 0; j < slot_delta->slot_delta_vec_len; j++ ) {
339 0 : fd_status_pair_t * pair = &slot_delta->slot_delta_vec[j];
340 0 : fd_hash_t * blockhash = &pair->hash;
341 0 : fd_txncache_set_txnhash_offset( ctx->status_cache, slot, blockhash->uc, pair->value.txn_idx );
342 0 : }
343 0 : }
344 :
345 0 : } FD_SPAD_FRAME_END;
346 0 : return ctx;
347 0 : }
348 :
349 : ulong
350 15 : fd_bank_epoch_get( fd_bank_t const * bank ) {
351 15 : fd_epoch_schedule_t epoch_schedule = fd_bank_epoch_schedule_get( bank );
352 : return fd_slot_to_epoch( &epoch_schedule, fd_bank_slot_get( bank ), NULL );
353 15 : }
|