LCOV - code coverage report
Current view: top level - flamenco/stakes - fd_vote_stakes.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 524 681 76.9 %
Date: 2026-11-11 12:40:43 Functions: 44 49 89.8 %

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

Generated by: LCOV version 1.14