LCOV - code coverage report
Current view: top level - choreo/hfork - fd_hfork.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 220 292 75.3 %
Date: 2026-03-31 06:22:16 Functions: 11 15 73.3 %

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

Generated by: LCOV version 1.14