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 */