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 : 6 : /* fd_multi_epoch_leaders is a wrapper around multiple fd_epoch_leaders 7 : objects. It simplifies tracking leader schedules for multiple epochs, 8 : and querying to find the leader for a given slot. While maintaining 9 : the leader schedule for the current epoch i, you can also prepare the 10 : schedule for epoch i+1 and send to the next epoch's leader as you 11 : approach the boundary. */ 12 : 13 : typedef uchar __attribute__((aligned(FD_EPOCH_LEADERS_ALIGN))) 14 : _lsched_t[FD_EPOCH_LEADERS_FOOTPRINT(MAX_STAKED_LEADERS, MAX_SLOTS_PER_EPOCH)]; 15 : 16 192 : #define MULTI_EPOCH_LEADERS_EPOCH_CNT (2UL) 17 : FD_STATIC_ASSERT(MULTI_EPOCH_LEADERS_EPOCH_CNT == 2UL, "This implementation depends on epoch_cnt==2"); 18 : 19 : struct fd_multi_epoch_leaders_priv { 20 : fd_epoch_leaders_t * lsched [ MULTI_EPOCH_LEADERS_EPOCH_CNT ]; 21 : fd_vote_stake_weight_t vote_stake_weight [ MAX_STAKED_LEADERS ]; 22 : 23 : /* has that epoch's mem experienced a stake_msg_fini? */ 24 : int init_done [ MULTI_EPOCH_LEADERS_EPOCH_CNT ]; 25 : struct { 26 : ulong epoch; 27 : ulong start_slot; 28 : ulong slot_cnt; 29 : ulong staked_cnt; 30 : ulong excluded_stake; 31 : ulong vote_keyed_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 24 : 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 24 : fd_multi_epoch_leaders_align( void ) { 57 24 : return FD_MULTI_EPOCH_LEADERS_ALIGN; 58 24 : } 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. If the leader for slot is part of the 108 : excluded_stake for that epoch, instead of returning the correct value 109 : (which is not known), returns a pointer to a pubkey with value 110 : FD_INDETERMINATE_LEADER. */ 111 : 112 : FD_FN_PURE fd_pubkey_t const * 113 : fd_multi_epoch_leaders_get_leader_for_slot( fd_multi_epoch_leaders_t const * mleaders, 114 : ulong slot ); 115 : 116 : /* fd_multi_epoch_leaders_get_lsched_for_{epoch,slot} return the leader 117 : schedule for epoch or epoch containing slot, respectively. Returns 118 : NULL if not tracked by mleaders. */ 119 : 120 : FD_FN_PURE fd_epoch_leaders_t const * 121 : fd_multi_epoch_leaders_get_lsched_for_epoch( fd_multi_epoch_leaders_t const * mleaders, 122 : ulong epoch ); 123 : FD_FN_PURE fd_epoch_leaders_t const * 124 : fd_multi_epoch_leaders_get_lsched_for_slot( fd_multi_epoch_leaders_t const * mleaders, 125 : ulong slot ); 126 : 127 : /* fd_multi_epoch_leaders_get_sorted_lscheds returns up to two lscheds, 128 : sorted in increasing epoch order. If we only have data for one epoch, 129 : the first element will be the corresponding lsched. If no lsched data, 130 : both will be null. Lifetime of returned pointers is until next call to 131 : fd_multi_epoch_leaders_stake_msg_fini. */ 132 : typedef struct { 133 : fd_epoch_leaders_t const * lscheds[2]; 134 : } fd_multi_epoch_leaders_lsched_sorted_t; 135 : 136 : FD_FN_PURE fd_multi_epoch_leaders_lsched_sorted_t 137 : fd_multi_epoch_leaders_get_sorted_lscheds( fd_multi_epoch_leaders_t const * mleaders ); 138 : 139 : 140 : /* fd_multi_epoch_leaders_get_next_slot returns the first slot on or after 141 : start_slot that 'leader' will be leader. If it can't find one, returns ULONG_MAX. 142 : 143 : Failures cases include: 144 : - mleaders does not track the epoch containing start_slot 145 : - It was either never initialized with that epoch information, or 146 : - It was overwritten by another epoch with the same parity 147 : - leader_q does not have a leader slot in the epochs tracked 148 : - leader_q was part of the excluded_stake for that epoch, and the lsched 149 : returns FD_INDETERMINATE_LEADER as the leader for leader_q's slots. 150 : */ 151 : 152 : FD_FN_PURE ulong 153 : fd_multi_epoch_leaders_get_next_slot( fd_multi_epoch_leaders_t const * mleaders, 154 : ulong start_slot, 155 : fd_pubkey_t const * leader_q ); 156 : 157 : /* ******** STAKE INFO UPDATE METHODS ******** */ 158 : 159 : /* fd_stake_ci_stake_msg_{init, fini} are used to handle messages 160 : containing stake weight updates from the Rust side of the splice,. 161 : Since these messages arrive on a dcache and can get overrun, both 162 : expose a init/fini model. Calling init multiple times without calling 163 : fini will not leak any resources. 164 : 165 : msg should be a pointer to the first byte of the dcache entry 166 : containing the stakes update. msg will be accessed 167 : msg->weights[i] for i in [0, msg->staked_cnt). msg->weights 168 : must contain at least one staked pubkey, and the pubkeys must be 169 : sorted in the usual way (by stake descending, ties broken by pubkey 170 : ascending). multi_epoch_leaders will only use the staked node. 171 : 172 : init does not maintain a read interest in msg after returning. */ 173 : 174 : void 175 : fd_multi_epoch_leaders_stake_msg_init( fd_multi_epoch_leaders_t * mleaders, 176 : fd_stake_weight_msg_t const * msg ); 177 : 178 : void 179 : fd_multi_epoch_leaders_stake_msg_fini( fd_multi_epoch_leaders_t * mleaders ); 180 : 181 : 182 : FD_PROTOTYPES_END 183 : 184 : #endif /* HEADER_fd_src_flamenco_leaders_fd_multi_epoch_leaders_h */