LCOV - code coverage report
Current view: top level - flamenco/stakes - fd_stake_delegations.h (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 34 41 82.9 %
Date: 2026-10-22 18:10:43 Functions: 11 775 1.4 %

          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 */

Generated by: LCOV version 1.14