LCOV - code coverage report
Current view: top level - flamenco/rewards - fd_epoch_rewards.h (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 7 7 100.0 %
Date: 2026-01-23 05:02:40 Functions: 1 91 1.1 %

          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_RUNTIME_SLOTS_PER_EPOCH is the
      29             :    number of slots in an epoch, there can be:
      30             :    FD_RUNTIME_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_RUNTIME_MAX_STAKE_ACCOUNTS,
      35             :    then the max number of partitions is
      36             :    div_ceil(FD_RUNTIME_MAX_STAKE_ACCOUNTS, STAKE_ACCOUNT_STORES_PER_BLOCK)
      37             :    == FD_RUNTIME_MAX_STAKE_ACCOUNTS / STAKE_ACCOUNT_STORES_PER_BLOCK
      38             :       + (FD_RUNTIME_MAX_STAKE_ACCOUNTS % STAKE_ACCOUNT_STORES_PER_BLOCK != 0)
      39             :    == 733UL partitions.
      40             : */
      41      497652 : #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 != 0))
      42             : FD_STATIC_ASSERT( FD_REWARDS_MAX_PARTITIONS == 733, "incorrect FD_REWARDS_MAX_PARTITIONS" );
      43             : FD_STATIC_ASSERT( FD_REWARDS_MAX_PARTITIONS <= FD_RUNTIME_SLOTS_PER_EPOCH / MAX_FACTOR_OF_REWARD_BLOCKS_IN_EPOCH, "incorrect FD_REWARDS_MAX_PARTITIONS" );
      44             : 
      45             : /* The max of footprint of fd_epoch_rewards is variable depending on the
      46             :    number of stake accounts that are supported. However, the size can be
      47             :    bounded out assuming worst case with 3M stake accounts. The total
      48             :    struct contains the top level header struct, the pool, and the dlists
      49             : 
      50             :    fd_epoch_rewards_t                                         = 5984 bytes
      51             : 
      52             :    pool's private meta:     32 bytes   + 96 bytes align       = 128 bytes
      53             :    all pool members:        64 bytes   * 3M                   = 192 MB
      54             :    total pool footprint:                                      = 192,000,128 bytes
      55             : 
      56             :    map footprint:           align_up( sizeof(MAP_T) + chain_cnt*sizeof(MAP_IDX_T), alignof(MAP_T) )
      57             :    chain_cnt:               2097152 chains is the largest power of 2 less than 3M.
      58             :    sizeof(MAP_T):           24 bytes
      59             :    alignof(MAP_T):          8 bytes
      60             :                             24 + 2097152 * sizeof(uint)      = 8,388,632 bytes
      61             : 
      62             : 
      63             :    each dlist:              16 bytes for sizeof(DLIST_T)      = 16 bytes
      64             :    all dlists:              16 bytes   * 733 max partitions   = 11,728 bytes
      65             : 
      66             :    total footprint:         5,984 bytes + 192,000,128 bytes + 8,388,632 bytes + 11,728 bytes = 200,406,472 bytes
      67             :    total footprint + align: align up to 128 bytes = 200,406,528 bytes
      68             : */
      69           6 : #define FD_EPOCH_REWARDS_FOOTPRINT (200406528UL)
      70             : 
      71        5364 : #define FD_EPOCH_REWARDS_ALIGN (128UL)
      72             : 
      73         324 : #define FD_EPOCH_REWARDS_MAGIC (0x122400081001UL)
      74             : 
      75             : struct fd_epoch_stake_reward {
      76             :   fd_pubkey_t stake_pubkey;
      77             :   ulong       credits_observed;
      78             :   ulong       lamports;
      79             :   /* Internal pointers for pool, dlist, and map. */
      80             :   uint        prev;
      81             :   uint        next;
      82             :   uint        parent;
      83             :   uint        next_map;
      84             : };
      85             : typedef struct fd_epoch_stake_reward fd_epoch_stake_reward_t;
      86             : 
      87             : /* TODO: Need to move the dlist into the .c file.  There needs to be a
      88             :    way to forward declare the iterator (see fd_map.h). */
      89             : 
      90             : #define DLIST_NAME  fd_epoch_stake_reward_dlist
      91             : #define DLIST_ELE_T fd_epoch_stake_reward_t
      92             : #define DLIST_IDX_T uint
      93             : #include "../../util/tmpl/fd_dlist.c"
      94             : 
      95             : struct fd_epoch_rewards_iter {
      96             :   fd_epoch_stake_reward_t *          pool;
      97             :   void *                             dlist;
      98             :   fd_epoch_stake_reward_dlist_iter_t iter;
      99             : };
     100             : typedef struct fd_epoch_rewards_iter fd_epoch_rewards_iter_t;
     101             : 
     102             : struct fd_epoch_rewards {
     103             :   ulong magic;
     104             : 
     105             :   /* Data representing the partitioned stake rewards */
     106             :   ulong stake_account_max;
     107             :   ulong starting_block_height;
     108             :   ulong num_partitions;
     109             :   ulong partitions_lengths[FD_REWARDS_MAX_PARTITIONS];
     110             : 
     111             :   /* Result of total rewards distribution */
     112             : 
     113             :   /* Total rewards for the epoch (including both vote rewards and stake
     114             :      rewards) */
     115             :   ulong total_rewards;
     116             : 
     117             :   /* total rewards points calculated for the current epoch, where points
     118             :      equals the sum of (delegated stake * credits observed) for all
     119             :      delegations */
     120             :   ulong distributed_rewards;
     121             : 
     122             :   /* Stake rewards that still need to be distributed, grouped by
     123             :      partition */
     124             :   fd_w_u128_t total_points;
     125             : 
     126             :   /* Total stake rewards to distribute as calculated during the epoch
     127             :      boundary */
     128             :   ulong total_stake_rewards;
     129             : 
     130             :   /* Total number of stake accounts that have rewards to distribute */
     131             :   ulong stake_rewards_cnt;
     132             : 
     133             :   /* Internal pointers for pool, dlist, and map. */
     134             :   ulong pool_offset;
     135             :   ulong map_offset;
     136             :   ulong dlists_offset;
     137             : 
     138             :   /* This will be followed by a pool of fd_epoch_stake_reward_t. This
     139             :      pool will be sized out to FD_RUNTIME_MAX_STAKE_ACCOUNTS. */
     140             : 
     141             :   /* The pool will be followed by up to FD_REWARDS_MAX_PARTITIONS
     142             :      that will all need to be joined. */
     143             : 
     144             : };
     145             : typedef struct fd_epoch_rewards fd_epoch_rewards_t;
     146             : 
     147             : 
     148             : /* fd_epoch_rewards_align returns the alignment of the epoch rewards
     149             :    struct. */
     150             : 
     151             : ulong
     152             : fd_epoch_rewards_align( void );
     153             : 
     154             : /* fd_epoch_rewards_footprint returns the footprint of the epoch rewards
     155             :    struct. */
     156             : 
     157             : ulong
     158             : fd_epoch_rewards_footprint( ulong stake_account_max );
     159             : 
     160             : /* fd_epoch_rewards_new initializes the epoch_rewards struct. */
     161             : void *
     162             : fd_epoch_rewards_new( void * shmem,
     163             :                       ulong  stake_account_max,
     164             :                       ulong  seed );
     165             : 
     166             : /* fd_epoch_rewards_join returns a pointer to the epoch rewards struct
     167             :    that is stored in the shared memory. */
     168             : 
     169             : fd_epoch_rewards_t *
     170             : fd_epoch_rewards_join( void * shmem );
     171             : 
     172             : /* fd_epoch_rewards_leave returns a pointer to the epoch rewards struct
     173             :    that is stored in the shared memory. */
     174             : 
     175             : void *
     176             : fd_epoch_rewards_leave( fd_epoch_rewards_t const * epoch_rewards );
     177             : 
     178             : /* fd_epoch_rewards_delete unformats the epoch rewards struct and the
     179             :    memory that the struct manages.  */
     180             : 
     181             : void *
     182             : fd_epoch_rewards_delete( void * epoch_rewards );
     183             : 
     184             : /* fd_epoch_rewards_init resets the epoch rewards struct to the initial
     185             :    state given a valid local join. */
     186             : 
     187             : void
     188             : fd_epoch_rewards_init( fd_epoch_rewards_t * epoch_rewards );
     189             : 
     190             : /* fd_epoch_rewards_insert stores the rewards for a given stake account
     191             :    into the data structure. */
     192             : 
     193             : void
     194             : fd_epoch_rewards_insert( fd_epoch_rewards_t * epoch_rewards,
     195             :                          fd_pubkey_t const *  pubkey,
     196             :                          ulong                credits,
     197             :                          ulong                lamports );
     198             : 
     199             : /* fd_epoch_rewards_hash_into_partitions hashes all of the stake
     200             :    accounts into the appropriate partitions. */
     201             : 
     202             : void
     203             : fd_epoch_rewards_hash_into_partitions( fd_epoch_rewards_t * epoch_rewards,
     204             :                                        fd_hash_t const *    parent_blockhash,
     205             :                                        ulong                num_partitions );
     206             : 
     207             : /* fd_epoch_rewards_get_distribution_partition_index determines the
     208             :    hash partition that the current block belongs in. */
     209             : 
     210             : ulong
     211             : fd_epoch_rewards_get_distribution_partition_index( fd_epoch_rewards_t const * epoch_rewards,
     212             :                                                    ulong                      curr_block_height );
     213             : 
     214             : /* fd_epoch_rewards_get_exclusive_ending_block_height returns the
     215             :    block height that the last partition ends at. */
     216             : 
     217             : static inline ulong
     218          24 : fd_epoch_rewards_get_exclusive_ending_block_height( fd_epoch_rewards_t const * epoch_rewards ) {
     219          24 :   return epoch_rewards->starting_block_height + epoch_rewards->num_partitions;
     220          24 : }
     221             : 
     222             : /* Iterator API for epoch rewards. The iterator is initialized with a
     223             :    call to fd_epoch_rewards_iter_init. The caller is responsible for
     224             :    managing the memory for the iterator. It is safe to call
     225             :    fd_epoch_rewards_iter_next if the result of
     226             :    fd_epoch_rewards_iter_done() ==0. It is safe to call
     227             :    fd_epoch_rewards_iter_ele() to get the current epoch reward.
     228             :    Elements that are iterated over are not safe to modify.
     229             : 
     230             :    Under the hood, the iterator is just a wrapper over the iterator used
     231             :    by the underlying dlist.
     232             : */
     233             : 
     234             : fd_epoch_stake_reward_t *
     235             : fd_epoch_rewards_iter_ele( fd_epoch_rewards_iter_t * iter );
     236             : 
     237             : fd_epoch_rewards_iter_t *
     238             : fd_epoch_rewards_iter_init( fd_epoch_rewards_iter_t *  iter,
     239             :                             fd_epoch_rewards_t const * epoch_rewards,
     240             :                             ulong                      partition_idx );
     241             : 
     242             : int
     243             : fd_epoch_rewards_iter_done( fd_epoch_rewards_iter_t * iter );
     244             : 
     245             : void
     246             : fd_epoch_rewards_iter_next( fd_epoch_rewards_iter_t * iter );
     247             : 
     248             : FD_PROTOTYPES_END
     249             : 
     250             : #endif /* HEADER_fd_src_flamenco_rewards_fd_epoch_rewards_h */

Generated by: LCOV version 1.14