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