LCOV - code coverage report
Current view: top level - flamenco/stakes - fd_vote_states.h (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 1 11 9.1 %
Date: 2025-10-27 04:40:00 Functions: 0 0 -

          Line data    Source code
       1             : #ifndef HEADER_fd_src_flamenco_stakes_fd_vote_states_h
       2             : #define HEADER_fd_src_flamenco_stakes_fd_vote_states_h
       3             : 
       4             : #include "../../util/fd_util_base.h"
       5             : #include "../../util/tmpl/fd_map.h"
       6             : #include "../types/fd_types_custom.h"
       7             : 
       8           3 : #define FD_VOTE_STATES_MAGIC (0xF17EDA2CE7601E70UL) /* FIREDANCER VOTER V0 */
       9             : 
      10             : /* fd_vote_states_t is a cache of vote accounts mapping the pubkey of
      11             :    a vote account to various information about the vote account
      12             :    including, stake, last vote slot/timestamp, and commission for the
      13             :    vote account.  The vote states are safe to be used across multiple
      14             :    threads but concurrent reads/writes must be synchronized by the
      15             :    caller.
      16             : 
      17             :    In the runtime, there are 3 instances of fd_vote_states_t that are
      18             :    maintained and used at different points, notably around epoch reward
      19             :    and leader schedule calculations. The 3 instances are:
      20             :    1. vote_states: This is the vote states for the current epoch. This
      21             :       is updated through the course of an epoch as vote accounts are
      22             :       updated.
      23             :    2. vote_states_prev: This is the vote states as of the end of
      24             :       previous epoch E-1 if we are currently executing epoch E.
      25             :       This gets updated at the end of an epoch when vote_states are
      26             :       copied into vote_states_prev.
      27             :    3. vote_states_prev_prev: This is the vote states as of the end of
      28             :       epoch E-2 if we are currently executing epoch E. This only gets
      29             :       updated at the end of an epoch when vote_states_prev is copied
      30             :       into vote_states_prev_prev.
      31             : 
      32             :    The implementation of fd_vote_states_t is a hash map which is backed
      33             :    by a memory pool. Callers are allowed to insert, replace, and remove
      34             :    entries from the map.
      35             : 
      36             :    In practice, fd_vote_states_t are updated in 3 cases:
      37             :    1. They are initially populated from the versioned vote account
      38             :       stake accounts in the snapshot manifest. These are populated from
      39             :       the raw vote account data. This is done in a single pass over the
      40             :       vote account data.
      41             :    2. The vote states for the current epoch can be updated after
      42             :       transaction execution. This is done for vote accounts that are
      43             :       referenced during a transaction.
      44             :    3. Vote states are updated at the epoch boundary. The stake
      45             :       information for the vote states is refreshed at the boundary.
      46             : 
      47             :    The vote states in reality manage a few different sets of
      48             :    information about the vote account:
      49             :    - vote account state: state from the vote account data including
      50             :      the last vote slot/timestamp, commission, and node pubkey.  This is
      51             :      used for clock sysvar and rewards calculations.
      52             :    - stake: stake as of the end of the previous epoch.  This is used
      53             :      eventually for leader schedule calculations.  The stake from epoch
      54             :      T-2 (stake_t_2) is used for the stake in clock calculations.
      55             :    - rewards: this information is only used at the epoch boundary.
      56             : */
      57             : 
      58           0 : #define FD_VOTE_STATES_ALIGN (128UL)
      59             : 
      60             : /* Agave defines the max number of epoch credits to store to be 64.
      61             :    https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v2.2.6/vote-interface/src/state/mod.rs#L37 */
      62             : #define EPOCH_CREDITS_MAX (64UL)
      63             : 
      64             : struct fd_vote_state_credits {
      65             :   ulong       credits_cnt;
      66             :   ushort      epoch       [ EPOCH_CREDITS_MAX ];
      67             :   ulong       credits     [ EPOCH_CREDITS_MAX ];
      68             :   ulong       prev_credits[ EPOCH_CREDITS_MAX ];
      69             : };
      70             : typedef struct fd_vote_state_credits fd_vote_state_credits_t;
      71             : 
      72             : struct fd_vote_state_ele {
      73             :   /* Internal pool/map use */
      74             :   ulong       idx;
      75             :   ulong       next_;
      76             : 
      77             :   /* Vote account stake information which is derived from the stake
      78             :      delegations.  This information is used for leader schedule
      79             :      calculation and clock stake-weighted median calculations. */
      80             :   ulong       stake;
      81             :   ulong       stake_t_2;
      82             : 
      83             :   /* Vote account information which is derived from the vote account
      84             :      data and is used for clock timestamp calculations. */
      85             :   fd_pubkey_t vote_account;
      86             :   fd_pubkey_t node_account;
      87             :   ulong       last_vote_slot;
      88             :   long        last_vote_timestamp;
      89             :   uchar       commission;
      90             : };
      91             : typedef struct fd_vote_state_ele fd_vote_state_ele_t;
      92             : 
      93             : /* Forward declare map iterator API generated by fd_map_chain.c */
      94             : typedef struct fd_vote_state_map_private fd_vote_state_map_t;
      95             : typedef struct fd_map_chain_iter fd_vote_state_map_iter_t;
      96             : struct fd_vote_states_iter {
      97             :   fd_vote_state_map_t *    map;
      98             :   fd_vote_state_ele_t *    pool;
      99             :   fd_vote_state_map_iter_t iter;
     100             : };
     101             : typedef struct fd_vote_states_iter fd_vote_states_iter_t;
     102             : 
     103             : struct __attribute__((aligned(FD_VOTE_STATES_ALIGN))) fd_vote_states {
     104             :   ulong magic;
     105             :   ulong max_vote_accounts_;
     106             :   ulong pool_offset_;
     107             :   ulong map_offset_;
     108             : };
     109             : typedef struct fd_vote_states fd_vote_states_t;
     110             : 
     111             : /* This guarantees that the pool element alignment is at most 128UL. */
     112             : FD_STATIC_ASSERT(alignof(fd_vote_state_ele_t)<=FD_VOTE_STATES_ALIGN, unexpected pool element alignment);
     113             : 
     114             : /* The static footprint of the vote states assumes that there are
     115             :    FD_RUNTIME_MAX_VOTE_ACCOUNTS. It also assumes worst case alignment
     116             :    for each struct. fd_vote_states_t is laid out as first the
     117             :    fd_vote_states_t struct, followed by a pool of fd_vote_state_ele_t
     118             :    structs, followed by a map of fd_vote_state_map_ele_t structs.
     119             :    The pool has FD_RUNTIME_MAX_VOTE_ACCOUNTS elements, and the map
     120             :    has a chain count deteremined by a call to
     121             :    fd_vote_states_chain_cnt_est.
     122             :    NOTE: the footprint is validated to be at least as large as the
     123             :    actual runtime-determined footprint (see test_vote_states.c) */
     124             : 
     125           0 : #define FD_VOTE_STATES_CHAIN_CNT_EST (32768UL)
     126             : #define FD_VOTE_STATES_FOOTPRINT                                                      \
     127             :   /* First, layout the struct with alignment */                                       \
     128           0 :   sizeof(fd_vote_states_t) + alignof(fd_vote_states_t) +                              \
     129           0 :   /* Now layout the pool's data footprint */                                          \
     130           0 :   FD_VOTE_STATES_ALIGN + sizeof(fd_vote_state_ele_t) * FD_RUNTIME_MAX_VOTE_ACCOUNTS + \
     131           0 :   /* Now layout the pool's meta footprint */                                          \
     132           0 :   FD_VOTE_STATES_ALIGN + 128UL /* POOL_ALIGN */ +                                     \
     133           0 :   /* Now layout the map.  We must make assumptions about the chain */                 \
     134           0 :   /* count to be equivalent to chain_cnt_est. */                                      \
     135           0 :   FD_VOTE_STATES_ALIGN + 128UL /* MAP_ALIGN */ + (FD_VOTE_STATES_CHAIN_CNT_EST * sizeof(ulong))
     136             : 
     137             : FD_PROTOTYPES_BEGIN
     138             : 
     139             : /* fd_vote_states_align returns the minimum alignment required for a
     140             :    vote states struct. */
     141             : 
     142             : FD_FN_CONST ulong
     143             : fd_vote_states_align( void );
     144             : 
     145             : /* fd_vote_states_footprint returns the footprint of the vote states
     146             :    struct for a given amount of max vote accounts. */
     147             : 
     148             : FD_FN_CONST ulong
     149             : fd_vote_states_footprint( ulong max_vote_accounts );
     150             : 
     151             : /* fd_vote_states_new creates a new vote states struct with a given
     152             :    number of max vote accounts and a seed. It formats a memory region
     153             :    which is sized based off of the number of vote accounts. */
     154             : 
     155             : void *
     156             : fd_vote_states_new( void * mem,
     157             :                     ulong  max_vote_accounts,
     158             :                     ulong  seed );
     159             : 
     160             : /* fd_vote_states_join joins a vote states struct from a
     161             :    memory region. There can be multiple valid joins for a given memory
     162             :    region but the caller is responsible for accessing memory in a
     163             :    thread-safe manner. */
     164             : 
     165             : fd_vote_states_t *
     166             : fd_vote_states_join( void * mem );
     167             : 
     168             : /* fd_vote_states_update inserts or updates the vote state corresponding
     169             :    to a given account. */
     170             : 
     171             : fd_vote_state_ele_t *
     172             : fd_vote_states_update( fd_vote_states_t *  vote_states,
     173             :                        fd_pubkey_t const * vote_account );
     174             : 
     175             : /* fd_vote_states_update_from_account inserts or updates the vote state
     176             :    corresponding to a valid vote account. This is the same as
     177             :    fd_vote_states_update but is also responsible for decoding the vote
     178             :    account data into a versioned vote state object and extracing the
     179             :    commission and credits. Kills the client if the vote state cannot
     180             :    be decoded. */
     181             : 
     182             : fd_vote_state_ele_t *
     183             : fd_vote_states_update_from_account( fd_vote_states_t *  vote_states,
     184             :                                     fd_pubkey_t const * vote_account,
     185             :                                     uchar const *       account_data,
     186             :                                     ulong               account_data_len );
     187             : 
     188             : /* fd_vote_states_reset_stakes_t resets the stakes to 0 for each of the
     189             :    vote accounts in fd_vote_states_t. */
     190             : 
     191             : void
     192             : fd_vote_states_reset_stakes( fd_vote_states_t * vote_states );
     193             : 
     194             : /* fd_vote_states_remove removes the vote state corresponding to a given
     195             :    account. Does nothing if the account does not exist. */
     196             : 
     197             : void
     198             : fd_vote_states_remove( fd_vote_states_t *  vote_states,
     199             :                        fd_pubkey_t const * vote_account );
     200             : 
     201             : /* fd_vote_states_query returns the vote state corresponding to a given
     202             :    account. Returns NULL if the account does not exist. This function is
     203             :    safe for concurrent reads, but the caller needs to synchronize
     204             :    concurrent writers to the fd_vote_state_ele_t. */
     205             : 
     206             : fd_vote_state_ele_t *
     207             : fd_vote_states_query( fd_vote_states_t const * vote_states,
     208             :                       fd_pubkey_t const *      vote_account );
     209             : 
     210             : /* fd_vote_states_query_const is the same as fd_vote_states but instead
     211             :    returns a const pointer. */
     212             : 
     213             : fd_vote_state_ele_t const *
     214             : fd_vote_states_query_const( fd_vote_states_t const * vote_states,
     215             :                             fd_pubkey_t const *      vote_account );
     216             : 
     217             : /* fd_vote_states_max returns the maximum number of vote accounts that
     218             :    the vote states struct can support. */
     219             : 
     220             : ulong
     221             : fd_vote_states_max( fd_vote_states_t const * vote_states );
     222             : 
     223             : /* fd_vote_states_cnt returns the number of vote states in the vote
     224             :    states struct. */
     225             : 
     226             : ulong
     227             : fd_vote_states_cnt( fd_vote_states_t const * vote_states );
     228             : 
     229             : /* Iterator API for vote states.  The iterator is initialized with a
     230             :    call to fd_vote_states_iter_init.  The caller is responsible for
     231             :    managing the memory for the iterator.  It is safe to call
     232             :    fd_vote_states_iter_next if the result of
     233             :    fd_vote_states_iter_done() ==0.  It is safe to call
     234             :    fd_vote_states_iter_ele() to get the current vote state. As a note,
     235             :    it is safe to modify the vote state acquired from
     236             :    fd_vote_states_iter_ele() as long as the next_ field is not modified
     237             :    (which the caller should never do).  It is unsafe to insert or remove
     238             :    fd_vote_state_ele_t from the vote states struct while iterating.
     239             : 
     240             :    Under the hood, the iterator is just a wrapper over the iterator in
     241             :    fd_map_chain.c.
     242             : 
     243             :    Example use:
     244             : 
     245             :    fd_vote_states_iter_t iter_[1];
     246             :    for( fd_vote_states_iter_t * iter = fd_vote_states_iter_init( vote_states, iter_ ); !fd_vote_states_iter_done( iter ); fd_vote_states_iter_next( iter ) ) {
     247             :      fd_vote_state_ele_t * vote_state = fd_vote_states_iter_ele( iter );
     248             :      // Do something with the vote state ...
     249             :    }
     250             : */
     251             : 
     252             : fd_vote_state_ele_t *
     253             : fd_vote_states_iter_ele( fd_vote_states_iter_t * iter );
     254             : 
     255             : fd_vote_states_iter_t *
     256             : fd_vote_states_iter_init( fd_vote_states_iter_t *  iter,
     257             :                           fd_vote_states_t const * vote_states );
     258             : 
     259             : int
     260             : fd_vote_states_iter_done( fd_vote_states_iter_t * iter );
     261             : 
     262             : void
     263             : fd_vote_states_iter_next( fd_vote_states_iter_t * iter );
     264             : 
     265             : FD_PROTOTYPES_END
     266             : 
     267             : #endif /* HEADER_fd_src_flamenco_stakes_fd_vote_states_h */

Generated by: LCOV version 1.14