LCOV - code coverage report
Current view: top level - choreo/votes - fd_votes.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 228 268 85.1 %
Date: 2026-09-17 04:28:31 Functions: 10 12 83.3 %

          Line data    Source code
       1             : #include "fd_votes.h"
       2             : 
       3             : /* fd_votes tracks blks, vtrs, and slots.
       4             : 
       5             :    blk_pool / blk_map (capacity blk_max = slot_max * vtr_max):
       6             : 
       7             :    Each blk tracks the aggregate stake voted for a particular block_id.
       8             : 
       9             :    vtr_pool / vtr_map / vtr_dlist / vtr_set (capacity vtr_max):
      10             : 
      11             :    Each vtr corresponds to a vote account address and has a bit position
      12             :    in the slot vtrs bitset.  vtr entries are explicitly managed by
      13             :    fd_votes_update_voters when the epoch stake set changes.
      14             :    dlist of all active voters, used for mark-sweep in
      15             :    fd_votes_update_voters.
      16             : 
      17             :    slot_pool / slot_map / blk_dlist (capacity slot_max):
      18             : 
      19             :    Each slot corresponds to a slot and tracks which voters have voted
      20             :    for that slot and all the blks that are associated for that slot.
      21             :    Each slot also tracks all the blks associated with that slot in
      22             :    blk_dlist.
      23             : 
      24             :             slot_map                           vtr_map
      25             :      map[0] +--------------------+     map[0] +--------------------+
      26             :             | (slot_t) {         |            | (vtr_t) {          |
      27             :             |   .slot = 100,     |            |   .vote_acc = X,   |
      28             :             |   .vtrs = ...,     |            |   .bit   = 0,      |
      29             :             |   .blk_dlist = ... |            | }                  |
      30             :             | }                  |            |                    |
      31             :      map[1] +--------------------+     map[1] +--------------------+
      32             :             | (slot_t) {         |            | (vtr_t) {          |
      33             :             |   .slot = 101,     |            |   .vote_acc = Y,   |
      34             :             |   .vtrs = ...,     |            |   .bit   = 1,      |
      35             :             |   .blk_dlist = +   |            | }                  |
      36             :             | }              |   |            |                    |
      37             :             +----------------|---+            +--------------------+
      38             :                              |
      39             :                              V
      40             :                              blk_dlist
      41             :                              +------------------+------------------+
      42             :                              | (blk_t) {        | (blk_t) {        |
      43             :                              |   .block_id = A, |   .block_id = B, |
      44             :                              |   .stake = 10,   |   .stake = 51,   |
      45             :                              |   ...            |   ...            |
      46             :                              | }                | }                |
      47             :                              +------------------+------------------+
      48             : 
      49             :    When a vote is counted, the voter's bit is set in the slot's vtrs
      50             :    bitset.  If the voter already voted for this slot (bit already set),
      51             :    the vote is ignored.  The vote's stake is added to both the slot's
      52             :    aggregate stake and the blk's stake.  blk entries are also in the
      53             :    global blk_map for O(1) lookup by block_id. */
      54             : 
      55             : typedef fd_votes_blk_t blk_t;
      56             : 
      57             : #define SET_NAME slot_vtrs
      58             : #include "../../util/tmpl/fd_set_dynamic.c"
      59             : 
      60             : #define POOL_NAME blk_pool
      61             : #define POOL_LAZY 1
      62          24 : #define POOL_T    blk_t
      63             : #define POOL_IDX_T uint
      64             : #include "../../util/tmpl/fd_pool.c"
      65             : 
      66             : #define MAP_NAME                           blk_map
      67         102 : #define MAP_ELE_T                          blk_t
      68             : #define MAP_KEY_T                          fd_votes_blk_key_t
      69         141 : #define MAP_KEY                            key
      70         522 : #define MAP_PREV                           map.prev
      71        5088 : #define MAP_NEXT                           map.next
      72        1539 : #define MAP_IDX_T                          uint
      73        5022 : #define MAP_KEY_EQ(k0,k1)                  ((k0)->slot==(k1)->slot && !memcmp((k0)->block_id.key,(k1)->block_id.key,32UL))
      74         837 : #define MAP_KEY_HASH(key,seed)             ((ulong)((key)->block_id.ul[1]^(key)->slot^(seed)))
      75             : #define MAP_OPTIMIZE_RANDOM_ACCESS_REMOVAL 1
      76             : #include "../../util/tmpl/fd_map_chain.c"
      77             : 
      78             : #define DLIST_NAME  blk_dlist
      79             : #define DLIST_ELE_T blk_t
      80         135 : #define DLIST_PREV  dlist.prev
      81         237 : #define DLIST_NEXT  dlist.next
      82             : #define DLIST_IDX_T uint
      83             : #include "../../util/tmpl/fd_dlist.c"
      84             : 
      85             : struct vtr {
      86             :   fd_pubkey_t vote_acc; /* vtr_map key */
      87             :   uint        next;     /* pool next */
      88             :   struct {
      89             :     uint prev;
      90             :     uint next;
      91             :   } map;
      92             :   struct {
      93             :     uint prev;
      94             :     uint next;
      95             :   } dlist;
      96             :   ulong bit;
      97             : };
      98             : typedef struct vtr vtr_t;
      99             : 
     100             : #define POOL_NAME vtr_pool
     101             : #define POOL_LAZY 1
     102          24 : #define POOL_T    vtr_t
     103             : #define POOL_IDX_T uint
     104             : #include "../../util/tmpl/fd_pool.c"
     105             : 
     106             : #define MAP_NAME                           vtr_map
     107           9 : #define MAP_ELE_T                          vtr_t
     108             : #define MAP_KEY_T                          fd_pubkey_t
     109         261 : #define MAP_KEY                            vote_acc
     110        1491 : #define MAP_PREV                           map.prev
     111       16968 : #define MAP_NEXT                           map.next
     112        1095 : #define MAP_IDX_T                          uint
     113       16221 : #define MAP_KEY_EQ(k0,k1)                  (!memcmp((k0)->key,(k1)->key,sizeof(fd_pubkey_t)))
     114         675 : #define MAP_KEY_HASH(key,seed)             ((ulong)((key)->ul[1]^(seed)))
     115             : #define MAP_OPTIMIZE_RANDOM_ACCESS_REMOVAL 1
     116             : #include "../../util/tmpl/fd_map_chain.c"
     117             : 
     118             : #define DLIST_NAME  vtr_dlist
     119             : #define DLIST_ELE_T vtr_t
     120         279 : #define DLIST_PREV  dlist.prev
     121         312 : #define DLIST_NEXT  dlist.next
     122             : #define DLIST_IDX_T uint
     123             : #include "../../util/tmpl/fd_dlist.c"
     124             : 
     125             : struct slot {
     126             :   ulong slot; /* map key, vote slot */
     127             :   uint next; /* pool next */
     128             :   struct {
     129             :     uint prev;
     130             :     uint next;
     131             :   } map;
     132             :   struct {
     133             :     uint prev;
     134             :     uint next;
     135             :   } dlist;
     136             :   blk_dlist_t * blks;
     137             :   ulong         blk_cnt; /* number of distinct block ids for this slot */
     138             :   slot_vtrs_t * vtrs;    /* who has voted for this slot, curr epoch */
     139             : };
     140             : typedef struct slot slot_t;
     141             : 
     142             : #define POOL_NAME slot_pool
     143             : #define POOL_LAZY 1
     144          36 : #define POOL_T    slot_t
     145             : #define POOL_IDX_T uint
     146             : #include "../../util/tmpl/fd_pool.c"
     147             : 
     148             : #define MAP_NAME                           slot_map
     149           6 : #define MAP_ELE_T                          slot_t
     150             : #define MAP_KEY_T                          ulong
     151          42 : #define MAP_KEY                            slot
     152          45 : #define MAP_PREV                           map.prev
     153          51 : #define MAP_NEXT                           map.next
     154         786 : #define MAP_IDX_T                          uint
     155         336 : #define MAP_KEY_EQ(k0,k1)                  (*(k0)==*(k1))
     156         411 : #define MAP_KEY_HASH(key,seed)             ((*key)^(seed))
     157             : #define MAP_OPTIMIZE_RANDOM_ACCESS_REMOVAL 1
     158             : #include "../../util/tmpl/fd_map_chain.c"
     159             : 
     160             : #define DLIST_NAME  slot_dlist
     161             : #define DLIST_ELE_T slot_t
     162          42 : #define DLIST_PREV  dlist.prev
     163          57 : #define DLIST_NEXT  dlist.next
     164             : #define DLIST_IDX_T uint
     165             : #include "../../util/tmpl/fd_dlist.c"
     166             : 
     167             : struct __attribute__((aligned(128UL))) fd_votes {
     168             :   ulong          root;
     169             :   ulong          slot_max;
     170             :   ulong          vtr_max;
     171             :   ulong          blk_max;
     172             :   slot_t *       slot_pool;
     173             :   slot_map_t *   slot_map;
     174             :   slot_dlist_t * slot_dlist;
     175             :   blk_t *        blk_pool;
     176             :   blk_map_t *    blk_map;
     177             :   vtr_t *        vtr_pool;
     178             :   vtr_map_t *    vtr_map;
     179             :   vtr_dlist_t *  vtr_dlist;
     180             :   slot_vtrs_t *  vtr_set;
     181             : };
     182             : 
     183             : FD_FN_CONST static inline ulong
     184             : fd_votes_blk_max( ulong slot_max,
     185          39 :                   ulong vtr_max ) {
     186          39 :   if( FD_UNLIKELY( !slot_max || !vtr_max ) ) return 0UL;
     187          39 :   slot_max = fd_ulong_pow2_up( slot_max );
     188          39 :   vtr_max  = fd_ulong_pow2_up( vtr_max  );
     189          39 :   if( FD_UNLIKELY( !slot_max || !vtr_max || slot_max>UINT_MAX/vtr_max ) ) return 0UL;
     190          36 :   ulong blk_max = fd_ulong_pow2_up( slot_max*vtr_max );
     191          36 :   return fd_ulong_if( blk_max<=UINT_MAX, blk_max, 0UL );
     192          39 : }
     193             : 
     194             : ulong
     195         108 : fd_votes_align( void ) {
     196         108 :   return 128UL;
     197         108 : }
     198             : 
     199             : ulong
     200             : fd_votes_footprint( ulong slot_max,
     201          27 :                     ulong vtr_max ) {
     202             : 
     203          27 :   ulong blk_max = fd_votes_blk_max( slot_max, vtr_max );
     204          27 :   if( FD_UNLIKELY( !blk_max ) ) return 0UL;
     205          24 :   slot_max      = fd_ulong_pow2_up( slot_max );
     206          24 :   vtr_max       = fd_ulong_pow2_up( vtr_max );
     207             : 
     208          24 :   ulong l = FD_LAYOUT_INIT;
     209          24 :   l = FD_LAYOUT_APPEND( l, 128UL,              sizeof(fd_votes_t)                                          );
     210          24 :   l = FD_LAYOUT_APPEND( l, slot_pool_align(),  slot_pool_footprint( slot_max )                             );
     211          24 :   l = FD_LAYOUT_APPEND( l, slot_map_align(),   slot_map_footprint( slot_map_chain_cnt_est( slot_max ) )    );
     212          24 :   l = FD_LAYOUT_APPEND( l, slot_dlist_align(), slot_dlist_footprint()                                      );
     213          24 :   l = FD_LAYOUT_APPEND( l, blk_pool_align(),   blk_pool_footprint( blk_max )                               );
     214          24 :   l = FD_LAYOUT_APPEND( l, blk_map_align(),    blk_map_footprint( blk_map_chain_cnt_est( blk_max ) )       );
     215          24 :   l = FD_LAYOUT_APPEND( l, vtr_pool_align(),   vtr_pool_footprint( vtr_max )                               );
     216          24 :   l = FD_LAYOUT_APPEND( l, vtr_map_align(),    vtr_map_footprint( vtr_map_chain_cnt_est( vtr_max ) )       );
     217          24 :   l = FD_LAYOUT_APPEND( l, vtr_dlist_align(),  vtr_dlist_footprint()                                       );
     218          24 :   l = FD_LAYOUT_APPEND( l, slot_vtrs_align(),  slot_vtrs_footprint( vtr_max )                              );
     219         216 :   for( ulong i = 0UL; i < slot_max; i++ ) {
     220         192 :     l = FD_LAYOUT_APPEND( l, slot_vtrs_align(), slot_vtrs_footprint( vtr_max ) );
     221         192 :     l = FD_LAYOUT_APPEND( l, blk_dlist_align(), blk_dlist_footprint()          );
     222         192 :   }
     223          24 :   return FD_LAYOUT_FINI( l, fd_votes_align() );
     224          27 : }
     225             : 
     226             : void *
     227             : fd_votes_new( void * shmem,
     228             :               ulong  slot_max,
     229             :               ulong  vtr_max,
     230          12 :               ulong  seed ) {
     231             : 
     232          12 :   if( FD_UNLIKELY( !shmem ) ) {
     233           0 :     FD_LOG_WARNING(( "NULL mem" ));
     234           0 :     return NULL;
     235           0 :   }
     236             : 
     237          12 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)shmem, fd_votes_align() ) ) ) {
     238           0 :     FD_LOG_WARNING(( "misaligned mem" ));
     239           0 :     return NULL;
     240           0 :   }
     241             : 
     242          12 :   ulong footprint = fd_votes_footprint( slot_max, vtr_max );
     243          12 :   if( FD_UNLIKELY( !footprint ) ) {
     244           0 :     FD_LOG_WARNING(( "bad slot_max (%lu) or vtr_max (%lu)", slot_max, vtr_max ));
     245           0 :     return NULL;
     246           0 :   }
     247             : 
     248          12 :   slot_max      = fd_ulong_pow2_up( slot_max );
     249          12 :   vtr_max       = fd_ulong_pow2_up( vtr_max );
     250          12 :   ulong blk_max = fd_votes_blk_max( slot_max, vtr_max );
     251             : 
     252          12 :   FD_SCRATCH_ALLOC_INIT( l, shmem );
     253          12 :   fd_votes_t * votes      = FD_SCRATCH_ALLOC_APPEND( l, 128UL,              sizeof(fd_votes_t)                                          );
     254          12 :   void *       slot_pool  = FD_SCRATCH_ALLOC_APPEND( l, slot_pool_align(),  slot_pool_footprint( slot_max )                             );
     255          12 :   void *       slot_map   = FD_SCRATCH_ALLOC_APPEND( l, slot_map_align(),   slot_map_footprint( slot_map_chain_cnt_est( slot_max ) )    );
     256          12 :   void *       slot_dlist = FD_SCRATCH_ALLOC_APPEND( l, slot_dlist_align(), slot_dlist_footprint()                                      );
     257          12 :   void *       blk_pool   = FD_SCRATCH_ALLOC_APPEND( l, blk_pool_align(),   blk_pool_footprint( blk_max )                               );
     258          12 :   void *       blk_map    = FD_SCRATCH_ALLOC_APPEND( l, blk_map_align(),    blk_map_footprint( blk_map_chain_cnt_est( blk_max ) )       );
     259          12 :   void *       vtr_pool   = FD_SCRATCH_ALLOC_APPEND( l, vtr_pool_align(),   vtr_pool_footprint( vtr_max )                               );
     260          12 :   void *       vtr_map    = FD_SCRATCH_ALLOC_APPEND( l, vtr_map_align(),    vtr_map_footprint( vtr_map_chain_cnt_est( vtr_max ) )       );
     261          12 :   void *       vtr_dlist  = FD_SCRATCH_ALLOC_APPEND( l, vtr_dlist_align(),  vtr_dlist_footprint()                                       );
     262          12 :   void *       vtr_set    = FD_SCRATCH_ALLOC_APPEND( l, slot_vtrs_align(),  slot_vtrs_footprint( vtr_max )                              );
     263             : 
     264          12 :   votes->root       = ULONG_MAX;
     265          12 :   votes->slot_max   = slot_max;
     266          12 :   votes->vtr_max    = vtr_max;
     267          12 :   votes->blk_max    = blk_max;
     268          12 :   votes->slot_pool  = slot_pool_new ( slot_pool, slot_max                                 );
     269          12 :   votes->slot_map   = slot_map_new  ( slot_map,  slot_map_chain_cnt_est( slot_max ), seed );
     270          12 :   votes->slot_dlist = slot_dlist_new( slot_dlist                                          );
     271          12 :   votes->blk_pool   = blk_pool_new  ( blk_pool,  blk_max                                  );
     272          12 :   votes->blk_map    = blk_map_new   ( blk_map,   blk_map_chain_cnt_est( blk_max ),   seed );
     273          12 :   votes->vtr_pool   = vtr_pool_new  ( vtr_pool,  vtr_max                                  );
     274          12 :   votes->vtr_map    = vtr_map_new   ( vtr_map,   vtr_map_chain_cnt_est( vtr_max ),   seed );
     275          12 :   votes->vtr_dlist  = vtr_dlist_new ( vtr_dlist                                           );
     276          12 :   votes->vtr_set    = slot_vtrs_new ( vtr_set,   vtr_max                                  );
     277             : 
     278             :   /* Pre-allocate a vtrs set and blk_dlist per slot pool position. */
     279             : 
     280          12 :   slot_t * slot_join = slot_pool_join( votes->slot_pool );
     281         108 :   for( ulong i = 0UL; i < slot_max; i++ ) {
     282          96 :     void * vtrs            = FD_SCRATCH_ALLOC_APPEND( l, slot_vtrs_align(), slot_vtrs_footprint( vtr_max ) );
     283          96 :     void * blk_dlist       = FD_SCRATCH_ALLOC_APPEND( l, blk_dlist_align(), blk_dlist_footprint()          );
     284          96 :     slot_join[i].vtrs      = slot_vtrs_new( vtrs, vtr_max );
     285          96 :     slot_join[i].blks      = blk_dlist_new( blk_dlist );
     286          96 :     slot_join[i].blk_cnt   = 0;
     287          96 :   }
     288          12 :   slot_pool_leave( slot_join );
     289             : 
     290          12 :   FD_TEST( FD_SCRATCH_ALLOC_FINI( l, fd_votes_align() ) == (ulong)shmem + footprint );
     291          12 :   return shmem;
     292          12 : }
     293             : 
     294             : fd_votes_t *
     295          12 : fd_votes_join( void * shvotes ) {
     296          12 :   fd_votes_t * votes = (fd_votes_t *)shvotes;
     297             : 
     298          12 :   if( FD_UNLIKELY( !votes ) ) {
     299           0 :     FD_LOG_WARNING(( "NULL votes" ));
     300           0 :     return NULL;
     301           0 :   }
     302             : 
     303          12 :   if( FD_UNLIKELY( !fd_ulong_is_aligned((ulong)votes, fd_votes_align() ) ) ) {
     304           0 :     FD_LOG_WARNING(( "misaligned votes" ));
     305           0 :     return NULL;
     306           0 :   }
     307             : 
     308          12 :   votes->slot_pool  = slot_pool_join ( votes->slot_pool  );
     309          12 :   votes->slot_map   = slot_map_join  ( votes->slot_map   );
     310          12 :   votes->slot_dlist = slot_dlist_join( votes->slot_dlist );
     311          12 :   votes->blk_pool   = blk_pool_join  ( votes->blk_pool   );
     312          12 :   votes->blk_map    = blk_map_join   ( votes->blk_map    );
     313          12 :   votes->vtr_pool   = vtr_pool_join  ( votes->vtr_pool   );
     314          12 :   votes->vtr_map    = vtr_map_join   ( votes->vtr_map    );
     315          12 :   votes->vtr_dlist  = vtr_dlist_join ( votes->vtr_dlist  );
     316          12 :   votes->vtr_set    = slot_vtrs_join ( votes->vtr_set    );
     317             : 
     318             :   /* Re-join vtrs sets and blk_dlists per slot pool position. */
     319             : 
     320         108 :   for( ulong i = 0UL; i < votes->slot_max; i++ ) {
     321          96 :     votes->slot_pool[i].vtrs = slot_vtrs_join( votes->slot_pool[i].vtrs );
     322          96 :     votes->slot_pool[i].blks = blk_dlist_join( votes->slot_pool[i].blks );
     323          96 :   }
     324             : 
     325          12 :   return votes;
     326          12 : }
     327             : 
     328             : void *
     329          12 : fd_votes_leave( fd_votes_t const * votes ) {
     330             : 
     331          12 :   if( FD_UNLIKELY( !votes ) ) {
     332           0 :     FD_LOG_WARNING(( "NULL votes" ));
     333           0 :     return NULL;
     334           0 :   }
     335             : 
     336          12 :   return (void *)votes;
     337          12 : }
     338             : 
     339             : void *
     340          12 : fd_votes_delete( void * votes ) {
     341             : 
     342          12 :   if( FD_UNLIKELY( !votes ) ) {
     343           0 :     FD_LOG_WARNING(( "NULL votes" ));
     344           0 :     return NULL;
     345           0 :   }
     346             : 
     347          12 :   if( FD_UNLIKELY( !fd_ulong_is_aligned((ulong)votes, fd_votes_align() ) ) ) {
     348           0 :     FD_LOG_WARNING(( "misaligned votes" ));
     349           0 :     return NULL;
     350           0 :   }
     351             : 
     352          12 :   return votes;
     353          12 : }
     354             : 
     355             : int
     356             : fd_votes_count_vote( fd_votes_t *        votes,
     357             :                      fd_pubkey_t const * vote_acc,
     358             :                      ulong               stake,
     359             :                      ulong               vote_slot,
     360         390 :                      fd_hash_t const *   vote_block_id ) {
     361             : 
     362         390 :   if( FD_UNLIKELY( vote_slot >= votes->root + votes->slot_max ) ) return FD_VOTES_ERR_VOTE_TOO_NEW;
     363             : 
     364         363 :   vtr_t * vtr = vtr_map_ele_query( votes->vtr_map, vote_acc, NULL, votes->vtr_pool );
     365         363 :   if( FD_UNLIKELY( !vtr ) ) return FD_VOTES_ERR_UNKNOWN_VTR;
     366             : 
     367             :   /* Check we haven't already counted the voter's stake for this slot.
     368             :      If a voter votes for multiple block ids for the same slot, we only
     369             :      count their first one.  Honest voters never vote more than once for
     370             :      the same slot so the percentage of stake doing this should be small
     371             :      as only malicious voters would equivocate votes this way. */
     372             : 
     373         357 :   slot_t * slot = slot_map_ele_query( votes->slot_map, &vote_slot, NULL, votes->slot_pool );
     374         357 :   if( FD_UNLIKELY( !slot ) ) {
     375          36 :     slot          = slot_pool_ele_acquire( votes->slot_pool );
     376          36 :     slot->slot    = vote_slot;
     377          36 :     slot->blk_cnt = 0;
     378          36 :     slot_vtrs_null( slot->vtrs );
     379          36 :     slot_map_ele_insert( votes->slot_map, slot, votes->slot_pool );
     380          36 :     slot_dlist_ele_push_tail( votes->slot_dlist, slot, votes->slot_pool );
     381          36 :   }
     382         357 :   if( FD_UNLIKELY( slot_vtrs_test( slot->vtrs, vtr->bit ) ) ) return FD_VOTES_ERR_ALREADY_VOTED;
     383         258 :   slot_vtrs_insert( slot->vtrs, vtr->bit );
     384             : 
     385         258 :   fd_votes_blk_key_t blk_key = { .slot = vote_slot, .block_id = *vote_block_id };
     386         258 :   blk_t * blk = blk_map_ele_query( votes->blk_map, &blk_key, NULL, votes->blk_pool );
     387         258 :   if( FD_UNLIKELY( !blk ) ) {
     388         135 :     blk        = blk_pool_ele_acquire( votes->blk_pool );
     389         135 :     blk->key   = blk_key;
     390         135 :     blk->stake = 0;
     391         135 :     blk->flags = 0;
     392         135 :     blk_map_ele_insert( votes->blk_map, blk, votes->blk_pool );
     393         135 :     blk_dlist_ele_push_tail( slot->blks, blk, votes->blk_pool );
     394         135 :     slot->blk_cnt++;
     395         135 :   }
     396         258 :   blk->stake += stake;
     397         258 :   return FD_VOTES_SUCCESS;
     398         357 : }
     399             : 
     400             : fd_votes_blk_t *
     401             : fd_votes_query( fd_votes_t *      votes,
     402             :                 ulong             slot,
     403           0 :                 fd_hash_t const * block_id ) {
     404             : 
     405           0 :   if( FD_LIKELY( block_id ) ) {
     406           0 :     fd_votes_blk_key_t key = { .slot = slot, .block_id = *block_id };
     407           0 :     return blk_map_ele_query( votes->blk_map, &key, NULL, votes->blk_pool );
     408           0 :   }
     409             : 
     410             :   /* NULL block_id: search all block_ids for this slot, return the one
     411             :      with the highest forward confirmation level. */
     412             : 
     413           0 :   slot_t * votes_slot = slot_map_ele_query( votes->slot_map, &slot, NULL, votes->slot_pool );
     414           0 :   if( FD_UNLIKELY( !votes_slot ) ) return NULL;
     415             : 
     416           0 :   blk_t * best = NULL;
     417           0 :   for( blk_dlist_iter_t iter = blk_dlist_iter_fwd_init( votes_slot->blks, votes->blk_pool );
     418           0 :        !blk_dlist_iter_done( iter, votes_slot->blks, votes->blk_pool );
     419           0 :        iter = blk_dlist_iter_fwd_next( iter, votes_slot->blks, votes->blk_pool ) ) {
     420           0 :     blk_t * blk = blk_dlist_iter_ele( iter, votes_slot->blks, votes->blk_pool );
     421           0 :     if( FD_UNLIKELY( ( blk->flags >> 4 ) > ( best ? best->flags >> 4 : 0 ) ) ) best = blk;
     422           0 :   }
     423           0 :   return best;
     424           0 : }
     425             : 
     426             : void
     427             : fd_votes_publish( fd_votes_t * votes,
     428          18 :                   ulong        root ) {
     429          18 :   if( FD_UNLIKELY( votes->root==ULONG_MAX ) ) { votes->root = root; return; }
     430          18 :   for( ulong slot = votes->root; slot < root; slot++ ) {
     431          12 :     slot_t * votes_slot = slot_map_ele_query( votes->slot_map, &slot, NULL, votes->slot_pool );
     432          12 :     if( FD_LIKELY( votes_slot ) ) {
     433         108 :       while( FD_LIKELY( !blk_dlist_is_empty( votes_slot->blks, votes->blk_pool ) ) ) {
     434         102 :         blk_t * blk = blk_dlist_ele_pop_head( votes_slot->blks, votes->blk_pool );
     435         102 :         blk_map_ele_remove_fast( votes->blk_map, blk, votes->blk_pool );
     436         102 :         blk_pool_ele_release( votes->blk_pool, blk );
     437         102 :       }
     438           6 :       slot_dlist_ele_remove( votes->slot_dlist, votes_slot, votes->slot_pool );
     439           6 :       slot_map_ele_remove_fast( votes->slot_map, votes_slot, votes->slot_pool );
     440           6 :       slot_pool_ele_release( votes->slot_pool, votes_slot );
     441           6 :     }
     442          12 :   }
     443           6 :   votes->root = root;
     444           6 : }
     445             : 
     446             : void
     447             : fd_votes_update_voters( fd_votes_t *        votes,
     448             :                         fd_pubkey_t const * vote_accs,
     449          12 :                         ulong               cnt ) {
     450             : 
     451             :   /* Mark all existing voters for removal. */
     452             : 
     453          12 :   for( vtr_dlist_iter_t iter = vtr_dlist_iter_fwd_init( votes->vtr_dlist, votes->vtr_pool );
     454          30 :        !vtr_dlist_iter_done( iter, votes->vtr_dlist, votes->vtr_pool );
     455          18 :        iter = vtr_dlist_iter_fwd_next( iter, votes->vtr_dlist, votes->vtr_pool ) ) {
     456          18 :     votes->vtr_pool[iter].next = 1; /* mark for removal */
     457          18 :   }
     458             : 
     459             :   /* First pass: unmark kept voters from being released.  Build a set
     460             :      of kept old bit positions.  Existing voters keep their old bit
     461             :      positions (no compaction). */
     462             : 
     463          12 :   slot_vtrs_null( votes->vtr_set );
     464             : 
     465          30 :   for( ulong i=0UL; i<cnt; i++ ) {
     466          18 :     fd_pubkey_t const * vote_acc = &vote_accs[i];
     467          18 :     vtr_t * vtr = vtr_map_ele_query( votes->vtr_map, vote_acc, NULL, votes->vtr_pool );
     468          18 :     if( FD_LIKELY( vtr ) ) {
     469           9 :       vtr_dlist_ele_remove( votes->vtr_dlist, vtr, votes->vtr_pool );
     470           9 :       slot_vtrs_insert( votes->vtr_set, vtr->bit );
     471           9 :       vtr->next  = 0; /* unmark for removal */
     472           9 :       vtr_dlist_ele_push_tail( votes->vtr_dlist, vtr, votes->vtr_pool );
     473           9 :     }
     474          18 :   }
     475             : 
     476             :   /* Pop and release marked voters until the first unmarked voter. */
     477             : 
     478          21 :   while( FD_LIKELY( !vtr_dlist_is_empty( votes->vtr_dlist, votes->vtr_pool ) ) ) {
     479          15 :     vtr_t * vtr = vtr_dlist_ele_pop_head( votes->vtr_dlist, votes->vtr_pool );
     480          15 :     if( FD_UNLIKELY( !vtr->next ) ) { /* can short-circuit since all the existing and new voters were appended */
     481           6 :       vtr_dlist_ele_push_tail( votes->vtr_dlist, vtr, votes->vtr_pool );
     482           6 :       break;
     483           6 :     }
     484           9 :     vtr_map_ele_remove_fast( votes->vtr_map, vtr, votes->vtr_pool );
     485           9 :     vtr_pool_ele_release( votes->vtr_pool, vtr );
     486           9 :   }
     487             : 
     488             :   /* Clear removed voters' bits from all existing slots' vtrs by
     489             :      intersecting with the kept set. */
     490             : 
     491          12 :   for( slot_dlist_iter_t iter = slot_dlist_iter_fwd_init( votes->slot_dlist, votes->slot_pool );
     492          27 :        !slot_dlist_iter_done( iter, votes->slot_dlist, votes->slot_pool );
     493          15 :        iter = slot_dlist_iter_fwd_next( iter, votes->slot_dlist, votes->slot_pool ) ) {
     494          15 :     slot_t * votes_slot = &votes->slot_pool[iter];
     495          15 :     slot_vtrs_intersect( votes_slot->vtrs, votes_slot->vtrs, votes->vtr_set );
     496          15 :   }
     497             : 
     498             :   /* Second pass: acquire and insert new voters, assigning bit positions
     499             :      from freed positions. */
     500             : 
     501          12 :   ulong free_bit = 0;
     502          30 :   for( ulong i=0UL; i<cnt; i++ ) {
     503          18 :     fd_pubkey_t const * vote_acc = &vote_accs[i];
     504          18 :     if( FD_LIKELY( vtr_map_ele_query( votes->vtr_map, vote_acc, NULL, votes->vtr_pool ) ) ) continue;
     505           9 :     vtr_t * vtr   = vtr_pool_ele_acquire( votes->vtr_pool );
     506           9 :     vtr->vote_acc = *vote_acc;
     507           9 :     vtr->next     = 0;
     508           9 :     while( slot_vtrs_test( votes->vtr_set, free_bit ) ) free_bit++;
     509           9 :     vtr->bit = free_bit;
     510           9 :     slot_vtrs_insert( votes->vtr_set, free_bit );
     511           9 :     free_bit++;
     512           9 :     vtr_map_ele_insert( votes->vtr_map, vtr, votes->vtr_pool );
     513           9 :     vtr_dlist_ele_push_tail( votes->vtr_dlist, vtr, votes->vtr_pool );
     514           9 :   }
     515          12 : }

Generated by: LCOV version 1.14