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/fd_runtime_const.h"
5 : #include "../runtime/sysvar/fd_sysvar_base.h"
6 : #include "../accdb/fd_accdb.h"
7 : #include "../fd_rwlock.h"
8 :
9 117 : #define FD_STAKE_DELEGATIONS_MAGIC (0xF17EDA2CE757A3E1) /* FIREDANCER STAKE V1 */
10 :
11 : /* fd_stake_delegations_t is a cache of stake accounts mapping the
12 : pubkey of the stake account to various information including
13 : stake, activation/deactivation epoch, corresponding vote_account,
14 : credits observed, and warmup cooldown rate. This is used to quickly
15 : iterate through all of the stake delegations in the system during
16 : epoch boundary reward calculations.
17 :
18 : The implementation of fd_stake_delegations_t is split into two:
19 : 1. The entire set of stake delegations are stored in the root as a
20 : map/pool pair. This root state is setup at boot (on snapshot
21 : load) and is not directly modified after that point.
22 : 2. As banks/forks execute, they will maintain a delta-based
23 : representation of the stake delegations. Each fork will hold its
24 : own set of deltas. These are then applied to the root set when
25 : the fork is finalized. This is implemented as each bank having
26 : its own map of deltas which are allocated from a pool shared
27 : across all stake delegation forks. The caller is expected to
28 : create a new fork index for each bank and add deltas to it.
29 :
30 : There is a third structure, the pubkey fallback tier, which just
31 : holds one slim (pubkey, refcnt) entry for every stake account
32 : referenced by the root or by a live fork delta. It will contain a
33 : superset of all stake accounts across forks and the root. The
34 : purpose of this is to handle cases where the existing capacity gets
35 : exceeded. Regular operation will never use this tier for execution.
36 : It is meant to allow rewards to continue (with the help of the
37 : accounts database) in the event that the existing capacity gets
38 : exceeded.
39 :
40 : There are some important invariants wrt fd_stake_delegations_t:
41 : 1. After execution has started, there will be no invalid stake
42 : accounts in the stake delegations struct.
43 : 2. The stake delegations struct can have valid delegations for vote
44 : accounts which no longer exist.
45 : 3. There are no stake accounts which are valid delegations which
46 : exist in the accounts database but not in fd_stake_delegations_t.
47 :
48 : In practice, fd_stake_delegations_t are updated in 3 cases:
49 : 1. During snapshot boot, snapin populates the root cache directly
50 : from the account stream. The cache is refreshed after all
51 : accounts are loaded to resolve duplicate account versions, remove
52 : stale entries, and calculate activation state.
53 :
54 : https://github.com/anza-xyz/agave/blob/v2.3.6/runtime/src/bank.rs#L1780-L1806
55 :
56 : 2. After transaction execution. If an update is made to a stake
57 : account, the updated state is reflected in the cache (or the entry
58 : is evicted).
59 : 3. During rewards distribution. Stake accounts are partitioned over
60 : several hundred slots where their rewards are distributed. In this
61 : case, the cache is updated to reflect each stake account post
62 : reward distribution.
63 : The stake accounts are read-only during the epoch boundary.
64 :
65 : The concurrency model is: every mutating operation takes the struct's
66 : write lock for its whole duration, so mutators are safe to call
67 : concurrently from any tile. fd_stake_delegations_{mark,unmark}_delta
68 : and the iterator are the exception: the caller holds the write lock
69 : across the whole mark/iterate/unmark bracket. */
70 :
71 3795 : #define FD_STAKE_DELEGATIONS_ALIGN (128UL)
72 : #define FD_STAKE_DELEGATIONS_FORK_MAX (4096UL)
73 69480 : #define FD_STAKE_DELEGATIONS_FORK_MAP_CHAIN_CNT (8192UL)
74 :
75 : /* The warmup cooldown rate can only be one of two values: 0.25 or 0.09.
76 : The reason that the double is mapped to an enum is to save space in
77 : the stake delegations struct. */
78 5514 : #define FD_STAKE_DELEGATIONS_WARMUP_COOLDOWN_RATE_ENUM_025 (0)
79 345 : #define FD_STAKE_DELEGATIONS_WARMUP_COOLDOWN_RATE_ENUM_009 (1)
80 795 : #define FD_STAKE_DELEGATIONS_WARMUP_COOLDOWN_RATE_025 (0.25)
81 321 : #define FD_STAKE_DELEGATIONS_WARMUP_COOLDOWN_RATE_009 (0.09)
82 :
83 : /* fd_stake_warmup_cooldown_rate gives the warmup/cooldown rate enum
84 : for a given epoch. In Agave, the per-delegation warmup_cooldown_rate
85 : field was deprecated (since v1.16.7) and unused in calculations.
86 : The rate is always determined by the epoch. */
87 :
88 : static inline uchar
89 201 : fd_stake_warmup_cooldown_rate( ulong current_epoch, ulong * new_rate_activation_epoch ) {
90 201 : ulong activation_epoch = new_rate_activation_epoch ? *new_rate_activation_epoch : ULONG_MAX;
91 201 : return current_epoch<activation_epoch
92 201 : ? (uchar)FD_STAKE_DELEGATIONS_WARMUP_COOLDOWN_RATE_ENUM_025
93 201 : : (uchar)FD_STAKE_DELEGATIONS_WARMUP_COOLDOWN_RATE_ENUM_009;
94 201 : }
95 :
96 : /* Most stake delegations are stable. So intuitively, there should be a
97 : way to return their effective stake in O(1). Essentially, at a given
98 : target_epoch, if we know that the delegation is in a stable state for
99 : the purposes of effective stake evaluation, then we can simply return
100 : the fully activated stake for WARMED, or 0 for COOLED, without
101 : running any warmup/cooldown simulation. The vast majority of
102 : delegations are in fact stable and can take the fast path for
103 : effective stake evaluation.
104 :
105 : We trust a tag's prescription of stable state (WARMED/COOLED) if
106 :
107 : - The delegation record (stake,activation_epoch,deactivation_epoch)
108 : hasn't changed since the delegation was most recently evaluated and
109 : tagged at tag_epoch
110 : - tag_epoch<=target_epoch
111 :
112 : Condition #1 is maintained by how delegations are tagged. Only root
113 : pool elements can take on non-UNKNOWN tags. Delta pool elements are
114 : unconditionally UNKNOWN. All delegation-updating operations funnel
115 : the delegation through the delta pool, so the delegation effectively
116 : gets invalidated for stable state query purposes. Delegations that
117 : the iterator resolves out of the accounts database in pubkey fallback
118 : mode are likewise unconditionally UNKNOWN.
119 :
120 : Condition #2 ultimately has to be maintained by the user of the tag
121 : who provides target_epoch. A key invariant here is that stable state
122 : tags are awarded when the delta list gets folded into the root pool,
123 : aka when a block roots. Currently, the only use cases of the tag are
124 : at the boundary.
125 :
126 : - For the refresh_vote_accounts() use case, the target_epoch is the
127 : upcoming epoch, which is naturally the largest epoch in the
128 : cluster. Since tag_epoch was sometime in the past when a block
129 : rooted, tag_epoch<=target_epoch holds trivially.
130 : - For the points calculation use case, recall that rewarded_epoch is
131 : the just-ended epoch. Since we are at the boundary of
132 : rewarded_epoch=>rewarded_epoch+1, we know that
133 : tag_epoch<=rewarded_epoch, because no slot has rooted for
134 : rewarded_epoch+1 yet. So if we constrain the target_epoch to be
135 : exactly rewarded_epoch, we get tag_epoch<=target_epoch. It doesn't
136 : hurt that most delegations are up to date on rewards payout, and
137 : the only epoch for which they have eligible points is precisely the
138 : rewarded_epoch.
139 : - We disable the tag fast path for recalculation during boot, because
140 : tags are computed fresh at the snapshot root, and so
141 : rewarded_epoch<tag_epoch.
142 :
143 : WARMED and COOLED are stable states and are the only tags that the
144 : fast paths act on. The unstable state tags (WARMING/COOLING) are
145 : defined for clarity and do not enable any fast path. As a side note,
146 : WARMING tags get a chance to be promoted to WARMED if the delegation
147 : gets any inflation rewards or is otherwise written. At rewards
148 : distribution time, the delegation will re-enter the delta pool and
149 : shortly afterwards get a chance to be re-classified when the
150 : distribution block roots. Fresh dust delegations that don't get any
151 : rewards will be sticky WARMING until the next boot or a write. */
152 4818 : #define FD_STAKE_DELEGATION_STATE_UNKNOWN ((uchar)0)
153 18 : #define FD_STAKE_DELEGATION_STATE_WARMING ((uchar)1) /* activating */
154 132 : #define FD_STAKE_DELEGATION_STATE_WARMED ((uchar)2) /* effective=delegated */
155 3 : #define FD_STAKE_DELEGATION_STATE_COOLING ((uchar)3) /* deactivating */
156 267 : #define FD_STAKE_DELEGATION_STATE_COOLED ((uchar)4) /* effective=0 */
157 :
158 : struct fd_stake_delegation {
159 : fd_pubkey_t stake_account;
160 : fd_pubkey_t vote_account;
161 : ulong stake;
162 : ulong lamports;
163 : ulong credits_observed;
164 : uint acc_dlen;
165 : uint next_; /* Internal pool/map usage */
166 : uint delta_idx; /* Tracking for stake delegation iteration */
167 : ushort activation_epoch;
168 : ushort deactivation_epoch;
169 : union {
170 : /* No storage conflict because one is for the delta pool and the
171 : other for the root pool. */
172 : uchar is_tombstone; /* Internal delta usage */
173 : uchar dne_in_root; /* Tracking for stake delegation iteration */
174 : };
175 : uchar warmup_cooldown_rate; /* enum representing 0.25 or 0.09 */
176 : uchar in_use; /* For the root pool only. Not meaningful in the delta pool. Set to
177 : 1 if this element holds a live delegation present in the root map, 0
178 : if the element has been reclaimed. */
179 : uchar state; /* Can only be non-UNKNOWN in the root pool. */
180 : };
181 : typedef struct fd_stake_delegation fd_stake_delegation_t;
182 :
183 : FD_STATIC_ASSERT( sizeof(fd_stake_delegation_t)==112UL, fd_stake_delegation );
184 :
185 : /* Used for the pubkey fallback tier. Holds a reference to a stake
186 : account that is referenced by the root or by a live fork delta. */
187 :
188 : struct fd_stake_delegation_ref {
189 : fd_pubkey_t stake_account;
190 : uint next_; /* Internal pool/map usage */
191 : uint refcnt;
192 : };
193 : typedef struct fd_stake_delegation_ref fd_stake_delegation_ref_t;
194 :
195 : struct fd_stake_delegations {
196 : ulong magic;
197 : ulong expected_stake_accounts_;
198 : ulong max_stake_accounts_;
199 :
200 : /* Root map + pool */
201 : ulong map_offset_;
202 : ulong pool_offset_;
203 : ulong pool_idx_wmk_; /* One past the highest root pool index ever acquired. Every index in
204 : [0, wmk) has been acquired at least once, so its in_use byte is
205 : well defined. */
206 :
207 : /* Delta pool + fork and fork map */
208 : ulong delta_pool_offset_;
209 : ulong fork_pool_offset_;
210 : ulong fork_map_offset_;
211 :
212 : /* Guards every mutating operation on the struct. */
213 : fd_rwlock_t lock;
214 :
215 : /* Pubkey fallback tier. */
216 : ulong pubkey_pool_offset_;
217 : ulong pubkey_map_offset_;
218 : ulong max_pubkeys_;
219 : ulong pubkey_idx_wmk_; /* One past the highest pubkey pool index ever acquired */
220 : int pubkey_fallback;
221 :
222 : /* Stake totals for the current root. */
223 : ulong effective_stake;
224 : ulong activating_stake;
225 : ulong deactivating_stake;
226 :
227 : /* Only relevant around upgrade_bpf_stake_program_to_v5_1 activation.
228 : See comment at consumer of this flag for why it's needed. Remove
229 : after the feature activates on all clusters. */
230 : uchar fp_warmed_awarded;
231 : };
232 : typedef struct fd_stake_delegations fd_stake_delegations_t;
233 :
234 0 : #define FD_STAKE_DELEGATIONS_ITER_BATCH (32UL)
235 :
236 : struct fd_stake_delegations_iter {
237 : fd_stake_delegation_t * root_pool;
238 : fd_stake_delegation_t * delta_pool;
239 : fd_stake_delegation_t * ele;
240 : ulong idx;
241 : ulong wmk;
242 :
243 : /* Fallback mode only. */
244 : int fallback;
245 : ulong scan_idx;
246 : ulong batch_cnt;
247 : ulong batch_idx;
248 : fd_stake_delegations_t const * stake_delegations;
249 : fd_accdb_t * accdb;
250 : fd_accdb_fork_id_t accdb_fork_id;
251 : ulong epoch;
252 : ulong * warmup_cooldown_rate_epoch;
253 : ulong batch_pool_idx[ FD_STAKE_DELEGATIONS_ITER_BATCH ];
254 : fd_stake_delegation_t batch[ FD_STAKE_DELEGATIONS_ITER_BATCH ];
255 : };
256 : typedef struct fd_stake_delegations_iter fd_stake_delegations_iter_t;
257 :
258 : #include "fd_stake_delegations_private.h"
259 :
260 : FD_PROTOTYPES_BEGIN
261 :
262 : static inline double
263 1116 : fd_stake_delegations_warmup_cooldown_rate_to_double( uchar warmup_cooldown_rate ) {
264 1116 : return warmup_cooldown_rate==FD_STAKE_DELEGATIONS_WARMUP_COOLDOWN_RATE_ENUM_025 ? FD_STAKE_DELEGATIONS_WARMUP_COOLDOWN_RATE_025 : FD_STAKE_DELEGATIONS_WARMUP_COOLDOWN_RATE_009;
265 1116 : }
266 :
267 : /* Classify stake given the activation status evaluated at the provided
268 : epoch. The provided epoch is expected to be >= activation epoch. */
269 : static inline uchar
270 : fd_stake_delegation_classify( fd_stake_delegation_t const * delegation,
271 : fd_stake_history_entry_t activation_status,
272 171 : ulong epoch ) {
273 : /* Activation epoch in Agave's stake program is either clock.epoch, or
274 : inherited from an existing activation epoch, so activation epoch <=
275 : current epoch always holds for delegations created by the stake
276 : program. Synthetic inputs do not conform to this, so we mark them
277 : UNKNOWN to force the slow path. */
278 171 : if( FD_UNLIKELY( delegation->activation_epoch!=(ushort)USHORT_MAX && epoch<delegation->activation_epoch ) ) {
279 18 : return FD_STAKE_DELEGATION_STATE_UNKNOWN;
280 18 : }
281 :
282 153 : if( activation_status.activating>0UL ) return FD_STAKE_DELEGATION_STATE_WARMING;
283 135 : if( activation_status.deactivating>0UL ) return FD_STAKE_DELEGATION_STATE_COOLING;
284 132 : if( activation_status.effective==delegation->stake && delegation->deactivation_epoch==(ushort)USHORT_MAX ) return FD_STAKE_DELEGATION_STATE_WARMED;
285 :
286 : /* When evaluated at >= activation_epoch, (0,0,0) implies a fully
287 : cooled delegation. One might think that we could simply
288 :
289 : if( activation_status.effective==0UL ) return FD_STAKE_DELEGATION_STATE_COOLED;
290 :
291 : and life would be great. In an unfortunate turn, Agave has a
292 : special branch that will assume stake has been fully activated if
293 : the activation epoch is not found in the stake history sysvar,
294 : regardless of whether the delegation fully warmed up or not when
295 : the simulation ran faithfully from the activation epoch.
296 :
297 : https://github.com/solana-program/stake/blob/interface%40v4.3.1/interface/src/state.rs#L969
298 :
299 : This means that a COOLED tag doesn't necessarily survive against
300 : future stake history sysvars. As the stake history sysvar window
301 : advances and evicts older epochs, a delegation's activation epoch
302 : will eventually be evicted. At that point an effective stake
303 : simulation would cooldown from the full delegated amount, which
304 : might be more than the effective stake simulated at tag time, if
305 : the delegation only partially warmed up at deactivation epoch.
306 : This can in theory lead to a nonzero effective stake at the target
307 : epoch, once the activation epoch is evicted, contradicting the
308 : COOLED tag. Note that this is exceedingly hard to pull off as it
309 : requires that (1) the delegation only partially warmed up at
310 : deactivation, and (2) the delegation failed to fully cooldown from
311 : the full delegated amount over the potentially hundreds of epochs
312 : between deactivation epoch and target epoch. AKA either warmup or
313 : cooldown congestion in the cluster over an extended period of time.
314 : The upshot is that we will only award the COOLED tag when the
315 : delegation is always COOLED independent of the history sysvar.
316 : This safe verdict loses by a few millis at the boundary, mostly in
317 : the refresh_vote_accounts() phase, compared to the naive but unsafe
318 : classify.
319 :
320 : https://github.com/solana-program/stake/blob/interface%40v4.3.1/interface/src/state.rs#L877
321 : https://github.com/solana-program/stake/blob/interface%40v4.3.1/interface/src/state.rs#L896
322 :
323 : Note that the same misfortune doesn't apply to the WARMED tag. The
324 : sysvar query miss's "assume fully effective" bias means that a
325 : WARMED delegation stays warmed forever until it's instructed to
326 : deactivate. */
327 0 : 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;
328 0 : return FD_STAKE_DELEGATION_STATE_UNKNOWN;
329 0 : }
330 :
331 :
332 : /* fd_stake_delegations_align returns the alignment of the stake
333 : delegations struct. */
334 :
335 : ulong
336 : fd_stake_delegations_align( void );
337 :
338 : /* fd_stake_delegations_footprint returns the footprint of the stake
339 : delegations struct for a given amount of max stake accounts, max
340 : fallback stake accounts, expected stake accounts, and max live slots . */
341 :
342 : ulong
343 : fd_stake_delegations_footprint( ulong max_stake_accounts,
344 : ulong max_fallback_stake_accounts,
345 : ulong expected_stake_accounts,
346 : ulong max_live_slots );
347 :
348 : /* fd_stake_delegations_new creates a new stake delegations struct
349 : with a given amount of max, max fallback, and expected stake accounts
350 : and max live slots. It formats a memory region which is sized based
351 : off the pool capacity, expected map occupancy, and per-fork delta
352 : structures. */
353 :
354 : void *
355 : fd_stake_delegations_new( void * mem,
356 : ulong seed,
357 : ulong max_stake_accounts,
358 : ulong max_fallback_stake_accounts,
359 : ulong expected_stake_accounts,
360 : ulong max_live_slots );
361 :
362 : /* fd_stake_delegations_join joins a stake delegations struct from a
363 : memory region. There can be multiple valid joins for a given memory
364 : region but the caller is responsible for accessing memory in a
365 : thread-safe manner. */
366 :
367 : fd_stake_delegations_t *
368 : fd_stake_delegations_join( void * mem );
369 :
370 : /* fd_stake_delegations_reset resets delegations to the post-new state. */
371 :
372 : void
373 : fd_stake_delegations_reset( fd_stake_delegations_t * stake_delegations );
374 :
375 : /* fd_stake_delegation_root_query looks up the stake delegation for the
376 : given stake account in the root map. */
377 :
378 : fd_stake_delegation_t const *
379 : fd_stake_delegation_root_query( fd_stake_delegations_t const * stake_delegations,
380 : fd_pubkey_t const * stake_account );
381 :
382 : /* fd_stake_delegations_root_update will either insert a new stake
383 : delegation if the pubkey doesn't exist yet, or it will update the
384 : stake delegation for the pubkey if already in the map, overriding any
385 : previous data. fd_stake_delegations_t must be a valid local join. */
386 :
387 : void
388 : fd_stake_delegations_root_update( fd_stake_delegations_t * stake_delegations,
389 : fd_pubkey_t const * stake_account,
390 : fd_pubkey_t const * vote_account,
391 : ulong stake,
392 : ulong activation_epoch,
393 : ulong deactivation_epoch,
394 : ulong credits_observed,
395 : ulong lamports,
396 : uint acc_dlen,
397 : uchar warmup_cooldown_rate );
398 :
399 : /* fd_stake_delegations_refresh is used to refresh the stake
400 : delegations stored in fd_stake_delegations_t which is owned by
401 : the bank. For a given database handle, read in the state of all
402 : stake accounts, decode their state, and update each stake delegation.
403 : This is meant to be called before any slots are executed, but after
404 : the snapshot has finished loading.
405 :
406 : Before this function is called, there are some important assumptions
407 : made about the state of the stake delegations:
408 : 1. fd_stake_delegations_t is not missing any valid entries
409 : 2. fd_stake_delegations_t may have some invalid entries that should
410 : be removed
411 :
412 : fd_stake_delegations_refresh will remove all of the invalid entries
413 : that are detected. An entry is considered invalid if the stake
414 : account does not exist (e.g. zero balance or no record) or if it
415 : has invalid state (e.g. not a stake account or invalid bincode data).
416 : No new entries are added to the struct at this point. */
417 :
418 : void
419 : fd_stake_delegations_refresh( fd_stake_delegations_t * stake_delegations,
420 : ulong epoch,
421 : fd_stake_history_t const * stake_history,
422 : ulong * warmup_cooldown_rate_epoch,
423 : int use_fixed_point_stake_math,
424 : fd_accdb_t * accdb,
425 : fd_accdb_fork_id_t fork_id );
426 :
427 : /* fd_stake_delegations_base_cnt returns the number of stake delegations
428 : in the base of stake delegations struct. */
429 :
430 : ulong
431 : fd_stake_delegations_base_cnt( fd_stake_delegations_t const * stake_delegations );
432 :
433 : /* fd_stake_delegations_pubkey_cnt returns the number of entries in the
434 : pubkey fallback tier. */
435 :
436 : ulong
437 : fd_stake_delegations_pubkey_cnt( fd_stake_delegations_t const * stake_delegations );
438 :
439 : /* fd_stake_delegations_pubkey_fallback returns non-zero if the stake
440 : delegations struct has entered fallback mode. */
441 :
442 : FD_FN_PURE static inline int
443 291 : fd_stake_delegations_pubkey_fallback( fd_stake_delegations_t const * stake_delegations ) {
444 291 : return stake_delegations->pubkey_fallback;
445 291 : }
446 :
447 : /* fd_stake_delegations_new_fork allocates a new fork index for the
448 : stake delegations. The fork index is returned to the caller. */
449 :
450 : ushort
451 : fd_stake_delegations_new_fork( fd_stake_delegations_t * stake_delegations );
452 :
453 : /* fd_stake_delegations_fork_update upserts a stake delegation delta for
454 : the fork. If an entry already exists for the stake account in this
455 : fork, it is overwritten in place. */
456 :
457 : void
458 : fd_stake_delegations_fork_update( fd_stake_delegations_t * stake_delegations,
459 : ushort fork_idx,
460 : fd_pubkey_t const * stake_account,
461 : fd_pubkey_t const * vote_account,
462 : ulong stake,
463 : ulong activation_epoch,
464 : ulong deactivation_epoch,
465 : ulong credits_observed,
466 : ulong lamports,
467 : uint acc_dlen,
468 : uchar warmup_cooldown_rate );
469 :
470 : /* fd_stake_delegations_fork_remove inserts a tombstone stake delegation
471 : entry for the given fork. The function will not actually remove or
472 : free any resources corresponding to the stake account. The reason a
473 : tombstone is stored is because each fork corresponds to a set of
474 : stake delegation deltas for a given slot. If an entry already exists
475 : for the stake account in this fork, it is overwritten in place. */
476 :
477 : void
478 : fd_stake_delegations_fork_remove( fd_stake_delegations_t * stake_delegations,
479 : ushort fork_idx,
480 : fd_pubkey_t const * stake_account );
481 :
482 : /* fd_stake_delegations_evict_fork removes/frees all stake delegation
483 : entries for a given fork. After this function is called it is no
484 : longer safe to have any references to the fork index (until it is
485 : reused via a call to fd_stake_delegations_new_fork). The caller is
486 : responsible for making sure references to this fork index are not
487 : being held. */
488 :
489 : void
490 : fd_stake_delegations_evict_fork( fd_stake_delegations_t * stake_delegations,
491 : ushort fork_idx );
492 :
493 : /* fd_stake_delegations_apply_fork_delta merges all stake delegation
494 : entries for fork_idx into the root map: non-tombstone entries are
495 : applied via fd_stake_delegations_root_update; tombstone entries remove
496 : the corresponding stake account from the root map. Caller must
497 : ensure no concurrent iteration on stake_delegations for this fork. */
498 :
499 : void
500 : fd_stake_delegations_apply_fork_delta( ulong epoch,
501 : fd_stake_history_t const * stake_history,
502 : ulong * warmup_cooldown_rate_epoch,
503 : int use_fixed_point_stake_math,
504 : fd_stake_delegations_t * stake_delegations,
505 : ushort fork_idx );
506 :
507 : /* fd_stake_delegations_{mark,unmark}_delta are used to temporarily
508 : tag delta elements from a given fork in the base/root stake
509 : delegation map/pool. This allows the caller to then iterator over
510 : the stake delegations for a given bank using just the deltas and the
511 : root without creating a copy. Each delta that is marked, must be
512 : unmarked after the caller is done iterating over the stake
513 : delegations.
514 :
515 : Under the hood, it reuses internal pointers for elements in the root
516 : map to point to the corresponding delta element. If the element is
517 : removed by a delta another field will be reused to ignore it during
518 : iteration. If an element is inserted by a delta, it will be
519 : temporarily added to the root, but will be removed with a call to
520 : unmark_delta. These functions are also used to temporarily update
521 : (and then unwind) the stake totals for the current root. */
522 :
523 : void
524 : fd_stake_delegations_mark_delta( fd_stake_delegations_t * stake_delegations,
525 : ulong epoch,
526 : fd_stake_history_t const * stake_history,
527 : ulong * warmup_cooldown_rate_epoch,
528 : int use_fixed_point_stake_math,
529 : ushort fork_idx );
530 :
531 : void
532 : fd_stake_delegations_unmark_delta( fd_stake_delegations_t * stake_delegations,
533 : ulong epoch,
534 : fd_stake_history_t const * stake_history,
535 : ulong * warmup_cooldown_rate_epoch,
536 : int use_fixed_point_stake_math,
537 : ushort fork_idx );
538 :
539 : /* Iterator API for stake delegations. The iterator is initialized with
540 : a call to fd_stake_delegations_iter_init. The caller is responsible
541 : for managing the memory for the iterator. It is safe to call
542 : fd_stake_delegations_iter_next if the result of
543 : fd_stake_delegations_iter_done()==0. It is safe to call
544 : fd_stake_delegations_iter_ele() to get the current stake delegation
545 : or fd_stake_delegations_iter_idx() to get the index of the current
546 : stake delegation. It is not safe to modify the stake delegation
547 : while iterating through it.
548 :
549 : Under the hood, the iterator walks the root pool, redirecting to the
550 : delta pool for entries a marked fork has changed. If the struct is
551 : in fallback mode it instead walks the pubkey fallback tier and reads
552 : each stake account out of the accounts database, in which case the
553 : pointer returned by fd_stake_delegations_iter_ele is only valid until
554 : the next call to fd_stake_delegations_iter_next.
555 :
556 : Example use:
557 :
558 : fd_stake_delegations_iter_t iter_[1];
559 : for( fd_stake_delegations_iter_t * iter = fd_stake_delegations_iter_init( iter_, stake_delegations, accdb, fork_id, epoch, warmup_cooldown_rate_epoch );
560 : !fd_stake_delegations_iter_done( iter );
561 : fd_stake_delegations_iter_next( iter ) ) {
562 : fd_stake_delegation_t * stake_delegation = fd_stake_delegations_iter_ele( iter );
563 : }
564 : */
565 :
566 : fd_stake_delegations_iter_t *
567 : fd_stake_delegations_iter_init( fd_stake_delegations_iter_t * iter,
568 : fd_stake_delegations_t const * stake_delegations,
569 : fd_accdb_t * accdb,
570 : fd_accdb_fork_id_t accdb_fork_id,
571 : ulong epoch,
572 : ulong * warmup_cooldown_rate_epoch );
573 :
574 : /* fd_stake_delegations_iter_advance_fallback is the out-of-line advance
575 : used in fallback mode. Not for direct use. */
576 :
577 : void
578 : fd_stake_delegations_iter_advance_fallback( fd_stake_delegations_iter_t * iter );
579 :
580 : static inline fd_stake_delegation_t *
581 1737 : fd_stake_delegations_iter_ele( fd_stake_delegations_iter_t * iter ) {
582 1737 : return iter->ele;
583 1737 : }
584 :
585 : static inline ulong
586 1005 : fd_stake_delegations_iter_idx( fd_stake_delegations_iter_t * iter ) {
587 1005 : return iter->idx;
588 1005 : }
589 :
590 : static inline void
591 1560 : fd_stake_delegations_iter_next( fd_stake_delegations_iter_t * iter ) {
592 1560 : if( FD_UNLIKELY( iter->fallback ) ) {
593 0 : iter->batch_idx++;
594 0 : fd_stake_delegations_iter_advance_fallback( iter );
595 0 : return;
596 0 : }
597 1560 : iter->idx++;
598 1560 : fd_stake_delegations_iter_advance_private( iter );
599 1560 : }
600 :
601 : static inline int
602 2856 : fd_stake_delegations_iter_done( fd_stake_delegations_iter_t * iter ) {
603 2856 : return !iter->ele;
604 2856 : }
605 :
606 : /* Invalidates every WARMED tag in the root pool. Only useful at
607 : boundaries where upgrade_bpf_stake_program_to_v5_1 is active but a
608 : WARMED tag may have been awarded under the old floating point math,
609 : aka fp_warmed_awarded is set. Forces fully warmed delegations to
610 : reevaluate their effective stake, in case there's a difference
611 : between the old floating point math and the new integer math. Also
612 : clears fp_warmed_awarded, since no WARMED tag survives the wipe.
613 :
614 : Unlike the other mutators, this one does not take the write lock
615 : itself: its only callers run inside the boundary's
616 : mark/iterate/unmark bracket, which already holds it. */
617 :
618 : void
619 : fd_stake_delegations_invalidate_warmed( fd_stake_delegations_t * stake_delegations );
620 :
621 : FD_PROTOTYPES_END
622 :
623 : #endif /* HEADER_fd_src_flamenco_stakes_fd_stake_delegations_h */
|