Line data Source code
1 : #ifndef HEADER_fd_src_flamenco_stakes_fd_vote_stakes_h
2 : #define HEADER_fd_src_flamenco_stakes_fd_vote_stakes_h
3 :
4 : #include "../../util/fd_util_base.h"
5 : #include "../fd_flamenco_base.h"
6 : #include "../accdb/fd_accdb_base.h"
7 : #include "../runtime/program/vote/fd_vote_codec.h"
8 :
9 24 : #define FD_VOTE_STAKES_FD (123454)
10 360 : #define FD_VOTE_STAKES_ALIGN (128UL)
11 2347 : #define FD_VOTE_STAKES_ALPENGLOW_RANK_NULL USHORT_MAX
12 :
13 : /* fd_vote_stakes is the Firedancer equivalent of the Agave epoch stakes
14 : data structure. It is a fork aware data structure that maintains the
15 : vote account state that is used for clock and VM syscall calculations
16 : during execution.
17 :
18 : The underlying data structure is a tiered set of caches:
19 : - t-2..t-5: these are caches used for the vote account states at
20 : the end of the t-2 through t-5 epochs assuming you are currently
21 : in the t epoch. These caches are shared across all forks.
22 : - t-1: computed at the most recent epoch boundary. These sets are
23 : ref-counted and fork specific, up to max_live_slots of them;
24 : cache_cnt are kept in memory and the rest spill to disk.
25 : - state: each bank has its own view of the t-2 state of vote
26 : accounts. This is what is actually used for clock calculations
27 : which is a stake weighted median of the last vote slot and
28 : timestamp.
29 :
30 : fd_vote_stakes also handles the creation of new vote stakes caches.
31 : The creation of new vote stakes caches happens at the epoch boundary
32 : and follows the rules defined in the validator_admission_ticket
33 : feature where every vote account must be in the top 2000 by stake.
34 : Under the hood, this is managed by a stake-sorted heap that is shared
35 : across all forks.
36 :
37 : NOTE:
38 : This system assumes that multiple forks can cross an epoch boundary
39 : but only one epoch boundary can be crossed at a time. This means
40 : that there can be multiple t-1 forks after an epoch boundary, but
41 : only one will be active when the first fork crosses into an epoch
42 : boundary. Similarly, this means that live forks address 5 epoch
43 : sets during an epoch boundary crossing and 4 most of the time:
44 : crossing into t+1 evicts the t-6 set, which no live fork addresses. */
45 :
46 : struct fd_vote_stakes;
47 : typedef struct fd_vote_stakes fd_vote_stakes_t;
48 :
49 : /* fd_vote_stakes_ele_t is a copy of one vote account in a vote stakes
50 : set. */
51 :
52 : struct fd_vote_stakes_ele {
53 : fd_pubkey_t pubkey;
54 : fd_pubkey_t node_account;
55 : ulong stake;
56 : ushort commission; /* inflation rewards, basis points */
57 : ushort block_revenue_commission_bps; /* SIMD-0123 */
58 : ulong pending_delegator_rewards; /* SIMD-0123 */
59 : fd_pubkey_t inflation_collector; /* SIMD-0232 */
60 : fd_pubkey_t block_collector; /* SIMD-0232 */
61 : ushort rank;
62 : uchar bls_key[ FD_BLS_PUBKEY_COMPRESSED_SZ ];
63 : uchar bls_key_uncompressed[ FD_BLS_PUBKEY_UNCOMPRESSED_SZ ];
64 :
65 : /* The fork's vote state, only set for t-2 (zero otherwise). */
66 : ulong last_vote_slot;
67 : long last_vote_ts;
68 : uchar is_valid;
69 : };
70 : typedef struct fd_vote_stakes_ele fd_vote_stakes_ele_t;
71 :
72 : FD_PROTOTYPES_BEGIN
73 :
74 : ulong
75 : fd_vote_stakes_align( void );
76 :
77 : ulong
78 : fd_vote_stakes_footprint( ulong max_live_slots,
79 : ulong cache_cnt );
80 :
81 : void *
82 : fd_vote_stakes_new( void * mem,
83 : int disk_fd,
84 : ulong max_live_slots,
85 : ulong cache_cnt,
86 : ulong seed );
87 :
88 : fd_vote_stakes_t *
89 : fd_vote_stakes_join( void * mem,
90 : int disk_fd );
91 :
92 : /* fd_vote_stakes_fork_epoch returns the epoch of the fork. */
93 :
94 : ulong
95 : fd_vote_stakes_fork_epoch( ulong fork_id );
96 :
97 : /* fd_vote_stakes_init initializes the vote stakes data structure for a
98 : given epoch. */
99 :
100 : ulong
101 : fd_vote_stakes_init( fd_vote_stakes_t * vote_stakes,
102 : ulong epoch );
103 :
104 : /* fd_vote_stakes_reset resets the vote stakes data structure. */
105 :
106 : void
107 : fd_vote_stakes_reset( fd_vote_stakes_t * vote_stakes );
108 :
109 : /* fd_vote_stakes_snap_insert_t_1 inserts a new vote account into the
110 : t-1 set. This skips over any vote account validation and should
111 : only be used when loading in vote accounts from a snapshot. */
112 :
113 : void
114 : fd_vote_stakes_snap_insert_t_1( fd_vote_stakes_t * vote_stakes,
115 : ulong fork_id,
116 : fd_pubkey_t const * pubkey,
117 : fd_pubkey_t const * node_account,
118 : ulong stake,
119 : ushort commission,
120 : uchar const bls_key[ static FD_BLS_PUBKEY_COMPRESSED_SZ ],
121 : ushort block_revenue_commission_bps,
122 : ulong pending_delegator_rewards,
123 : fd_pubkey_t const * inflation_collector_opt,
124 : fd_pubkey_t const * block_collector_opt );
125 :
126 : /* fd_vote_stakes_snap_insert_t_2 inserts a new vote account into the
127 : t-2 set. This skips over any vote account validation and should
128 : only be used when loading in vote accounts from a snapshot. */
129 :
130 : void
131 : fd_vote_stakes_snap_insert_t_2( fd_vote_stakes_t * vote_stakes,
132 : ulong fork_id,
133 : fd_pubkey_t const * pubkey,
134 : fd_pubkey_t const * node_account,
135 : ulong stake,
136 : ushort commission,
137 : uchar const bls_key[ static FD_BLS_PUBKEY_COMPRESSED_SZ ],
138 : ushort block_revenue_commission_bps,
139 : ulong pending_delegator_rewards,
140 : fd_pubkey_t const * inflation_collector_opt,
141 : fd_pubkey_t const * block_collector_opt );
142 :
143 : /* fd_vote_stakes_snap_insert_t_n inserts a new vote account into the
144 : t-n set, n in 3..5, and marks that set resident. This skips over
145 : any vote account validation and should only be used when loading in
146 : vote accounts from a snapshot. */
147 :
148 : void
149 : fd_vote_stakes_snap_insert_t_n( fd_vote_stakes_t * vote_stakes,
150 : ulong fork_id,
151 : ulong n,
152 : fd_pubkey_t const * pubkey,
153 : fd_pubkey_t const * node_account,
154 : ulong stake,
155 : ushort commission,
156 : uchar const bls_key[ static FD_BLS_PUBKEY_COMPRESSED_SZ ],
157 : ushort block_revenue_commission_bps,
158 : ulong pending_delegator_rewards,
159 : fd_pubkey_t const * inflation_collector_opt,
160 : fd_pubkey_t const * block_collector_opt );
161 :
162 : /* fd_vote_stakes_insert inserts a new vote account into the t-1 set.
163 : This can be called repeatedly after a boundary-crossing
164 : fd_vote_stakes_new_fork. An account will only be inserted if it
165 : matches the semantics of the validator_admission_ticket feature.
166 : The top 2000 vote accounts by stake are inserted into the t-1 set. */
167 :
168 : void
169 : fd_vote_stakes_insert( fd_vote_stakes_t * vote_stakes,
170 : ulong fork_id,
171 : fd_pubkey_t const * pubkey,
172 : fd_pubkey_t const * node_account,
173 : ulong stake,
174 : ushort commission,
175 : uchar const bls_key[ static FD_BLS_PUBKEY_COMPRESSED_SZ ],
176 : ushort block_revenue_commission_bps,
177 : ulong pending_delegator_rewards,
178 : fd_pubkey_t const * inflation_collector_opt,
179 : fd_pubkey_t const * block_collector_opt );
180 :
181 : /* fd_vote_stakes_finalize assigns Alpenglow ranks to the set that
182 : iter_kind addresses for fork_id. Accounts with invalid or infinity
183 : BLS keys or duplicate BLS/identity keys retain
184 : FD_VOTE_STAKES_ALPENGLOW_RANK_NULL but remain in the set. This is a
185 : no-op if the set is not resident. */
186 :
187 : void
188 : fd_vote_stakes_finalize( fd_vote_stakes_t * vote_stakes,
189 : ulong fork_id,
190 : int iter_kind );
191 :
192 : /* fd_vote_stakes_purge_fork removes a t-1 set. This should be called
193 : when the banks are evicting a bank or during root advancement. */
194 :
195 : void
196 : fd_vote_stakes_purge_fork( fd_vote_stakes_t * vote_stakes,
197 : ulong fork_id );
198 :
199 : /* fd_vote_stakes_new_fork creates a child of the given parent fork.
200 : Within an epoch, the child shares the parent's t-1 set and receives a
201 : copy of its t-2 state. At an epoch boundary, it rotates t-1 into
202 : t-2 and acquires fresh t-1 and t-2 state. */
203 :
204 : ulong
205 : fd_vote_stakes_new_fork( fd_vote_stakes_t * vote_stakes,
206 : ulong parent_fork_id,
207 : ulong epoch );
208 :
209 : /* fd_vote_stakes_update updates the vote account state for a given
210 : fork id. It is a no-op if the pubkey corresponds to an account not
211 : in the t-2 set. If is_valid==0, the last vote arguments will be
212 : ignored. */
213 :
214 : void
215 : fd_vote_stakes_update_state( fd_vote_stakes_t * vote_stakes,
216 : ulong fork_id,
217 : fd_pubkey_t const * pubkey,
218 : ulong last_vote_slot,
219 : long last_vote_ts,
220 : uchar is_valid );
221 :
222 : void
223 : fd_vote_stakes_refresh( fd_vote_stakes_t * vote_stakes,
224 : ulong fork_id,
225 : fd_accdb_t * accdb,
226 : fd_accdb_fork_id_t accdb_fork_id );
227 :
228 : /* fd_vote_stakes_query_t_{1,2,3} look up pubkey in the fork's t-1,
229 : t-2 or t-3 set and, if ele_out_opt is non-NULL, copy the entry to
230 : it. Only query_t_2 sets the vote state fields. Returns 1 if the
231 : account is in the set, 0 otherwise. */
232 :
233 : int
234 : fd_vote_stakes_query_t_1( fd_vote_stakes_t * vote_stakes,
235 : ulong fork_id,
236 : fd_pubkey_t const * pubkey,
237 : fd_vote_stakes_ele_t * ele_out_opt );
238 :
239 : int
240 : fd_vote_stakes_query_t_2( fd_vote_stakes_t const * vote_stakes,
241 : ulong fork_id,
242 : fd_pubkey_t const * pubkey,
243 : fd_vote_stakes_ele_t * ele_out_opt );
244 :
245 : int
246 : fd_vote_stakes_query_t_3( fd_vote_stakes_t const * vote_stakes,
247 : ulong fork_id,
248 : fd_pubkey_t const * pubkey,
249 : fd_vote_stakes_ele_t * ele_out_opt );
250 :
251 : /* fd_vote_stakes_cnt_t_{1,2} returns the number of vote accounts in the
252 : t-1 and t-2 sets respectively. */
253 :
254 : ulong
255 : fd_vote_stakes_cnt_t_1( fd_vote_stakes_t * vote_stakes,
256 : ulong fork_id );
257 :
258 : ulong
259 : fd_vote_stakes_cnt_t_2( fd_vote_stakes_t const * vote_stakes,
260 : ulong fork_id );
261 :
262 : ulong
263 : fd_vote_stakes_t_2_votes( fd_vote_stakes_t const * vote_stakes,
264 : ulong fork_id,
265 : ulong * stake,
266 : ulong * last_vote_slot,
267 : long * last_vote_ts );
268 :
269 : /* fd_vote_stakes_total_stake returns the total stake of the epoch
270 : stakes set for epoch.
271 : epoch must be one of the t-2 through t-5 sets; returns 0 if that
272 : epoch's set is not resident. */
273 :
274 : ulong
275 : fd_vote_stakes_total_stake( fd_vote_stakes_t const * vote_stakes,
276 : ulong epoch );
277 :
278 : /* The iterator will NOT skip over invalid vote accounts
279 : intentionally. The reason being is that invalid vote accounts are
280 : still considered for stake and leader calculations: the caller is
281 : expected to handle the state of each account correctly. */
282 :
283 : #define FD_VOTE_STAKES_ITER_FOOTPRINT (16UL)
284 : #define FD_VOTE_STAKES_ITER_ALIGN (8UL)
285 419 : #define FD_VOTE_STAKES_ITER_T_1 (1)
286 18946 : #define FD_VOTE_STAKES_ITER_T_2 (2)
287 185 : #define FD_VOTE_STAKES_ITER_T_3 (3)
288 0 : #define FD_VOTE_STAKES_ITER_T_4 (4)
289 0 : #define FD_VOTE_STAKES_ITER_T_5 (5)
290 :
291 : struct vacc_map_iter;
292 : typedef struct vacc_map_iter fd_vote_stakes_iter_t;
293 :
294 : /* The iterator walks the fork's set selected by iter_kind. The vote
295 : state fields of the element are only set for T_2. */
296 :
297 : fd_vote_stakes_iter_t *
298 : fd_vote_stakes_iter_init( fd_vote_stakes_t * vote_stakes,
299 : ulong fork_id,
300 : int iter_kind,
301 : uchar iter_mem[ static FD_VOTE_STAKES_ITER_FOOTPRINT ] );
302 :
303 : int
304 : fd_vote_stakes_iter_done( fd_vote_stakes_t * vote_stakes,
305 : ulong fork_id,
306 : int iter_kind,
307 : fd_vote_stakes_iter_t * iter );
308 :
309 : void
310 : fd_vote_stakes_iter_next( fd_vote_stakes_t * vote_stakes,
311 : ulong fork_id,
312 : int iter_kind,
313 : fd_vote_stakes_iter_t * iter );
314 :
315 : void
316 : fd_vote_stakes_iter_ele( fd_vote_stakes_t * vote_stakes,
317 : ulong fork_id,
318 : int iter_kind,
319 : fd_vote_stakes_iter_t * iter,
320 : fd_vote_stakes_ele_t * ele_out );
321 :
322 : FD_PROTOTYPES_END
323 :
324 : #endif /* HEADER_fd_src_flamenco_stakes_fd_vote_stakes_h */
|