LCOV - code coverage report
Current view: top level - choreo/hfork - fd_hfork.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 282 306 92.2 %
Date: 2026-06-29 05:51:35 Functions: 12 15 80.0 %

          Line data    Source code
       1             : #include "fd_hfork.h"
       2             : 
       3             : /* fd_hfork maintains four pools and four maps:
       4             : 
       5             :    bhm_pool (capacity max = per_vtr_max * vtr_max): pool of bhm_t
       6             :             elements, where bhm stands for "bank hash matcher".  Each
       7             :             bhm tracks the aggregate stake and vote count for a
       8             :             particular (block_id, bank_hash) pair.
       9             : 
      10             :    bhm_map  (capacity max): maps bhm_key_t (block_id, bank_hash) ->
      11             :             bhm_t for O(1) lookup of a specific bank hash matcher.
      12             : 
      13             :    blk_pool (capacity max): pool of blk_t elements.  Each blk_t stores
      14             :             per-block metadata (our_bank_hash, replayed, dead, matched,
      15             :             mismatched, checked) and owns a bhm_dlist of all bhm entries sharing the
      16             :             same block_id.
      17             : 
      18             :    blk_map  (capacity max): maps block_id -> blk_t for O(1) lookup of
      19             :             per-block metadata.
      20             : 
      21             :    vte_pool (capacity max): pool of vte_t elements.  Each vte
      22             :             records a single vote (block_id, bank_hash, slot, stake)
      23             :             from a voter, stored in that voter's vte_dlist.  When a
      24             :             voter's vte_dlist reaches per_vtr_max entries, the oldest vte
      25             :             is popped and its stake contribution is subtracted from
      26             :             the corresponding bhm.
      27             : 
      28             :    vte_map  (capacity max): maps vte_key_t (vote_acc, block_id) -> vte_t
      29             :             for O(1) check of whether a voter has already voted for a
      30             :             given block_id.  If they have, the vote is ignored.
      31             : 
      32             :    vtr_map  (capacity vtr_max): maps vote_acc -> vtr_t, tracking each
      33             :             known voter.  vtr entries are explicitly managed by
      34             :             fd_hfork_update_voters when the epoch stake set changes.
      35             :             Each vtr has a pre-allocated vte_dlist that tracks the
      36             :             voter's recent votes in FIFO order.
      37             : 
      38             :             vtr_map                        blk_map
      39             :      map[0] +--------------------+  map[0] +--------------------+
      40             :             | (vtr_t) {          |         | (blk_t) {          |
      41             :             |   .vote_acc = X,   |         |   .block_id  = A,  |
      42             :             |   ...              |         |   ...              |
      43             :             |   .vte_dlist = ... |         |   .bhm_dlist = ... |
      44             :             | }                  |         | }                  |
      45             :      map[1] +--------------------+  map[1] +--------------------+
      46             :             | (vtr_t) {          |         | (blk_t) {          |
      47             :             |   .vote_acc = Y,   |         |   .block_id = B    |
      48             :             |   ...              |         |   ...              |
      49             :             |   .vte_dlist = +   |         |   .bhm_dlist = +   |
      50             :             | }              |   |         | }              |   |
      51             :             +----------------|---+         +----------------|---+
      52             :                              |                              |
      53             :                              |                              |
      54             :                              |                              |
      55             :                              |             bhm_dlist <------+
      56             :                              |             +--------------+--------------+--------------+
      57             :                              |             | (bhm_t) {    | (bhm_t) {    | (bhm_t) {    |
      58             :                              |             |   .key = A0, |   .key = A1, |   .key = A2, |
      59             :                              |             |   ...        |   ...        |   ...        |
      60             :                              |             | }            | }            | }            |
      61             :                              |             +--------------+--------------+--------------+
      62             :                              |
      63             :                              V
      64             :                              vte_dlist
      65             :                              +------------------------+------------------------+------------------------+
      66             :                              | (vte_t) {              | (vte_t) {              | (vte_t) {              |
      67             :                              |   .key.vote_acc  = Y,  |   .key.vote_acc = Y,   |   .key.vote_acc = Y,   |
      68             :                              |   .key.block_id  = A,  |   .key.block_id  = C,  |   .key.block_id  = B,  |
      69             :                              |   .bank_hash     = A0, |   .bank_hash     = C0, |   .bank_hash     = B0, |
      70             :                              |   ...                  |   ...                  |   ...                  |
      71             :                              | }                      | }                      | }                      |
      72             :                              +------------------------+------------------------+------------------------+
      73             :                              oldest                                                    newest
      74             : 
      75             :    vte_map prevents a voter from voting for the same block_id twice.
      76             :    The adversary is bounded because when vte_cnt == per_vtr_max, the
      77             :    oldest vte is popped and its stake is subtracted from the matching
      78             :    bhm. */
      79             : 
      80             : typedef struct {
      81             :   fd_hash_t block_id;
      82             :   fd_hash_t bank_hash;
      83             : } bhm_key_t;
      84             : 
      85             : struct bhm {
      86             :   bhm_key_t key;  /* bhm_map key */
      87             :   ulong     next; /* pool next */
      88             :   struct {
      89             :     ulong prev;
      90             :     ulong next;
      91             :   } map;
      92             :   struct {
      93             :     ulong prev;
      94             :     ulong next;
      95             :   } dlist;
      96             :   ulong slot;
      97             :   ulong stake;
      98             : };
      99             : typedef struct bhm bhm_t;
     100             : 
     101             : #define POOL_NAME bhm_pool
     102          54 : #define POOL_T    bhm_t
     103             : #include "../../util/tmpl/fd_pool.c"
     104             : 
     105             : #define MAP_NAME                           bhm_map
     106          12 : #define MAP_ELE_T                          bhm_t
     107             : #define MAP_KEY_T                          bhm_key_t
     108         207 : #define MAP_PREV                           map.prev
     109         342 : #define MAP_NEXT                           map.next
     110         246 : #define MAP_KEY_EQ(k0,k1)                  (!memcmp((k0)->block_id.key,(k1)->block_id.key,32UL) & \
     111         246 :                                             !memcmp((k0)->bank_hash.key,(k1)->bank_hash.key,32UL))
     112         222 : #define MAP_KEY_HASH(key,seed)             ((ulong)((key)->block_id.ul[1]^(key)->bank_hash.ul[1]^(seed)))
     113             : #define MAP_OPTIMIZE_RANDOM_ACCESS_REMOVAL 1
     114             : #include "../../util/tmpl/fd_map_chain.c"
     115             : 
     116             : #define DLIST_NAME  bhm_dlist
     117             : #define DLIST_ELE_T bhm_t
     118         210 : #define DLIST_PREV  dlist.prev
     119         213 : #define DLIST_NEXT  dlist.next
     120             : #include "../../util/tmpl/fd_dlist.c"
     121             : 
     122             : struct blk {
     123             :   fd_hash_t block_id;      /* blk_map key */
     124             :   ulong     prev;          /* blk_map prev */
     125             :   ulong     next;          /* pool next / blk_map next */
     126             :   struct {
     127             :     ulong prev;
     128             :     ulong next;
     129             :   } dlist;
     130             :   fd_hash_t our_bank_hash; /* 0: not replayed, -1: dead, else: our bank hash */
     131             :   void *    bhm_dlist;     /* dlist of bank hash objects for this block id */
     132             :   ulong     bhm_cnt;       /* number of competing bank hashes for this block id */
     133             : };
     134             : typedef struct blk blk_t;
     135             : 
     136             : #define POOL_NAME blk_pool
     137          81 : #define POOL_T    blk_t
     138             : #include "../../util/tmpl/fd_pool.c"
     139             : 
     140             : #define MAP_NAME                           blk_map
     141          18 : #define MAP_ELE_T                          blk_t
     142             : #define MAP_KEY_T                          fd_hash_t
     143         114 : #define MAP_KEY                            block_id
     144         249 : #define MAP_PREV                           prev
     145         612 : #define MAP_NEXT                           next
     146         525 : #define MAP_KEY_EQ(k0,k1)                  (!memcmp((k0)->key,(k1)->key,32UL))
     147         330 : #define MAP_KEY_HASH(key,seed)             ((ulong)((key)->ul[1]^(seed)))
     148             : #define MAP_OPTIMIZE_RANDOM_ACCESS_REMOVAL 1
     149             : #include "../../util/tmpl/fd_map_chain.c"
     150             : 
     151             : #define DLIST_NAME  blk_dlist
     152             : #define DLIST_ELE_T blk_t
     153          72 : #define DLIST_PREV  dlist.prev
     154          78 : #define DLIST_NEXT  dlist.next
     155             : #include "../../util/tmpl/fd_dlist.c"
     156             : 
     157             : typedef struct {
     158             :   fd_pubkey_t vote_acc;
     159             :   fd_hash_t   block_id;
     160             : } vte_key_t;
     161             : 
     162             : struct vte {
     163             :   vte_key_t key; /* vte_map key: (vote_acc, block_id) */
     164             :   ulong     next;
     165             :   struct {
     166             :     ulong prev;
     167             :     ulong next;
     168             :   } vte_map;
     169             :   struct {
     170             :     ulong prev;
     171             :     ulong next;
     172             :   } dlist;
     173             :   fd_hash_t bank_hash;
     174             :   ulong     slot;
     175             :   ulong     stake;
     176             : };
     177             : typedef struct vte vte_t;
     178             : 
     179             : #define POOL_NAME vte_pool
     180          54 : #define POOL_T    vte_t
     181             : #include "../../util/tmpl/fd_pool.c"
     182             : 
     183             : #define MAP_NAME                           vte_map
     184          18 : #define MAP_ELE_T                          vte_t
     185             : #define MAP_KEY_T                          vte_key_t
     186         123 : #define MAP_PREV                           vte_map.prev
     187         192 : #define MAP_NEXT                           vte_map.next
     188          96 : #define MAP_KEY_EQ(k0,k1)                  (!memcmp((k0)->vote_acc.key,(k1)->vote_acc.key,32UL) & \
     189          96 :                                             !memcmp((k0)->block_id.key,(k1)->block_id.key,32UL))
     190         150 : #define MAP_KEY_HASH(key,seed)             ((ulong)((key)->vote_acc.ul[1]^(key)->block_id.ul[1]^(seed)))
     191             : #define MAP_OPTIMIZE_RANDOM_ACCESS_REMOVAL 1
     192             : #include "../../util/tmpl/fd_map_chain.c"
     193             : 
     194             : #define DLIST_NAME  vte_dlist
     195             : #define DLIST_ELE_T vte_t
     196          69 : #define DLIST_PREV  dlist.prev
     197          87 : #define DLIST_NEXT  dlist.next
     198             : #include "../../util/tmpl/fd_dlist.c"
     199             : 
     200             : struct vtr {
     201             :   fd_pubkey_t vote_acc;
     202             :   ulong       next; /* pool next; reused as kept flag during update_voters */
     203             :   struct {
     204             :     ulong prev;
     205             :     ulong next;
     206             :   } map;
     207             :   struct {
     208             :     ulong prev;
     209             :     ulong next;
     210             :   } dlist;
     211             :   vte_dlist_t * vte_dlist;
     212             :   ulong         vte_cnt;
     213             : };
     214             : typedef struct vtr vtr_t;
     215             : 
     216             : #define POOL_NAME vtr_pool
     217          81 : #define POOL_T    vtr_t
     218             : #include "../../util/tmpl/fd_pool.c"
     219             : 
     220             : #define MAP_NAME                           vtr_map
     221          12 : #define MAP_ELE_T                          vtr_t
     222             : #define MAP_KEY_T                          fd_pubkey_t
     223          60 : #define MAP_KEY                            vote_acc
     224         252 : #define MAP_PREV                           map.prev
     225         369 : #define MAP_NEXT                           map.next
     226         249 : #define MAP_KEY_EQ(k0,k1)                  (!memcmp((k0)->key,(k1)->key,sizeof(fd_pubkey_t)))
     227         204 : #define MAP_KEY_HASH(key,seed)             ((ulong)((key)->ul[1]^(seed)))
     228             : #define MAP_OPTIMIZE_RANDOM_ACCESS_REMOVAL 1
     229             : #include "../../util/tmpl/fd_map_chain.c"
     230             : 
     231             : #define DLIST_NAME  vtr_dlist
     232             : #define DLIST_ELE_T vtr_t
     233          75 : #define DLIST_PREV  dlist.prev
     234         114 : #define DLIST_NEXT  dlist.next
     235             : #include "../../util/tmpl/fd_dlist.c"
     236             : 
     237             : struct __attribute__((aligned(128UL))) fd_hfork {
     238             :   ulong         max;
     239             :   ulong         per_vtr_max;
     240             :   ulong         vtr_max;
     241             :   bhm_t *       bhm_pool;
     242             :   bhm_map_t *   bhm_map;
     243             :   blk_t *       blk_pool;
     244             :   blk_map_t *   blk_map;
     245             :   blk_dlist_t * blk_dlist;
     246             :   vte_t *       vte_pool;
     247             :   vte_map_t *   vte_map;
     248             :   vtr_t *       vtr_pool;
     249             :   vtr_map_t *   vtr_map;
     250             :   vtr_dlist_t * vtr_dlist;
     251             : };
     252             : typedef struct fd_hfork fd_hfork_t;
     253             : 
     254             : 
     255             : /* bhm_remove removes a bhm from bhm_map, its owning blk's bhm_dlist,
     256             :    and releases it back to bhm_pool.  If the blk has no remaining bhm
     257             :    entries, then the blk is also removed and released. */
     258             : 
     259             : static void
     260             : bhm_remove( fd_hfork_t * hfork,
     261          12 :             bhm_t *      bhm ) {
     262          12 :   blk_t * blk = blk_map_ele_query( hfork->blk_map, &bhm->key.block_id, NULL, hfork->blk_pool );
     263          12 :   FD_TEST( blk );
     264          12 :   bhm_dlist_ele_remove( blk->bhm_dlist, bhm, hfork->bhm_pool );
     265          12 :   bhm_map_ele_remove_fast( hfork->bhm_map, bhm, hfork->bhm_pool );
     266          12 :   bhm_pool_ele_release( hfork->bhm_pool, bhm );
     267          12 :   blk->bhm_cnt--;
     268          12 :   if( FD_UNLIKELY( !blk->bhm_cnt ) ) {
     269          12 :     blk_map_ele_remove_fast( hfork->blk_map, blk, hfork->blk_pool );
     270          12 :     blk_pool_ele_release( hfork->blk_pool, blk );
     271          12 :   }
     272          12 : }
     273             : 
     274             : static int
     275             : compare( blk_t * blk,
     276             :          bhm_t * bhm,
     277          75 :          ulong   total_stake ) {
     278             : 
     279          75 :   if( FD_UNLIKELY( 0==memcmp( &blk->our_bank_hash, &hash_null, sizeof(fd_hash_t) ) ) ) return 0;
     280             : 
     281          18 :   double pct = (double)bhm->stake * 100.0 / (double)total_stake;
     282          18 :   if( FD_UNLIKELY( pct < 52.0 ) ) return 0;
     283             : 
     284           9 :   if( FD_UNLIKELY( 0!=memcmp( &blk->our_bank_hash, &bhm->key.bank_hash, sizeof(fd_hash_t) ) ) ) return -1;
     285           3 :   return 1;
     286           9 : }
     287             : 
     288             : ulong
     289         270 : fd_hfork_align( void ) {
     290         270 :   return 128UL;
     291         270 : }
     292             : 
     293             : ulong
     294             : fd_hfork_footprint( ulong per_vtr_max,
     295          54 :                     ulong vtr_max ) {
     296             : 
     297          54 :   vtr_max   = fd_ulong_pow2_up( vtr_max );
     298          54 :   ulong max = fd_ulong_pow2_up( per_vtr_max * vtr_max );
     299             : 
     300          54 :   ulong l = FD_LAYOUT_INIT;
     301          54 :   l = FD_LAYOUT_APPEND( l, alignof(fd_hfork_t), sizeof(fd_hfork_t)                                       );
     302          54 :   l = FD_LAYOUT_APPEND( l, bhm_pool_align(),    bhm_pool_footprint( max )                                 );
     303          54 :   l = FD_LAYOUT_APPEND( l, bhm_map_align(),     bhm_map_footprint( bhm_map_chain_cnt_est( max ) )         );
     304          54 :   l = FD_LAYOUT_APPEND( l, blk_pool_align(),    blk_pool_footprint( max )                                 );
     305          54 :   l = FD_LAYOUT_APPEND( l, blk_map_align(),     blk_map_footprint( blk_map_chain_cnt_est( max ) )         );
     306          54 :   l = FD_LAYOUT_APPEND( l, blk_dlist_align(),   blk_dlist_footprint()                                     );
     307          54 :   l = FD_LAYOUT_APPEND( l, vte_pool_align(),    vte_pool_footprint( max )                                 );
     308          54 :   l = FD_LAYOUT_APPEND( l, vte_map_align(),     vte_map_footprint( vte_map_chain_cnt_est( max ) )         );
     309          54 :   l = FD_LAYOUT_APPEND( l, vtr_pool_align(),    vtr_pool_footprint( vtr_max )                             );
     310          54 :   l = FD_LAYOUT_APPEND( l, vtr_map_align(),     vtr_map_footprint( vtr_map_chain_cnt_est( vtr_max ) )     );
     311          54 :   l = FD_LAYOUT_APPEND( l, vtr_dlist_align(),   vtr_dlist_footprint()                                     );
     312         726 :   for( ulong i = 0UL; i < max; i++ ) {
     313         672 :     l = FD_LAYOUT_APPEND( l, bhm_dlist_align(), bhm_dlist_footprint() );
     314         672 :   }
     315         186 :   for( ulong i = 0UL; i < vtr_max; i++ ) {
     316         132 :     l = FD_LAYOUT_APPEND( l, vte_dlist_align(), vte_dlist_footprint() );
     317         132 :   }
     318          54 :   return FD_LAYOUT_FINI( l, fd_hfork_align() );
     319          54 : }
     320             : 
     321             : void *
     322             : fd_hfork_new( void * shmem,
     323             :               ulong  per_vtr_max,
     324             :               ulong  vtr_max,
     325          27 :               ulong  seed ) {
     326             : 
     327          27 :   if( FD_UNLIKELY( !shmem ) ) {
     328           0 :     FD_LOG_WARNING(( "NULL mem" ));
     329           0 :     return NULL;
     330           0 :   }
     331             : 
     332          27 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)shmem, fd_hfork_align() ) ) ) {
     333           0 :     FD_LOG_WARNING(( "misaligned mem" ));
     334           0 :     return NULL;
     335           0 :   }
     336             : 
     337          27 :   ulong footprint = fd_hfork_footprint( per_vtr_max, vtr_max );
     338          27 :   if( FD_UNLIKELY( !footprint ) ) {
     339           0 :     FD_LOG_WARNING(( "bad per_vtr_max (%lu) or vtr_max (%lu)", per_vtr_max, vtr_max ));
     340           0 :     return NULL;
     341           0 :   }
     342             : 
     343          27 :   fd_memset( shmem, 0, footprint );
     344             : 
     345          27 :   vtr_max   = fd_ulong_pow2_up( vtr_max );
     346          27 :   ulong max = fd_ulong_pow2_up( per_vtr_max * vtr_max );
     347             : 
     348          27 :   FD_SCRATCH_ALLOC_INIT( l, shmem );
     349          27 :   fd_hfork_t * hfork     = FD_SCRATCH_ALLOC_APPEND( l, fd_hfork_align(),  sizeof(fd_hfork_t)                                      );
     350          27 :   void *       bhm_pool  = FD_SCRATCH_ALLOC_APPEND( l, bhm_pool_align(),  bhm_pool_footprint( max )                               );
     351          27 :   void *       bhm_map   = FD_SCRATCH_ALLOC_APPEND( l, bhm_map_align(),   bhm_map_footprint( bhm_map_chain_cnt_est( max ) )       );
     352          27 :   void *       blk_pool  = FD_SCRATCH_ALLOC_APPEND( l, blk_pool_align(),  blk_pool_footprint( max )                               );
     353          27 :   void *       blk_map   = FD_SCRATCH_ALLOC_APPEND( l, blk_map_align(),   blk_map_footprint( blk_map_chain_cnt_est( max ) )       );
     354          27 :   void *       blk_dlist = FD_SCRATCH_ALLOC_APPEND( l, blk_dlist_align(), blk_dlist_footprint()                                   );
     355          27 :   void *       vte_pool  = FD_SCRATCH_ALLOC_APPEND( l, vte_pool_align(),  vte_pool_footprint( max )                               );
     356          27 :   void *       vte_map   = FD_SCRATCH_ALLOC_APPEND( l, vte_map_align(),   vte_map_footprint( vte_map_chain_cnt_est( max ) )       );
     357          27 :   void *       vtr_pool  = FD_SCRATCH_ALLOC_APPEND( l, vtr_pool_align(),  vtr_pool_footprint( vtr_max )                           );
     358          27 :   void *       vtr_map   = FD_SCRATCH_ALLOC_APPEND( l, vtr_map_align(),   vtr_map_footprint( vtr_map_chain_cnt_est( vtr_max ) )   );
     359          27 :   void *       vtr_dlist = FD_SCRATCH_ALLOC_APPEND( l, vtr_dlist_align(), vtr_dlist_footprint()                                   );
     360             : 
     361          27 :   hfork->max         = max;
     362          27 :   hfork->per_vtr_max = per_vtr_max;
     363          27 :   hfork->vtr_max     = vtr_max;
     364          27 :   hfork->bhm_pool    = bhm_pool_new ( bhm_pool,  max                                    );
     365          27 :   hfork->bhm_map     = bhm_map_new  ( bhm_map,   bhm_map_chain_cnt_est( max ),     seed );
     366          27 :   hfork->blk_pool    = blk_pool_new ( blk_pool,  max                                    );
     367          27 :   hfork->blk_map     = blk_map_new  ( blk_map,   blk_map_chain_cnt_est( max ),     seed );
     368          27 :   hfork->blk_dlist   = blk_dlist_new( blk_dlist                                         );
     369          27 :   hfork->vte_pool    = vte_pool_new ( vte_pool,  max                                    );
     370          27 :   hfork->vte_map     = vte_map_new  ( vte_map,   vte_map_chain_cnt_est( max ),     seed );
     371          27 :   hfork->vtr_pool    = vtr_pool_new ( vtr_pool,  vtr_max                                );
     372          27 :   hfork->vtr_map     = vtr_map_new  ( vtr_map,   vtr_map_chain_cnt_est( vtr_max ), seed );
     373          27 :   hfork->vtr_dlist   = vtr_dlist_new( vtr_dlist                                         );
     374             : 
     375          27 :   blk_t * blk_join = blk_pool_join( hfork->blk_pool );
     376         363 :   for( ulong i = 0UL; i < max; i++ ) {
     377         336 :     void * bhm_dlist       = FD_SCRATCH_ALLOC_APPEND( l, bhm_dlist_align(), bhm_dlist_footprint() );
     378         336 :     blk_join[i].bhm_cnt    = 0;
     379         336 :     blk_join[i].bhm_dlist  = bhm_dlist_new( bhm_dlist );
     380         336 :   }
     381             : 
     382          27 :   vtr_t * vtr_join = vtr_pool_join( hfork->vtr_pool );
     383          93 :   for( ulong i = 0UL; i < vtr_max; i++ ) {
     384          66 :     void * vte_dlist       = FD_SCRATCH_ALLOC_APPEND( l, vte_dlist_align(), vte_dlist_footprint() );
     385          66 :     vtr_join[i].vte_cnt    = 0;
     386          66 :     vtr_join[i].vte_dlist  = vte_dlist_new( vte_dlist );
     387          66 :   }
     388          27 :   FD_TEST( FD_SCRATCH_ALLOC_FINI( l, fd_hfork_align() ) == (ulong)shmem + footprint );
     389          27 :   return shmem;
     390          27 : }
     391             : 
     392             : fd_hfork_t *
     393          27 : fd_hfork_join( void * shhfork ) {
     394          27 :   fd_hfork_t * hfork = (fd_hfork_t *)shhfork;
     395             : 
     396          27 :   if( FD_UNLIKELY( !hfork ) ) {
     397           0 :     FD_LOG_WARNING(( "NULL hfork" ));
     398           0 :     return NULL;
     399           0 :   }
     400             : 
     401          27 :   if( FD_UNLIKELY( !fd_ulong_is_aligned((ulong)hfork, fd_hfork_align() ) ) ) {
     402           0 :     FD_LOG_WARNING(( "misaligned hfork" ));
     403           0 :     return NULL;
     404           0 :   }
     405             : 
     406          27 :   hfork->bhm_pool  = bhm_pool_join ( hfork->bhm_pool  );
     407          27 :   hfork->bhm_map   = bhm_map_join  ( hfork->bhm_map   );
     408          27 :   hfork->blk_pool  = blk_pool_join ( hfork->blk_pool  );
     409          27 :   hfork->blk_map   = blk_map_join  ( hfork->blk_map   );
     410          27 :   hfork->blk_dlist = blk_dlist_join( hfork->blk_dlist );
     411          27 :   hfork->vte_pool  = vte_pool_join ( hfork->vte_pool  );
     412          27 :   hfork->vte_map   = vte_map_join  ( hfork->vte_map   );
     413          27 :   hfork->vtr_pool  = vtr_pool_join ( hfork->vtr_pool  );
     414          27 :   hfork->vtr_map   = vtr_map_join  ( hfork->vtr_map   );
     415          27 :   hfork->vtr_dlist = vtr_dlist_join( hfork->vtr_dlist );
     416         363 :   for( ulong i = 0UL; i < hfork->max; i++ ) {
     417         336 :     hfork->blk_pool[i].bhm_dlist = bhm_dlist_join( hfork->blk_pool[i].bhm_dlist );
     418         336 :   }
     419          93 :   for( ulong i = 0UL; i < hfork->vtr_max; i++ ) {
     420          66 :     hfork->vtr_pool[i].vte_dlist = vte_dlist_join( hfork->vtr_pool[i].vte_dlist );
     421          66 :   }
     422             : 
     423          27 :   return hfork;
     424          27 : }
     425             : 
     426             : void *
     427          27 : fd_hfork_leave( fd_hfork_t const * hfork ) {
     428             : 
     429          27 :   if( FD_UNLIKELY( !hfork ) ) {
     430           0 :     FD_LOG_WARNING(( "NULL hfork" ));
     431           0 :     return NULL;
     432           0 :   }
     433             : 
     434          27 :   return (void *)hfork;
     435          27 : }
     436             : 
     437             : void *
     438          27 : fd_hfork_delete( void * hfork ) {
     439             : 
     440          27 :   if( FD_UNLIKELY( !hfork ) ) {
     441           0 :     FD_LOG_WARNING(( "NULL hfork" ));
     442           0 :     return NULL;
     443           0 :   }
     444             : 
     445          27 :   if( FD_UNLIKELY( !fd_ulong_is_aligned((ulong)hfork, fd_hfork_align() ) ) ) {
     446           0 :     FD_LOG_WARNING(( "misaligned hfork" ));
     447           0 :     return NULL;
     448           0 :   }
     449             : 
     450          27 :   return hfork;
     451          27 : }
     452             : 
     453             : static blk_t *
     454             : blk_insert( fd_hfork_t      * hfork,
     455         105 :             fd_hash_t const * block_id ) {
     456         105 :   if( FD_UNLIKELY( !blk_pool_free( hfork->blk_pool ) ) ) {
     457           9 :     if( FD_UNLIKELY( blk_dlist_is_empty( hfork->blk_dlist, hfork->blk_pool ) ) ) return NULL;
     458           6 :     blk_t * evicted = blk_dlist_ele_pop_head( hfork->blk_dlist, hfork->blk_pool );
     459           6 :     blk_map_ele_remove_fast( hfork->blk_map, evicted, hfork->blk_pool );
     460           6 :     blk_pool_ele_release( hfork->blk_pool, evicted );
     461           6 :   }
     462         102 :   blk_t * blk        = blk_pool_ele_acquire( hfork->blk_pool );
     463         102 :   blk->block_id      = *block_id;
     464         102 :   blk->our_bank_hash = hash_null;
     465         102 :   blk->bhm_cnt       = 0;
     466         102 :   blk_map_ele_insert( hfork->blk_map, blk, hfork->blk_pool );
     467         102 :   return blk;
     468         105 : }
     469             : 
     470             : int
     471             : fd_hfork_count_vote( fd_hfork_t *        hfork,
     472             :                      fd_pubkey_t const * vote_acc,
     473             :                      fd_hash_t const *   block_id,
     474             :                      fd_hash_t const *   bank_hash,
     475             :                      ulong               slot,
     476             :                      ulong               stake,
     477          78 :                      ulong               total_stake ) {
     478             : 
     479             :   /* Get the vtr.  If not in the voter set, ignore. */
     480             : 
     481          78 :   vtr_t * vtr = vtr_map_ele_query( hfork->vtr_map, vote_acc, NULL, hfork->vtr_pool );
     482          78 :   if( FD_UNLIKELY( !vtr ) ) return FD_HFORK_ERR_UNKNOWN_VTR;
     483             : 
     484             :   /* If voter already voted for this block_id, ignore. */
     485             : 
     486          75 :   bhm_key_t bhm_key = { .block_id = *block_id, .bank_hash = *bank_hash };
     487          75 :   vte_key_t vte_key = { .vote_acc = *vote_acc, .block_id  = *block_id };
     488          75 :   if( FD_UNLIKELY( vte_map_ele_query_const( hfork->vte_map, &vte_key, NULL, hfork->vte_pool ) ) ) return FD_HFORK_ERR_ALREADY_VOTED;
     489             : 
     490             :   /* Only process newer votes (by vote slot) from a given voter. */
     491             : 
     492          72 :   if( FD_UNLIKELY( vtr->vte_cnt && vte_dlist_ele_peek_tail_const( vtr->vte_dlist, hfork->vte_pool )->slot >= slot ) ) return FD_HFORK_ERR_VOTE_TOO_OLD;
     493             : 
     494             :   /* Zero-stake votes don't contribute to hard fork detection. */
     495             : 
     496          69 :   if( FD_UNLIKELY( !stake ) ) return FD_HFORK_SUCCESS;
     497             : 
     498             :   /* If voter has reached their quota, evict their oldest vote. */
     499             : 
     500          69 :   if( FD_UNLIKELY( vtr->vte_cnt==hfork->per_vtr_max ) ) {
     501           9 :     vte_t * evicted_vte = vte_dlist_ele_pop_head( vtr->vte_dlist, hfork->vte_pool );
     502           9 :     bhm_key_t evicted_bhm_key = { .block_id = evicted_vte->key.block_id, .bank_hash = evicted_vte->bank_hash };
     503           9 :     ulong     evicted_stake   = evicted_vte->stake;
     504           9 :     vte_map_ele_remove_fast( hfork->vte_map, evicted_vte, hfork->vte_pool );
     505           9 :     vte_pool_ele_release( hfork->vte_pool, evicted_vte );
     506           9 :     vtr->vte_cnt--;
     507             : 
     508           9 :     bhm_t * bhm = bhm_map_ele_query( hfork->bhm_map, &evicted_bhm_key, NULL, hfork->bhm_pool );
     509           9 :     bhm->stake -= evicted_stake;
     510           9 :     if( FD_UNLIKELY( !bhm->stake ) ) bhm_remove( hfork, bhm );
     511           9 :   }
     512             : 
     513             :   /* Upsert the blk. */
     514             : 
     515          69 :   blk_t * blk = blk_map_ele_query( hfork->blk_map, block_id, NULL, hfork->blk_pool );
     516          69 :   if     ( FD_UNLIKELY( !blk          ) ) blk = blk_insert( hfork, block_id );
     517          27 :   else if( FD_UNLIKELY( !blk->bhm_cnt ) ) blk_dlist_ele_remove( hfork->blk_dlist, blk, hfork->blk_pool ); /* record_our_bank_hash before any count_vote */
     518             : 
     519             :   /* Upsert the bhm. */
     520             : 
     521          69 :   bhm_t * bhm = bhm_map_ele_query( hfork->bhm_map, &bhm_key, NULL, hfork->bhm_pool );
     522          69 :   if( FD_UNLIKELY( !bhm ) ) {
     523          60 :     bhm          = bhm_pool_ele_acquire( hfork->bhm_pool );
     524          60 :     bhm->key     = bhm_key;
     525          60 :     bhm->slot    = slot;
     526          60 :     bhm->stake   = 0UL;
     527          60 :     bhm_map_ele_insert( hfork->bhm_map, bhm, hfork->bhm_pool );
     528          60 :     bhm_dlist_ele_push_tail( blk->bhm_dlist, bhm, hfork->bhm_pool );
     529          60 :     blk->bhm_cnt++;
     530          60 :   }
     531          69 :   bhm->stake += stake;
     532          69 :   bhm_dlist_ele_remove( blk->bhm_dlist, bhm, hfork->bhm_pool );
     533          69 :   bhm_dlist_ele_push_tail( blk->bhm_dlist, bhm, hfork->bhm_pool );
     534             : 
     535             :   /* Push the vte onto the vtr. */
     536             : 
     537          69 :   vte_t * vte    = vte_pool_ele_acquire( hfork->vte_pool );
     538          69 :   vte->key       = vte_key;
     539          69 :   vte->bank_hash = *bank_hash;
     540          69 :   vte->slot      = slot;
     541          69 :   vte->stake     = stake;
     542          69 :   vte_map_ele_insert( hfork->vte_map, vte, hfork->vte_pool );
     543          69 :   vte_dlist_ele_push_tail( vtr->vte_dlist, vte, hfork->vte_pool );
     544          69 :   vtr->vte_cnt++;
     545             : 
     546             :   /* Check for hard forks. */
     547             : 
     548          69 :   return compare( blk, bhm, total_stake );
     549          69 : }
     550             : 
     551             : int
     552             : fd_hfork_record_our_bank_hash( fd_hfork_t *      hfork,
     553             :                                fd_hash_t const * block_id,
     554             :                                fd_hash_t const * bank_hash,
     555          69 :                                ulong             total_stake ) {
     556             : 
     557          69 :   blk_t * blk = blk_map_ele_query( hfork->blk_map, block_id, NULL, hfork->blk_pool );
     558          69 :   if( FD_LIKELY( !blk ) ) {
     559          63 :     blk = blk_insert( hfork, block_id );
     560          63 :     if( FD_UNLIKELY( !blk ) ) return 0;
     561          60 :     blk_dlist_ele_push_tail( hfork->blk_dlist, blk, hfork->blk_pool );
     562          60 :   }
     563          66 :   blk->our_bank_hash = *fd_ptr_if( !!bank_hash, bank_hash, &hash_invalid );
     564             : 
     565             :   /* Check all bhm entries for this block_id. */
     566             : 
     567          66 :   for( bhm_dlist_iter_t iter = bhm_dlist_iter_fwd_init( blk->bhm_dlist, hfork->bhm_pool );
     568          69 :                               !bhm_dlist_iter_done( iter, blk->bhm_dlist, hfork->bhm_pool );
     569          66 :                         iter = bhm_dlist_iter_fwd_next( iter, blk->bhm_dlist, hfork->bhm_pool ) ) {
     570           6 :     bhm_t * bhm = bhm_dlist_iter_ele( iter, blk->bhm_dlist, hfork->bhm_pool );
     571           6 :     int     cmp = compare( blk, bhm, total_stake );
     572           6 :     if( cmp ) return cmp;
     573           6 :   }
     574          63 :   return 0;
     575          66 : }
     576             : 
     577             : void
     578             : fd_hfork_update_voters( fd_hfork_t *        hfork,
     579             :                         fd_pubkey_t const * vote_accs,
     580          21 :                         ulong               cnt ) {
     581             : 
     582          21 :   for( vtr_dlist_iter_t iter = vtr_dlist_iter_fwd_init( hfork->vtr_dlist, hfork->vtr_pool );
     583          42 :        !vtr_dlist_iter_done( iter, hfork->vtr_dlist, hfork->vtr_pool );
     584          21 :        iter = vtr_dlist_iter_fwd_next( iter, hfork->vtr_dlist, hfork->vtr_pool ) ) {
     585          21 :     hfork->vtr_pool[iter].next = 1; /* mark for removal */
     586          21 :   }
     587             : 
     588             :   /* First pass: unmark kept voters from being released. */
     589             : 
     590          42 :   for( ulong i=0UL; i<cnt; i++ ) {
     591          21 :     fd_pubkey_t const * vote_acc = &vote_accs[i];
     592          21 :     vtr_t *             vtr      = vtr_map_ele_query( hfork->vtr_map, vote_acc, NULL, hfork->vtr_pool );
     593          21 :     if( FD_LIKELY( vtr ) ) {
     594           9 :       vtr_dlist_ele_remove( hfork->vtr_dlist, vtr, hfork->vtr_pool );
     595           9 :       vtr->next = 0; /* unmark for removal */
     596           9 :       vtr_dlist_ele_push_tail( hfork->vtr_dlist, vtr, hfork->vtr_pool );
     597           9 :     }
     598          21 :   }
     599             : 
     600             :   /* Pop and release marked voters until the first unmarked voter. */
     601             : 
     602          33 :   while( FD_LIKELY( !vtr_dlist_is_empty( hfork->vtr_dlist, hfork->vtr_pool ) ) ) {
     603          18 :     vtr_t * vtr = vtr_dlist_ele_pop_head( hfork->vtr_dlist, hfork->vtr_pool );
     604          18 :     if( FD_UNLIKELY( !vtr->next ) ) { /* can short-circuit since all the existing and new voters were appended */
     605           6 :       vtr_dlist_ele_push_tail( hfork->vtr_dlist, vtr, hfork->vtr_pool );
     606           6 :       break;
     607           6 :     }
     608          21 :     while( FD_LIKELY( !vte_dlist_is_empty( vtr->vte_dlist, hfork->vte_pool ) ) ) {
     609           9 :       vte_t * vte = vte_dlist_ele_pop_head( vtr->vte_dlist, hfork->vte_pool );
     610           9 :       vte_map_ele_remove_fast( hfork->vte_map, vte, hfork->vte_pool );
     611             : 
     612           9 :       bhm_key_t vte_xid = { .block_id = vte->key.block_id, .bank_hash = vte->bank_hash };
     613           9 :       bhm_t * bhm = bhm_map_ele_query( hfork->bhm_map, &vte_xid, NULL, hfork->bhm_pool );
     614           9 :       if( FD_LIKELY( bhm ) ) {
     615           9 :         bhm->stake -= vte->stake;
     616           9 :         if( FD_UNLIKELY( !bhm->stake ) ) bhm_remove( hfork, bhm );
     617           9 :       }
     618             : 
     619           9 :       vte_pool_ele_release( hfork->vte_pool, vte );
     620           9 :     }
     621          12 :     vtr_map_ele_remove_fast( hfork->vtr_map, vtr, hfork->vtr_pool );
     622          12 :     vtr_pool_ele_release( hfork->vtr_pool, vtr );
     623          12 :   }
     624             : 
     625             :   /* Second pass: acquire and insert new voters. */
     626             : 
     627          42 :   for( ulong i=0UL; i<cnt; i++ ) {
     628          21 :     fd_pubkey_t const * vote_acc = &vote_accs[i];
     629          21 :     if( FD_LIKELY( vtr_map_ele_query( hfork->vtr_map, vote_acc, NULL, hfork->vtr_pool ) ) ) continue;
     630          12 :     vtr_t * vtr  = vtr_pool_ele_acquire( hfork->vtr_pool );
     631          12 :     vtr->vote_acc = *vote_acc;
     632          12 :     vtr->vte_cnt = 0;
     633          12 :     vtr->next    = 0;
     634          12 :     vtr_map_ele_insert( hfork->vtr_map, vtr, hfork->vtr_pool );
     635          12 :     vtr_dlist_ele_push_tail( hfork->vtr_dlist, vtr, hfork->vtr_pool );
     636          12 :   }
     637          21 : }

Generated by: LCOV version 1.14