Line data Source code
1 : #include "fd_stake_delegations.h"
2 : #include "../fd_rwlock.h"
3 : #include "fd_stakes.h"
4 : #include "../runtime/sysvar/fd_sysvar_stake_history.h"
5 : #include "../events/fd_event_runtime.h"
6 : #include "../../util/fd_hash32.h"
7 :
8 : #include <errno.h>
9 : #include <unistd.h>
10 :
11 : struct fd_stake_delegations {
12 : ulong magic;
13 : ulong seed_;
14 : ulong max_stake_accounts_;
15 :
16 : /* Root map + pool */
17 : ulong map_offset_;
18 : ulong pool_offset_;
19 : ulong pool_idx_wmk_; /* One past the highest root pool index ever acquired. Every index in
20 : [0, wmk) has been acquired at least once, so its in_use byte is
21 : well defined. */
22 :
23 : /* Delta map + pool and fork pool */
24 : ulong delta_pool_offset_;
25 : ulong fork_pool_offset_;
26 : ulong delta_map_offset_;
27 :
28 : /* Guards every mutating operation on the struct. */
29 : fd_rwlock_t lock;
30 :
31 : /* File descriptor number for this instance's backing file.
32 : Every process joining this object must map the same file at this
33 : descriptor number for the object's lifetime. */
34 : int disk_fd_;
35 :
36 : /* Management for stake delegations that spill to disk. */
37 : ulong max_disk_records_; /* disk delta capacity; disk root capacity is derived */
38 : ulong disk_root_cnt_; /* number of disk-backed root records */
39 : ulong disk_delta_cnt_;
40 : ulong disk_bucket_cnt_; /* pow2 buckets in each disk hash index */
41 : ulong disk_root_tombstone_cnt_;
42 : ulong disk_delta_tombstone_cnt_;
43 : uint disk_root_gen_; /* buckets with an older generation are empty */
44 : uint disk_delta_gen_;
45 :
46 : /* Stake totals for the current root. */
47 : ulong effective_stake;
48 : ulong activating_stake;
49 : ulong deactivating_stake;
50 :
51 : /* Epoch and target fork of the active frontier query. */
52 : ulong frontier_query_epoch;
53 : ushort frontier_query_fork;
54 :
55 : /* Only relevant around upgrade_bpf_stake_program_to_v5_1 activation.
56 : See comment at consumer of this flag for why it's needed. Remove
57 : after the feature activates on all clusters. */
58 : uchar fp_warmed_awarded;
59 : };
60 :
61 : /* map/pool pair to represent in-memory state */
62 : #define POOL_NAME root_pool
63 24 : #define POOL_T fd_stake_delegation_t
64 0 : #define POOL_NEXT next_
65 : #define POOL_IDX_T uint
66 : #define POOL_LAZY 1
67 : #include "../../util/tmpl/fd_pool.c"
68 :
69 : #define MAP_NAME root_map
70 : #define MAP_KEY_T fd_pubkey_t
71 : #define MAP_ELE_T fd_stake_delegation_t
72 1074 : #define MAP_KEY stake_account
73 275 : #define MAP_KEY_EQ(k0,k1) (fd_pubkey_eq( k0, k1 ))
74 2422 : #define MAP_KEY_HASH(key,seed) (fd_hash32( key->uc, seed ))
75 1075 : #define MAP_NEXT next_
76 4397 : #define MAP_IDX_T uint
77 : #include "../../util/tmpl/fd_map_chain.c"
78 :
79 : /* pool/map pair to represent in-memory delta state */
80 : #define POOL_NAME delta_pool
81 24 : #define POOL_T fd_stake_delegation_t
82 136 : #define POOL_NEXT next_
83 : #define POOL_IDX_T uint
84 : #define POOL_LAZY 1
85 : #include "../../util/tmpl/fd_pool.c"
86 :
87 : #define MAP_NAME delta_map
88 : #define MAP_KEY_T fd_stake_delegation_key_t
89 : #define MAP_ELE_T fd_stake_delegation_t
90 136 : #define MAP_KEY key
91 137 : #define MAP_KEY_EQ(k0,k1) ((k0)->fork_idx==(k1)->fork_idx && fd_pubkey_eq( &(k0)->stake_account, &(k1)->stake_account ))
92 408 : #define MAP_KEY_HASH(key,seed) (fd_hash32( (key)->stake_account.uc, (seed) ^ (ulong)(key)->fork_idx ))
93 273 : #define MAP_NEXT next_
94 1171 : #define MAP_IDX_T uint
95 : #include "../../util/tmpl/fd_map_chain.c"
96 :
97 : /* Pool/map pair for fork ids. Just holds metadata. */
98 : struct fork_pool_ele {
99 : ushort next;
100 : ushort parent;
101 : uint delta_head;
102 : uint disk_delta_head;
103 : };
104 : typedef struct fork_pool_ele fork_pool_ele_t;
105 :
106 : FD_STATIC_ASSERT( sizeof(fork_pool_ele_t)==12UL, fork_pool_ele_size );
107 :
108 : /* A live parent is a fork index or USHORT_MAX for the rooted state. */
109 1880 : #define FORK_PARENT_FREE ((ushort)FD_STAKE_DELEGATIONS_FORK_MAX)
110 : FD_STATIC_ASSERT( FD_STAKE_DELEGATIONS_FORK_MAX<USHORT_MAX, fork_idx_width );
111 :
112 : #define POOL_NAME fork_pool
113 24 : #define POOL_T fork_pool_ele_t
114 : #define POOL_IDX_T ushort
115 : #include "../../util/tmpl/fd_pool.c"
116 :
117 : /* Internal getters for base map + pool */
118 :
119 : static inline fd_stake_delegation_t *
120 4488 : get_root_pool( fd_stake_delegations_t const * stake_delegations ) {
121 4488 : return fd_type_pun( (uchar *)stake_delegations + stake_delegations->pool_offset_ );
122 4488 : }
123 :
124 : static inline root_map_t *
125 3036 : get_root_map( fd_stake_delegations_t const * stake_delegations ) {
126 3036 : return fd_type_pun( (uchar *)stake_delegations + stake_delegations->map_offset_ );
127 3036 : }
128 :
129 : /* Internal getters for delta pool + fork structures */
130 :
131 : static inline fd_stake_delegation_t *
132 3694 : get_delta_pool( fd_stake_delegations_t const * stake_delegations ) {
133 3694 : return fd_type_pun( (uchar *)stake_delegations + stake_delegations->delta_pool_offset_ );
134 3694 : }
135 :
136 : static inline fork_pool_ele_t *
137 4743 : get_fork_pool( fd_stake_delegations_t const * stake_delegations ) {
138 4743 : return fd_type_pun( (uchar *)stake_delegations + stake_delegations->fork_pool_offset_ );
139 4743 : }
140 :
141 : static inline delta_map_t *
142 1502 : get_delta_map( fd_stake_delegations_t const * stake_delegations ) {
143 1502 : return fd_type_pun( (uchar *)stake_delegations + stake_delegations->delta_map_offset_ );
144 1502 : }
145 :
146 : /* Collect leaf to root; callers process the result in reverse order. */
147 : static ulong
148 : fork_ancestry( fd_stake_delegations_t const * stake_delegations,
149 : ushort fork_idx,
150 1375 : ushort * fork_ids ) {
151 1375 : fork_pool_ele_t const * fork_pool = get_fork_pool( stake_delegations );
152 1375 : ulong max_forks = fork_pool_max( fork_pool );
153 1375 : ulong cnt = 0UL;
154 2751 : for( ; fork_idx!=USHORT_MAX; fork_idx=fork_pool[ fork_idx ].parent ) {
155 1376 : FD_CHECK_CRIT( cnt<max_forks && (ulong)fork_idx<max_forks && fork_pool[ fork_idx ].parent!=FORK_PARENT_FREE,
156 1376 : "invalid stake delegation ancestry" );
157 1376 : fork_ids[ cnt++ ] = fork_idx;
158 1376 : }
159 1375 : return cnt;
160 1375 : }
161 :
162 : /* The spill file contains two compact open-addressed indexes followed
163 : by dense root and delta record arrays. Index generations make reset
164 : O(1). Root and delta indexes are separate so root mutation while
165 : applying a fork cannot disturb traversal of that fork's deltas. */
166 :
167 0 : #define DISK_BUCKET_TOMBSTONE (UINT_MAX-1U)
168 :
169 : struct disk_bucket {
170 : uint idx;
171 : uint gen;
172 : };
173 : typedef struct disk_bucket disk_bucket_t;
174 :
175 : struct disk_delta {
176 : fd_stake_delegation_t delegation;
177 : uint next;
178 : uint prev;
179 : ushort fork_idx;
180 : };
181 : typedef struct disk_delta disk_delta_t;
182 :
183 : FD_STATIC_ASSERT( sizeof(disk_bucket_t)==8UL, disk_bucket );
184 : FD_STATIC_ASSERT( sizeof(disk_delta_t)==128UL, disk_delta );
185 : FD_STATIC_ASSERT( offsetof( disk_delta_t, delegation )== 0UL, disk_delta );
186 : FD_STATIC_ASSERT( offsetof( disk_delta_t, next )==112UL, disk_delta );
187 : FD_STATIC_ASSERT( offsetof( disk_delta_t, prev )==116UL, disk_delta );
188 : FD_STATIC_ASSERT( offsetof( disk_delta_t, fork_idx )==120UL, disk_delta );
189 :
190 : static inline ulong
191 12 : disk_root_record_cap( fd_stake_delegations_t const * stake_delegations ) {
192 12 : return 2UL*stake_delegations->max_disk_records_ + stake_delegations->max_stake_accounts_;
193 12 : }
194 :
195 : static inline ulong
196 0 : disk_delta_bucket_off( fd_stake_delegations_t const * stake_delegations ) {
197 0 : return stake_delegations->disk_bucket_cnt_*sizeof(disk_bucket_t);
198 0 : }
199 :
200 : static inline ulong
201 0 : disk_root_record_off( fd_stake_delegations_t const * stake_delegations ) {
202 0 : return fd_ulong_align_up( 2UL*stake_delegations->disk_bucket_cnt_*sizeof(disk_bucket_t), alignof(fd_stake_delegation_t) );
203 0 : }
204 :
205 : static inline ulong
206 0 : disk_delta_record_off( fd_stake_delegations_t const * stake_delegations ) {
207 0 : return fd_ulong_align_up( disk_root_record_off( stake_delegations ) +
208 0 : disk_root_record_cap( stake_delegations )*sizeof(fd_stake_delegation_t),
209 0 : alignof(disk_delta_t) );
210 0 : }
211 :
212 : /* Reads short at EOF zero-fill. The spill file is sparse and is not
213 : required to be pre-sized. */
214 :
215 : static void
216 : disk_read( fd_stake_delegations_t const * stake_delegations,
217 : void * dst,
218 : ulong off,
219 0 : ulong sz ) {
220 0 : ulong got = 0UL;
221 0 : while( got<sz ) {
222 0 : long n = pread( stake_delegations->disk_fd_, (uchar *)dst+got, sz-got, (long)(off+got) );
223 0 : if( FD_UNLIKELY( n<0L ) ) {
224 0 : if( FD_LIKELY( errno==EINTR ) ) continue;
225 0 : FD_LOG_CRIT(( "pread(stake delegation spill file, fd=%d) failed (%i-%s)", stake_delegations->disk_fd_, errno, fd_io_strerror( errno ) ));
226 0 : }
227 0 : if( FD_UNLIKELY( !n ) ) break;
228 0 : got += (ulong)n;
229 0 : }
230 0 : if( FD_UNLIKELY( got<sz ) ) fd_memset( (uchar *)dst+got, 0, sz-got );
231 0 : }
232 :
233 : static void
234 : disk_write( fd_stake_delegations_t const * stake_delegations,
235 : void const * src,
236 : ulong off,
237 0 : ulong sz ) {
238 0 : ulong put = 0UL;
239 0 : while( put<sz ) {
240 0 : long n = pwrite( stake_delegations->disk_fd_, (uchar const *)src+put, sz-put, (long)(off+put) );
241 0 : if( FD_LIKELY( n>0L ) ) { put += (ulong)n; continue; }
242 0 : if( FD_UNLIKELY( n<0L && errno==EINTR ) ) continue;
243 0 : if( FD_UNLIKELY( !n ) ) errno = EIO;
244 0 : FD_LOG_CRIT(( "pwrite(stake delegation spill file, fd=%d) failed (%i-%s)", stake_delegations->disk_fd_, errno, fd_io_strerror( errno ) ));
245 0 : }
246 0 : }
247 :
248 : static inline int
249 : disk_bucket_is_empty( disk_bucket_t const * bucket,
250 0 : uint gen ) {
251 0 : return bucket->gen!=gen;
252 0 : }
253 :
254 : static inline int
255 : disk_bucket_is_tombstone( disk_bucket_t const * bucket,
256 0 : uint gen ) {
257 0 : return bucket->gen==gen && bucket->idx==DISK_BUCKET_TOMBSTONE;
258 0 : }
259 :
260 : static inline ulong
261 : disk_bucket_off( ulong base,
262 0 : ulong bucket_idx ) {
263 0 : return base + bucket_idx*sizeof(disk_bucket_t);
264 0 : }
265 :
266 : static void
267 : disk_bucket_store( fd_stake_delegations_t const * stake_delegations,
268 : ulong base,
269 : ulong bucket_idx,
270 : uint gen,
271 0 : uint idx ) {
272 0 : disk_bucket_t bucket = {
273 0 : .idx = idx,
274 0 : .gen = gen,
275 0 : };
276 0 : disk_write( stake_delegations, &bucket, disk_bucket_off( base, bucket_idx ), sizeof(disk_bucket_t) );
277 0 : }
278 :
279 : static void
280 : disk_root_read( fd_stake_delegations_t const * stake_delegations,
281 : uint idx,
282 0 : fd_stake_delegation_t * delegation ) {
283 0 : disk_read( stake_delegations, delegation,
284 0 : disk_root_record_off( stake_delegations ) + (ulong)idx*sizeof(fd_stake_delegation_t),
285 0 : sizeof(fd_stake_delegation_t) );
286 0 : }
287 :
288 : static void
289 : disk_root_write( fd_stake_delegations_t const * stake_delegations,
290 : uint idx,
291 0 : fd_stake_delegation_t const * delegation ) {
292 0 : disk_write( stake_delegations, delegation,
293 0 : disk_root_record_off( stake_delegations ) + (ulong)idx*sizeof(fd_stake_delegation_t),
294 0 : sizeof(fd_stake_delegation_t) );
295 0 : }
296 :
297 : static void
298 : disk_delta_read( fd_stake_delegations_t const * stake_delegations,
299 : uint idx,
300 0 : disk_delta_t * delta ) {
301 0 : disk_read( stake_delegations, delta,
302 0 : disk_delta_record_off( stake_delegations ) + (ulong)idx*sizeof(disk_delta_t),
303 0 : sizeof(disk_delta_t) );
304 0 : }
305 :
306 : static void
307 : disk_delta_write( fd_stake_delegations_t const * stake_delegations,
308 : uint idx,
309 0 : disk_delta_t const * delta ) {
310 0 : disk_write( stake_delegations, delta,
311 0 : disk_delta_record_off( stake_delegations ) + (ulong)idx*sizeof(disk_delta_t),
312 0 : sizeof(disk_delta_t) );
313 0 : }
314 :
315 : static void
316 : disk_root_find( fd_stake_delegations_t const * stake_delegations,
317 : fd_pubkey_t const * stake_account,
318 : fd_stake_delegation_t * delegation,
319 : uint * idx,
320 : ulong * found_bucket_idx,
321 0 : ulong * free_bucket_idx ) {
322 : /* Default to not found and no reusable bucket. */
323 0 : *idx = UINT_MAX;
324 0 : *found_bucket_idx = ULONG_MAX;
325 0 : *free_bucket_idx = ULONG_MAX;
326 0 : ulong bucket_cnt = stake_delegations->disk_bucket_cnt_;
327 :
328 : /* Hash the stake account to its initial bucket. */
329 0 : ulong bucket_idx = (ulong)fd_hash32( stake_account->uc, stake_delegations->seed_ ) & (bucket_cnt-1UL);
330 0 : ulong base = 0UL;
331 0 : for( ulong probe=0UL; probe<bucket_cnt; probe++ ) {
332 0 : disk_bucket_t bucket;
333 0 : disk_read( stake_delegations, &bucket, disk_bucket_off( base, bucket_idx ), sizeof(disk_bucket_t) );
334 :
335 : /* An empty bucket ends the search and can be reused for insertion. */
336 0 : if( FD_UNLIKELY( disk_bucket_is_empty( &bucket, stake_delegations->disk_root_gen_ ) ) ) {
337 0 : if( *free_bucket_idx==ULONG_MAX ) *free_bucket_idx = bucket_idx;
338 0 : return;
339 0 : }
340 :
341 : /* Remember the first tombstone, but keep searching past it. If the
342 : record doesn't exist already, the tombstone will be used. */
343 0 : if( FD_UNLIKELY( bucket.idx==DISK_BUCKET_TOMBSTONE ) ) {
344 0 : if( *free_bucket_idx==ULONG_MAX ) *free_bucket_idx = bucket_idx;
345 0 : } else {
346 : /* A live bucket points to a record in the dense disk array. */
347 0 : FD_CHECK_CRIT( (ulong)bucket.idx<stake_delegations->disk_root_cnt_, "corrupt stake delegation disk root index" );
348 0 : fd_stake_delegation_t candidate;
349 0 : disk_root_read( stake_delegations, bucket.idx, &candidate );
350 0 : if( FD_UNLIKELY( fd_pubkey_eq( &candidate.stake_account, stake_account ) ) ) {
351 : /* Return the record and both of its disk locations. */
352 0 : if( delegation ) *delegation = candidate;
353 0 : *idx = bucket.idx;
354 0 : *found_bucket_idx = bucket_idx;
355 0 : return;
356 0 : }
357 0 : }
358 :
359 : /* Resolve a collision by advancing, wrapping at the table end. */
360 0 : bucket_idx = (bucket_idx+1UL) & (bucket_cnt-1UL);
361 0 : }
362 0 : }
363 :
364 : struct disk_delta_query {
365 : uint record_idx; /* UINT_MAX if absent */
366 : ulong bucket_idx; /* Matching entry or first reusable slot */
367 : int is_tombstone; /* Reusable slot holds a deleted entry */
368 : };
369 : typedef struct disk_delta_query disk_delta_query_t;
370 :
371 : static void
372 : disk_delta_find( fd_stake_delegations_t const * stake_delegations,
373 : ushort fork_idx,
374 : fd_pubkey_t const * stake_account,
375 : disk_delta_t * delta,
376 0 : disk_delta_query_t * query ) {
377 0 : query->record_idx = UINT_MAX;
378 0 : query->bucket_idx = ULONG_MAX;
379 0 : query->is_tombstone = 0;
380 0 : ulong bucket_cnt = stake_delegations->disk_bucket_cnt_;
381 0 : ulong seed = stake_delegations->seed_ ^ (ulong)fork_idx;
382 0 : ulong bucket_idx = (ulong)fd_hash32( stake_account->uc, seed ) & (bucket_cnt-1UL);
383 0 : ulong base = disk_delta_bucket_off( stake_delegations );
384 0 : for( ulong probe=0UL; probe<bucket_cnt; probe++ ) {
385 0 : disk_bucket_t bucket;
386 0 : disk_read( stake_delegations, &bucket, disk_bucket_off( base, bucket_idx ), sizeof(disk_bucket_t) );
387 0 : if( FD_UNLIKELY( disk_bucket_is_empty( &bucket, stake_delegations->disk_delta_gen_ ) ) ) {
388 0 : if( query->bucket_idx==ULONG_MAX ) query->bucket_idx = bucket_idx;
389 0 : return;
390 0 : }
391 0 : if( FD_UNLIKELY( bucket.idx==DISK_BUCKET_TOMBSTONE ) ) {
392 0 : if( query->bucket_idx==ULONG_MAX ) {
393 0 : query->bucket_idx = bucket_idx;
394 0 : query->is_tombstone = 1;
395 0 : }
396 0 : } else {
397 0 : FD_CHECK_CRIT( (ulong)bucket.idx<stake_delegations->disk_delta_cnt_, "corrupt stake delegation disk delta index" );
398 0 : disk_delta_t candidate;
399 0 : disk_delta_read( stake_delegations, bucket.idx, &candidate );
400 0 : if( FD_UNLIKELY( candidate.fork_idx==fork_idx &&
401 0 : fd_pubkey_eq( &candidate.delegation.stake_account, stake_account ) ) ) {
402 0 : if( delta ) *delta = candidate;
403 0 : query->record_idx = bucket.idx;
404 0 : query->bucket_idx = bucket_idx;
405 0 : query->is_tombstone = 0;
406 0 : return;
407 0 : }
408 0 : }
409 0 : bucket_idx = (bucket_idx+1UL) & (bucket_cnt-1UL);
410 0 : }
411 0 : }
412 :
413 : static inline void
414 0 : disk_delta_record_reserve( fd_stake_delegations_t const * stake_delegations ) {
415 0 : FD_CHECK_CRIT( stake_delegations->disk_delta_cnt_<stake_delegations->max_disk_records_,
416 0 : "stake delegation disk delta spill exhausted" );
417 0 : }
418 :
419 : static inline uint
420 1230 : disk_gen_next( uint gen ) {
421 1230 : FD_CHECK_CRIT( gen<UINT_MAX, "stake delegation disk generation exhausted" );
422 1230 : return gen+1U;
423 1230 : }
424 :
425 : static inline int
426 : disk_index_needs_rebuild( ulong tombstone_cnt,
427 : ulong live_cnt,
428 0 : ulong bucket_cnt ) {
429 0 : return tombstone_cnt>live_cnt || tombstone_cnt>(bucket_cnt>>2);
430 0 : }
431 :
432 : static void
433 0 : disk_root_rebuild( fd_stake_delegations_t * stake_delegations ) {
434 0 : stake_delegations->disk_root_gen_ = disk_gen_next( stake_delegations->disk_root_gen_ );
435 0 : stake_delegations->disk_root_tombstone_cnt_ = 0UL;
436 :
437 0 : for( uint idx=0U; (ulong)idx<stake_delegations->disk_root_cnt_; idx++ ) {
438 0 : fd_stake_delegation_t delegation;
439 0 : disk_root_read( stake_delegations, idx, &delegation );
440 :
441 0 : uint found_idx;
442 0 : ulong found_bucket_idx;
443 0 : ulong free_bucket_idx;
444 0 : disk_root_find( stake_delegations, &delegation.stake_account, NULL, &found_idx, &found_bucket_idx, &free_bucket_idx );
445 0 : FD_CHECK_CRIT( found_idx==UINT_MAX && free_bucket_idx!=ULONG_MAX, "unable to rebuild stake delegation disk root index" );
446 0 : disk_bucket_store( stake_delegations, 0UL, free_bucket_idx,
447 0 : stake_delegations->disk_root_gen_, idx );
448 0 : }
449 0 : }
450 :
451 : static void
452 0 : disk_delta_rebuild( fd_stake_delegations_t * stake_delegations ) {
453 0 : stake_delegations->disk_delta_gen_ = disk_gen_next( stake_delegations->disk_delta_gen_ );
454 0 : stake_delegations->disk_delta_tombstone_cnt_ = 0UL;
455 :
456 0 : for( uint idx=0U; (ulong)idx<stake_delegations->disk_delta_cnt_; idx++ ) {
457 0 : disk_delta_t delta;
458 0 : disk_delta_read( stake_delegations, idx, &delta );
459 :
460 0 : disk_delta_query_t query;
461 0 : disk_delta_find( stake_delegations, delta.fork_idx, &delta.delegation.stake_account, NULL, &query );
462 0 : FD_CHECK_CRIT( query.record_idx==UINT_MAX && query.bucket_idx!=ULONG_MAX, "unable to rebuild stake delegation disk delta index" );
463 0 : disk_bucket_store( stake_delegations, disk_delta_bucket_off( stake_delegations ), query.bucket_idx,
464 0 : stake_delegations->disk_delta_gen_, idx );
465 0 : }
466 0 : }
467 :
468 : static void
469 687 : disk_root_maintain( fd_stake_delegations_t * stake_delegations ) {
470 687 : if( FD_UNLIKELY( !stake_delegations->disk_root_cnt_ ) ) {
471 687 : if( FD_UNLIKELY( stake_delegations->disk_root_tombstone_cnt_ ) ) {
472 0 : stake_delegations->disk_root_gen_ = disk_gen_next( stake_delegations->disk_root_gen_ );
473 0 : stake_delegations->disk_root_tombstone_cnt_ = 0UL;
474 0 : }
475 687 : return;
476 687 : }
477 0 : if( FD_UNLIKELY( stake_delegations->frontier_query_epoch==ULONG_MAX &&
478 0 : disk_index_needs_rebuild( stake_delegations->disk_root_tombstone_cnt_,
479 0 : stake_delegations->disk_root_cnt_,
480 0 : stake_delegations->disk_bucket_cnt_ ) ) ) {
481 0 : disk_root_rebuild( stake_delegations );
482 0 : }
483 0 : }
484 :
485 : static void
486 0 : disk_delta_maintain( fd_stake_delegations_t * stake_delegations ) {
487 0 : if( FD_UNLIKELY( !stake_delegations->disk_delta_cnt_ ) ) {
488 0 : if( FD_UNLIKELY( stake_delegations->disk_delta_tombstone_cnt_ ) ) {
489 0 : stake_delegations->disk_delta_gen_ = disk_gen_next( stake_delegations->disk_delta_gen_ );
490 0 : stake_delegations->disk_delta_tombstone_cnt_ = 0UL;
491 0 : }
492 0 : return;
493 0 : }
494 0 : if( FD_UNLIKELY( disk_index_needs_rebuild( stake_delegations->disk_delta_tombstone_cnt_,
495 0 : stake_delegations->disk_delta_cnt_,
496 0 : stake_delegations->disk_bucket_cnt_ ) ) ) {
497 0 : disk_delta_rebuild( stake_delegations );
498 0 : }
499 0 : }
500 :
501 : static void
502 : disk_root_insert( fd_stake_delegations_t * stake_delegations,
503 0 : fd_stake_delegation_t const * delegation ) {
504 0 : FD_CHECK_CRIT( stake_delegations->disk_root_cnt_<disk_root_record_cap( stake_delegations ),
505 0 : "stake delegation disk root spill exhausted" );
506 :
507 0 : uint idx;
508 0 : ulong found_bucket_idx;
509 0 : ulong free_bucket_idx;
510 0 : disk_root_find( stake_delegations, &delegation->stake_account, NULL, &idx, &found_bucket_idx, &free_bucket_idx );
511 0 : FD_CHECK_CRIT( idx==UINT_MAX, "duplicate stake delegation disk root" );
512 0 : FD_CHECK_CRIT( free_bucket_idx!=ULONG_MAX, "stake delegation disk root index exhausted" );
513 :
514 0 : idx = (uint)stake_delegations->disk_root_cnt_;
515 0 : disk_root_write( stake_delegations, idx, delegation );
516 0 : disk_bucket_t free_bucket;
517 0 : disk_read( stake_delegations, &free_bucket, disk_bucket_off( 0UL, free_bucket_idx ), sizeof(disk_bucket_t) );
518 0 : if( FD_UNLIKELY( disk_bucket_is_tombstone( &free_bucket, stake_delegations->disk_root_gen_ ) ) ) {
519 0 : FD_CHECK_CRIT( stake_delegations->disk_root_tombstone_cnt_, "corrupt stake delegation disk root tombstone count" );
520 0 : stake_delegations->disk_root_tombstone_cnt_--;
521 0 : }
522 0 : disk_bucket_store( stake_delegations, 0UL, free_bucket_idx,
523 0 : stake_delegations->disk_root_gen_, idx );
524 0 : stake_delegations->disk_root_cnt_++;
525 0 : }
526 :
527 : static void
528 : disk_root_remove( fd_stake_delegations_t * stake_delegations,
529 : uint idx,
530 0 : ulong bucket_idx ) {
531 0 : FD_CHECK_CRIT( (ulong)idx<stake_delegations->disk_root_cnt_, "invalid stake delegation disk root removal" );
532 0 : disk_bucket_store( stake_delegations, 0UL, bucket_idx,
533 0 : stake_delegations->disk_root_gen_, DISK_BUCKET_TOMBSTONE );
534 :
535 0 : uint last = (uint)(stake_delegations->disk_root_cnt_-1UL);
536 0 : if( FD_UNLIKELY( idx!=last ) ) {
537 0 : fd_stake_delegation_t moved;
538 0 : disk_root_read( stake_delegations, last, &moved );
539 0 : disk_root_write( stake_delegations, idx, &moved );
540 :
541 0 : uint moved_idx;
542 0 : ulong moved_bucket_idx;
543 0 : ulong free_bucket_idx;
544 0 : disk_root_find( stake_delegations, &moved.stake_account, NULL, &moved_idx, &moved_bucket_idx, &free_bucket_idx );
545 0 : FD_CHECK_CRIT( moved_idx==last, "missing moved stake delegation disk root" );
546 0 : disk_bucket_store( stake_delegations, 0UL, moved_bucket_idx,
547 0 : stake_delegations->disk_root_gen_, idx );
548 0 : }
549 0 : stake_delegations->disk_root_cnt_--;
550 0 : stake_delegations->disk_root_tombstone_cnt_++;
551 0 : disk_root_maintain( stake_delegations );
552 0 : }
553 :
554 : static void
555 : disk_delta_insert( fd_stake_delegations_t * stake_delegations,
556 : ushort fork_idx,
557 : fd_stake_delegation_t const * delegation,
558 0 : disk_delta_query_t const * query ) {
559 0 : disk_delta_record_reserve( stake_delegations );
560 :
561 0 : FD_CHECK_CRIT( query->bucket_idx!=ULONG_MAX, "stake delegation disk delta index exhausted" );
562 :
563 0 : uint idx = (uint)stake_delegations->disk_delta_cnt_;
564 0 : fork_pool_ele_t * fork = get_fork_pool( stake_delegations ) + fork_idx;
565 0 : disk_delta_t delta = {
566 0 : .delegation = *delegation,
567 0 : .next = fork->disk_delta_head,
568 0 : .prev = UINT_MAX,
569 0 : .fork_idx = fork_idx,
570 0 : };
571 0 : disk_delta_write( stake_delegations, idx, &delta );
572 0 : if( FD_LIKELY( delta.next!=UINT_MAX ) ) {
573 0 : disk_write( stake_delegations, &idx,
574 0 : disk_delta_record_off( stake_delegations ) + (ulong)delta.next*sizeof(disk_delta_t) + offsetof(disk_delta_t, prev),
575 0 : sizeof(uint) );
576 0 : }
577 0 : if( FD_UNLIKELY( query->is_tombstone ) ) {
578 0 : FD_CHECK_CRIT( stake_delegations->disk_delta_tombstone_cnt_, "corrupt stake delegation disk delta tombstone count" );
579 0 : stake_delegations->disk_delta_tombstone_cnt_--;
580 0 : }
581 0 : disk_bucket_store( stake_delegations, disk_delta_bucket_off( stake_delegations ), query->bucket_idx,
582 0 : stake_delegations->disk_delta_gen_, idx );
583 0 : fork->disk_delta_head = idx;
584 0 : stake_delegations->disk_delta_cnt_++;
585 0 : }
586 :
587 : static void
588 : disk_delta_remove( fd_stake_delegations_t * stake_delegations,
589 : uint idx,
590 0 : disk_delta_t const * removed ) {
591 0 : FD_CHECK_CRIT( (ulong)idx<stake_delegations->disk_delta_cnt_, "invalid stake delegation disk delta removal" );
592 :
593 0 : fork_pool_ele_t * fork = get_fork_pool( stake_delegations ) + removed->fork_idx;
594 0 : if( FD_LIKELY( removed->prev!=UINT_MAX ) ) {
595 0 : disk_write( stake_delegations, &removed->next,
596 0 : disk_delta_record_off( stake_delegations ) + (ulong)removed->prev*sizeof(disk_delta_t) + offsetof(disk_delta_t, next),
597 0 : sizeof(uint) );
598 0 : } else {
599 0 : fork->disk_delta_head = removed->next;
600 0 : }
601 0 : if( FD_LIKELY( removed->next!=UINT_MAX ) ) {
602 0 : disk_write( stake_delegations, &removed->prev,
603 0 : disk_delta_record_off( stake_delegations ) + (ulong)removed->next*sizeof(disk_delta_t) + offsetof(disk_delta_t, prev),
604 0 : sizeof(uint) );
605 0 : }
606 :
607 0 : disk_delta_query_t query;
608 0 : disk_delta_find( stake_delegations, removed->fork_idx, &removed->delegation.stake_account, NULL, &query );
609 0 : FD_CHECK_CRIT( query.record_idx==idx, "missing stake delegation disk delta removal" );
610 0 : disk_bucket_store( stake_delegations, disk_delta_bucket_off( stake_delegations ), query.bucket_idx,
611 0 : stake_delegations->disk_delta_gen_, DISK_BUCKET_TOMBSTONE );
612 :
613 0 : uint last = (uint)(stake_delegations->disk_delta_cnt_-1UL);
614 0 : if( FD_UNLIKELY( idx!=last ) ) {
615 0 : disk_delta_t moved;
616 0 : disk_delta_read( stake_delegations, last, &moved );
617 0 : disk_delta_write( stake_delegations, idx, &moved );
618 :
619 0 : disk_delta_find( stake_delegations, moved.fork_idx, &moved.delegation.stake_account, NULL, &query );
620 0 : FD_CHECK_CRIT( query.record_idx==last, "missing moved stake delegation disk delta" );
621 0 : disk_bucket_store( stake_delegations, disk_delta_bucket_off( stake_delegations ), query.bucket_idx,
622 0 : stake_delegations->disk_delta_gen_, idx );
623 :
624 0 : fork_pool_ele_t * moved_fork = get_fork_pool( stake_delegations ) + moved.fork_idx;
625 0 : if( FD_LIKELY( moved.prev!=UINT_MAX ) ) {
626 0 : disk_write( stake_delegations, &idx,
627 0 : disk_delta_record_off( stake_delegations ) + (ulong)moved.prev*sizeof(disk_delta_t) + offsetof(disk_delta_t, next),
628 0 : sizeof(uint) );
629 0 : } else {
630 0 : moved_fork->disk_delta_head = idx;
631 0 : }
632 0 : if( FD_LIKELY( moved.next!=UINT_MAX ) ) {
633 0 : disk_write( stake_delegations, &idx,
634 0 : disk_delta_record_off( stake_delegations ) + (ulong)moved.next*sizeof(disk_delta_t) + offsetof(disk_delta_t, prev),
635 0 : sizeof(uint) );
636 0 : }
637 0 : }
638 0 : stake_delegations->disk_delta_cnt_--;
639 0 : stake_delegations->disk_delta_tombstone_cnt_++;
640 0 : disk_delta_maintain( stake_delegations );
641 0 : }
642 :
643 : ulong
644 384 : fd_stake_delegations_align( void ) {
645 384 : return FD_STAKE_DELEGATIONS_ALIGN;
646 384 : }
647 :
648 : static inline ulong
649 72 : fd_stake_delegations_delta_pool_ele_max( ulong root_ele_max ) {
650 72 : return fd_ulong_max( root_ele_max/FD_STAKE_DELEGATIONS_DELTA_POOL_DIVISOR, 1UL );
651 72 : }
652 :
653 : ulong
654 : fd_stake_delegations_footprint( ulong max_stake_accounts,
655 60 : ulong max_live_slots ) {
656 60 : ulong map_chain_cnt = root_map_chain_cnt_est( max_stake_accounts );
657 60 : ulong delta_ele_max = fd_stake_delegations_delta_pool_ele_max( max_stake_accounts );
658 60 : ulong delta_map_chain_cnt = delta_map_chain_cnt_est( delta_ele_max );
659 :
660 60 : ulong l = FD_LAYOUT_INIT;
661 60 : l = FD_LAYOUT_APPEND( l, fd_stake_delegations_align(), sizeof(fd_stake_delegations_t) );
662 60 : l = FD_LAYOUT_APPEND( l, root_pool_align(), root_pool_footprint( max_stake_accounts ) );
663 60 : l = FD_LAYOUT_APPEND( l, root_map_align(), root_map_footprint( map_chain_cnt ) );
664 60 : l = FD_LAYOUT_APPEND( l, delta_pool_align(), delta_pool_footprint( delta_ele_max ) );
665 60 : l = FD_LAYOUT_APPEND( l, fork_pool_align(), fork_pool_footprint( max_live_slots ) );
666 60 : l = FD_LAYOUT_APPEND( l, delta_map_align(), delta_map_footprint( delta_map_chain_cnt ) );
667 :
668 60 : return FD_LAYOUT_FINI( l, fd_stake_delegations_align() );
669 60 : }
670 :
671 : void *
672 : fd_stake_delegations_new( void * mem,
673 : int disk_fd,
674 : ulong seed,
675 : ulong max_stake_accounts,
676 : ulong max_disk_records,
677 12 : ulong max_live_slots ) {
678 12 : if( FD_UNLIKELY( !mem ) ) {
679 0 : FD_LOG_WARNING(( "NULL mem" ));
680 0 : return NULL;
681 0 : }
682 :
683 12 : if( FD_UNLIKELY( disk_fd<0 ) ) {
684 0 : FD_LOG_WARNING(( "invalid disk fd" ));
685 0 : return NULL;
686 0 : }
687 :
688 12 : if( FD_UNLIKELY( !max_stake_accounts ) ) {
689 0 : FD_LOG_WARNING(( "max_stake_accounts is 0" ));
690 0 : return NULL;
691 0 : }
692 :
693 12 : if( FD_UNLIKELY( max_stake_accounts>=(ulong)FD_STAKE_DELEGATIONS_DELTA_DISK_TAG ) ) {
694 0 : FD_LOG_WARNING(( "max_stake_accounts is too large" ));
695 0 : return NULL;
696 0 : }
697 :
698 12 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)mem, fd_stake_delegations_align() ) ) ) {
699 0 : FD_LOG_WARNING(( "misaligned mem" ));
700 0 : return NULL;
701 0 : }
702 :
703 12 : if( FD_UNLIKELY( max_live_slots>FD_STAKE_DELEGATIONS_FORK_MAX ) ) {
704 0 : FD_LOG_WARNING(( "max_live_slots is too large" ));
705 0 : return NULL;
706 0 : }
707 :
708 12 : if( FD_UNLIKELY( max_disk_records>=(ulong)FD_STAKE_DELEGATIONS_DELTA_DISK_TAG ) ) {
709 0 : FD_LOG_WARNING(( "max_disk_records is too large" ));
710 0 : return NULL;
711 0 : }
712 :
713 12 : if( FD_UNLIKELY( max_stake_accounts>(ulong)FD_STAKE_DELEGATIONS_DELTA_IDX_MASK ||
714 12 : max_disk_records>
715 12 : ((ulong)FD_STAKE_DELEGATIONS_DELTA_IDX_MASK-max_stake_accounts)/2UL ) ) {
716 0 : FD_LOG_WARNING(( "combined stake delegation disk root capacity is too large" ));
717 0 : return NULL;
718 0 : }
719 :
720 12 : ulong map_chain_cnt = root_map_chain_cnt_est( max_stake_accounts );
721 12 : ulong delta_ele_max = fd_stake_delegations_delta_pool_ele_max( max_stake_accounts );
722 12 : ulong delta_map_chain_cnt = delta_map_chain_cnt_est( delta_ele_max );
723 :
724 12 : FD_SCRATCH_ALLOC_INIT( l, mem );
725 12 : fd_stake_delegations_t * stake_delegations = FD_SCRATCH_ALLOC_APPEND( l, fd_stake_delegations_align(), sizeof(fd_stake_delegations_t) );
726 12 : void * pool_mem = FD_SCRATCH_ALLOC_APPEND( l, root_pool_align(), root_pool_footprint( max_stake_accounts ) );
727 12 : void * map_mem = FD_SCRATCH_ALLOC_APPEND( l, root_map_align(), root_map_footprint( map_chain_cnt ) );
728 12 : void * delta_pool_mem = FD_SCRATCH_ALLOC_APPEND( l, delta_pool_align(), delta_pool_footprint( delta_ele_max ) );
729 12 : void * fork_pool_mem = FD_SCRATCH_ALLOC_APPEND( l, fork_pool_align(), fork_pool_footprint( max_live_slots ) );
730 12 : void * delta_map_mem = FD_SCRATCH_ALLOC_APPEND( l, delta_map_align(), delta_map_footprint( delta_map_chain_cnt ) );
731 :
732 12 : if( FD_UNLIKELY( FD_SCRATCH_ALLOC_FINI( l, fd_stake_delegations_align() )!=(ulong)mem+fd_stake_delegations_footprint( max_stake_accounts, max_live_slots ) ) ) {
733 0 : FD_LOG_WARNING(( "fd_stake_delegations_new: bad layout" ));
734 0 : return NULL;
735 0 : }
736 :
737 12 : fd_stake_delegation_t * root_pool = root_pool_join( root_pool_new( pool_mem, max_stake_accounts ) );
738 12 : if( FD_UNLIKELY( !root_pool ) ) {
739 0 : FD_LOG_WARNING(( "Failed to create stake delegations pool" ));
740 0 : return NULL;
741 0 : }
742 :
743 12 : root_map_t * root_map = root_map_join( root_map_new( map_mem, map_chain_cnt, seed ) );
744 12 : if( FD_UNLIKELY( !root_map ) ) {
745 0 : FD_LOG_WARNING(( "Failed to create stake delegations map" ));
746 0 : return NULL;
747 0 : }
748 :
749 12 : fd_stake_delegation_t * delta_pool = delta_pool_join( delta_pool_new( delta_pool_mem, delta_ele_max ) );
750 12 : if( FD_UNLIKELY( !delta_pool ) ) {
751 0 : FD_LOG_WARNING(( "Failed to create stake delegation delta pool" ));
752 0 : return NULL;
753 0 : }
754 :
755 12 : fork_pool_ele_t * fork_pool = fork_pool_join( fork_pool_new( fork_pool_mem, max_live_slots ) );
756 12 : if( FD_UNLIKELY( !fork_pool ) ) {
757 0 : FD_LOG_WARNING(( "Failed to create fork pool" ));
758 0 : return NULL;
759 0 : }
760 :
761 36 : for( ulong i=0UL; i<max_live_slots; i++ ) fork_pool[ i ].parent = FORK_PARENT_FREE;
762 :
763 12 : delta_map_t * delta_map = delta_map_join( delta_map_new( delta_map_mem, delta_map_chain_cnt, seed ) );
764 12 : if( FD_UNLIKELY( !delta_map ) ) {
765 0 : FD_LOG_WARNING(( "Failed to create delta map" ));
766 0 : return NULL;
767 0 : }
768 :
769 12 : stake_delegations->seed_ = seed;
770 12 : stake_delegations->max_stake_accounts_ = max_stake_accounts;
771 12 : stake_delegations->pool_offset_ = (ulong)root_pool - (ulong)mem;
772 12 : stake_delegations->map_offset_ = (ulong)root_map - (ulong)mem;
773 12 : stake_delegations->delta_pool_offset_ = (ulong)delta_pool - (ulong)mem;
774 12 : stake_delegations->fork_pool_offset_ = (ulong)fork_pool - (ulong)mem;
775 12 : stake_delegations->delta_map_offset_ = (ulong)delta_map - (ulong)mem;
776 12 : stake_delegations->disk_fd_ = disk_fd;
777 12 : stake_delegations->max_disk_records_ = max_disk_records;
778 12 : stake_delegations->disk_root_cnt_ = 0UL;
779 12 : stake_delegations->disk_delta_cnt_ = 0UL;
780 12 : ulong disk_root_cap = disk_root_record_cap( stake_delegations );
781 12 : stake_delegations->disk_bucket_cnt_ = fd_ulong_pow2_up( disk_root_cap + (disk_root_cap>>1) + 1UL );
782 12 : stake_delegations->disk_root_tombstone_cnt_ = 0UL;
783 12 : stake_delegations->disk_delta_tombstone_cnt_ = 0UL;
784 12 : stake_delegations->disk_root_gen_ = 1U;
785 12 : stake_delegations->disk_delta_gen_ = 1U;
786 :
787 12 : stake_delegations->effective_stake = 0UL;
788 12 : stake_delegations->activating_stake = 0UL;
789 12 : stake_delegations->deactivating_stake = 0UL;
790 12 : stake_delegations->frontier_query_epoch = ULONG_MAX;
791 12 : stake_delegations->frontier_query_fork = USHORT_MAX;
792 12 : stake_delegations->pool_idx_wmk_ = 0UL;
793 12 : stake_delegations->fp_warmed_awarded = 0;
794 :
795 12 : fd_rwlock_new( &stake_delegations->lock );
796 :
797 12 : FD_COMPILER_MFENCE();
798 12 : FD_VOLATILE( stake_delegations->magic ) = FD_STAKE_DELEGATIONS_MAGIC;
799 12 : FD_COMPILER_MFENCE();
800 :
801 12 : return mem;
802 12 : }
803 :
804 : fd_stake_delegations_t *
805 : fd_stake_delegations_join( void * mem,
806 24 : int disk_fd ) {
807 24 : if( FD_UNLIKELY( !mem ) ) {
808 0 : FD_LOG_WARNING(( "NULL mem" ));
809 0 : return NULL;
810 0 : }
811 :
812 24 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)mem, fd_stake_delegations_align() ) ) ) {
813 0 : FD_LOG_WARNING(( "misaligned mem" ));
814 0 : return NULL;
815 0 : }
816 :
817 24 : fd_stake_delegations_t * stake_delegations = (fd_stake_delegations_t *)mem;
818 :
819 24 : if( FD_UNLIKELY( stake_delegations->magic!=FD_STAKE_DELEGATIONS_MAGIC ) ) {
820 0 : FD_LOG_WARNING(( "Invalid stake delegations magic" ));
821 0 : return NULL;
822 0 : }
823 :
824 24 : if( FD_UNLIKELY( stake_delegations->disk_fd_!=disk_fd ) ) {
825 0 : FD_LOG_WARNING(( "stake delegations disk fd mismatch (expected %d, got %d)",
826 0 : stake_delegations->disk_fd_, disk_fd ));
827 0 : return NULL;
828 0 : }
829 :
830 24 : return stake_delegations;
831 24 : }
832 :
833 : void
834 615 : fd_stake_delegations_reset( fd_stake_delegations_t * stake_delegations ) {
835 615 : fd_rwlock_write( &stake_delegations->lock );
836 615 : root_pool_reset( get_root_pool( stake_delegations ) );
837 615 : root_map_reset( get_root_map( stake_delegations ) );
838 615 : delta_pool_reset( get_delta_pool( stake_delegations ) );
839 615 : delta_map_reset( get_delta_map( stake_delegations ) );
840 615 : fork_pool_ele_t * fork_pool = get_fork_pool( stake_delegations );
841 615 : ulong max_forks = fork_pool_max( fork_pool );
842 1845 : for( ulong i=0UL; i<max_forks; i++ ) {
843 1230 : fork_pool[ i ].parent = FORK_PARENT_FREE;
844 1230 : fork_pool[ i ].delta_head = UINT_MAX;
845 1230 : fork_pool[ i ].disk_delta_head = UINT_MAX;
846 1230 : }
847 615 : fork_pool_reset( fork_pool );
848 615 : stake_delegations->disk_root_cnt_ = 0UL;
849 615 : stake_delegations->disk_delta_cnt_ = 0UL;
850 615 : stake_delegations->disk_root_tombstone_cnt_ = 0UL;
851 615 : stake_delegations->disk_delta_tombstone_cnt_ = 0UL;
852 615 : stake_delegations->disk_root_gen_ = disk_gen_next( stake_delegations->disk_root_gen_ );
853 615 : stake_delegations->disk_delta_gen_ = disk_gen_next( stake_delegations->disk_delta_gen_ );
854 615 : stake_delegations->effective_stake = 0UL;
855 615 : stake_delegations->activating_stake = 0UL;
856 615 : stake_delegations->deactivating_stake = 0UL;
857 615 : stake_delegations->frontier_query_epoch = ULONG_MAX;
858 615 : stake_delegations->frontier_query_fork = USHORT_MAX;
859 615 : stake_delegations->pool_idx_wmk_ = 0UL;
860 615 : stake_delegations->fp_warmed_awarded = 0;
861 615 : fd_rwlock_unwrite( &stake_delegations->lock );
862 615 : }
863 :
864 : struct root_query {
865 : fd_stake_delegation_t * ele;
866 : ulong disk_bucket_idx;
867 : uint disk_idx;
868 : int is_disk;
869 : fd_stake_delegation_t disk_ele;
870 : };
871 : typedef struct root_query root_query_t;
872 :
873 : static int
874 : root_query( fd_stake_delegations_t * stake_delegations,
875 : fd_pubkey_t const * stake_account,
876 1348 : root_query_t * query ) {
877 1348 : query->ele = NULL;
878 1348 : query->disk_bucket_idx = ULONG_MAX;
879 1348 : query->disk_idx = UINT_MAX;
880 1348 : query->is_disk = 0;
881 :
882 1348 : fd_stake_delegation_t * pool = get_root_pool( stake_delegations );
883 1348 : root_map_t * map = get_root_map( stake_delegations );
884 :
885 1348 : fd_stake_delegation_t * in_memory = root_map_ele_query( map, stake_account, NULL, pool );
886 1348 : if( FD_LIKELY( in_memory ) ) {
887 272 : query->ele = in_memory;
888 272 : return 1;
889 272 : }
890 :
891 1076 : if( FD_LIKELY( !stake_delegations->disk_root_cnt_ ) ) return 0;
892 0 : ulong free_bucket_idx;
893 0 : disk_root_find( stake_delegations, stake_account, &query->disk_ele,
894 0 : &query->disk_idx, &query->disk_bucket_idx, &free_bucket_idx );
895 0 : if( FD_LIKELY( query->disk_idx==UINT_MAX ) ) return 0;
896 0 : query->ele = &query->disk_ele;
897 0 : query->is_disk = 1;
898 0 : return 1;
899 0 : }
900 :
901 : static void
902 : root_insert( fd_stake_delegations_t * stake_delegations,
903 1076 : fd_stake_delegation_t const * delegation ) {
904 1076 : fd_stake_delegation_t * pool = get_root_pool( stake_delegations );
905 1076 : if( FD_LIKELY( root_pool_free( pool ) ) ) {
906 1075 : fd_stake_delegation_t * in_memory = root_pool_ele_acquire( pool );
907 1075 : *in_memory = *delegation;
908 1075 : stake_delegations->pool_idx_wmk_ = fd_ulong_max( stake_delegations->pool_idx_wmk_, root_pool_idx( pool, in_memory )+1UL );
909 1075 : FD_CHECK_CRIT( root_map_ele_insert( get_root_map( stake_delegations ), in_memory, pool ),
910 1075 : "unable to insert stake delegation into root map" );
911 1075 : return;
912 1075 : }
913 :
914 1 : disk_root_insert( stake_delegations, delegation );
915 1 : }
916 :
917 : #if FD_HAS_DOUBLE
918 :
919 : static void
920 : disk_root_promote( fd_stake_delegations_t * stake_delegations,
921 : uint disk_idx,
922 0 : fd_stake_delegation_t const * delegation ) {
923 0 : FD_CHECK_CRIT( root_pool_free( get_root_pool( stake_delegations ) ),
924 0 : "no in-memory slot for promoted stake delegation root" );
925 0 : root_insert( stake_delegations, delegation );
926 :
927 0 : uint found_idx;
928 0 : ulong found_bucket_idx;
929 0 : ulong free_bucket_idx;
930 0 : disk_root_find( stake_delegations, &delegation->stake_account, NULL,
931 0 : &found_idx, &found_bucket_idx, &free_bucket_idx );
932 0 : FD_CHECK_CRIT( found_idx==disk_idx, "missing promoted stake delegation disk root" );
933 0 : disk_root_remove( stake_delegations, disk_idx, found_bucket_idx );
934 0 : }
935 :
936 : #endif
937 :
938 : static void
939 : root_store( fd_stake_delegations_t * stake_delegations,
940 : root_query_t const * query,
941 272 : fd_stake_delegation_t const * delegation ) {
942 272 : if( FD_LIKELY( !query->is_disk ) ) {
943 272 : uint next = query->ele->next_;
944 272 : *query->ele = *delegation;
945 272 : query->ele->next_ = next;
946 272 : } else {
947 0 : disk_root_write( stake_delegations, query->disk_idx, delegation );
948 0 : }
949 272 : }
950 :
951 : static void
952 : root_remove( fd_stake_delegations_t * stake_delegations,
953 0 : root_query_t const * query ) {
954 0 : if( FD_LIKELY( !query->is_disk ) ) {
955 0 : fd_stake_delegation_t * pool = get_root_pool( stake_delegations );
956 0 : uint idx = (uint)root_pool_idx( pool, query->ele );
957 0 : fd_pubkey_t stake_account = query->ele->stake_account;
958 0 : root_map_idx_remove( get_root_map( stake_delegations ), &stake_account, idx, pool );
959 0 : query->ele->in_use = 0;
960 0 : root_pool_idx_release( pool, idx );
961 0 : } else {
962 0 : disk_root_remove( stake_delegations, query->disk_idx, query->disk_bucket_idx );
963 0 : }
964 0 : }
965 :
966 : /* Unlocked root upsert used by public updates and fork rooting. */
967 :
968 : static void
969 : root_upsert( fd_stake_delegations_t * stake_delegations,
970 : root_query_t const * query,
971 1076 : fd_stake_delegation_t * delegation ) {
972 1076 : delegation->delta_idx = UINT_MAX;
973 1076 : delegation->in_use = 1;
974 :
975 1076 : if( FD_LIKELY( query->ele ) ) {
976 0 : delegation->dne_in_root = query->ele->dne_in_root;
977 0 : root_store( stake_delegations, query, delegation );
978 0 : return;
979 0 : }
980 :
981 1076 : delegation->dne_in_root = 0;
982 1076 : root_insert( stake_delegations, delegation );
983 1076 : }
984 :
985 : fd_stake_history_entry_t
986 32 : fd_stake_delegations_totals( fd_stake_delegations_t const * stake_delegations ) {
987 32 : return (fd_stake_history_entry_t) {
988 32 : .effective = stake_delegations->effective_stake,
989 32 : .activating = stake_delegations->activating_stake,
990 32 : .deactivating = stake_delegations->deactivating_stake,
991 32 : };
992 32 : }
993 :
994 : void
995 : fd_stake_delegations_set_totals( fd_stake_delegations_t * stake_delegations,
996 : ulong effective,
997 : ulong activating,
998 0 : ulong deactivating ) {
999 0 : fd_rwlock_write( &stake_delegations->lock );
1000 0 : stake_delegations->effective_stake = effective;
1001 0 : stake_delegations->activating_stake = activating;
1002 0 : stake_delegations->deactivating_stake = deactivating;
1003 0 : fd_rwlock_unwrite( &stake_delegations->lock );
1004 0 : }
1005 :
1006 : #if FD_HAS_DOUBLE
1007 :
1008 : ulong
1009 : fd_stake_delegations_prune_inactive_root( fd_stake_delegations_t * stake_delegations,
1010 : ulong epoch,
1011 : fd_stake_history_t const * stake_history,
1012 : ulong * warmup_cooldown_rate_epoch,
1013 : int use_fixed_point_stake_math,
1014 0 : fd_bank_t const * emit_bank ) {
1015 0 : fd_rwlock_write( &stake_delegations->lock );
1016 :
1017 0 : root_map_t * map = get_root_map( stake_delegations );
1018 0 : fd_stake_delegation_t * pool = get_root_pool( stake_delegations );
1019 0 : ulong prev_epoch = epoch ? epoch-1UL : 0UL;
1020 0 : ulong pruned = 0UL;
1021 :
1022 0 : for( ulong idx=0UL; idx<stake_delegations->pool_idx_wmk_; idx++ ) {
1023 0 : fd_stake_delegation_t * delegation = pool+idx;
1024 0 : if( FD_LIKELY( !delegation->in_use ) ) continue;
1025 :
1026 0 : if( FD_LIKELY( !fd_stake_delegation_is_inactive( delegation, epoch, stake_history, warmup_cooldown_rate_epoch, use_fixed_point_stake_math ) ||
1027 0 : !fd_stake_delegation_is_inactive( delegation, prev_epoch, stake_history, warmup_cooldown_rate_epoch, use_fixed_point_stake_math ) ) ) continue;
1028 :
1029 0 : if( FD_UNLIKELY( emit_bank ) ) fd_event_runtime_stake_delegation_remove_emit( emit_bank, delegation->stake_account.uc );
1030 0 : pruned++;
1031 :
1032 0 : fd_pubkey_t stake_account = delegation->stake_account;
1033 0 : root_map_idx_remove( map, &stake_account, (uint)idx, pool );
1034 0 : delegation->in_use = 0;
1035 0 : root_pool_idx_release( pool, (uint)idx );
1036 0 : }
1037 :
1038 0 : for( uint idx=0U; (ulong)idx<stake_delegations->disk_root_cnt_; ) {
1039 0 : fd_stake_delegation_t delegation;
1040 0 : disk_root_read( stake_delegations, idx, &delegation );
1041 0 : if( FD_LIKELY( !fd_stake_delegation_is_inactive( &delegation, epoch, stake_history, warmup_cooldown_rate_epoch, use_fixed_point_stake_math ) ||
1042 0 : !fd_stake_delegation_is_inactive( &delegation, prev_epoch, stake_history, warmup_cooldown_rate_epoch, use_fixed_point_stake_math ) ) ) {
1043 0 : idx++;
1044 0 : continue;
1045 0 : }
1046 :
1047 0 : if( FD_UNLIKELY( emit_bank ) ) fd_event_runtime_stake_delegation_remove_emit( emit_bank, delegation.stake_account.uc );
1048 0 : pruned++;
1049 :
1050 0 : uint found_idx;
1051 0 : ulong found_bucket_idx;
1052 0 : ulong free_bucket_idx;
1053 0 : disk_root_find( stake_delegations, &delegation.stake_account, NULL, &found_idx, &found_bucket_idx, &free_bucket_idx );
1054 0 : FD_CHECK_CRIT( found_idx==idx, "missing inactive stake delegation disk root" );
1055 0 : disk_root_remove( stake_delegations, idx, found_bucket_idx );
1056 0 : }
1057 :
1058 : /* Keep the in-memory root tier packed after pruning. */
1059 0 : while( root_pool_free( pool ) && stake_delegations->disk_root_cnt_ ) {
1060 0 : uint idx = (uint)(stake_delegations->disk_root_cnt_-1UL);
1061 0 : fd_stake_delegation_t delegation;
1062 0 : disk_root_read( stake_delegations, idx, &delegation );
1063 0 : disk_root_promote( stake_delegations, idx, &delegation );
1064 0 : }
1065 :
1066 0 : fd_rwlock_unwrite( &stake_delegations->lock );
1067 :
1068 0 : return pruned;
1069 0 : }
1070 :
1071 : void
1072 : fd_stake_delegations_refresh( fd_stake_delegations_t * stake_delegations,
1073 : ulong epoch,
1074 : fd_stake_history_t const * stake_history,
1075 : ulong * warmup_cooldown_rate_epoch,
1076 : int use_fixed_point_stake_math,
1077 615 : int remove_inactive_stakes ) {
1078 615 : fd_rwlock_write( &stake_delegations->lock );
1079 :
1080 615 : int history_contiguous = fd_sysvar_stake_history_is_contiguous( stake_history );
1081 :
1082 615 : stake_delegations->effective_stake = 0UL;
1083 615 : stake_delegations->activating_stake = 0UL;
1084 615 : stake_delegations->deactivating_stake = 0UL;
1085 615 : stake_delegations->fp_warmed_awarded = 0;
1086 :
1087 615 : fd_stake_delegation_t * pool = get_root_pool( stake_delegations );
1088 615 : ulong const wmk = stake_delegations->pool_idx_wmk_;
1089 615 : ulong prev_epoch = epoch ? epoch-1UL : 0UL;
1090 :
1091 1691 : for( ulong i=0UL; i<wmk; i++ ) {
1092 1076 : if( FD_UNLIKELY( !pool[ i ].in_use ) ) continue;
1093 1076 : fd_stake_delegation_t * delegation = pool+i;
1094 1076 : root_query_t query = { .ele = delegation };
1095 :
1096 : /* Tombstone left by fd_stake_delegations_fork_remove. */
1097 1076 : if( FD_UNLIKELY( !delegation->lamports ) ) {
1098 0 : root_remove( stake_delegations, &query );
1099 0 : continue;
1100 0 : }
1101 :
1102 1076 : if( FD_UNLIKELY( remove_inactive_stakes &&
1103 1076 : fd_stake_delegation_is_inactive( delegation, epoch, stake_history, warmup_cooldown_rate_epoch, use_fixed_point_stake_math ) &&
1104 1076 : fd_stake_delegation_is_inactive( delegation, prev_epoch, stake_history, warmup_cooldown_rate_epoch, use_fixed_point_stake_math ) ) ) {
1105 0 : root_remove( stake_delegations, &query );
1106 0 : continue;
1107 0 : }
1108 :
1109 1076 : fd_stake_history_entry_t history = fd_stake_delegation_activation_status( delegation, epoch, stake_history, warmup_cooldown_rate_epoch, use_fixed_point_stake_math );
1110 1076 : stake_delegations->effective_stake += history.effective;
1111 1076 : stake_delegations->activating_stake += history.activating;
1112 1076 : stake_delegations->deactivating_stake += history.deactivating;
1113 :
1114 1076 : delegation->slot = 0U;
1115 1076 : delegation->state = history_contiguous
1116 1076 : ? fd_stake_delegation_classify( delegation, history, epoch ) & 0x7U
1117 1076 : : FD_STAKE_DELEGATION_STATE_UNKNOWN;
1118 1076 : if( FD_LIKELY( delegation->state==FD_STAKE_DELEGATION_STATE_WARMED && !use_fixed_point_stake_math ) ) {
1119 654 : stake_delegations->fp_warmed_awarded = 1;
1120 654 : }
1121 1076 : }
1122 :
1123 : /* Disk roots are dense, so removals leave the moved last record at
1124 : the current index. Survivors move back to in-memory storage
1125 : whenever pruning made a root-pool slot available. */
1126 615 : uint disk_idx = 0U;
1127 615 : while( (ulong)disk_idx<stake_delegations->disk_root_cnt_ ) {
1128 0 : fd_stake_delegation_t delegation;
1129 0 : disk_root_read( stake_delegations, disk_idx, &delegation );
1130 :
1131 0 : int remove = !delegation.lamports;
1132 0 : if( FD_LIKELY( !remove ) && FD_UNLIKELY( remove_inactive_stakes ) ) {
1133 0 : remove = fd_stake_delegation_is_inactive( &delegation, epoch, stake_history, warmup_cooldown_rate_epoch, use_fixed_point_stake_math ) &&
1134 0 : fd_stake_delegation_is_inactive( &delegation, prev_epoch, stake_history, warmup_cooldown_rate_epoch, use_fixed_point_stake_math );
1135 0 : }
1136 :
1137 0 : if( FD_UNLIKELY( remove ) ) {
1138 0 : uint found_idx;
1139 0 : ulong found_bucket_idx;
1140 0 : ulong free_bucket_idx;
1141 0 : disk_root_find( stake_delegations, &delegation.stake_account, NULL, &found_idx, &found_bucket_idx, &free_bucket_idx );
1142 0 : FD_CHECK_CRIT( found_idx==disk_idx, "missing refreshed stake delegation disk root" );
1143 0 : disk_root_remove( stake_delegations, disk_idx, found_bucket_idx );
1144 0 : continue;
1145 0 : }
1146 :
1147 0 : fd_stake_history_entry_t history = fd_stake_delegation_activation_status( &delegation, epoch, stake_history, warmup_cooldown_rate_epoch, use_fixed_point_stake_math );
1148 0 : stake_delegations->effective_stake += history.effective;
1149 0 : stake_delegations->activating_stake += history.activating;
1150 0 : stake_delegations->deactivating_stake += history.deactivating;
1151 :
1152 0 : delegation.slot = 0U;
1153 0 : delegation.state = history_contiguous
1154 0 : ? fd_stake_delegation_classify( &delegation, history, epoch ) & 0x7U
1155 0 : : FD_STAKE_DELEGATION_STATE_UNKNOWN;
1156 0 : root_query_t query = {
1157 0 : .ele = &delegation,
1158 0 : .disk_idx = disk_idx,
1159 0 : .is_disk = 1,
1160 0 : };
1161 0 : root_upsert( stake_delegations, &query, &delegation );
1162 0 : if( FD_LIKELY( delegation.state==FD_STAKE_DELEGATION_STATE_WARMED && !use_fixed_point_stake_math ) ) {
1163 0 : stake_delegations->fp_warmed_awarded = 1;
1164 0 : }
1165 :
1166 0 : if( FD_UNLIKELY( root_pool_free( pool ) ) ) {
1167 0 : disk_root_promote( stake_delegations, disk_idx, &delegation );
1168 0 : continue;
1169 0 : }
1170 0 : disk_idx++;
1171 0 : }
1172 :
1173 615 : fd_rwlock_unwrite( &stake_delegations->lock );
1174 615 : }
1175 :
1176 : #endif
1177 :
1178 : fd_stake_history_entry_t
1179 : fd_stake_delegations_vote_account_status( fd_stake_delegations_t * stake_delegations,
1180 : fd_pubkey_t const * vote_account,
1181 : ulong epoch,
1182 : fd_stake_history_t const * stake_history,
1183 : ulong * warmup_cooldown_rate_epoch,
1184 0 : int use_fixed_point_stake_math ) {
1185 0 : fd_stake_history_entry_t total = {0};
1186 0 : fd_rwlock_write( &stake_delegations->lock );
1187 0 : fd_stake_delegations_iter_t iter_[1];
1188 0 : for( fd_stake_delegations_iter_t * iter = fd_stake_delegations_iter_init( iter_, stake_delegations );
1189 0 : !fd_stake_delegations_iter_done( iter );
1190 0 : fd_stake_delegations_iter_next( iter ) ) {
1191 0 : fd_stake_delegation_t const * d = fd_stake_delegations_iter_ele( iter );
1192 0 : if( FD_LIKELY( !fd_pubkey_eq( &d->vote_account, vote_account ) ) ) continue;
1193 0 : fd_stake_history_entry_t status = fd_stake_delegation_activation_status( d, epoch, stake_history, warmup_cooldown_rate_epoch, use_fixed_point_stake_math );
1194 0 : total.effective += status.effective;
1195 0 : total.activating += status.activating;
1196 0 : total.deactivating += status.deactivating;
1197 0 : }
1198 0 : fd_rwlock_unwrite( &stake_delegations->lock );
1199 0 : return total;
1200 0 : }
1201 :
1202 : /* Fork-aware delta operations */
1203 :
1204 : ushort
1205 : fd_stake_delegations_new_fork( fd_stake_delegations_t * stake_delegations,
1206 627 : ushort parent_fork_idx ) {
1207 627 : fd_rwlock_write( &stake_delegations->lock );
1208 627 : fork_pool_ele_t * fork_pool = get_fork_pool( stake_delegations );
1209 627 : FD_CHECK_CRIT( parent_fork_idx==USHORT_MAX ||
1210 627 : ((ulong)parent_fork_idx<fork_pool_max( fork_pool ) && fork_pool[ parent_fork_idx ].parent!=FORK_PARENT_FREE),
1211 627 : "invalid stake delegation parent fork" );
1212 627 : FD_CHECK_CRIT( fork_pool_free( fork_pool ), "no free forks in pool. The system has forked too wide." );
1213 627 : ushort fork_idx = (ushort)fork_pool_idx_acquire( fork_pool );
1214 627 : fork_pool[ fork_idx ].parent = parent_fork_idx;
1215 627 : fork_pool[ fork_idx ].delta_head = UINT_MAX;
1216 627 : fork_pool[ fork_idx ].disk_delta_head = UINT_MAX;
1217 627 : fd_rwlock_unwrite( &stake_delegations->lock );
1218 :
1219 627 : return fork_idx;
1220 627 : }
1221 :
1222 : struct fork_query {
1223 : fd_stake_delegation_t * ele; /* in-memory delta, or NULL */
1224 : disk_delta_query_t disk_query; /* spilled delta, record_idx UINT_MAX if absent */
1225 : disk_delta_t disk_ele;
1226 : };
1227 : typedef struct fork_query fork_query_t;
1228 :
1229 : /* Finds the fork's delta for stake_account in memory or on disk.
1230 : Returns it, or NULL if the fork has none. */
1231 :
1232 : static fd_stake_delegation_t const *
1233 : fork_query( fd_stake_delegations_t * stake_delegations,
1234 : ushort fork_idx,
1235 : fd_pubkey_t const * stake_account,
1236 136 : fork_query_t * query ) {
1237 136 : fd_stake_delegation_t * delta_pool = get_delta_pool( stake_delegations );
1238 136 : fd_stake_delegation_key_t key = {
1239 136 : .stake_account = *stake_account,
1240 136 : .fork_idx = fork_idx
1241 136 : };
1242 136 : query->ele = delta_map_ele_query( get_delta_map( stake_delegations ), &key, NULL, delta_pool );
1243 136 : query->disk_query.record_idx = UINT_MAX;
1244 136 : query->disk_query.bucket_idx = ULONG_MAX;
1245 136 : if( FD_LIKELY( query->ele ) ) return query->ele;
1246 136 : if( FD_UNLIKELY( stake_delegations->disk_delta_cnt_ || !delta_pool_free( delta_pool ) ) ) {
1247 0 : disk_delta_find( stake_delegations, fork_idx, stake_account, &query->disk_ele, &query->disk_query );
1248 0 : if( FD_UNLIKELY( query->disk_query.record_idx!=UINT_MAX ) ) return &query->disk_ele.delegation;
1249 0 : }
1250 136 : return NULL;
1251 136 : }
1252 :
1253 : /* Overwrites the queried delta or inserts a new one. */
1254 :
1255 : static void
1256 : fork_upsert( fd_stake_delegations_t * stake_delegations,
1257 : ushort fork_idx,
1258 : fork_query_t * query,
1259 136 : fd_stake_delegation_t const * delegation ) {
1260 136 : if( FD_LIKELY( query->ele ) ) {
1261 0 : fd_stake_delegation_t * in_memory = query->ele;
1262 0 : uint next = in_memory->next_;
1263 0 : uint fork_next = in_memory->fork_next;
1264 0 : *in_memory = *delegation;
1265 0 : in_memory->next_ = next;
1266 0 : in_memory->fork_next = fork_next;
1267 0 : in_memory->fork_idx = fork_idx;
1268 0 : return;
1269 0 : }
1270 :
1271 136 : if( FD_UNLIKELY( query->disk_query.record_idx!=UINT_MAX ) ) {
1272 0 : query->disk_ele.delegation = *delegation;
1273 0 : disk_delta_write( stake_delegations, query->disk_query.record_idx, &query->disk_ele );
1274 0 : return;
1275 0 : }
1276 :
1277 136 : fd_stake_delegation_t * delta_pool = get_delta_pool( stake_delegations );
1278 136 : if( FD_LIKELY( delta_pool_free( delta_pool ) ) ) {
1279 136 : fork_pool_ele_t * fork = get_fork_pool( stake_delegations ) + fork_idx;
1280 136 : fd_stake_delegation_t * in_memory = delta_pool_ele_acquire( delta_pool );
1281 136 : *in_memory = *delegation;
1282 136 : uint idx = (uint)delta_pool_idx( delta_pool, in_memory );
1283 136 : in_memory->fork_next = fork->delta_head;
1284 136 : in_memory->fork_idx = fork_idx;
1285 136 : FD_CHECK_CRIT( delta_map_ele_insert( get_delta_map( stake_delegations ), in_memory, delta_pool ),
1286 136 : "unable to insert stake delegation into delta map" );
1287 136 : fork->delta_head = idx;
1288 136 : } else {
1289 0 : disk_delta_insert( stake_delegations, fork_idx, delegation, &query->disk_query );
1290 0 : }
1291 136 : }
1292 :
1293 : /* Root tier: newest slot wins. Caller holds the write lock. */
1294 :
1295 : static void
1296 : root_write( fd_stake_delegations_t * stake_delegations,
1297 1077 : fd_stake_delegation_t * delegation ) {
1298 1077 : root_query_t query;
1299 1077 : root_query( stake_delegations, &delegation->stake_account, &query );
1300 1077 : if( FD_UNLIKELY( query.ele && query.ele->slot>delegation->slot ) ) return;
1301 1077 : root_upsert( stake_delegations, &query, delegation );
1302 1077 : }
1303 :
1304 : /* Fork tier: newest slot wins. Caller holds the write lock. */
1305 :
1306 : static void
1307 : fork_write( fd_stake_delegations_t * stake_delegations,
1308 : ushort fork_idx,
1309 136 : fd_stake_delegation_t const * delegation ) {
1310 136 : fork_query_t query;
1311 136 : fd_stake_delegation_t const * existing = fork_query( stake_delegations, fork_idx, &delegation->stake_account, &query );
1312 136 : if( FD_UNLIKELY( existing && existing->slot>delegation->slot ) ) return;
1313 136 : fork_upsert( stake_delegations, fork_idx, &query, delegation );
1314 136 : }
1315 :
1316 : /* fork_idx USHORT_MAX writes the root, otherwise the fork.
1317 : replacing_full_entry writes nothing unless the root holds the
1318 : account. */
1319 :
1320 : static void
1321 : delegation_write( fd_stake_delegations_t * stake_delegations,
1322 : ushort fork_idx,
1323 : fd_stake_delegation_t * delegation,
1324 1213 : int replacing_full_entry ) {
1325 1213 : fd_rwlock_write( &stake_delegations->lock );
1326 1213 : root_query_t query;
1327 1213 : if( FD_LIKELY( !replacing_full_entry || root_query( stake_delegations, &delegation->stake_account, &query ) ) ) {
1328 1213 : if( FD_LIKELY( fork_idx==USHORT_MAX ) ) root_write( stake_delegations, delegation );
1329 136 : else fork_write( stake_delegations, fork_idx, delegation );
1330 1213 : }
1331 1213 : fd_rwlock_unwrite( &stake_delegations->lock );
1332 1213 : }
1333 :
1334 : void
1335 : fd_stake_delegations_fork_update( fd_stake_delegations_t * stake_delegations,
1336 : ushort fork_idx,
1337 : ulong slot,
1338 : fd_pubkey_t const * stake_account,
1339 : fd_pubkey_t const * vote_account,
1340 : ulong stake,
1341 : ulong activation_epoch,
1342 : ulong deactivation_epoch,
1343 : ulong credits_observed,
1344 : ulong lamports,
1345 1212 : uint acc_dlen ) {
1346 1212 : FD_CHECK_ERR( (long)activation_epoch <USHORT_MAX, "activation_epoch overflow" );
1347 1212 : FD_CHECK_ERR( (long)deactivation_epoch<USHORT_MAX, "deactivation_epoch overflow" );
1348 1212 : FD_CHECK_CRIT( slot<=UINT_MAX, "slot exceeds 2^32-1" );
1349 1212 : fd_stake_delegation_t delegation = {
1350 1212 : .stake_account = *stake_account,
1351 1212 : .vote_account = *vote_account,
1352 1212 : .stake = stake,
1353 1212 : .lamports = lamports,
1354 1212 : .credits_observed = credits_observed,
1355 1212 : .acc_dlen = acc_dlen,
1356 1212 : .activation_epoch = (ushort)activation_epoch,
1357 1212 : .deactivation_epoch = (ushort)deactivation_epoch,
1358 1212 : .slot = (uint)slot,
1359 1212 : };
1360 1212 : delegation_write( stake_delegations, fork_idx, &delegation, 0 );
1361 1212 : }
1362 :
1363 : void
1364 : fd_stake_delegations_root_update( fd_stake_delegations_t * stake_delegations,
1365 : fd_pubkey_t const * stake_account,
1366 : fd_pubkey_t const * vote_account,
1367 : ulong stake,
1368 : ulong activation_epoch,
1369 : ulong deactivation_epoch,
1370 : ulong credits_observed,
1371 : ulong lamports,
1372 1077 : uint acc_dlen ) {
1373 1077 : fd_stake_delegations_fork_update( stake_delegations, USHORT_MAX, 0UL, stake_account, vote_account, stake,
1374 1077 : activation_epoch, deactivation_epoch, credits_observed, lamports, acc_dlen );
1375 1077 : }
1376 :
1377 : void
1378 : fd_stake_delegations_fork_remove( fd_stake_delegations_t * stake_delegations,
1379 : ushort fork_idx,
1380 : ulong slot,
1381 : fd_pubkey_t const * stake_account,
1382 0 : int replacing_full_entry ) {
1383 0 : FD_CHECK_CRIT( slot<=UINT_MAX, "slot exceeds 2^32-1" );
1384 0 : fd_stake_delegation_t tombstone = {
1385 0 : .stake_account = *stake_account,
1386 0 : .is_tombstone = 1,
1387 0 : .slot = (uint)slot,
1388 0 : };
1389 0 : delegation_write( stake_delegations, fork_idx, &tombstone, replacing_full_entry );
1390 0 : }
1391 :
1392 : void
1393 : fd_stake_delegations_evict_fork( fd_stake_delegations_t * stake_delegations,
1394 614 : ushort fork_idx ) {
1395 614 : if( fork_idx==USHORT_MAX ) return;
1396 :
1397 614 : fd_rwlock_write( &stake_delegations->lock );
1398 :
1399 614 : fd_stake_delegation_t * delta_pool = get_delta_pool( stake_delegations );
1400 614 : delta_map_t * delta_map = get_delta_map( stake_delegations );
1401 614 : fork_pool_ele_t * fork_pool = get_fork_pool( stake_delegations );
1402 614 : FD_CHECK_CRIT( (ulong)fork_idx<fork_pool_max( fork_pool ) && fork_pool[ fork_idx ].parent!=FORK_PARENT_FREE,
1403 614 : "invalid stake delegation fork" );
1404 :
1405 614 : uint idx = fork_pool[ fork_idx ].delta_head;
1406 750 : while( idx!=UINT_MAX ) {
1407 136 : fd_stake_delegation_t * ele = delta_pool + idx;
1408 136 : FD_CHECK_CRIT( delta_map_ele_remove( delta_map, &ele->key, NULL, delta_pool )==ele,
1409 136 : "stake delegation missing from delta map" );
1410 136 : idx = ele->fork_next;
1411 136 : delta_pool_ele_release( delta_pool, ele );
1412 136 : }
1413 614 : fork_pool[ fork_idx ].delta_head = UINT_MAX;
1414 :
1415 614 : while( fork_pool[ fork_idx ].disk_delta_head!=UINT_MAX ) {
1416 0 : uint disk_idx = fork_pool[ fork_idx ].disk_delta_head;
1417 0 : disk_delta_t disk_delta;
1418 0 : disk_delta_read( stake_delegations, disk_idx, &disk_delta );
1419 0 : disk_delta_remove( stake_delegations, disk_idx, &disk_delta );
1420 0 : }
1421 614 : fork_pool[ fork_idx ].disk_delta_head = UINT_MAX;
1422 :
1423 614 : fork_pool[ fork_idx ].parent = FORK_PARENT_FREE;
1424 614 : fork_pool_idx_release( fork_pool, fork_idx );
1425 :
1426 614 : fd_rwlock_unwrite( &stake_delegations->lock );
1427 614 : }
1428 :
1429 : /* skip_stake_math applies the delta without updating totals or
1430 : state, for the snapshot loader; fd_stake_delegations_refresh
1431 : recomputes them afterwards. */
1432 :
1433 : static void
1434 : apply_delta( ulong epoch,
1435 : fd_stake_history_t const * stake_history,
1436 : ulong * warmup_cooldown_rate_epoch,
1437 : int use_fixed_point_stake_math,
1438 : int history_contiguous,
1439 : int skip_stake_math,
1440 : fd_stake_delegations_t * stake_delegations,
1441 0 : fd_stake_delegation_t const * delta ) {
1442 0 : root_query_t query;
1443 0 : int found = root_query( stake_delegations, &delta->stake_account, &query );
1444 :
1445 0 : if( FD_UNLIKELY( skip_stake_math ) ) {
1446 0 : if( FD_UNLIKELY( delta->is_tombstone ) ) {
1447 0 : if( FD_LIKELY( found ) ) root_remove( stake_delegations, &query );
1448 0 : return;
1449 0 : }
1450 0 : fd_stake_delegation_t root_delegation = *delta;
1451 0 : root_upsert( stake_delegations, &query, &root_delegation );
1452 0 : return;
1453 0 : }
1454 :
1455 0 : if( FD_LIKELY( found ) ) {
1456 0 : fd_stake_history_entry_t old_entry = fd_stake_delegation_activation_status(
1457 0 : query.ele, epoch, stake_history, warmup_cooldown_rate_epoch, use_fixed_point_stake_math );
1458 0 : stake_delegations->effective_stake -= old_entry.effective;
1459 0 : stake_delegations->activating_stake -= old_entry.activating;
1460 0 : stake_delegations->deactivating_stake -= old_entry.deactivating;
1461 0 : }
1462 :
1463 0 : if( FD_UNLIKELY( delta->is_tombstone ) ) {
1464 0 : if( FD_LIKELY( found ) ) root_remove( stake_delegations, &query );
1465 0 : return;
1466 0 : }
1467 :
1468 0 : fd_stake_delegation_t root_delegation = *delta;
1469 0 : fd_stake_history_entry_t new_entry = fd_stake_delegation_activation_status(
1470 0 : delta, epoch, stake_history, warmup_cooldown_rate_epoch, use_fixed_point_stake_math );
1471 0 : stake_delegations->effective_stake += new_entry.effective;
1472 0 : stake_delegations->activating_stake += new_entry.activating;
1473 0 : stake_delegations->deactivating_stake += new_entry.deactivating;
1474 :
1475 0 : root_delegation.state = history_contiguous
1476 0 : ? fd_stake_delegation_classify( &root_delegation, new_entry, epoch ) & 0x7U
1477 0 : : FD_STAKE_DELEGATION_STATE_UNKNOWN;
1478 0 : root_upsert( stake_delegations, &query, &root_delegation );
1479 0 : if( FD_LIKELY( root_delegation.state==FD_STAKE_DELEGATION_STATE_WARMED && !use_fixed_point_stake_math ) ) {
1480 0 : stake_delegations->fp_warmed_awarded = 1;
1481 0 : }
1482 0 : }
1483 :
1484 : static void
1485 : apply_fork_delta( ulong epoch,
1486 : fd_stake_history_t const * stake_history,
1487 : ulong * warmup_cooldown_rate_epoch,
1488 : int use_fixed_point_stake_math,
1489 : int history_contiguous,
1490 : int skip_stake_math,
1491 : fd_stake_delegations_t * stake_delegations,
1492 : ushort fork_idx,
1493 0 : fd_stake_delegations_delta_stats_t * stake_delegations_delta_stats ) {
1494 0 : fd_stake_delegation_t * delta_pool = get_delta_pool( stake_delegations );
1495 0 : ulong upserts = 0UL;
1496 0 : ulong removes = 0UL;
1497 :
1498 : /* Apply disk deltas first. */
1499 0 : fork_pool_ele_t * fork_pool = get_fork_pool( stake_delegations );
1500 0 : while( fork_pool[ fork_idx ].disk_delta_head!=UINT_MAX ) {
1501 0 : uint disk_idx = fork_pool[ fork_idx ].disk_delta_head;
1502 0 : disk_delta_t disk_delta;
1503 0 : disk_delta_read( stake_delegations, disk_idx, &disk_delta );
1504 0 : fd_stake_delegation_t delegation = disk_delta.delegation;
1505 0 : disk_delta_remove( stake_delegations, disk_idx, &disk_delta );
1506 0 : upserts += (ulong)!delegation.is_tombstone;
1507 0 : removes += (ulong)!!delegation.is_tombstone;
1508 0 : apply_delta( epoch, stake_history, warmup_cooldown_rate_epoch, use_fixed_point_stake_math,
1509 0 : history_contiguous, skip_stake_math, stake_delegations, &delegation );
1510 0 : }
1511 :
1512 : /* Apply in-memory deltas. */
1513 0 : for( uint idx=fork_pool[ fork_idx ].delta_head; idx!=UINT_MAX; idx=delta_pool[ idx ].fork_next ) {
1514 0 : fd_stake_delegation_t * delegation = delta_pool + idx;
1515 0 : upserts += (ulong)!delegation->is_tombstone;
1516 0 : removes += (ulong)!!delegation->is_tombstone;
1517 0 : apply_delta( epoch, stake_history, warmup_cooldown_rate_epoch, use_fixed_point_stake_math,
1518 0 : history_contiguous, skip_stake_math, stake_delegations, delegation );
1519 0 : }
1520 :
1521 0 : if( stake_delegations_delta_stats ) {
1522 0 : stake_delegations_delta_stats->upserts += upserts;
1523 0 : stake_delegations_delta_stats->removes += removes;
1524 0 : }
1525 0 : }
1526 :
1527 : void
1528 : fd_stake_delegations_advance_root( ulong epoch,
1529 : fd_stake_history_t const * stake_history,
1530 : ulong * warmup_cooldown_rate_epoch,
1531 : int use_fixed_point_stake_math,
1532 : int skip_stake_math,
1533 : fd_stake_delegations_t * stake_delegations,
1534 : ushort fork_idx,
1535 0 : fd_stake_delegations_delta_stats_t * stake_delegations_delta_stats ) {
1536 0 : fd_rwlock_write( &stake_delegations->lock );
1537 :
1538 0 : ushort fork_ids[ FD_STAKE_DELEGATIONS_FORK_MAX ];
1539 0 : ulong fork_id_cnt = fork_ancestry( stake_delegations, fork_idx, fork_ids );
1540 0 : int history_contiguous = !skip_stake_math && fd_sysvar_stake_history_is_contiguous( stake_history );
1541 0 : for( ulong i=fork_id_cnt; i; i-- ) {
1542 0 : apply_fork_delta( epoch, stake_history, warmup_cooldown_rate_epoch, use_fixed_point_stake_math,
1543 0 : history_contiguous, skip_stake_math, stake_delegations, fork_ids[ i-1UL ], stake_delegations_delta_stats );
1544 0 : }
1545 : /* The target is now rooted. Its children no longer need a parent
1546 : delta layer, and its descriptor can be freed by the caller. */
1547 0 : if( fork_idx!=USHORT_MAX ) {
1548 0 : fork_pool_ele_t * fork_pool = get_fork_pool( stake_delegations );
1549 0 : for( ulong i=0UL; i<fork_pool_max( fork_pool ); i++ ) {
1550 0 : if( fork_pool[ i ].parent==fork_idx ) fork_pool[ i ].parent = USHORT_MAX;
1551 0 : }
1552 0 : fork_pool[ fork_idx ].parent = USHORT_MAX;
1553 0 : }
1554 0 : if( FD_UNLIKELY( stake_delegations_delta_stats ) ) {
1555 0 : stake_delegations_delta_stats->root_cnt = root_pool_used( get_root_pool( stake_delegations ) ) + stake_delegations->disk_root_cnt_;
1556 0 : }
1557 0 : FD_LOG_DEBUG(( "effective_stake=%lu, activating_stake=%lu, deactivating_stake=%lu", stake_delegations->effective_stake, stake_delegations->activating_stake, stake_delegations->deactivating_stake ));
1558 :
1559 0 : fd_rwlock_unwrite( &stake_delegations->lock );
1560 0 : }
1561 :
1562 : void
1563 : fd_stake_delegations_iter_read_disk_delta_private( fd_stake_delegations_iter_t * iter,
1564 0 : uint delta_idx ) {
1565 0 : FD_CHECK_CRIT( (ulong)delta_idx<iter->stake_delegations->disk_delta_cnt_,
1566 0 : "invalid stake delegation iterator disk delta" );
1567 0 : disk_delta_t delta;
1568 0 : disk_delta_read( iter->stake_delegations, delta_idx, &delta );
1569 0 : iter->disk_ele = delta.delegation;
1570 0 : iter->ele = iter->disk_ele.is_tombstone ? NULL : &iter->disk_ele;
1571 0 : }
1572 :
1573 : void
1574 816 : fd_stake_delegations_iter_advance_disk_root_private( fd_stake_delegations_iter_t * iter ) {
1575 816 : fd_stake_delegations_t const * stake_delegations = iter->stake_delegations;
1576 816 : while( iter->disk_idx<stake_delegations->disk_root_cnt_ ) {
1577 0 : ulong batch_idx = iter->disk_idx % FD_STAKE_DELEGATIONS_ITER_BATCH_CNT;
1578 0 : if( FD_UNLIKELY( !batch_idx ) ) {
1579 0 : ulong cnt = fd_ulong_min( FD_STAKE_DELEGATIONS_ITER_BATCH_CNT, stake_delegations->disk_root_cnt_-iter->disk_idx );
1580 0 : disk_read( stake_delegations, iter->disk_batch,
1581 0 : disk_root_record_off( stake_delegations ) + iter->disk_idx*sizeof(fd_stake_delegation_t),
1582 0 : cnt*sizeof(fd_stake_delegation_t) );
1583 0 : }
1584 0 : fd_stake_delegation_t * root = iter->disk_batch + batch_idx;
1585 0 : iter->idx = stake_delegations->max_stake_accounts_ + iter->disk_idx;
1586 :
1587 0 : uint delta_idx = root->delta_idx;
1588 0 : if( FD_LIKELY( delta_idx==UINT_MAX ) ) {
1589 0 : iter->ele = root;
1590 0 : return;
1591 0 : }
1592 0 : if( FD_UNLIKELY( delta_idx & FD_STAKE_DELEGATIONS_DELTA_DISK_TAG ) ) {
1593 0 : fd_stake_delegations_iter_read_disk_delta_private( iter, delta_idx & FD_STAKE_DELEGATIONS_DELTA_IDX_MASK );
1594 0 : if( FD_LIKELY( iter->ele ) ) return;
1595 0 : } else {
1596 0 : fd_stake_delegation_t * delta = iter->delta_pool + delta_idx;
1597 0 : if( FD_LIKELY( !delta->is_tombstone ) ) {
1598 0 : iter->ele = delta;
1599 0 : return;
1600 0 : }
1601 0 : }
1602 0 : iter->disk_idx++;
1603 0 : }
1604 816 : iter->ele = NULL;
1605 816 : }
1606 :
1607 : fd_stake_delegations_iter_t *
1608 : fd_stake_delegations_iter_init( fd_stake_delegations_iter_t * iter,
1609 816 : fd_stake_delegations_t const * stake_delegations ) {
1610 816 : if( FD_UNLIKELY( !stake_delegations ) ) {
1611 0 : FD_LOG_CRIT(( "NULL stake_delegations" ));
1612 0 : }
1613 :
1614 816 : iter->root_pool = get_root_pool( stake_delegations );
1615 816 : iter->delta_pool = get_delta_pool( stake_delegations );
1616 816 : iter->stake_delegations = stake_delegations;
1617 816 : iter->idx = 0UL;
1618 816 : iter->wmk = stake_delegations->pool_idx_wmk_;
1619 816 : iter->root_max = stake_delegations->max_stake_accounts_;
1620 816 : iter->disk_idx = 0UL;
1621 816 : fd_stake_delegations_iter_advance_private( iter );
1622 :
1623 816 : return iter;
1624 816 : }
1625 :
1626 : static fd_stake_delegation_t const *
1627 : delta_ref_query( fd_stake_delegations_t const * stake_delegations,
1628 : uint delta_ref,
1629 0 : fd_stake_delegation_t * disk_copy ) {
1630 0 : if( FD_UNLIKELY( delta_ref & FD_STAKE_DELEGATIONS_DELTA_DISK_TAG ) ) {
1631 0 : uint idx = delta_ref & FD_STAKE_DELEGATIONS_DELTA_IDX_MASK;
1632 0 : FD_CHECK_CRIT( (ulong)idx<stake_delegations->disk_delta_cnt_, "invalid stake delegation disk delta reference" );
1633 0 : disk_delta_t disk_delta;
1634 0 : disk_delta_read( stake_delegations, idx, &disk_delta );
1635 0 : *disk_copy = disk_delta.delegation;
1636 0 : return disk_copy;
1637 0 : }
1638 0 : return get_delta_pool( stake_delegations ) + delta_ref;
1639 0 : }
1640 :
1641 : static void
1642 : mark_delta_one( fd_stake_delegations_t * stake_delegations,
1643 : ulong epoch,
1644 : fd_stake_history_t const * stake_history,
1645 : ulong * warmup_cooldown_rate_epoch,
1646 : int use_fixed_point_stake_math,
1647 : fd_stake_delegation_t const * delta,
1648 136 : uint delta_ref ) {
1649 136 : root_query_t query;
1650 136 : int found = root_query( stake_delegations, &delta->stake_account, &query );
1651 136 : fd_stake_delegation_t base;
1652 136 : if( FD_UNLIKELY( !found ) ) {
1653 0 : base = (fd_stake_delegation_t) {
1654 0 : .stake_account = delta->stake_account,
1655 0 : .delta_idx = delta_ref,
1656 0 : .dne_in_root = 1,
1657 0 : .in_use = 1,
1658 0 : };
1659 0 : root_insert( stake_delegations, &base );
1660 136 : } else {
1661 136 : base = *query.ele;
1662 136 : fd_stake_delegation_t disk_copy;
1663 136 : fd_stake_delegation_t const * old = base.delta_idx==UINT_MAX
1664 136 : ? &base
1665 136 : : delta_ref_query( stake_delegations, base.delta_idx, &disk_copy );
1666 136 : if( FD_LIKELY( !old->is_tombstone ) ) {
1667 136 : fd_stake_history_entry_t old_entry = fd_stake_delegation_activation_status(
1668 136 : old, epoch, stake_history, warmup_cooldown_rate_epoch, use_fixed_point_stake_math );
1669 136 : stake_delegations->effective_stake -= old_entry.effective;
1670 136 : stake_delegations->activating_stake -= old_entry.activating;
1671 136 : stake_delegations->deactivating_stake -= old_entry.deactivating;
1672 136 : }
1673 136 : base.delta_idx = delta_ref;
1674 136 : root_store( stake_delegations, &query, &base );
1675 136 : }
1676 :
1677 136 : if( FD_LIKELY( !delta->is_tombstone ) ) {
1678 136 : fd_stake_history_entry_t new_entry = fd_stake_delegation_activation_status(
1679 136 : delta, epoch, stake_history, warmup_cooldown_rate_epoch, use_fixed_point_stake_math );
1680 136 : stake_delegations->effective_stake += new_entry.effective;
1681 136 : stake_delegations->activating_stake += new_entry.activating;
1682 136 : stake_delegations->deactivating_stake += new_entry.deactivating;
1683 136 : }
1684 136 : }
1685 :
1686 : static void
1687 : unmark_delta_one( fd_stake_delegations_t * stake_delegations,
1688 : ulong epoch,
1689 : fd_stake_history_t const * stake_history,
1690 : ulong * warmup_cooldown_rate_epoch,
1691 : int use_fixed_point_stake_math,
1692 : fd_stake_delegation_t const * delta,
1693 136 : uint delta_ref ) {
1694 136 : root_query_t query;
1695 136 : if( FD_UNLIKELY( !root_query( stake_delegations, &delta->stake_account, &query ) ||
1696 136 : query.ele->delta_idx!=delta_ref ) ) return;
1697 136 : fd_stake_delegation_t base = *query.ele;
1698 :
1699 136 : if( FD_LIKELY( !delta->is_tombstone ) ) {
1700 136 : fd_stake_history_entry_t entry = fd_stake_delegation_activation_status(
1701 136 : delta, epoch, stake_history, warmup_cooldown_rate_epoch, use_fixed_point_stake_math );
1702 136 : stake_delegations->effective_stake -= entry.effective;
1703 136 : stake_delegations->activating_stake -= entry.activating;
1704 136 : stake_delegations->deactivating_stake -= entry.deactivating;
1705 136 : }
1706 :
1707 136 : if( FD_UNLIKELY( base.dne_in_root ) ) {
1708 0 : root_remove( stake_delegations, &query );
1709 0 : return;
1710 0 : }
1711 :
1712 136 : base.delta_idx = UINT_MAX;
1713 136 : root_store( stake_delegations, &query, &base );
1714 136 : fd_stake_history_entry_t entry = fd_stake_delegation_activation_status(
1715 136 : &base, epoch, stake_history, warmup_cooldown_rate_epoch, use_fixed_point_stake_math );
1716 136 : stake_delegations->effective_stake += entry.effective;
1717 136 : stake_delegations->activating_stake += entry.activating;
1718 136 : stake_delegations->deactivating_stake += entry.deactivating;
1719 136 : }
1720 :
1721 : void
1722 : fd_stake_delegations_view_begin( fd_stake_delegations_t * stake_delegations,
1723 : ulong epoch,
1724 : fd_stake_history_t const * stake_history,
1725 : ulong * warmup_cooldown_rate_epoch,
1726 : int use_fixed_point_stake_math,
1727 688 : ushort fork_idx ) {
1728 688 : fd_rwlock_write( &stake_delegations->lock );
1729 :
1730 688 : fd_stake_delegation_t * delta_pool = get_delta_pool( stake_delegations );
1731 688 : fork_pool_ele_t * fork_pool = get_fork_pool( stake_delegations );
1732 :
1733 688 : ushort fork_ids[ FD_STAKE_DELEGATIONS_FORK_MAX ];
1734 688 : ulong fork_id_cnt = fork_ancestry( stake_delegations, fork_idx, fork_ids );
1735 688 : stake_delegations->frontier_query_epoch = epoch;
1736 688 : stake_delegations->frontier_query_fork = fork_idx;
1737 1375 : for( ulong i=fork_id_cnt; i; i-- ) {
1738 687 : fork_pool_ele_t const * fork = fork_pool + fork_ids[ i-1UL ];
1739 823 : for( uint delta_ref=fork->delta_head; delta_ref!=UINT_MAX; delta_ref=delta_pool[ delta_ref ].fork_next ) {
1740 136 : fd_stake_delegation_t * delta_delegation = delta_pool + delta_ref;
1741 136 : mark_delta_one( stake_delegations, epoch, stake_history, warmup_cooldown_rate_epoch,
1742 136 : use_fixed_point_stake_math, delta_delegation, delta_ref );
1743 136 : }
1744 :
1745 687 : uint disk_idx = fork->disk_delta_head;
1746 687 : while( disk_idx!=UINT_MAX ) {
1747 0 : disk_delta_t disk_delta;
1748 0 : disk_delta_read( stake_delegations, disk_idx, &disk_delta );
1749 0 : mark_delta_one( stake_delegations, epoch, stake_history, warmup_cooldown_rate_epoch,
1750 0 : use_fixed_point_stake_math, &disk_delta.delegation,
1751 0 : disk_idx | FD_STAKE_DELEGATIONS_DELTA_DISK_TAG );
1752 0 : disk_idx = disk_delta.next;
1753 0 : }
1754 687 : }
1755 688 : }
1756 :
1757 : void
1758 : fd_stake_delegations_view_end( fd_stake_delegations_t * stake_delegations,
1759 : fd_stake_history_t const * stake_history,
1760 : ulong * warmup_cooldown_rate_epoch,
1761 688 : int use_fixed_point_stake_math ) {
1762 688 : ulong epoch = stake_delegations->frontier_query_epoch;
1763 :
1764 688 : fd_stake_delegation_t * delta_pool = get_delta_pool( stake_delegations );
1765 688 : fork_pool_ele_t * fork_pool = get_fork_pool( stake_delegations );
1766 :
1767 688 : ushort fork_ids[ FD_STAKE_DELEGATIONS_FORK_MAX ];
1768 688 : ulong fork_id_cnt = fork_ancestry( stake_delegations, stake_delegations->frontier_query_fork, fork_ids );
1769 1376 : for( ulong i=fork_id_cnt; i; i-- ) {
1770 688 : fork_pool_ele_t const * fork = fork_pool + fork_ids[ i-1UL ];
1771 824 : for( uint delta_ref=fork->delta_head; delta_ref!=UINT_MAX; delta_ref=delta_pool[ delta_ref ].fork_next ) {
1772 136 : fd_stake_delegation_t * delta_delegation = delta_pool + delta_ref;
1773 136 : unmark_delta_one( stake_delegations, epoch, stake_history, warmup_cooldown_rate_epoch,
1774 136 : use_fixed_point_stake_math, delta_delegation, delta_ref );
1775 136 : }
1776 :
1777 688 : uint disk_idx = fork->disk_delta_head;
1778 688 : while( disk_idx!=UINT_MAX ) {
1779 0 : disk_delta_t disk_delta;
1780 0 : disk_delta_read( stake_delegations, disk_idx, &disk_delta );
1781 0 : unmark_delta_one( stake_delegations, epoch, stake_history, warmup_cooldown_rate_epoch,
1782 0 : use_fixed_point_stake_math, &disk_delta.delegation,
1783 0 : disk_idx | FD_STAKE_DELEGATIONS_DELTA_DISK_TAG );
1784 0 : disk_idx = disk_delta.next;
1785 0 : }
1786 688 : }
1787 688 : stake_delegations->frontier_query_epoch = ULONG_MAX;
1788 688 : stake_delegations->frontier_query_fork = USHORT_MAX;
1789 688 : disk_root_maintain( stake_delegations );
1790 688 : fd_rwlock_unwrite( &stake_delegations->lock );
1791 688 : }
1792 :
1793 : void
1794 : fd_stake_delegations_invalidate_warmed( fd_stake_delegations_t * stake_delegations,
1795 25 : int force ) {
1796 25 : if( FD_LIKELY( !force && !stake_delegations->fp_warmed_awarded ) ) return;
1797 :
1798 19 : fd_stake_delegation_t * root_pool = get_root_pool( stake_delegations );
1799 105 : for( ulong i=0UL; i<stake_delegations->pool_idx_wmk_; i++ ) {
1800 86 : fd_stake_delegation_t * delegation = &root_pool[ i ];
1801 86 : if( FD_LIKELY( delegation->in_use && delegation->state==FD_STAKE_DELEGATION_STATE_WARMED ) ) {
1802 23 : delegation->state = FD_STAKE_DELEGATION_STATE_UNKNOWN;
1803 23 : }
1804 86 : }
1805 19 : for( uint i=0U; (ulong)i<stake_delegations->disk_root_cnt_; i++ ) {
1806 0 : fd_stake_delegation_t delegation;
1807 0 : disk_root_read( stake_delegations, i, &delegation );
1808 0 : if( FD_LIKELY( delegation.state==FD_STAKE_DELEGATION_STATE_WARMED ) ) {
1809 0 : delegation.state = FD_STAKE_DELEGATION_STATE_UNKNOWN;
1810 0 : disk_root_write( stake_delegations, i, &delegation );
1811 0 : }
1812 0 : }
1813 19 : stake_delegations->fp_warmed_awarded = 0;
1814 19 : }
|