Line data Source code
1 : #ifndef HEADER_fd_src_choreo_tower_fd_epoch_stakes_h 2 : #define HEADER_fd_src_choreo_tower_fd_epoch_stakes_h 3 : 4 : /* fd_epoch_stakes_t tracks stakes of each voter in the epoch. In 5 : general, the stakes for each voter remain constant throughout the 6 : epoch. However, if we fork across the epoch boundary, the set of 7 : voters and their stakes may be different depending on the fork. If 8 : we have not rooted an epoch slot yet, this could be different from 9 : other forks, but if we have rooted an epoch slot, the stakes for each 10 : voter are the same for all forks. 11 : 12 : This is currently exclusively used for the switch check, which 13 : requires the stakes of each voter on the HEAVIEST fork in the epoch. 14 : We need to do all this tracking because tower cannot query the vote 15 : states from any bank at will. Replay determines which banks can be 16 : queried, and won't know beforehand which banks will be the heaviest. 17 : 18 : fd_epoch_stakes_t is backed by two hash maps: 19 : 1. fd_voter_stake_map: this maps a vote account to a voter stake 20 : 2. fd_epoch_stakes_slot_map: this maps a slot to a voter stake index 21 : 22 : The voter_stake_map has a compound key {vote_account, slot}, so that 23 : the map can be queries O(1) by slot and vote account. As we populate 24 : the map, we also thread a linkedlist through all the entries for the 25 : same slot. This is possible because the vote stake map is populated/ 26 : updated all at once when a slot arrives from the bank, so we can 27 : sequentially link the current entry to the previous entry. Then the 28 : last entry in the linkedlist (last voter we process for a slot) will 29 : have its key {vote_account, slot} put in the slot_stakes_map. 30 : This way on publish, we have a way to query all the stakes / voters 31 : for a slot without doing a full scan of the voter_stake_map. 32 : 33 : */ 34 : 35 : #include "../fd_choreo_base.h" 36 : 37 : struct fd_voter_stake_key { 38 : fd_hash_t vote_account; 39 : ulong slot; 40 : }; 41 : typedef struct fd_voter_stake_key fd_voter_stake_key_t; 42 : 43 : static const fd_voter_stake_key_t fd_voter_stake_key_null = { .vote_account = {{ 0 }}, .slot = 0UL }; 44 : 45 : struct fd_voter_stake { 46 : fd_voter_stake_key_t key; 47 : ulong next; 48 : ulong stake; 49 : ulong prev; 50 : }; 51 : typedef struct fd_voter_stake fd_voter_stake_t; 52 : 53 : #define MAP_NAME fd_voter_stake_map 54 : #define MAP_ELE_T fd_voter_stake_t 55 : #define MAP_KEY_T fd_voter_stake_key_t 56 24 : #define MAP_KEY_EQ(k0,k1) (!memcmp( k0, k1, sizeof(fd_voter_stake_key_t) )) 57 69 : #define MAP_KEY_HASH(key, seed) fd_ulong_hash( ((key)->slot) ^ ((key)->vote_account.ul[0]) ^ (seed) ) 58 : #include "../../util/tmpl/fd_map_chain.c" 59 : 60 : #define POOL_NAME fd_voter_stake_pool 61 18 : #define POOL_T fd_voter_stake_t 62 : #include "../../util/tmpl/fd_pool.c" 63 : 64 : /* Some really terrible witchcraft to track used vote accounts for 65 : whatever reason. For example, switch check needs to make sure it's 66 : not repeating usage of the same vote account. We can flip a bit on 67 : the vote stake pool index if its been used. Caller should ensure that 68 : the set is cleared before and after each use. The size of the set 69 : will be the number of elements in the vote stake pool. */ 70 : 71 : #define SET_NAME fd_used_acc_scratch 72 : #include "../../util/tmpl/fd_set_dynamic.c" 73 : 74 : struct fd_epoch_stakes_slot { 75 : ulong slot; 76 : ulong voter_stake_idx; /* head of linkedlist*/ 77 : }; 78 : typedef struct fd_epoch_stakes_slot fd_epoch_stakes_slot_t; 79 : 80 : #define MAP_NAME fd_epoch_stakes_slot_map 81 93 : #define MAP_T fd_epoch_stakes_slot_t 82 1227 : #define MAP_KEY slot 83 : #define MAP_MEMOIZE 0 84 : #include "../../util/tmpl/fd_map_dynamic.c" 85 : 86 : struct __attribute__((aligned(128UL))) fd_epoch_stakes { 87 : fd_voter_stake_map_t * voter_stake_map; 88 : fd_voter_stake_t * voter_stake_pool; 89 : fd_epoch_stakes_slot_t * slot_stakes_map; 90 : fd_used_acc_scratch_t * used_acc_scratch; 91 : }; 92 : typedef struct fd_epoch_stakes fd_epoch_stakes_t; 93 : 94 : FD_PROTOTYPES_BEGIN 95 : 96 : FD_FN_CONST static inline ulong 97 72 : fd_epoch_stakes_align( void ) { 98 72 : return alignof(fd_epoch_stakes_t); 99 72 : } 100 : 101 : FD_FN_CONST static inline ulong 102 18 : fd_epoch_stakes_footprint( ulong slot_max ) { 103 18 : int lg_slot_cnt = fd_ulong_find_msb( fd_ulong_pow2_up( slot_max ) ) + 1; 104 18 : return FD_LAYOUT_FINI( 105 18 : FD_LAYOUT_APPEND( 106 18 : FD_LAYOUT_APPEND( 107 18 : FD_LAYOUT_APPEND( 108 18 : FD_LAYOUT_APPEND( 109 18 : FD_LAYOUT_APPEND( 110 18 : FD_LAYOUT_INIT, 111 18 : alignof(fd_epoch_stakes_t), sizeof(fd_epoch_stakes_t) ), 112 18 : fd_voter_stake_map_align(), fd_voter_stake_map_footprint ( FD_VOTER_MAX * slot_max ) ), 113 18 : fd_voter_stake_pool_align(), fd_voter_stake_pool_footprint( FD_VOTER_MAX * slot_max ) ), 114 18 : fd_epoch_stakes_slot_map_align(), fd_epoch_stakes_slot_map_footprint( lg_slot_cnt ) ), 115 18 : fd_used_acc_scratch_align(), fd_used_acc_scratch_footprint( FD_VOTER_MAX * slot_max ) ), 116 18 : fd_epoch_stakes_align() ); 117 18 : } 118 : 119 : void * 120 : fd_epoch_stakes_new( void * shmem, 121 : ulong slot_max ); 122 : 123 : fd_epoch_stakes_t * 124 : fd_epoch_stakes_join( void * shepoch_stakes ); 125 : 126 : /* fd_epoch_slot_stakes_add adds a new stake for a voter to the epoch stakes 127 : for a specific slot, and returns the index of the new voter stake in the pool. 128 : prev_voter_idx is the index of the previous voter stake in the pool. If this 129 : is the first voter inserted for this slot, prev_voter_idx should be ULONG_MAX. 130 : 131 : Usage should look like: 132 : prev_voter_idx = ULONG_MAX; 133 : for( v : voters ) { 134 : voter_idx = fd_epoch_stakes_slot_stakes_add( epoch_stakes, slot, v.vote_account, v.stake, prev_voter_idx ); 135 : prev_voter_idx = voter_idx; 136 : } */ 137 : ulong 138 : fd_epoch_stakes_slot_stakes_add( fd_epoch_stakes_t * epoch_stakes, ulong slot, fd_hash_t const * vote_account, ulong stake, ulong prev_voter_idx ); 139 : 140 : void 141 : fd_epoch_stakes_slot_stakes_remove( fd_epoch_stakes_t * epoch_stakes, fd_epoch_stakes_slot_t * slot ); 142 : 143 : #endif /* HEADER_fd_src_choreo_tower_fd_epoch_stakes_h */