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