Line data Source code
1 : #ifndef HEADER_fd_src_flamenco_leaders_fd_multi_epoch_leaders_h 2 : #define HEADER_fd_src_flamenco_leaders_fd_multi_epoch_leaders_h 3 : 4 : #include "fd_leaders.h" 5 : #include "fd_leaders_base.h" 6 : 7 : /* fd_multi_epoch_leaders is a wrapper around multiple fd_epoch_leaders 8 : objects. It simplifies tracking leader schedules for multiple epochs, 9 : and querying to find the leader for a given slot. While maintaining 10 : the leader schedule for the current epoch i, you can also prepare the 11 : schedule for epoch i+1 and send to the next epoch's leader as you 12 : approach the boundary. */ 13 : 14 : typedef uchar __attribute__((aligned(FD_EPOCH_LEADERS_ALIGN))) 15 : _lsched_t[FD_EPOCH_LEADERS_FOOTPRINT(MAX_STAKE_WEIGHTS, MAX_SLOTS_PER_EPOCH)]; 16 : 17 1308 : #define MULTI_EPOCH_LEADERS_EPOCH_CNT (2UL) 18 : FD_STATIC_ASSERT(MULTI_EPOCH_LEADERS_EPOCH_CNT == 2UL, "This implementation depends on epoch_cnt==2"); 19 : 20 : struct fd_multi_epoch_leaders_priv { 21 : fd_epoch_leaders_t * lsched [ MULTI_EPOCH_LEADERS_EPOCH_CNT ]; 22 : fd_vote_stake_weight_t vote_stake_weight [ MAX_STAKE_WEIGHTS ]; 23 : 24 : /* has that epoch's mem experienced a stake_msg_fini? */ 25 : int init_done [ MULTI_EPOCH_LEADERS_EPOCH_CNT ]; 26 : struct { 27 : ulong epoch; 28 : ulong start_slot; 29 : ulong slot_cnt; 30 : ulong staked_cnt; 31 : int geo_aware_lsched; 32 : } scratch[1]; 33 : 34 : _lsched_t _lsched[MULTI_EPOCH_LEADERS_EPOCH_CNT]; 35 : }; 36 : typedef struct fd_multi_epoch_leaders_priv fd_multi_epoch_leaders_priv_t; 37 : 38 : typedef fd_multi_epoch_leaders_priv_t fd_multi_epoch_leaders_t; 39 : 40 : 41 : FD_PROTOTYPES_BEGIN 42 : 43 : /* ******** OBJECT LIFECYCLE FUNCTIONS ******** */ 44 : 45 : /* fd_epoch_leaders_{align,footprint} describe the required footprint 46 : and alignment of the leader schedule object. They have compile friendly 47 : versions for static allocation of underlying mem */ 48 : 49 : #define FD_MULTI_EPOCH_LEADERS_ALIGN \ 50 156 : FD_ULONG_MAX( FD_EPOCH_LEADERS_ALIGN, alignof(fd_multi_epoch_leaders_t) ) 51 : 52 : #define FD_MULTI_EPOCH_LEADERS_FOOTPRINT \ 53 0 : sizeof(fd_multi_epoch_leaders_t) 54 : 55 : FD_FN_CONST static inline ulong 56 156 : fd_multi_epoch_leaders_align( void ) { 57 156 : return FD_MULTI_EPOCH_LEADERS_ALIGN; 58 156 : } 59 : 60 : FD_FN_CONST static inline ulong 61 0 : fd_multi_epoch_leaders_footprint( void ) { 62 0 : return FD_MULTI_EPOCH_LEADERS_FOOTPRINT; 63 0 : } 64 : 65 : /* fd_multi_epoch_leaders_new formats a memory region for use as a multi-epoch 66 : leader schedule object. shmem points to the first byte of a memory 67 : region with matching alignment and footprint requirements. Returns NULL 68 : if shmem is NULL or misaligned. Else returns pointer to formatted memory. 69 : Does not join. */ 70 : 71 : void * 72 : fd_multi_epoch_leaders_new( void * shmem ); 73 : 74 : /* fd_multi_epoch_leaders_join joins the caller to the leader schedule object. 75 : fd_multi_epoch_leaders_leave undoes an existing join. */ 76 : 77 : fd_multi_epoch_leaders_t * 78 : fd_multi_epoch_leaders_join( void * shleaders ); 79 : 80 : void * 81 : fd_multi_epoch_leaders_leave( fd_multi_epoch_leaders_t * mleaders ); 82 : 83 : /* fd_multi_epoch_leaders_delete unformats a memory region and returns owner- 84 : ship back to the caller. */ 85 : 86 : void * 87 : fd_multi_epoch_leaders_delete( void * shleaders ); 88 : 89 : /* ******** LEADER INFO GETTER FUNCTIONS ******** */ 90 : 91 : /* fd_multi_epoch_leaders_get_stake_{weights,cnt} returns a pointer to 92 : the stake weights and count for the latest epoch. Returns null if never 93 : initialized. The pointer lifetime is until the next leave on mleaders. 94 : However, cnt is the valid length for stake_weights only until the next 95 : call to stake_msg_init. */ 96 : FD_FN_PURE static inline fd_vote_stake_weight_t const * 97 0 : fd_multi_epoch_leaders_get_stake_weights( fd_multi_epoch_leaders_t const * mleaders ) { 98 0 : return fd_ptr_if( mleaders->init_done[0] | mleaders->init_done[1], (fd_vote_stake_weight_t const *)mleaders->vote_stake_weight, NULL ); 99 0 : } 100 : FD_FN_PURE static inline ulong 101 0 : fd_multi_epoch_leaders_get_stake_cnt( fd_multi_epoch_leaders_t const * mleaders ) { 102 0 : return mleaders->scratch->staked_cnt; 103 0 : } 104 : 105 : /* fd_multi_epoch_leaders_get_leader_for_slot returns a pointer to the selected 106 : public key given a slot. Returns NULL if slot is not in epochs tracked 107 : by multi-epoch leader object. */ 108 : 109 : FD_FN_PURE fd_pubkey_t const * 110 : fd_multi_epoch_leaders_get_leader_for_slot( fd_multi_epoch_leaders_t const * mleaders, 111 : ulong slot ); 112 : 113 : /* fd_multi_epoch_leaders_get_lsched_for_{epoch,slot} return the leader 114 : schedule for epoch or epoch containing slot, respectively. Returns 115 : NULL if not tracked by mleaders. */ 116 : 117 : FD_FN_PURE fd_epoch_leaders_t const * 118 : fd_multi_epoch_leaders_get_lsched_for_epoch( fd_multi_epoch_leaders_t const * mleaders, 119 : ulong epoch ); 120 : FD_FN_PURE fd_epoch_leaders_t const * 121 : fd_multi_epoch_leaders_get_lsched_for_slot( fd_multi_epoch_leaders_t const * mleaders, 122 : ulong slot ); 123 : 124 : /* fd_multi_epoch_leaders_get_sorted_lscheds returns up to two lscheds, 125 : sorted in increasing epoch order. If we only have data for one epoch, 126 : the first element will be the corresponding lsched. If no lsched data, 127 : both will be null. Lifetime of returned pointers is until next call to 128 : fd_multi_epoch_leaders_stake_msg_fini. */ 129 : typedef struct { 130 : fd_epoch_leaders_t const * lscheds[2]; 131 : } fd_multi_epoch_leaders_lsched_sorted_t; 132 : 133 : FD_FN_PURE fd_multi_epoch_leaders_lsched_sorted_t 134 : fd_multi_epoch_leaders_get_sorted_lscheds( fd_multi_epoch_leaders_t const * mleaders ); 135 : 136 : 137 : /* fd_multi_epoch_leaders_get_next_slot returns the first slot on or after 138 : start_slot that 'leader' will be leader. If it can't find one, returns ULONG_MAX. 139 : 140 : Failures cases include: 141 : - mleaders does not track the epoch containing start_slot 142 : - It was either never initialized with that epoch information, or 143 : - It was overwritten by another epoch with the same parity 144 : - leader_q does not have a leader slot in the epochs tracked 145 : */ 146 : 147 : FD_FN_PURE ulong 148 : fd_multi_epoch_leaders_get_next_slot( fd_multi_epoch_leaders_t const * mleaders, 149 : ulong start_slot, 150 : fd_pubkey_t const * leader_q ); 151 : 152 : /* ******** STAKE INFO UPDATE METHODS ******** */ 153 : 154 : /* fd_multi_epoch_leaders_stake_msg_{init, fini} handle messages 155 : containing stake weight updates. 156 : Since these messages arrive on a dcache and can get overrun, both 157 : expose a init/fini model. Calling init multiple times without calling 158 : fini will not leak any resources. 159 : 160 : msg should be a pointer to the first byte of the dcache entry 161 : containing the stakes update. The weights are accessed via 162 : fd_stake_weight_msg_stake_weights(msg), with msg->staked_vote_cnt 163 : entries. At least one pubkey must be staked. Pubkeys must be 164 : sorted in the usual way (by stake descending, ties broken by pubkey 165 : ascending). multi_epoch_leaders will only use the staked node. 166 : 167 : init does not maintain a read interest in msg after returning. */ 168 : 169 : void 170 : fd_multi_epoch_leaders_stake_msg_init( fd_multi_epoch_leaders_t * mleaders, 171 : fd_stake_weight_msg_t const * msg ); 172 : 173 : void 174 : fd_multi_epoch_leaders_stake_msg_fini( fd_multi_epoch_leaders_t * mleaders ); 175 : 176 : 177 : /* fd_multi_epoch_leaders_epoch_msg_{init, fini} are equivalents to 178 : fd_multi_epoch_leaders_stake_msg_{init, fini} that take a different 179 : input message structure (fd_epoch_info_msg_t vs 180 : fd_stake_weight_msg_t). */ 181 : 182 : void 183 : fd_multi_epoch_leaders_epoch_msg_init( fd_multi_epoch_leaders_t * mleaders, 184 : fd_epoch_info_msg_t const * msg ); 185 : 186 : void 187 : fd_multi_epoch_leaders_epoch_msg_fini( fd_multi_epoch_leaders_t * mleaders ); 188 : 189 : FD_PROTOTYPES_END 190 : 191 : #endif /* HEADER_fd_src_flamenco_leaders_fd_multi_epoch_leaders_h */