Line data Source code
1 : #include "fd_vote_stakes.h"
2 : #include "../accdb/fd_accdb.h"
3 : #include "../fd_flamenco_base.h"
4 : #include "../runtime/fd_bank.h"
5 : #include "../runtime/fd_runtime_const.h"
6 : #include "../runtime/program/vote/fd_vote_state_versioned.h"
7 : #include "../../util/bits/fd_bits.h"
8 : #include "../../util/fd_hash32.h"
9 : #include "../../util/log/fd_log.h"
10 : #include "../../ballet/bls/fd_bls12_381.h"
11 :
12 126 : #define FD_VOTE_STAKES_MAGIC (0xF17EDA2CE7601E70UL) /* FIREDANCER VOTE STAKES V0 */
13 78537 : #define FD_VOTE_STAKES_EPOCH_CACHE_CNT (3UL)
14 :
15 : struct vacc {
16 : fd_pubkey_t pubkey;
17 : fd_pubkey_t node_account;
18 : ulong stake;
19 : ushort commission;
20 : ushort alpenglow_rank;
21 : uchar bls_key[ FD_BLS_PUBKEY_COMPRESSED_SZ ]; /* zero if unregistered */
22 : uchar bls_key_uncompressed[ FD_BLS_PUBKEY_UNCOMPRESSED_SZ ]; /* decompressed by finalize, valid iff alpenglow_rank!=NULL */
23 : uint left;
24 : uint right;
25 : uint next;
26 : };
27 : typedef struct vacc vacc_t;
28 :
29 : struct vacc_rank {
30 : vacc_t * vacc;
31 : ulong drop;
32 : };
33 : typedef struct vacc_rank vacc_rank_t;
34 :
35 : #define SORT_NAME vacc_rank_sort
36 45 : #define SORT_KEY_T vacc_rank_t
37 24 : #define SORT_BEFORE(a,b) (((a).vacc->stake>(b).vacc->stake) | ((a).vacc->stake==(b).vacc->stake && \
38 24 : memcmp( (a).vacc->bls_key, (b).vacc->bls_key, FD_BLS_PUBKEY_COMPRESSED_SZ )<0 ))
39 : #include "../../util/tmpl/fd_sort.c"
40 :
41 : #define SORT_NAME vacc_rank_bls_sort
42 69 : #define SORT_KEY_T vacc_rank_t
43 39 : #define SORT_BEFORE(a,b) (memcmp( (a).vacc->bls_key, (b).vacc->bls_key, FD_BLS_PUBKEY_COMPRESSED_SZ )<0)
44 : #include "../../util/tmpl/fd_sort.c"
45 :
46 : #define SORT_NAME vacc_rank_identity_sort
47 72 : #define SORT_KEY_T vacc_rank_t
48 42 : #define SORT_BEFORE(a,b) (memcmp( &(a).vacc->node_account, &(b).vacc->node_account, sizeof(fd_pubkey_t) )<0)
49 : #include "../../util/tmpl/fd_sort.c"
50 :
51 : #define HEAP_NAME vacc_heap
52 6357 : #define HEAP_IDX_T uint
53 : #define HEAP_T vacc_t
54 54912 : #define HEAP_LT(e0,e1) ( ((e0)->stake < (e1)->stake) | \
55 54912 : (((e0)->stake==(e1)->stake) & \
56 54912 : (memcmp( &(e0)->pubkey, &(e1)->pubkey, sizeof(fd_pubkey_t) )<0 ) ) )
57 : #include "../../util/tmpl/fd_heap.c"
58 :
59 : #define POOL_NAME vacc_pool
60 1932 : #define POOL_T vacc_t
61 : #define POOL_IDX_T uint
62 : #define POOL_LAZY 1
63 : #include "../../util/tmpl/fd_pool.c"
64 :
65 : #define MAP_NAME vacc_map
66 : #define MAP_KEY_T fd_pubkey_t
67 : #define MAP_ELE_T vacc_t
68 14856 : #define MAP_KEY pubkey
69 5046 : #define MAP_KEY_EQ(k0,k1) (!memcmp( k0, k1, sizeof(fd_pubkey_t) ))
70 19953 : #define MAP_KEY_HASH(key,seed) (fd_hash32( key->uc, seed ))
71 100491 : #define MAP_IDX_T uint
72 : #include "../../util/tmpl/fd_map_chain.c"
73 :
74 : struct vacc_fork {
75 : uint ref_cnt;
76 : uint next;
77 : };
78 : typedef struct vacc_fork vacc_fork_t;
79 :
80 : #define POOL_NAME vacc_fork_pool
81 252 : #define POOL_T vacc_fork_t
82 : #define POOL_IDX_T uint
83 : #define POOL_LAZY 1
84 : #include "../../util/tmpl/fd_pool.c"
85 :
86 : struct vacc_states {
87 : struct {
88 : ulong last_vote_slot;
89 : long last_vote_ts;
90 : uchar is_valid;
91 : } states[ FD_RUNTIME_MAX_VAT_VOTE_ACCOUNTS ];
92 : uint next;
93 : };
94 : typedef struct vacc_states vacc_states_t;
95 :
96 : #define POOL_NAME vacc_state_pool
97 252 : #define POOL_T vacc_states_t
98 : #define POOL_IDX_T uint
99 : #define POOL_LAZY 1
100 : #include "../../util/tmpl/fd_pool.c"
101 :
102 : struct fd_vote_stakes {
103 : ulong magic;
104 : ulong max_fork_width;
105 : ulong max_live_slots;
106 : ulong min_stake_wmark;
107 :
108 : /* (pubkey, stake) pairs for the t-2 epoch. These are shared across
109 : forks/banks. */
110 : ulong t_2_epoch[ FD_VOTE_STAKES_EPOCH_CACHE_CNT ];
111 : uint t_2_vacc_pool_off[ FD_VOTE_STAKES_EPOCH_CACHE_CNT ];
112 : uint t_2_vacc_map_off [ FD_VOTE_STAKES_EPOCH_CACHE_CNT ];
113 :
114 : /* (pubkey, stake) pairs for the t-1 epoch. These can be different
115 : for every fork across the epoch boundary. These must be sized to
116 : the max fork width of the running system. These are keyed by fork
117 : idx. */
118 : uint vacc_fork_pool_off;
119 : uint t_1_vacc_pools_off;
120 : uint t_1_vacc_maps_off;
121 : uint vacc_heap_off;
122 :
123 : /* Per-bank state about the t-2 vote state. Each per-bank state will
124 : be an array sized to length max_live_slots which is a system-wide
125 : bound. Keyed by bank idx. */
126 : uint vacc_states_pool_off;
127 : };
128 : typedef struct fd_vote_stakes fd_vote_stakes_t;
129 :
130 : FD_FN_UNUSED static inline vacc_t *
131 : t_2_vacc_pool( fd_vote_stakes_t const * vote_stakes,
132 62190 : ulong idx ) {
133 62190 : return fd_type_pun( (uchar *)vote_stakes + vote_stakes->t_2_vacc_pool_off[ idx ] );
134 62190 : }
135 :
136 : FD_FN_UNUSED static inline vacc_map_t *
137 : t_2_vacc_map( fd_vote_stakes_t const * vote_stakes,
138 62178 : ulong idx ) {
139 62178 : return fd_type_pun( (uchar *)vote_stakes + vote_stakes->t_2_vacc_map_off[ idx ] );
140 62178 : }
141 :
142 : FD_FN_UNUSED static inline vacc_fork_t *
143 18306 : vacc_fork_pool( fd_vote_stakes_t const * vote_stakes ) {
144 18306 : return fd_type_pun( (uchar *)vote_stakes + vote_stakes->vacc_fork_pool_off );
145 18306 : }
146 :
147 : FD_FN_UNUSED static inline vacc_t *
148 : t_1_vacc_pool( fd_vote_stakes_t const * vote_stakes,
149 54984 : ulong width_idx ) {
150 54984 : ulong off = vote_stakes->t_1_vacc_pools_off + width_idx*vacc_pool_footprint( FD_RUNTIME_MAX_VAT_VOTE_ACCOUNTS );
151 54984 : return fd_type_pun( (uchar *)vote_stakes + off );
152 54984 : }
153 :
154 : FD_FN_UNUSED static inline vacc_map_t *
155 : t_1_vacc_map( fd_vote_stakes_t const * vote_stakes,
156 54960 : ulong width_idx ) {
157 54960 : ulong chain_cnt = vacc_map_chain_cnt_est( FD_RUNTIME_MAX_VAT_VOTE_ACCOUNTS );
158 54960 : ulong off = vote_stakes->t_1_vacc_maps_off + width_idx*vacc_map_footprint( chain_cnt );
159 54960 : return fd_type_pun( (uchar *)vote_stakes + off );
160 54960 : }
161 :
162 : FD_FN_UNUSED static inline vacc_heap_t *
163 14838 : vacc_heap( fd_vote_stakes_t const * vote_stakes ) {
164 14838 : return fd_type_pun( (uchar *)vote_stakes + vote_stakes->vacc_heap_off );
165 14838 : }
166 :
167 : FD_FN_UNUSED static inline vacc_states_t *
168 28173 : vacc_states_pool( fd_vote_stakes_t const * vote_stakes ) {
169 28173 : return fd_type_pun( (uchar *)vote_stakes + vote_stakes->vacc_states_pool_off );
170 28173 : }
171 :
172 : /* The fork id is a compound of the epoch, width idx, and bank idx. */
173 :
174 : FD_FN_UNUSED static inline ushort
175 16074 : fork_id_bank_id( ulong fork_id ) {
176 16074 : return (ushort)(fork_id & (ulong)USHORT_MAX);
177 16074 : }
178 :
179 : FD_FN_UNUSED static inline ushort
180 23919 : fork_id_width_id( ulong fork_id ) {
181 23919 : return (ushort)((fork_id>>16UL) & (ulong)USHORT_MAX);
182 23919 : }
183 :
184 : FD_FN_UNUSED static inline ushort
185 47019 : fork_id_epoch( ulong fork_id ) {
186 47019 : return (ushort)((fork_id>>32UL) & (ulong)USHORT_MAX);
187 47019 : }
188 :
189 : FD_FN_UNUSED static inline void
190 : fork_id_set_bank_id( ulong * fork_id,
191 9207 : ushort bank_id ) {
192 9207 : *fork_id = (*fork_id & ~(ulong)USHORT_MAX) | (ulong)bank_id;
193 9207 : }
194 :
195 : FD_FN_UNUSED static inline void
196 : fork_id_set_width_id( ulong * fork_id,
197 9207 : ushort width_id ) {
198 9207 : *fork_id = (*fork_id & ~((ulong)USHORT_MAX<<16UL)) | ((ulong)width_id<<16UL);
199 9207 : }
200 :
201 : FD_FN_UNUSED static inline void
202 : fork_id_set_epoch( ulong * fork_id,
203 9207 : ushort epoch ) {
204 9207 : *fork_id = (*fork_id & ~((ulong)USHORT_MAX<<32UL)) | ((ulong)epoch<<32UL);
205 9207 : }
206 :
207 : ulong
208 3867 : fd_vote_stakes_align( void ) {
209 3867 : return FD_VOTE_STAKES_ALIGN;
210 3867 : }
211 :
212 : ulong
213 : fd_vote_stakes_footprint( ulong max_live_slots,
214 648 : ulong max_fork_width ) {
215 648 : if( FD_UNLIKELY( !max_live_slots || max_live_slots>USHORT_MAX ) ) return 0UL;
216 648 : if( FD_UNLIKELY( !max_fork_width || max_fork_width>FD_BANKS_MAX_BANKS ) ) return 0UL;
217 :
218 645 : ulong map_chain_cnt = vacc_map_chain_cnt_est( FD_RUNTIME_MAX_VAT_VOTE_ACCOUNTS );
219 645 : ulong pool_footprint = vacc_pool_footprint( FD_RUNTIME_MAX_VAT_VOTE_ACCOUNTS );
220 645 : ulong map_footprint = vacc_map_footprint( map_chain_cnt );
221 645 : ulong width_cnt = max_fork_width + 1UL;
222 :
223 645 : ulong l = FD_LAYOUT_INIT;
224 645 : l = FD_LAYOUT_APPEND( l, fd_vote_stakes_align(), sizeof(fd_vote_stakes_t) );
225 2580 : for( ulong i=0UL; i<FD_VOTE_STAKES_EPOCH_CACHE_CNT; i++ ) {
226 1935 : l = FD_LAYOUT_APPEND( l, vacc_pool_align(), pool_footprint );
227 1935 : l = FD_LAYOUT_APPEND( l, vacc_map_align(), map_footprint );
228 1935 : }
229 645 : l = FD_LAYOUT_APPEND( l, vacc_fork_pool_align(), vacc_fork_pool_footprint( width_cnt ) );
230 645 : l = FD_LAYOUT_APPEND( l, vacc_pool_align(), width_cnt*pool_footprint );
231 645 : l = FD_LAYOUT_APPEND( l, vacc_map_align(), width_cnt*map_footprint );
232 645 : l = FD_LAYOUT_APPEND( l, vacc_heap_align(), vacc_heap_footprint( FD_RUNTIME_MAX_VAT_VOTE_ACCOUNTS ) );
233 645 : l = FD_LAYOUT_APPEND( l, vacc_state_pool_align(), vacc_state_pool_footprint( max_live_slots ) );
234 645 : return FD_LAYOUT_FINI( l, fd_vote_stakes_align() );
235 648 : }
236 :
237 : void *
238 : fd_vote_stakes_new( void * mem,
239 : ulong max_live_slots,
240 : ulong max_fork_width,
241 126 : ulong seed ) {
242 126 : if( FD_UNLIKELY( !mem ) ) {
243 0 : FD_LOG_WARNING(( "NULL mem" ));
244 0 : return NULL;
245 0 : }
246 :
247 126 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)mem, fd_vote_stakes_align() ) ) ) {
248 0 : FD_LOG_WARNING(( "misaligned mem" ));
249 0 : return NULL;
250 0 : }
251 :
252 126 : if( FD_UNLIKELY( !max_live_slots || max_live_slots>USHORT_MAX ) ) {
253 0 : FD_LOG_WARNING(( "invalid max_live_slots" ));
254 0 : return NULL;
255 0 : }
256 :
257 126 : if( FD_UNLIKELY( !max_fork_width || max_fork_width>FD_BANKS_MAX_BANKS ) ) {
258 0 : FD_LOG_WARNING(( "invalid max_fork_width" ));
259 0 : return NULL;
260 0 : }
261 :
262 126 : ulong map_chain_cnt = vacc_map_chain_cnt_est( FD_RUNTIME_MAX_VAT_VOTE_ACCOUNTS );
263 126 : ulong pool_footprint = vacc_pool_footprint( FD_RUNTIME_MAX_VAT_VOTE_ACCOUNTS );
264 126 : ulong map_footprint = vacc_map_footprint( map_chain_cnt );
265 126 : ulong width_cnt = max_fork_width + 1UL;
266 :
267 126 : FD_SCRATCH_ALLOC_INIT( l, mem );
268 126 : fd_vote_stakes_t * vote_stakes = FD_SCRATCH_ALLOC_APPEND( l, fd_vote_stakes_align(), sizeof(fd_vote_stakes_t) );
269 126 : void * t_2_pool_mem[ FD_VOTE_STAKES_EPOCH_CACHE_CNT ];
270 126 : void * t_2_map_mem [ FD_VOTE_STAKES_EPOCH_CACHE_CNT ];
271 504 : for( ulong i=0UL; i<FD_VOTE_STAKES_EPOCH_CACHE_CNT; i++ ) {
272 378 : t_2_pool_mem[ i ] = FD_SCRATCH_ALLOC_APPEND( l, vacc_pool_align(), pool_footprint );
273 378 : t_2_map_mem [ i ] = FD_SCRATCH_ALLOC_APPEND( l, vacc_map_align(), map_footprint );
274 378 : }
275 126 : void * vacc_fork_pool_mem = FD_SCRATCH_ALLOC_APPEND( l, vacc_fork_pool_align(), vacc_fork_pool_footprint( width_cnt ) );
276 126 : void * t_1_vacc_pools_mem = FD_SCRATCH_ALLOC_APPEND( l, vacc_pool_align(), width_cnt*pool_footprint );
277 126 : void * t_1_vacc_maps_mem = FD_SCRATCH_ALLOC_APPEND( l, vacc_map_align(), width_cnt*map_footprint );
278 126 : void * vacc_heap_mem = FD_SCRATCH_ALLOC_APPEND( l, vacc_heap_align(), vacc_heap_footprint( FD_RUNTIME_MAX_VAT_VOTE_ACCOUNTS ) );
279 126 : void * vacc_states_pool_mem = FD_SCRATCH_ALLOC_APPEND( l, vacc_state_pool_align(), vacc_state_pool_footprint( max_live_slots ) );
280 :
281 126 : if( FD_UNLIKELY( FD_SCRATCH_ALLOC_FINI( l, fd_vote_stakes_align() )!=(ulong)mem+fd_vote_stakes_footprint( max_live_slots, max_fork_width ) ) ) {
282 0 : FD_LOG_WARNING(( "fd_vote_stakes_new: bad layout" ));
283 0 : return NULL;
284 0 : }
285 :
286 504 : for( ulong i=0UL; i<FD_VOTE_STAKES_EPOCH_CACHE_CNT; i++ ) {
287 378 : vacc_t * t_2_pool = vacc_pool_join( vacc_pool_new( t_2_pool_mem[ i ], FD_RUNTIME_MAX_VAT_VOTE_ACCOUNTS ) );
288 378 : if( FD_UNLIKELY( !t_2_pool ) ) {
289 0 : FD_LOG_WARNING(( "Failed to create t-2 vote account pool" ));
290 0 : return NULL;
291 0 : }
292 :
293 378 : vacc_map_t * t_2_map = vacc_map_join( vacc_map_new( t_2_map_mem[ i ], map_chain_cnt, seed ) );
294 378 : if( FD_UNLIKELY( !t_2_map ) ) {
295 0 : FD_LOG_WARNING(( "Failed to create t-2 vote account map" ));
296 0 : return NULL;
297 0 : }
298 :
299 378 : vote_stakes->t_2_vacc_pool_off[ i ] = (uint)((ulong)t_2_pool - (ulong)mem);
300 378 : vote_stakes->t_2_vacc_map_off [ i ] = (uint)((ulong)t_2_map - (ulong)mem);
301 378 : }
302 :
303 126 : vacc_fork_t * fork_pool = vacc_fork_pool_join( vacc_fork_pool_new( vacc_fork_pool_mem, width_cnt ) );
304 126 : if( FD_UNLIKELY( !fork_pool ) ) {
305 0 : FD_LOG_WARNING(( "Failed to create vote account fork pool" ));
306 0 : return NULL;
307 0 : }
308 126 : vote_stakes->vacc_fork_pool_off = (uint)((ulong)fork_pool - (ulong)mem);
309 :
310 714 : for( ulong i=0UL; i<width_cnt; i++ ) {
311 588 : void * pool_mem = (uchar *)t_1_vacc_pools_mem + i*pool_footprint;
312 588 : vacc_t * pool = vacc_pool_join( vacc_pool_new( pool_mem, FD_RUNTIME_MAX_VAT_VOTE_ACCOUNTS ) );
313 588 : if( FD_UNLIKELY( !pool ) ) {
314 0 : FD_LOG_WARNING(( "Failed to create t-1 vote account pool" ));
315 0 : return NULL;
316 0 : }
317 588 : if( FD_UNLIKELY( !i ) ) vote_stakes->t_1_vacc_pools_off = (uint)((ulong)pool - (ulong)mem);
318 :
319 588 : void * map_mem = (uchar *)t_1_vacc_maps_mem + i*map_footprint;
320 588 : vacc_map_t * map = vacc_map_join( vacc_map_new( map_mem, map_chain_cnt, seed ) );
321 588 : if( FD_UNLIKELY( !map ) ) {
322 0 : FD_LOG_WARNING(( "Failed to create t-1 vote account map" ));
323 0 : return NULL;
324 0 : }
325 588 : }
326 126 : vote_stakes->t_1_vacc_maps_off = (uint)((ulong)t_1_vacc_maps_mem - (ulong)mem);
327 :
328 126 : vacc_heap_t * heap = vacc_heap_join( vacc_heap_new( vacc_heap_mem, FD_RUNTIME_MAX_VAT_VOTE_ACCOUNTS ) );
329 126 : if( FD_UNLIKELY( !heap ) ) {
330 0 : FD_LOG_WARNING(( "Failed to create vote account heap" ));
331 0 : return NULL;
332 0 : }
333 :
334 126 : vacc_states_t * vacc_states_pool = vacc_state_pool_join( vacc_state_pool_new( vacc_states_pool_mem, max_live_slots ) );
335 126 : if( FD_UNLIKELY( !vacc_states_pool ) ) {
336 0 : FD_LOG_WARNING(( "Failed to create vote account state pool" ));
337 0 : return NULL;
338 0 : }
339 :
340 126 : vote_stakes->max_live_slots = max_live_slots;
341 126 : vote_stakes->max_fork_width = max_fork_width;
342 126 : vote_stakes->min_stake_wmark = 0UL;
343 126 : vote_stakes->vacc_heap_off = (uint)((ulong)heap - (ulong)mem);
344 126 : vote_stakes->vacc_states_pool_off = (uint)((ulong)vacc_states_pool - (ulong)mem);
345 504 : for( ulong i=0UL; i<FD_VOTE_STAKES_EPOCH_CACHE_CNT; i++ ) vote_stakes->t_2_epoch[ i ] = ULONG_MAX;
346 :
347 126 : FD_COMPILER_MFENCE();
348 126 : FD_VOLATILE( vote_stakes->magic ) = FD_VOTE_STAKES_MAGIC;
349 126 : FD_COMPILER_MFENCE();
350 :
351 126 : return vote_stakes;
352 126 : }
353 :
354 : fd_vote_stakes_t *
355 249 : fd_vote_stakes_join( void * mem ) {
356 249 : fd_vote_stakes_t * vote_stakes = (fd_vote_stakes_t *)mem;
357 :
358 249 : if( FD_UNLIKELY( !vote_stakes ) ) {
359 0 : FD_LOG_WARNING(( "NULL vote stakes" ));
360 0 : return NULL;
361 0 : }
362 :
363 249 : if( FD_UNLIKELY( vote_stakes->magic!=FD_VOTE_STAKES_MAGIC ) ) {
364 0 : FD_LOG_WARNING(( "Invalid vote stakes magic" ));
365 0 : return NULL;
366 0 : }
367 :
368 249 : return vote_stakes;
369 249 : }
370 :
371 : ulong
372 4530 : fd_vote_stakes_fork_epoch( ulong fork_id ) {
373 4530 : return (ulong)fork_id_epoch( fork_id );
374 4530 : }
375 :
376 : ulong
377 : fd_vote_stakes_init( fd_vote_stakes_t * vote_stakes,
378 4068 : ulong epoch ) {
379 : /* Acquire t-1 key set width idx */
380 4068 : vacc_fork_t * fork_pool = vacc_fork_pool( vote_stakes );
381 4068 : FD_CHECK_CRIT( vacc_fork_pool_free( fork_pool ), "vote stakes width pool exhausted" );
382 4068 : ushort width_idx = (ushort)vacc_fork_pool_idx_acquire( fork_pool );
383 4068 : vacc_fork_pool_ele( fork_pool, width_idx )->ref_cnt = 1U;
384 :
385 : /* Acquire t-2 state bank idx */
386 4068 : vacc_states_t * states_pool = vacc_states_pool( vote_stakes );
387 4068 : FD_CHECK_CRIT( vacc_state_pool_free( states_pool ), "vote stakes state pool exhausted" );
388 4068 : ushort bank_idx = (ushort)vacc_state_pool_idx_acquire( states_pool );
389 4068 : vacc_states_t * states = vacc_state_pool_ele( states_pool, bank_idx );
390 4068 : memset( states->states, 0, sizeof(states->states) );
391 4068 : vote_stakes->t_2_epoch[ epoch % FD_VOTE_STAKES_EPOCH_CACHE_CNT ] = epoch;
392 :
393 : /* Construct the fork id */
394 4068 : ulong fork_id = 0UL;
395 4068 : fork_id_set_bank_id( &fork_id, bank_idx );
396 4068 : fork_id_set_width_id( &fork_id, width_idx );
397 4068 : fork_id_set_epoch( &fork_id, (ushort)epoch );
398 4068 : return fork_id;
399 4068 : }
400 :
401 : void
402 8031 : fd_vote_stakes_reset( fd_vote_stakes_t * vote_stakes ) {
403 32124 : for( ulong i=0UL; i<FD_VOTE_STAKES_EPOCH_CACHE_CNT; i++ ) {
404 24093 : vacc_map_reset( t_2_vacc_map( vote_stakes, i ) );
405 24093 : vacc_pool_reset( t_2_vacc_pool( vote_stakes, i ) );
406 24093 : vote_stakes->t_2_epoch[ i ] = ULONG_MAX;
407 24093 : }
408 :
409 8031 : ulong width_cnt = vote_stakes->max_fork_width + 1UL;
410 48144 : for( ulong i=0UL; i<width_cnt; i++ ) {
411 40113 : vacc_map_reset( t_1_vacc_map( vote_stakes, i ) );
412 40113 : vacc_pool_reset( t_1_vacc_pool( vote_stakes, i ) );
413 40113 : }
414 :
415 8031 : vacc_fork_pool_reset( vacc_fork_pool( vote_stakes ) );
416 8031 : vacc_state_pool_reset( vacc_states_pool( vote_stakes ) );
417 8031 : FD_TEST( vacc_heap_new( vacc_heap( vote_stakes ), FD_RUNTIME_MAX_VAT_VOTE_ACCOUNTS ) );
418 8031 : vote_stakes->min_stake_wmark = 0UL;
419 8031 : }
420 :
421 : void
422 : fd_vote_stakes_snap_insert_t_1( fd_vote_stakes_t * vote_stakes,
423 : ulong fork_id,
424 : fd_pubkey_t const * pubkey,
425 : fd_pubkey_t const * node_account,
426 : ulong stake,
427 : ushort commission,
428 3999 : uchar const bls_key[ static FD_BLS_PUBKEY_COMPRESSED_SZ ] ) {
429 3999 : if( FD_UNLIKELY( !stake ) ) return;
430 :
431 3999 : vacc_t * pool = t_1_vacc_pool( vote_stakes, fork_id_width_id( fork_id ) );
432 3999 : vacc_map_t * map = t_1_vacc_map ( vote_stakes, fork_id_width_id( fork_id ) );
433 :
434 3999 : vacc_t * vacc = vacc_pool_ele_acquire( pool );
435 3999 : vacc->pubkey = *pubkey;
436 3999 : vacc->node_account = *node_account;
437 3999 : vacc->stake = stake;
438 3999 : vacc->commission = commission;
439 3999 : vacc->alpenglow_rank = FD_VOTE_STAKES_ALPENGLOW_RANK_NULL;
440 3999 : memcpy( vacc->bls_key, bls_key, FD_BLS_PUBKEY_COMPRESSED_SZ );
441 3999 : FD_TEST( vacc_map_ele_insert( map, vacc, pool ) );
442 3999 : }
443 :
444 : void
445 : fd_vote_stakes_snap_insert_t_2( fd_vote_stakes_t * vote_stakes,
446 : ulong fork_id,
447 : fd_pubkey_t const * pubkey,
448 : fd_pubkey_t const * node_account,
449 : ulong stake,
450 : ushort commission,
451 4023 : uchar const bls_key[ static FD_BLS_PUBKEY_COMPRESSED_SZ ] ) {
452 4023 : if( FD_UNLIKELY( !stake ) ) return;
453 :
454 4023 : ulong epoch_idx = (ulong)fork_id_epoch( fork_id ) % FD_VOTE_STAKES_EPOCH_CACHE_CNT;
455 4023 : vacc_t * pool = t_2_vacc_pool( vote_stakes, epoch_idx );
456 4023 : vacc_map_t * map = t_2_vacc_map ( vote_stakes, epoch_idx );
457 :
458 4023 : vacc_t * vacc = vacc_pool_ele_acquire( pool );
459 4023 : vacc->pubkey = *pubkey;
460 4023 : vacc->node_account = *node_account;
461 4023 : vacc->stake = stake;
462 4023 : vacc->commission = commission;
463 4023 : vacc->alpenglow_rank = FD_VOTE_STAKES_ALPENGLOW_RANK_NULL;
464 4023 : memcpy( vacc->bls_key, bls_key, FD_BLS_PUBKEY_COMPRESSED_SZ );
465 4023 : FD_TEST( vacc_map_ele_insert( map, vacc, pool ) );
466 4023 : }
467 :
468 : void
469 : fd_vote_stakes_snap_insert_t_3( fd_vote_stakes_t * vote_stakes,
470 : ulong fork_id,
471 : fd_pubkey_t const * pubkey,
472 : fd_pubkey_t const * node_account,
473 : ulong stake,
474 : ushort commission,
475 21 : uchar const bls_key[ static FD_BLS_PUBKEY_COMPRESSED_SZ ] ) {
476 21 : ulong epoch = (ulong)fork_id_epoch( fork_id );
477 21 : if( FD_UNLIKELY( !epoch || !stake ) ) return;
478 :
479 21 : ulong t_3_epoch = epoch - 1UL;
480 21 : ulong t_3_idx = t_3_epoch % FD_VOTE_STAKES_EPOCH_CACHE_CNT;
481 21 : vacc_t * pool = t_2_vacc_pool( vote_stakes, t_3_idx );
482 21 : vacc_map_t * map = t_2_vacc_map ( vote_stakes, t_3_idx );
483 :
484 21 : vacc_t * vacc = vacc_pool_ele_acquire( pool );
485 21 : vacc->pubkey = *pubkey;
486 21 : vacc->node_account = *node_account;
487 21 : vacc->stake = stake;
488 21 : vacc->commission = commission;
489 21 : vacc->alpenglow_rank = FD_VOTE_STAKES_ALPENGLOW_RANK_NULL;
490 21 : memcpy( vacc->bls_key, bls_key, FD_BLS_PUBKEY_COMPRESSED_SZ );
491 21 : FD_TEST( vacc_map_ele_insert( map, vacc, pool ) );
492 21 : vote_stakes->t_2_epoch[ t_3_idx ] = t_3_epoch;
493 21 : }
494 :
495 : void
496 : fd_vote_stakes_insert( fd_vote_stakes_t * vote_stakes,
497 : ulong fork_id,
498 : fd_pubkey_t const * pubkey,
499 : fd_pubkey_t const * node_account,
500 : ulong stake,
501 : ushort commission,
502 6354 : uchar const bls_key[ static FD_BLS_PUBKEY_COMPRESSED_SZ ] ) {
503 6354 : ulong width_idx = (ulong)fork_id_width_id( fork_id );
504 6354 : vacc_t * pool = t_1_vacc_pool( vote_stakes, width_idx );
505 6354 : vacc_map_t * map = t_1_vacc_map ( vote_stakes, width_idx );
506 6354 : vacc_heap_t * heap = vacc_heap( vote_stakes );
507 :
508 6354 : if( FD_UNLIKELY( stake==0UL || stake<=vote_stakes->min_stake_wmark ) ) return;
509 :
510 6354 : if( FD_UNLIKELY( vacc_heap_ele_cnt( heap )==vacc_heap_ele_max( heap ) ) ) {
511 3 : vacc_t * ele = vacc_heap_ele_peek_min( heap, pool );
512 3 : ulong min_stake = ele->stake;
513 3 : if( stake<min_stake ) return;
514 :
515 3 : vote_stakes->min_stake_wmark = min_stake;
516 9 : while( (ele=vacc_heap_ele_peek_min( heap, pool )) && min_stake==ele->stake ) {
517 6 : vacc_heap_ele_remove_min( heap, pool );
518 6 : vacc_map_ele_remove( map, &ele->pubkey, NULL, pool );
519 6 : vacc_pool_ele_release( pool, ele );
520 6 : }
521 3 : if( FD_UNLIKELY( stake==min_stake ) ) return;
522 3 : }
523 :
524 6351 : vacc_t * vacc = vacc_pool_ele_acquire( pool );
525 6351 : vacc->pubkey = *pubkey;
526 6351 : vacc->node_account = *node_account;
527 6351 : vacc->stake = stake;
528 6351 : vacc->commission = commission;
529 6351 : vacc->alpenglow_rank = FD_VOTE_STAKES_ALPENGLOW_RANK_NULL;
530 6351 : memcpy( vacc->bls_key, bls_key, FD_BLS_PUBKEY_COMPRESSED_SZ );
531 6351 : vacc_heap_ele_insert( heap, vacc, pool );
532 6351 : FD_TEST( vacc_map_ele_insert( map, vacc, pool ) );
533 6351 : }
534 :
535 : void
536 : fd_vote_stakes_finalize( fd_vote_stakes_t * vote_stakes,
537 435 : ulong epoch ) {
538 435 : ulong epoch_idx = epoch % FD_VOTE_STAKES_EPOCH_CACHE_CNT;
539 435 : if( FD_UNLIKELY( vote_stakes->t_2_epoch[ epoch_idx ]!=epoch ) ) return;
540 :
541 435 : vacc_t * pool = t_2_vacc_pool( vote_stakes, epoch_idx );
542 435 : vacc_map_t * map = t_2_vacc_map ( vote_stakes, epoch_idx );
543 :
544 435 : vacc_rank_t rank[ FD_RUNTIME_MAX_VAT_VOTE_ACCOUNTS ];
545 435 : ulong rank_cnt = 0UL;
546 435 : for( vacc_map_iter_t iter = vacc_map_iter_init( map, pool );
547 954 : !vacc_map_iter_done( iter, map, pool );
548 519 : iter = vacc_map_iter_next( iter, map, pool ) ) {
549 519 : vacc_t * vacc = vacc_map_iter_ele( iter, map, pool );
550 519 : vacc->alpenglow_rank = FD_VOTE_STAKES_ALPENGLOW_RANK_NULL;
551 519 : if( FD_UNLIKELY( !vacc->stake ) ) continue;
552 519 : if( FD_UNLIKELY( fd_bls12_381_g1_decompress_syscall( vacc->bls_key_uncompressed, vacc->bls_key, 1 ) ) ) continue;
553 60 : FD_TEST( rank_cnt<FD_RUNTIME_MAX_VAT_VOTE_ACCOUNTS );
554 60 : rank[ rank_cnt++ ] = (vacc_rank_t){ .vacc=vacc, .drop=0UL };
555 60 : }
556 :
557 435 : vacc_rank_bls_sort_inplace( rank, rank_cnt );
558 465 : for( ulong i=1UL; i<rank_cnt; i++ ) {
559 30 : if( FD_UNLIKELY( !memcmp( rank[i].vacc->bls_key, rank[i-1UL].vacc->bls_key, FD_BLS_PUBKEY_COMPRESSED_SZ ) ) ) {
560 3 : rank[i].drop = rank[i-1UL].drop = 1UL;
561 3 : }
562 30 : }
563 :
564 435 : vacc_rank_identity_sort_inplace( rank, rank_cnt );
565 465 : for( ulong i=1UL; i<rank_cnt; i++ ) {
566 30 : if( FD_UNLIKELY( fd_pubkey_eq( &rank[i].vacc->node_account, &rank[i-1UL].vacc->node_account ) ) ) {
567 3 : rank[i].drop = rank[i-1UL].drop = 1UL;
568 3 : }
569 30 : }
570 :
571 435 : ulong survivor_cnt = 0UL;
572 495 : for( ulong i=0UL; i<rank_cnt; i++ ) {
573 60 : if( FD_LIKELY( !rank[i].drop ) ) rank[ survivor_cnt++ ] = rank[i];
574 60 : }
575 435 : vacc_rank_sort_inplace( rank, survivor_cnt );
576 :
577 483 : for( ulong i=0UL; i<survivor_cnt; i++ ) rank[i].vacc->alpenglow_rank = (ushort)i;
578 435 : }
579 :
580 : void
581 : fd_vote_stakes_purge_fork( fd_vote_stakes_t * vote_stakes,
582 1068 : ulong fork_id ) {
583 1068 : ushort bank_id = fork_id_bank_id( fork_id );
584 1068 : ushort width_id = fork_id_width_id( fork_id );
585 :
586 1068 : vacc_state_pool_idx_release( vacc_states_pool( vote_stakes ), bank_id );
587 :
588 1068 : vacc_fork_t * fork_pool = vacc_fork_pool( vote_stakes );
589 1068 : vacc_fork_t * fork = vacc_fork_pool_ele( fork_pool, width_id );
590 1068 : FD_TEST( fork->ref_cnt );
591 1068 : if( !--fork->ref_cnt ) {
592 252 : vacc_map_reset( t_1_vacc_map( vote_stakes, width_id ) );
593 252 : vacc_pool_reset( t_1_vacc_pool( vote_stakes, width_id ) );
594 252 : vacc_fork_pool_idx_release( fork_pool, width_id );
595 252 : }
596 1068 : }
597 :
598 : ulong
599 : fd_vote_stakes_new_fork( fd_vote_stakes_t * vote_stakes,
600 : ulong parent_fork_id,
601 5139 : ulong epoch ) {
602 5139 : FD_TEST( epoch<=USHORT_MAX );
603 :
604 5139 : vacc_states_t * states_pool = vacc_states_pool( vote_stakes );
605 5139 : FD_CHECK_CRIT( vacc_state_pool_free( states_pool ), "vote stakes state pool exhausted" );
606 5139 : ushort parent_bank_id = fork_id_bank_id( parent_fork_id );
607 5139 : ushort bank_id = (ushort)vacc_state_pool_idx_acquire( states_pool );
608 5139 : ushort parent_epoch = fork_id_epoch( parent_fork_id );
609 5139 : ushort width_id;
610 :
611 5139 : vacc_states_t * child_state = vacc_state_pool_ele( states_pool, bank_id );
612 5139 : if( FD_LIKELY( epoch==(ulong)parent_epoch ) ) {
613 : /* Standard case: just inherit the parent's t-1 set (and increment
614 : the ref count) and copy over the t-2 state. */
615 4686 : width_id = fork_id_width_id( parent_fork_id );
616 4686 : vacc_fork_pool_ele( vacc_fork_pool( vote_stakes ), width_id )->ref_cnt++;
617 :
618 4686 : vacc_states_t const * parent_state = vacc_state_pool_ele_const( states_pool, parent_bank_id );
619 4686 : memcpy( child_state->states, parent_state->states, sizeof(child_state->states) );
620 4686 : } else {
621 : /* Epoch boundary: copy the parent's t-1 set into the t-2 set and
622 : acquire new t-1 and t-2 state. */
623 :
624 : /* Copy the t-1 set iff it's the first time crossing into a new
625 : epoch boundary. */
626 453 : ulong t_2_idx = epoch % FD_VOTE_STAKES_EPOCH_CACHE_CNT;
627 453 : if( vote_stakes->t_2_epoch[ t_2_idx ]!=epoch ) {
628 402 : ulong parent_width_id = (ulong)fork_id_width_id( parent_fork_id );
629 402 : vacc_t * t_1_pool = t_1_vacc_pool( vote_stakes, parent_width_id );
630 402 : vacc_map_t * t_1_map = t_1_vacc_map ( vote_stakes, parent_width_id );
631 402 : vacc_t * t_2_pool = t_2_vacc_pool( vote_stakes, t_2_idx );
632 402 : vacc_map_t * t_2_map = t_2_vacc_map ( vote_stakes, t_2_idx );
633 :
634 402 : vacc_map_reset( t_2_map );
635 402 : vacc_pool_reset( t_2_pool );
636 402 : for( vacc_map_iter_t iter = vacc_map_iter_init( t_1_map, t_1_pool );
637 864 : !vacc_map_iter_done( iter, t_1_map, t_1_pool );
638 462 : iter = vacc_map_iter_next( iter, t_1_map, t_1_pool ) ) {
639 462 : vacc_t const * src = vacc_map_iter_ele_const( iter, t_1_map, t_1_pool );
640 462 : vacc_t * dst = vacc_pool_ele_acquire( t_2_pool );
641 462 : dst->pubkey = src->pubkey;
642 462 : dst->node_account = src->node_account;
643 462 : dst->stake = src->stake;
644 462 : dst->commission = src->commission;
645 462 : dst->alpenglow_rank = src->alpenglow_rank;
646 462 : memcpy( dst->bls_key, src->bls_key, FD_BLS_PUBKEY_COMPRESSED_SZ );
647 462 : memcpy( dst->bls_key_uncompressed, src->bls_key_uncompressed, FD_BLS_PUBKEY_UNCOMPRESSED_SZ );
648 462 : FD_TEST( vacc_map_ele_insert( t_2_map, dst, t_2_pool ) );
649 462 : }
650 402 : vote_stakes->t_2_epoch[ t_2_idx ] = epoch;
651 402 : fd_vote_stakes_finalize( vote_stakes, epoch );
652 402 : }
653 :
654 : /* Reset the t-2 states for the new fork and prepare the heap for
655 : insertion into the new t-1 set. */
656 453 : vacc_fork_t * fork_pool = vacc_fork_pool( vote_stakes );
657 453 : FD_CHECK_CRIT( vacc_fork_pool_free( fork_pool ), "vote stakes width pool exhausted" );
658 453 : width_id = (ushort)vacc_fork_pool_idx_acquire( fork_pool );
659 453 : vacc_fork_pool_ele( fork_pool, width_id )->ref_cnt = 1U;
660 453 : vacc_map_reset( t_1_vacc_map( vote_stakes, width_id ) );
661 453 : vacc_pool_reset( t_1_vacc_pool( vote_stakes, width_id ) );
662 453 : memset( child_state->states, 0, sizeof(child_state->states) );
663 :
664 453 : FD_TEST( vacc_heap_new( vacc_heap( vote_stakes ), FD_RUNTIME_MAX_VAT_VOTE_ACCOUNTS ) );
665 453 : vote_stakes->min_stake_wmark = 0UL;
666 453 : }
667 :
668 5139 : ulong fork_id = 0UL;
669 5139 : fork_id_set_bank_id( &fork_id, bank_id );
670 5139 : fork_id_set_width_id( &fork_id, width_id );
671 5139 : fork_id_set_epoch( &fork_id, (ushort)epoch );
672 5139 : return fork_id;
673 5139 : }
674 :
675 : void
676 : fd_vote_stakes_update_state( fd_vote_stakes_t * vote_stakes,
677 : ulong fork_id,
678 : fd_pubkey_t const * pubkey,
679 : ulong last_vote_slot,
680 : long last_vote_ts,
681 4404 : uchar is_valid ) {
682 4404 : ulong epoch_idx = (ulong)fork_id_epoch( fork_id ) % FD_VOTE_STAKES_EPOCH_CACHE_CNT;
683 4404 : vacc_t * pool = t_2_vacc_pool( vote_stakes, epoch_idx );
684 4404 : vacc_map_t * map = t_2_vacc_map ( vote_stakes, epoch_idx );
685 :
686 4404 : vacc_t const * vacc = vacc_map_ele_query_const( map, pubkey, NULL, pool );
687 4404 : if( FD_UNLIKELY( !vacc ) ) return;
688 :
689 4344 : ulong vacc_idx = vacc_pool_idx( pool, vacc );
690 4344 : vacc_states_t * states = vacc_state_pool_ele( vacc_states_pool( vote_stakes ), fork_id_bank_id( fork_id ) );
691 4344 : states->states[ vacc_idx ].is_valid = (uchar)!!is_valid;
692 4344 : if( is_valid ) {
693 4338 : states->states[ vacc_idx ].last_vote_slot = last_vote_slot;
694 4338 : states->states[ vacc_idx ].last_vote_ts = last_vote_ts;
695 4338 : }
696 4344 : }
697 :
698 : void
699 : fd_vote_stakes_refresh( fd_vote_stakes_t * vote_stakes,
700 : ulong fork_id,
701 : fd_accdb_t * accdb,
702 282 : fd_accdb_fork_id_t accdb_fork_id ) {
703 282 : uchar __attribute__((aligned(FD_VOTE_STAKES_ITER_ALIGN))) iter_mem[ FD_VOTE_STAKES_ITER_FOOTPRINT ];
704 :
705 282 : for( fd_vote_stakes_iter_t * iter = fd_vote_stakes_iter_init( vote_stakes, fork_id, FD_VOTE_STAKES_ITER_T_2, iter_mem );
706 633 : !fd_vote_stakes_iter_done( vote_stakes, fork_id, FD_VOTE_STAKES_ITER_T_2, iter );
707 351 : fd_vote_stakes_iter_next( vote_stakes, fork_id, FD_VOTE_STAKES_ITER_T_2, iter ) ) {
708 351 : fd_pubkey_t pubkey;
709 351 : fd_vote_stakes_iter_ele( vote_stakes, fork_id, FD_VOTE_STAKES_ITER_T_2, iter,
710 351 : &pubkey, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL );
711 :
712 351 : fd_acc_t acc = fd_accdb_read_one( accdb, accdb_fork_id, pubkey.uc );
713 351 : if( FD_UNLIKELY( !acc.lamports || !fd_vsv_is_correct_size_owner_and_init( acc.owner, acc.data, acc.data_len ) ) ) {
714 3 : fd_accdb_unread_one( accdb, &acc );
715 3 : fd_vote_stakes_update_state( vote_stakes, fork_id, &pubkey, 0UL, 0L, 0 );
716 3 : continue;
717 3 : }
718 :
719 348 : fd_vote_block_timestamp_t last_vote;
720 348 : FD_TEST( !fd_vote_account_last_timestamp( acc.data, acc.data_len, &last_vote ) );
721 348 : fd_vote_stakes_update_state( vote_stakes, fork_id, &pubkey, last_vote.slot, last_vote.timestamp, 1 );
722 348 : fd_accdb_unread_one( accdb, &acc );
723 348 : }
724 282 : }
725 :
726 : int
727 : fd_vote_stakes_query_t_1( fd_vote_stakes_t const * vote_stakes,
728 : ulong fork_id,
729 : fd_pubkey_t const * pubkey,
730 : fd_pubkey_t * node_account_out_opt,
731 : ulong * stake_out_opt,
732 60 : ushort * commission_out_opt ) {
733 60 : ulong width_idx = (ulong)fork_id_width_id( fork_id );
734 60 : vacc_t * pool = t_1_vacc_pool( vote_stakes, width_idx );
735 60 : vacc_map_t * map = t_1_vacc_map ( vote_stakes, width_idx );
736 :
737 60 : vacc_t const * vacc = vacc_map_ele_query_const( map, pubkey, NULL, pool );
738 60 : if( FD_UNLIKELY( !vacc ) ) return 0;
739 :
740 45 : if( node_account_out_opt ) *node_account_out_opt = vacc->node_account;
741 45 : if( stake_out_opt ) *stake_out_opt = vacc->stake;
742 45 : if( commission_out_opt ) *commission_out_opt = vacc->commission;
743 45 : return 1;
744 60 : }
745 :
746 : int
747 : fd_vote_stakes_query_t_2( fd_vote_stakes_t const * vote_stakes,
748 : ulong fork_id,
749 : fd_pubkey_t const * pubkey,
750 : fd_pubkey_t * node_account_out_opt,
751 : ulong * stake_out_opt,
752 : ulong * last_vote_slot_out_opt,
753 : long * last_vote_ts_out_opt,
754 : ushort * commission_out_opt,
755 366 : uchar * is_valid_out_opt ) {
756 366 : ulong epoch_idx = (ulong)fork_id_epoch( fork_id ) % FD_VOTE_STAKES_EPOCH_CACHE_CNT;
757 366 : vacc_t * pool = t_2_vacc_pool( vote_stakes, epoch_idx );
758 366 : vacc_map_t * map = t_2_vacc_map ( vote_stakes, epoch_idx );
759 :
760 366 : vacc_t const * vacc = vacc_map_ele_query_const( map, pubkey, NULL, pool );
761 366 : if( FD_UNLIKELY( !vacc ) ) return 0;
762 :
763 363 : ulong vacc_idx = vacc_pool_idx( pool, vacc );
764 363 : vacc_states_t const * states = vacc_state_pool_ele_const( vacc_states_pool( vote_stakes ), fork_id_bank_id( fork_id ) );
765 :
766 363 : if( node_account_out_opt ) *node_account_out_opt = vacc->node_account;
767 363 : if( stake_out_opt ) *stake_out_opt = vacc->stake;
768 363 : if( last_vote_slot_out_opt ) *last_vote_slot_out_opt = states->states[ vacc_idx ].last_vote_slot;
769 363 : if( last_vote_ts_out_opt ) *last_vote_ts_out_opt = states->states[ vacc_idx ].last_vote_ts;
770 363 : if( commission_out_opt ) *commission_out_opt = vacc->commission;
771 363 : if( is_valid_out_opt ) *is_valid_out_opt = states->states[ vacc_idx ].is_valid;
772 363 : return 1;
773 366 : }
774 :
775 : int
776 : fd_vote_stakes_query_t_3( fd_vote_stakes_t const * vote_stakes,
777 : ulong fork_id,
778 : fd_pubkey_t const * pubkey,
779 : fd_pubkey_t * node_account_out_opt,
780 : ulong * stake_out_opt,
781 366 : ushort * commission_out_opt ) {
782 366 : ulong epoch = (ulong)fork_id_epoch( fork_id );
783 366 : if( FD_UNLIKELY( !epoch ) ) return 0;
784 :
785 366 : ulong t_3_epoch = epoch - 1UL;
786 366 : ulong t_3_idx = t_3_epoch % FD_VOTE_STAKES_EPOCH_CACHE_CNT;
787 366 : if( FD_UNLIKELY( vote_stakes->t_2_epoch[ t_3_idx ]!=t_3_epoch ) ) return 0;
788 :
789 261 : vacc_t * pool = t_2_vacc_pool( vote_stakes, t_3_idx );
790 261 : vacc_map_t * map = t_2_vacc_map ( vote_stakes, t_3_idx );
791 261 : vacc_t const * vacc = vacc_map_ele_query_const( map, pubkey, NULL, pool );
792 261 : if( FD_UNLIKELY( !vacc ) ) return 0;
793 :
794 258 : if( node_account_out_opt ) *node_account_out_opt = vacc->node_account;
795 258 : if( stake_out_opt ) *stake_out_opt = vacc->stake;
796 258 : if( commission_out_opt ) *commission_out_opt = vacc->commission;
797 258 : return 1;
798 261 : }
799 :
800 : ulong
801 : fd_vote_stakes_cnt_t_1( fd_vote_stakes_t const * vote_stakes,
802 24 : ulong fork_id ) {
803 24 : return vacc_pool_used( t_1_vacc_pool( vote_stakes, fork_id_width_id( fork_id ) ) );
804 24 : }
805 :
806 : ulong
807 : fd_vote_stakes_cnt_t_2( fd_vote_stakes_t const * vote_stakes,
808 12 : ulong fork_id ) {
809 12 : return vacc_pool_used( t_2_vacc_pool( vote_stakes, (ulong)fork_id_epoch( fork_id ) % FD_VOTE_STAKES_EPOCH_CACHE_CNT ) );
810 12 : }
811 :
812 : ulong
813 : fd_vote_stakes_total_stake( fd_vote_stakes_t const * vote_stakes,
814 15 : ulong epoch ) {
815 15 : ulong idx = epoch % FD_VOTE_STAKES_EPOCH_CACHE_CNT;
816 15 : if( FD_UNLIKELY( vote_stakes->t_2_epoch[ idx ]!=epoch ) ) return 0UL;
817 :
818 15 : vacc_t * pool = t_2_vacc_pool( vote_stakes, idx );
819 15 : vacc_map_t * map = t_2_vacc_map ( vote_stakes, idx );
820 :
821 15 : ulong total = 0UL;
822 15 : for( vacc_map_iter_t iter = vacc_map_iter_init( map, pool );
823 45 : !vacc_map_iter_done( iter, map, pool );
824 30 : iter = vacc_map_iter_next( iter, map, pool ) ) {
825 30 : total += vacc_map_iter_ele_const( iter, map, pool )->stake;
826 30 : }
827 15 : return total;
828 15 : }
829 :
830 : FD_STATIC_ASSERT( FD_VOTE_STAKES_ITER_FOOTPRINT == sizeof(vacc_map_iter_t), iter_footprint );
831 : FD_STATIC_ASSERT( FD_VOTE_STAKES_ITER_ALIGN == alignof(vacc_map_iter_t), iter_align );
832 :
833 : fd_vote_stakes_iter_t *
834 : fd_vote_stakes_iter_init( fd_vote_stakes_t const * vote_stakes,
835 : ulong fork_id,
836 : int iter_kind,
837 5739 : uchar iter_mem[ static FD_VOTE_STAKES_ITER_FOOTPRINT ] ) {
838 5739 : vacc_map_iter_t iter = {0};
839 5739 : if( FD_LIKELY( iter_kind==FD_VOTE_STAKES_ITER_T_1 ) ) {
840 594 : ulong width_idx = (ulong)fork_id_width_id( fork_id );
841 594 : vacc_t * pool = t_1_vacc_pool( vote_stakes, width_idx );
842 594 : vacc_map_t * map = t_1_vacc_map ( vote_stakes, width_idx );
843 594 : iter = vacc_map_iter_init( map, pool );
844 5145 : } else {
845 5145 : FD_TEST( iter_kind==FD_VOTE_STAKES_ITER_T_2 || iter_kind==FD_VOTE_STAKES_ITER_T_3 );
846 5145 : ulong fork_epoch = (ulong)fork_id_epoch( fork_id );
847 5145 : if( FD_LIKELY( iter_kind==FD_VOTE_STAKES_ITER_T_2 || fork_epoch ) ) {
848 5145 : ulong epoch = fork_epoch-(ulong)(iter_kind==FD_VOTE_STAKES_ITER_T_3);
849 5145 : ulong epoch_idx = epoch % FD_VOTE_STAKES_EPOCH_CACHE_CNT;
850 5145 : if( FD_LIKELY( vote_stakes->t_2_epoch[ epoch_idx ]==epoch ) ) {
851 5145 : vacc_t * pool = t_2_vacc_pool( vote_stakes, epoch_idx );
852 5145 : vacc_map_t * map = t_2_vacc_map ( vote_stakes, epoch_idx );
853 5145 : iter = vacc_map_iter_init( map, pool );
854 5145 : }
855 5145 : }
856 5145 : }
857 5739 : memcpy( iter_mem, &iter, sizeof(iter) );
858 5739 : return (fd_vote_stakes_iter_t *)iter_mem;
859 5739 : }
860 :
861 : int
862 : fd_vote_stakes_iter_done( fd_vote_stakes_t const * vote_stakes,
863 : ulong fork_id,
864 : int iter_kind,
865 12384 : fd_vote_stakes_iter_t * iter ) {
866 12384 : if( FD_LIKELY( iter_kind==FD_VOTE_STAKES_ITER_T_1 ) ) {
867 1302 : ulong width_idx = (ulong)fork_id_width_id( fork_id );
868 1302 : vacc_t * pool = t_1_vacc_pool( vote_stakes, width_idx );
869 1302 : vacc_map_t * map = t_1_vacc_map ( vote_stakes, width_idx );
870 1302 : return vacc_map_iter_done( *(vacc_map_iter_t *)iter, map, pool );
871 1302 : }
872 :
873 11082 : FD_TEST( iter_kind==FD_VOTE_STAKES_ITER_T_2 || iter_kind==FD_VOTE_STAKES_ITER_T_3 );
874 11082 : ulong fork_epoch = (ulong)fork_id_epoch( fork_id );
875 11082 : if( FD_UNLIKELY( iter_kind==FD_VOTE_STAKES_ITER_T_3 && !fork_epoch ) ) return 1;
876 :
877 11082 : ulong epoch = fork_epoch-(ulong)(iter_kind==FD_VOTE_STAKES_ITER_T_3);
878 11082 : ulong epoch_idx = epoch % FD_VOTE_STAKES_EPOCH_CACHE_CNT;
879 11082 : if( FD_UNLIKELY( vote_stakes->t_2_epoch[ epoch_idx ]!=epoch ) ) return 1;
880 :
881 11082 : vacc_t * pool = t_2_vacc_pool( vote_stakes, epoch_idx );
882 11082 : vacc_map_t * map = t_2_vacc_map ( vote_stakes, epoch_idx );
883 11082 : return vacc_map_iter_done( *(vacc_map_iter_t *)iter, map, pool );
884 11082 : }
885 :
886 : void
887 : fd_vote_stakes_iter_next( fd_vote_stakes_t const * vote_stakes,
888 : ulong fork_id,
889 : int iter_kind,
890 6657 : fd_vote_stakes_iter_t * iter ) {
891 6657 : vacc_t * pool;
892 6657 : vacc_map_t * map;
893 6657 : if( FD_LIKELY( iter_kind==FD_VOTE_STAKES_ITER_T_1 ) ) {
894 714 : ulong width_idx = (ulong)fork_id_width_id( fork_id );
895 714 : pool = t_1_vacc_pool( vote_stakes, width_idx );
896 714 : map = t_1_vacc_map ( vote_stakes, width_idx );
897 5943 : } else {
898 5943 : FD_TEST( iter_kind==FD_VOTE_STAKES_ITER_T_2 || iter_kind==FD_VOTE_STAKES_ITER_T_3 );
899 5943 : ulong epoch_idx = ((ulong)fork_id_epoch( fork_id )-(ulong)(iter_kind==FD_VOTE_STAKES_ITER_T_3)) % FD_VOTE_STAKES_EPOCH_CACHE_CNT;
900 5943 : pool = t_2_vacc_pool( vote_stakes, epoch_idx );
901 5943 : map = t_2_vacc_map ( vote_stakes, epoch_idx );
902 5943 : }
903 6657 : vacc_map_iter_t * map_iter = (vacc_map_iter_t *)iter;
904 6657 : *map_iter = vacc_map_iter_next( *map_iter, map, pool );
905 6657 : }
906 :
907 : void
908 : fd_vote_stakes_iter_ele( fd_vote_stakes_t const * vote_stakes,
909 : ulong fork_id,
910 : int iter_kind,
911 : fd_vote_stakes_iter_t * iter,
912 : fd_pubkey_t * pubkey_out,
913 : fd_pubkey_t * node_account_out_opt,
914 : ulong * stake_out_opt,
915 : ulong * last_vote_slot_out_opt,
916 : long * last_vote_ts_out_opt,
917 : ushort * commission_out_opt,
918 : uchar * is_valid_out_opt,
919 : ushort * alpenglow_rank_out_opt,
920 : uchar bls_key_out_opt[ FD_BLS_PUBKEY_COMPRESSED_SZ ],
921 6705 : uchar bls_key_uncomp_out_opt[ FD_BLS_PUBKEY_UNCOMPRESSED_SZ ] ) {
922 6705 : vacc_t * pool;
923 6705 : vacc_map_t * map;
924 6705 : if( FD_LIKELY( iter_kind==FD_VOTE_STAKES_ITER_T_1 ) ) {
925 717 : ulong width_idx = (ulong)fork_id_width_id( fork_id );
926 717 : pool = t_1_vacc_pool( vote_stakes, width_idx );
927 717 : map = t_1_vacc_map ( vote_stakes, width_idx );
928 5988 : } else {
929 5988 : FD_TEST( iter_kind==FD_VOTE_STAKES_ITER_T_2 || iter_kind==FD_VOTE_STAKES_ITER_T_3 );
930 5988 : ulong epoch_idx = ((ulong)fork_id_epoch( fork_id )-(ulong)(iter_kind==FD_VOTE_STAKES_ITER_T_3)) % FD_VOTE_STAKES_EPOCH_CACHE_CNT;
931 5988 : pool = t_2_vacc_pool( vote_stakes, epoch_idx );
932 5988 : map = t_2_vacc_map ( vote_stakes, epoch_idx );
933 5988 : }
934 6705 : vacc_t const * vacc = vacc_map_iter_ele_const( *(vacc_map_iter_t *)iter, map, pool );
935 :
936 6705 : *pubkey_out = vacc->pubkey;
937 6705 : if( node_account_out_opt ) *node_account_out_opt = vacc->node_account;
938 6705 : if( stake_out_opt ) *stake_out_opt = vacc->stake;
939 6705 : if( commission_out_opt ) *commission_out_opt = vacc->commission;
940 6705 : if( alpenglow_rank_out_opt ) *alpenglow_rank_out_opt = vacc->alpenglow_rank;
941 6705 : if( bls_key_out_opt ) memcpy( bls_key_out_opt, vacc->bls_key, FD_BLS_PUBKEY_COMPRESSED_SZ );
942 6705 : if( bls_key_uncomp_out_opt ) memcpy( bls_key_uncomp_out_opt, vacc->bls_key_uncompressed, FD_BLS_PUBKEY_UNCOMPRESSED_SZ );
943 :
944 6705 : if( last_vote_slot_out_opt || last_vote_ts_out_opt || is_valid_out_opt ) {
945 5160 : FD_TEST( iter_kind==FD_VOTE_STAKES_ITER_T_2 );
946 5160 : ulong vacc_idx = vacc_pool_idx( pool, vacc );
947 5160 : vacc_states_t const * states = vacc_state_pool_ele_const( vacc_states_pool( vote_stakes ), fork_id_bank_id( fork_id ) );
948 5160 : if( last_vote_slot_out_opt ) *last_vote_slot_out_opt = states->states[ vacc_idx ].last_vote_slot;
949 5160 : if( last_vote_ts_out_opt ) *last_vote_ts_out_opt = states->states[ vacc_idx ].last_vote_ts;
950 5160 : if( is_valid_out_opt ) *is_valid_out_opt = states->states[ vacc_idx ].is_valid;
951 5160 : }
952 6705 : }
|