Line data Source code
1 : #ifndef HEADER_fd_src_flamenco_stakes_fd_stake_delegations_h 2 : #define HEADER_fd_src_flamenco_stakes_fd_stake_delegations_h 3 : 4 : #include "../rewards/fd_rewards_base.h" 5 : #include "../runtime/fd_cost_tracker.h" 6 : #include "../../util/tmpl/fd_map.h" 7 : 8 30 : #define FD_STAKE_DELEGATIONS_MAGIC (0xF17EDA2CE757A3E0) /* FIREDANCER STAKE V0 */ 9 : 10 : /* fd_stakes_delegations_t is a cache of stake accounts mapping the 11 : pubkey of the stake account to various information including 12 : stake, activation/deactivation epoch, corresponding vote_account, 13 : credits observed, and warmup cooldown rate. This is used to quickly 14 : iterate through all of the stake delegations in the system during 15 : epoch boundary reward calculations. 16 : 17 : The implementation of fd_stakes_delegations_t is a hash map which 18 : is backed by a memory pool. Callers are allowed to insert, replace, 19 : and remove entries from the map. 20 : 21 : fd_stakes_delegations_t can also exist in two modes: with and without 22 : tombstones. The mode is determined by the leave_tombstones flag 23 : passed to fd_stake_delegations_new. If tombstones are enabled, then 24 : calling fd_stake_delegations_remove will not remove the entry from 25 : the map, but rather set the is_tombstone flag to true. This is 26 : useful for delta updates where we want to keep the entry in the map 27 : for future reference. In practice, this struct is used in both modes 28 : by the bank. The stake delegations corresponding to each slot are 29 : stored in a delta struct which is used to update the main cache. 30 : 31 : There are some important invariants wrt fd_stake_delegations_t: 32 : 1. After execution has started, there will be no invalid stake 33 : accounts in the stake delegations struct. 34 : 2. The stake delegations struct can have valid delegations for vote 35 : accounts which no longer exist. 36 : 3. There are no stake accounts which are valid delegations which 37 : exist in the accounts database but not in fd_stake_delegations_t. 38 : 39 : In practice, fd_stakes_delegations_t are updated in 3 cases: 40 : 1. During bootup when the snapshot manifest is loaded in. The cache 41 : is also refreshed during the bootup process to ensure that the 42 : states are valid and up-to-date. 43 : 44 : The reason we can't populate the stake accounts from the cache 45 : is because the cache in the manifest is partially incomplete: 46 : all of the expected keys are there, but the values are not. 47 : Notably, the credits_observed field is not available until all of 48 : the accounts are loaded into the database. 49 : 50 : https://github.com/anza-xyz/agave/blob/v2.3.6/runtime/src/bank.rs#L1780-L1806 51 : 52 : 2. After transaction execution. If an update is made to a stake 53 : account, the updated state is reflected in the cache (or the entry 54 : is evicted). 55 : 3. During rewards distribution. Stake accounts are partitioned over 56 : several hundred slots where their rewards are distributed. In this 57 : case, the cache is updated to reflect each stake account post 58 : reward distribution. 59 : The stake accounts are read-only during the epoch boundary. */ 60 : 61 : /* The max number of stake accounts that can be modified in a current 62 : slot from transactions can be bounded based on CU consumption. The 63 : best strategy to maximize the max number of stake accounts modified 64 : in a single transaction. A stake program instruction can either 65 : modify one or two stake accounts. Stake program instructions that 66 : modify two stake accounts (merge/split) are assumed to be at least 2x 67 : as expensive as a stake program instruction that modifies one stake 68 : account. So we will assume that the most efficient strategy is to 69 : modify one stake account per instruction and have as many instruction 70 : as posssible in this transaction. We can have 63 stake program 71 : instructions in this transaction because one account will be the fee 72 : payer/signature and the other 63 are free to be writable accounts. 73 : 74 : Given the above: 75 : 100000000 - CUs per slot 76 : 64 - Max number of writable accounts per transaction. 77 : 63 - Max number of writable stake accounts per transaction. 78 : 720 - Cost of a signature 79 : 300 - Cost of a writable account lock. 80 : 6000 - Cost of a stake program instruction. 81 : 82 : We can have (100000000 / (720 + 300 * 64 + 6000 * 63)) = 251 83 : optimal stake program transactions per slot. With 63 stake accounts 84 : per transaction, we can have 251 * 63 = 15813 stake accounts modified 85 : in a slot. */ 86 : 87 15 : #define MAX_OPTIMAL_STAKE_ACCOUNTS_POSSIBLE_IN_TXN (FD_MAX_BLOCK_UNITS_SIMD_0286/(FD_WRITE_LOCK_UNITS * FD_RUNTIME_MAX_WRITABLE_ACCOUNTS_PER_TRANSACTION + FD_RUNTIME_MIN_STAKE_INSN_CUS * (FD_RUNTIME_MAX_WRITABLE_ACCOUNTS_PER_TRANSACTION - 1UL) + FD_PACK_COST_PER_SIGNATURE)) 88 : FD_STATIC_ASSERT(MAX_OPTIMAL_STAKE_ACCOUNTS_POSSIBLE_IN_TXN==251, "Incorrect MAX_STAKE_ACCOUNTS_POSSIBLE_IN_TXN"); 89 15 : #define MAX_STAKE_ACCOUNTS_POSSIBLE_IN_SLOT_FROM_TXNS (MAX_OPTIMAL_STAKE_ACCOUNTS_POSSIBLE_IN_TXN * (FD_RUNTIME_MAX_WRITABLE_ACCOUNTS_PER_TRANSACTION - 1UL)) 90 : FD_STATIC_ASSERT(MAX_STAKE_ACCOUNTS_POSSIBLE_IN_SLOT_FROM_TXNS==15813, "Incorrect MAX_STAKE_ACCOUNTS_PER_SLOT"); 91 : 92 : /* The static footprint of fd_stake_delegations_t when it is a delta 93 : is determined by the max total number of stake accounts that can get 94 : changed in a single slot. Stake accounts can get modified in two ways: 95 : 1. Through transactions. This bound is calculated using CU 96 : consumption as described above. 97 : 2. Through epoch rewards. This is a protocol-level bound is defined 98 : in fd_rewards_base.h and is the max number of stake accounts that 99 : can reside in a single reward partition. */ 100 : 101 15 : #define FD_STAKE_DELEGATIONS_MAX_PER_SLOT (MAX_STAKE_ACCOUNTS_POSSIBLE_IN_SLOT_FROM_TXNS + STAKE_ACCOUNT_STORES_PER_BLOCK) 102 : 103 : /* The static footprint of the vote states assumes that there are 104 : FD_RUNTIME_MAX_STAKE_ACCOUNTS. It also assumes worst case alignment 105 : for each struct. fd_stake_delegations_t is laid out as first the 106 : fd_stake_delegations_t struct, followed by a pool of 107 : fd_stake_delegation_t structs, followed by a map of 108 : fd_stake_delegation_map_ele_t structs. The pool has 109 : FD_RUNTIME_MAX_STAKE_ACCOUNTS elements, and the map has a chain count 110 : determined by a call to fd_stake_delegations_chain_cnt_est. 111 : NOTE: the footprint is validated to be at least as large as the 112 : actual runtime-determined footprint (see test_stake_delegations.c) */ 113 : 114 24 : #define FD_STAKE_DELEGATIONS_CHAIN_CNT_EST (2097152UL) 115 : #define FD_STAKE_DELEGATIONS_FOOTPRINT \ 116 : /* First, layout the struct with alignment */ \ 117 24 : sizeof(fd_stake_delegations_t) + alignof(fd_stake_delegations_t) + \ 118 24 : /* Now layout the pool's data footprint */ \ 119 24 : FD_STAKE_DELEGATIONS_ALIGN + sizeof(fd_stake_delegation_t) * FD_RUNTIME_MAX_STAKE_ACCOUNTS + \ 120 24 : /* Now layout the pool's meta footprint */ \ 121 24 : FD_STAKE_DELEGATIONS_ALIGN + 128UL /* POOL_ALIGN */ + \ 122 24 : /* Now layout the map. We must make assumptions about the chain */ \ 123 24 : /* count to be equivalent to chain_cnt_est. */ \ 124 24 : FD_STAKE_DELEGATIONS_ALIGN + 128UL /* MAP_ALIGN */ + (FD_STAKE_DELEGATIONS_CHAIN_CNT_EST * sizeof(ulong)) 125 : 126 : /* We need a footprint for the max amount of stake delegations that 127 : can be added in a single slot. We know that there can be up to 128 : 8192 writable accounts in a slot (bound determined from the cost 129 : tracker). Using the same calculation as above, we get 120 bytes per 130 : stake delegation with up to ~19K delegations we have a total 131 : footprint of ~2.5MB. */ 132 : 133 : #define FD_STAKE_DELEGATIONS_DELTA_CHAIN_CNT_EST (16384UL) 134 : #define FD_STAKE_DELEGATIONS_DELTA_FOOTPRINT \ 135 : /* First, layout the struct with alignment */ \ 136 : sizeof(fd_stake_delegations_t) + alignof(fd_stake_delegations_t) + \ 137 : /* Now layout the pool's data footprint */ \ 138 : FD_STAKE_DELEGATIONS_ALIGN + sizeof(fd_stake_delegation_t) * FD_STAKE_DELEGATIONS_MAX_PER_SLOT + \ 139 : /* Now layout the pool's meta footprint */ \ 140 : FD_STAKE_DELEGATIONS_ALIGN + 128UL /* POOL_ALIGN */ + \ 141 : /* Now layout the map. We must make assumptions about the chain */ \ 142 : /* count to be equivalent to chain_cnt_est. */ \ 143 : FD_STAKE_DELEGATIONS_ALIGN + 128UL /* MAP_ALIGN */ + (FD_STAKE_DELEGATIONS_DELTA_CHAIN_CNT_EST * sizeof(ulong)) 144 : 145 72 : #define FD_STAKE_DELEGATIONS_ALIGN (128UL) 146 : 147 : struct fd_stake_delegation { 148 : fd_pubkey_t stake_account; 149 : fd_pubkey_t vote_account; 150 : ulong next_; /* Only for internal pool/map usage */ 151 : ulong stake; 152 : ulong activation_epoch; 153 : ulong deactivation_epoch; 154 : ulong credits_observed; 155 : double warmup_cooldown_rate; 156 : int is_tombstone; 157 : }; 158 : typedef struct fd_stake_delegation fd_stake_delegation_t; 159 : 160 : struct fd_stake_delegations { 161 : ulong magic; 162 : ulong map_offset_; 163 : ulong pool_offset_; 164 : ulong max_stake_accounts_; 165 : int leave_tombstones_; 166 : }; 167 : typedef struct fd_stake_delegations fd_stake_delegations_t; 168 : 169 : /* Forward declare map iterator API generated by fd_map_chain.c */ 170 : typedef struct fd_stake_delegation_map_private fd_stake_delegation_map_t; 171 : typedef struct fd_map_chain_iter fd_stake_delegation_map_iter_t; 172 : struct fd_stake_delegations_iter { 173 : fd_stake_delegation_map_t * map; 174 : fd_stake_delegation_t * pool; 175 : fd_stake_delegation_map_iter_t iter; 176 : }; 177 : typedef struct fd_stake_delegations_iter fd_stake_delegations_iter_t; 178 : 179 : FD_PROTOTYPES_BEGIN 180 : 181 : /* fd_stake_delegations_align returns the alignment of the stake 182 : delegations struct. */ 183 : 184 : ulong 185 : fd_stake_delegations_align( void ); 186 : 187 : /* fd_stake_delegations_footprint returns the footprint of the stake 188 : delegations struct for a given amount of max stake accounts. */ 189 : 190 : ulong 191 : fd_stake_delegations_footprint( ulong max_stake_accounts ); 192 : 193 : /* fd_stake_delegations_new creates a new stake delegations struct 194 : with a given amount of max stake accounts. It formats a memory region 195 : which is sized based off of the number of stake accounts. The struct 196 : can optionally be configured to leave tombstones in the map. This is 197 : useful if fd_stake_delegations is being used as a delta. */ 198 : 199 : void * 200 : fd_stake_delegations_new( void * mem, 201 : ulong max_stake_accounts, 202 : int leave_tombstones ); 203 : 204 : /* fd_stake_delegations_join joins a stake delegations struct from a 205 : memory region. There can be multiple valid joins for a given memory 206 : region but the caller is responsible for accessing memory in a 207 : thread-safe manner. */ 208 : 209 : fd_stake_delegations_t * 210 : fd_stake_delegations_join( void * mem ); 211 : 212 : /* fd_stake_delegations_leave returns the stake delegations struct 213 : from a memory region. */ 214 : 215 : void * 216 : fd_stake_delegations_leave( fd_stake_delegations_t * self ); 217 : 218 : /* fd_stake_delegations_delete unformats a memory region that was 219 : formatted by fd_stake_delegations_new. */ 220 : 221 : void * 222 : fd_stake_delegations_delete( void * mem ); 223 : 224 : /* fd_stake_delegations_update will either insert a new stake delegation 225 : if the pubkey doesn't exist yet, or it will update the stake 226 : delegation for the pubkey if already in the map, overriding any 227 : previous data. fd_stake_delegations_t must be a valid local join. 228 : 229 : NOTE: This function CAN be called while iterating over the map, but 230 : ONLY for keys which already exist in the map. */ 231 : 232 : void 233 : fd_stake_delegations_update( fd_stake_delegations_t * stake_delegations, 234 : fd_pubkey_t const * stake_account, 235 : fd_pubkey_t const * vote_account, 236 : ulong stake, 237 : ulong activation_epoch, 238 : ulong deactivation_epoch, 239 : ulong credits_observed, 240 : double warmup_cooldown_rate ); 241 : 242 : /* fd_stake_delegations_remove removes a stake delegation corresponding 243 : to a stake account's pubkey if one exists. Nothing happens if the 244 : key doesn't exist in the stake delegations. fd_stake_delegations_t 245 : must be a valid local join. 246 : 247 : NOTE: If the leave_tombstones flag is set, then the entry is not 248 : removed from the map, but rather set to a tombstone. If the 249 : delegation does not exist in the map, then a tombstone is actually 250 : inserted into the struct. */ 251 : 252 : void 253 : fd_stake_delegations_remove( fd_stake_delegations_t * stake_delegations, 254 : fd_pubkey_t const * stake_account ); 255 : 256 : 257 : /* fd_stake_delegations_query returns the stake delegation for a 258 : stake account's pubkey if one exists. If one does not exist, returns 259 : NULL. fd_stake_delegations_t must be a valid local join. */ 260 : 261 : fd_stake_delegation_t const * 262 : fd_stake_delegations_query( fd_stake_delegations_t const * stake_delegations, 263 : fd_pubkey_t const * stake_account ); 264 : 265 : /* fd_stake_delegations_refresh is used to refresh the stake 266 : delegations stored in fd_stake_delegations_t which is owned by 267 : the bank. For a given database handle, read in the state of all 268 : stake accounts, decode their state, and update each stake delegation. 269 : This is meant to be called before any slots are executed, but after 270 : the snapshot has finished loading. 271 : 272 : Before this function is called, there are some important assumptions 273 : made about the state of the stake delegations that are enforced by 274 : the Agave client: 275 : 1. fd_stake_delegations_t is not missing any valid entries 276 : 2. fd_stake_delegations_t may have some invalid entries that should 277 : be removed 278 : 279 : fd_stake_delegations_refresh will remove all of the invalid entries 280 : that are detected. An entry is considered invalid if the stake 281 : account does not exist (e.g. zero balance or no record) or if it 282 : has invalid state (e.g. not a stake account or invalid bincode data). 283 : No new entries are added to the struct at this point. */ 284 : 285 : void 286 : fd_stake_delegations_refresh( fd_stake_delegations_t * stake_delegations, 287 : fd_funk_t * funk, 288 : fd_funk_txn_xid_t const * xid ); 289 : 290 : /* fd_stake_delegations_cnt returns the number of stake delegations 291 : in the stake delegations struct. fd_stake_delegations_t must be a 292 : valid local join. 293 : 294 : NOTE: The cnt will return the number of stake delegations that are 295 : in the underlying map. This number includes tombstones if the 296 : leave_tombstones flag is set. */ 297 : 298 : ulong 299 : fd_stake_delegations_cnt( fd_stake_delegations_t const * stake_delegations ); 300 : 301 : static inline ulong 302 3 : fd_stake_delegations_max( fd_stake_delegations_t const * stake_delegations ) { 303 3 : if( FD_UNLIKELY( !stake_delegations ) ) { 304 0 : FD_LOG_CRIT(( "NULL stake_delegations" )); 305 0 : } 306 : 307 3 : return stake_delegations->max_stake_accounts_; 308 3 : } 309 : 310 : /* Iterator API for stake delegations. The iterator is initialized with 311 : a call to fd_stake_delegations_iter_init. The caller is responsible 312 : for managing the memory for the iterator. It is safe to call 313 : fd_stake_delegations_iter_next if the result of 314 : fd_stake_delegations_iter_done() ==0. It is safe to call 315 : fd_stake_delegations_iter_ele() to get the current stake delegation. 316 : As a note, it is safe to modify the stake delegation acquired from 317 : fd_stake_delegations_iter_ele() as long as the next_ field is not 318 : modified (which the caller should never do). It is unsafe to insert 319 : or remove fd_stake_delegation_t from the stake delegations struct 320 : while iterating. 321 : 322 : Under the hood, the iterator is just a wrapper over the iterator in 323 : fd_map_chain.c. 324 : 325 : Example use: 326 : 327 : fd_stake_delegations_iter_t iter_[1]; 328 : for( fd_stake_delegations_iter_t * iter = fd_stake_delegations_iter_init( iter_, stake_delegations ); !fd_stake_delegations_iter_done( iter ); fd_stake_delegations_iter_next( iter ) ) { 329 : fd_stake_delegation_t * stake_delegation = fd_stake_delegations_iter_ele( iter ); 330 : // Do something with the stake delegation ... 331 : } 332 : */ 333 : 334 : fd_stake_delegation_t * 335 : fd_stake_delegations_iter_ele( fd_stake_delegations_iter_t * iter ); 336 : 337 : fd_stake_delegations_iter_t * 338 : fd_stake_delegations_iter_init( fd_stake_delegations_iter_t * iter, 339 : fd_stake_delegations_t const * stake_delegations ); 340 : 341 : void 342 : fd_stake_delegations_iter_next( fd_stake_delegations_iter_t * iter ); 343 : 344 : int 345 : fd_stake_delegations_iter_done( fd_stake_delegations_iter_t * iter ); 346 : 347 : FD_PROTOTYPES_END 348 : 349 : #endif /* HEADER_fd_src_flamenco_stakes_fd_stake_delegations_h */