Line data Source code
1 : #ifndef HEADER_fd_src_flamenco_stakes_fd_stake_delegations_h
2 : #define HEADER_fd_src_flamenco_stakes_fd_stake_delegations_h
3 :
4 : #include "../runtime/sysvar/fd_sysvar_base.h"
5 : #include "../accdb/fd_accdb.h"
6 :
7 12 : #define FD_STAKE_DELEGATIONS_MAGIC (0xF17EDA2CE757A3E1) /* FIREDANCER STAKE V1 */
8 :
9 : /* fd_stake_delegations_t is a cache of stake accounts mapping the
10 : pubkey of the stake account to various information including
11 : stake, activation/deactivation epoch, corresponding vote_account,
12 : and credits observed. This is used to quickly iterate through all
13 : of the stake delegations in the system during epoch boundary
14 : reward calculations.
15 :
16 : The implementation of fd_stake_delegations_t is split into two:
17 : 1. The entire set of stake delegations are stored in the in-memory
18 : root map/pool and, when needed, the disk root store. This root
19 : state is setup at boot (on snapshot load) and is not directly
20 : modified after that point.
21 : 2. As banks/forks execute, they will maintain a delta-based
22 : representation of the stake delegations. Each fork will hold its
23 : own set of deltas. These are then applied to the root set when
24 : the fork is finalized. Deltas share one map keyed by stake
25 : account and fork index, and one pool across all forks. Each
26 : fork maintains a list of its deltas. The caller is expected to
27 : create a new fork index for each bank and add deltas to it.
28 :
29 : Root and delta entries that exceed their respective in-memory pools
30 : spill to disk as full fd_stake_delegation_t records. Root records
31 : are keyed by stake pubkey. Delta records are keyed by fork index
32 : and stake pubkey, so sibling forks retain independent versions.
33 :
34 : There are some important invariants wrt fd_stake_delegations_t:
35 : 1. After execution has started, there will be no invalid stake
36 : accounts in the stake delegations struct.
37 : 2. The stake delegations struct can have valid delegations for vote
38 : accounts which no longer exist.
39 : 3. There are no stake accounts which are valid delegations which
40 : exist in the accounts database but not in fd_stake_delegations_t.
41 :
42 : In practice, fd_stake_delegations_t are updated in 3 cases:
43 : 1. During snapshot boot, snapin populates the root cache directly
44 : from the account stream. The cache is refreshed after all
45 : accounts are loaded to resolve duplicate account versions, remove
46 : stale entries, and calculate activation state.
47 :
48 : https://github.com/anza-xyz/agave/blob/v2.3.6/runtime/src/bank.rs#L1780-L1806
49 :
50 : 2. After transaction execution. If an update is made to a stake
51 : account, the updated state is reflected in the cache (or the entry
52 : is evicted).
53 : 3. During rewards distribution. Stake accounts are partitioned over
54 : several hundred slots where their rewards are distributed. In this
55 : case, the cache is updated to reflect each stake account post
56 : reward distribution.
57 : The stake accounts are read-only during the epoch boundary.
58 :
59 : The concurrency model is: every mutating operation takes the struct's
60 : write lock for its whole duration, so mutators are safe to call
61 : concurrently from any tile. fd_stake_delegations_{mark,unmark}_delta
62 : and the iterator are the exception: the caller holds the write lock
63 : across the whole mark/iterate/unmark bracket.
64 :
65 : max_disk_records bounds the number of delta records that can spill
66 : to disk. The disk root capacity is max_stake_accounts plus twice
67 : max_disk_records. */
68 :
69 384 : #define FD_STAKE_DELEGATIONS_ALIGN (128UL)
70 1868 : #define FD_STAKE_DELEGATIONS_FORK_MAX (65534UL)
71 72 : #define FD_STAKE_DELEGATIONS_DELTA_POOL_DIVISOR (2UL)
72 :
73 : /* 123454 is vote stakes, 123455 is cost tracker, 123456 is epoch
74 : credits, 123458/123459 are store, 123460/123461 are accdb, and
75 : 123462+ are reserved by XDP. */
76 :
77 0 : #define FD_STAKE_DELEGATIONS_FD (123457)
78 :
79 : /* delta_idx uses the high bit to distinguish a dense disk-delta index
80 : from an in-memory delta-pool index. */
81 :
82 0 : #define FD_STAKE_DELEGATIONS_DELTA_DISK_TAG (1U<<31)
83 0 : #define FD_STAKE_DELEGATIONS_DELTA_IDX_MASK (FD_STAKE_DELEGATIONS_DELTA_DISK_TAG-1U)
84 :
85 : /* Most stake delegations are stable. So intuitively, there should be a
86 : way to return their effective stake in O(1). Essentially, at a given
87 : target_epoch, if we know that the delegation is in a stable state for
88 : the purposes of effective stake evaluation, then we can simply return
89 : the fully activated stake for WARMED, or 0 for COOLED, without
90 : running any warmup/cooldown simulation. The vast majority of
91 : delegations are in fact stable and can take the fast path for
92 : effective stake evaluation.
93 :
94 : We trust a tag's prescription of stable state (WARMED/COOLED) if
95 :
96 : - The delegation record (stake,activation_epoch,deactivation_epoch)
97 : hasn't changed since the delegation was most recently evaluated and
98 : tagged at tag_epoch
99 : - tag_epoch<=target_epoch
100 :
101 : Condition #1 is maintained by how delegations are tagged. Only root
102 : records can take on non-UNKNOWN tags. Delta records are
103 : unconditionally UNKNOWN. All delegation-updating operations funnel
104 : the delegation through a delta, so the delegation effectively gets
105 : invalidated for stable state query purposes.
106 :
107 : Condition #2 ultimately has to be maintained by the user of the tag
108 : who provides target_epoch. A key invariant here is that stable state
109 : tags are awarded when the delta list gets folded into the root state,
110 : aka when a block roots. Currently, the only use cases of the tag are
111 : at the boundary.
112 :
113 : - For the refresh_vote_accounts() use case, the target_epoch is the
114 : upcoming epoch, which is naturally the largest epoch in the
115 : cluster. Since tag_epoch was sometime in the past when a block
116 : rooted, tag_epoch<=target_epoch holds trivially.
117 : - For the points calculation use case, recall that rewarded_epoch is
118 : the just-ended epoch. Since we are at the boundary of
119 : rewarded_epoch=>rewarded_epoch+1, we know that
120 : tag_epoch<=rewarded_epoch, because no slot has rooted for
121 : rewarded_epoch+1 yet. So if we constrain the target_epoch to be
122 : exactly rewarded_epoch, we get tag_epoch<=target_epoch. It doesn't
123 : hurt that most delegations are up to date on rewards payout, and
124 : the only epoch for which they have eligible points is precisely the
125 : rewarded_epoch.
126 : - We disable the tag fast path for recalculation during boot, because
127 : tags are computed fresh at the snapshot root, and so
128 : rewarded_epoch<tag_epoch.
129 :
130 : WARMED and COOLED are stable states and are the only tags that the
131 : fast paths act on. The unstable state tags (WARMING/COOLING) are
132 : defined for clarity and do not enable any fast path. As a side note,
133 : WARMING tags get a chance to be promoted to WARMED if the delegation
134 : gets any inflation rewards or is otherwise written. At rewards
135 : distribution time, the delegation will re-enter a delta tier and
136 : shortly afterwards get a chance to be re-classified when the
137 : distribution block roots. Fresh dust delegations that don't get any
138 : rewards will be sticky WARMING until the next boot or a write. */
139 1353 : #define FD_STAKE_DELEGATION_STATE_UNKNOWN ((uchar)0)
140 106 : #define FD_STAKE_DELEGATION_STATE_WARMING ((uchar)1) /* activating */
141 717 : #define FD_STAKE_DELEGATION_STATE_WARMED ((uchar)2) /* effective=delegated */
142 25 : #define FD_STAKE_DELEGATION_STATE_COOLING ((uchar)3) /* deactivating */
143 218 : #define FD_STAKE_DELEGATION_STATE_COOLED ((uchar)4) /* effective=0 */
144 :
145 : struct fd_stake_delegation_key {
146 : fd_pubkey_t stake_account;
147 : ushort fork_idx;
148 : };
149 : typedef struct fd_stake_delegation_key fd_stake_delegation_key_t;
150 :
151 : struct fd_stake_delegation {
152 : /* The delta map uses key; root lookups use stake_account directly. */
153 : union {
154 : fd_stake_delegation_key_t key;
155 : struct {
156 : fd_pubkey_t stake_account;
157 : ushort fork_idx; /* In-memory delta's fork index */
158 : };
159 : };
160 : ushort activation_epoch;
161 : ushort deactivation_epoch;
162 : union {
163 : /* No storage conflict because one is for delta records and the
164 : other for root records. */
165 : uchar is_tombstone; /* Internal delta usage */
166 : uchar dne_in_root; /* Tracking for stake delegation iteration */
167 : };
168 : uchar in_use : 1; /* For the in-memory root pool only. Not meaningful in the
169 : delta pool. Set to 1 if this element holds a live delegation
170 : present in the root map, 0 if the element has been reclaimed. */
171 : uchar state : 3; /* Can only be non-UNKNOWN in a root record. */
172 : fd_pubkey_t vote_account;
173 : ulong stake;
174 : ulong lamports;
175 : ulong credits_observed;
176 : uint acc_dlen;
177 : uint next_; /* Internal pool/map usage */
178 : union {
179 : uint delta_idx; /* Root's in-memory/disk delta reference for iteration */
180 : uint fork_next; /* Next in-memory delta in this fork */
181 : };
182 : uint slot; /* Snapshot loading only: slot of the account version this
183 : record came from, newest wins. Runtime writes store 0. */
184 : };
185 : typedef struct fd_stake_delegation fd_stake_delegation_t;
186 :
187 : FD_STATIC_ASSERT( sizeof(fd_stake_delegation_t)==112UL, fd_stake_delegation );
188 :
189 : typedef struct fd_stake_delegations fd_stake_delegations_t;
190 :
191 0 : #define FD_STAKE_DELEGATIONS_ITER_BATCH_CNT (128UL)
192 :
193 : struct fd_stake_delegations_iter {
194 : fd_stake_delegation_t * root_pool;
195 : fd_stake_delegation_t * delta_pool;
196 : fd_stake_delegation_t * ele;
197 : fd_stake_delegations_t const * stake_delegations;
198 : ulong idx; /* externally visible index */
199 : ulong wmk; /* in-memory root watermark */
200 : ulong root_max; /* in-memory root capacity */
201 : ulong disk_idx; /* dense disk-root cursor */
202 : fd_stake_delegation_t disk_ele;
203 : fd_stake_delegation_t disk_batch[ FD_STAKE_DELEGATIONS_ITER_BATCH_CNT ];
204 : };
205 : typedef struct fd_stake_delegations_iter fd_stake_delegations_iter_t;
206 :
207 : #include "fd_stake_delegations_private.h"
208 :
209 : FD_PROTOTYPES_BEGIN
210 :
211 : /* Classify stake given the activation status evaluated at the provided
212 : epoch. The provided epoch is expected to be >= activation epoch. */
213 : static inline uchar
214 : fd_stake_delegation_classify( fd_stake_delegation_t const * delegation,
215 : fd_stake_history_entry_t activation_status,
216 1039 : ulong epoch ) {
217 : /* Activation epoch in Agave's stake program is either clock.epoch, or
218 : inherited from an existing activation epoch, so activation epoch <=
219 : current epoch always holds for delegations created by the stake
220 : program. Synthetic inputs do not conform to this, so we mark them
221 : UNKNOWN to force the slow path. */
222 1039 : if( FD_UNLIKELY( delegation->activation_epoch!=(ushort)USHORT_MAX && epoch<delegation->activation_epoch ) ) {
223 172 : return FD_STAKE_DELEGATION_STATE_UNKNOWN;
224 172 : }
225 :
226 867 : if( activation_status.activating>0UL ) return FD_STAKE_DELEGATION_STATE_WARMING;
227 761 : if( activation_status.deactivating>0UL ) return FD_STAKE_DELEGATION_STATE_COOLING;
228 736 : if( activation_status.effective==delegation->stake && delegation->deactivation_epoch==(ushort)USHORT_MAX ) return FD_STAKE_DELEGATION_STATE_WARMED;
229 :
230 : /* When evaluated at >= activation_epoch, (0,0,0) implies a fully
231 : cooled delegation. One might think that we could simply
232 :
233 : if( activation_status.effective==0UL ) return FD_STAKE_DELEGATION_STATE_COOLED;
234 :
235 : and life would be great. In an unfortunate turn, Agave has a
236 : special branch that will assume stake has been fully activated if
237 : the activation epoch is not found in the stake history sysvar,
238 : regardless of whether the delegation fully warmed up or not when
239 : the simulation ran faithfully from the activation epoch.
240 :
241 : https://github.com/solana-program/stake/blob/interface%40v4.3.1/interface/src/state.rs#L969
242 :
243 : This means that a COOLED tag doesn't necessarily survive against
244 : future stake history sysvars. As the stake history sysvar window
245 : advances and evicts older epochs, a delegation's activation epoch
246 : will eventually be evicted. At that point an effective stake
247 : simulation would cooldown from the full delegated amount, which
248 : might be more than the effective stake simulated at tag time, if
249 : the delegation only partially warmed up at deactivation epoch.
250 : This can in theory lead to a nonzero effective stake at the target
251 : epoch, once the activation epoch is evicted, contradicting the
252 : COOLED tag. Note that this is exceedingly hard to pull off as it
253 : requires that (1) the delegation only partially warmed up at
254 : deactivation, and (2) the delegation failed to fully cooldown from
255 : the full delegated amount over the potentially hundreds of epochs
256 : between deactivation epoch and target epoch. AKA either warmup or
257 : cooldown congestion in the cluster over an extended period of time.
258 : The upshot is that we will only award the COOLED tag when the
259 : delegation is always COOLED independent of the history sysvar.
260 : This safe verdict loses by a few millis at the boundary, mostly in
261 : the refresh_vote_accounts() phase, compared to the naive but unsafe
262 : classify.
263 :
264 : https://github.com/solana-program/stake/blob/interface%40v4.3.1/interface/src/state.rs#L877
265 : https://github.com/solana-program/stake/blob/interface%40v4.3.1/interface/src/state.rs#L896
266 :
267 : Note that the same misfortune doesn't apply to the WARMED tag. The
268 : sysvar query miss's "assume fully effective" bias means that a
269 : WARMED delegation stays warmed forever until it's instructed to
270 : deactivate. */
271 54 : if( epoch>(delegation->deactivation_epoch+FD_SYSVAR_STAKE_HISTORY_CAP) || delegation->activation_epoch==delegation->deactivation_epoch || delegation->stake==0UL ) return FD_STAKE_DELEGATION_STATE_COOLED;
272 45 : return FD_STAKE_DELEGATION_STATE_UNKNOWN;
273 54 : }
274 :
275 :
276 : /* fd_stake_delegations_align returns the alignment of the stake
277 : delegations struct. */
278 :
279 : ulong
280 : fd_stake_delegations_align( void );
281 :
282 : /* fd_stake_delegations_footprint returns the footprint of the stake
283 : delegations struct for the given root pool capacity and max live
284 : slots. The in-memory delta pool holds one eighth as many elements,
285 : with a minimum capacity of one; further deltas spill to disk. */
286 :
287 : ulong
288 : fd_stake_delegations_footprint( ulong max_stake_accounts,
289 : ulong max_live_slots );
290 :
291 : /* fd_stake_delegations_new creates a new stake delegations struct with
292 : the given backing descriptor, in-memory pool capacity, disk spill
293 : capacity, and max live slots. Every process joining the object must
294 : have the same backing file open at disk_fd. It formats a memory
295 : region sized from the pool capacity and per-fork delta structures. */
296 :
297 : void *
298 : fd_stake_delegations_new( void * mem,
299 : int disk_fd,
300 : ulong seed,
301 : ulong max_stake_accounts,
302 : ulong max_disk_records,
303 : ulong max_live_slots );
304 :
305 : /* fd_stake_delegations_join joins a stake delegations struct from a
306 : memory region. disk_fd must match the descriptor number provided to
307 : fd_stake_delegations_new. There can be multiple valid joins for a
308 : given memory region but the caller is responsible for accessing
309 : memory in a thread-safe manner. */
310 :
311 : fd_stake_delegations_t *
312 : fd_stake_delegations_join( void * mem,
313 : int disk_fd );
314 :
315 : /* fd_stake_delegations_reset resets delegations to the post-new state. */
316 :
317 : void
318 : fd_stake_delegations_reset( fd_stake_delegations_t * stake_delegations );
319 :
320 : /* Read the current aggregate stake totals. During a frontier query,
321 : these include the marked deltas. The returned epoch is zero; callers
322 : supply the epoch when constructing a stake-history entry. The caller
323 : must exclude concurrent mutations or hold the frontier-query lock. */
324 :
325 : fd_stake_history_entry_t
326 : fd_stake_delegations_totals( fd_stake_delegations_t const * stake_delegations );
327 :
328 : /* Replace aggregate totals when the root crosses an epoch boundary.
329 : Acquires the store write lock; do not call inside a frontier query. */
330 :
331 : void
332 : fd_stake_delegations_set_totals( fd_stake_delegations_t * stake_delegations,
333 : ulong effective,
334 : ulong activating,
335 : ulong deactivating );
336 :
337 : /* fd_stake_delegations_root_update upserts a stake delegation into the
338 : in-memory or disk root, overriding any previous data. */
339 :
340 : void
341 : fd_stake_delegations_root_update( fd_stake_delegations_t * stake_delegations,
342 : fd_pubkey_t const * stake_account,
343 : fd_pubkey_t const * vote_account,
344 : ulong stake,
345 : ulong activation_epoch,
346 : ulong deactivation_epoch,
347 : ulong credits_observed,
348 : ulong lamports,
349 : uint acc_dlen );
350 :
351 : /* fd_stake_delegations_prune_inactive_root removes root delegations
352 : that are inactive in both epoch and epoch-1. This function removes
353 : all inactive delegations from the in-memory and disk roots. It is a
354 : parallel to Agave removing inactive stake accounts directly at the
355 : epoch boundary. Returns the number of delegations removed. When
356 : emit_bank is non-NULL, a runtime_stake_delegation remove event is
357 : emitted for each removal (NULL to skip). */
358 :
359 : ulong
360 : fd_stake_delegations_prune_inactive_root( fd_stake_delegations_t * stake_delegations,
361 : ulong epoch,
362 : fd_stake_history_t const * stake_history,
363 : ulong * warmup_cooldown_rate_epoch,
364 : int use_fixed_point_stake_math,
365 : fd_bank_t const * emit_bank );
366 :
367 : /* fd_stake_delegations_refresh finalizes the root after a snapshot or
368 : genesis load, before any slot executes. Pure in-memory pass: drops
369 : tombstones, drops delegations inactive in both epoch and epoch-1
370 : when remove_inactive_stakes is set, sets the warmup rate and state
371 : tag, accumulates the stake totals and refills the pool from disk
372 : roots. stake_history may be NULL. */
373 :
374 : void
375 : fd_stake_delegations_refresh( fd_stake_delegations_t * stake_delegations,
376 : ulong epoch,
377 : fd_stake_history_t const * stake_history,
378 : ulong * warmup_cooldown_rate_epoch,
379 : int use_fixed_point_stake_math,
380 : int remove_inactive_stakes );
381 :
382 : /* fd_stake_delegations_vote_account_status sums the root activation
383 : status of every delegation to vote_account at epoch. Acquires the
384 : store write lock; do not call inside a frontier query. stake_history
385 : may be NULL. */
386 :
387 : fd_stake_history_entry_t
388 : fd_stake_delegations_vote_account_status( fd_stake_delegations_t * stake_delegations,
389 : fd_pubkey_t const * vote_account,
390 : ulong epoch,
391 : fd_stake_history_t const * stake_history,
392 : ulong * warmup_cooldown_rate_epoch,
393 : int use_fixed_point_stake_math );
394 :
395 : /* fd_stake_delegations_new_fork allocates a child of parent_fork_idx.
396 : USHORT_MAX indicates that the parent is the fork. */
397 :
398 : ushort
399 : fd_stake_delegations_new_fork( fd_stake_delegations_t * stake_delegations,
400 : ushort parent_fork_idx );
401 :
402 : /* fd_stake_delegations_fork_update upserts a stake delegation into the
403 : fork's deltas, or into the root when fork_idx is USHORT_MAX. slot is
404 : the slot of the account version being written: a write is dropped if
405 : the entry already holds a newer slot, so snapin tiles can apply
406 : versions in any order. Runtime callers pass 0. */
407 :
408 : void
409 : fd_stake_delegations_fork_update( fd_stake_delegations_t * stake_delegations,
410 : ushort fork_idx,
411 : ulong slot,
412 : fd_pubkey_t const * stake_account,
413 : fd_pubkey_t const * vote_account,
414 : ulong stake,
415 : ulong activation_epoch,
416 : ulong deactivation_epoch,
417 : ulong credits_observed,
418 : ulong lamports,
419 : uint acc_dlen );
420 :
421 : /* fd_stake_delegations_fork_remove inserts a tombstone stake delegation
422 : entry for the given fork, or for the root when fork_idx is
423 : USHORT_MAX. The function will not actually remove or free any
424 : resources corresponding to the stake account. The reason a
425 : tombstone is stored is because each fork corresponds to a set of
426 : stake delegation deltas for a given slot. If an entry already
427 : exists for the stake account in this fork, it is overwritten in
428 : place. slot is as in fd_stake_delegations_fork_update.
429 : replacing_full_entry means the replaced version was loaded by the
430 : full snapshot and this one by the incremental, so an account not in
431 : the root is left alone. */
432 :
433 : void
434 : fd_stake_delegations_fork_remove( fd_stake_delegations_t * stake_delegations,
435 : ushort fork_idx,
436 : ulong slot,
437 : fd_pubkey_t const * stake_account,
438 : int replacing_full_entry );
439 :
440 : /* fd_stake_delegations_evict_fork frees a fork's deltas and its ID.
441 : The caller must no longer need this fork or query its descendants.
442 : Root application detaches surviving children before this is called. */
443 :
444 : void
445 : fd_stake_delegations_evict_fork( fd_stake_delegations_t * stake_delegations,
446 : ushort fork_idx );
447 :
448 : /* upserts and removes accumulate across applications. root_cnt is
449 : overwritten with the current in-memory and disk root count. */
450 :
451 : struct fd_stake_delegations_delta_stats {
452 : ulong upserts;
453 : ulong removes;
454 : ulong root_cnt;
455 : };
456 : typedef struct fd_stake_delegations_delta_stats fd_stake_delegations_delta_stats_t;
457 :
458 : /* fd_stake_delegations_advance_root applies the target and its
459 : ancestors in root-to-leaf order. Its direct children then inherit
460 : from rooted state. The caller frees the applied fork descriptors
461 : with evict_fork. USHORT_MAX applies no deltas.
462 :
463 : skip_stake_math applies the deltas without updating totals or state,
464 : for the snapshot loader; fd_stake_delegations_refresh recomputes them
465 : afterwards and the stake history arguments may be NULL.
466 :
467 : If stake_delegations_delta_stats is non-NULL, upserts and removes
468 : accumulate into it (caller zeroes). */
469 :
470 : void
471 : fd_stake_delegations_advance_root( ulong epoch,
472 : fd_stake_history_t const * stake_history,
473 : ulong * warmup_cooldown_rate_epoch,
474 : int use_fixed_point_stake_math,
475 : int skip_stake_math,
476 : fd_stake_delegations_t * stake_delegations,
477 : ushort fork_idx,
478 : fd_stake_delegations_delta_stats_t * stake_delegations_delta_stats );
479 :
480 : /* fd_stake_delegations_view_{begin,end} temporarily overlay
481 : delta elements from the target fork's ancestry onto the base/root
482 : stake delegation stores. This allows the caller to iterate over the
483 : delegations for a bank using the root and its deltas without creating
484 : a copy.
485 :
486 : Under the hood, each in-memory or disk root record points to the
487 : corresponding in-memory or disk delta. If an element is inserted by
488 : a delta, a temporary root record is added and then removed by
489 : view_end. These functions also temporarily update and
490 : unwind the stake totals for the current root. */
491 :
492 : void
493 : fd_stake_delegations_view_begin( fd_stake_delegations_t * stake_delegations,
494 : ulong epoch,
495 : fd_stake_history_t const * stake_history,
496 : ulong * warmup_cooldown_rate_epoch,
497 : int use_fixed_point_stake_math,
498 : ushort fork_idx );
499 :
500 : void
501 : fd_stake_delegations_view_end( fd_stake_delegations_t * stake_delegations,
502 : fd_stake_history_t const * stake_history,
503 : ulong * warmup_cooldown_rate_epoch,
504 : int use_fixed_point_stake_math );
505 :
506 : /* Iterator API for stake delegations. The iterator is initialized with
507 : a call to fd_stake_delegations_iter_init. The caller is responsible
508 : for managing the memory for the iterator. It is safe to call
509 : fd_stake_delegations_iter_next if the result of
510 : fd_stake_delegations_iter_done()==0. It is safe to call
511 : fd_stake_delegations_iter_ele() to get the current stake delegation
512 : or fd_stake_delegations_iter_idx() to get the index of the current
513 : stake delegation. It is not safe to modify the stake delegation
514 : while iterating through it.
515 :
516 : Under the hood, the iterator walks in-memory roots followed by dense
517 : disk roots, redirecting through tagged in-memory/disk delta references
518 : for entries a marked fork has changed. Disk roots are read in batches
519 : of up to 128 records (14 KiB per iterator). Disk records are returned
520 : through iterator-owned storage, so that pointer remains valid only
521 : until the next call to fd_stake_delegations_iter_next. Reinitialize
522 : the iterator after modifying the store or changing the marked forks.
523 :
524 : Example use:
525 :
526 : fd_stake_delegations_iter_t iter_[1];
527 : for( fd_stake_delegations_iter_t * iter = fd_stake_delegations_iter_init( iter_, stake_delegations );
528 : !fd_stake_delegations_iter_done( iter );
529 : fd_stake_delegations_iter_next( iter ) ) {
530 : fd_stake_delegation_t const * stake_delegation = fd_stake_delegations_iter_ele( iter );
531 : }
532 : */
533 :
534 : fd_stake_delegations_iter_t *
535 : fd_stake_delegations_iter_init( fd_stake_delegations_iter_t * iter,
536 : fd_stake_delegations_t const * stake_delegations );
537 :
538 : static inline fd_stake_delegation_t const *
539 2119 : fd_stake_delegations_iter_ele( fd_stake_delegations_iter_t const * iter ) {
540 2119 : return iter->ele;
541 2119 : }
542 :
543 : static inline ulong
544 828 : fd_stake_delegations_iter_idx( fd_stake_delegations_iter_t * iter ) {
545 828 : return iter->idx;
546 828 : }
547 :
548 : static inline void
549 2119 : fd_stake_delegations_iter_next( fd_stake_delegations_iter_t * iter ) {
550 2119 : if( FD_LIKELY( iter->idx<iter->root_max ) ) {
551 2119 : iter->idx++;
552 2119 : fd_stake_delegations_iter_advance_private( iter );
553 2119 : } else {
554 0 : iter->disk_idx++;
555 0 : fd_stake_delegations_iter_advance_disk_root_private( iter );
556 0 : }
557 2119 : }
558 :
559 : static inline int
560 2935 : fd_stake_delegations_iter_done( fd_stake_delegations_iter_t * iter ) {
561 2935 : return !iter->ele;
562 2935 : }
563 :
564 : /* Invalidates WARMED tags in the in-memory and disk roots. With
565 : force==0, skip the scan if no tag was awarded under floating point
566 : math. Use this when upgrade_bpf_stake_program_to_v5_1 is active.
567 : With force!=0, invalidate all WARMED tags regardless of how they were
568 : calculated, for example when stake history is not contiguous. The
569 : next scan reevaluates each formerly WARMED delegation.
570 :
571 : Unlike the other mutators, this one does not take the write lock
572 : itself: its only callers run inside the boundary's
573 : mark/iterate/unmark bracket, which already holds it. */
574 :
575 : void
576 : fd_stake_delegations_invalidate_warmed( fd_stake_delegations_t * stake_delegations,
577 : int force );
578 :
579 : FD_PROTOTYPES_END
580 :
581 : #endif /* HEADER_fd_src_flamenco_stakes_fd_stake_delegations_h */
|