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