LCOV - code coverage report
Current view: top level - flamenco/rewards - fd_stake_rewards.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 158 188 84.0 %
Date: 2026-04-02 06:08:24 Functions: 19 19 100.0 %

          Line data    Source code
       1             : #include "fd_stake_rewards.h"
       2             : #include "fd_rewards_base.h"
       3             : #include "../../ballet/siphash13/fd_siphash13.h"
       4             : 
       5             : #define MAX_SUPPORTED_FORKS (128UL)
       6             : 
       7         369 : #define FD_STAKE_REWARDS_MAGIC (0xF17EDA2CE757A4E0) /* FIREDANCER STAKE V0 */
       8             : 
       9             : struct index_key {
      10             :   fd_pubkey_t pubkey;
      11             :   ulong       lamports;
      12             :   ulong       credits_observed;
      13             : };
      14             : typedef struct index_key index_key_t;
      15             : 
      16             : struct index_ele {
      17             :   union {
      18             :     struct {
      19             :       fd_pubkey_t pubkey;
      20             :       ulong       lamports;
      21             :       ulong       credits_observed;
      22             :     };
      23             :     index_key_t index_key;
      24             :   };
      25             :   uint next;
      26             : };
      27             : typedef struct index_ele index_ele_t;
      28             : 
      29             : #define MAP_NAME               index_map
      30             : #define MAP_KEY_T              index_key_t
      31             : #define MAP_ELE_T              index_ele_t
      32          27 : #define MAP_KEY                index_key
      33           0 : #define MAP_KEY_EQ(k0,k1)      (!memcmp( k0, k1, sizeof(index_key_t) ))
      34          54 : #define MAP_KEY_HASH(key,seed) (fd_hash( seed, key, sizeof(index_key_t) ))
      35          27 : #define MAP_NEXT               next
      36         519 : #define MAP_IDX_T              uint
      37             : #include "../../util/tmpl/fd_map_chain.c"
      38             : 
      39             : struct fork {
      40             :   int next;
      41             : };
      42             : typedef struct fork fork_t;
      43             : 
      44             : #define POOL_NAME  fork_pool
      45         738 : #define POOL_T     fork_t
      46         849 : #define POOL_NEXT  next
      47             : #define POOL_IDX_T int
      48             : #include "../../util/tmpl/fd_pool.c"
      49             : 
      50             : struct partition_ele  {
      51             :   uint index;
      52             :   uint next;
      53             : };
      54             : typedef struct partition_ele partition_ele_t;
      55             : 
      56             : struct fork_info {
      57             :   uint  ele_cnt;
      58             :   uint  partition_cnt;
      59             :   uint  partition_idxs_head[MAX_PARTITIONS_PER_EPOCH];
      60             :   uint  partition_idxs_tail[MAX_PARTITIONS_PER_EPOCH];
      61             :   ulong starting_block_height;
      62             :   ulong total_stake_rewards;
      63             : 
      64             : };
      65             : typedef struct fork_info fork_info_t;
      66             : 
      67             : struct fd_stake_rewards {
      68             :   ulong       magic;
      69             :   uint        total_ele_used;
      70             :   ulong       max_stake_accounts;
      71             :   fork_info_t fork_info[MAX_SUPPORTED_FORKS];
      72             :   ulong       fork_pool_offset;
      73             :   ulong       index_pool_offset;
      74             :   ulong       index_map_offset;
      75             :   ulong       partitions_offset;
      76             :   ulong       epoch;
      77             : 
      78             :   /* Temporary storage for the current stake reward being computed. */
      79             :   fd_hash_t parent_blockhash;
      80             :   uint      iter_curr_fork_idx;
      81             : };
      82             : typedef struct fd_stake_rewards fd_stake_rewards_t;
      83             : 
      84             : static inline fork_t *
      85          69 : get_fork_pool( fd_stake_rewards_t const * stake_rewards ) {
      86          69 :   return fd_type_pun( (uchar *)stake_rewards + stake_rewards->fork_pool_offset );
      87          69 : }
      88             : static inline index_ele_t *
      89          33 : get_index_pool( fd_stake_rewards_t const * stake_rewards ) {
      90          33 :   return fd_type_pun( (uchar *)stake_rewards + stake_rewards->index_pool_offset );
      91          33 : }
      92             : static inline index_map_t *
      93          96 : get_index_map( fd_stake_rewards_t const * stake_rewards ) {
      94          96 :   return fd_type_pun( (uchar *)stake_rewards + stake_rewards->index_map_offset );
      95          96 : }
      96             : 
      97             : static inline partition_ele_t *
      98             : get_partition_ele( fd_stake_rewards_t const * stake_rewards,
      99             :                    uchar                      fork_idx,
     100          39 :                    uint                       ele_cnt ) {
     101             : 
     102          39 :   return fd_type_pun( (uchar *)stake_rewards + stake_rewards->partitions_offset +
     103          39 :                       (fork_idx * stake_rewards->max_stake_accounts * sizeof(partition_ele_t)) +
     104          39 :                       (ele_cnt * sizeof(partition_ele_t)) );
     105          39 : }
     106             : 
     107             : ulong
     108        9558 : fd_stake_rewards_align( void ) {
     109        9558 :   return FD_STAKE_REWARDS_ALIGN;
     110        9558 : }
     111             : 
     112             : ulong
     113             : fd_stake_rewards_footprint( ulong max_stake_accounts,
     114             :                             ulong expected_stake_accs,
     115        1470 :                             ulong max_fork_width ) {
     116        1470 :   ulong map_chain_cnt = index_map_chain_cnt_est( expected_stake_accs );
     117             : 
     118        1470 :   ulong l = FD_LAYOUT_INIT;
     119        1470 :   l  = FD_LAYOUT_APPEND( l, fd_stake_rewards_align(),  sizeof(fd_stake_rewards_t) );
     120        1470 :   l =  FD_LAYOUT_APPEND( l, fork_pool_align(),         fork_pool_footprint( max_fork_width ) );
     121        1470 :   l  = FD_LAYOUT_APPEND( l, alignof(index_ele_t),      sizeof(index_ele_t) * max_stake_accounts );
     122        1470 :   l  = FD_LAYOUT_APPEND( l, index_map_align(),         index_map_footprint( map_chain_cnt ) );
     123        1470 :   l  = FD_LAYOUT_APPEND( l, alignof(partition_ele_t),  max_fork_width * max_stake_accounts * sizeof(partition_ele_t) );
     124             : 
     125             :   /* we take advantage of the fact that the number of partitions * 8192
     126             :      is always == fd_ulong_align_up( max_stake_accounts, 8192UL ) */
     127             : 
     128        1470 :   return FD_LAYOUT_FINI( l, fd_stake_rewards_align() );
     129        1470 : }
     130             : 
     131             : void *
     132             : fd_stake_rewards_new( void * shmem,
     133             :                       ulong  max_stake_accounts,
     134             :                       ulong  expected_stake_accs,
     135             :                       ulong  max_fork_width,
     136         369 :                       ulong  seed ) {
     137         369 :   if( FD_UNLIKELY( !shmem ) ) {
     138           0 :     FD_LOG_WARNING(( "NULL shmem" ));
     139           0 :     return NULL;
     140           0 :   }
     141         369 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)shmem, fd_stake_rewards_align() ) ) ) {
     142           0 :     FD_LOG_WARNING(( "misaligned shmem" ));
     143           0 :     return NULL;
     144           0 :   }
     145             : 
     146         369 :   ulong map_chain_cnt = index_map_chain_cnt_est( expected_stake_accs );
     147             : 
     148         369 :   FD_SCRATCH_ALLOC_INIT( l, shmem );
     149         369 :   fd_stake_rewards_t * stake_rewards  = FD_SCRATCH_ALLOC_APPEND( l, fd_stake_rewards_align(), sizeof(fd_stake_rewards_t) );
     150         369 :   void *               fork_pool_mem  = FD_SCRATCH_ALLOC_APPEND( l, fork_pool_align(),        fork_pool_footprint( max_fork_width ) );
     151         369 :   void *               index_pool_mem = FD_SCRATCH_ALLOC_APPEND( l, alignof(index_ele_t),     sizeof(index_ele_t) * max_stake_accounts );
     152         369 :   void *               index_map_mem  = FD_SCRATCH_ALLOC_APPEND( l, index_map_align(),        index_map_footprint( map_chain_cnt ) );
     153         369 :   void *               partitions_mem = FD_SCRATCH_ALLOC_APPEND( l, alignof(partition_ele_t), max_fork_width * max_stake_accounts * sizeof(partition_ele_t) );
     154             : 
     155           0 :   fork_t * fork_pool = fork_pool_join( fork_pool_new( fork_pool_mem, max_fork_width ) );
     156         369 :   if( FD_UNLIKELY( !fork_pool ) ) {
     157           0 :     FD_LOG_WARNING(( "Failed to create fork pool" ));
     158           0 :     return NULL;
     159           0 :   }
     160         369 :   stake_rewards->fork_pool_offset = (ulong)fork_pool - (ulong)shmem;
     161             : 
     162         369 :   stake_rewards->index_pool_offset = (ulong)index_pool_mem - (ulong)shmem;
     163             : 
     164         369 :   index_map_t * index_map = index_map_join( index_map_new( index_map_mem, map_chain_cnt, seed ) );
     165         369 :   if( FD_UNLIKELY( !index_map ) ) {
     166           0 :     FD_LOG_WARNING(( "Failed to create index map" ));
     167           0 :     return NULL;
     168           0 :   }
     169         369 :   stake_rewards->index_map_offset   = (ulong)index_map - (ulong)shmem;
     170         369 :   stake_rewards->partitions_offset  = (ulong)partitions_mem - (ulong)shmem;
     171         369 :   stake_rewards->max_stake_accounts = max_stake_accounts;
     172         369 :   stake_rewards->epoch              = ULONG_MAX;
     173         369 :   stake_rewards->total_ele_used     = 0UL;
     174             : 
     175         369 :   FD_COMPILER_MFENCE();
     176         369 :   FD_VOLATILE( stake_rewards->magic ) = FD_STAKE_REWARDS_MAGIC;
     177         369 :   FD_COMPILER_MFENCE();
     178             : 
     179         369 :   return shmem;
     180         369 : }
     181             : 
     182             : fd_stake_rewards_t *
     183         738 : fd_stake_rewards_join( void * shmem ) {
     184         738 :   if( FD_UNLIKELY( !shmem ) ) {
     185           0 :     FD_LOG_WARNING(( "NULL shmem" ));
     186           0 :     return NULL;
     187           0 :   }
     188             : 
     189         738 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)shmem, fd_stake_rewards_align() ) ) ) {
     190           0 :     FD_LOG_WARNING(( "misaligned shmem" ));
     191           0 :     return NULL;
     192           0 :   }
     193             : 
     194         738 :   fd_stake_rewards_t * stake_rewards = (fd_stake_rewards_t *)shmem;
     195         738 :   if( FD_UNLIKELY( stake_rewards->magic != FD_STAKE_REWARDS_MAGIC ) ) {
     196           0 :     FD_LOG_WARNING(( "Invalid stake rewards magic" ));
     197           0 :     return NULL;
     198           0 :   }
     199         738 :   return stake_rewards;
     200         738 : }
     201             : 
     202             : void
     203          42 : fd_stake_rewards_clear( fd_stake_rewards_t * stake_rewards ) {
     204          42 :   fork_pool_reset( get_fork_pool( stake_rewards ) );
     205          42 :   index_map_reset( get_index_map( stake_rewards ) );
     206          42 :   stake_rewards->epoch          = ULONG_MAX;
     207          42 :   stake_rewards->total_ele_used = 0UL;
     208          42 : }
     209             : 
     210             : uchar
     211             : fd_stake_rewards_init( fd_stake_rewards_t * stake_rewards,
     212             :                        ulong                epoch,
     213             :                        fd_hash_t const *    parent_blockhash,
     214             :                        ulong                starting_block_height,
     215          27 :                        uint                 partitions_cnt ) {
     216          27 :   index_map_t * index_map = get_index_map( stake_rewards );
     217          27 :   fork_t *      fork_pool = get_fork_pool( stake_rewards );
     218             : 
     219             :   /* If this is the first reference to the stake rewards, we need to
     220             :      reset the backing map and pool all the forks will share. */
     221          27 :   if( FD_LIKELY( stake_rewards->epoch!=epoch ) ) {
     222          27 :     fork_pool_reset( fork_pool );
     223          27 :     index_map_reset( index_map );
     224          27 :     stake_rewards->epoch          = epoch;
     225          27 :     stake_rewards->total_ele_used = 0UL;
     226          27 :   }
     227             : 
     228          27 :   uchar fork_idx = (uchar)fork_pool_idx_acquire( fork_pool );
     229             : 
     230          27 :   stake_rewards->parent_blockhash = *parent_blockhash;
     231             : 
     232          27 :   stake_rewards->fork_info[fork_idx].partition_cnt         = partitions_cnt;
     233          27 :   stake_rewards->fork_info[fork_idx].starting_block_height = starting_block_height;
     234          27 :   stake_rewards->fork_info[fork_idx].ele_cnt               = 0UL;
     235          27 :   stake_rewards->fork_info[fork_idx].total_stake_rewards   = 0UL;
     236          27 :   memset( stake_rewards->fork_info[fork_idx].partition_idxs_head, 0xFF, sizeof(stake_rewards->fork_info[fork_idx].partition_idxs_head) );
     237          27 :   memset( stake_rewards->fork_info[fork_idx].partition_idxs_tail, 0xFF, sizeof(stake_rewards->fork_info[fork_idx].partition_idxs_tail) );
     238             : 
     239          27 :   return fork_idx;
     240          27 : }
     241             : 
     242             : void
     243             : fd_stake_rewards_insert( fd_stake_rewards_t * stake_rewards,
     244             :                          uchar                fork_idx,
     245             :                          fd_pubkey_t const *  pubkey,
     246             :                          ulong                lamports,
     247          27 :                          ulong                credits_observed ) {
     248          27 :   index_ele_t * index_ele = get_index_pool( stake_rewards );
     249          27 :   index_map_t * index_map = get_index_map( stake_rewards );
     250             : 
     251          27 :   index_key_t index_key = {
     252          27 :     .pubkey           = *pubkey,
     253          27 :     .lamports         = lamports,
     254          27 :     .credits_observed = credits_observed,
     255          27 :   };
     256             : 
     257          27 :   uint index = (uint)index_map_idx_query( index_map, &index_key, UINT_MAX, index_ele );
     258          27 :   if( FD_LIKELY( index==UINT_MAX ) ) {
     259          27 :     index = stake_rewards->total_ele_used;
     260          27 :     stake_rewards->total_ele_used++;
     261          27 :     if( FD_UNLIKELY( index>=stake_rewards->max_stake_accounts ) ) {
     262           0 :       FD_LOG_CRIT(( "invariant violation: index>=stake_rewards->max_stake_accounts" ));
     263           0 :     }
     264          27 :     index_ele_t * ele = (index_ele_t *)index_ele + index;
     265          27 :     ele->index_key = index_key;
     266          27 :     index_map_ele_insert( index_map, ele, index_ele );
     267          27 :   }
     268             : 
     269             :   /* We have an invariant that there can never be more than 8192 entries
     270             :      in a partition. */
     271          27 :   fd_siphash13_t   sip[1] = {0};
     272          27 :   fd_siphash13_t * hasher = fd_siphash13_init( sip, 0UL, 0UL );
     273          27 :   hasher = fd_siphash13_append( hasher, stake_rewards->parent_blockhash.hash, sizeof(fd_hash_t) );
     274          27 :   fd_siphash13_append( hasher, (uchar const *)pubkey->uc, sizeof(fd_pubkey_t) );
     275          27 :   ulong hash64 = fd_siphash13_fini( hasher );
     276             : 
     277          27 :   ulong partition_index = (ulong)((uint128)stake_rewards->fork_info[fork_idx].partition_cnt * (uint128) hash64 / ((uint128)ULONG_MAX + 1));
     278             : 
     279          27 :   uint curr_fork_len = stake_rewards->fork_info[fork_idx].ele_cnt;
     280          27 :   if( FD_UNLIKELY( curr_fork_len>=stake_rewards->max_stake_accounts ) ) {
     281           0 :     FD_LOG_CRIT(( "invariant violation: curr_fork_len>=stake_rewards->max_stake_accounts" ));
     282           0 :   }
     283             : 
     284          27 :   partition_ele_t * partition_ele = get_partition_ele( stake_rewards, fork_idx, curr_fork_len );
     285          27 :   partition_ele->index = index;
     286          27 :   partition_ele->next  = UINT_MAX;
     287             : 
     288          27 :   int is_first_ele = stake_rewards->fork_info[fork_idx].partition_idxs_head[partition_index] == UINT_MAX;
     289             : 
     290          27 :   if( FD_LIKELY( !is_first_ele ) ) {
     291           0 :     partition_ele_t * prev_partition_ele = get_partition_ele( stake_rewards, fork_idx, stake_rewards->fork_info[fork_idx].partition_idxs_tail[partition_index] );
     292           0 :     prev_partition_ele->next = curr_fork_len;
     293           0 :     stake_rewards->fork_info[fork_idx].partition_idxs_tail[partition_index] = curr_fork_len;
     294          27 :   } else {
     295          27 :     stake_rewards->fork_info[fork_idx].partition_idxs_head[partition_index] = curr_fork_len;
     296          27 :     stake_rewards->fork_info[fork_idx].partition_idxs_tail[partition_index] = curr_fork_len;
     297          27 :   }
     298             : 
     299          27 :   stake_rewards->fork_info[fork_idx].ele_cnt++;
     300          27 :   stake_rewards->fork_info[fork_idx].total_stake_rewards += lamports;
     301          27 : }
     302             : 
     303             : void
     304             : fd_stake_rewards_iter_init( fd_stake_rewards_t * stake_rewards,
     305             :                             uchar                fork_idx,
     306           6 :                             uint                 partition_idx ) {
     307           6 :   uint first_fork_idx = stake_rewards->fork_info[fork_idx].partition_idxs_head[partition_idx];
     308           6 :   stake_rewards->iter_curr_fork_idx = first_fork_idx;
     309           6 : }
     310             : 
     311             : void
     312             : fd_stake_rewards_iter_next( fd_stake_rewards_t * stake_rewards,
     313           6 :                             uchar                fork_idx ) {
     314           6 :   partition_ele_t * partition_ele = get_partition_ele( stake_rewards, fork_idx, stake_rewards->iter_curr_fork_idx );
     315           6 :   stake_rewards->iter_curr_fork_idx = partition_ele->next;
     316           6 : }
     317             : 
     318             : int
     319          12 : fd_stake_rewards_iter_done( fd_stake_rewards_t * stake_rewards ) {
     320          12 :   return stake_rewards->iter_curr_fork_idx == UINT_MAX;
     321          12 : }
     322             : 
     323             : void
     324             : fd_stake_rewards_iter_ele( fd_stake_rewards_t * stake_rewards,
     325             :                            uchar                fork_idx,
     326             :                            fd_pubkey_t *        pubkey_out,
     327             :                            ulong *              lamports_out,
     328           6 :                            ulong *              credits_observed_out ) {
     329           6 :   partition_ele_t * partition_ele = get_partition_ele( stake_rewards, fork_idx, stake_rewards->iter_curr_fork_idx );
     330             : 
     331           6 :   index_ele_t * index_ele = get_index_pool( stake_rewards ) + partition_ele->index;
     332           6 :   *pubkey_out = index_ele->index_key.pubkey;
     333           6 :   *lamports_out = index_ele->index_key.lamports;
     334           6 :   *credits_observed_out = index_ele->index_key.credits_observed;
     335           6 : }
     336             : 
     337             : ulong
     338             : fd_stake_rewards_total_rewards( fd_stake_rewards_t const * stake_rewards,
     339          27 :                                 uchar                      fork_idx ) {
     340          27 :   return stake_rewards->fork_info[fork_idx].total_stake_rewards;
     341          27 : }
     342             : 
     343             : uint
     344             : fd_stake_rewards_num_partitions( fd_stake_rewards_t const * stake_rewards,
     345          60 :                                  uchar                      fork_idx ) {
     346          60 :   return stake_rewards->fork_info[fork_idx].partition_cnt;
     347          60 : }
     348             : 
     349             : ulong
     350             : fd_stake_rewards_starting_block_height( fd_stake_rewards_t const * stake_rewards,
     351          33 :                                         uchar                      fork_idx ) {
     352          33 :   return stake_rewards->fork_info[fork_idx].starting_block_height;
     353          33 : }
     354             : 
     355             : ulong
     356             : fd_stake_rewards_exclusive_ending_block_height( fd_stake_rewards_t const * stake_rewards,
     357          33 :                                                 uchar                      fork_idx ) {
     358          33 :   return stake_rewards->fork_info[fork_idx].starting_block_height + stake_rewards->fork_info[fork_idx].partition_cnt;
     359          33 : }

Generated by: LCOV version 1.14