Line data Source code
1 : #ifndef HEADER_fd_src_choreo_tower_fd_tower_stakes_h 2 : #define HEADER_fd_src_choreo_tower_fd_tower_stakes_h 3 : 4 : /* fd_tower_stakes_t tracks stakes of the top k voters in a slot (as of 5 : SIMD-0357, k=2000). While the stakes are usually the same for every 6 : slot in a given epoch, it is not guaranteed. Specifically, if there 7 : is a fork across an epoch boundary, the set of voters and their 8 : stakes may be different across slots in the same epoch because the 9 : slots are on different forks that calculated the stakes differently. 10 : If we have not rooted a slot in the new epoch yet, this could be 11 : different from other forks, but if we have rooted an epoch slot, the 12 : stakes for each voter are the same for all forks that descend from 13 : that root (forks that do not descend will have been pruned). 14 : 15 : This is currently exclusively used for the switch check, which 16 : requires the stakes of each voter on the HEAVIEST fork in the epoch. 17 : We need to do all this tracking because tower cannot query the vote 18 : states from any bank at will. Replay determines which banks can be 19 : queried, and won't know beforehand which banks will be the heaviest. 20 : 21 : fd_tower_stakes_t is backed by two hash maps: 22 : 1. fd_tower_stakes_vtr_map: this maps a vote account to a voter stake 23 : 2. fd_tower_stakes_slot_map: this maps a slot to a voter stake index 24 : 25 : The voter_stake_map has a compound key {vote_account, slot}, so that 26 : the map can be queries O(1) by slot and vote account. As we populate 27 : the map, we also thread a linkedlist through all the entries for the 28 : same slot. This is possible because the vote stake map is populated/ 29 : updated all at once when a slot arrives from the bank, so we can 30 : sequentially link the current entry to the previous entry. Then the 31 : last entry in the linkedlist (last voter we process for a slot) will 32 : have its key {vote_account, slot} put in the slot_stakes_map. This 33 : way on publish, we have a way to query all the stakes / voters for a 34 : slot without doing a full scan of the voter_stake_map. */ 35 : 36 : #include "../fd_choreo_base.h" 37 : 38 : struct fd_tower_stakes_vtr_xid { 39 : fd_hash_t addr; /* vote account address */ 40 : ulong slot; 41 : }; 42 : typedef struct fd_tower_stakes_vtr_xid fd_tower_stakes_vtr_xid_t; 43 : 44 : static const fd_tower_stakes_vtr_xid_t fd_tower_stakes_vtr_xid_null = { .addr = {{ 0 }}, .slot = 0UL }; 45 : 46 : struct fd_tower_stakes_vtr { 47 : fd_tower_stakes_vtr_xid_t key; 48 : ulong prev; 49 : ulong next; 50 : ulong stake; 51 : }; 52 : typedef struct fd_tower_stakes_vtr fd_tower_stakes_vtr_t; 53 : 54 : #define MAP_NAME fd_tower_stakes_vtr_map 55 : #define MAP_ELE_T fd_tower_stakes_vtr_t 56 : #define MAP_KEY_T fd_tower_stakes_vtr_xid_t 57 33 : #define MAP_KEY_EQ(k0,k1) (!memcmp( k0, k1, sizeof(fd_tower_stakes_vtr_xid_t) )) 58 93 : #define MAP_KEY_HASH(key, seed) fd_ulong_hash( ((key)->slot) ^ ((key)->addr.ul[0]) ^ (seed) ) 59 : #include "../../util/tmpl/fd_map_chain.c" 60 : 61 : #define POOL_NAME fd_tower_stakes_vtr_pool 62 108 : #define POOL_T fd_tower_stakes_vtr_t 63 : #define POOL_LAZY 1 64 : #include "../../util/tmpl/fd_pool.c" 65 : 66 : /* Some really terrible witchcraft to track used vote accounts for 67 : whatever reason. For example, switch check needs to make sure it's 68 : not repeating usage of the same vote account. We can flip a bit on 69 : the vote stake pool index if its been used. Caller should ensure that 70 : the set is cleared before and after each use. The size of the set 71 : will be the number of elements in the vote stake pool. */ 72 : 73 : #define SET_NAME fd_used_acc_scratch 74 : #include "../../util/tmpl/fd_set_dynamic.c" 75 : 76 : struct fd_tower_stakes_slot { 77 : ulong slot; 78 : ulong head; /* pool idx of the head of a linked list of voters in this slot */ 79 : }; 80 : typedef struct fd_tower_stakes_slot fd_tower_stakes_slot_t; 81 : 82 : #define MAP_NAME fd_tower_stakes_slot 83 219 : #define MAP_T fd_tower_stakes_slot_t 84 5163 : #define MAP_KEY slot 85 5052 : #define MAP_KEY_NULL ULONG_MAX 86 111 : #define MAP_KEY_INVAL(key) ((key)==ULONG_MAX) 87 : #define MAP_MEMOIZE 0 88 : #include "../../util/tmpl/fd_map_dynamic.c" 89 : 90 : struct fd_tower; 91 : 92 : FD_PROTOTYPES_BEGIN 93 : 94 : /* fd_tower_stakes_insert adds a new (voter, stake) pair to the epoch 95 : stakes for a specific slot, and returns the index of the new voter 96 : stake in the pool. prev_voter_idx is the index of the previous voter 97 : stake in the pool. If this is the first voter inserted for this 98 : slot, prev_voter_idx should be ULONG_MAX. Example usage: 99 : 100 : prev_voter_idx = ULONG_MAX; 101 : for( v : voters ) { 102 : voter_idx = fd_tower_stakes_insert( tower, slot, v.vote_account, v.stake, prev_voter_idx ); 103 : prev_voter_idx = voter_idx; 104 : } */ 105 : 106 : ulong 107 : fd_tower_stakes_insert( struct fd_tower * tower, 108 : ulong slot, 109 : fd_hash_t const * vote_account, 110 : ulong stake, 111 : ulong prev_voter_idx ); 112 : 113 : void 114 : fd_tower_stakes_remove( struct fd_tower * tower, 115 : ulong slot ); 116 : 117 : #endif /* HEADER_fd_src_choreo_tower_fd_tower_stakes_h */