LCOV - code coverage report
Current view: top level - flamenco/stakes - fd_stake_delegations.h (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 45 53 84.9 %
Date: 2026-08-15 04:34:25 Functions: 22 1200 1.8 %

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

Generated by: LCOV version 1.14