LCOV - code coverage report
Current view: top level - flamenco/rewards - fd_epoch_rewards.h (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 47 47 100.0 %
Date: 2025-08-05 05:04:49 Functions: 13 1469 0.9 %

          Line data    Source code
       1             : #ifndef HEADER_fd_src_flamenco_rewards_fd_epoch_rewards_h
       2             : #define HEADER_fd_src_flamenco_rewards_fd_epoch_rewards_h
       3             : 
       4             : #include "../runtime/fd_runtime_const.h"
       5             : #include "fd_rewards_base.h"
       6             : 
       7             : FD_PROTOTYPES_BEGIN
       8             : 
       9             : /* fd_epoch_rewards_t is the main struct that stores the epoch rewards
      10             :    data. Specifically, the struct manages storing the stake account
      11             :    rewards which are distributed over many slots. The number of
      12             :    partitions are determined by a simple function on the number of stake
      13             :    accounts. The rewards distribution starts on the first block after
      14             :    an epoch boundary and the rewards for each partition is distributed
      15             :    during a single slot. The partitions and reward schedule are
      16             :    calculated during the epoch boundary and distributed after.
      17             : 
      18             :    fd_epoch_rewards_t is usually managed by the banks. It is only
      19             :    written to during the epoch boundary and is read-only after that. */
      20             : 
      21             : /* Some useful bounds to size out the epoch rewards struct. */
      22             : 
      23             : /* The max number of partitions is bounded by the the slots_per_epoch
      24             :    divided by the MAX_FACTOR_OF_REWARD_BLOCKS_IN_EPOCH.
      25             :    See hash_rewards_into_partitions() and
      26             :    Bank::get_reward_distribution_num_blocks().
      27             : 
      28             :    We can find a loose bound by assuming FD_BANKS_SLOTS_PER_EPOCH is the
      29             :    number of slots in an epoch, there can be:
      30             :    FD_BANKS_SLOTS_PER_EPOCH / MAX_FACTOR_OF_REWARD_BLOCKS_IN_EPOCH
      31             :    == 43200UL partitions.
      32             : 
      33             :    However, it is possible to find a tighter bound. If we assume that
      34             :    the max number of stake accounts is FD_BANKS_MAX_STAKE_ACCOUNTS,
      35             :    then the max number of partiitions is
      36             :    (FD_BANKS_MAX_STAKE_ACCOUNTS / (STAKE_ACCOUNT_STORES_PER_BLOCK + (FD_BANKS_MAX_STAKE_ACCOUNTS % STAKE_ACCOUNT_STORES_PER_BLOCK)))
      37             :    == 515UL partitions.
      38             : */
      39        9291 : #define FD_REWARDS_MAX_PARTITIONS (FD_RUNTIME_MAX_STAKE_ACCOUNTS / (STAKE_ACCOUNT_STORES_PER_BLOCK + (FD_RUNTIME_MAX_STAKE_ACCOUNTS % STAKE_ACCOUNT_STORES_PER_BLOCK)))
      40             : FD_STATIC_ASSERT( FD_REWARDS_MAX_PARTITIONS == 515UL, "incorrect FD_REWARDS_MAX_PARTITIONS" );
      41             : FD_STATIC_ASSERT( FD_REWARDS_MAX_PARTITIONS <= FD_RUNTIME_SLOTS_PER_EPOCH / MAX_FACTOR_OF_REWARD_BLOCKS_IN_EPOCH, "incorrect FD_REWARDS_MAX_PARTITIONS" );
      42             : 
      43             : /* The max of footprint of fd_epoch_stakes is variable depending on the
      44             :    number of stake accounts that are supported. However, the size can be
      45             :    bounded out assuming worst case with 3M stake accounts. The total
      46             :    struct contains the top level header struct, the pool, and the dlists
      47             : 
      48             :    fd_epoch_stake_reward_t: 4192 bytes + 64 bytes align       = 4224 bytes
      49             : 
      50             :    pool's private meta:     32 bytes   + 96 bytes align       = 128 bytes
      51             :    each pool member:        72 bytes   + 56 bytes align       = 128 bytes
      52             :    all pool members:        128 bytes  * 3M                   = 384 MB
      53             : 
      54             :    each dlist:              24 bytes for sizeof(DLIST_T)      = 24 bytes
      55             :    all dlists:              24 bytes   * 515 max partitions   = 12360 bytes
      56             : 
      57             :    total footprint:         4224 bytes + 384 MB + 12360 bytes = 384016584 bytes
      58             : */
      59             : #define FD_EPOCH_REWARDS_FOOTPRINT (384016584UL)
      60             : /* TODO: Add some static asserts to validate this calculation*/
      61             : 
      62         843 : #define FD_EPOCH_REWARDS_ALIGN (128UL)
      63             : 
      64           6 : #define FD_EPOCH_REWARDS_MAGIC (0x122400081001UL)
      65             : 
      66             : struct fd_epoch_stake_reward {
      67             :   ulong       prev;
      68             :   ulong       next;
      69             :   ulong       parent;
      70             :   fd_pubkey_t stake_pubkey;
      71             :   ulong       credits_observed;
      72             :   ulong       lamports;
      73             : };
      74             : typedef struct fd_epoch_stake_reward fd_epoch_stake_reward_t;
      75             : 
      76             : #define POOL_NAME fd_epoch_stake_reward_pool
      77          30 : #define POOL_T    fd_epoch_stake_reward_t
      78             : #include "../../util/tmpl/fd_pool.c"
      79             : 
      80             : #define DLIST_NAME  fd_epoch_stake_reward_dlist
      81             : #define DLIST_ELE_T fd_epoch_stake_reward_t
      82             : #include "../../util/tmpl/fd_dlist.c"
      83             : 
      84             : struct fd_epoch_rewards {
      85             :   ulong magic;
      86             : 
      87             :   /* Data representing the partitioned stake rewards */
      88             :   int   is_active_;
      89             :   ulong stake_account_max_;
      90             :   ulong starting_block_height_;
      91             :   ulong num_partitions_;
      92             :   ulong partitions_lengths_[FD_REWARDS_MAX_PARTITIONS];
      93             : 
      94             :   /* Result of total rewards distribution */
      95             : 
      96             :   /* Total rewards for the epoch (including both vote rewards and stake
      97             :      rewards) */
      98             :   ulong total_rewards_;
      99             :   /* total rewards points calculated for the current epoch, where points
     100             :      equals the sum of (delegated stake * credits observed) for all
     101             :      delegations */
     102             :   ulong distributed_rewards_;
     103             :   /* Stake rewards that still need to be distributed, grouped by
     104             :      partition */
     105             :   uint128 total_points_;
     106             : 
     107             :   /* This will be followed by a pool of fd_epoch_stake_reward_t. This
     108             :      pool will be sized out to FD_BANKS_MAX_STAKE_ACCOUNTS. */
     109             : 
     110             :   /* The pool will be followed by up to FD_REWARDS_MAX_PARTITIONS
     111             :      that will all need to be joined. */
     112             : 
     113             : };
     114             : typedef struct fd_epoch_rewards fd_epoch_rewards_t;
     115             : 
     116             : 
     117             : /* fd_epoch_rewards_align returns the alignment of the epoch rewards
     118             :    struct. */
     119             : 
     120             : ulong
     121             : fd_epoch_rewards_align( void );
     122             : 
     123             : /* fd_epoch_rewards_footprint returns the footprint of the epoch rewards
     124             :    struct. */
     125             : 
     126             : ulong
     127             : fd_epoch_rewards_footprint( ulong stake_account_max );
     128             : 
     129             : /* fd_epoch_rewards_new initializes the epoch_rewards struct. */
     130             : void *
     131             : fd_epoch_rewards_new( void * shmem, ulong stake_account_max );
     132             : 
     133             : /* fd_epoch_rewards_join returns a pointer to the epoch rewards struct
     134             :    that is stored in the shared memory. */
     135             : 
     136             : fd_epoch_rewards_t *
     137             : fd_epoch_rewards_join( void * shmem );
     138             : 
     139             : /* fd_epoch_rewards_leave returns a pointer to the epoch rewards struct
     140             :    that is stored in the shared memory. */
     141             : 
     142             : void *
     143             : fd_epoch_rewards_leave( fd_epoch_rewards_t const * epoch_rewards );
     144             : 
     145             : /* fd_epoch_rewards_delete unformats the epoch rewards struct and the
     146             :    memory that the struct manages.  */
     147             : 
     148             : void *
     149             : fd_epoch_rewards_delete( void * epoch_rewards );
     150             : 
     151             : /* fd_epoch_rewards_get_partition_index returns a pointer to the dlist
     152             :    of stake rewards for the given partition index. */
     153             : 
     154             : fd_epoch_stake_reward_dlist_t *
     155             : fd_epoch_rewards_get_partition_index( fd_epoch_rewards_t const * epoch_rewards, ulong idx );
     156             : 
     157             : /* fd_epoch_rewards_get_stake_reward_pool returns a pointer to the pool
     158             :    of stake rewards. */
     159             : 
     160             : fd_epoch_stake_reward_t *
     161             : fd_epoch_rewards_get_stake_reward_pool( fd_epoch_rewards_t const * epoch_rewards );
     162             : 
     163             : /* fd_epoch_rewards_hash_and_insert determines the hash partition that
     164             :    the stake pubkey belongs in and stores the pubkey along with the
     165             :    total amount of credits and lamports. */
     166             : 
     167             : int
     168             : fd_epoch_rewards_hash_and_insert( fd_epoch_rewards_t * epoch_rewards,
     169             :                                   fd_hash_t const *    parent_blockhash,
     170             :                                   fd_pubkey_t const *  pubkey,
     171             :                                   ulong                credits,
     172             :                                   ulong                lamports );
     173             : 
     174             : /* fd_epoch_rewards_get_distribution_partition_index determines the
     175             :    hash partition that the current block belongs in. */
     176             : 
     177             : ulong
     178             : fd_epoch_rewards_get_distribution_partition_index( fd_epoch_rewards_t const * epoch_rewards, ulong curr_block_height );
     179             : 
     180             : /* Simple inline mutator functions */
     181             : 
     182             : static void FD_FN_UNUSED
     183           3 : fd_epoch_rewards_set_active( fd_epoch_rewards_t * epoch_rewards, int is_active ) {
     184           3 :   epoch_rewards->is_active_ = is_active;
     185           3 : }
     186             : 
     187             : static void FD_FN_UNUSED
     188           3 : fd_epoch_rewards_set_starting_block_height( fd_epoch_rewards_t * epoch_rewards, ulong block_height ) {
     189           3 :   epoch_rewards->starting_block_height_ = block_height;
     190           3 : }
     191             : 
     192             : static void FD_FN_UNUSED
     193           6 : fd_epoch_rewards_set_num_partitions( fd_epoch_rewards_t * epoch_rewards, ulong num_partitions ) {
     194           6 :   if( FD_UNLIKELY( num_partitions>FD_REWARDS_MAX_PARTITIONS ) ) {
     195           3 :     FD_LOG_WARNING(( "num_partitions: %lu is greater than FD_REWARDS_MAX_PARTITIONS: %lu", num_partitions, FD_REWARDS_MAX_PARTITIONS ));
     196           3 :     return;
     197           3 :   }
     198           3 :   epoch_rewards->num_partitions_ = num_partitions;
     199           3 : }
     200             : 
     201             : static void FD_FN_UNUSED
     202           3 : fd_epoch_rewards_set_distributed_rewards( fd_epoch_rewards_t * epoch_rewards, ulong distributed_rewards ) {
     203           3 :   epoch_rewards->distributed_rewards_ = distributed_rewards;
     204           3 : }
     205             : 
     206             : static void FD_FN_UNUSED
     207           3 : fd_epoch_rewards_set_total_rewards( fd_epoch_rewards_t * epoch_rewards, ulong total_rewards ) {
     208           3 :   epoch_rewards->total_rewards_ = total_rewards;
     209           3 : }
     210             : 
     211             : static void FD_FN_UNUSED
     212           3 : fd_epoch_rewards_set_total_points( fd_epoch_rewards_t * epoch_rewards, uint128 total_points ) {
     213           3 :   epoch_rewards->total_points_ = total_points;
     214           3 : }
     215             : 
     216             : /* Simple inline accessor functions */
     217             : 
     218             : static int FD_FN_UNUSED
     219           9 : fd_epoch_rewards_is_active( fd_epoch_rewards_t const * epoch_rewards ) {
     220           9 :   return epoch_rewards->is_active_;
     221           9 : }
     222             : 
     223             : static ulong FD_FN_UNUSED
     224         312 : fd_epoch_rewards_get_num_partitions( fd_epoch_rewards_t const * epoch_rewards ) {
     225         312 :   return epoch_rewards->num_partitions_;
     226         312 : }
     227             : 
     228             : static ulong FD_FN_UNUSED
     229           9 : fd_epoch_rewards_get_starting_block_height( fd_epoch_rewards_t const * epoch_rewards ) {
     230           9 :   return epoch_rewards->starting_block_height_;
     231           9 : }
     232             : 
     233             : static ulong FD_FN_UNUSED
     234           6 : fd_epoch_rewards_get_exclusive_ending_block_height( fd_epoch_rewards_t const * epoch_rewards ) {
     235           6 :   return epoch_rewards->starting_block_height_ + epoch_rewards->num_partitions_;
     236           6 : }
     237             : 
     238             : static ulong FD_FN_UNUSED
     239           9 : fd_epoch_rewards_get_distributed_rewards( fd_epoch_rewards_t const * epoch_rewards ) {
     240           9 :   return epoch_rewards->distributed_rewards_;
     241           9 : }
     242             : 
     243             : static uint128 FD_FN_UNUSED
     244           9 : fd_epoch_rewards_get_total_points( fd_epoch_rewards_t const * epoch_rewards ) {
     245           9 :   return epoch_rewards->total_points_;
     246           9 : }
     247             : 
     248             : static ulong FD_FN_UNUSED
     249           9 : fd_epoch_rewards_get_total_rewards( fd_epoch_rewards_t const * epoch_rewards ) {
     250           9 :   return epoch_rewards->total_rewards_;
     251           9 : }
     252             : 
     253             : FD_PROTOTYPES_END
     254             : 
     255             : #endif /* HEADER_fd_src_flamenco_rewards_fd_epoch_rewards_h */

Generated by: LCOV version 1.14