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

Generated by: LCOV version 1.14