Line data Source code
1 : #ifndef HEADER_fd_src_flamenco_stakes_fd_vote_states_h 2 : #define HEADER_fd_src_flamenco_stakes_fd_vote_states_h 3 : 4 : #include "../../util/fd_util_base.h" 5 : #include "../../util/tmpl/fd_map.h" 6 : #include "../types/fd_types_custom.h" 7 : 8 3 : #define FD_VOTE_STATES_MAGIC (0xF17EDA2CE7601E70UL) /* FIREDANCER VOTER V0 */ 9 : 10 : /* fd_vote_states_t is a cache of vote accounts mapping the pubkey of 11 : a vote account to various information about the vote account 12 : including, stake, last vote slot/timestamp, and commission for the 13 : vote account. The vote states are safe to be used across multiple 14 : threads but concurrent reads/writes must be synchronized by the 15 : caller. 16 : 17 : In the runtime, there are 3 instances of fd_vote_states_t that are 18 : maintained and used at different points, notably around epoch reward 19 : and leader schedule calculations. The 3 instances are: 20 : 1. vote_states: This is the vote states for the current epoch. This 21 : is updated through the course of an epoch as vote accounts are 22 : updated. 23 : 2. vote_states_prev: This is the vote states as of the end of 24 : previous epoch E-1 if we are currently executing epoch E. 25 : This gets updated at the end of an epoch when vote_states are 26 : copied into vote_states_prev. 27 : 3. vote_states_prev_prev: This is the vote states as of the end of 28 : epoch E-2 if we are currently executing epoch E. This only gets 29 : updated at the end of an epoch when vote_states_prev is copied 30 : into vote_states_prev_prev. 31 : 32 : The implementation of fd_vote_states_t is a hash map which is backed 33 : by a memory pool. Callers are allowed to insert, replace, and remove 34 : entries from the map. 35 : 36 : In practice, fd_vote_states_t are updated in 3 cases: 37 : 1. They are initially populated from the versioned vote account 38 : stake accounts in the snapshot manifest. These are populated from 39 : the raw vote account data. This is done in a single pass over the 40 : vote account data. 41 : 2. The vote states for the current epoch can be updated after 42 : transaction execution. This is done for vote accounts that are 43 : referenced during a transaction. 44 : 3. Vote states are updated at the epoch boundary. The stake 45 : information for the vote states is refreshed at the boundary. 46 : 47 : The vote states in reality manage a few different sets of 48 : information about the vote account: 49 : - vote account state: state from the vote account data including 50 : the last vote slot/timestamp, commission, and node pubkey. This is 51 : used for clock sysvar and rewards calculations. 52 : - stake: stake as of the end of the previous epoch. This is used 53 : eventually for leader schedule calculations. The stake from epoch 54 : T-2 (stake_t_2) is used for the stake in clock calculations. 55 : - rewards: this information is only used at the epoch boundary. 56 : */ 57 : 58 0 : #define FD_VOTE_STATES_ALIGN (128UL) 59 : 60 : /* Agave defines the max number of epoch credits to store to be 64. 61 : https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v2.2.6/vote-interface/src/state/mod.rs#L37 */ 62 : #define EPOCH_CREDITS_MAX (64UL) 63 : 64 : struct fd_vote_state_credits { 65 : ulong credits_cnt; 66 : ushort epoch [ EPOCH_CREDITS_MAX ]; 67 : ulong credits [ EPOCH_CREDITS_MAX ]; 68 : ulong prev_credits[ EPOCH_CREDITS_MAX ]; 69 : }; 70 : typedef struct fd_vote_state_credits fd_vote_state_credits_t; 71 : 72 : struct fd_vote_state_ele { 73 : /* Internal pool/map use */ 74 : ulong idx; 75 : ulong next_; 76 : 77 : /* Vote account stake information which is derived from the stake 78 : delegations. This information is used for leader schedule 79 : calculation and clock stake-weighted median calculations. */ 80 : ulong stake; 81 : ulong stake_t_2; 82 : 83 : /* Vote account information which is derived from the vote account 84 : data and is used for clock timestamp calculations. */ 85 : fd_pubkey_t vote_account; 86 : fd_pubkey_t node_account; 87 : ulong last_vote_slot; 88 : long last_vote_timestamp; 89 : uchar commission; 90 : }; 91 : typedef struct fd_vote_state_ele fd_vote_state_ele_t; 92 : 93 : /* Forward declare map iterator API generated by fd_map_chain.c */ 94 : typedef struct fd_vote_state_map_private fd_vote_state_map_t; 95 : typedef struct fd_map_chain_iter fd_vote_state_map_iter_t; 96 : struct fd_vote_states_iter { 97 : fd_vote_state_map_t * map; 98 : fd_vote_state_ele_t * pool; 99 : fd_vote_state_map_iter_t iter; 100 : }; 101 : typedef struct fd_vote_states_iter fd_vote_states_iter_t; 102 : 103 : struct __attribute__((aligned(FD_VOTE_STATES_ALIGN))) fd_vote_states { 104 : ulong magic; 105 : ulong max_vote_accounts_; 106 : ulong pool_offset_; 107 : ulong map_offset_; 108 : }; 109 : typedef struct fd_vote_states fd_vote_states_t; 110 : 111 : /* This guarantees that the pool element alignment is at most 128UL. */ 112 : FD_STATIC_ASSERT(alignof(fd_vote_state_ele_t)<=FD_VOTE_STATES_ALIGN, unexpected pool element alignment); 113 : 114 : /* The static footprint of the vote states assumes that there are 115 : FD_RUNTIME_MAX_VOTE_ACCOUNTS. It also assumes worst case alignment 116 : for each struct. fd_vote_states_t is laid out as first the 117 : fd_vote_states_t struct, followed by a pool of fd_vote_state_ele_t 118 : structs, followed by a map of fd_vote_state_map_ele_t structs. 119 : The pool has FD_RUNTIME_MAX_VOTE_ACCOUNTS elements, and the map 120 : has a chain count deteremined by a call to 121 : fd_vote_states_chain_cnt_est. 122 : NOTE: the footprint is validated to be at least as large as the 123 : actual runtime-determined footprint (see test_vote_states.c) */ 124 : 125 0 : #define FD_VOTE_STATES_CHAIN_CNT_EST (32768UL) 126 : #define FD_VOTE_STATES_FOOTPRINT \ 127 : /* First, layout the struct with alignment */ \ 128 0 : sizeof(fd_vote_states_t) + alignof(fd_vote_states_t) + \ 129 0 : /* Now layout the pool's data footprint */ \ 130 0 : FD_VOTE_STATES_ALIGN + sizeof(fd_vote_state_ele_t) * FD_RUNTIME_MAX_VOTE_ACCOUNTS + \ 131 0 : /* Now layout the pool's meta footprint */ \ 132 0 : FD_VOTE_STATES_ALIGN + 128UL /* POOL_ALIGN */ + \ 133 0 : /* Now layout the map. We must make assumptions about the chain */ \ 134 0 : /* count to be equivalent to chain_cnt_est. */ \ 135 0 : FD_VOTE_STATES_ALIGN + 128UL /* MAP_ALIGN */ + (FD_VOTE_STATES_CHAIN_CNT_EST * sizeof(ulong)) 136 : 137 : FD_PROTOTYPES_BEGIN 138 : 139 : /* fd_vote_states_align returns the minimum alignment required for a 140 : vote states struct. */ 141 : 142 : FD_FN_CONST ulong 143 : fd_vote_states_align( void ); 144 : 145 : /* fd_vote_states_footprint returns the footprint of the vote states 146 : struct for a given amount of max vote accounts. */ 147 : 148 : FD_FN_CONST ulong 149 : fd_vote_states_footprint( ulong max_vote_accounts ); 150 : 151 : /* fd_vote_states_new creates a new vote states struct with a given 152 : number of max vote accounts and a seed. It formats a memory region 153 : which is sized based off of the number of vote accounts. */ 154 : 155 : void * 156 : fd_vote_states_new( void * mem, 157 : ulong max_vote_accounts, 158 : ulong seed ); 159 : 160 : /* fd_vote_states_join joins a vote states struct from a 161 : memory region. There can be multiple valid joins for a given memory 162 : region but the caller is responsible for accessing memory in a 163 : thread-safe manner. */ 164 : 165 : fd_vote_states_t * 166 : fd_vote_states_join( void * mem ); 167 : 168 : /* fd_vote_states_update inserts or updates the vote state corresponding 169 : to a given account. */ 170 : 171 : fd_vote_state_ele_t * 172 : fd_vote_states_update( fd_vote_states_t * vote_states, 173 : fd_pubkey_t const * vote_account ); 174 : 175 : /* fd_vote_states_update_from_account inserts or updates the vote state 176 : corresponding to a valid vote account. This is the same as 177 : fd_vote_states_update but is also responsible for decoding the vote 178 : account data into a versioned vote state object and extracing the 179 : commission and credits. Kills the client if the vote state cannot 180 : be decoded. */ 181 : 182 : fd_vote_state_ele_t * 183 : fd_vote_states_update_from_account( fd_vote_states_t * vote_states, 184 : fd_pubkey_t const * vote_account, 185 : uchar const * account_data, 186 : ulong account_data_len ); 187 : 188 : /* fd_vote_states_reset_stakes_t resets the stakes to 0 for each of the 189 : vote accounts in fd_vote_states_t. */ 190 : 191 : void 192 : fd_vote_states_reset_stakes( fd_vote_states_t * vote_states ); 193 : 194 : /* fd_vote_states_remove removes the vote state corresponding to a given 195 : account. Does nothing if the account does not exist. */ 196 : 197 : void 198 : fd_vote_states_remove( fd_vote_states_t * vote_states, 199 : fd_pubkey_t const * vote_account ); 200 : 201 : /* fd_vote_states_query returns the vote state corresponding to a given 202 : account. Returns NULL if the account does not exist. This function is 203 : safe for concurrent reads, but the caller needs to synchronize 204 : concurrent writers to the fd_vote_state_ele_t. */ 205 : 206 : fd_vote_state_ele_t * 207 : fd_vote_states_query( fd_vote_states_t const * vote_states, 208 : fd_pubkey_t const * vote_account ); 209 : 210 : /* fd_vote_states_query_const is the same as fd_vote_states but instead 211 : returns a const pointer. */ 212 : 213 : fd_vote_state_ele_t const * 214 : fd_vote_states_query_const( fd_vote_states_t const * vote_states, 215 : fd_pubkey_t const * vote_account ); 216 : 217 : /* fd_vote_states_max returns the maximum number of vote accounts that 218 : the vote states struct can support. */ 219 : 220 : ulong 221 : fd_vote_states_max( fd_vote_states_t const * vote_states ); 222 : 223 : /* fd_vote_states_cnt returns the number of vote states in the vote 224 : states struct. */ 225 : 226 : ulong 227 : fd_vote_states_cnt( fd_vote_states_t const * vote_states ); 228 : 229 : /* Iterator API for vote states. The iterator is initialized with a 230 : call to fd_vote_states_iter_init. The caller is responsible for 231 : managing the memory for the iterator. It is safe to call 232 : fd_vote_states_iter_next if the result of 233 : fd_vote_states_iter_done() ==0. It is safe to call 234 : fd_vote_states_iter_ele() to get the current vote state. As a note, 235 : it is safe to modify the vote state acquired from 236 : fd_vote_states_iter_ele() as long as the next_ field is not modified 237 : (which the caller should never do). It is unsafe to insert or remove 238 : fd_vote_state_ele_t from the vote states struct while iterating. 239 : 240 : Under the hood, the iterator is just a wrapper over the iterator in 241 : fd_map_chain.c. 242 : 243 : Example use: 244 : 245 : fd_vote_states_iter_t iter_[1]; 246 : for( fd_vote_states_iter_t * iter = fd_vote_states_iter_init( vote_states, iter_ ); !fd_vote_states_iter_done( iter ); fd_vote_states_iter_next( iter ) ) { 247 : fd_vote_state_ele_t * vote_state = fd_vote_states_iter_ele( iter ); 248 : // Do something with the vote state ... 249 : } 250 : */ 251 : 252 : fd_vote_state_ele_t * 253 : fd_vote_states_iter_ele( fd_vote_states_iter_t * iter ); 254 : 255 : fd_vote_states_iter_t * 256 : fd_vote_states_iter_init( fd_vote_states_iter_t * iter, 257 : fd_vote_states_t const * vote_states ); 258 : 259 : int 260 : fd_vote_states_iter_done( fd_vote_states_iter_t * iter ); 261 : 262 : void 263 : fd_vote_states_iter_next( fd_vote_states_iter_t * iter ); 264 : 265 : FD_PROTOTYPES_END 266 : 267 : #endif /* HEADER_fd_src_flamenco_stakes_fd_vote_states_h */