Line data Source code
1 : #ifndef HEADER_fd_src_choreo_voter_fd_voter_h 2 : #define HEADER_fd_src_choreo_voter_fd_voter_h 3 : 4 : #include "../fd_choreo_base.h" 5 : #include "../../funk/fd_funk_rec.h" 6 : 7 : /* FD_VOTER_USE_HANDHOLDING: Define this to non-zero at compile time 8 : to turn on additional runtime checks and logging. */ 9 : 10 : #ifndef FD_VOTER_USE_HANDHOLDING 11 : #define FD_VOTER_USE_HANDHOLDING 1 12 : #endif 13 : 14 : #define FD_VOTER_STATE_V0_23_5 (0) 15 : #define FD_VOTER_STATE_V1_14_11 (1) 16 : #define FD_VOTER_STATE_CURRENT (2) 17 : FD_STATIC_ASSERT(FD_VOTER_STATE_V0_23_5 ==fd_vote_state_versioned_enum_v0_23_5, FD_VOTER_STATE_V0_23_5 ); 18 : FD_STATIC_ASSERT(FD_VOTER_STATE_V1_14_11==fd_vote_state_versioned_enum_v1_14_11, FD_VOTER_STATE_V1_14_11); 19 : FD_STATIC_ASSERT(FD_VOTER_STATE_CURRENT ==fd_vote_state_versioned_enum_current, FD_VOTER_STATE_CURRENT ); 20 : 21 : /* Useful to keep both the block_id and slot in the vote record, 22 : for handling equivocation cases. Potentially re-evaluate removing the 23 : slot altogether.*/ 24 : struct fd_vote_record { 25 : ulong slot; 26 : fd_hash_t hash; 27 : }; 28 : typedef struct fd_vote_record fd_vote_record_t; 29 : 30 : /* A fd_voter_t describes a voter. The voter is generic to the context 31 : in which it is used, eg. it might be a voter in a slot-level context 32 : in which its stake value may be different from the same voter in an 33 : epoch-level context which in turn is different from the same voter in 34 : the prior epoch's context. 35 : 36 : The voter is used by various choreo APIs including fd_epoch which 37 : tracks all the voters in a given epoch, fd_forks which performs 38 : choreo-related fork updates after replaying a slot, and ghost and 39 : tower which both require bookkeeping the epoch voters. */ 40 : 41 : struct fd_voter { 42 : union { 43 : fd_pubkey_t key; /* vote account address */ 44 : fd_funk_rec_key_t rec; /* funk record key to query above */ 45 : }; 46 : uint hash; /* reserved for fd_map_dynamic.c */ 47 : 48 : /* IMPORTANT! The values below should only be modified by fd_epoch and 49 : fd_ghost. */ 50 : 51 : ulong stake; /* voter's stake */ 52 : fd_vote_record_t replay_vote; /* cached read of last tower vote via replay */ 53 : fd_vote_record_t gossip_vote; /* cached read of last tower vote via gossip */ 54 : fd_vote_record_t rooted_vote; /* cached read of last tower root via replay */ 55 : }; 56 : typedef struct fd_voter fd_voter_t; 57 : 58 : /* fd_voter_{vote_old, vote, meta, meta_old, state} are struct 59 : representations of the bincode-serialized layout of a voter's state 60 : stored in a vote account. These structs are used to support zero-copy 61 : reads of the vote account. 62 : 63 : The voter's state is versioned, and the serialized formats differ 64 : depending on this. Currently, the only version that differs from the 65 : others that is relevant here is v0.23.5. Thus v0.23.5 has its own 66 : dedicated struct definition with a different set of fields that 67 : precede the votes than the other versions. Furthermore, v0.23.5 68 : contains votes of type `fd_vote_lockout_t` vs. the other versions 69 : which are of type `fd_landed_vote_t`. The only difference between 70 : `fd_vote_lockout_t` and `fd_landed_vote_t` is there is an additional 71 : uchar field `latency`, so that is we include an offset of 0 or 1 72 : depending on which vote state type it is. 73 : 74 : The layout begins with a set of fields providing important metadata 75 : about the voter. Immediately following these fields is the tower 76 : itself. The tower layout begins with the number of votes currently in 77 : the tower ie. `cnt`. Then the votes themselves follow. The format of 78 : the votes varies depending on the version. Finally the layout 79 : concludes with the tower's root slot. 80 : 81 : -------- 82 : metadata <- sizeof(fd_voter_meta_t) or sizeof(fd_voter_meta_old_t) 83 : -------- 84 : votes <- {sizeof(vote) or sizeof(vote_old)} * cnt 85 : -------- 86 : root <- 5 or 1 byte(s). bincode-serialized Option<u64> 87 : -------- 88 : */ 89 : 90 : struct __attribute__((packed)) fd_voter_vote_old { 91 : ulong slot; 92 : uint conf; 93 : }; 94 : typedef struct fd_voter_vote_old fd_voter_vote_old_t; 95 : 96 : struct __attribute__((packed)) fd_voter_vote { 97 : uchar latency; 98 : ulong slot; 99 : uint conf; 100 : }; 101 : typedef struct fd_voter_vote fd_voter_vote_t; 102 : 103 : struct __attribute__((packed)) fd_voter_meta_old { 104 : fd_pubkey_t node_pubkey; 105 : fd_pubkey_t authorized_voter; 106 : ulong authorized_voter_epoch; 107 : uchar prior_voters[ (32*56+sizeof(ulong)) /* serialized bincode sz */ ]; 108 : fd_pubkey_t authorized_withdrawer; 109 : uchar commission; 110 : ulong cnt; 111 : }; 112 : typedef struct fd_voter_meta_old fd_voter_meta_old_t; 113 : 114 : struct __attribute__((packed)) fd_voter_meta { 115 : fd_pubkey_t node_pubkey; 116 : fd_pubkey_t authorized_withdrawer; 117 : uchar commission; 118 : ulong cnt; 119 : }; 120 : typedef struct fd_voter_meta fd_voter_meta_t; 121 : 122 : struct __attribute__((packed)) fd_voter_state { 123 : uint discriminant; 124 : union { 125 : struct __attribute__((packed)) { 126 : fd_voter_meta_old_t meta; 127 : fd_voter_vote_old_t votes[31]; 128 : } v0_23_5; 129 : 130 : struct __attribute__((packed)) { 131 : fd_voter_meta_t meta; 132 : fd_voter_vote_old_t votes[31]; 133 : } v1_14_11; 134 : 135 : struct __attribute__((packed)) { 136 : fd_voter_meta_t meta; 137 : fd_voter_vote_t votes[31]; 138 : }; 139 : 140 : /* The voter's root (a bincode-serialized Option<u64>) follows 141 : votes. Because the preceding votes are variable-length in 142 : serialized form, we cannot encode the root directly inside the 143 : struct. */ 144 : }; 145 : }; 146 : typedef struct fd_voter_state fd_voter_state_t; 147 : 148 : 149 : /* fd_voter_state queries funk for the record in the provided `txn` and 150 : `key`. Returns a pointer to the start of the voter's state. Assumes 151 : `key` is a vote account address and the record is a voter's state 152 : (fd_voter_state_t). U.B. if `key` does not point to a valid vote 153 : account. 154 : 155 : It will update the given Funk query with the version at the point of 156 : querying. fd_funk_rec_query_test must be called after usage to check 157 : that the record has not been modified. */ 158 : 159 : fd_voter_state_t const * 160 : fd_voter_state( fd_funk_t * funk, fd_funk_rec_t const * rec ); 161 : 162 : /* fd_voter_state_cnt returns the number of votes in the voter's tower. 163 : Assumes `state` is a valid fd_voter_state_t. */ 164 : 165 : FD_FN_PURE static inline ulong 166 0 : fd_voter_state_cnt( fd_voter_state_t const * state ) { 167 0 : if( FD_UNLIKELY( state->discriminant == FD_VOTER_STATE_V0_23_5 ) ) return state->v0_23_5.meta.cnt; 168 0 : if( FD_UNLIKELY( state->discriminant == FD_VOTER_STATE_V1_14_11 ) ) return state->v1_14_11.meta.cnt; 169 0 : return state->meta.cnt; 170 0 : } 171 : 172 : /* fd_voter_state_vote returns the voter's most recent vote (ie. the 173 : last vote of the tower in the voter's state). Assumes `state` is a 174 : valid fd_voter_state_t. */ 175 : 176 : FD_FN_PURE static inline ulong 177 0 : fd_voter_state_vote( fd_voter_state_t const * state ) { 178 0 : ulong cnt = fd_voter_state_cnt( state ); 179 0 : if( FD_UNLIKELY( !cnt ) ) return FD_SLOT_NULL; 180 : 181 0 : if( FD_UNLIKELY( state->discriminant == FD_VOTER_STATE_V0_23_5 ) ) return state->v0_23_5.votes[cnt - 1].slot; 182 0 : if( FD_UNLIKELY( state->discriminant == FD_VOTER_STATE_V1_14_11 ) ) return state->v1_14_11.votes[cnt - 1].slot; 183 0 : return state->votes[cnt - 1].slot; 184 0 : } 185 : 186 : /* fd_voter_state_root returns the voter's tower root. Assumes `state` 187 : is a valid fd_voter_state_t. */ 188 : 189 : static inline ulong 190 0 : fd_voter_state_root( fd_voter_state_t const * state ) { 191 0 : ulong cnt = fd_voter_state_cnt( state ); 192 0 : if( FD_UNLIKELY( !cnt ) ) return FD_SLOT_NULL; 193 : 194 0 : uchar * root = NULL; 195 0 : if( FD_UNLIKELY( state->discriminant == FD_VOTER_STATE_V0_23_5 ) ) root = (uchar *)&state->v0_23_5.votes[cnt]; 196 0 : else if( FD_UNLIKELY( state->discriminant == FD_VOTER_STATE_V1_14_11 ) ) root = (uchar *)&state->v1_14_11.votes[cnt]; 197 0 : else root = (uchar *)&state->votes[cnt]; 198 : 199 0 : #if FD_VOTER_USE_HANDHOLDING 200 0 : FD_TEST( root ); 201 0 : #endif 202 : 203 0 : return *(uchar *)root ? *(ulong *)(root+sizeof(uchar)) /* Some(root) */ : FD_SLOT_NULL /* None */; 204 0 : } 205 : 206 : #endif /* HEADER_fd_src_choreo_voter_fd_voter_h */