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_stake_weight_t 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 : } scratch[1]; 32 : 33 : _lsched_t _lsched[MULTI_EPOCH_LEADERS_EPOCH_CNT]; 34 : }; 35 : typedef struct fd_multi_epoch_leaders_priv fd_multi_epoch_leaders_priv_t; 36 : 37 : typedef fd_multi_epoch_leaders_priv_t fd_multi_epoch_leaders_t; 38 : 39 : 40 : FD_PROTOTYPES_BEGIN 41 : 42 : /* ******** OBJECT LIFECYCLE FUNCTIONS ******** */ 43 : 44 : /* fd_epoch_leaders_{align,footprint} describe the required footprint 45 : and alignment of the leader schedule object. They have compile friendly 46 : versions for static allocation of underlying mem */ 47 : 48 : #define FD_MULTI_EPOCH_LEADERS_ALIGN \ 49 24 : FD_ULONG_MAX( FD_EPOCH_LEADERS_ALIGN, alignof(fd_multi_epoch_leaders_t) ) 50 : 51 : #define FD_MULTI_EPOCH_LEADERS_FOOTPRINT \ 52 0 : sizeof(fd_multi_epoch_leaders_t) 53 : 54 : FD_FN_CONST static inline ulong 55 24 : fd_multi_epoch_leaders_align( void ) { 56 24 : return FD_MULTI_EPOCH_LEADERS_ALIGN; 57 24 : } 58 : 59 : FD_FN_CONST static inline ulong 60 0 : fd_multi_epoch_leaders_footprint( void ) { 61 0 : return FD_MULTI_EPOCH_LEADERS_FOOTPRINT; 62 0 : } 63 : 64 : /* fd_multi_epoch_leaders_new formats a memory region for use as a multi-epoch 65 : leader schedule object. shmem points to the first byte of a memory 66 : region with matching alignment and footprint requirements. Returns NULL 67 : if shmem is NULL or misaligned. Else returns pointer to formatted memory. 68 : Does not join. */ 69 : 70 : void * 71 : fd_multi_epoch_leaders_new( void * shmem ); 72 : 73 : /* fd_multi_epoch_leaders_join joins the caller to the leader schedule object. 74 : fd_multi_epoch_leaders_leave undoes an existing join. */ 75 : 76 : fd_multi_epoch_leaders_t * 77 : fd_multi_epoch_leaders_join( void * shleaders ); 78 : 79 : void * 80 : fd_multi_epoch_leaders_leave( fd_multi_epoch_leaders_t * mleaders ); 81 : 82 : /* fd_multi_epoch_leaders_delete unformats a memory region and returns owner- 83 : ship back to the caller. */ 84 : 85 : void * 86 : fd_multi_epoch_leaders_delete( void * shleaders ); 87 : 88 : /* ******** LEADER INFO GETTER FUNCTIONS ******** */ 89 : 90 : /* fd_multi_epoch_leaders_get_stake_{weights,cnt} returns a pointer to 91 : the stake weights and count for the latest epoch. Returns null if never 92 : initialized. The pointer lifetime is until the next leave on mleaders. 93 : However, cnt is the valid length for stake_weights only until the next 94 : call to stake_msg_init. */ 95 : FD_FN_PURE static inline fd_stake_weight_t const * 96 0 : fd_multi_epoch_leaders_get_stake_weights( fd_multi_epoch_leaders_t const * mleaders ) { 97 0 : return fd_ptr_if( mleaders->init_done[0] | mleaders->init_done[1], (fd_stake_weight_t const *)mleaders->stake_weight, NULL ); 98 0 : } 99 : FD_FN_PURE static inline ulong 100 0 : fd_multi_epoch_leaders_get_stake_cnt( fd_multi_epoch_leaders_t const * mleaders ) { 101 0 : return mleaders->scratch->staked_cnt; 102 0 : } 103 : 104 : /* fd_multi_epoch_leaders_get_leader_for_slot returns a pointer to the selected 105 : public key given a slot. Returns NULL if slot is not in epochs tracked 106 : by multi-epoch leader object. If the leader for slot is part of the 107 : excluded_stake for that epoch, instead of returning the correct value 108 : (which is not known), returns a pointer to a pubkey with value 109 : FD_INDETERMINATE_LEADER. */ 110 : 111 : FD_FN_PURE fd_pubkey_t const * 112 : fd_multi_epoch_leaders_get_leader_for_slot( fd_multi_epoch_leaders_t const * mleaders, 113 : ulong slot ); 114 : 115 : /* fd_multi_epoch_leaders_get_lsched_for_{epoch,slot} return the leader 116 : schedule for epoch or epoch containing slot, respectively. Returns 117 : NULL if not tracked by mleaders. */ 118 : 119 : FD_FN_PURE fd_epoch_leaders_t const * 120 : fd_multi_epoch_leaders_get_lsched_for_epoch( fd_multi_epoch_leaders_t const * mleaders, 121 : ulong epoch ); 122 : FD_FN_PURE fd_epoch_leaders_t const * 123 : fd_multi_epoch_leaders_get_lsched_for_slot( fd_multi_epoch_leaders_t const * mleaders, 124 : ulong slot ); 125 : 126 : /* fd_multi_epoch_leaders_get_sorted_lscheds returns up to two lscheds, 127 : sorted in increasing epoch order. If we only have data for one epoch, 128 : the first element will be the corresponding lsched. If no lsched data, 129 : both will be null. Lifetime of returned pointers is until next call to 130 : fd_multi_epoch_leaders_stake_msg_fini. */ 131 : typedef struct { 132 : fd_epoch_leaders_t const * lscheds[2]; 133 : } fd_multi_epoch_leaders_lsched_sorted_t; 134 : 135 : FD_FN_PURE fd_multi_epoch_leaders_lsched_sorted_t 136 : fd_multi_epoch_leaders_get_sorted_lscheds( fd_multi_epoch_leaders_t const * mleaders ); 137 : 138 : 139 : /* fd_multi_epoch_leaders_get_next_slot returns the first slot on or after 140 : start_slot that 'leader' will be leader. If it can't find one, returns ULONG_MAX. 141 : 142 : Failures cases include: 143 : - mleaders does not track the epoch containing start_slot 144 : - It was either never initialized with that epoch information, or 145 : - It was overwritten by another epoch with the same parity 146 : - leader_q does not have a leader slot in the epochs tracked 147 : - leader_q was part of the excluded_stake for that epoch, and the lsched 148 : returns FD_INDETERMINATE_LEADER as the leader for leader_q's slots. 149 : */ 150 : 151 : FD_FN_PURE ulong 152 : fd_multi_epoch_leaders_get_next_slot( fd_multi_epoch_leaders_t const * mleaders, 153 : ulong start_slot, 154 : fd_pubkey_t const * leader_q ); 155 : 156 : /* ******** STAKE INFO UPDATE METHODS ******** */ 157 : 158 : /* fd_stake_ci_stake_msg_{init, fini} are used to handle messages 159 : containing stake weight updates from the Rust side of the splice,. 160 : Since these messages arrive on a dcache and can get overrun, both 161 : expose a init/fini model. Calling init multiple times without calling 162 : fini will not leak any resources. 163 : 164 : msg should be a pointer to the first byte of the dcache entry 165 : containing the stakes update. msg will be accessed 166 : msg->weights[i] for i in [0, msg->staked_cnt). msg->weights 167 : must contain at least one staked pubkey, and the pubkeys must be 168 : sorted in the usual way (by stake descending, ties broken by pubkey 169 : ascending). multi_epoch_leaders will only use the staked node. 170 : 171 : init does not maintain a read interest in msg after returning. */ 172 : 173 : void 174 : fd_multi_epoch_leaders_stake_msg_init( fd_multi_epoch_leaders_t * mleaders, 175 : fd_stake_weight_msg_t const * msg ); 176 : 177 : void 178 : fd_multi_epoch_leaders_stake_msg_fini( fd_multi_epoch_leaders_t * mleaders ); 179 : 180 : 181 : FD_PROTOTYPES_END 182 : 183 : #endif /* HEADER_fd_src_flamenco_leaders_fd_multi_epoch_leaders_h */