LCOV - code coverage report
Current view: top level - flamenco/stakes - fd_vote_stakes.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 545 587 92.8 %
Date: 2026-09-17 04:28:31 Functions: 39 39 100.0 %

          Line data    Source code
       1             : #include "fd_vote_stakes.h"
       2             : #include "../accdb/fd_accdb.h"
       3             : #include "../fd_flamenco_base.h"
       4             : #include "../runtime/fd_bank.h"
       5             : #include "../runtime/fd_runtime_const.h"
       6             : #include "../runtime/program/vote/fd_vote_state_versioned.h"
       7             : #include "../../util/bits/fd_bits.h"
       8             : #include "../../util/fd_hash32.h"
       9             : #include "../../util/log/fd_log.h"
      10             : #include "../../ballet/bls/fd_bls12_381.h"
      11             : 
      12         126 : #define FD_VOTE_STAKES_MAGIC           (0xF17EDA2CE7601E70UL) /* FIREDANCER VOTE STAKES V0 */
      13       78537 : #define FD_VOTE_STAKES_EPOCH_CACHE_CNT (3UL)
      14             : 
      15             : struct vacc {
      16             :   fd_pubkey_t pubkey;
      17             :   fd_pubkey_t node_account;
      18             :   ulong       stake;
      19             :   ushort      commission;
      20             :   ushort      alpenglow_rank;
      21             :   uchar       bls_key[ FD_BLS_PUBKEY_COMPRESSED_SZ ]; /* zero if unregistered */
      22             :   uchar       bls_key_uncompressed[ FD_BLS_PUBKEY_UNCOMPRESSED_SZ ]; /* decompressed by finalize, valid iff alpenglow_rank!=NULL */
      23             :   uint        left;
      24             :   uint        right;
      25             :   uint        next;
      26             : };
      27             : typedef struct vacc vacc_t;
      28             : 
      29             : struct vacc_rank {
      30             :   vacc_t * vacc;
      31             :   ulong    drop;
      32             : };
      33             : typedef struct vacc_rank vacc_rank_t;
      34             : 
      35             : #define SORT_NAME        vacc_rank_sort
      36          45 : #define SORT_KEY_T       vacc_rank_t
      37          24 : #define SORT_BEFORE(a,b) (((a).vacc->stake>(b).vacc->stake) | ((a).vacc->stake==(b).vacc->stake && \
      38          24 :                            memcmp( (a).vacc->bls_key, (b).vacc->bls_key, FD_BLS_PUBKEY_COMPRESSED_SZ )<0 ))
      39             : #include "../../util/tmpl/fd_sort.c"
      40             : 
      41             : #define SORT_NAME        vacc_rank_bls_sort
      42          69 : #define SORT_KEY_T       vacc_rank_t
      43          39 : #define SORT_BEFORE(a,b) (memcmp( (a).vacc->bls_key, (b).vacc->bls_key, FD_BLS_PUBKEY_COMPRESSED_SZ )<0)
      44             : #include "../../util/tmpl/fd_sort.c"
      45             : 
      46             : #define SORT_NAME        vacc_rank_identity_sort
      47          72 : #define SORT_KEY_T       vacc_rank_t
      48          42 : #define SORT_BEFORE(a,b) (memcmp( &(a).vacc->node_account, &(b).vacc->node_account, sizeof(fd_pubkey_t) )<0)
      49             : #include "../../util/tmpl/fd_sort.c"
      50             : 
      51             : #define HEAP_NAME       vacc_heap
      52        6357 : #define HEAP_IDX_T      uint
      53             : #define HEAP_T          vacc_t
      54       54912 : #define HEAP_LT(e0,e1) ( ((e0)->stake < (e1)->stake) | \
      55       54912 :                          (((e0)->stake==(e1)->stake) & \
      56       54912 :                           (memcmp( &(e0)->pubkey, &(e1)->pubkey, sizeof(fd_pubkey_t) )<0 ) ) )
      57             : #include "../../util/tmpl/fd_heap.c"
      58             : 
      59             : #define POOL_NAME  vacc_pool
      60        1932 : #define POOL_T     vacc_t
      61             : #define POOL_IDX_T uint
      62             : #define POOL_LAZY  1
      63             : #include "../../util/tmpl/fd_pool.c"
      64             : 
      65             : #define MAP_NAME               vacc_map
      66             : #define MAP_KEY_T              fd_pubkey_t
      67             : #define MAP_ELE_T              vacc_t
      68       14856 : #define MAP_KEY                pubkey
      69        5046 : #define MAP_KEY_EQ(k0,k1)      (!memcmp( k0, k1, sizeof(fd_pubkey_t) ))
      70       19953 : #define MAP_KEY_HASH(key,seed) (fd_hash32( key->uc, seed ))
      71      100491 : #define MAP_IDX_T              uint
      72             : #include "../../util/tmpl/fd_map_chain.c"
      73             : 
      74             : struct vacc_fork {
      75             :   uint ref_cnt;
      76             :   uint next;
      77             : };
      78             : typedef struct vacc_fork vacc_fork_t;
      79             : 
      80             : #define POOL_NAME  vacc_fork_pool
      81         252 : #define POOL_T     vacc_fork_t
      82             : #define POOL_IDX_T uint
      83             : #define POOL_LAZY  1
      84             : #include "../../util/tmpl/fd_pool.c"
      85             : 
      86             : struct vacc_states {
      87             :   struct {
      88             :     ulong last_vote_slot;
      89             :     long  last_vote_ts;
      90             :     uchar is_valid;
      91             :   } states[ FD_RUNTIME_MAX_VAT_VOTE_ACCOUNTS ];
      92             :   uint next;
      93             : };
      94             : typedef struct vacc_states vacc_states_t;
      95             : 
      96             : #define POOL_NAME  vacc_state_pool
      97         252 : #define POOL_T     vacc_states_t
      98             : #define POOL_IDX_T uint
      99             : #define POOL_LAZY  1
     100             : #include "../../util/tmpl/fd_pool.c"
     101             : 
     102             : struct fd_vote_stakes {
     103             :   ulong magic;
     104             :   ulong max_fork_width;
     105             :   ulong max_live_slots;
     106             :   ulong min_stake_wmark;
     107             : 
     108             :   /* (pubkey, stake) pairs for the t-2 epoch.  These are shared across
     109             :      forks/banks. */
     110             :   ulong t_2_epoch[ FD_VOTE_STAKES_EPOCH_CACHE_CNT ];
     111             :   uint  t_2_vacc_pool_off[ FD_VOTE_STAKES_EPOCH_CACHE_CNT ];
     112             :   uint  t_2_vacc_map_off [ FD_VOTE_STAKES_EPOCH_CACHE_CNT ];
     113             : 
     114             :   /* (pubkey, stake) pairs for the t-1 epoch.  These can be different
     115             :      for every fork across the epoch boundary.  These must be sized to
     116             :      the max fork width of the running system.  These are keyed by fork
     117             :      idx. */
     118             :   uint vacc_fork_pool_off;
     119             :   uint t_1_vacc_pools_off;
     120             :   uint t_1_vacc_maps_off;
     121             :   uint vacc_heap_off;
     122             : 
     123             :   /* Per-bank state about the t-2 vote state.  Each per-bank state will
     124             :      be an array sized to length max_live_slots which is a system-wide
     125             :      bound.  Keyed by bank idx. */
     126             :   uint vacc_states_pool_off;
     127             : };
     128             : typedef struct fd_vote_stakes fd_vote_stakes_t;
     129             : 
     130             : FD_FN_UNUSED static inline vacc_t *
     131             : t_2_vacc_pool( fd_vote_stakes_t const * vote_stakes,
     132       62190 :                ulong                    idx ) {
     133       62190 :   return fd_type_pun( (uchar *)vote_stakes + vote_stakes->t_2_vacc_pool_off[ idx ] );
     134       62190 : }
     135             : 
     136             : FD_FN_UNUSED static inline vacc_map_t *
     137             : t_2_vacc_map( fd_vote_stakes_t const * vote_stakes,
     138       62178 :               ulong                    idx ) {
     139       62178 :   return fd_type_pun( (uchar *)vote_stakes + vote_stakes->t_2_vacc_map_off[ idx ] );
     140       62178 : }
     141             : 
     142             : FD_FN_UNUSED static inline vacc_fork_t *
     143       18306 : vacc_fork_pool( fd_vote_stakes_t const * vote_stakes ) {
     144       18306 :   return fd_type_pun( (uchar *)vote_stakes + vote_stakes->vacc_fork_pool_off );
     145       18306 : }
     146             : 
     147             : FD_FN_UNUSED static inline vacc_t *
     148             : t_1_vacc_pool( fd_vote_stakes_t const * vote_stakes,
     149       54984 :                ulong                    width_idx ) {
     150       54984 :   ulong off = vote_stakes->t_1_vacc_pools_off + width_idx*vacc_pool_footprint( FD_RUNTIME_MAX_VAT_VOTE_ACCOUNTS );
     151       54984 :   return fd_type_pun( (uchar *)vote_stakes + off );
     152       54984 : }
     153             : 
     154             : FD_FN_UNUSED static inline vacc_map_t *
     155             : t_1_vacc_map( fd_vote_stakes_t const * vote_stakes,
     156       54960 :               ulong                    width_idx ) {
     157       54960 :   ulong chain_cnt = vacc_map_chain_cnt_est( FD_RUNTIME_MAX_VAT_VOTE_ACCOUNTS );
     158       54960 :   ulong off       = vote_stakes->t_1_vacc_maps_off + width_idx*vacc_map_footprint( chain_cnt );
     159       54960 :   return fd_type_pun( (uchar *)vote_stakes + off );
     160       54960 : }
     161             : 
     162             : FD_FN_UNUSED static inline vacc_heap_t *
     163       14838 : vacc_heap( fd_vote_stakes_t const * vote_stakes ) {
     164       14838 :   return fd_type_pun( (uchar *)vote_stakes + vote_stakes->vacc_heap_off );
     165       14838 : }
     166             : 
     167             : FD_FN_UNUSED static inline vacc_states_t *
     168       28173 : vacc_states_pool( fd_vote_stakes_t const * vote_stakes ) {
     169       28173 :   return fd_type_pun( (uchar *)vote_stakes + vote_stakes->vacc_states_pool_off );
     170       28173 : }
     171             : 
     172             : /* The fork id is a compound of the epoch, width idx, and bank idx. */
     173             : 
     174             : FD_FN_UNUSED static inline ushort
     175       16074 : fork_id_bank_id( ulong fork_id ) {
     176       16074 :   return (ushort)(fork_id & (ulong)USHORT_MAX);
     177       16074 : }
     178             : 
     179             : FD_FN_UNUSED static inline ushort
     180       23919 : fork_id_width_id( ulong fork_id ) {
     181       23919 :   return (ushort)((fork_id>>16UL) & (ulong)USHORT_MAX);
     182       23919 : }
     183             : 
     184             : FD_FN_UNUSED static inline ushort
     185       47019 : fork_id_epoch( ulong fork_id ) {
     186       47019 :   return (ushort)((fork_id>>32UL) & (ulong)USHORT_MAX);
     187       47019 : }
     188             : 
     189             : FD_FN_UNUSED static inline void
     190             : fork_id_set_bank_id( ulong * fork_id,
     191        9207 :                      ushort  bank_id ) {
     192        9207 :   *fork_id = (*fork_id & ~(ulong)USHORT_MAX) | (ulong)bank_id;
     193        9207 : }
     194             : 
     195             : FD_FN_UNUSED static inline void
     196             : fork_id_set_width_id( ulong * fork_id,
     197        9207 :                       ushort  width_id ) {
     198        9207 :   *fork_id = (*fork_id & ~((ulong)USHORT_MAX<<16UL)) | ((ulong)width_id<<16UL);
     199        9207 : }
     200             : 
     201             : FD_FN_UNUSED static inline void
     202             : fork_id_set_epoch( ulong * fork_id,
     203        9207 :                    ushort  epoch ) {
     204        9207 :   *fork_id = (*fork_id & ~((ulong)USHORT_MAX<<32UL)) | ((ulong)epoch<<32UL);
     205        9207 : }
     206             : 
     207             : ulong
     208        3867 : fd_vote_stakes_align( void ) {
     209        3867 :   return FD_VOTE_STAKES_ALIGN;
     210        3867 : }
     211             : 
     212             : ulong
     213             : fd_vote_stakes_footprint( ulong max_live_slots,
     214         648 :                           ulong max_fork_width ) {
     215         648 :   if( FD_UNLIKELY( !max_live_slots || max_live_slots>USHORT_MAX ) ) return 0UL;
     216         648 :   if( FD_UNLIKELY( !max_fork_width || max_fork_width>FD_BANKS_MAX_BANKS ) ) return 0UL;
     217             : 
     218         645 :   ulong map_chain_cnt  = vacc_map_chain_cnt_est( FD_RUNTIME_MAX_VAT_VOTE_ACCOUNTS );
     219         645 :   ulong pool_footprint = vacc_pool_footprint( FD_RUNTIME_MAX_VAT_VOTE_ACCOUNTS );
     220         645 :   ulong map_footprint  = vacc_map_footprint( map_chain_cnt );
     221         645 :   ulong width_cnt      = max_fork_width + 1UL;
     222             : 
     223         645 :   ulong l = FD_LAYOUT_INIT;
     224         645 :   l = FD_LAYOUT_APPEND( l, fd_vote_stakes_align(), sizeof(fd_vote_stakes_t) );
     225        2580 :   for( ulong i=0UL; i<FD_VOTE_STAKES_EPOCH_CACHE_CNT; i++ ) {
     226        1935 :     l = FD_LAYOUT_APPEND( l, vacc_pool_align(), pool_footprint );
     227        1935 :     l = FD_LAYOUT_APPEND( l, vacc_map_align(),  map_footprint );
     228        1935 :   }
     229         645 :   l = FD_LAYOUT_APPEND( l, vacc_fork_pool_align(),  vacc_fork_pool_footprint( width_cnt ) );
     230         645 :   l = FD_LAYOUT_APPEND( l, vacc_pool_align(),       width_cnt*pool_footprint );
     231         645 :   l = FD_LAYOUT_APPEND( l, vacc_map_align(),        width_cnt*map_footprint );
     232         645 :   l = FD_LAYOUT_APPEND( l, vacc_heap_align(),       vacc_heap_footprint( FD_RUNTIME_MAX_VAT_VOTE_ACCOUNTS ) );
     233         645 :   l = FD_LAYOUT_APPEND( l, vacc_state_pool_align(), vacc_state_pool_footprint( max_live_slots ) );
     234         645 :   return FD_LAYOUT_FINI( l, fd_vote_stakes_align() );
     235         648 : }
     236             : 
     237             : void *
     238             : fd_vote_stakes_new( void * mem,
     239             :                     ulong  max_live_slots,
     240             :                     ulong  max_fork_width,
     241         126 :                     ulong  seed ) {
     242         126 :   if( FD_UNLIKELY( !mem ) ) {
     243           0 :     FD_LOG_WARNING(( "NULL mem" ));
     244           0 :     return NULL;
     245           0 :   }
     246             : 
     247         126 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)mem, fd_vote_stakes_align() ) ) ) {
     248           0 :     FD_LOG_WARNING(( "misaligned mem" ));
     249           0 :     return NULL;
     250           0 :   }
     251             : 
     252         126 :   if( FD_UNLIKELY( !max_live_slots || max_live_slots>USHORT_MAX ) ) {
     253           0 :     FD_LOG_WARNING(( "invalid max_live_slots" ));
     254           0 :     return NULL;
     255           0 :   }
     256             : 
     257         126 :   if( FD_UNLIKELY( !max_fork_width || max_fork_width>FD_BANKS_MAX_BANKS ) ) {
     258           0 :     FD_LOG_WARNING(( "invalid max_fork_width" ));
     259           0 :     return NULL;
     260           0 :   }
     261             : 
     262         126 :   ulong map_chain_cnt  = vacc_map_chain_cnt_est( FD_RUNTIME_MAX_VAT_VOTE_ACCOUNTS );
     263         126 :   ulong pool_footprint = vacc_pool_footprint( FD_RUNTIME_MAX_VAT_VOTE_ACCOUNTS );
     264         126 :   ulong map_footprint  = vacc_map_footprint( map_chain_cnt );
     265         126 :   ulong width_cnt      = max_fork_width + 1UL;
     266             : 
     267         126 :   FD_SCRATCH_ALLOC_INIT( l, mem );
     268         126 :   fd_vote_stakes_t * vote_stakes = FD_SCRATCH_ALLOC_APPEND( l, fd_vote_stakes_align(), sizeof(fd_vote_stakes_t) );
     269         126 :   void * t_2_pool_mem[ FD_VOTE_STAKES_EPOCH_CACHE_CNT ];
     270         126 :   void * t_2_map_mem [ FD_VOTE_STAKES_EPOCH_CACHE_CNT ];
     271         504 :   for( ulong i=0UL; i<FD_VOTE_STAKES_EPOCH_CACHE_CNT; i++ ) {
     272         378 :     t_2_pool_mem[ i ] = FD_SCRATCH_ALLOC_APPEND( l, vacc_pool_align(), pool_footprint );
     273         378 :     t_2_map_mem [ i ] = FD_SCRATCH_ALLOC_APPEND( l, vacc_map_align(),  map_footprint );
     274         378 :   }
     275         126 :   void * vacc_fork_pool_mem   = FD_SCRATCH_ALLOC_APPEND( l, vacc_fork_pool_align(), vacc_fork_pool_footprint( width_cnt ) );
     276         126 :   void * t_1_vacc_pools_mem   = FD_SCRATCH_ALLOC_APPEND( l, vacc_pool_align(),      width_cnt*pool_footprint );
     277         126 :   void * t_1_vacc_maps_mem    = FD_SCRATCH_ALLOC_APPEND( l, vacc_map_align(),       width_cnt*map_footprint );
     278         126 :   void * vacc_heap_mem        = FD_SCRATCH_ALLOC_APPEND( l, vacc_heap_align(),       vacc_heap_footprint( FD_RUNTIME_MAX_VAT_VOTE_ACCOUNTS ) );
     279         126 :   void * vacc_states_pool_mem = FD_SCRATCH_ALLOC_APPEND( l, vacc_state_pool_align(), vacc_state_pool_footprint( max_live_slots ) );
     280             : 
     281         126 :   if( FD_UNLIKELY( FD_SCRATCH_ALLOC_FINI( l, fd_vote_stakes_align() )!=(ulong)mem+fd_vote_stakes_footprint( max_live_slots, max_fork_width ) ) ) {
     282           0 :     FD_LOG_WARNING(( "fd_vote_stakes_new: bad layout" ));
     283           0 :     return NULL;
     284           0 :   }
     285             : 
     286         504 :   for( ulong i=0UL; i<FD_VOTE_STAKES_EPOCH_CACHE_CNT; i++ ) {
     287         378 :     vacc_t * t_2_pool = vacc_pool_join( vacc_pool_new( t_2_pool_mem[ i ], FD_RUNTIME_MAX_VAT_VOTE_ACCOUNTS ) );
     288         378 :     if( FD_UNLIKELY( !t_2_pool ) ) {
     289           0 :       FD_LOG_WARNING(( "Failed to create t-2 vote account pool" ));
     290           0 :       return NULL;
     291           0 :     }
     292             : 
     293         378 :     vacc_map_t * t_2_map = vacc_map_join( vacc_map_new( t_2_map_mem[ i ], map_chain_cnt, seed ) );
     294         378 :     if( FD_UNLIKELY( !t_2_map ) ) {
     295           0 :       FD_LOG_WARNING(( "Failed to create t-2 vote account map" ));
     296           0 :       return NULL;
     297           0 :     }
     298             : 
     299         378 :     vote_stakes->t_2_vacc_pool_off[ i ] = (uint)((ulong)t_2_pool - (ulong)mem);
     300         378 :     vote_stakes->t_2_vacc_map_off [ i ] = (uint)((ulong)t_2_map  - (ulong)mem);
     301         378 :   }
     302             : 
     303         126 :   vacc_fork_t * fork_pool = vacc_fork_pool_join( vacc_fork_pool_new( vacc_fork_pool_mem, width_cnt ) );
     304         126 :   if( FD_UNLIKELY( !fork_pool ) ) {
     305           0 :     FD_LOG_WARNING(( "Failed to create vote account fork pool" ));
     306           0 :     return NULL;
     307           0 :   }
     308         126 :   vote_stakes->vacc_fork_pool_off = (uint)((ulong)fork_pool - (ulong)mem);
     309             : 
     310         714 :   for( ulong i=0UL; i<width_cnt; i++ ) {
     311         588 :     void *   pool_mem = (uchar *)t_1_vacc_pools_mem + i*pool_footprint;
     312         588 :     vacc_t * pool     = vacc_pool_join( vacc_pool_new( pool_mem, FD_RUNTIME_MAX_VAT_VOTE_ACCOUNTS ) );
     313         588 :     if( FD_UNLIKELY( !pool ) ) {
     314           0 :       FD_LOG_WARNING(( "Failed to create t-1 vote account pool" ));
     315           0 :       return NULL;
     316           0 :     }
     317         588 :     if( FD_UNLIKELY( !i ) ) vote_stakes->t_1_vacc_pools_off = (uint)((ulong)pool - (ulong)mem);
     318             : 
     319         588 :     void *       map_mem = (uchar *)t_1_vacc_maps_mem + i*map_footprint;
     320         588 :     vacc_map_t * map = vacc_map_join( vacc_map_new( map_mem, map_chain_cnt, seed ) );
     321         588 :     if( FD_UNLIKELY( !map ) ) {
     322           0 :       FD_LOG_WARNING(( "Failed to create t-1 vote account map" ));
     323           0 :       return NULL;
     324           0 :     }
     325         588 :   }
     326         126 :   vote_stakes->t_1_vacc_maps_off = (uint)((ulong)t_1_vacc_maps_mem - (ulong)mem);
     327             : 
     328         126 :   vacc_heap_t * heap = vacc_heap_join( vacc_heap_new( vacc_heap_mem, FD_RUNTIME_MAX_VAT_VOTE_ACCOUNTS ) );
     329         126 :   if( FD_UNLIKELY( !heap ) ) {
     330           0 :     FD_LOG_WARNING(( "Failed to create vote account heap" ));
     331           0 :     return NULL;
     332           0 :   }
     333             : 
     334         126 :   vacc_states_t * vacc_states_pool = vacc_state_pool_join( vacc_state_pool_new( vacc_states_pool_mem, max_live_slots ) );
     335         126 :   if( FD_UNLIKELY( !vacc_states_pool ) ) {
     336           0 :     FD_LOG_WARNING(( "Failed to create vote account state pool" ));
     337           0 :     return NULL;
     338           0 :   }
     339             : 
     340         126 :   vote_stakes->max_live_slots       = max_live_slots;
     341         126 :   vote_stakes->max_fork_width       = max_fork_width;
     342         126 :   vote_stakes->min_stake_wmark      = 0UL;
     343         126 :   vote_stakes->vacc_heap_off        = (uint)((ulong)heap - (ulong)mem);
     344         126 :   vote_stakes->vacc_states_pool_off = (uint)((ulong)vacc_states_pool - (ulong)mem);
     345         504 :   for( ulong i=0UL; i<FD_VOTE_STAKES_EPOCH_CACHE_CNT; i++ ) vote_stakes->t_2_epoch[ i ] = ULONG_MAX;
     346             : 
     347         126 :   FD_COMPILER_MFENCE();
     348         126 :   FD_VOLATILE( vote_stakes->magic ) = FD_VOTE_STAKES_MAGIC;
     349         126 :   FD_COMPILER_MFENCE();
     350             : 
     351         126 :   return vote_stakes;
     352         126 : }
     353             : 
     354             : fd_vote_stakes_t *
     355         249 : fd_vote_stakes_join( void * mem ) {
     356         249 :   fd_vote_stakes_t * vote_stakes = (fd_vote_stakes_t *)mem;
     357             : 
     358         249 :   if( FD_UNLIKELY( !vote_stakes ) ) {
     359           0 :     FD_LOG_WARNING(( "NULL vote stakes" ));
     360           0 :     return NULL;
     361           0 :   }
     362             : 
     363         249 :   if( FD_UNLIKELY( vote_stakes->magic!=FD_VOTE_STAKES_MAGIC ) ) {
     364           0 :     FD_LOG_WARNING(( "Invalid vote stakes magic" ));
     365           0 :     return NULL;
     366           0 :   }
     367             : 
     368         249 :   return vote_stakes;
     369         249 : }
     370             : 
     371             : ulong
     372        4530 : fd_vote_stakes_fork_epoch( ulong fork_id ) {
     373        4530 :   return (ulong)fork_id_epoch( fork_id );
     374        4530 : }
     375             : 
     376             : ulong
     377             : fd_vote_stakes_init( fd_vote_stakes_t * vote_stakes,
     378        4068 :                      ulong              epoch ) {
     379             :   /* Acquire t-1 key set width idx */
     380        4068 :   vacc_fork_t * fork_pool = vacc_fork_pool( vote_stakes );
     381        4068 :   FD_CHECK_CRIT( vacc_fork_pool_free( fork_pool ), "vote stakes width pool exhausted" );
     382        4068 :   ushort        width_idx = (ushort)vacc_fork_pool_idx_acquire( fork_pool );
     383        4068 :   vacc_fork_pool_ele( fork_pool, width_idx )->ref_cnt = 1U;
     384             : 
     385             :   /* Acquire t-2 state bank idx */
     386        4068 :   vacc_states_t * states_pool = vacc_states_pool( vote_stakes );
     387        4068 :   FD_CHECK_CRIT( vacc_state_pool_free( states_pool ), "vote stakes state pool exhausted" );
     388        4068 :   ushort          bank_idx    = (ushort)vacc_state_pool_idx_acquire( states_pool );
     389        4068 :   vacc_states_t * states = vacc_state_pool_ele( states_pool, bank_idx );
     390        4068 :   memset( states->states, 0, sizeof(states->states) );
     391        4068 :   vote_stakes->t_2_epoch[ epoch % FD_VOTE_STAKES_EPOCH_CACHE_CNT ] = epoch;
     392             : 
     393             :   /* Construct the fork id */
     394        4068 :   ulong fork_id = 0UL;
     395        4068 :   fork_id_set_bank_id( &fork_id, bank_idx );
     396        4068 :   fork_id_set_width_id( &fork_id, width_idx );
     397        4068 :   fork_id_set_epoch( &fork_id, (ushort)epoch );
     398        4068 :   return fork_id;
     399        4068 : }
     400             : 
     401             : void
     402        8031 : fd_vote_stakes_reset( fd_vote_stakes_t * vote_stakes ) {
     403       32124 :   for( ulong i=0UL; i<FD_VOTE_STAKES_EPOCH_CACHE_CNT; i++ ) {
     404       24093 :     vacc_map_reset( t_2_vacc_map( vote_stakes, i ) );
     405       24093 :     vacc_pool_reset( t_2_vacc_pool( vote_stakes, i ) );
     406       24093 :     vote_stakes->t_2_epoch[ i ] = ULONG_MAX;
     407       24093 :   }
     408             : 
     409        8031 :   ulong width_cnt = vote_stakes->max_fork_width + 1UL;
     410       48144 :   for( ulong i=0UL; i<width_cnt; i++ ) {
     411       40113 :     vacc_map_reset( t_1_vacc_map( vote_stakes, i ) );
     412       40113 :     vacc_pool_reset( t_1_vacc_pool( vote_stakes, i ) );
     413       40113 :   }
     414             : 
     415        8031 :   vacc_fork_pool_reset( vacc_fork_pool( vote_stakes ) );
     416        8031 :   vacc_state_pool_reset( vacc_states_pool( vote_stakes ) );
     417        8031 :   FD_TEST( vacc_heap_new( vacc_heap( vote_stakes ), FD_RUNTIME_MAX_VAT_VOTE_ACCOUNTS ) );
     418        8031 :   vote_stakes->min_stake_wmark = 0UL;
     419        8031 : }
     420             : 
     421             : void
     422             : fd_vote_stakes_snap_insert_t_1( fd_vote_stakes_t *  vote_stakes,
     423             :                                 ulong               fork_id,
     424             :                                 fd_pubkey_t const * pubkey,
     425             :                                 fd_pubkey_t const * node_account,
     426             :                                 ulong               stake,
     427             :                                 ushort              commission,
     428        3999 :                                 uchar const         bls_key[ static FD_BLS_PUBKEY_COMPRESSED_SZ ] ) {
     429        3999 :   if( FD_UNLIKELY( !stake ) ) return;
     430             : 
     431        3999 :   vacc_t *     pool = t_1_vacc_pool( vote_stakes, fork_id_width_id( fork_id ) );
     432        3999 :   vacc_map_t * map  = t_1_vacc_map ( vote_stakes, fork_id_width_id( fork_id ) );
     433             : 
     434        3999 :   vacc_t * vacc        = vacc_pool_ele_acquire( pool );
     435        3999 :   vacc->pubkey         = *pubkey;
     436        3999 :   vacc->node_account   = *node_account;
     437        3999 :   vacc->stake          = stake;
     438        3999 :   vacc->commission     = commission;
     439        3999 :   vacc->alpenglow_rank = FD_VOTE_STAKES_ALPENGLOW_RANK_NULL;
     440        3999 :   memcpy( vacc->bls_key, bls_key, FD_BLS_PUBKEY_COMPRESSED_SZ );
     441        3999 :   FD_TEST( vacc_map_ele_insert( map, vacc, pool ) );
     442        3999 : }
     443             : 
     444             : void
     445             : fd_vote_stakes_snap_insert_t_2( fd_vote_stakes_t *  vote_stakes,
     446             :                                 ulong               fork_id,
     447             :                                 fd_pubkey_t const * pubkey,
     448             :                                 fd_pubkey_t const * node_account,
     449             :                                 ulong               stake,
     450             :                                 ushort              commission,
     451        4023 :                                 uchar const         bls_key[ static FD_BLS_PUBKEY_COMPRESSED_SZ ] ) {
     452        4023 :   if( FD_UNLIKELY( !stake ) ) return;
     453             : 
     454        4023 :   ulong        epoch_idx = (ulong)fork_id_epoch( fork_id ) % FD_VOTE_STAKES_EPOCH_CACHE_CNT;
     455        4023 :   vacc_t *     pool      = t_2_vacc_pool( vote_stakes, epoch_idx );
     456        4023 :   vacc_map_t * map       = t_2_vacc_map ( vote_stakes, epoch_idx );
     457             : 
     458        4023 :   vacc_t * vacc        = vacc_pool_ele_acquire( pool );
     459        4023 :   vacc->pubkey         = *pubkey;
     460        4023 :   vacc->node_account   = *node_account;
     461        4023 :   vacc->stake          = stake;
     462        4023 :   vacc->commission     = commission;
     463        4023 :   vacc->alpenglow_rank = FD_VOTE_STAKES_ALPENGLOW_RANK_NULL;
     464        4023 :   memcpy( vacc->bls_key, bls_key, FD_BLS_PUBKEY_COMPRESSED_SZ );
     465        4023 :   FD_TEST( vacc_map_ele_insert( map, vacc, pool ) );
     466        4023 : }
     467             : 
     468             : void
     469             : fd_vote_stakes_snap_insert_t_3( fd_vote_stakes_t *  vote_stakes,
     470             :                                 ulong               fork_id,
     471             :                                 fd_pubkey_t const * pubkey,
     472             :                                 fd_pubkey_t const * node_account,
     473             :                                 ulong               stake,
     474             :                                 ushort              commission,
     475          21 :                                 uchar const         bls_key[ static FD_BLS_PUBKEY_COMPRESSED_SZ ] ) {
     476          21 :   ulong epoch = (ulong)fork_id_epoch( fork_id );
     477          21 :   if( FD_UNLIKELY( !epoch || !stake ) ) return;
     478             : 
     479          21 :   ulong        t_3_epoch = epoch - 1UL;
     480          21 :   ulong        t_3_idx   = t_3_epoch % FD_VOTE_STAKES_EPOCH_CACHE_CNT;
     481          21 :   vacc_t *     pool = t_2_vacc_pool( vote_stakes, t_3_idx );
     482          21 :   vacc_map_t * map  = t_2_vacc_map ( vote_stakes, t_3_idx );
     483             : 
     484          21 :   vacc_t * vacc        = vacc_pool_ele_acquire( pool );
     485          21 :   vacc->pubkey         = *pubkey;
     486          21 :   vacc->node_account   = *node_account;
     487          21 :   vacc->stake          = stake;
     488          21 :   vacc->commission     = commission;
     489          21 :   vacc->alpenglow_rank = FD_VOTE_STAKES_ALPENGLOW_RANK_NULL;
     490          21 :   memcpy( vacc->bls_key, bls_key, FD_BLS_PUBKEY_COMPRESSED_SZ );
     491          21 :   FD_TEST( vacc_map_ele_insert( map, vacc, pool ) );
     492          21 :   vote_stakes->t_2_epoch[ t_3_idx ] = t_3_epoch;
     493          21 : }
     494             : 
     495             : void
     496             : fd_vote_stakes_insert( fd_vote_stakes_t *  vote_stakes,
     497             :                        ulong               fork_id,
     498             :                        fd_pubkey_t const * pubkey,
     499             :                        fd_pubkey_t const * node_account,
     500             :                        ulong               stake,
     501             :                        ushort              commission,
     502        6354 :                        uchar const         bls_key[ static FD_BLS_PUBKEY_COMPRESSED_SZ ] ) {
     503        6354 :   ulong         width_idx = (ulong)fork_id_width_id( fork_id );
     504        6354 :   vacc_t *      pool      = t_1_vacc_pool( vote_stakes, width_idx );
     505        6354 :   vacc_map_t *  map       = t_1_vacc_map ( vote_stakes, width_idx );
     506        6354 :   vacc_heap_t * heap      = vacc_heap( vote_stakes );
     507             : 
     508        6354 :   if( FD_UNLIKELY( stake==0UL || stake<=vote_stakes->min_stake_wmark ) ) return;
     509             : 
     510        6354 :   if( FD_UNLIKELY( vacc_heap_ele_cnt( heap )==vacc_heap_ele_max( heap ) ) ) {
     511           3 :     vacc_t * ele       = vacc_heap_ele_peek_min( heap, pool );
     512           3 :     ulong    min_stake = ele->stake;
     513           3 :     if( stake<min_stake ) return;
     514             : 
     515           3 :     vote_stakes->min_stake_wmark = min_stake;
     516           9 :     while( (ele=vacc_heap_ele_peek_min( heap, pool )) && min_stake==ele->stake ) {
     517           6 :       vacc_heap_ele_remove_min( heap, pool );
     518           6 :       vacc_map_ele_remove( map, &ele->pubkey, NULL, pool );
     519           6 :       vacc_pool_ele_release( pool, ele );
     520           6 :     }
     521           3 :     if( FD_UNLIKELY( stake==min_stake ) ) return;
     522           3 :   }
     523             : 
     524        6351 :   vacc_t * vacc        = vacc_pool_ele_acquire( pool );
     525        6351 :   vacc->pubkey         = *pubkey;
     526        6351 :   vacc->node_account   = *node_account;
     527        6351 :   vacc->stake          = stake;
     528        6351 :   vacc->commission     = commission;
     529        6351 :   vacc->alpenglow_rank = FD_VOTE_STAKES_ALPENGLOW_RANK_NULL;
     530        6351 :   memcpy( vacc->bls_key, bls_key, FD_BLS_PUBKEY_COMPRESSED_SZ );
     531        6351 :   vacc_heap_ele_insert( heap, vacc, pool );
     532        6351 :   FD_TEST( vacc_map_ele_insert( map, vacc, pool ) );
     533        6351 : }
     534             : 
     535             : void
     536             : fd_vote_stakes_finalize( fd_vote_stakes_t * vote_stakes,
     537         435 :                          ulong              epoch ) {
     538         435 :   ulong epoch_idx = epoch % FD_VOTE_STAKES_EPOCH_CACHE_CNT;
     539         435 :   if( FD_UNLIKELY( vote_stakes->t_2_epoch[ epoch_idx ]!=epoch ) ) return;
     540             : 
     541         435 :   vacc_t *     pool = t_2_vacc_pool( vote_stakes, epoch_idx );
     542         435 :   vacc_map_t * map  = t_2_vacc_map ( vote_stakes, epoch_idx );
     543             : 
     544         435 :   vacc_rank_t rank[ FD_RUNTIME_MAX_VAT_VOTE_ACCOUNTS ];
     545         435 :   ulong       rank_cnt = 0UL;
     546         435 :   for( vacc_map_iter_t iter = vacc_map_iter_init( map, pool );
     547         954 :        !vacc_map_iter_done( iter, map, pool );
     548         519 :        iter = vacc_map_iter_next( iter, map, pool ) ) {
     549         519 :     vacc_t * vacc = vacc_map_iter_ele( iter, map, pool );
     550         519 :     vacc->alpenglow_rank = FD_VOTE_STAKES_ALPENGLOW_RANK_NULL;
     551         519 :     if( FD_UNLIKELY( !vacc->stake ) ) continue;
     552         519 :     if( FD_UNLIKELY( fd_bls12_381_g1_decompress_syscall( vacc->bls_key_uncompressed, vacc->bls_key, 1 ) ) ) continue;
     553          60 :     FD_TEST( rank_cnt<FD_RUNTIME_MAX_VAT_VOTE_ACCOUNTS );
     554          60 :     rank[ rank_cnt++ ] = (vacc_rank_t){ .vacc=vacc, .drop=0UL };
     555          60 :   }
     556             : 
     557         435 :   vacc_rank_bls_sort_inplace( rank, rank_cnt );
     558         465 :   for( ulong i=1UL; i<rank_cnt; i++ ) {
     559          30 :     if( FD_UNLIKELY( !memcmp( rank[i].vacc->bls_key, rank[i-1UL].vacc->bls_key, FD_BLS_PUBKEY_COMPRESSED_SZ ) ) ) {
     560           3 :       rank[i].drop = rank[i-1UL].drop = 1UL;
     561           3 :     }
     562          30 :   }
     563             : 
     564         435 :   vacc_rank_identity_sort_inplace( rank, rank_cnt );
     565         465 :   for( ulong i=1UL; i<rank_cnt; i++ ) {
     566          30 :     if( FD_UNLIKELY( fd_pubkey_eq( &rank[i].vacc->node_account, &rank[i-1UL].vacc->node_account ) ) ) {
     567           3 :       rank[i].drop = rank[i-1UL].drop = 1UL;
     568           3 :     }
     569          30 :   }
     570             : 
     571         435 :   ulong survivor_cnt = 0UL;
     572         495 :   for( ulong i=0UL; i<rank_cnt; i++ ) {
     573          60 :     if( FD_LIKELY( !rank[i].drop ) ) rank[ survivor_cnt++ ] = rank[i];
     574          60 :   }
     575         435 :   vacc_rank_sort_inplace( rank, survivor_cnt );
     576             : 
     577         483 :   for( ulong i=0UL; i<survivor_cnt; i++ ) rank[i].vacc->alpenglow_rank = (ushort)i;
     578         435 : }
     579             : 
     580             : void
     581             : fd_vote_stakes_purge_fork( fd_vote_stakes_t * vote_stakes,
     582        1068 :                            ulong              fork_id ) {
     583        1068 :   ushort bank_id  = fork_id_bank_id( fork_id );
     584        1068 :   ushort width_id = fork_id_width_id( fork_id );
     585             : 
     586        1068 :   vacc_state_pool_idx_release( vacc_states_pool( vote_stakes ), bank_id );
     587             : 
     588        1068 :   vacc_fork_t * fork_pool = vacc_fork_pool( vote_stakes );
     589        1068 :   vacc_fork_t * fork      = vacc_fork_pool_ele( fork_pool, width_id );
     590        1068 :   FD_TEST( fork->ref_cnt );
     591        1068 :   if( !--fork->ref_cnt ) {
     592         252 :     vacc_map_reset( t_1_vacc_map( vote_stakes, width_id ) );
     593         252 :     vacc_pool_reset( t_1_vacc_pool( vote_stakes, width_id ) );
     594         252 :     vacc_fork_pool_idx_release( fork_pool, width_id );
     595         252 :   }
     596        1068 : }
     597             : 
     598             : ulong
     599             : fd_vote_stakes_new_fork( fd_vote_stakes_t * vote_stakes,
     600             :                          ulong              parent_fork_id,
     601        5139 :                          ulong              epoch ) {
     602        5139 :   FD_TEST( epoch<=USHORT_MAX );
     603             : 
     604        5139 :   vacc_states_t * states_pool    = vacc_states_pool( vote_stakes );
     605        5139 :   FD_CHECK_CRIT( vacc_state_pool_free( states_pool ), "vote stakes state pool exhausted" );
     606        5139 :   ushort          parent_bank_id = fork_id_bank_id( parent_fork_id );
     607        5139 :   ushort          bank_id        = (ushort)vacc_state_pool_idx_acquire( states_pool );
     608        5139 :   ushort          parent_epoch   = fork_id_epoch( parent_fork_id );
     609        5139 :   ushort          width_id;
     610             : 
     611        5139 :   vacc_states_t * child_state = vacc_state_pool_ele( states_pool, bank_id );
     612        5139 :   if( FD_LIKELY( epoch==(ulong)parent_epoch ) ) {
     613             :     /* Standard case: just inherit the parent's t-1 set (and increment
     614             :        the ref count) and copy over the t-2 state. */
     615        4686 :     width_id = fork_id_width_id( parent_fork_id );
     616        4686 :     vacc_fork_pool_ele( vacc_fork_pool( vote_stakes ), width_id )->ref_cnt++;
     617             : 
     618        4686 :     vacc_states_t const * parent_state = vacc_state_pool_ele_const( states_pool, parent_bank_id );
     619        4686 :     memcpy( child_state->states, parent_state->states, sizeof(child_state->states) );
     620        4686 :   } else {
     621             :     /* Epoch boundary: copy the parent's t-1 set into the t-2 set and
     622             :        acquire new t-1 and t-2 state. */
     623             : 
     624             :     /* Copy the t-1 set iff it's the first time crossing into a new
     625             :        epoch boundary. */
     626         453 :     ulong t_2_idx = epoch % FD_VOTE_STAKES_EPOCH_CACHE_CNT;
     627         453 :     if( vote_stakes->t_2_epoch[ t_2_idx ]!=epoch ) {
     628         402 :       ulong        parent_width_id = (ulong)fork_id_width_id( parent_fork_id );
     629         402 :       vacc_t *     t_1_pool        = t_1_vacc_pool( vote_stakes, parent_width_id );
     630         402 :       vacc_map_t * t_1_map         = t_1_vacc_map ( vote_stakes, parent_width_id );
     631         402 :       vacc_t *     t_2_pool        = t_2_vacc_pool( vote_stakes, t_2_idx );
     632         402 :       vacc_map_t * t_2_map         = t_2_vacc_map ( vote_stakes, t_2_idx );
     633             : 
     634         402 :       vacc_map_reset( t_2_map );
     635         402 :       vacc_pool_reset( t_2_pool );
     636         402 :       for( vacc_map_iter_t iter = vacc_map_iter_init( t_1_map, t_1_pool );
     637         864 :            !vacc_map_iter_done( iter, t_1_map, t_1_pool );
     638         462 :            iter = vacc_map_iter_next( iter, t_1_map, t_1_pool ) ) {
     639         462 :         vacc_t const * src = vacc_map_iter_ele_const( iter, t_1_map, t_1_pool );
     640         462 :         vacc_t *       dst = vacc_pool_ele_acquire( t_2_pool );
     641         462 :         dst->pubkey         = src->pubkey;
     642         462 :         dst->node_account   = src->node_account;
     643         462 :         dst->stake          = src->stake;
     644         462 :         dst->commission     = src->commission;
     645         462 :         dst->alpenglow_rank = src->alpenglow_rank;
     646         462 :         memcpy( dst->bls_key, src->bls_key, FD_BLS_PUBKEY_COMPRESSED_SZ );
     647         462 :         memcpy( dst->bls_key_uncompressed, src->bls_key_uncompressed, FD_BLS_PUBKEY_UNCOMPRESSED_SZ );
     648         462 :         FD_TEST( vacc_map_ele_insert( t_2_map, dst, t_2_pool ) );
     649         462 :       }
     650         402 :       vote_stakes->t_2_epoch[ t_2_idx ] = epoch;
     651         402 :       fd_vote_stakes_finalize( vote_stakes, epoch );
     652         402 :     }
     653             : 
     654             :     /* Reset the t-2 states for the new fork and prepare the heap for
     655             :        insertion into the new t-1 set. */
     656         453 :     vacc_fork_t * fork_pool = vacc_fork_pool( vote_stakes );
     657         453 :     FD_CHECK_CRIT( vacc_fork_pool_free( fork_pool ), "vote stakes width pool exhausted" );
     658         453 :     width_id = (ushort)vacc_fork_pool_idx_acquire( fork_pool );
     659         453 :     vacc_fork_pool_ele( fork_pool, width_id )->ref_cnt = 1U;
     660         453 :     vacc_map_reset( t_1_vacc_map( vote_stakes, width_id ) );
     661         453 :     vacc_pool_reset( t_1_vacc_pool( vote_stakes, width_id ) );
     662         453 :     memset( child_state->states, 0, sizeof(child_state->states) );
     663             : 
     664         453 :     FD_TEST( vacc_heap_new( vacc_heap( vote_stakes ), FD_RUNTIME_MAX_VAT_VOTE_ACCOUNTS ) );
     665         453 :     vote_stakes->min_stake_wmark = 0UL;
     666         453 :   }
     667             : 
     668        5139 :   ulong fork_id = 0UL;
     669        5139 :   fork_id_set_bank_id( &fork_id, bank_id );
     670        5139 :   fork_id_set_width_id( &fork_id, width_id );
     671        5139 :   fork_id_set_epoch( &fork_id, (ushort)epoch );
     672        5139 :   return fork_id;
     673        5139 : }
     674             : 
     675             : void
     676             : fd_vote_stakes_update_state( fd_vote_stakes_t *  vote_stakes,
     677             :                              ulong               fork_id,
     678             :                              fd_pubkey_t const * pubkey,
     679             :                              ulong               last_vote_slot,
     680             :                              long                last_vote_ts,
     681        4404 :                              uchar               is_valid ) {
     682        4404 :   ulong        epoch_idx = (ulong)fork_id_epoch( fork_id ) % FD_VOTE_STAKES_EPOCH_CACHE_CNT;
     683        4404 :   vacc_t *     pool      = t_2_vacc_pool( vote_stakes, epoch_idx );
     684        4404 :   vacc_map_t * map       = t_2_vacc_map ( vote_stakes, epoch_idx );
     685             : 
     686        4404 :   vacc_t const * vacc = vacc_map_ele_query_const( map, pubkey, NULL, pool );
     687        4404 :   if( FD_UNLIKELY( !vacc ) ) return;
     688             : 
     689        4344 :   ulong           vacc_idx = vacc_pool_idx( pool, vacc );
     690        4344 :   vacc_states_t * states   = vacc_state_pool_ele( vacc_states_pool( vote_stakes ), fork_id_bank_id( fork_id ) );
     691        4344 :   states->states[ vacc_idx ].is_valid = (uchar)!!is_valid;
     692        4344 :   if( is_valid ) {
     693        4338 :     states->states[ vacc_idx ].last_vote_slot = last_vote_slot;
     694        4338 :     states->states[ vacc_idx ].last_vote_ts   = last_vote_ts;
     695        4338 :   }
     696        4344 : }
     697             : 
     698             : void
     699             : fd_vote_stakes_refresh( fd_vote_stakes_t * vote_stakes,
     700             :                         ulong              fork_id,
     701             :                         fd_accdb_t *        accdb,
     702         282 :                         fd_accdb_fork_id_t  accdb_fork_id ) {
     703         282 :   uchar __attribute__((aligned(FD_VOTE_STAKES_ITER_ALIGN))) iter_mem[ FD_VOTE_STAKES_ITER_FOOTPRINT ];
     704             : 
     705         282 :   for( fd_vote_stakes_iter_t * iter = fd_vote_stakes_iter_init( vote_stakes, fork_id, FD_VOTE_STAKES_ITER_T_2, iter_mem );
     706         633 :        !fd_vote_stakes_iter_done( vote_stakes, fork_id, FD_VOTE_STAKES_ITER_T_2, iter );
     707         351 :        fd_vote_stakes_iter_next( vote_stakes, fork_id, FD_VOTE_STAKES_ITER_T_2, iter ) ) {
     708         351 :     fd_pubkey_t pubkey;
     709         351 :     fd_vote_stakes_iter_ele( vote_stakes, fork_id, FD_VOTE_STAKES_ITER_T_2, iter,
     710         351 :                              &pubkey, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL );
     711             : 
     712         351 :     fd_acc_t acc = fd_accdb_read_one( accdb, accdb_fork_id, pubkey.uc );
     713         351 :     if( FD_UNLIKELY( !acc.lamports || !fd_vsv_is_correct_size_owner_and_init( acc.owner, acc.data, acc.data_len ) ) ) {
     714           3 :       fd_accdb_unread_one( accdb, &acc );
     715           3 :       fd_vote_stakes_update_state( vote_stakes, fork_id, &pubkey, 0UL, 0L, 0 );
     716           3 :       continue;
     717           3 :     }
     718             : 
     719         348 :     fd_vote_block_timestamp_t last_vote;
     720         348 :     FD_TEST( !fd_vote_account_last_timestamp( acc.data, acc.data_len, &last_vote ) );
     721         348 :     fd_vote_stakes_update_state( vote_stakes, fork_id, &pubkey, last_vote.slot, last_vote.timestamp, 1 );
     722         348 :     fd_accdb_unread_one( accdb, &acc );
     723         348 :   }
     724         282 : }
     725             : 
     726             : int
     727             : fd_vote_stakes_query_t_1( fd_vote_stakes_t const * vote_stakes,
     728             :                           ulong                    fork_id,
     729             :                           fd_pubkey_t const *      pubkey,
     730             :                           fd_pubkey_t *            node_account_out_opt,
     731             :                           ulong *                  stake_out_opt,
     732          60 :                           ushort *                 commission_out_opt ) {
     733          60 :   ulong        width_idx = (ulong)fork_id_width_id( fork_id );
     734          60 :   vacc_t *     pool      = t_1_vacc_pool( vote_stakes, width_idx );
     735          60 :   vacc_map_t * map       = t_1_vacc_map ( vote_stakes, width_idx );
     736             : 
     737          60 :   vacc_t const * vacc = vacc_map_ele_query_const( map, pubkey, NULL, pool );
     738          60 :   if( FD_UNLIKELY( !vacc ) ) return 0;
     739             : 
     740          45 :   if( node_account_out_opt ) *node_account_out_opt = vacc->node_account;
     741          45 :   if( stake_out_opt )        *stake_out_opt        = vacc->stake;
     742          45 :   if( commission_out_opt )   *commission_out_opt   = vacc->commission;
     743          45 :   return 1;
     744          60 : }
     745             : 
     746             : int
     747             : fd_vote_stakes_query_t_2( fd_vote_stakes_t const * vote_stakes,
     748             :                           ulong                    fork_id,
     749             :                           fd_pubkey_t const *      pubkey,
     750             :                           fd_pubkey_t *            node_account_out_opt,
     751             :                           ulong *                  stake_out_opt,
     752             :                           ulong *                  last_vote_slot_out_opt,
     753             :                           long *                   last_vote_ts_out_opt,
     754             :                           ushort *                 commission_out_opt,
     755         366 :                           uchar *                  is_valid_out_opt ) {
     756         366 :   ulong        epoch_idx = (ulong)fork_id_epoch( fork_id ) % FD_VOTE_STAKES_EPOCH_CACHE_CNT;
     757         366 :   vacc_t *     pool      = t_2_vacc_pool( vote_stakes, epoch_idx );
     758         366 :   vacc_map_t * map       = t_2_vacc_map ( vote_stakes, epoch_idx );
     759             : 
     760         366 :   vacc_t const * vacc = vacc_map_ele_query_const( map, pubkey, NULL, pool );
     761         366 :   if( FD_UNLIKELY( !vacc ) ) return 0;
     762             : 
     763         363 :   ulong                 vacc_idx = vacc_pool_idx( pool, vacc );
     764         363 :   vacc_states_t const * states   = vacc_state_pool_ele_const( vacc_states_pool( vote_stakes ), fork_id_bank_id( fork_id ) );
     765             : 
     766         363 :   if( node_account_out_opt )   *node_account_out_opt   = vacc->node_account;
     767         363 :   if( stake_out_opt )          *stake_out_opt          = vacc->stake;
     768         363 :   if( last_vote_slot_out_opt ) *last_vote_slot_out_opt = states->states[ vacc_idx ].last_vote_slot;
     769         363 :   if( last_vote_ts_out_opt )   *last_vote_ts_out_opt   = states->states[ vacc_idx ].last_vote_ts;
     770         363 :   if( commission_out_opt )     *commission_out_opt     = vacc->commission;
     771         363 :   if( is_valid_out_opt )       *is_valid_out_opt       = states->states[ vacc_idx ].is_valid;
     772         363 :   return 1;
     773         366 : }
     774             : 
     775             : int
     776             : fd_vote_stakes_query_t_3( fd_vote_stakes_t const * vote_stakes,
     777             :                           ulong                    fork_id,
     778             :                           fd_pubkey_t const *      pubkey,
     779             :                           fd_pubkey_t *            node_account_out_opt,
     780             :                           ulong *                  stake_out_opt,
     781         366 :                           ushort *                 commission_out_opt ) {
     782         366 :   ulong epoch = (ulong)fork_id_epoch( fork_id );
     783         366 :   if( FD_UNLIKELY( !epoch ) ) return 0;
     784             : 
     785         366 :   ulong t_3_epoch = epoch - 1UL;
     786         366 :   ulong t_3_idx   = t_3_epoch % FD_VOTE_STAKES_EPOCH_CACHE_CNT;
     787         366 :   if( FD_UNLIKELY( vote_stakes->t_2_epoch[ t_3_idx ]!=t_3_epoch ) ) return 0;
     788             : 
     789         261 :   vacc_t *       pool = t_2_vacc_pool( vote_stakes, t_3_idx );
     790         261 :   vacc_map_t *   map  = t_2_vacc_map ( vote_stakes, t_3_idx );
     791         261 :   vacc_t const * vacc = vacc_map_ele_query_const( map, pubkey, NULL, pool );
     792         261 :   if( FD_UNLIKELY( !vacc ) ) return 0;
     793             : 
     794         258 :   if( node_account_out_opt ) *node_account_out_opt = vacc->node_account;
     795         258 :   if( stake_out_opt )        *stake_out_opt        = vacc->stake;
     796         258 :   if( commission_out_opt )   *commission_out_opt   = vacc->commission;
     797         258 :   return 1;
     798         261 : }
     799             : 
     800             : ulong
     801             : fd_vote_stakes_cnt_t_1( fd_vote_stakes_t const * vote_stakes,
     802          24 :                         ulong                    fork_id ) {
     803          24 :   return vacc_pool_used( t_1_vacc_pool( vote_stakes, fork_id_width_id( fork_id ) ) );
     804          24 : }
     805             : 
     806             : ulong
     807             : fd_vote_stakes_cnt_t_2( fd_vote_stakes_t const * vote_stakes,
     808          12 :                         ulong                    fork_id ) {
     809          12 :   return vacc_pool_used( t_2_vacc_pool( vote_stakes, (ulong)fork_id_epoch( fork_id ) % FD_VOTE_STAKES_EPOCH_CACHE_CNT ) );
     810          12 : }
     811             : 
     812             : ulong
     813             : fd_vote_stakes_total_stake( fd_vote_stakes_t const * vote_stakes,
     814          15 :                             ulong                    epoch ) {
     815          15 :   ulong idx = epoch % FD_VOTE_STAKES_EPOCH_CACHE_CNT;
     816          15 :   if( FD_UNLIKELY( vote_stakes->t_2_epoch[ idx ]!=epoch ) ) return 0UL;
     817             : 
     818          15 :   vacc_t *     pool = t_2_vacc_pool( vote_stakes, idx );
     819          15 :   vacc_map_t * map  = t_2_vacc_map ( vote_stakes, idx );
     820             : 
     821          15 :   ulong total = 0UL;
     822          15 :   for( vacc_map_iter_t iter = vacc_map_iter_init( map, pool );
     823          45 :        !vacc_map_iter_done( iter, map, pool );
     824          30 :        iter = vacc_map_iter_next( iter, map, pool ) ) {
     825          30 :     total += vacc_map_iter_ele_const( iter, map, pool )->stake;
     826          30 :   }
     827          15 :   return total;
     828          15 : }
     829             : 
     830             : FD_STATIC_ASSERT( FD_VOTE_STAKES_ITER_FOOTPRINT == sizeof(vacc_map_iter_t), iter_footprint );
     831             : FD_STATIC_ASSERT( FD_VOTE_STAKES_ITER_ALIGN == alignof(vacc_map_iter_t), iter_align );
     832             : 
     833             : fd_vote_stakes_iter_t *
     834             : fd_vote_stakes_iter_init( fd_vote_stakes_t const * vote_stakes,
     835             :                           ulong                    fork_id,
     836             :                           int                      iter_kind,
     837        5739 :                           uchar                    iter_mem[ static FD_VOTE_STAKES_ITER_FOOTPRINT ] ) {
     838        5739 :   vacc_map_iter_t iter = {0};
     839        5739 :   if( FD_LIKELY( iter_kind==FD_VOTE_STAKES_ITER_T_1 ) ) {
     840         594 :     ulong        width_idx = (ulong)fork_id_width_id( fork_id );
     841         594 :     vacc_t *     pool      = t_1_vacc_pool( vote_stakes, width_idx );
     842         594 :     vacc_map_t * map       = t_1_vacc_map ( vote_stakes, width_idx );
     843         594 :     iter = vacc_map_iter_init( map, pool );
     844        5145 :   } else {
     845        5145 :     FD_TEST( iter_kind==FD_VOTE_STAKES_ITER_T_2 || iter_kind==FD_VOTE_STAKES_ITER_T_3 );
     846        5145 :     ulong fork_epoch = (ulong)fork_id_epoch( fork_id );
     847        5145 :     if( FD_LIKELY( iter_kind==FD_VOTE_STAKES_ITER_T_2 || fork_epoch ) ) {
     848        5145 :       ulong epoch     = fork_epoch-(ulong)(iter_kind==FD_VOTE_STAKES_ITER_T_3);
     849        5145 :       ulong epoch_idx = epoch % FD_VOTE_STAKES_EPOCH_CACHE_CNT;
     850        5145 :       if( FD_LIKELY( vote_stakes->t_2_epoch[ epoch_idx ]==epoch ) ) {
     851        5145 :         vacc_t *     pool = t_2_vacc_pool( vote_stakes, epoch_idx );
     852        5145 :         vacc_map_t * map  = t_2_vacc_map ( vote_stakes, epoch_idx );
     853        5145 :         iter = vacc_map_iter_init( map, pool );
     854        5145 :       }
     855        5145 :     }
     856        5145 :   }
     857        5739 :   memcpy( iter_mem, &iter, sizeof(iter) );
     858        5739 :   return (fd_vote_stakes_iter_t *)iter_mem;
     859        5739 : }
     860             : 
     861             : int
     862             : fd_vote_stakes_iter_done( fd_vote_stakes_t const * vote_stakes,
     863             :                           ulong                    fork_id,
     864             :                           int                      iter_kind,
     865       12384 :                           fd_vote_stakes_iter_t *  iter ) {
     866       12384 :   if( FD_LIKELY( iter_kind==FD_VOTE_STAKES_ITER_T_1 ) ) {
     867        1302 :     ulong        width_idx = (ulong)fork_id_width_id( fork_id );
     868        1302 :     vacc_t *     pool      = t_1_vacc_pool( vote_stakes, width_idx );
     869        1302 :     vacc_map_t * map       = t_1_vacc_map ( vote_stakes, width_idx );
     870        1302 :     return vacc_map_iter_done( *(vacc_map_iter_t *)iter, map, pool );
     871        1302 :   }
     872             : 
     873       11082 :   FD_TEST( iter_kind==FD_VOTE_STAKES_ITER_T_2 || iter_kind==FD_VOTE_STAKES_ITER_T_3 );
     874       11082 :   ulong fork_epoch = (ulong)fork_id_epoch( fork_id );
     875       11082 :   if( FD_UNLIKELY( iter_kind==FD_VOTE_STAKES_ITER_T_3 && !fork_epoch ) ) return 1;
     876             : 
     877       11082 :   ulong epoch     = fork_epoch-(ulong)(iter_kind==FD_VOTE_STAKES_ITER_T_3);
     878       11082 :   ulong epoch_idx = epoch % FD_VOTE_STAKES_EPOCH_CACHE_CNT;
     879       11082 :   if( FD_UNLIKELY( vote_stakes->t_2_epoch[ epoch_idx ]!=epoch ) ) return 1;
     880             : 
     881       11082 :   vacc_t *     pool = t_2_vacc_pool( vote_stakes, epoch_idx );
     882       11082 :   vacc_map_t * map  = t_2_vacc_map ( vote_stakes, epoch_idx );
     883       11082 :   return vacc_map_iter_done( *(vacc_map_iter_t *)iter, map, pool );
     884       11082 : }
     885             : 
     886             : void
     887             : fd_vote_stakes_iter_next( fd_vote_stakes_t const * vote_stakes,
     888             :                           ulong                    fork_id,
     889             :                           int                      iter_kind,
     890        6657 :                           fd_vote_stakes_iter_t *  iter ) {
     891        6657 :   vacc_t *     pool;
     892        6657 :   vacc_map_t * map;
     893        6657 :   if( FD_LIKELY( iter_kind==FD_VOTE_STAKES_ITER_T_1 ) ) {
     894         714 :     ulong width_idx = (ulong)fork_id_width_id( fork_id );
     895         714 :     pool = t_1_vacc_pool( vote_stakes, width_idx );
     896         714 :     map  = t_1_vacc_map ( vote_stakes, width_idx );
     897        5943 :   } else {
     898        5943 :     FD_TEST( iter_kind==FD_VOTE_STAKES_ITER_T_2 || iter_kind==FD_VOTE_STAKES_ITER_T_3 );
     899        5943 :     ulong epoch_idx = ((ulong)fork_id_epoch( fork_id )-(ulong)(iter_kind==FD_VOTE_STAKES_ITER_T_3)) % FD_VOTE_STAKES_EPOCH_CACHE_CNT;
     900        5943 :     pool = t_2_vacc_pool( vote_stakes, epoch_idx );
     901        5943 :     map  = t_2_vacc_map ( vote_stakes, epoch_idx );
     902        5943 :   }
     903        6657 :   vacc_map_iter_t * map_iter = (vacc_map_iter_t *)iter;
     904        6657 :   *map_iter = vacc_map_iter_next( *map_iter, map, pool );
     905        6657 : }
     906             : 
     907             : void
     908             : fd_vote_stakes_iter_ele( fd_vote_stakes_t const * vote_stakes,
     909             :                          ulong                    fork_id,
     910             :                          int                      iter_kind,
     911             :                          fd_vote_stakes_iter_t *  iter,
     912             :                          fd_pubkey_t *            pubkey_out,
     913             :                          fd_pubkey_t *            node_account_out_opt,
     914             :                          ulong *                  stake_out_opt,
     915             :                          ulong *                  last_vote_slot_out_opt,
     916             :                          long *                   last_vote_ts_out_opt,
     917             :                          ushort *                 commission_out_opt,
     918             :                          uchar *                  is_valid_out_opt,
     919             :                          ushort *                 alpenglow_rank_out_opt,
     920             :                          uchar                    bls_key_out_opt[ FD_BLS_PUBKEY_COMPRESSED_SZ ],
     921        6705 :                          uchar                    bls_key_uncomp_out_opt[ FD_BLS_PUBKEY_UNCOMPRESSED_SZ ] ) {
     922        6705 :   vacc_t *     pool;
     923        6705 :   vacc_map_t * map;
     924        6705 :   if( FD_LIKELY( iter_kind==FD_VOTE_STAKES_ITER_T_1 ) ) {
     925         717 :     ulong width_idx = (ulong)fork_id_width_id( fork_id );
     926         717 :     pool = t_1_vacc_pool( vote_stakes, width_idx );
     927         717 :     map  = t_1_vacc_map ( vote_stakes, width_idx );
     928        5988 :   } else {
     929        5988 :     FD_TEST( iter_kind==FD_VOTE_STAKES_ITER_T_2 || iter_kind==FD_VOTE_STAKES_ITER_T_3 );
     930        5988 :     ulong epoch_idx = ((ulong)fork_id_epoch( fork_id )-(ulong)(iter_kind==FD_VOTE_STAKES_ITER_T_3)) % FD_VOTE_STAKES_EPOCH_CACHE_CNT;
     931        5988 :     pool = t_2_vacc_pool( vote_stakes, epoch_idx );
     932        5988 :     map  = t_2_vacc_map ( vote_stakes, epoch_idx );
     933        5988 :   }
     934        6705 :   vacc_t const * vacc      = vacc_map_iter_ele_const( *(vacc_map_iter_t *)iter, map, pool );
     935             : 
     936        6705 :   *pubkey_out = vacc->pubkey;
     937        6705 :   if( node_account_out_opt )   *node_account_out_opt   = vacc->node_account;
     938        6705 :   if( stake_out_opt )          *stake_out_opt          = vacc->stake;
     939        6705 :   if( commission_out_opt )     *commission_out_opt     = vacc->commission;
     940        6705 :   if( alpenglow_rank_out_opt ) *alpenglow_rank_out_opt = vacc->alpenglow_rank;
     941        6705 :   if( bls_key_out_opt )        memcpy( bls_key_out_opt, vacc->bls_key, FD_BLS_PUBKEY_COMPRESSED_SZ );
     942        6705 :   if( bls_key_uncomp_out_opt ) memcpy( bls_key_uncomp_out_opt, vacc->bls_key_uncompressed, FD_BLS_PUBKEY_UNCOMPRESSED_SZ );
     943             : 
     944        6705 :   if( last_vote_slot_out_opt || last_vote_ts_out_opt || is_valid_out_opt ) {
     945        5160 :     FD_TEST( iter_kind==FD_VOTE_STAKES_ITER_T_2 );
     946        5160 :     ulong                 vacc_idx = vacc_pool_idx( pool, vacc );
     947        5160 :     vacc_states_t const * states   = vacc_state_pool_ele_const( vacc_states_pool( vote_stakes ), fork_id_bank_id( fork_id ) );
     948        5160 :     if( last_vote_slot_out_opt ) *last_vote_slot_out_opt = states->states[ vacc_idx ].last_vote_slot;
     949        5160 :     if( last_vote_ts_out_opt )   *last_vote_ts_out_opt   = states->states[ vacc_idx ].last_vote_ts;
     950        5160 :     if( is_valid_out_opt )       *is_valid_out_opt       = states->states[ vacc_idx ].is_valid;
     951        5160 :   }
     952        6705 : }

Generated by: LCOV version 1.14