Line data Source code
1 : #ifndef HEADER_fd_src_flamenco_rewards_fd_stake_rewards_h 2 : #define HEADER_fd_src_flamenco_rewards_fd_stake_rewards_h 3 : 4 : #include "../fd_flamenco_base.h" 5 : 6 : /* fd_stake_rewards is a fork aware structure that stores and keeps 7 : track of pending stake rewards for the purposes of partitioned epoch 8 : rewards that occurs after the epoch boundary. 9 : 10 : The access pattern is as follows: 11 : 1. Insertion/Hashing: This occurs at the epoch boundary after stake 12 : rewards are computed before rewards are distributed. The stake 13 : account along with corresponding lamports and credits observed are 14 : hashed into a rewards partition. These rewards will be paid out 15 : later. 16 : 2. Iteration: A partition is paid out per slot. All of the accounts 17 : in the partition are iterated over and the rewards are distributed 18 : to the stake accounts involved. 19 : 20 : The protocol level guarantees is just that there can be up to 43200 21 : rewards slots. There is no gap on the number of stake rewards paid 22 : out per slot. 23 : 24 : A naive approach with a worst case number of stake accounts (assume 25 : ~200M) and a reasonable amount of forks across the epoch boundary 26 : (assume 32) would require an element of size 48 (pubkey, lamports, and 27 : credits observed). So we would need a structure of size: 48 bytes * 28 : 200M accounts * 32 forks = 307GB of memory. This also doesn't involve 29 : any data to keep track of pool/map overhead. 30 : 31 : Instead we use the property that across forks almost every single 32 : stake account will have the same rewards. So we can use a shared 33 : index of (pubkey, stake, credit) entries to store the rewards for all 34 : forks. 35 : 36 : For each fork, we will need to keep track of what elements are in 37 : each partition. But each partition can be of unequal size so we use 38 : a singly linked list to store the elements in each partition. Each 39 : partition member will just contain a linked-list pointer and an index 40 : into the aforementioned index pool. When stake rewards are being paid 41 : out, the iterator will iterate through the linked list and dereference 42 : the index pool to get the pubkey and associated rewards. 43 : 44 : As a note, the structure is also only partially fork-aware. It safely 45 : assumes that the epoch boundary of a second epoch will not happen 46 : while the stake rewards are still being paid out of a first epoch. 47 : The protocol guarantees this because stake rewards must be paid out 48 : within the first 10% of an epoch. 49 : 50 : It is assumed that there will not be concurrent users of the stake 51 : rewards structure. The caller is expected to manage synchronization 52 : between threads. */ 53 : 54 15450 : #define FD_STAKE_REWARDS_ALIGN (128UL) 55 : 56 : struct fd_stake_rewards; 57 : typedef struct fd_stake_rewards fd_stake_rewards_t; 58 : 59 : FD_PROTOTYPES_BEGIN 60 : 61 : /* fd_stake_rewards_align is used to get the alignment for the stake 62 : rewards structure. */ 63 : 64 : ulong 65 : fd_stake_rewards_align( void ); 66 : 67 : /* fd_stake_rewards_footprint is used to get the footprint for the stake 68 : rewards structure given the max number of stake accounts, the max 69 : number of forks, and the expected number of stake accounts. The 70 : expected number of stake accounts is used to internally size out the 71 : map chain for the index. */ 72 : 73 : ulong 74 : fd_stake_rewards_footprint( ulong max_stake_accounts, 75 : ulong expected_stake_accs, 76 : ulong max_fork_width ); 77 : 78 : /* fd_stake_rewards_new creates a new stake rewards structure. */ 79 : 80 : void * 81 : fd_stake_rewards_new( void * shmem, 82 : ulong max_stake_accounts, 83 : ulong expected_stake_accs, 84 : ulong max_fork_width, 85 : ulong seed ); 86 : 87 : /* fd_stake_rewards_join joins the caller to the stake rewards 88 : structure. */ 89 : 90 : fd_stake_rewards_t * 91 : fd_stake_rewards_join( void * shmem ); 92 : 93 : /* fd_stake_rewards_clear resets the stake rewards structure to a 94 : post-new state. */ 95 : 96 : void 97 : fd_stake_rewards_clear( fd_stake_rewards_t * stake_rewards ); 98 : 99 : /* fd_stake_rewards_init initializes the stake rewards structure for a 100 : given fork. It should be used at the start of epoch reward 101 : calculation or recalculation. It returns a fork index. */ 102 : 103 : uchar 104 : fd_stake_rewards_init( fd_stake_rewards_t * stake_rewards, 105 : ulong epoch, 106 : fd_hash_t const * parent_blockhash, 107 : ulong starting_block_height, 108 : uint partitions_cnt ); 109 : 110 : /* fd_stake_rewards_insert inserts a new stake reward for a given 111 : fork. It adds it to the index and hashes it into the approporiate 112 : partition. */ 113 : 114 : void 115 : fd_stake_rewards_insert( fd_stake_rewards_t * stake_rewards, 116 : uchar fork_idx, 117 : fd_pubkey_t const * pubkey, 118 : ulong lamports, 119 : ulong credits_observed ); 120 : 121 : /* Iterator for iterating over the stake rewards for a given fork and 122 : partition. The caller should not interleave any other iteration or 123 : modification of the stake rewards structure while iterating. 124 : 125 : Example use: 126 : for( fd_stake_rewards_iter_init( stake_rewards, fork_idx, partition_idx ); 127 : !fd_stake_rewards_iter_done( stake_rewards, fork_idx ); 128 : fd_stake_rewards_iter_next( stake_rewards, fork_idx ) ) { 129 : fd_pubkey_t pubkey; 130 : ulong lamports; 131 : ulong credits_observed; 132 : fd_stake_rewards_iter_ele( iter, &pubkey, &lamports, &credits_observed ); 133 : } 134 : */ 135 : 136 : void 137 : fd_stake_rewards_iter_init( fd_stake_rewards_t * stake_rewards, 138 : uchar fork_idx, 139 : uint partition_idx ); 140 : 141 : void 142 : fd_stake_rewards_iter_next( fd_stake_rewards_t * stake_rewards, 143 : uchar fork_idx ); 144 : 145 : int 146 : fd_stake_rewards_iter_done( fd_stake_rewards_t * stake_rewards ); 147 : 148 : void 149 : fd_stake_rewards_iter_ele( fd_stake_rewards_t * stake_rewards, 150 : uchar fork_idx, 151 : fd_pubkey_t * pubkey_out, 152 : ulong * lamports_out, 153 : ulong * credits_observed_out ); 154 : 155 : /* Simple accessors for stake rewards information. */ 156 : 157 : ulong 158 : fd_stake_rewards_total_rewards( fd_stake_rewards_t const * stake_rewards, 159 : uchar fork_idx ); 160 : 161 : uint 162 : fd_stake_rewards_num_partitions( fd_stake_rewards_t const * stake_rewards, 163 : uchar fork_idx ); 164 : 165 : ulong 166 : fd_stake_rewards_starting_block_height( fd_stake_rewards_t const * stake_rewards, 167 : uchar fork_idx ); 168 : 169 : ulong 170 : fd_stake_rewards_exclusive_ending_block_height( fd_stake_rewards_t const * stake_rewards, 171 : uchar fork_idx ); 172 : 173 : FD_PROTOTYPES_END 174 : 175 : #endif /* HEADER_fd_src_flamenco_rewards_fd_stake_rewards_h */