LCOV - code coverage report
Current view: top level - flamenco/stakes - fd_vote_stakes.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 568 725 78.3 %
Date: 2026-10-23 17:13:25 Functions: 49 54 90.7 %

          Line data    Source code
       1             : #include "fd_vote_stakes.h"
       2             : #include "../fd_rwlock.h"
       3             : #include "../accdb/fd_accdb.h"
       4             : #include "../fd_flamenco_base.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/log/fd_log.h"
       9             : #include "../../ballet/bls/fd_bls12_381.h"
      10             : 
      11             : #include <errno.h>
      12             : #include <unistd.h>
      13             : 
      14          12 : #define FD_VOTE_STAKES_MAGIC           (0xF17EDA2CE7601E70UL) /* FIREDANCER VOTE STAKES V0 */
      15       84291 : #define FD_VOTE_STAKES_EPOCH_CACHE_CNT (5UL) /* t-2..t-5 sets plus one so crossing into t+1 evicts t-6 */
      16             : 
      17             : struct vacc {
      18             :   fd_pubkey_t pubkey;
      19             :   fd_pubkey_t node_account;
      20             :   ulong       stake;
      21             :   ushort      commission;                   /* inflation rewards, basis points */
      22             :   ushort      block_revenue_commission_bps; /* SIMD-0123 */
      23             :   ulong       pending_delegator_rewards;    /* SIMD-0123, as of the set's epoch boundary */
      24             :   fd_pubkey_t inflation_collector;          /* SIMD-0232, defaults to pubkey */
      25             :   fd_pubkey_t block_collector;              /* SIMD-0232, defaults to node_account */
      26             :   ushort      rank;
      27             :   uchar       bls_key[ FD_BLS_PUBKEY_COMPRESSED_SZ ]; /* zero if unregistered */
      28             :   uchar       bls_key_uncompressed[ FD_BLS_PUBKEY_UNCOMPRESSED_SZ ]; /* decompressed by finalize, valid iff rank!=NULL */
      29             :   uint        left;
      30             :   uint        right;
      31             :   uint        next;
      32             : };
      33             : typedef struct vacc vacc_t;
      34             : 
      35             : struct vacc_rank {
      36             :   vacc_t * vacc;
      37             :   ulong    drop;
      38             : };
      39             : typedef struct vacc_rank vacc_rank_t;
      40             : 
      41             : #define SORT_NAME        vacc_rank_sort
      42           0 : #define SORT_KEY_T       vacc_rank_t
      43           0 : #define SORT_BEFORE(a,b) (((a).vacc->stake>(b).vacc->stake) | ((a).vacc->stake==(b).vacc->stake && \
      44           0 :                            memcmp( (a).vacc->bls_key, (b).vacc->bls_key, FD_BLS_PUBKEY_COMPRESSED_SZ )<0 ))
      45             : #include "../../util/tmpl/fd_sort.c"
      46             : 
      47             : #define SORT_NAME        vacc_rank_bls_sort
      48           0 : #define SORT_KEY_T       vacc_rank_t
      49           0 : #define SORT_BEFORE(a,b) (memcmp( (a).vacc->bls_key, (b).vacc->bls_key, FD_BLS_PUBKEY_COMPRESSED_SZ )<0)
      50             : #include "../../util/tmpl/fd_sort.c"
      51             : 
      52             : #define SORT_NAME        vacc_rank_identity_sort
      53           0 : #define SORT_KEY_T       vacc_rank_t
      54           0 : #define SORT_BEFORE(a,b) (memcmp( &(a).vacc->node_account, &(b).vacc->node_account, sizeof(fd_pubkey_t) )<0)
      55             : #include "../../util/tmpl/fd_sort.c"
      56             : 
      57             : #define HEAP_NAME       vacc_heap
      58         104 : #define HEAP_IDX_T      uint
      59             : #define HEAP_T          vacc_t
      60         137 : #define HEAP_LT(e0,e1) ( ((e0)->stake < (e1)->stake) | \
      61         137 :                          (((e0)->stake==(e1)->stake) & \
      62         137 :                           (memcmp( &(e0)->pubkey, &(e1)->pubkey, sizeof(fd_pubkey_t) )<0 ) ) )
      63             : #include "../../util/tmpl/fd_heap.c"
      64             : 
      65             : #define POOL_NAME  vacc_pool
      66         168 : #define POOL_T     vacc_t
      67             : #define POOL_IDX_T uint
      68             : #define POOL_LAZY  1
      69             : #include "../../util/tmpl/fd_pool.c"
      70             : 
      71             : #define MAP_NAME               vacc_map
      72             : #define MAP_KEY_T              fd_pubkey_t
      73             : #define MAP_ELE_T              vacc_t
      74        2470 : #define MAP_KEY                pubkey
      75        3182 : #define MAP_KEY_EQ(k0,k1)      (!memcmp( k0, k1, sizeof(fd_pubkey_t) ))
      76        5729 : #define MAP_KEY_HASH(key,seed) (fd_hash32( key->uc, seed ))
      77       80391 : #define MAP_IDX_T              uint
      78             : #include "../../util/tmpl/fd_map_chain.c"
      79             : 
      80             : struct vacc_fork {
      81             :   uint ref_cnt;
      82             :   uint next;
      83             :   uint cache_idx; /* UINT_MAX if the t-1 set is not cached */
      84             : };
      85             : typedef struct vacc_fork vacc_fork_t;
      86             : 
      87             : #define POOL_NAME  vacc_fork_pool
      88          24 : #define POOL_T     vacc_fork_t
      89             : #define POOL_IDX_T uint
      90             : #define POOL_LAZY  1
      91             : #include "../../util/tmpl/fd_pool.c"
      92             : 
      93             : struct vacc_states {
      94             :   struct {
      95             :     ulong last_vote_slot;
      96             :     long  last_vote_ts;
      97             :     uchar is_valid;
      98             :   } states[ FD_RUNTIME_MAX_VAT_VOTE_ACCOUNTS ];
      99             :   uint next;
     100             : };
     101             : typedef struct vacc_states vacc_states_t;
     102             : 
     103             : #define POOL_NAME  vacc_state_pool
     104          24 : #define POOL_T     vacc_states_t
     105             : #define POOL_IDX_T uint
     106             : #define POOL_LAZY  1
     107             : #include "../../util/tmpl/fd_pool.c"
     108             : 
     109             : struct t_1_cache_ent {
     110             :   ulong set_idx; /* ULONG_MAX if the entry is empty */
     111             :   ulong lru;
     112             : };
     113             : typedef struct t_1_cache_ent t_1_cache_ent_t;
     114             : 
     115             : struct fd_vote_stakes {
     116             :   ulong       magic;
     117             :   ulong       cache_cnt;
     118             :   ulong       max_live_slots;
     119             :   ulong       min_stake_wmark;
     120             :   int         disk_fd;
     121             :   ulong       lru;
     122             :   fd_rwlock_t lock;
     123             : 
     124             :   /* (pubkey, stake) pairs for the t-2 epoch.  These are shared across
     125             :      forks/banks. */
     126             :   ulong t_2_epoch[ FD_VOTE_STAKES_EPOCH_CACHE_CNT ];
     127             :   ulong t_2_vacc_pool_off[ FD_VOTE_STAKES_EPOCH_CACHE_CNT ];
     128             :   ulong t_2_vacc_map_off [ FD_VOTE_STAKES_EPOCH_CACHE_CNT ];
     129             : 
     130             :   /* (pubkey, stake) pairs for the t-1 epoch.  These can be different
     131             :      for every fork across the epoch boundary.  Up to max_live_slots
     132             :      sets exist, keyed by set idx; cache_cnt of them are held in memory
     133             :      and the rest are spilled to disk_fd. */
     134             :   ulong vacc_fork_pool_off;
     135             :   ulong t_1_cache_off;
     136             :   ulong t_1_vacc_pools_mem_off;
     137             :   ulong t_1_vacc_pools_off;
     138             :   ulong t_1_vacc_maps_off;
     139             :   ulong vacc_heap_off;
     140             : 
     141             :   /* Per-bank state about the t-2 vote state.  Each per-bank state will
     142             :      be an array sized to length max_live_slots which is a system-wide
     143             :      bound.  Keyed by bank idx. */
     144             :   ulong vacc_states_pool_off;
     145             : };
     146             : typedef struct fd_vote_stakes fd_vote_stakes_t;
     147             : 
     148             : static inline vacc_t *
     149             : t_2_vacc_pool( fd_vote_stakes_t const * vote_stakes,
     150       17211 :                ulong                    idx ) {
     151       17211 :   return fd_type_pun( (uchar *)vote_stakes + vote_stakes->t_2_vacc_pool_off[ idx ] );
     152       17211 : }
     153             : 
     154             : static inline vacc_map_t *
     155             : t_2_vacc_map( fd_vote_stakes_t const * vote_stakes,
     156       16597 :               ulong                    idx ) {
     157       16597 :   return fd_type_pun( (uchar *)vote_stakes + vote_stakes->t_2_vacc_map_off[ idx ] );
     158       16597 : }
     159             : 
     160             : static inline vacc_fork_t *
     161      265255 : vacc_fork_pool( fd_vote_stakes_t const * vote_stakes ) {
     162      265255 :   return fd_type_pun( (uchar *)vote_stakes + vote_stakes->vacc_fork_pool_off );
     163      265255 : }
     164             : 
     165             : static inline t_1_cache_ent_t *
     166      132137 : t_1_cache( fd_vote_stakes_t const * vote_stakes ) {
     167      132137 :   return fd_type_pun( (uchar *)vote_stakes + vote_stakes->t_1_cache_off );
     168      132137 : }
     169             : 
     170             : static inline vacc_t *
     171             : t_1_cache_pool( fd_vote_stakes_t const * vote_stakes,
     172       69985 :                 ulong                    cache_idx ) {
     173       69985 :   ulong off = vote_stakes->t_1_vacc_pools_off + cache_idx*vacc_pool_footprint( FD_RUNTIME_MAX_VAT_VOTE_ACCOUNTS );
     174       69985 :   return fd_type_pun( (uchar *)vote_stakes + off );
     175       69985 : }
     176             : 
     177             : static inline vacc_map_t *
     178             : t_1_cache_map( fd_vote_stakes_t const * vote_stakes,
     179       69985 :                ulong                    cache_idx ) {
     180       69985 :   ulong chain_cnt = vacc_map_chain_cnt_est( FD_RUNTIME_MAX_VAT_VOTE_ACCOUNTS );
     181       69985 :   ulong off       = vote_stakes->t_1_vacc_maps_off + cache_idx*vacc_map_footprint( chain_cnt );
     182       69985 :   return fd_type_pun( (uchar *)vote_stakes + off );
     183       69985 : }
     184             : 
     185             : static void
     186             : t_1_disk_io( fd_vote_stakes_t * vote_stakes,
     187             :              ulong              cache_idx,
     188             :              ulong              set_idx,
     189           0 :              int                write ) {
     190           0 :   ulong pool_sz = vacc_pool_footprint( FD_RUNTIME_MAX_VAT_VOTE_ACCOUNTS );
     191           0 :   ulong map_sz  = vacc_map_footprint( vacc_map_chain_cnt_est( FD_RUNTIME_MAX_VAT_VOTE_ACCOUNTS ) );
     192           0 :   uchar * bufs[2] = { (uchar *)vote_stakes + vote_stakes->t_1_vacc_pools_mem_off + cache_idx*pool_sz,
     193           0 :                       (uchar *)t_1_cache_map( vote_stakes, cache_idx ) };
     194           0 :   ulong   szs [2] = { pool_sz, map_sz };
     195           0 :   ulong   off     = set_idx*(pool_sz+map_sz);
     196           0 :   for( ulong b=0UL; b<2UL; b++ ) {
     197           0 :     ulong done = 0UL;
     198           0 :     while( done<szs[b] ) {
     199           0 :       long n = write ? pwrite( vote_stakes->disk_fd, bufs[b]+done, szs[b]-done, (long)(off+done) )
     200           0 :                      : pread ( vote_stakes->disk_fd, bufs[b]+done, szs[b]-done, (long)(off+done) );
     201           0 :       if( FD_UNLIKELY( n<0L ) ) {
     202           0 :         if( FD_LIKELY( errno==EINTR ) ) continue;
     203           0 :         FD_LOG_CRIT(( "%s(vote stakes spill file, fd=%d) failed (%i-%s)", write ? "pwrite" : "pread", vote_stakes->disk_fd, errno, fd_io_strerror( errno ) ));
     204           0 :       }
     205           0 :       if( FD_UNLIKELY( !n ) ) FD_LOG_CRIT(( "%s(vote stakes spill file, fd=%d) returned 0 at offset %lu (set=%lu)",
     206           0 :                                             write ? "pwrite" : "pread", vote_stakes->disk_fd, off+done, set_idx ));
     207           0 :       done += (ulong)n;
     208           0 :     }
     209           0 :     off += szs[b];
     210           0 :   }
     211           0 : }
     212             : 
     213             : static ulong
     214             : t_1_evict( fd_vote_stakes_t * vote_stakes,
     215       65765 :            ulong              set_idx ) {
     216       65765 :   t_1_cache_ent_t * ent    = t_1_cache( vote_stakes );
     217       65765 :   t_1_cache_ent_t * victim = ent;
     218      131523 :   for( ulong i=1UL; i<vote_stakes->cache_cnt; i++ ) if( ent[i].lru<victim->lru ) victim = ent+i;
     219             : 
     220       65765 :   ulong         cache_idx = (ulong)(victim-ent);
     221       65765 :   vacc_fork_t * fork_pool = vacc_fork_pool( vote_stakes );
     222       65765 :   if( victim->set_idx!=ULONG_MAX ) {
     223           0 :     t_1_disk_io( vote_stakes, cache_idx, victim->set_idx, 1 );
     224           0 :     vacc_fork_pool_ele( fork_pool, victim->set_idx )->cache_idx = UINT_MAX;
     225           0 :   }
     226       65765 :   victim->set_idx = set_idx;
     227       65765 :   victim->lru     = ++vote_stakes->lru;
     228       65765 :   vacc_fork_pool_ele( fork_pool, set_idx )->cache_idx = (uint)cache_idx;
     229       65765 :   return cache_idx;
     230       65765 : }
     231             : 
     232             : static ulong
     233             : t_1_lock( fd_vote_stakes_t * vote_stakes,
     234             :           ulong              set_idx,
     235        2966 :           int                write ) {
     236        2966 :   if( FD_LIKELY( !write ) ) {
     237        1743 :     fd_rwlock_read( &vote_stakes->lock );
     238        1743 :     ulong cache_idx = vacc_fork_pool_ele_const( vacc_fork_pool( vote_stakes ), set_idx )->cache_idx;
     239        1743 :     if( FD_LIKELY( cache_idx!=UINT_MAX ) ) return cache_idx;
     240           0 :     fd_rwlock_unread( &vote_stakes->lock );
     241           0 :   }
     242        1223 :   fd_rwlock_write( &vote_stakes->lock );
     243        1223 :   ulong cache_idx = vacc_fork_pool_ele_const( vacc_fork_pool( vote_stakes ), set_idx )->cache_idx;
     244        1223 :   if( FD_UNLIKELY( cache_idx==UINT_MAX ) ) {
     245           0 :     cache_idx = t_1_evict( vote_stakes, set_idx );
     246           0 :     t_1_disk_io( vote_stakes, cache_idx, set_idx, 0 );
     247        1223 :   } else {
     248        1223 :     t_1_cache( vote_stakes )[ cache_idx ].lru = ++vote_stakes->lru;
     249        1223 :   }
     250        1223 :   if( FD_LIKELY( !write ) ) fd_rwlock_demote( &vote_stakes->lock );
     251        1223 :   return cache_idx;
     252        2966 : }
     253             : 
     254             : static void
     255             : t_1_unlock( fd_vote_stakes_t * vote_stakes,
     256        2966 :             int                write ) {
     257        2966 :   if( FD_UNLIKELY( write ) ) fd_rwlock_unwrite( &vote_stakes->lock );
     258        1743 :   else                       fd_rwlock_unread ( &vote_stakes->lock );
     259        2966 : }
     260             : 
     261             : static ushort
     262       65760 : t_1_acquire( fd_vote_stakes_t * vote_stakes ) {
     263       65760 :   vacc_fork_t * fork_pool = vacc_fork_pool( vote_stakes );
     264       65760 :   FD_CHECK_CRIT( vacc_fork_pool_free( fork_pool ), "vote stakes t-1 set pool exhausted" );
     265       65760 :   ulong set_idx = vacc_fork_pool_idx_acquire( fork_pool );
     266       65760 :   vacc_fork_pool_ele( fork_pool, set_idx )->ref_cnt = 1U;
     267       65760 :   fd_rwlock_write( &vote_stakes->lock );
     268       65760 :   ulong cache_idx = t_1_evict( vote_stakes, set_idx );
     269       65760 :   vacc_map_reset ( t_1_cache_map ( vote_stakes, cache_idx ) );
     270       65760 :   vacc_pool_reset( t_1_cache_pool( vote_stakes, cache_idx ) );
     271       65760 :   fd_rwlock_unwrite( &vote_stakes->lock );
     272       65760 :   return (ushort)set_idx;
     273       65760 : }
     274             : 
     275             : static void
     276             : t_1_release( fd_vote_stakes_t * vote_stakes,
     277       64526 :              ulong              set_idx ) {
     278       64526 :   vacc_fork_t * fork_pool = vacc_fork_pool( vote_stakes );
     279       64526 :   vacc_fork_t * fork      = vacc_fork_pool_ele( fork_pool, set_idx );
     280       64526 :   fd_rwlock_write( &vote_stakes->lock );
     281       64542 :   if( fork->cache_idx!=UINT_MAX ) {
     282       64542 :     t_1_cache_ent_t * ent = t_1_cache( vote_stakes ) + fork->cache_idx;
     283       64542 :     ent->set_idx    = ULONG_MAX;
     284       64542 :     ent->lru        = 0UL;
     285       64542 :     fork->cache_idx = UINT_MAX;
     286       64542 :   }
     287       64526 :   fd_rwlock_unwrite( &vote_stakes->lock );
     288       64526 :   vacc_fork_pool_idx_release( fork_pool, set_idx );
     289       64526 : }
     290             : 
     291             : static inline vacc_heap_t *
     292         763 : vacc_heap( fd_vote_stakes_t const * vote_stakes ) {
     293         763 :   return fd_type_pun( (uchar *)vote_stakes + vote_stakes->vacc_heap_off );
     294         763 : }
     295             : 
     296             : static inline vacc_states_t *
     297      137133 : vacc_states_pool( fd_vote_stakes_t const * vote_stakes ) {
     298      137133 :   return fd_type_pun( (uchar *)vote_stakes + vote_stakes->vacc_states_pool_off );
     299      137133 : }
     300             : 
     301             : /* The fork id is a compound of the epoch, t-1 set idx, and bank idx. */
     302             : 
     303             : static inline ushort
     304       70781 : fork_id_bank_id( ulong fork_id ) {
     305       70781 :   return (ushort)(fork_id & (ulong)USHORT_MAX);
     306       70781 : }
     307             : 
     308             : static inline ushort
     309       68725 : fork_id_t_1_idx( ulong fork_id ) {
     310       68725 :   return (ushort)((fork_id>>16UL) & (ulong)USHORT_MAX);
     311       68725 : }
     312             : 
     313             : static inline ushort
     314       15458 : fork_id_epoch( ulong fork_id ) {
     315       15458 :   return (ushort)((fork_id>>32UL) & (ulong)USHORT_MAX);
     316       15458 : }
     317             : 
     318             : static inline void
     319             : fork_id_set_bank_id( ulong * fork_id,
     320       66409 :                      ushort  bank_id ) {
     321       66409 :   *fork_id = (*fork_id & ~(ulong)USHORT_MAX) | (ulong)bank_id;
     322       66409 : }
     323             : 
     324             : static inline void
     325             : fork_id_set_t_1_idx( ulong * fork_id,
     326       66400 :                      ushort  t_1_idx ) {
     327       66400 :   *fork_id = (*fork_id & ~((ulong)USHORT_MAX<<16UL)) | ((ulong)t_1_idx<<16UL);
     328       66400 : }
     329             : 
     330             : static inline void
     331             : fork_id_set_epoch( ulong * fork_id,
     332       66400 :                    ushort  epoch ) {
     333       66400 :   *fork_id = (*fork_id & ~((ulong)USHORT_MAX<<32UL)) | ((ulong)epoch<<32UL);
     334       66400 : }
     335             : 
     336             : ulong
     337         360 : fd_vote_stakes_align( void ) {
     338         360 :   return FD_VOTE_STAKES_ALIGN;
     339         360 : }
     340             : 
     341             : ulong
     342             : fd_vote_stakes_footprint( ulong max_live_slots,
     343          60 :                           ulong cache_cnt ) {
     344          60 :   if( FD_UNLIKELY( !max_live_slots || max_live_slots>USHORT_MAX ) ) return 0UL;
     345          60 :   if( FD_UNLIKELY( !cache_cnt ) ) return 0UL;
     346          60 :   cache_cnt = fd_ulong_min( cache_cnt, max_live_slots );
     347             : 
     348          60 :   ulong map_chain_cnt  = vacc_map_chain_cnt_est( FD_RUNTIME_MAX_VAT_VOTE_ACCOUNTS );
     349          60 :   ulong pool_footprint = vacc_pool_footprint( FD_RUNTIME_MAX_VAT_VOTE_ACCOUNTS );
     350          60 :   ulong map_footprint  = vacc_map_footprint( map_chain_cnt );
     351             : 
     352          60 :   ulong l = FD_LAYOUT_INIT;
     353          60 :   l = FD_LAYOUT_APPEND( l, fd_vote_stakes_align(), sizeof(fd_vote_stakes_t) );
     354         360 :   for( ulong i=0UL; i<FD_VOTE_STAKES_EPOCH_CACHE_CNT; i++ ) {
     355         300 :     l = FD_LAYOUT_APPEND( l, vacc_pool_align(), pool_footprint );
     356         300 :     l = FD_LAYOUT_APPEND( l, vacc_map_align(),  map_footprint );
     357         300 :   }
     358          60 :   l = FD_LAYOUT_APPEND( l, vacc_fork_pool_align(),  vacc_fork_pool_footprint( max_live_slots ) );
     359          60 :   l = FD_LAYOUT_APPEND( l, alignof(t_1_cache_ent_t), cache_cnt*sizeof(t_1_cache_ent_t) );
     360          60 :   l = FD_LAYOUT_APPEND( l, vacc_pool_align(),       cache_cnt*pool_footprint );
     361          60 :   l = FD_LAYOUT_APPEND( l, vacc_map_align(),        cache_cnt*map_footprint );
     362          60 :   l = FD_LAYOUT_APPEND( l, vacc_heap_align(),       vacc_heap_footprint( FD_RUNTIME_MAX_VAT_VOTE_ACCOUNTS ) );
     363          60 :   l = FD_LAYOUT_APPEND( l, vacc_state_pool_align(), vacc_state_pool_footprint( max_live_slots ) );
     364          60 :   return FD_LAYOUT_FINI( l, fd_vote_stakes_align() );
     365          60 : }
     366             : 
     367             : void *
     368             : fd_vote_stakes_new( void * mem,
     369             :                     int    disk_fd,
     370             :                     ulong  max_live_slots,
     371             :                     ulong  cache_cnt,
     372          12 :                     ulong  seed ) {
     373          12 :   if( FD_UNLIKELY( !mem ) ) {
     374           0 :     FD_LOG_WARNING(( "NULL mem" ));
     375           0 :     return NULL;
     376           0 :   }
     377             : 
     378          12 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)mem, fd_vote_stakes_align() ) ) ) {
     379           0 :     FD_LOG_WARNING(( "misaligned mem" ));
     380           0 :     return NULL;
     381           0 :   }
     382             : 
     383          12 :   if( FD_UNLIKELY( !max_live_slots || max_live_slots>USHORT_MAX ) ) {
     384           0 :     FD_LOG_WARNING(( "invalid max_live_slots" ));
     385           0 :     return NULL;
     386           0 :   }
     387             : 
     388          12 :   if( FD_UNLIKELY( !cache_cnt ) ) {
     389           0 :     FD_LOG_WARNING(( "invalid cache_cnt" ));
     390           0 :     return NULL;
     391           0 :   }
     392             : 
     393          12 :   if( FD_UNLIKELY( disk_fd<0 ) ) {
     394           0 :     FD_LOG_WARNING(( "invalid disk_fd %d", disk_fd ));
     395           0 :     return NULL;
     396           0 :   }
     397             : 
     398          12 :   ulong footprint = fd_vote_stakes_footprint( max_live_slots, cache_cnt );
     399          12 :   cache_cnt = fd_ulong_min( cache_cnt, max_live_slots );
     400             : 
     401          12 :   ulong map_chain_cnt  = vacc_map_chain_cnt_est( FD_RUNTIME_MAX_VAT_VOTE_ACCOUNTS );
     402          12 :   ulong pool_footprint = vacc_pool_footprint( FD_RUNTIME_MAX_VAT_VOTE_ACCOUNTS );
     403          12 :   ulong map_footprint  = vacc_map_footprint( map_chain_cnt );
     404             : 
     405          12 :   FD_SCRATCH_ALLOC_INIT( l, mem );
     406          12 :   fd_vote_stakes_t * vote_stakes = FD_SCRATCH_ALLOC_APPEND( l, fd_vote_stakes_align(), sizeof(fd_vote_stakes_t) );
     407           0 :   void * t_2_pool_mem[ FD_VOTE_STAKES_EPOCH_CACHE_CNT ];
     408          12 :   void * t_2_map_mem [ FD_VOTE_STAKES_EPOCH_CACHE_CNT ];
     409          72 :   for( ulong i=0UL; i<FD_VOTE_STAKES_EPOCH_CACHE_CNT; i++ ) {
     410          60 :     t_2_pool_mem[ i ] = FD_SCRATCH_ALLOC_APPEND( l, vacc_pool_align(), pool_footprint );
     411          60 :     t_2_map_mem [ i ] = FD_SCRATCH_ALLOC_APPEND( l, vacc_map_align(),  map_footprint );
     412          60 :   }
     413          12 :   void * vacc_fork_pool_mem   = FD_SCRATCH_ALLOC_APPEND( l, vacc_fork_pool_align(),   vacc_fork_pool_footprint( max_live_slots ) );
     414          12 :   void * t_1_cache_mem        = FD_SCRATCH_ALLOC_APPEND( l, alignof(t_1_cache_ent_t), cache_cnt*sizeof(t_1_cache_ent_t) );
     415          12 :   void * t_1_vacc_pools_mem   = FD_SCRATCH_ALLOC_APPEND( l, vacc_pool_align(),        cache_cnt*pool_footprint );
     416          12 :   void * t_1_vacc_maps_mem    = FD_SCRATCH_ALLOC_APPEND( l, vacc_map_align(),         cache_cnt*map_footprint );
     417          12 :   void * vacc_heap_mem        = FD_SCRATCH_ALLOC_APPEND( l, vacc_heap_align(),        vacc_heap_footprint( FD_RUNTIME_MAX_VAT_VOTE_ACCOUNTS ) );
     418          12 :   void * vacc_states_pool_mem = FD_SCRATCH_ALLOC_APPEND( l, vacc_state_pool_align(),  vacc_state_pool_footprint( max_live_slots ) );
     419             : 
     420          12 :   if( FD_UNLIKELY( FD_SCRATCH_ALLOC_FINI( l, fd_vote_stakes_align() )!=(ulong)mem+footprint ) ) {
     421           0 :     FD_LOG_WARNING(( "fd_vote_stakes_new: bad layout" ));
     422           0 :     return NULL;
     423           0 :   }
     424             : 
     425          72 :   for( ulong i=0UL; i<FD_VOTE_STAKES_EPOCH_CACHE_CNT; i++ ) {
     426          60 :     vacc_t * t_2_pool = vacc_pool_join( vacc_pool_new( t_2_pool_mem[ i ], FD_RUNTIME_MAX_VAT_VOTE_ACCOUNTS ) );
     427          60 :     if( FD_UNLIKELY( !t_2_pool ) ) {
     428           0 :       FD_LOG_WARNING(( "Failed to create t-2 vote account pool" ));
     429           0 :       return NULL;
     430           0 :     }
     431             : 
     432          60 :     vacc_map_t * t_2_map = vacc_map_join( vacc_map_new( t_2_map_mem[ i ], map_chain_cnt, seed ) );
     433          60 :     if( FD_UNLIKELY( !t_2_map ) ) {
     434           0 :       FD_LOG_WARNING(( "Failed to create t-2 vote account map" ));
     435           0 :       return NULL;
     436           0 :     }
     437             : 
     438          60 :     vote_stakes->t_2_vacc_pool_off[ i ] = (ulong)t_2_pool - (ulong)mem;
     439          60 :     vote_stakes->t_2_vacc_map_off [ i ] = (ulong)t_2_map  - (ulong)mem;
     440          60 :   }
     441             : 
     442          12 :   vacc_fork_t * fork_pool = vacc_fork_pool_join( vacc_fork_pool_new( vacc_fork_pool_mem, max_live_slots ) );
     443          12 :   if( FD_UNLIKELY( !fork_pool ) ) {
     444           0 :     FD_LOG_WARNING(( "Failed to create vote account fork pool" ));
     445           0 :     return NULL;
     446           0 :   }
     447          12 :   vote_stakes->vacc_fork_pool_off = (ulong)fork_pool - (ulong)mem;
     448             : 
     449          12 :   t_1_cache_ent_t * t_1_cache_ents = t_1_cache_mem;
     450          36 :   for( ulong i=0UL; i<cache_cnt; i++ ) t_1_cache_ents[ i ] = (t_1_cache_ent_t){ .set_idx = ULONG_MAX };
     451          12 :   vote_stakes->t_1_cache_off          = (ulong)t_1_cache_mem      - (ulong)mem;
     452          12 :   vote_stakes->t_1_vacc_pools_mem_off = (ulong)t_1_vacc_pools_mem - (ulong)mem;
     453             : 
     454          36 :   for( ulong i=0UL; i<cache_cnt; i++ ) {
     455          24 :     void *   pool_mem = (uchar *)t_1_vacc_pools_mem + i*pool_footprint;
     456          24 :     vacc_t * pool     = vacc_pool_join( vacc_pool_new( pool_mem, FD_RUNTIME_MAX_VAT_VOTE_ACCOUNTS ) );
     457          24 :     if( FD_UNLIKELY( !pool ) ) {
     458           0 :       FD_LOG_WARNING(( "Failed to create t-1 vote account pool" ));
     459           0 :       return NULL;
     460           0 :     }
     461          24 :     if( FD_UNLIKELY( !i ) ) vote_stakes->t_1_vacc_pools_off = (ulong)pool - (ulong)mem;
     462             : 
     463          24 :     void *       map_mem = (uchar *)t_1_vacc_maps_mem + i*map_footprint;
     464          24 :     vacc_map_t * map     = vacc_map_join( vacc_map_new( map_mem, map_chain_cnt, seed ) );
     465          24 :     if( FD_UNLIKELY( !map ) ) {
     466           0 :       FD_LOG_WARNING(( "Failed to create t-1 vote account map" ));
     467           0 :       return NULL;
     468           0 :     }
     469          24 :   }
     470          12 :   vote_stakes->t_1_vacc_maps_off = (ulong)t_1_vacc_maps_mem - (ulong)mem;
     471             : 
     472          12 :   vacc_heap_t * heap = vacc_heap_join( vacc_heap_new( vacc_heap_mem, FD_RUNTIME_MAX_VAT_VOTE_ACCOUNTS ) );
     473          12 :   if( FD_UNLIKELY( !heap ) ) {
     474           0 :     FD_LOG_WARNING(( "Failed to create vote account heap" ));
     475           0 :     return NULL;
     476           0 :   }
     477             : 
     478          12 :   vacc_states_t * vacc_states_pool = vacc_state_pool_join( vacc_state_pool_new( vacc_states_pool_mem, max_live_slots ) );
     479          12 :   if( FD_UNLIKELY( !vacc_states_pool ) ) {
     480           0 :     FD_LOG_WARNING(( "Failed to create vote account state pool" ));
     481           0 :     return NULL;
     482           0 :   }
     483             : 
     484          12 :   vote_stakes->max_live_slots       = max_live_slots;
     485          12 :   vote_stakes->cache_cnt            = cache_cnt;
     486          12 :   vote_stakes->disk_fd              = disk_fd;
     487          12 :   vote_stakes->lru                  = 0UL;
     488          12 :   vote_stakes->min_stake_wmark      = 0UL;
     489          12 :   vote_stakes->vacc_heap_off        = (ulong)heap - (ulong)mem;
     490          12 :   vote_stakes->vacc_states_pool_off = (ulong)vacc_states_pool - (ulong)mem;
     491          12 :   fd_rwlock_new( &vote_stakes->lock );
     492          72 :   for( ulong i=0UL; i<FD_VOTE_STAKES_EPOCH_CACHE_CNT; i++ ) vote_stakes->t_2_epoch[ i ] = ULONG_MAX;
     493             : 
     494          12 :   FD_COMPILER_MFENCE();
     495          12 :   FD_VOLATILE( vote_stakes->magic ) = FD_VOTE_STAKES_MAGIC;
     496          12 :   FD_COMPILER_MFENCE();
     497             : 
     498          12 :   return vote_stakes;
     499          12 : }
     500             : 
     501             : fd_vote_stakes_t *
     502             : fd_vote_stakes_join( void * mem,
     503          24 :                      int    disk_fd ) {
     504          24 :   fd_vote_stakes_t * vote_stakes = (fd_vote_stakes_t *)mem;
     505             : 
     506          24 :   if( FD_UNLIKELY( !vote_stakes ) ) {
     507           0 :     FD_LOG_WARNING(( "NULL vote stakes" ));
     508           0 :     return NULL;
     509           0 :   }
     510             : 
     511          24 :   if( FD_UNLIKELY( vote_stakes->magic!=FD_VOTE_STAKES_MAGIC ) ) {
     512           0 :     FD_LOG_WARNING(( "Invalid vote stakes magic" ));
     513           0 :     return NULL;
     514           0 :   }
     515             : 
     516          24 :   if( FD_UNLIKELY( vote_stakes->disk_fd!=disk_fd ) ) {
     517           0 :     FD_LOG_WARNING(( "disk_fd mismatch (vote_stakes=%d join=%d)", vote_stakes->disk_fd, disk_fd ));
     518           0 :     return NULL;
     519           0 :   }
     520             : 
     521          24 :   return vote_stakes;
     522          24 : }
     523             : 
     524             : ulong
     525         615 : fd_vote_stakes_fork_epoch( ulong fork_id ) {
     526         615 :   return (ulong)fork_id_epoch( fork_id );
     527         615 : }
     528             : 
     529             : ulong
     530             : fd_vote_stakes_init( fd_vote_stakes_t * vote_stakes,
     531       65734 :                      ulong              epoch ) {
     532             :   /* Acquire t-1 key set idx */
     533       65734 :   ushort t_1_idx = t_1_acquire( vote_stakes );
     534             : 
     535             :   /* Acquire t-2 state bank idx */
     536       65734 :   vacc_states_t * states_pool = vacc_states_pool( vote_stakes );
     537       65734 :   FD_CHECK_CRIT( vacc_state_pool_free( states_pool ), "vote stakes state pool exhausted" );
     538       65734 :   ushort          bank_idx    = (ushort)vacc_state_pool_idx_acquire( states_pool );
     539       65734 :   vacc_states_t * states = vacc_state_pool_ele( states_pool, bank_idx );
     540       65734 :   memset( states->states, 0, sizeof(states->states) );
     541       65734 :   vote_stakes->t_2_epoch[ epoch % FD_VOTE_STAKES_EPOCH_CACHE_CNT ] = epoch;
     542             : 
     543             :   /* Construct the fork id */
     544       65734 :   ulong fork_id = 0UL;
     545       65734 :   fork_id_set_bank_id( &fork_id, bank_idx );
     546       65734 :   fork_id_set_t_1_idx( &fork_id, t_1_idx );
     547       65734 :   fork_id_set_epoch( &fork_id, (ushort)epoch );
     548       65734 :   return fork_id;
     549       65734 : }
     550             : 
     551             : void
     552         627 : fd_vote_stakes_reset( fd_vote_stakes_t * vote_stakes ) {
     553        3762 :   for( ulong i=0UL; i<FD_VOTE_STAKES_EPOCH_CACHE_CNT; i++ ) {
     554        3135 :     vacc_map_reset( t_2_vacc_map( vote_stakes, i ) );
     555        3135 :     vacc_pool_reset( t_2_vacc_pool( vote_stakes, i ) );
     556        3135 :     vote_stakes->t_2_epoch[ i ] = ULONG_MAX;
     557        3135 :   }
     558             : 
     559         627 :   t_1_cache_ent_t * t_1_cache_ents = t_1_cache( vote_stakes );
     560        1881 :   for( ulong i=0UL; i<vote_stakes->cache_cnt; i++ ) {
     561        1254 :     t_1_cache_ents[ i ] = (t_1_cache_ent_t){ .set_idx = ULONG_MAX };
     562        1254 :     vacc_map_reset ( t_1_cache_map ( vote_stakes, i ) );
     563        1254 :     vacc_pool_reset( t_1_cache_pool( vote_stakes, i ) );
     564        1254 :   }
     565         627 :   vote_stakes->lru = 0UL;
     566             : 
     567         627 :   vacc_fork_pool_reset( vacc_fork_pool( vote_stakes ) );
     568         627 :   vacc_state_pool_reset( vacc_states_pool( vote_stakes ) );
     569         627 :   FD_TEST( vacc_heap_new( vacc_heap( vote_stakes ), FD_RUNTIME_MAX_VAT_VOTE_ACCOUNTS ) );
     570         627 :   vote_stakes->min_stake_wmark = 0UL;
     571         627 : }
     572             : 
     573             : static void
     574             : vacc_init( vacc_t *            vacc,
     575             :            fd_pubkey_t const * pubkey,
     576             :            fd_pubkey_t const * node_account,
     577             :            ulong               stake,
     578             :            ushort              commission,
     579             :            uchar const         bls_key[ static FD_BLS_PUBKEY_COMPRESSED_SZ ],
     580             :            ushort              block_revenue_commission_bps,
     581             :            ulong               pending_delegator_rewards,
     582             :            fd_pubkey_t const * inflation_collector_opt,
     583        2243 :            fd_pubkey_t const * block_collector_opt ) {
     584        2243 :   vacc->pubkey                       = *pubkey;
     585        2243 :   vacc->node_account                 = *node_account;
     586        2243 :   vacc->stake                        = stake;
     587        2243 :   vacc->commission                   = commission;
     588        2243 :   vacc->block_revenue_commission_bps = block_revenue_commission_bps;
     589        2243 :   vacc->pending_delegator_rewards    = pending_delegator_rewards;
     590        2243 :   vacc->inflation_collector          = inflation_collector_opt ? *inflation_collector_opt : *pubkey;
     591        2243 :   vacc->block_collector              = block_collector_opt     ? *block_collector_opt     : *node_account;
     592        2243 :   vacc->rank                         = FD_VOTE_STAKES_ALPENGLOW_RANK_NULL;
     593        2243 :   memcpy( vacc->bls_key, bls_key, FD_BLS_PUBKEY_COMPRESSED_SZ );
     594        2243 : }
     595             : 
     596             : void
     597             : fd_vote_stakes_snap_insert_t_1( fd_vote_stakes_t *  vote_stakes,
     598             :                                 ulong               fork_id,
     599             :                                 fd_pubkey_t const * pubkey,
     600             :                                 fd_pubkey_t const * node_account,
     601             :                                 ulong               stake,
     602             :                                 ushort              commission,
     603             :                                 uchar const         bls_key[ static FD_BLS_PUBKEY_COMPRESSED_SZ ],
     604             :                                 ushort              block_revenue_commission_bps,
     605             :                                 ulong               pending_delegator_rewards,
     606             :                                 fd_pubkey_t const * inflation_collector_opt,
     607        1087 :                                 fd_pubkey_t const * block_collector_opt ) {
     608        1087 :   if( FD_UNLIKELY( !stake ) ) return;
     609             : 
     610        1087 :   ulong        cache_idx = t_1_lock( vote_stakes, fork_id_t_1_idx( fork_id ), 1 );
     611        1087 :   vacc_t *     pool      = t_1_cache_pool( vote_stakes, cache_idx );
     612        1087 :   vacc_map_t * map       = t_1_cache_map ( vote_stakes, cache_idx );
     613             : 
     614        1087 :   vacc_t * vacc        = vacc_pool_ele_acquire( pool );
     615        1087 :   vacc_init( vacc, pubkey, node_account, stake, commission, bls_key,
     616        1087 :              block_revenue_commission_bps, pending_delegator_rewards, inflation_collector_opt, block_collector_opt );
     617        1087 :   FD_TEST( vacc_map_ele_insert( map, vacc, pool ) );
     618        1087 :   t_1_unlock( vote_stakes, 1 );
     619        1087 : }
     620             : 
     621             : void
     622             : fd_vote_stakes_snap_insert_t_2( fd_vote_stakes_t *  vote_stakes,
     623             :                                 ulong               fork_id,
     624             :                                 fd_pubkey_t const * pubkey,
     625             :                                 fd_pubkey_t const * node_account,
     626             :                                 ulong               stake,
     627             :                                 ushort              commission,
     628             :                                 uchar const         bls_key[ static FD_BLS_PUBKEY_COMPRESSED_SZ ],
     629             :                                 ushort              block_revenue_commission_bps,
     630             :                                 ulong               pending_delegator_rewards,
     631             :                                 fd_pubkey_t const * inflation_collector_opt,
     632        1052 :                                 fd_pubkey_t const * block_collector_opt ) {
     633        1052 :   if( FD_UNLIKELY( !stake ) ) return;
     634             : 
     635        1052 :   ulong        epoch_idx = (ulong)fork_id_epoch( fork_id ) % FD_VOTE_STAKES_EPOCH_CACHE_CNT;
     636        1052 :   vacc_t *     pool      = t_2_vacc_pool( vote_stakes, epoch_idx );
     637        1052 :   vacc_map_t * map       = t_2_vacc_map ( vote_stakes, epoch_idx );
     638             : 
     639        1052 :   vacc_t * vacc        = vacc_pool_ele_acquire( pool );
     640        1052 :   vacc_init( vacc, pubkey, node_account, stake, commission, bls_key,
     641        1052 :              block_revenue_commission_bps, pending_delegator_rewards, inflation_collector_opt, block_collector_opt );
     642        1052 :   FD_TEST( vacc_map_ele_insert( map, vacc, pool ) );
     643        1052 : }
     644             : 
     645             : void
     646             : fd_vote_stakes_snap_insert_t_n( fd_vote_stakes_t *  vote_stakes,
     647             :                                 ulong               fork_id,
     648             :                                 ulong               n,
     649             :                                 fd_pubkey_t const * pubkey,
     650             :                                 fd_pubkey_t const * node_account,
     651             :                                 ulong               stake,
     652             :                                 ushort              commission,
     653             :                                 uchar const         bls_key[ static FD_BLS_PUBKEY_COMPRESSED_SZ ],
     654             :                                 ushort              block_revenue_commission_bps,
     655             :                                 ulong               pending_delegator_rewards,
     656             :                                 fd_pubkey_t const * inflation_collector_opt,
     657           0 :                                 fd_pubkey_t const * block_collector_opt ) {
     658           0 :   ulong epoch = (ulong)fork_id_epoch( fork_id );
     659           0 :   ulong back  = n-2UL;
     660           0 :   if( FD_UNLIKELY( epoch<back || !stake ) ) return;
     661             : 
     662           0 :   ulong        t_n_epoch = epoch - back;
     663           0 :   ulong        t_n_idx   = t_n_epoch % FD_VOTE_STAKES_EPOCH_CACHE_CNT;
     664           0 :   vacc_t *     pool = t_2_vacc_pool( vote_stakes, t_n_idx );
     665           0 :   vacc_map_t * map  = t_2_vacc_map ( vote_stakes, t_n_idx );
     666             : 
     667           0 :   vacc_t * vacc        = vacc_pool_ele_acquire( pool );
     668           0 :   vacc_init( vacc, pubkey, node_account, stake, commission, bls_key,
     669           0 :              block_revenue_commission_bps, pending_delegator_rewards, inflation_collector_opt, block_collector_opt );
     670           0 :   FD_TEST( vacc_map_ele_insert( map, vacc, pool ) );
     671           0 :   vote_stakes->t_2_epoch[ t_n_idx ] = t_n_epoch;
     672           0 : }
     673             : 
     674             : static void
     675             : t_1_insert( fd_vote_stakes_t *  vote_stakes,
     676             :             vacc_t *            pool,
     677             :             vacc_map_t *        map,
     678             :             fd_pubkey_t const * pubkey,
     679             :             fd_pubkey_t const * node_account,
     680             :             ulong               stake,
     681             :             ushort              commission,
     682             :             uchar const         bls_key[ static FD_BLS_PUBKEY_COMPRESSED_SZ ],
     683             :             ushort              block_revenue_commission_bps,
     684             :             ulong               pending_delegator_rewards,
     685             :             fd_pubkey_t const * inflation_collector_opt,
     686         104 :             fd_pubkey_t const * block_collector_opt ) {
     687         104 :   vacc_heap_t * heap = vacc_heap( vote_stakes );
     688             : 
     689         104 :   if( FD_UNLIKELY( stake==0UL || stake<=vote_stakes->min_stake_wmark ) ) return;
     690             : 
     691         104 :   if( FD_UNLIKELY( vacc_heap_ele_cnt( heap )==vacc_heap_ele_max( heap ) ) ) {
     692           0 :     vacc_t * ele       = vacc_heap_ele_peek_min( heap, pool );
     693           0 :     ulong    min_stake = ele->stake;
     694           0 :     if( stake<min_stake ) return;
     695             : 
     696           0 :     vote_stakes->min_stake_wmark = min_stake;
     697           0 :     while( (ele=vacc_heap_ele_peek_min( heap, pool )) && min_stake==ele->stake ) {
     698           0 :       vacc_heap_ele_remove_min( heap, pool );
     699           0 :       vacc_map_ele_remove( map, &ele->pubkey, NULL, pool );
     700           0 :       vacc_pool_ele_release( pool, ele );
     701           0 :     }
     702           0 :     if( FD_UNLIKELY( stake==min_stake ) ) return;
     703           0 :   }
     704             : 
     705         104 :   vacc_t * vacc        = vacc_pool_ele_acquire( pool );
     706         104 :   vacc_init( vacc, pubkey, node_account, stake, commission, bls_key,
     707         104 :              block_revenue_commission_bps, pending_delegator_rewards, inflation_collector_opt, block_collector_opt );
     708         104 :   vacc_heap_ele_insert( heap, vacc, pool );
     709         104 :   FD_TEST( vacc_map_ele_insert( map, vacc, pool ) );
     710         104 : }
     711             : 
     712             : void
     713             : fd_vote_stakes_insert( fd_vote_stakes_t *  vote_stakes,
     714             :                        ulong               fork_id,
     715             :                        fd_pubkey_t const * pubkey,
     716             :                        fd_pubkey_t const * node_account,
     717             :                        ulong               stake,
     718             :                        ushort              commission,
     719             :                        uchar const         bls_key[ static FD_BLS_PUBKEY_COMPRESSED_SZ ],
     720             :                        ushort              block_revenue_commission_bps,
     721             :                        ulong               pending_delegator_rewards,
     722             :                        fd_pubkey_t const * inflation_collector_opt,
     723         104 :                        fd_pubkey_t const * block_collector_opt ) {
     724         104 :   ulong cache_idx = t_1_lock( vote_stakes, fork_id_t_1_idx( fork_id ), 1 );
     725         104 :   t_1_insert( vote_stakes, t_1_cache_pool( vote_stakes, cache_idx ), t_1_cache_map( vote_stakes, cache_idx ),
     726         104 :               pubkey, node_account, stake, commission, bls_key,
     727         104 :               block_revenue_commission_bps, pending_delegator_rewards, inflation_collector_opt, block_collector_opt );
     728         104 :   t_1_unlock( vote_stakes, 1 );
     729         104 : }
     730             : 
     731             : void
     732             : fd_vote_stakes_finalize( fd_vote_stakes_t * vote_stakes,
     733             :                          ulong              fork_id,
     734          32 :                          int                iter_kind ) {
     735          32 :   vacc_t *     pool;
     736          32 :   vacc_map_t * map;
     737          32 :   ulong        cache_idx = ULONG_MAX;
     738          32 :   if( FD_LIKELY( iter_kind==FD_VOTE_STAKES_ITER_T_1 ) ) {
     739          32 :     cache_idx = t_1_lock( vote_stakes, fork_id_t_1_idx( fork_id ), 1 );
     740          32 :     pool = t_1_cache_pool( vote_stakes, cache_idx );
     741          32 :     map  = t_1_cache_map ( vote_stakes, cache_idx );
     742          32 :   } else {
     743           0 :     FD_TEST( iter_kind>=FD_VOTE_STAKES_ITER_T_2 && iter_kind<=FD_VOTE_STAKES_ITER_T_5 );
     744           0 :     ulong fork_epoch = (ulong)fork_id_epoch( fork_id );
     745           0 :     ulong back       = (ulong)(iter_kind-FD_VOTE_STAKES_ITER_T_2);
     746           0 :     if( FD_UNLIKELY( fork_epoch<back ) ) return;
     747             : 
     748           0 :     ulong epoch     = fork_epoch-back;
     749           0 :     ulong epoch_idx = epoch % FD_VOTE_STAKES_EPOCH_CACHE_CNT;
     750           0 :     if( FD_UNLIKELY( vote_stakes->t_2_epoch[ epoch_idx ]!=epoch ) ) return;
     751           0 :     pool = t_2_vacc_pool( vote_stakes, epoch_idx );
     752           0 :     map  = t_2_vacc_map ( vote_stakes, epoch_idx );
     753           0 :   }
     754             : 
     755          32 :   vacc_rank_t rank[ FD_RUNTIME_MAX_VAT_VOTE_ACCOUNTS ];
     756          32 :   ulong       rank_cnt = 0UL;
     757          32 :   for( vacc_map_iter_t iter = vacc_map_iter_init( map, pool );
     758         136 :        !vacc_map_iter_done( iter, map, pool );
     759         104 :        iter = vacc_map_iter_next( iter, map, pool ) ) {
     760         104 :     vacc_t * vacc = vacc_map_iter_ele( iter, map, pool );
     761         104 :     vacc->rank = FD_VOTE_STAKES_ALPENGLOW_RANK_NULL;
     762         104 :     if( FD_UNLIKELY( !vacc->stake ) ) continue;
     763         104 :     if( FD_UNLIKELY( fd_bls12_381_g1_decompress_syscall( vacc->bls_key_uncompressed, vacc->bls_key, 1 ) ) ) continue;
     764           0 :     if( FD_UNLIKELY( vacc->bls_key[0]&0x40 ) ) continue; /* infinity: agave's rank map rejects the identity point */
     765           0 :     FD_TEST( rank_cnt<FD_RUNTIME_MAX_VAT_VOTE_ACCOUNTS );
     766           0 :     rank[ rank_cnt++ ] = (vacc_rank_t){ .vacc=vacc, .drop=0UL };
     767           0 :   }
     768             : 
     769          32 :   vacc_rank_bls_sort_inplace( rank, rank_cnt );
     770          32 :   for( ulong i=1UL; i<rank_cnt; i++ ) {
     771           0 :     if( FD_UNLIKELY( !memcmp( rank[i].vacc->bls_key, rank[i-1UL].vacc->bls_key, FD_BLS_PUBKEY_COMPRESSED_SZ ) ) ) {
     772           0 :       rank[i].drop = rank[i-1UL].drop = 1UL;
     773           0 :     }
     774           0 :   }
     775             : 
     776          32 :   vacc_rank_identity_sort_inplace( rank, rank_cnt );
     777          32 :   for( ulong i=1UL; i<rank_cnt; i++ ) {
     778           0 :     if( FD_UNLIKELY( fd_pubkey_eq( &rank[i].vacc->node_account, &rank[i-1UL].vacc->node_account ) ) ) {
     779           0 :       rank[i].drop = rank[i-1UL].drop = 1UL;
     780           0 :     }
     781           0 :   }
     782             : 
     783          32 :   ulong survivor_cnt = 0UL;
     784          32 :   for( ulong i=0UL; i<rank_cnt; i++ ) {
     785           0 :     if( FD_LIKELY( !rank[i].drop ) ) rank[ survivor_cnt++ ] = rank[i];
     786           0 :   }
     787          32 :   vacc_rank_sort_inplace( rank, survivor_cnt );
     788             : 
     789          32 :   for( ulong i=0UL; i<survivor_cnt; i++ ) rank[i].vacc->rank = (ushort)i;
     790          32 :   if( cache_idx!=ULONG_MAX ) t_1_unlock( vote_stakes, 1 );
     791          32 : }
     792             : 
     793             : void
     794             : fd_vote_stakes_purge_fork( fd_vote_stakes_t * vote_stakes,
     795       65132 :                            ulong              fork_id ) {
     796       65132 :   ushort bank_id  = fork_id_bank_id( fork_id );
     797       65132 :   ushort t_1_idx = fork_id_t_1_idx( fork_id );
     798             : 
     799       65132 :   vacc_state_pool_idx_release( vacc_states_pool( vote_stakes ), bank_id );
     800             : 
     801       65132 :   vacc_fork_t * fork = vacc_fork_pool_ele( vacc_fork_pool( vote_stakes ), t_1_idx );
     802       65132 :   FD_TEST( fork->ref_cnt );
     803       65132 :   if( !--fork->ref_cnt ) t_1_release( vote_stakes, t_1_idx );
     804       65132 : }
     805             : 
     806             : ulong
     807             : fd_vote_stakes_new_fork( fd_vote_stakes_t * vote_stakes,
     808             :                          ulong              parent_fork_id,
     809         659 :                          ulong              epoch ) {
     810         659 :   FD_TEST( epoch<=USHORT_MAX );
     811             : 
     812         659 :   vacc_states_t * states_pool    = vacc_states_pool( vote_stakes );
     813         659 :   FD_CHECK_CRIT( vacc_state_pool_free( states_pool ), "vote stakes state pool exhausted" );
     814         659 :   ushort          parent_bank_id = fork_id_bank_id( parent_fork_id );
     815         659 :   ushort          bank_id        = (ushort)vacc_state_pool_idx_acquire( states_pool );
     816         659 :   ushort          parent_epoch   = fork_id_epoch( parent_fork_id );
     817         659 :   ushort          t_1_idx;
     818             : 
     819         659 :   vacc_states_t * child_state = vacc_state_pool_ele( states_pool, bank_id );
     820         659 :   if( FD_LIKELY( epoch==(ulong)parent_epoch ) ) {
     821             :     /* Standard case: just inherit the parent's t-1 set (and increment
     822             :        the ref count) and copy over the t-2 state. */
     823         627 :     t_1_idx = fork_id_t_1_idx( parent_fork_id );
     824         627 :     vacc_fork_pool_ele( vacc_fork_pool( vote_stakes ), t_1_idx )->ref_cnt++;
     825             : 
     826         627 :     vacc_states_t const * parent_state = vacc_state_pool_ele_const( states_pool, parent_bank_id );
     827         627 :     memcpy( child_state->states, parent_state->states, sizeof(child_state->states) );
     828         627 :   } else {
     829             :     /* Epoch boundary: copy the parent's t-1 set into the t-2 set and
     830             :        acquire new t-1 and t-2 state. */
     831             : 
     832             :     /* Copy the t-1 set, ranks included, iff it's the first time
     833             :        crossing into a new epoch boundary. */
     834          32 :     ulong t_2_idx = epoch % FD_VOTE_STAKES_EPOCH_CACHE_CNT;
     835          32 :     if( vote_stakes->t_2_epoch[ t_2_idx ]!=epoch ) {
     836          32 :       ulong        t_1_cache_idx = t_1_lock( vote_stakes, fork_id_t_1_idx( parent_fork_id ), 0 );
     837          32 :       vacc_t *     t_1_pool      = t_1_cache_pool( vote_stakes, t_1_cache_idx );
     838          32 :       vacc_map_t * t_1_map       = t_1_cache_map ( vote_stakes, t_1_cache_idx );
     839          32 :       vacc_t *     t_2_pool      = t_2_vacc_pool( vote_stakes, t_2_idx );
     840          32 :       vacc_map_t * t_2_map       = t_2_vacc_map ( vote_stakes, t_2_idx );
     841             : 
     842          32 :       vacc_map_reset( t_2_map );
     843          32 :       vacc_pool_reset( t_2_pool );
     844          32 :       for( vacc_map_iter_t iter = vacc_map_iter_init( t_1_map, t_1_pool );
     845         259 :            !vacc_map_iter_done( iter, t_1_map, t_1_pool );
     846         227 :            iter = vacc_map_iter_next( iter, t_1_map, t_1_pool ) ) {
     847         227 :         vacc_t const * src = vacc_map_iter_ele_const( iter, t_1_map, t_1_pool );
     848         227 :         vacc_t *       dst = vacc_pool_ele_acquire( t_2_pool );
     849         227 :         dst->pubkey         = src->pubkey;
     850         227 :         dst->node_account   = src->node_account;
     851         227 :         dst->stake          = src->stake;
     852         227 :         dst->commission     = src->commission;
     853         227 :         dst->block_revenue_commission_bps = src->block_revenue_commission_bps;
     854         227 :         dst->pending_delegator_rewards    = src->pending_delegator_rewards;
     855         227 :         dst->inflation_collector          = src->inflation_collector;
     856         227 :         dst->block_collector              = src->block_collector;
     857         227 :         dst->rank = src->rank;
     858         227 :         memcpy( dst->bls_key, src->bls_key, FD_BLS_PUBKEY_COMPRESSED_SZ );
     859         227 :         memcpy( dst->bls_key_uncompressed, src->bls_key_uncompressed, FD_BLS_PUBKEY_UNCOMPRESSED_SZ );
     860         227 :         FD_TEST( vacc_map_ele_insert( t_2_map, dst, t_2_pool ) );
     861         227 :       }
     862          32 :       t_1_unlock( vote_stakes, 0 );
     863          32 :       vote_stakes->t_2_epoch[ t_2_idx ] = epoch;
     864          32 :     }
     865             : 
     866             :     /* Reset the t-2 states for the new fork and prepare the heap for
     867             :        insertion into the new t-1 set. */
     868          32 :     t_1_idx = t_1_acquire( vote_stakes );
     869          32 :     memset( child_state->states, 0, sizeof(child_state->states) );
     870             : 
     871          32 :     FD_TEST( vacc_heap_new( vacc_heap( vote_stakes ), FD_RUNTIME_MAX_VAT_VOTE_ACCOUNTS ) );
     872          32 :     vote_stakes->min_stake_wmark = 0UL;
     873          32 :   }
     874             : 
     875         659 :   ulong fork_id = 0UL;
     876         659 :   fork_id_set_bank_id( &fork_id, bank_id );
     877         659 :   fork_id_set_t_1_idx( &fork_id, t_1_idx );
     878         659 :   fork_id_set_epoch( &fork_id, (ushort)epoch );
     879         659 :   return fork_id;
     880         659 : }
     881             : 
     882             : void
     883             : fd_vote_stakes_update_state( fd_vote_stakes_t *  vote_stakes,
     884             :                              ulong               fork_id,
     885             :                              fd_pubkey_t const * pubkey,
     886             :                              ulong               last_vote_slot,
     887             :                              long                last_vote_ts,
     888        1580 :                              uchar               is_valid ) {
     889        1580 :   ulong        epoch_idx = (ulong)fork_id_epoch( fork_id ) % FD_VOTE_STAKES_EPOCH_CACHE_CNT;
     890        1580 :   vacc_t *     pool      = t_2_vacc_pool( vote_stakes, epoch_idx );
     891        1580 :   vacc_map_t * map       = t_2_vacc_map ( vote_stakes, epoch_idx );
     892             : 
     893        1580 :   vacc_t const * vacc = vacc_map_ele_query_const( map, pubkey, NULL, pool );
     894        1580 :   if( FD_UNLIKELY( !vacc ) ) return;
     895             : 
     896        1580 :   ulong           vacc_idx = vacc_pool_idx( pool, vacc );
     897        1580 :   vacc_states_t * states   = vacc_state_pool_ele( vacc_states_pool( vote_stakes ), fork_id_bank_id( fork_id ) );
     898        1580 :   states->states[ vacc_idx ].is_valid = (uchar)!!is_valid;
     899        1580 :   if( is_valid ) {
     900        1542 :     states->states[ vacc_idx ].last_vote_slot = last_vote_slot;
     901        1542 :     states->states[ vacc_idx ].last_vote_ts   = last_vote_ts;
     902        1542 :   }
     903        1580 : }
     904             : 
     905             : void
     906             : fd_vote_stakes_refresh( fd_vote_stakes_t * vote_stakes,
     907             :                         ulong              fork_id,
     908             :                         fd_accdb_t *        accdb,
     909         647 :                         fd_accdb_fork_id_t  accdb_fork_id ) {
     910         647 :   uchar __attribute__((aligned(FD_VOTE_STAKES_ITER_ALIGN))) iter_mem[ FD_VOTE_STAKES_ITER_FOOTPRINT ];
     911             : 
     912         647 :   for( fd_vote_stakes_iter_t * iter = fd_vote_stakes_iter_init( vote_stakes, fork_id, FD_VOTE_STAKES_ITER_T_2, iter_mem );
     913        1926 :        !fd_vote_stakes_iter_done( vote_stakes, fork_id, FD_VOTE_STAKES_ITER_T_2, iter );
     914        1279 :        fd_vote_stakes_iter_next( vote_stakes, fork_id, FD_VOTE_STAKES_ITER_T_2, iter ) ) {
     915        1279 :     fd_vote_stakes_ele_t ele[1];
     916        1279 :     fd_vote_stakes_iter_ele( vote_stakes, fork_id, FD_VOTE_STAKES_ITER_T_2, iter, ele );
     917        1279 :     fd_pubkey_t const * pubkey = &ele->pubkey;
     918             : 
     919        1279 :     fd_acc_t acc = fd_accdb_read_one( accdb, accdb_fork_id, pubkey->uc );
     920        1279 :     if( FD_UNLIKELY( !acc.lamports || !fd_vsv_is_correct_size_owner_and_init( acc.owner, acc.data, acc.data_len ) ) ) {
     921          38 :       fd_accdb_unread_one( accdb, &acc );
     922          38 :       fd_vote_stakes_update_state( vote_stakes, fork_id, pubkey, 0UL, 0L, 0 );
     923          38 :       continue;
     924          38 :     }
     925             : 
     926        1241 :     fd_vote_block_timestamp_t last_vote;
     927        1241 :     FD_TEST( !fd_vote_account_last_timestamp( acc.data, acc.data_len, &last_vote ) );
     928        1241 :     fd_vote_stakes_update_state( vote_stakes, fork_id, pubkey, last_vote.slot, last_vote.timestamp, 1 );
     929        1241 :     fd_accdb_unread_one( accdb, &acc );
     930        1241 :   }
     931         647 : }
     932             : 
     933             : /* t_1_vacc_query_const looks up pubkey in the fork's t-1 set under
     934             :    the shared t-1 lock, which the caller must release with
     935             :    t_1_unlock( vote_stakes, 0 ). */
     936             : 
     937             : static vacc_t const *
     938             : t_1_vacc_query_const( fd_vote_stakes_t *  vote_stakes,
     939             :                       ulong               fork_id,
     940        1324 :                       fd_pubkey_t const * pubkey ) {
     941        1324 :   ulong cache_idx = t_1_lock( vote_stakes, fork_id_t_1_idx( fork_id ), 0 );
     942        1324 :   return vacc_map_ele_query_const( t_1_cache_map( vote_stakes, cache_idx ), pubkey, NULL, t_1_cache_pool( vote_stakes, cache_idx ) );
     943        1324 : }
     944             : 
     945             : /* t_n_vacc_query looks up pubkey in the fork's t-n set, n in 2..5,
     946             :    and sets *pool to the set's pool.  Returns NULL if the set is not
     947             :    resident or pubkey is not in it.  The query does not reorder the
     948             :    map, which other tiles may be iterating. */
     949             : static vacc_t const *
     950             : t_n_vacc_query( fd_vote_stakes_t const * vote_stakes,
     951             :                 ulong                    fork_id,
     952             :                 ulong                    n,
     953             :                 fd_pubkey_t const *      pubkey,
     954         495 :                 vacc_t const **          pool ) {
     955         495 :   ulong epoch = (ulong)fork_id_epoch( fork_id );
     956         495 :   ulong back  = n-2UL;
     957         495 :   if( FD_UNLIKELY( epoch<back ) ) return NULL;
     958         495 :   ulong epoch_idx = (epoch-back) % FD_VOTE_STAKES_EPOCH_CACHE_CNT;
     959         495 :   if( FD_UNLIKELY( vote_stakes->t_2_epoch[ epoch_idx ]!=epoch-back ) ) return NULL;
     960         356 :   *pool = t_2_vacc_pool( vote_stakes, epoch_idx );
     961         356 :   return vacc_map_ele_query_const( t_2_vacc_map( vote_stakes, epoch_idx ), pubkey, NULL, *pool );
     962         495 : }
     963             : 
     964             : /* vacc_ele copies vacc to ele_out.  vacc_ele_state adds the fork's
     965             :    vote state for vacc, which must be in the t-2 pool. */
     966             : 
     967             : static void
     968             : vacc_ele( vacc_t const *         vacc,
     969        3229 :           fd_vote_stakes_ele_t * ele_out ) {
     970        3229 :   *ele_out = (fd_vote_stakes_ele_t){
     971        3229 :     .pubkey                       = vacc->pubkey,
     972        3229 :     .node_account                 = vacc->node_account,
     973        3229 :     .stake                        = vacc->stake,
     974        3229 :     .commission                   = vacc->commission,
     975        3229 :     .block_revenue_commission_bps = vacc->block_revenue_commission_bps,
     976        3229 :     .pending_delegator_rewards    = vacc->pending_delegator_rewards,
     977        3229 :     .inflation_collector          = vacc->inflation_collector,
     978        3229 :     .block_collector              = vacc->block_collector,
     979        3229 :     .rank                         = vacc->rank,
     980        3229 :   };
     981        3229 :   memcpy( ele_out->bls_key,              vacc->bls_key,              FD_BLS_PUBKEY_COMPRESSED_SZ   );
     982        3229 :   memcpy( ele_out->bls_key_uncompressed, vacc->bls_key_uncompressed, FD_BLS_PUBKEY_UNCOMPRESSED_SZ );
     983        3229 : }
     984             : 
     985             : static void
     986             : vacc_ele_state( fd_vote_stakes_t const * vote_stakes,
     987             :                 ulong                    fork_id,
     988             :                 vacc_t const *           pool,
     989             :                 vacc_t const *           vacc,
     990        2801 :                 fd_vote_stakes_ele_t *   ele_out ) {
     991        2801 :   ulong                 vacc_idx = vacc_pool_idx( pool, vacc );
     992        2801 :   vacc_states_t const * states   = vacc_state_pool_ele_const( vacc_states_pool( vote_stakes ), fork_id_bank_id( fork_id ) );
     993        2801 :   ele_out->last_vote_slot = states->states[ vacc_idx ].last_vote_slot;
     994        2801 :   ele_out->last_vote_ts   = states->states[ vacc_idx ].last_vote_ts;
     995        2801 :   ele_out->is_valid       = states->states[ vacc_idx ].is_valid;
     996        2801 : }
     997             : 
     998             : int
     999             : fd_vote_stakes_query_t_1( fd_vote_stakes_t *     vote_stakes,
    1000             :                           ulong                  fork_id,
    1001             :                           fd_pubkey_t const *    pubkey,
    1002        1324 :                           fd_vote_stakes_ele_t * ele_out_opt ) {
    1003        1324 :   vacc_t const * vacc = t_1_vacc_query_const( vote_stakes, fork_id, pubkey );
    1004        1324 :   if( FD_LIKELY( vacc && ele_out_opt ) ) vacc_ele( vacc, ele_out_opt );
    1005        1324 :   t_1_unlock( vote_stakes, 0 );
    1006        1324 :   return !!vacc;
    1007        1324 : }
    1008             : 
    1009             : int
    1010             : fd_vote_stakes_query_t_2( fd_vote_stakes_t const * vote_stakes,
    1011             :                           ulong                    fork_id,
    1012             :                           fd_pubkey_t const *      pubkey,
    1013         252 :                           fd_vote_stakes_ele_t *   ele_out_opt ) {
    1014         252 :   vacc_t const * pool;
    1015         252 :   vacc_t const * vacc = t_n_vacc_query( vote_stakes, fork_id, 2UL, pubkey, &pool );
    1016         252 :   if( FD_UNLIKELY( !vacc ) ) return 0;
    1017         243 :   if( ele_out_opt ) {
    1018         243 :     vacc_ele( vacc, ele_out_opt );
    1019         243 :     vacc_ele_state( vote_stakes, fork_id, pool, vacc, ele_out_opt );
    1020         243 :   }
    1021         243 :   return 1;
    1022         252 : }
    1023             : 
    1024             : int
    1025             : fd_vote_stakes_query_t_3( fd_vote_stakes_t const * vote_stakes,
    1026             :                           ulong                    fork_id,
    1027             :                           fd_pubkey_t const *      pubkey,
    1028         243 :                           fd_vote_stakes_ele_t *   ele_out_opt ) {
    1029         243 :   vacc_t const * pool;
    1030         243 :   vacc_t const * vacc = t_n_vacc_query( vote_stakes, fork_id, 3UL, pubkey, &pool );
    1031         243 :   if( FD_UNLIKELY( !vacc ) ) return 0;
    1032          87 :   if( ele_out_opt ) vacc_ele( vacc, ele_out_opt );
    1033          87 :   return 1;
    1034         243 : }
    1035             : 
    1036             : ulong
    1037             : fd_vote_stakes_cnt_t_1( fd_vote_stakes_t * vote_stakes,
    1038           0 :                         ulong              fork_id ) {
    1039           0 :   ulong cache_idx = t_1_lock( vote_stakes, fork_id_t_1_idx( fork_id ), 0 );
    1040           0 :   ulong cnt       = vacc_pool_used( t_1_cache_pool( vote_stakes, cache_idx ) );
    1041           0 :   t_1_unlock( vote_stakes, 0 );
    1042           0 :   return cnt;
    1043           0 : }
    1044             : 
    1045             : ulong
    1046             : fd_vote_stakes_cnt_t_2( fd_vote_stakes_t const * vote_stakes,
    1047           0 :                         ulong                    fork_id ) {
    1048           0 :   return vacc_pool_used( t_2_vacc_pool( vote_stakes, (ulong)fork_id_epoch( fork_id ) % FD_VOTE_STAKES_EPOCH_CACHE_CNT ) );
    1049           0 : }
    1050             : 
    1051             : ulong
    1052             : fd_vote_stakes_t_2_votes( fd_vote_stakes_t const * vote_stakes,
    1053             :                           ulong                    fork_id,
    1054             :                           ulong *                  stake,
    1055             :                           ulong *                  last_vote_slot,
    1056         614 :                           long *                   last_vote_ts ) {
    1057         614 :   ulong epoch     = (ulong)fork_id_epoch( fork_id );
    1058         614 :   ulong epoch_idx = epoch % FD_VOTE_STAKES_EPOCH_CACHE_CNT;
    1059         614 :   if( FD_UNLIKELY( vote_stakes->t_2_epoch[ epoch_idx ]!=epoch ) ) return 0UL;
    1060             : 
    1061             :   /* t-2 pools are only ever reset and refilled, so [0,used) is dense. */
    1062         614 :   vacc_t const *        pool   = t_2_vacc_pool( vote_stakes, epoch_idx );
    1063         614 :   vacc_states_t const * states = vacc_state_pool_ele_const( vacc_states_pool( vote_stakes ), fork_id_bank_id( fork_id ) );
    1064         614 :   ulong                 used   = vacc_pool_used( pool );
    1065         614 :   ulong                 cnt    = 0UL;
    1066        1695 :   for( ulong i=0UL; i<used; i++ ) {
    1067        1081 :     if( !states->states[ i ].is_valid ) continue;
    1068        1044 :     stake         [ cnt ] = pool[ i ].stake;
    1069        1044 :     last_vote_slot[ cnt ] = states->states[ i ].last_vote_slot;
    1070        1044 :     last_vote_ts  [ cnt ] = states->states[ i ].last_vote_ts;
    1071        1044 :     cnt++;
    1072        1044 :   }
    1073         614 :   return cnt;
    1074         614 : }
    1075             : 
    1076             : ulong
    1077             : fd_vote_stakes_total_stake( fd_vote_stakes_t const * vote_stakes,
    1078           0 :                             ulong                    epoch ) {
    1079           0 :   ulong idx = epoch % FD_VOTE_STAKES_EPOCH_CACHE_CNT;
    1080           0 :   if( FD_UNLIKELY( vote_stakes->t_2_epoch[ idx ]!=epoch ) ) return 0UL;
    1081             : 
    1082           0 :   vacc_t *     pool = t_2_vacc_pool( vote_stakes, idx );
    1083           0 :   vacc_map_t * map  = t_2_vacc_map ( vote_stakes, idx );
    1084             : 
    1085           0 :   ulong total = 0UL;
    1086           0 :   for( vacc_map_iter_t iter = vacc_map_iter_init( map, pool );
    1087           0 :        !vacc_map_iter_done( iter, map, pool );
    1088           0 :        iter = vacc_map_iter_next( iter, map, pool ) ) {
    1089           0 :     total += vacc_map_iter_ele_const( iter, map, pool )->stake;
    1090           0 :   }
    1091           0 :   return total;
    1092           0 : }
    1093             : 
    1094             : FD_STATIC_ASSERT( FD_VOTE_STAKES_ITER_FOOTPRINT == sizeof(vacc_map_iter_t), iter_footprint );
    1095             : FD_STATIC_ASSERT( FD_VOTE_STAKES_ITER_ALIGN == alignof(vacc_map_iter_t), iter_align );
    1096             : 
    1097             : fd_vote_stakes_iter_t *
    1098             : fd_vote_stakes_iter_init( fd_vote_stakes_t * vote_stakes,
    1099             :                           ulong              fork_id,
    1100             :                           int                iter_kind,
    1101        1334 :                           uchar              iter_mem[ static FD_VOTE_STAKES_ITER_FOOTPRINT ] ) {
    1102        1334 :   vacc_map_iter_t iter = {0};
    1103        1334 :   if( FD_LIKELY( iter_kind==FD_VOTE_STAKES_ITER_T_1 ) ) {
    1104          36 :     ulong cache_idx = t_1_lock( vote_stakes, fork_id_t_1_idx( fork_id ), 0 );
    1105          36 :     iter = vacc_map_iter_init( t_1_cache_map( vote_stakes, cache_idx ), t_1_cache_pool( vote_stakes, cache_idx ) );
    1106          36 :     t_1_unlock( vote_stakes, 0 );
    1107        1298 :   } else {
    1108        1298 :     FD_TEST( iter_kind>=FD_VOTE_STAKES_ITER_T_2 && iter_kind<=FD_VOTE_STAKES_ITER_T_5 );
    1109        1298 :     ulong fork_epoch = (ulong)fork_id_epoch( fork_id );
    1110        1298 :     ulong back       = (ulong)(iter_kind-FD_VOTE_STAKES_ITER_T_2);
    1111        1298 :     if( FD_LIKELY( fork_epoch>=back ) ) {
    1112        1298 :       ulong epoch     = fork_epoch-back;
    1113        1298 :       ulong epoch_idx = epoch % FD_VOTE_STAKES_EPOCH_CACHE_CNT;
    1114        1298 :       if( FD_LIKELY( vote_stakes->t_2_epoch[ epoch_idx ]==epoch ) ) {
    1115        1298 :         vacc_t *     pool = t_2_vacc_pool( vote_stakes, epoch_idx );
    1116        1298 :         vacc_map_t * map  = t_2_vacc_map ( vote_stakes, epoch_idx );
    1117        1298 :         iter = vacc_map_iter_init( map, pool );
    1118        1298 :       }
    1119        1298 :     }
    1120        1298 :   }
    1121        1334 :   memcpy( iter_mem, &iter, sizeof(iter) );
    1122        1334 :   return (fd_vote_stakes_iter_t *)iter_mem;
    1123        1334 : }
    1124             : 
    1125             : int
    1126             : fd_vote_stakes_iter_done( fd_vote_stakes_t *      vote_stakes,
    1127             :                           ulong                   fork_id,
    1128             :                           int                     iter_kind,
    1129        4055 :                           fd_vote_stakes_iter_t * iter ) {
    1130        4055 :   if( FD_LIKELY( iter_kind==FD_VOTE_STAKES_ITER_T_1 ) ) {
    1131         141 :     ulong cache_idx = t_1_lock( vote_stakes, fork_id_t_1_idx( fork_id ), 0 );
    1132         141 :     int   done      = vacc_map_iter_done( *(vacc_map_iter_t *)iter, t_1_cache_map( vote_stakes, cache_idx ), t_1_cache_pool( vote_stakes, cache_idx ) );
    1133         141 :     t_1_unlock( vote_stakes, 0 );
    1134         141 :     return done;
    1135         141 :   }
    1136             : 
    1137        3914 :   FD_TEST( iter_kind>=FD_VOTE_STAKES_ITER_T_2 && iter_kind<=FD_VOTE_STAKES_ITER_T_5 );
    1138        3914 :   ulong fork_epoch = (ulong)fork_id_epoch( fork_id );
    1139        3914 :   ulong back       = (ulong)(iter_kind-FD_VOTE_STAKES_ITER_T_2);
    1140        3914 :   if( FD_UNLIKELY( fork_epoch<back ) ) return 1;
    1141             : 
    1142        3914 :   ulong epoch     = fork_epoch-back;
    1143        3914 :   ulong epoch_idx = epoch % FD_VOTE_STAKES_EPOCH_CACHE_CNT;
    1144        3914 :   if( FD_UNLIKELY( vote_stakes->t_2_epoch[ epoch_idx ]!=epoch ) ) return 1;
    1145             : 
    1146        3914 :   vacc_t *     pool = t_2_vacc_pool( vote_stakes, epoch_idx );
    1147        3914 :   vacc_map_t * map  = t_2_vacc_map ( vote_stakes, epoch_idx );
    1148        3914 :   return vacc_map_iter_done( *(vacc_map_iter_t *)iter, map, pool );
    1149        3914 : }
    1150             : 
    1151             : static ulong
    1152             : iter_set( fd_vote_stakes_t * vote_stakes,
    1153             :           ulong              fork_id,
    1154             :           int                iter_kind,
    1155             :           vacc_t **          pool,
    1156        5444 :           vacc_map_t **      map ) {
    1157        5444 :   if( FD_LIKELY( iter_kind==FD_VOTE_STAKES_ITER_T_1 ) ) {
    1158         210 :     ulong cache_idx = t_1_lock( vote_stakes, fork_id_t_1_idx( fork_id ), 0 );
    1159         210 :     *pool = t_1_cache_pool( vote_stakes, cache_idx );
    1160         210 :     *map  = t_1_cache_map ( vote_stakes, cache_idx );
    1161         210 :     return cache_idx;
    1162         210 :   }
    1163        5234 :   FD_TEST( iter_kind>=FD_VOTE_STAKES_ITER_T_2 && iter_kind<=FD_VOTE_STAKES_ITER_T_5 );
    1164        5234 :   ulong epoch_idx = ((ulong)fork_id_epoch( fork_id )-(ulong)(iter_kind-FD_VOTE_STAKES_ITER_T_2)) % FD_VOTE_STAKES_EPOCH_CACHE_CNT;
    1165        5234 :   *pool = t_2_vacc_pool( vote_stakes, epoch_idx );
    1166        5234 :   *map  = t_2_vacc_map ( vote_stakes, epoch_idx );
    1167        5234 :   return ULONG_MAX;
    1168        5234 : }
    1169             : 
    1170             : void
    1171             : fd_vote_stakes_iter_next( fd_vote_stakes_t *      vote_stakes,
    1172             :                           ulong                   fork_id,
    1173             :                           int                     iter_kind,
    1174        2722 :                           fd_vote_stakes_iter_t * iter ) {
    1175        2722 :   vacc_t *     pool;
    1176        2722 :   vacc_map_t * map;
    1177        2722 :   ulong        cache_idx = iter_set( vote_stakes, fork_id, iter_kind, &pool, &map );
    1178        2722 :   *(vacc_map_iter_t *)iter = vacc_map_iter_next( *(vacc_map_iter_t *)iter, map, pool );
    1179        2722 :   if( cache_idx!=ULONG_MAX ) t_1_unlock( vote_stakes, 0 );
    1180        2722 : }
    1181             : 
    1182             : void
    1183             : fd_vote_stakes_iter_ele( fd_vote_stakes_t *      vote_stakes,
    1184             :                          ulong                   fork_id,
    1185             :                          int                     iter_kind,
    1186             :                          fd_vote_stakes_iter_t * iter,
    1187        2722 :                          fd_vote_stakes_ele_t *  ele_out ) {
    1188        2722 :   vacc_t *       pool;
    1189        2722 :   vacc_map_t *   map;
    1190        2722 :   ulong          cache_idx = iter_set( vote_stakes, fork_id, iter_kind, &pool, &map );
    1191        2722 :   vacc_t const * vacc      = vacc_map_iter_ele_const( *(vacc_map_iter_t *)iter, map, pool );
    1192        2722 :   vacc_ele( vacc, ele_out );
    1193        2722 :   if( iter_kind==FD_VOTE_STAKES_ITER_T_2 ) vacc_ele_state( vote_stakes, fork_id, pool, vacc, ele_out );
    1194        2722 :   if( cache_idx!=ULONG_MAX ) t_1_unlock( vote_stakes, 0 );
    1195        2722 : }

Generated by: LCOV version 1.14