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