LCOV - code coverage report
Current view: top level - flamenco/rewards - fd_epoch_rewards.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 133 182 73.1 %
Date: 2025-10-27 04:40:00 Functions: 15 15 100.0 %

          Line data    Source code
       1             : #include "fd_epoch_rewards.h"
       2             : #include "../../ballet/siphash13/fd_siphash13.h"
       3             : 
       4             : #define POOL_NAME fd_epoch_stake_reward_pool
       5          36 : #define POOL_T    fd_epoch_stake_reward_t
       6             : #include "../../util/tmpl/fd_pool.c"
       7             : 
       8             : #define MAP_NAME               fd_epoch_stake_reward_map
       9             : #define MAP_KEY_T              fd_pubkey_t
      10             : #define MAP_ELE_T              fd_epoch_stake_reward_t
      11           9 : #define MAP_KEY                stake_pubkey
      12           0 : #define MAP_KEY_EQ(k0,k1)      (fd_pubkey_eq( k0, k1 ))
      13          18 : #define MAP_KEY_HASH(key,seed) (fd_hash( seed, key, sizeof(fd_pubkey_t) ))
      14          18 : #define MAP_NEXT               next_map
      15             : #include "../../util/tmpl/fd_map_chain.c"
      16             : 
      17             : ulong
      18         198 : fd_epoch_rewards_align( void ) {
      19         198 :   return FD_EPOCH_REWARDS_ALIGN;
      20         198 : }
      21             : 
      22             : ulong
      23          24 : fd_epoch_rewards_footprint( ulong stake_account_max ) {
      24          24 :   ulong chain_cnt_est = fd_epoch_stake_reward_map_chain_cnt_est( stake_account_max );
      25             : 
      26          24 :   ulong l = FD_LAYOUT_INIT;
      27          24 :   l = FD_LAYOUT_APPEND( l, fd_epoch_rewards_align(), sizeof(fd_epoch_rewards_t) );
      28          24 :   l = FD_LAYOUT_APPEND( l, fd_epoch_stake_reward_pool_align(), fd_epoch_stake_reward_pool_footprint( stake_account_max ) );
      29          24 :   l = FD_LAYOUT_APPEND( l, fd_epoch_stake_reward_map_align(), fd_epoch_stake_reward_map_footprint( chain_cnt_est ) );
      30          24 :   l = FD_LAYOUT_APPEND( l, fd_epoch_stake_reward_dlist_align(), fd_epoch_stake_reward_dlist_footprint() * FD_REWARDS_MAX_PARTITIONS );
      31          24 :   return FD_LAYOUT_FINI( l, fd_epoch_rewards_align() );
      32          24 : }
      33             : 
      34             : void *
      35           9 : fd_epoch_rewards_new( void * shmem, ulong stake_account_max ) {
      36             : 
      37           9 :   if( FD_UNLIKELY( !shmem ) ) {
      38           3 :     FD_LOG_WARNING(( "NULL mem" ));
      39           3 :     return NULL;
      40           3 :   }
      41             : 
      42           6 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)shmem, fd_epoch_rewards_align() ) ) ) {
      43           0 :     FD_LOG_WARNING(( "misaligned mem" ));
      44           0 :     return NULL;
      45           0 :   }
      46             : 
      47           6 :   FD_SCRATCH_ALLOC_INIT( l, shmem );
      48           6 :   fd_epoch_rewards_t * epoch_rewards = FD_SCRATCH_ALLOC_APPEND( l, fd_epoch_rewards_align(), sizeof(fd_epoch_rewards_t) );
      49           0 :   memset( epoch_rewards, 0, sizeof(fd_epoch_rewards_t) );
      50             : 
      51           6 :   void * pool = FD_SCRATCH_ALLOC_APPEND( l, fd_epoch_stake_reward_pool_align(), fd_epoch_stake_reward_pool_footprint( stake_account_max ) );
      52           0 :   epoch_rewards->pool_offset = (ulong)pool - (ulong)shmem;
      53           6 :   if( FD_UNLIKELY( !fd_epoch_stake_reward_pool_new( pool, stake_account_max ) ) ) {
      54           0 :     FD_LOG_WARNING(( "bad pool" ));
      55           0 :     return NULL;
      56           0 :   }
      57             : 
      58           6 :   ulong chain_cnt_est = fd_epoch_stake_reward_map_chain_cnt_est( stake_account_max );
      59           6 :   void * map = FD_SCRATCH_ALLOC_APPEND( l, fd_epoch_stake_reward_map_align(), fd_epoch_stake_reward_map_footprint( chain_cnt_est ) );
      60           0 :   epoch_rewards->map_offset = (ulong)map - (ulong)shmem;
      61           6 :   if( FD_UNLIKELY( !fd_epoch_stake_reward_map_new( map, chain_cnt_est, 0UL ) ) ) {
      62           0 :     FD_LOG_WARNING(( "bad map" ));
      63           0 :     return NULL;
      64           0 :   }
      65             : 
      66        4404 :   for( ulong i=0UL; i<FD_REWARDS_MAX_PARTITIONS; i++ ) {
      67        4398 :     void * dlist = FD_SCRATCH_ALLOC_APPEND( l, fd_epoch_stake_reward_dlist_align(), fd_epoch_stake_reward_dlist_footprint() );
      68        4398 :     if( i==0UL ) epoch_rewards->dlists_offset = (ulong)dlist - (ulong)shmem;
      69        4398 :     if( FD_UNLIKELY( !fd_epoch_stake_reward_dlist_new( dlist ) ) ) {
      70           0 :       FD_LOG_WARNING(( "bad dlist at idx %lu", i ));
      71           0 :       return NULL;
      72           0 :     }
      73        4398 :   }
      74             : 
      75           6 :   if( FD_UNLIKELY( FD_SCRATCH_ALLOC_FINI( l, fd_epoch_rewards_align() ) != (ulong)shmem+fd_epoch_rewards_footprint( stake_account_max ) ) ) {
      76           0 :     FD_LOG_WARNING(( "bad footprint" ));
      77           0 :     return NULL;
      78           0 :   }
      79             : 
      80             : 
      81           6 :   FD_COMPILER_MFENCE();
      82           6 :   epoch_rewards->magic = FD_EPOCH_REWARDS_MAGIC;
      83           6 :   FD_COMPILER_MFENCE();
      84             : 
      85           6 :   epoch_rewards->stake_account_max = stake_account_max;
      86             : 
      87           6 :   return shmem;
      88           6 : }
      89             : 
      90             : fd_epoch_rewards_t *
      91          15 : fd_epoch_rewards_join( void * shmem ) {
      92          15 :   if( FD_UNLIKELY( !shmem ) ) {
      93           3 :     FD_LOG_WARNING(( "NULL mem" ));
      94           3 :     return NULL;
      95           3 :   }
      96             : 
      97          12 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)shmem, fd_epoch_rewards_align() ) ) ) {
      98           0 :     FD_LOG_WARNING(( "misaligned mem" ));
      99           0 :     return NULL;
     100           0 :   }
     101             : 
     102          12 :   FD_SCRATCH_ALLOC_INIT( l, shmem );
     103          12 :   fd_epoch_rewards_t * epoch_rewards = FD_SCRATCH_ALLOC_APPEND( l, fd_epoch_rewards_align(), sizeof(fd_epoch_rewards_t) );
     104           0 :   ulong stake_account_max = epoch_rewards->stake_account_max;
     105             : 
     106          12 :   void * pool = FD_SCRATCH_ALLOC_APPEND( l, fd_epoch_stake_reward_pool_align(), fd_epoch_stake_reward_pool_footprint( stake_account_max ) );
     107          12 :   if( FD_UNLIKELY( !fd_epoch_stake_reward_pool_join( pool ) ) ) {
     108           0 :     FD_LOG_WARNING(( "bad pool" ));
     109           0 :     return NULL;
     110           0 :   }
     111             : 
     112          12 :   ulong chain_cnt_est = fd_epoch_stake_reward_map_chain_cnt_est( stake_account_max );
     113          12 :   void * map = FD_SCRATCH_ALLOC_APPEND( l, fd_epoch_stake_reward_map_align(), fd_epoch_stake_reward_map_footprint( chain_cnt_est ) );
     114          12 :   if( FD_UNLIKELY( !fd_epoch_stake_reward_map_join( map ) ) ) {
     115           0 :     FD_LOG_WARNING(( "bad map" ));
     116           0 :     return NULL;
     117           0 :   }
     118             : 
     119        8808 :   for( ulong i=0UL; i<FD_REWARDS_MAX_PARTITIONS; i++ ) {
     120        8796 :     void * dlist = FD_SCRATCH_ALLOC_APPEND( l, fd_epoch_stake_reward_dlist_align(), fd_epoch_stake_reward_dlist_footprint() );
     121        8796 :     if( FD_UNLIKELY( !fd_epoch_stake_reward_dlist_join( dlist ) ) ) {
     122           0 :       FD_LOG_WARNING(( "bad dlist at idx %lu", i ));
     123           0 :       return NULL;
     124           0 :     }
     125        8796 :   }
     126             : 
     127          12 :   if( FD_UNLIKELY( FD_SCRATCH_ALLOC_FINI( l, fd_epoch_rewards_align() )!=(ulong)shmem+fd_epoch_rewards_footprint( stake_account_max ) ) ) {
     128           0 :     FD_LOG_WARNING(( "bad footprint" ));
     129           0 :     return NULL;
     130           0 :   }
     131             : 
     132          12 :   if( FD_UNLIKELY( epoch_rewards->magic!=FD_EPOCH_REWARDS_MAGIC ) ) {
     133           6 :     FD_LOG_WARNING(( "bad magic" ));
     134           6 :     return NULL;
     135           6 :   }
     136             : 
     137           6 :   return epoch_rewards;
     138          12 : }
     139             : 
     140             : void *
     141           6 : fd_epoch_rewards_leave( fd_epoch_rewards_t const * epoch_rewards ) {
     142           6 :   return (void *)epoch_rewards;
     143           6 : }
     144             : 
     145             : void *
     146           3 : fd_epoch_rewards_delete( void * epoch_rewards_shmem ) {
     147           3 :   fd_epoch_rewards_t * epoch_rewards = (fd_epoch_rewards_t *)epoch_rewards_shmem;
     148             : 
     149           3 :   if( FD_UNLIKELY( !epoch_rewards ) ) {
     150           0 :     FD_LOG_WARNING(( "NULL epoch_rewards" ));
     151           0 :     return NULL;
     152           0 :   }
     153             : 
     154           3 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)epoch_rewards, fd_epoch_rewards_align() ) ) ) {
     155           0 :     FD_LOG_WARNING(( "misaligned epoch_rewards" ));
     156           0 :     return NULL;
     157           0 :   }
     158             : 
     159           3 :   if( FD_UNLIKELY( epoch_rewards->magic != FD_EPOCH_REWARDS_MAGIC ) ) {
     160           0 :     FD_LOG_WARNING(( "bad magic" ));
     161           0 :     return NULL;
     162           0 :   }
     163             : 
     164           3 :   epoch_rewards->magic = 0UL;
     165             : 
     166           3 :   return epoch_rewards_shmem;
     167           3 : }
     168             : 
     169             : static inline fd_epoch_stake_reward_dlist_t *
     170          15 : fd_epoch_rewards_get_partition_index( fd_epoch_rewards_t const * epoch_rewards, ulong idx ) {
     171          15 :   if( FD_UNLIKELY( idx>=epoch_rewards->num_partitions ) ) {
     172           0 :     FD_LOG_WARNING(( "idx: %lu is greater than num_partitions: %lu", idx, epoch_rewards->num_partitions ));
     173           0 :     return NULL;
     174           0 :   }
     175             : 
     176          15 :   fd_epoch_stake_reward_dlist_t * dlist_idx_zero  = (fd_epoch_stake_reward_dlist_t *)((uchar *)epoch_rewards + epoch_rewards->dlists_offset);
     177          15 :   fd_epoch_stake_reward_dlist_t * partition_dlist = fd_epoch_stake_reward_dlist_join( dlist_idx_zero + idx );
     178          15 :   return partition_dlist;
     179          15 : }
     180             : 
     181             : static inline fd_epoch_stake_reward_t *
     182          18 : fd_epoch_rewards_get_stake_reward_pool( fd_epoch_rewards_t const * epoch_rewards ) {
     183          18 :   return fd_epoch_stake_reward_pool_join( (uchar *)epoch_rewards + epoch_rewards->pool_offset );
     184          18 : }
     185             : 
     186             : static inline fd_epoch_stake_reward_map_t *
     187          12 : fd_epoch_rewards_get_stake_reward_map( fd_epoch_rewards_t const * epoch_rewards ) {
     188          12 :   return fd_epoch_stake_reward_map_join( (uchar *)epoch_rewards + epoch_rewards->map_offset );
     189          12 : }
     190             : 
     191             : void
     192             : fd_epoch_rewards_insert( fd_epoch_rewards_t * epoch_rewards,
     193             :                          fd_pubkey_t const *  pubkey,
     194             :                          ulong                credits,
     195           9 :                          ulong                lamports ) {
     196           9 :   fd_epoch_stake_reward_t *     stake_reward_pool = fd_epoch_rewards_get_stake_reward_pool( epoch_rewards );
     197           9 :   fd_epoch_stake_reward_map_t * stake_reward_map  = fd_epoch_rewards_get_stake_reward_map( epoch_rewards );
     198             : 
     199           9 :   if( FD_UNLIKELY( fd_epoch_stake_reward_map_ele_query( stake_reward_map, pubkey, NULL, stake_reward_pool ) ) ) {
     200           0 :     FD_LOG_CRIT(( "invariant violation: stake reward entry already exists" ));
     201           0 :   }
     202             : 
     203           9 :   fd_epoch_stake_reward_t * stake_reward = fd_epoch_stake_reward_pool_ele_acquire( stake_reward_pool );
     204             : 
     205           9 :   stake_reward->stake_pubkey     = *pubkey;
     206           9 :   stake_reward->credits_observed = credits;
     207           9 :   stake_reward->lamports         = lamports;
     208             : 
     209           9 :   fd_epoch_stake_reward_map_ele_insert( stake_reward_map, stake_reward, stake_reward_pool );
     210             : 
     211           9 :   epoch_rewards->total_stake_rewards += lamports;
     212           9 :   epoch_rewards->stake_rewards_cnt++;
     213             : 
     214           9 : }
     215             : 
     216             : void
     217             : fd_epoch_rewards_hash_into_partitions( fd_epoch_rewards_t * epoch_rewards,
     218             :                                        fd_hash_t const *    parent_blockhash,
     219           3 :                                        ulong                num_partitions ) {
     220             : 
     221           3 :   fd_epoch_stake_reward_t *     stake_reward_pool = fd_epoch_rewards_get_stake_reward_pool( epoch_rewards );
     222           3 :   fd_epoch_stake_reward_map_t * stake_reward_map  = fd_epoch_rewards_get_stake_reward_map( epoch_rewards );
     223             : 
     224           3 :   epoch_rewards->num_partitions = num_partitions;
     225             : 
     226           3 :   for( fd_epoch_stake_reward_map_iter_t iter = fd_epoch_stake_reward_map_iter_init( stake_reward_map, stake_reward_pool );
     227          12 :        !fd_epoch_stake_reward_map_iter_done( iter, stake_reward_map, stake_reward_pool );
     228           9 :        iter = fd_epoch_stake_reward_map_iter_next( iter, stake_reward_map, stake_reward_pool ) ) {
     229             : 
     230           9 :     fd_epoch_stake_reward_t * stake_reward = fd_epoch_stake_reward_map_iter_ele( iter, stake_reward_map, stake_reward_pool );
     231             : 
     232           9 :     fd_siphash13_t   sip[1] = {0};
     233           9 :     fd_siphash13_t * hasher = fd_siphash13_init( sip, 0UL, 0UL );
     234           9 :     hasher = fd_siphash13_append( hasher, parent_blockhash->hash, sizeof(fd_hash_t) );
     235           9 :     fd_siphash13_append( hasher, (uchar const *)stake_reward->stake_pubkey.uc, sizeof(fd_pubkey_t) );
     236           9 :     ulong hash64 = fd_siphash13_fini( hasher );
     237             : 
     238             :     /* Now get the correct dlist based on the hash. */
     239           9 :     ulong partition_index = (ulong)((uint128)num_partitions * (uint128) hash64 / ((uint128)ULONG_MAX + 1));
     240             : 
     241           9 :     fd_epoch_stake_reward_dlist_t * partition_dlist = fd_epoch_rewards_get_partition_index( epoch_rewards, partition_index );
     242             : 
     243           9 :     fd_epoch_stake_reward_dlist_ele_push_tail( partition_dlist, stake_reward, stake_reward_pool );
     244           9 :   }
     245           3 : }
     246             : 
     247             : fd_epoch_stake_reward_t *
     248           9 : fd_epoch_rewards_iter_ele( fd_epoch_rewards_iter_t * iter ) {
     249           9 :   return fd_epoch_stake_reward_dlist_iter_ele( iter->iter, iter->dlist, iter->pool );
     250           9 : }
     251             : 
     252             : fd_epoch_rewards_iter_t *
     253             : fd_epoch_rewards_iter_init( fd_epoch_rewards_iter_t *  iter,
     254             :                             fd_epoch_rewards_t const * epoch_rewards,
     255           6 :                             ulong                      partition_idx ) {
     256           6 :   iter->pool  = fd_epoch_rewards_get_stake_reward_pool( epoch_rewards );
     257           6 :   iter->dlist = fd_epoch_rewards_get_partition_index( epoch_rewards, partition_idx );
     258           6 :   iter->iter  = fd_epoch_stake_reward_dlist_iter_fwd_init( iter->dlist, iter->pool );
     259           6 :   return iter;
     260           6 : }
     261             : 
     262             : int
     263          15 : fd_epoch_rewards_iter_done( fd_epoch_rewards_iter_t * iter ) {
     264          15 :   return fd_epoch_stake_reward_dlist_iter_done( iter->iter, iter->dlist, iter->pool );
     265          15 : }
     266             : 
     267             : void
     268           9 : fd_epoch_rewards_iter_next( fd_epoch_rewards_iter_t * iter ) {
     269           9 :   iter->iter = fd_epoch_stake_reward_dlist_iter_fwd_next( iter->iter, iter->dlist, iter->pool );
     270           9 : }

Generated by: LCOV version 1.14