LCOV - code coverage report
Current view: top level - choreo/tower - fd_tower_stakes.h (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 26 0.0 %
Date: 2026-03-19 18:19:27 Functions: 0 46 0.0 %

          Line data    Source code
       1             : #ifndef HEADER_fd_src_choreo_tower_fd_tower_stakes_h
       2             : #define HEADER_fd_src_choreo_tower_fd_tower_stakes_h
       3             : 
       4             : /* fd_tower_stakes_t tracks stakes of the top k voters in a slot (as of
       5             :    SIMD-0357, k=2000).  While the stakes are usually the same for every
       6             :    slot in a given epoch, it is not guaranteed.  Specifically, if there
       7             :    is a fork across an epoch boundary, the set of voters and their
       8             :    stakes may be different across slots in the same epoch because the
       9             :    slots are on different forks that calculated the stakes differently.
      10             :    If we have not rooted a slot in the new epoch yet, this could be
      11             :    different from other forks, but if we have rooted an epoch slot, the
      12             :    stakes for each voter are the same for all forks that descend from
      13             :    that root (forks that do not descend will have been pruned).
      14             : 
      15             :    This is currently exclusively used for the switch check, which
      16             :    requires the stakes of each voter on the HEAVIEST fork in the epoch.
      17             :    We need to do all this tracking because tower cannot query the vote
      18             :    states from any bank at will.  Replay determines which banks can be
      19             :    queried, and won't know beforehand which banks will be the heaviest.
      20             : 
      21             :    fd_tower_stakes_t is backed by two hash maps:
      22             :    1. fd_tower_stakes_vtr_map: this maps a vote account to a voter stake
      23             :    2. fd_tower_stakes_slot_map: this maps a slot to a voter stake index
      24             : 
      25             :    The voter_stake_map has a compound key {vote_account, slot}, so that
      26             :    the map can be queries O(1) by slot and vote account. As we populate
      27             :    the map, we also thread a linkedlist through all the entries for the
      28             :    same slot. This is possible because the vote stake map is populated/
      29             :    updated all at once when a slot arrives from the bank, so we can
      30             :    sequentially link the current entry to the previous entry. Then the
      31             :    last entry in the linkedlist (last voter we process for a slot) will
      32             :    have its key {vote_account, slot} put in the slot_stakes_map. This
      33             :    way on publish, we have a way to query all the stakes / voters for a
      34             :    slot without doing a full scan of the voter_stake_map.
      35             : 
      36             : */
      37             : 
      38             : #include "../fd_choreo_base.h"
      39             : 
      40             : struct fd_tower_stakes_vtr_xid {
      41             :     fd_hash_t addr; /* vote account address */
      42             :     ulong     slot;
      43             : };
      44             : typedef struct fd_tower_stakes_vtr_xid fd_tower_stakes_vtr_xid_t;
      45             : 
      46             : static const fd_tower_stakes_vtr_xid_t fd_tower_stakes_vtr_xid_null = { .addr = {{ 0 }}, .slot = 0UL };
      47             : 
      48             : struct fd_tower_stakes_vtr {
      49             :   fd_tower_stakes_vtr_xid_t key;
      50             :   ulong                     prev;
      51             :   ulong                     next;
      52             :   ulong                     stake;
      53             : };
      54             : typedef struct fd_tower_stakes_vtr fd_tower_stakes_vtr_t;
      55             : 
      56             : #define MAP_NAME                fd_tower_stakes_vtr_map
      57             : #define MAP_ELE_T               fd_tower_stakes_vtr_t
      58             : #define MAP_KEY_T               fd_tower_stakes_vtr_xid_t
      59           0 : #define MAP_KEY_EQ(k0,k1)       (!memcmp( k0, k1, sizeof(fd_tower_stakes_vtr_xid_t) ))
      60           0 : #define MAP_KEY_HASH(key, seed) fd_ulong_hash( ((key)->slot) ^ ((key)->addr.ul[0]) ^ (seed) )
      61             : #include "../../util/tmpl/fd_map_chain.c"
      62             : 
      63             : #define POOL_NAME fd_tower_stakes_vtr_pool
      64           0 : #define POOL_T    fd_tower_stakes_vtr_t
      65             : #include "../../util/tmpl/fd_pool.c"
      66             : 
      67             : /* Some really terrible witchcraft to track used vote accounts for
      68             :    whatever reason. For example, switch check needs to make sure it's
      69             :    not repeating usage of the same vote account. We can flip a bit on
      70             :    the vote stake pool index if its been used. Caller should ensure that
      71             :    the set is cleared before and after each use. The size of the set
      72             :    will be the number of elements in the vote stake pool. */
      73             : 
      74             : #define SET_NAME    fd_used_acc_scratch
      75             : #include "../../util/tmpl/fd_set_dynamic.c"
      76             : 
      77             : struct fd_tower_stakes_slot {
      78             :   ulong slot;
      79             :   ulong head; /* pool idx of the head of a linked list of voters in this slot */
      80             : };
      81             : typedef struct fd_tower_stakes_slot fd_tower_stakes_slot_t;
      82             : 
      83             : #define MAP_NAME           fd_tower_stakes_slot
      84           0 : #define MAP_T              fd_tower_stakes_slot_t
      85           0 : #define MAP_KEY            slot
      86           0 : #define MAP_KEY_NULL       ULONG_MAX
      87           0 : #define MAP_KEY_INVAL(key) ((key)==ULONG_MAX)
      88             : #define MAP_MEMOIZE        0
      89             : #include "../../util/tmpl/fd_map_dynamic.c"
      90             : 
      91             : struct __attribute__((aligned(128UL))) fd_tower_stakes {
      92             :   fd_tower_stakes_vtr_map_t * vtr_map;
      93             :   fd_tower_stakes_vtr_t *     vtr_pool;
      94             :   fd_tower_stakes_slot_t *    slot_map;
      95             :   fd_used_acc_scratch_t *     used_acc_scratch;
      96             : };
      97             : typedef struct fd_tower_stakes fd_tower_stakes_t;
      98             : 
      99             : FD_PROTOTYPES_BEGIN
     100             : 
     101             : FD_FN_CONST static inline ulong
     102           0 : fd_tower_stakes_align( void ) {
     103           0 :   return alignof(fd_tower_stakes_t);
     104           0 : }
     105             : 
     106             : FD_FN_CONST static inline ulong
     107           0 : fd_tower_stakes_footprint( ulong slot_max ) {
     108           0 :   int lg_slot_cnt = fd_ulong_find_msb( fd_ulong_pow2_up( slot_max ) ) + 1;
     109           0 :   return FD_LAYOUT_FINI(
     110           0 :     FD_LAYOUT_APPEND(
     111           0 :     FD_LAYOUT_APPEND(
     112           0 :     FD_LAYOUT_APPEND(
     113           0 :     FD_LAYOUT_APPEND(
     114           0 :     FD_LAYOUT_APPEND(
     115           0 :     FD_LAYOUT_INIT,
     116           0 :       alignof(fd_tower_stakes_t),       sizeof(fd_tower_stakes_t)                                     ),
     117           0 :       fd_tower_stakes_vtr_map_align(),  fd_tower_stakes_vtr_map_footprint ( FD_VOTER_MAX * slot_max ) ),
     118           0 :       fd_tower_stakes_vtr_pool_align(), fd_tower_stakes_vtr_pool_footprint( FD_VOTER_MAX * slot_max ) ),
     119           0 :       fd_tower_stakes_slot_align(),      fd_tower_stakes_slot_footprint( lg_slot_cnt )                  ),
     120           0 :       fd_used_acc_scratch_align(),      fd_used_acc_scratch_footprint( FD_VOTER_MAX * slot_max )      ),
     121           0 :     fd_tower_stakes_align() );
     122           0 : }
     123             : 
     124             : /* fd_tower_stakes_new formats an unused memory region for use as a
     125             :    tower_stakes.  mem is a non-NULL pointer to this region in the local
     126             :    address space with the required footprint and alignment. */
     127             : 
     128             : void *
     129             : fd_tower_stakes_new( void * shmem,
     130             :                      ulong  slot_max,
     131             :                      ulong  seed );
     132             : 
     133             : /* fd_tower_stakes_join joins the caller to the tower_stakes. shstakes
     134             :    points to the first byte of the memory region backing the shstakes in
     135             :    the caller's address space.
     136             : 
     137             :    Returns a pointer in the local address space to stakes on success. */
     138             : 
     139             : fd_tower_stakes_t *
     140             : fd_tower_stakes_join( void * shstakes );
     141             : 
     142             : /* fd_tower_stakes_leave stakes a current local join.  Returns a pointer
     143             :    to the underlying shared memory region on success and NULL on failure
     144             :    (logs details).  Reasons for failure include stakes is NULL. */
     145             : 
     146             : void *
     147             : fd_tower_stakes_leave( fd_tower_stakes_t const * stakes );
     148             : 
     149             : /* fd_tower_stakes_delete unformats a memory region used as a stakes.
     150             :    Assumes only the local process is joined to the region.  Returns a
     151             :    pointer to the underlying shared memory region or NULL if used
     152             :    obviously in error (e.g. stakes is obviously not a stakes ...  logs
     153             :    details).  The ownership of the memory region is transferred to the
     154             :    caller. */
     155             : 
     156             : void *
     157             : fd_tower_stakes_delete( void * stakes );
     158             : 
     159             : /* fd_tower_stakes_insert adds a new (voter, stake) pair to the epoch
     160             :    stakes for a specific slot, and returns the index of the new voter
     161             :    stake in the pool.  prev_voter_idx is the index of the previous voter
     162             :    stake in the pool.  If this is the first voter inserted for this
     163             :    slot, prev_voter_idx should be ULONG_MAX.  Example usage:
     164             : 
     165             :    prev_voter_idx = ULONG_MAX;
     166             :    for( v : voters ) {
     167             :      voter_idx = fd_tower_stakes_insert( tower_stakes, slot, v.vote_account, v.stake, prev_voter_idx );
     168             :      prev_voter_idx = voter_idx;
     169             :    } */
     170             : 
     171             : ulong
     172             : fd_tower_stakes_insert( fd_tower_stakes_t * tower_stakes,
     173             :                         ulong               slot,
     174             :                         fd_hash_t const *   vote_account,
     175             :                         ulong               stake,
     176             :                         ulong               prev_voter_idx );
     177             : 
     178             : void
     179             : fd_tower_stakes_remove( fd_tower_stakes_t * tower_stakes,
     180             :                         ulong               slot );
     181             : 
     182             : #endif /* HEADER_fd_src_choreo_tower_fd_tower_stakes_h */

Generated by: LCOV version 1.14