LCOV - code coverage report
Current view: top level - choreo/tower - fd_epoch_stakes.h (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 24 24 100.0 %
Date: 2025-12-06 04:45:29 Functions: 4 18 22.2 %

          Line data    Source code
       1             : #ifndef HEADER_fd_src_choreo_tower_fd_epoch_stakes_h
       2             : #define HEADER_fd_src_choreo_tower_fd_epoch_stakes_h
       3             : 
       4             : /* fd_epoch_stakes_t tracks stakes of each voter in the epoch.  In
       5             :    general, the stakes for each voter remain constant throughout the
       6             :    epoch.  However, if we fork across the epoch boundary, the set of
       7             :    voters and their stakes may be different depending on the fork.  If
       8             :    we have not rooted an epoch slot yet, this could be different from
       9             :    other forks, but if we have rooted an epoch slot, the stakes for each
      10             :    voter are the same for all forks.
      11             : 
      12             :    This is currently exclusively used for the switch check, which
      13             :    requires the stakes of each voter on the HEAVIEST fork in the epoch.
      14             :    We need to do all this tracking because tower cannot query the vote
      15             :    states from any bank at will.  Replay determines which banks can be
      16             :    queried, and won't know beforehand which banks will be the heaviest.
      17             : 
      18             :    fd_epoch_stakes_t is backed by two hash maps:
      19             :    1. fd_voter_stake_map: this maps a vote account to a voter stake
      20             :    2. fd_epoch_stakes_slot_map: this maps a slot to a voter stake index
      21             : 
      22             :    The voter_stake_map has a compound key {vote_account, slot}, so that
      23             :    the map can be queries O(1) by slot and vote account. As we populate
      24             :    the map, we also thread a linkedlist through all the entries for the
      25             :    same slot. This is possible because the vote stake map is populated/
      26             :    updated all at once when a slot arrives from the bank, so we can
      27             :    sequentially link the current entry to the previous entry. Then the
      28             :    last entry in the linkedlist (last voter we process for a slot) will
      29             :    have its key {vote_account, slot} put in the slot_stakes_map.
      30             :    This way on publish, we have a way to query all the stakes / voters
      31             :    for a slot without doing a full scan of the voter_stake_map.
      32             : 
      33             : */
      34             : 
      35             : #include "../fd_choreo_base.h"
      36             : 
      37             : struct fd_voter_stake_key {
      38             :     fd_hash_t vote_account;
      39             :     ulong     slot;
      40             : };
      41             : typedef struct fd_voter_stake_key fd_voter_stake_key_t;
      42             : 
      43             : static const fd_voter_stake_key_t fd_voter_stake_key_null = { .vote_account = {{ 0 }}, .slot = 0UL };
      44             : 
      45             : struct fd_voter_stake {
      46             :   fd_voter_stake_key_t key;
      47             :   ulong                next;
      48             :   ulong                stake;
      49             :   ulong                prev;
      50             : };
      51             : typedef struct fd_voter_stake fd_voter_stake_t;
      52             : 
      53             : #define MAP_NAME                fd_voter_stake_map
      54             : #define MAP_ELE_T               fd_voter_stake_t
      55             : #define MAP_KEY_T               fd_voter_stake_key_t
      56          24 : #define MAP_KEY_EQ(k0,k1)       (!memcmp( k0, k1, sizeof(fd_voter_stake_key_t) ))
      57          69 : #define MAP_KEY_HASH(key, seed) fd_ulong_hash( ((key)->slot) ^ ((key)->vote_account.ul[0]) ^ (seed) )
      58             : #include "../../util/tmpl/fd_map_chain.c"
      59             : 
      60             : #define POOL_NAME fd_voter_stake_pool
      61          18 : #define POOL_T    fd_voter_stake_t
      62             : #include "../../util/tmpl/fd_pool.c"
      63             : 
      64             : /* Some really terrible witchcraft to track used vote accounts for
      65             :    whatever reason. For example, switch check needs to make sure it's
      66             :    not repeating usage of the same vote account. We can flip a bit on
      67             :    the vote stake pool index if its been used. Caller should ensure that
      68             :    the set is cleared before and after each use. The size of the set
      69             :    will be the number of elements in the vote stake pool. */
      70             : 
      71             : #define SET_NAME    fd_used_acc_scratch
      72             : #include "../../util/tmpl/fd_set_dynamic.c"
      73             : 
      74             : struct fd_epoch_stakes_slot {
      75             :   ulong slot;
      76             :   ulong voter_stake_idx; /* head of linkedlist*/
      77             : };
      78             : typedef struct fd_epoch_stakes_slot fd_epoch_stakes_slot_t;
      79             : 
      80             : #define MAP_NAME    fd_epoch_stakes_slot_map
      81          93 : #define MAP_T       fd_epoch_stakes_slot_t
      82        1227 : #define MAP_KEY     slot
      83             : #define MAP_MEMOIZE 0
      84             : #include "../../util/tmpl/fd_map_dynamic.c"
      85             : 
      86             : struct __attribute__((aligned(128UL))) fd_epoch_stakes {
      87             :   fd_voter_stake_map_t   * voter_stake_map;
      88             :   fd_voter_stake_t       * voter_stake_pool;
      89             :   fd_epoch_stakes_slot_t * slot_stakes_map;
      90             :   fd_used_acc_scratch_t  * used_acc_scratch;
      91             : };
      92             : typedef struct fd_epoch_stakes fd_epoch_stakes_t;
      93             : 
      94             : FD_PROTOTYPES_BEGIN
      95             : 
      96             : FD_FN_CONST static inline ulong
      97          72 : fd_epoch_stakes_align( void ) {
      98          72 :   return alignof(fd_epoch_stakes_t);
      99          72 : }
     100             : 
     101             : FD_FN_CONST static inline ulong
     102          18 : fd_epoch_stakes_footprint( ulong slot_max ) {
     103          18 :   int lg_slot_cnt = fd_ulong_find_msb( fd_ulong_pow2_up( slot_max ) ) + 1;
     104          18 :   return FD_LAYOUT_FINI(
     105          18 :     FD_LAYOUT_APPEND(
     106          18 :     FD_LAYOUT_APPEND(
     107          18 :     FD_LAYOUT_APPEND(
     108          18 :     FD_LAYOUT_APPEND(
     109          18 :     FD_LAYOUT_APPEND(
     110          18 :     FD_LAYOUT_INIT,
     111          18 :       alignof(fd_epoch_stakes_t),       sizeof(fd_epoch_stakes_t) ),
     112          18 :       fd_voter_stake_map_align(),       fd_voter_stake_map_footprint ( FD_VOTER_MAX * slot_max ) ),
     113          18 :       fd_voter_stake_pool_align(),      fd_voter_stake_pool_footprint( FD_VOTER_MAX * slot_max ) ),
     114          18 :       fd_epoch_stakes_slot_map_align(), fd_epoch_stakes_slot_map_footprint( lg_slot_cnt ) ),
     115          18 :       fd_used_acc_scratch_align(),      fd_used_acc_scratch_footprint( FD_VOTER_MAX * slot_max ) ),
     116          18 :     fd_epoch_stakes_align() );
     117          18 : }
     118             : 
     119             : void *
     120             : fd_epoch_stakes_new( void * shmem,
     121             :                      ulong  slot_max );
     122             : 
     123             : fd_epoch_stakes_t *
     124             : fd_epoch_stakes_join( void * shepoch_stakes );
     125             : 
     126             : /* fd_epoch_slot_stakes_add adds a new stake for a voter to the epoch stakes
     127             :    for a specific slot, and returns the index of the new voter stake in the pool.
     128             :    prev_voter_idx is the index of the previous voter stake in the pool. If this
     129             :    is the first voter inserted for this slot, prev_voter_idx should be ULONG_MAX.
     130             : 
     131             :    Usage should look like:
     132             :    prev_voter_idx = ULONG_MAX;
     133             :    for( v : voters ) {
     134             :      voter_idx = fd_epoch_stakes_slot_stakes_add( epoch_stakes, slot, v.vote_account, v.stake, prev_voter_idx );
     135             :      prev_voter_idx = voter_idx;
     136             :    } */
     137             : ulong
     138             : fd_epoch_stakes_slot_stakes_add( fd_epoch_stakes_t * epoch_stakes, ulong slot, fd_hash_t const * vote_account, ulong stake, ulong prev_voter_idx );
     139             : 
     140             : void
     141             : fd_epoch_stakes_slot_stakes_remove( fd_epoch_stakes_t * epoch_stakes, fd_epoch_stakes_slot_t * slot );
     142             : 
     143             : #endif /* HEADER_fd_src_choreo_tower_fd_epoch_stakes_h */

Generated by: LCOV version 1.14