LCOV - code coverage report
Current view: top level - flamenco/stakes - fd_stake_delegations.h (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 40 40 100.0 %
Date: 2026-11-12 04:25:03 Functions: 32 845 3.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/sysvar/fd_sysvar_base.h"
       5             : #include "../accdb/fd_accdb.h"
       6             : 
       7         228 : #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_view_{begin,end}
      62             :    and the iterator are the exception: view_begin takes the write lock
      63             :    and view_end releases it, so it is held across the whole
      64             :    view_begin/iterate/view_end bracket.
      65             : 
      66             :    max_disk_records bounds the number of delta records that can spill
      67             :    to disk.  The disk root capacity is max_stake_accounts plus twice
      68             :    max_disk_records. */
      69             : 
      70        7470 : #define FD_STAKE_DELEGATIONS_ALIGN    (128UL)
      71      872313 : #define FD_STAKE_DELEGATIONS_FORK_MAX (65534UL)
      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         285 : #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       20928 : #define FD_STAKE_DELEGATIONS_DELTA_DISK_TAG (1U<<31)
      83       13344 : #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 reward stake accumulation in refresh_vote_accounts(),
     118             :      recall that rewarded_epoch is the just-ended epoch.  Since we are
     119             :      at the boundary of 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.
     123             : 
     124             :    WARMED and COOLED are stable states and are the only tags that the
     125             :    fast paths act on.  The unstable state tags (WARMING/COOLING) are
     126             :    defined for clarity and do not enable any fast path.  As a side note,
     127             :    WARMING tags get a chance to be promoted to WARMED if the delegation
     128             :    gets any inflation rewards or is otherwise written.  At rewards
     129             :    distribution time, the delegation will re-enter a delta tier and
     130             :    shortly afterwards get a chance to be re-classified when the
     131             :    distribution block roots.  Fresh dust delegations that don't get any
     132             :    rewards will be sticky WARMING until the next boot or a write. */
     133          24 : #define FD_STAKE_DELEGATION_STATE_UNKNOWN ((uchar)0)
     134          75 : #define FD_STAKE_DELEGATION_STATE_WARMING ((uchar)1) /* activating */
     135         429 : #define FD_STAKE_DELEGATION_STATE_WARMED  ((uchar)2) /* effective=delegated */
     136           3 : #define FD_STAKE_DELEGATION_STATE_COOLING ((uchar)3) /* deactivating */
     137         708 : #define FD_STAKE_DELEGATION_STATE_COOLED  ((uchar)4) /* effective=0 */
     138             : 
     139             : struct fd_stake_delegation_key {
     140             :   fd_pubkey_t stake_account;
     141             :   ushort      fork_idx;
     142             : };
     143             : typedef struct fd_stake_delegation_key fd_stake_delegation_key_t;
     144             : 
     145             : struct fd_stake_delegation {
     146             :   /* The delta map uses key; root lookups use stake_account directly. */
     147             :   union {
     148             :     fd_stake_delegation_key_t key;
     149             :     struct {
     150             :       fd_pubkey_t stake_account;
     151             :       ushort      fork_idx; /* In-memory delta's fork index */
     152             :     };
     153             :   };
     154             :   ushort      activation_epoch;
     155             :   ushort      deactivation_epoch;
     156             :   union {
     157             :     /* No storage conflict because one is for delta records and the
     158             :        other for root records. */
     159             :     uchar     is_tombstone; /* Internal delta usage */
     160             :     uchar     dne_in_root;  /* Tracking for stake delegation iteration */
     161             :   };
     162             :   uchar       in_use : 1; /* For the in-memory root pool only.  Not meaningful in the
     163             :                              delta pool.  Set to 1 if this element holds a live delegation
     164             :                              present in the root map, 0 if the element has been reclaimed. */
     165             :   uchar       state  : 3; /* Can only be non-UNKNOWN in a root record. */
     166             :   fd_pubkey_t vote_account;
     167             :   ulong       stake;
     168             :   ulong       lamports;
     169             :   ulong       credits_observed;
     170             :   uint        acc_dlen;
     171             :   uint        next_;     /* Internal pool/map usage */
     172             :   union {
     173             :     uint      delta_idx; /* Root's in-memory/disk delta reference for iteration */
     174             :     uint      fork_next; /* Next in-memory delta in this fork */
     175             :   };
     176             :   uint        slot;   /* Snapshot loading only: slot of the account version this
     177             :                          record came from, newest wins.  Runtime writes store 0. */
     178             : };
     179             : typedef struct fd_stake_delegation fd_stake_delegation_t;
     180             : 
     181             : FD_STATIC_ASSERT( sizeof(fd_stake_delegation_t)==112UL, fd_stake_delegation );
     182             : 
     183             : typedef struct fd_stake_delegations fd_stake_delegations_t;
     184             : 
     185        5286 : #define FD_STAKE_DELEGATIONS_ITER_BATCH_CNT (128UL)
     186             : 
     187             : struct fd_stake_delegations_iter {
     188             :   fd_stake_delegation_t *        root_pool;
     189             :   fd_stake_delegation_t *        delta_pool;
     190             :   fd_stake_delegation_t *        ele;
     191             :   fd_stake_delegations_t const * stake_delegations;
     192             :   ulong                          idx;      /* externally visible index */
     193             :   ulong                          wmk;      /* in-memory root watermark */
     194             :   ulong                          root_max; /* in-memory root capacity */
     195             :   ulong                          disk_idx; /* dense disk-root cursor */
     196             :   fd_stake_delegation_t          disk_ele;
     197             :   fd_stake_delegation_t          disk_batch[ FD_STAKE_DELEGATIONS_ITER_BATCH_CNT ];
     198             : };
     199             : typedef struct fd_stake_delegations_iter fd_stake_delegations_iter_t;
     200             : 
     201             : #include "fd_stake_delegations_private.h"
     202             : 
     203             : FD_PROTOTYPES_BEGIN
     204             : 
     205             : /* Classify stake given the activation status evaluated at the provided
     206             :    epoch.  The provided epoch is expected to be >= activation epoch. */
     207             : static inline uchar
     208             : fd_stake_delegation_classify( fd_stake_delegation_t const * delegation,
     209             :                               fd_stake_history_entry_t      activation_status,
     210         537 :                               ulong                         epoch ) {
     211             :   /* Activation epoch in Agave's stake program is either clock.epoch, or
     212             :      inherited from an existing activation epoch, so activation epoch <=
     213             :      current epoch always holds for delegations created by the stake
     214             :      program.  Synthetic inputs do not conform to this, so we mark them
     215             :      UNKNOWN to force the slow path. */
     216         537 :   if( FD_UNLIKELY( delegation->activation_epoch!=(ushort)USHORT_MAX && epoch<delegation->activation_epoch ) ) {
     217          18 :     return FD_STAKE_DELEGATION_STATE_UNKNOWN;
     218          18 :   }
     219             : 
     220         519 :   if( activation_status.activating>0UL   ) return FD_STAKE_DELEGATION_STATE_WARMING;
     221         444 :   if( activation_status.deactivating>0UL ) return FD_STAKE_DELEGATION_STATE_COOLING;
     222         441 :   if( activation_status.effective==delegation->stake && delegation->deactivation_epoch==(ushort)USHORT_MAX ) return FD_STAKE_DELEGATION_STATE_WARMED;
     223             : 
     224             :   /* When evaluated at >= activation_epoch, (0,0,0) implies a fully
     225             :      cooled delegation.  One might think that we could simply
     226             : 
     227             :      if( activation_status.effective==0UL ) return FD_STAKE_DELEGATION_STATE_COOLED;
     228             : 
     229             :      and life would be great.  In an unfortunate turn, Agave has a
     230             :      special branch that will assume stake has been fully activated if
     231             :      the activation epoch is not found in the stake history sysvar,
     232             :      regardless of whether the delegation fully warmed up or not when
     233             :      the simulation ran faithfully from the activation epoch.
     234             : 
     235             :      https://github.com/solana-program/stake/blob/interface%40v4.3.1/interface/src/state.rs#L969
     236             : 
     237             :      This means that a COOLED tag doesn't necessarily survive against
     238             :      future stake history sysvars.  As the stake history sysvar window
     239             :      advances and evicts older epochs, a delegation's activation epoch
     240             :      will eventually be evicted.  At that point an effective stake
     241             :      simulation would cooldown from the full delegated amount, which
     242             :      might be more than the effective stake simulated at tag time, if
     243             :      the delegation only partially warmed up at deactivation epoch.
     244             :      This can in theory lead to a nonzero effective stake at the target
     245             :      epoch, once the activation epoch is evicted, contradicting the
     246             :      COOLED tag.  Note that this is exceedingly hard to pull off as it
     247             :      requires that (1) the delegation only partially warmed up at
     248             :      deactivation, and (2) the delegation failed to fully cooldown from
     249             :      the full delegated amount over the potentially hundreds of epochs
     250             :      between deactivation epoch and target epoch.  AKA either warmup or
     251             :      cooldown congestion in the cluster over an extended period of time.
     252             :      The upshot is that we will only award the COOLED tag when the
     253             :      delegation is always COOLED independent of the history sysvar.
     254             :      This safe verdict loses by a few millis at the boundary, mostly in
     255             :      the refresh_vote_accounts() phase, compared to the naive but unsafe
     256             :      classify.
     257             : 
     258             :      https://github.com/solana-program/stake/blob/interface%40v4.3.1/interface/src/state.rs#L877
     259             :      https://github.com/solana-program/stake/blob/interface%40v4.3.1/interface/src/state.rs#L896
     260             : 
     261             :      Note that the same misfortune doesn't apply to the WARMED tag.  The
     262             :      sysvar query miss's "assume fully effective" bias means that a
     263             :      WARMED delegation stays warmed forever until it's instructed to
     264             :      deactivate. */
     265          12 :   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;
     266           6 :   return FD_STAKE_DELEGATION_STATE_UNKNOWN;
     267          12 : }
     268             : 
     269             : 
     270             : /* fd_stake_delegations_align returns the alignment of the stake
     271             :    delegations struct. */
     272             : 
     273             : ulong
     274             : fd_stake_delegations_align( void );
     275             : 
     276             : /* fd_stake_delegations_footprint returns the footprint of the stake
     277             :    delegations struct for the given root pool capacity and max live
     278             :    slots.  The in-memory delta pool has the same capacity as the root
     279             :    pool (max_stake_accounts); further deltas spill to disk. */
     280             : 
     281             : ulong
     282             : fd_stake_delegations_footprint( ulong max_stake_accounts,
     283             :                                 ulong max_live_slots );
     284             : 
     285             : /* fd_stake_delegations_new creates a new stake delegations struct with
     286             :    the given backing descriptor, in-memory pool capacity, disk spill
     287             :    capacity, and max live slots.  Every process joining the object must
     288             :    have the same backing file open at disk_fd.  It formats a memory
     289             :    region sized from the pool capacity and per-fork delta structures. */
     290             : 
     291             : void *
     292             : fd_stake_delegations_new( void * mem,
     293             :                           int    disk_fd,
     294             :                           ulong  seed,
     295             :                           ulong  max_stake_accounts,
     296             :                           ulong  max_disk_records,
     297             :                           ulong  max_live_slots );
     298             : 
     299             : /* fd_stake_delegations_join joins a stake delegations struct from a
     300             :    memory region.  disk_fd must match the descriptor number provided to
     301             :    fd_stake_delegations_new.  There can be multiple valid joins for a
     302             :    given memory region but the caller is responsible for accessing
     303             :    memory in a thread-safe manner. */
     304             : 
     305             : fd_stake_delegations_t *
     306             : fd_stake_delegations_join( void * mem,
     307             :                            int    disk_fd );
     308             : 
     309             : /* fd_stake_delegations_reset resets delegations to the post-new state. */
     310             : 
     311             : void
     312             : fd_stake_delegations_reset( fd_stake_delegations_t * stake_delegations );
     313             : 
     314             : /* Read the current aggregate stake totals.  During a frontier query,
     315             :    these include the marked deltas.  The returned epoch is zero; callers
     316             :    supply the epoch when constructing a stake-history entry.  The caller
     317             :    must exclude concurrent mutations or hold the frontier-query lock. */
     318             : 
     319             : fd_stake_history_entry_t
     320             : fd_stake_delegations_totals( fd_stake_delegations_t const * stake_delegations );
     321             : 
     322             : /* fd_stake_delegations_metrics_t holds the in-memory occupancy and disk
     323             :    spill metrics.  *_spilled_cnt and disk_* only grow; disk_ticks is in
     324             :    fd_tickcount() units. */
     325             : 
     326             : struct fd_stake_delegations_metrics {
     327             :   ulong root_used;
     328             :   ulong root_capacity;
     329             :   ulong root_disk_cnt;
     330             :   ulong delta_used;
     331             :   ulong delta_capacity;
     332             :   ulong delta_disk_cnt;
     333             :   ulong root_spilled_cnt;
     334             :   ulong delta_spilled_cnt;
     335             :   ulong disk_read_cnt;
     336             :   ulong disk_written_cnt;
     337             :   ulong disk_ticks;
     338             : };
     339             : 
     340             : typedef struct fd_stake_delegations_metrics fd_stake_delegations_metrics_t;
     341             : 
     342             : /* fd_stake_delegations_metrics_snap copies the metrics of
     343             :    stake_delegations into out without taking the lock, so values from
     344             :    different fields may be slightly out of step. */
     345             : 
     346             : void
     347             : fd_stake_delegations_metrics_snap( fd_stake_delegations_t const *   stake_delegations,
     348             :                                    fd_stake_delegations_metrics_t * out );
     349             : 
     350             : /* Replace aggregate totals when the root crosses an epoch boundary.
     351             :    Acquires the store write lock; do not call inside a frontier query. */
     352             : 
     353             : void
     354             : fd_stake_delegations_set_totals( fd_stake_delegations_t * stake_delegations,
     355             :                                  ulong                    effective,
     356             :                                  ulong                    activating,
     357             :                                  ulong                    deactivating );
     358             : 
     359             : /* fd_stake_delegations_root_update upserts a stake delegation into the
     360             :    in-memory or disk root, overriding any previous data. */
     361             : 
     362             : void
     363             : fd_stake_delegations_root_update( fd_stake_delegations_t * stake_delegations,
     364             :                                   fd_pubkey_t const *      stake_account,
     365             :                                   fd_pubkey_t const *      vote_account,
     366             :                                   ulong                    stake,
     367             :                                   ulong                    activation_epoch,
     368             :                                   ulong                    deactivation_epoch,
     369             :                                   ulong                    credits_observed,
     370             :                                   ulong                    lamports,
     371             :                                   uint                     acc_dlen );
     372             : 
     373             : /* fd_stake_delegations_prune_inactive_root removes root delegations
     374             :    that are inactive in both epoch and epoch-1.  This function removes
     375             :    all inactive delegations from the in-memory and disk roots.  It is a
     376             :    parallel to Agave removing inactive stake accounts directly at the
     377             :    epoch boundary.  Returns the number of delegations removed.  When
     378             :    emit_bank is non-NULL, a runtime_stake_delegation remove event is
     379             :    emitted for each removal (NULL to skip). */
     380             : 
     381             : ulong
     382             : fd_stake_delegations_prune_inactive_root( fd_stake_delegations_t *   stake_delegations,
     383             :                                           ulong                      epoch,
     384             :                                           fd_stake_history_t const * stake_history,
     385             :                                           ulong *                    warmup_cooldown_rate_epoch,
     386             :                                           fd_bank_t const *          emit_bank );
     387             : 
     388             : /* fd_stake_delegations_refresh finalizes the root after a snapshot or
     389             :    genesis load, before any slot executes.  Pure in-memory pass: drops
     390             :    tombstones, drops delegations inactive in both epoch and epoch-1
     391             :    when remove_inactive_stakes is set, sets the warmup rate and state
     392             :    tag, accumulates the stake totals and refills the pool from disk
     393             :    roots.  stake_history may be NULL. */
     394             : 
     395             : void
     396             : fd_stake_delegations_refresh( fd_stake_delegations_t *   stake_delegations,
     397             :                               ulong                      epoch,
     398             :                               fd_stake_history_t const * stake_history,
     399             :                               ulong *                    warmup_cooldown_rate_epoch,
     400             :                               int                        remove_inactive_stakes );
     401             : 
     402             : /* fd_stake_delegations_vote_account_status sums the root activation
     403             :    status of every delegation to vote_account at epoch.  Acquires the
     404             :    store write lock; do not call inside a frontier query.  stake_history
     405             :    may be NULL. */
     406             : 
     407             : fd_stake_history_entry_t
     408             : fd_stake_delegations_vote_account_status( fd_stake_delegations_t *   stake_delegations,
     409             :                                           fd_pubkey_t const *        vote_account,
     410             :                                           ulong                      epoch,
     411             :                                           fd_stake_history_t const * stake_history,
     412             :                                           ulong *                    warmup_cooldown_rate_epoch );
     413             : 
     414             : /* fd_stake_delegations_new_fork allocates a child of parent_fork_idx.
     415             :    USHORT_MAX indicates that the parent is the root. */
     416             : 
     417             : ushort
     418             : fd_stake_delegations_new_fork( fd_stake_delegations_t * stake_delegations,
     419             :                                 ushort                  parent_fork_idx );
     420             : 
     421             : /* fd_stake_delegations_fork_update upserts a stake delegation into the
     422             :    fork's deltas, or into the root when fork_idx is USHORT_MAX.  slot is
     423             :    the slot of the account version being written: a write is dropped if
     424             :    the entry already holds a newer slot, so snapin tiles can apply
     425             :    versions in any order.  Runtime callers pass 0. */
     426             : 
     427             : void
     428             : fd_stake_delegations_fork_update( fd_stake_delegations_t * stake_delegations,
     429             :                                   ushort                   fork_idx,
     430             :                                   ulong                    slot,
     431             :                                   fd_pubkey_t const *      stake_account,
     432             :                                   fd_pubkey_t const *      vote_account,
     433             :                                   ulong                    stake,
     434             :                                   ulong                    activation_epoch,
     435             :                                   ulong                    deactivation_epoch,
     436             :                                   ulong                    credits_observed,
     437             :                                   ulong                    lamports,
     438             :                                   uint                     acc_dlen );
     439             : 
     440             : /* fd_stake_delegations_fork_remove inserts a tombstone stake delegation
     441             :    entry for the given fork, or for the root when fork_idx is
     442             :    USHORT_MAX.  The function will not actually remove or free any
     443             :    resources corresponding to the stake account.  The reason a
     444             :    tombstone is stored is because each fork corresponds to a set of
     445             :    stake delegation deltas for a given slot.  If an entry already
     446             :    exists for the stake account in this fork, it is overwritten in
     447             :    place.  slot is as in fd_stake_delegations_fork_update.
     448             :    replacing_full_entry means the replaced version was loaded by the
     449             :    full snapshot and this one by the incremental, so an account not in
     450             :    the root is left alone. */
     451             : 
     452             : void
     453             : fd_stake_delegations_fork_remove( fd_stake_delegations_t * stake_delegations,
     454             :                                   ushort                   fork_idx,
     455             :                                   ulong                    slot,
     456             :                                   fd_pubkey_t const *      stake_account,
     457             :                                   int                      replacing_full_entry );
     458             : 
     459             : /* fd_stake_delegations_evict_fork frees a fork's deltas and its ID.
     460             :    The caller must no longer need this fork or query its descendants.
     461             :    Root application detaches surviving children before this is called. */
     462             : 
     463             : void
     464             : fd_stake_delegations_evict_fork( fd_stake_delegations_t * stake_delegations,
     465             :                                  ushort                   fork_idx );
     466             : 
     467             : /* upserts and removes accumulate across applications.  root_cnt is
     468             :    overwritten with the current in-memory and disk root count. */
     469             : 
     470             : struct fd_stake_delegations_delta_stats {
     471             :   ulong upserts;
     472             :   ulong removes;
     473             :   ulong root_cnt;
     474             : };
     475             : typedef struct fd_stake_delegations_delta_stats fd_stake_delegations_delta_stats_t;
     476             : 
     477             : /* fd_stake_delegations_advance_root applies the target and its
     478             :    ancestors in root-to-leaf order.  Its direct children then inherit
     479             :    from rooted state.  The caller frees the applied fork descriptors
     480             :    with evict_fork.  USHORT_MAX applies no deltas.
     481             : 
     482             :    skip_stake_math applies the deltas without updating totals or state,
     483             :    for the snapshot loader; fd_stake_delegations_refresh recomputes them
     484             :    afterwards and the stake history arguments may be NULL.
     485             : 
     486             :    If stake_delegations_delta_stats is non-NULL, upserts and removes
     487             :    accumulate into it (caller zeroes). */
     488             : 
     489             : void
     490             : fd_stake_delegations_advance_root( ulong                                epoch,
     491             :                                    fd_stake_history_t const *           stake_history,
     492             :                                    ulong *                              warmup_cooldown_rate_epoch,
     493             :                                    int                                  skip_stake_math,
     494             :                                    fd_stake_delegations_t *             stake_delegations,
     495             :                                    ushort                               fork_idx,
     496             :                                    fd_stake_delegations_delta_stats_t * stake_delegations_delta_stats );
     497             : 
     498             : /* fd_stake_delegations_view_{begin,end} temporarily overlay
     499             :    delta elements from the target fork's ancestry onto the base/root
     500             :    stake delegation stores.  This allows the caller to iterate over the
     501             :    delegations for a bank using the root and its deltas without creating
     502             :    a copy.
     503             : 
     504             :    Under the hood, each in-memory or disk root record points to the
     505             :    corresponding in-memory or disk delta.  If an element is inserted by
     506             :    a delta, a temporary root record is added and then removed by
     507             :    view_end.  These functions also temporarily update and
     508             :    unwind the stake totals for the current root. */
     509             : 
     510             : void
     511             : fd_stake_delegations_view_begin( fd_stake_delegations_t *   stake_delegations,
     512             :                                  ulong                      epoch,
     513             :                                  fd_stake_history_t const * stake_history,
     514             :                                  ulong *                    warmup_cooldown_rate_epoch,
     515             :                                  ushort                     fork_idx );
     516             : 
     517             : void
     518             : fd_stake_delegations_view_end( fd_stake_delegations_t *   stake_delegations,
     519             :                                fd_stake_history_t const * stake_history,
     520             :                                ulong *                    warmup_cooldown_rate_epoch );
     521             : 
     522             : /* Iterator API for stake delegations.  The iterator is initialized with
     523             :    a call to fd_stake_delegations_iter_init.  The caller is responsible
     524             :    for managing the memory for the iterator.  It is safe to call
     525             :    fd_stake_delegations_iter_next if the result of
     526             :    fd_stake_delegations_iter_done()==0.  It is safe to call
     527             :    fd_stake_delegations_iter_ele() to get the current stake delegation
     528             :    or fd_stake_delegations_iter_idx() to get the index of the current
     529             :    stake delegation.  It is not safe to modify the stake delegation
     530             :    while iterating through it.
     531             : 
     532             :    Under the hood, the iterator walks in-memory roots followed by dense
     533             :    disk roots, redirecting through tagged in-memory/disk delta references
     534             :    for entries a marked fork has changed.  Disk roots are read in batches
     535             :    of up to 128 records (14 KiB per iterator).  Disk records are returned
     536             :    through iterator-owned storage, so that pointer remains valid only
     537             :    until the next call to fd_stake_delegations_iter_next.  Reinitialize
     538             :    the iterator after modifying the store or changing the marked forks.
     539             : 
     540             :    Example use:
     541             : 
     542             :    fd_stake_delegations_iter_t iter_[1];
     543             :    for( fd_stake_delegations_iter_t * iter = fd_stake_delegations_iter_init( iter_, stake_delegations );
     544             :         !fd_stake_delegations_iter_done( iter );
     545             :         fd_stake_delegations_iter_next( iter ) ) {
     546             :      fd_stake_delegation_t const * stake_delegation = fd_stake_delegations_iter_ele( iter );
     547             :    }
     548             : */
     549             : 
     550             : fd_stake_delegations_iter_t *
     551             : fd_stake_delegations_iter_init( fd_stake_delegations_iter_t *  iter,
     552             :                                 fd_stake_delegations_t const * stake_delegations );
     553             : 
     554             : static inline fd_stake_delegation_t const *
     555       53148 : fd_stake_delegations_iter_ele( fd_stake_delegations_iter_t const * iter ) {
     556       53148 :   return iter->ele;
     557       53148 : }
     558             : 
     559             : static inline ulong
     560        5472 : fd_stake_delegations_iter_idx( fd_stake_delegations_iter_t * iter ) {
     561        5472 :   return iter->idx;
     562        5472 : }
     563             : 
     564             : static inline void
     565       41256 : fd_stake_delegations_iter_next( fd_stake_delegations_iter_t * iter ) {
     566       41256 :   if( FD_LIKELY( iter->idx<iter->root_max ) ) {
     567       37110 :     iter->idx++;
     568       37110 :     fd_stake_delegations_iter_advance_private( iter );
     569       37110 :   } else {
     570        4146 :     iter->disk_idx++;
     571        4146 :     fd_stake_delegations_iter_advance_disk_root_private( iter );
     572        4146 :   }
     573       41256 : }
     574             : 
     575             : static inline int
     576       73224 : fd_stake_delegations_iter_done( fd_stake_delegations_iter_t * iter ) {
     577       73224 :   return !iter->ele;
     578       73224 : }
     579             : 
     580             : FD_PROTOTYPES_END
     581             : 
     582             : #endif /* HEADER_fd_src_flamenco_stakes_fd_stake_delegations_h */

Generated by: LCOV version 1.14