LCOV - code coverage report
Current view: top level - choreo/votes - fd_votes.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 217 257 84.4 %
Date: 2026-06-29 05:51:35 Functions: 9 10 90.0 %

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

Generated by: LCOV version 1.14