LCOV - code coverage report
Current view: top level - choreo/votes - fd_votes.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 164 251 65.3 %
Date: 2026-03-31 06:22:16 Functions: 8 10 80.0 %

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

Generated by: LCOV version 1.14