Line data Source code
1 : #include "fd_stake_delegations.h"
2 : #include "fd_stakes.h"
3 : #include "../runtime/sysvar/fd_sysvar_stake_history.h"
4 : #include "../events/fd_event_runtime.h"
5 : #include "../../util/fd_hash32.h"
6 :
7 : #define POOL_NAME root_pool
8 258 : #define POOL_T fd_stake_delegation_t
9 165 : #define POOL_NEXT next_
10 : #define POOL_IDX_T uint
11 : #define POOL_LAZY 1
12 : #include "../../util/tmpl/fd_pool.c"
13 :
14 : #define MAP_NAME root_map
15 : #define MAP_KEY_T fd_pubkey_t
16 : #define MAP_ELE_T fd_stake_delegation_t
17 4206 : #define MAP_KEY stake_account
18 714 : #define MAP_KEY_EQ(k0,k1) (fd_pubkey_eq( k0, k1 ))
19 9120 : #define MAP_KEY_HASH(key,seed) (fd_hash32( key->uc, seed ))
20 4407 : #define MAP_NEXT next_
21 17889 : #define MAP_IDX_T uint
22 : #include "../../util/tmpl/fd_map_chain.c"
23 :
24 : #define MAP_NAME fork_map
25 : #define MAP_KEY_T fd_pubkey_t
26 : #define MAP_ELE_T fd_stake_delegation_t
27 450 : #define MAP_KEY stake_account
28 102 : #define MAP_KEY_EQ(k0,k1) (fd_pubkey_eq( k0, k1 ))
29 1005 : #define MAP_KEY_HASH(key,seed) (fd_hash32( key->uc, seed ))
30 1230 : #define MAP_NEXT next_
31 71535 : #define MAP_IDX_T uint
32 : #include "../../util/tmpl/fd_map_chain.c"
33 :
34 : #define POOL_NAME delta_pool
35 258 : #define POOL_T fd_stake_delegation_t
36 429 : #define POOL_NEXT next_
37 : #define POOL_IDX_T uint
38 : #define POOL_LAZY 1
39 : #include "../../util/tmpl/fd_pool.c"
40 :
41 : struct fork_pool_ele { ushort next; };
42 : typedef struct fork_pool_ele fork_pool_ele_t;
43 :
44 : #define POOL_NAME fork_pool
45 258 : #define POOL_T fork_pool_ele_t
46 : #define POOL_IDX_T ushort
47 : #include "../../util/tmpl/fd_pool.c"
48 :
49 : #define POOL_NAME pubkey_pool
50 258 : #define POOL_T fd_stake_delegation_ref_t
51 111 : #define POOL_NEXT next_
52 : #define POOL_IDX_T uint
53 : #define POOL_LAZY 1
54 : #include "../../util/tmpl/fd_pool.c"
55 :
56 : #define MAP_NAME pubkey_map
57 : #define MAP_KEY_T fd_pubkey_t
58 : #define MAP_ELE_T fd_stake_delegation_ref_t
59 4248 : #define MAP_KEY stake_account
60 816 : #define MAP_KEY_EQ(k0,k1) (fd_pubkey_eq( k0, k1 ))
61 9195 : #define MAP_KEY_HASH(key,seed) (fd_hash32( key->uc, seed ))
62 4485 : #define MAP_NEXT next_
63 18177 : #define MAP_IDX_T uint
64 : #include "../../util/tmpl/fd_map_chain.c"
65 :
66 : /* Internal getters for base map + pool */
67 :
68 : static inline fd_stake_delegation_t *
69 10605 : get_root_pool( fd_stake_delegations_t const * stake_delegations ) {
70 10605 : return fd_type_pun( (uchar *)stake_delegations + stake_delegations->pool_offset_ );
71 10605 : }
72 :
73 : static inline root_map_t *
74 9435 : get_root_map( fd_stake_delegations_t const * stake_delegations ) {
75 9435 : return fd_type_pun( (uchar *)stake_delegations + stake_delegations->map_offset_ );
76 9435 : }
77 :
78 : /* Internal getters for delta pool + fork structures */
79 :
80 : static inline fd_stake_delegation_t *
81 7899 : get_delta_pool( fd_stake_delegations_t const * stake_delegations ) {
82 7899 : return fd_type_pun( (uchar *)stake_delegations + stake_delegations->delta_pool_offset_ );
83 7899 : }
84 :
85 : static inline fork_pool_ele_t *
86 9561 : get_fork_pool( fd_stake_delegations_t const * stake_delegations ) {
87 9561 : return fd_type_pun( (uchar *)stake_delegations + stake_delegations->fork_pool_offset_ );
88 9561 : }
89 :
90 : static inline fork_map_t *
91 : get_fork_map( fd_stake_delegations_t const * stake_delegations,
92 67170 : ushort fork_idx ) {
93 67170 : ulong map_footprint = fork_map_footprint( FD_STAKE_DELEGATIONS_FORK_MAP_CHAIN_CNT );
94 67170 : return fd_type_pun( (uchar *)stake_delegations + stake_delegations->fork_map_offset_ + (ulong)fork_idx*map_footprint );
95 67170 : }
96 :
97 : static inline fd_stake_delegation_ref_t *
98 8919 : get_pubkey_pool( fd_stake_delegations_t const * stake_delegations ) {
99 8919 : return fd_type_pun( (uchar *)stake_delegations + stake_delegations->pubkey_pool_offset_ );
100 8919 : }
101 :
102 : static inline pubkey_map_t *
103 8862 : get_pubkey_map( fd_stake_delegations_t const * stake_delegations ) {
104 8862 : return fd_type_pun( (uchar *)stake_delegations + stake_delegations->pubkey_map_offset_ );
105 8862 : }
106 :
107 : static void
108 : pubkey_ref_acquire( fd_stake_delegations_t * stake_delegations,
109 4590 : fd_pubkey_t const * stake_account ) {
110 4590 : fd_stake_delegation_ref_t * pool = get_pubkey_pool( stake_delegations );
111 4590 : pubkey_map_t * map = get_pubkey_map( stake_delegations );
112 :
113 4590 : fd_stake_delegation_ref_t * ref = pubkey_map_ele_query( map, stake_account, NULL, pool );
114 4590 : if( FD_UNLIKELY( !ref ) ) {
115 4248 : FD_CHECK_CRIT( pubkey_pool_free( pool ), "no free entries in stake delegation pubkey pool" );
116 4248 : ref = pubkey_pool_ele_acquire( pool );
117 4248 : ref->stake_account = *stake_account;
118 4248 : ref->refcnt = 0U;
119 4248 : stake_delegations->pubkey_idx_wmk_ = fd_ulong_max( stake_delegations->pubkey_idx_wmk_, pubkey_pool_idx( pool, ref )+1UL );
120 4248 : FD_CHECK_CRIT( pubkey_map_ele_insert( map, ref, pool ), "unable to insert into stake delegation pubkey map" );
121 4248 : }
122 4590 : ref->refcnt++;
123 4590 : }
124 :
125 : static void
126 : pubkey_ref_release( fd_stake_delegations_t * stake_delegations,
127 303 : fd_pubkey_t const * stake_account ) {
128 303 : if( FD_UNLIKELY( stake_delegations->pubkey_fallback ) ) return;
129 :
130 267 : fd_stake_delegation_ref_t * pool = get_pubkey_pool( stake_delegations );
131 267 : pubkey_map_t * map = get_pubkey_map( stake_delegations );
132 :
133 267 : fd_stake_delegation_ref_t * ref = pubkey_map_ele_query( map, stake_account, NULL, pool );
134 267 : if( FD_UNLIKELY( !ref ) ) return;
135 267 : if( FD_UNLIKELY( !ref->refcnt ) ) return;
136 :
137 267 : if( FD_LIKELY( !--ref->refcnt ) ) {
138 81 : pubkey_map_ele_remove( map, stake_account, NULL, pool );
139 81 : pubkey_pool_ele_release( pool, ref );
140 81 : }
141 267 : }
142 :
143 : static void
144 : pubkey_fallback_enter( fd_stake_delegations_t * stake_delegations,
145 9 : fd_pubkey_t const * stake_account ) {
146 9 : if( FD_UNLIKELY( !stake_delegations->pubkey_fallback ) ) {
147 9 : FD_LOG_WARNING(( "stake delegation pool exhausted at %lu stake accounts; falling back to "
148 9 : "resolving stake delegations from the accounts database at the epoch boundary",
149 9 : fd_stake_delegations_pubkey_cnt( stake_delegations ) ));
150 9 : stake_delegations->pubkey_fallback = 1;
151 9 : }
152 9 : pubkey_ref_acquire( stake_delegations, stake_account );
153 9 : }
154 :
155 : ulong
156 4047 : fd_stake_delegations_align( void ) {
157 4047 : return FD_STAKE_DELEGATIONS_ALIGN;
158 4047 : }
159 :
160 : ulong
161 : fd_stake_delegations_footprint( ulong max_stake_accounts,
162 : ulong max_fallback_stake_accounts,
163 : ulong expected_stake_accounts,
164 651 : ulong max_live_slots ) {
165 :
166 651 : ulong map_chain_cnt = root_map_chain_cnt_est( expected_stake_accounts );
167 651 : ulong pubkey_max = max_fallback_stake_accounts;
168 651 : ulong pubkey_chain_cnt = pubkey_map_chain_cnt_est( expected_stake_accounts );
169 :
170 651 : ulong l = FD_LAYOUT_INIT;
171 651 : l = FD_LAYOUT_APPEND( l, fd_stake_delegations_align(), sizeof(fd_stake_delegations_t) );
172 651 : l = FD_LAYOUT_APPEND( l, root_pool_align(), root_pool_footprint( max_stake_accounts ) );
173 651 : l = FD_LAYOUT_APPEND( l, root_map_align(), root_map_footprint( map_chain_cnt ) );
174 651 : l = FD_LAYOUT_APPEND( l, delta_pool_align(), delta_pool_footprint( max_stake_accounts ) );
175 651 : l = FD_LAYOUT_APPEND( l, fork_pool_align(), fork_pool_footprint( max_live_slots ) );
176 651 : l = FD_LAYOUT_APPEND( l, fork_map_align(), max_live_slots*fork_map_footprint( FD_STAKE_DELEGATIONS_FORK_MAP_CHAIN_CNT ) );
177 651 : l = FD_LAYOUT_APPEND( l, pubkey_pool_align(), pubkey_pool_footprint( pubkey_max ) );
178 651 : l = FD_LAYOUT_APPEND( l, pubkey_map_align(), pubkey_map_footprint( pubkey_chain_cnt ) );
179 :
180 651 : return FD_LAYOUT_FINI( l, fd_stake_delegations_align() );
181 651 : }
182 :
183 : void *
184 : fd_stake_delegations_new( void * mem,
185 : ulong seed,
186 : ulong max_stake_accounts,
187 : ulong max_fallback_stake_accounts,
188 : ulong expected_stake_accounts,
189 135 : ulong max_live_slots ) {
190 135 : if( FD_UNLIKELY( !mem ) ) {
191 3 : FD_LOG_WARNING(( "NULL mem" ));
192 3 : return NULL;
193 3 : }
194 :
195 132 : if( FD_UNLIKELY( !max_stake_accounts ) ) {
196 3 : FD_LOG_WARNING(( "max_stake_accounts is 0" ));
197 3 : return NULL;
198 3 : }
199 :
200 129 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)mem, fd_stake_delegations_align() ) ) ) {
201 0 : FD_LOG_WARNING(( "misaligned mem" ));
202 0 : return NULL;
203 0 : }
204 :
205 129 : if( FD_UNLIKELY( max_live_slots>FD_STAKE_DELEGATIONS_FORK_MAX ) ) {
206 0 : FD_LOG_WARNING(( "max_live_slots is too large" ));
207 0 : return NULL;
208 0 : }
209 :
210 129 : ulong map_chain_cnt = root_map_chain_cnt_est( expected_stake_accounts );
211 129 : ulong pubkey_max = max_fallback_stake_accounts;
212 129 : ulong pubkey_chain_cnt = pubkey_map_chain_cnt_est( expected_stake_accounts );
213 :
214 129 : FD_SCRATCH_ALLOC_INIT( l, mem );
215 129 : fd_stake_delegations_t * stake_delegations = FD_SCRATCH_ALLOC_APPEND( l, fd_stake_delegations_align(), sizeof(fd_stake_delegations_t) );
216 129 : void * pool_mem = FD_SCRATCH_ALLOC_APPEND( l, root_pool_align(), root_pool_footprint( max_stake_accounts ) );
217 129 : void * map_mem = FD_SCRATCH_ALLOC_APPEND( l, root_map_align(), root_map_footprint( map_chain_cnt ) );
218 129 : void * delta_pool_mem = FD_SCRATCH_ALLOC_APPEND( l, delta_pool_align(), delta_pool_footprint( max_stake_accounts ) );
219 129 : void * fork_pool_mem = FD_SCRATCH_ALLOC_APPEND( l, fork_pool_align(), fork_pool_footprint( max_live_slots ) );
220 129 : void * fork_map_mem = FD_SCRATCH_ALLOC_APPEND( l, fork_map_align(), max_live_slots*fork_map_footprint( FD_STAKE_DELEGATIONS_FORK_MAP_CHAIN_CNT ) );
221 129 : void * pubkey_pool_mem = FD_SCRATCH_ALLOC_APPEND( l, pubkey_pool_align(), pubkey_pool_footprint( pubkey_max ) );
222 129 : void * pubkey_map_mem = FD_SCRATCH_ALLOC_APPEND( l, pubkey_map_align(), pubkey_map_footprint( pubkey_chain_cnt ) );
223 1977 : for( ushort i=0; i<(ushort)max_live_slots; i++ ) {
224 1848 : void * fork_map_mem_i = (uchar *)fork_map_mem + (ulong)i*fork_map_footprint( FD_STAKE_DELEGATIONS_FORK_MAP_CHAIN_CNT );
225 1848 : fork_map_t * map = fork_map_join( fork_map_new( fork_map_mem_i, FD_STAKE_DELEGATIONS_FORK_MAP_CHAIN_CNT, seed ) );
226 1848 : if( FD_UNLIKELY( !map ) ) {
227 0 : FD_LOG_WARNING(( "Failed to create fork map" ));
228 0 : return NULL;
229 0 : }
230 1848 : }
231 :
232 129 : if( FD_UNLIKELY( FD_SCRATCH_ALLOC_FINI( l, fd_stake_delegations_align() )!=(ulong)mem+fd_stake_delegations_footprint( max_stake_accounts, max_fallback_stake_accounts, expected_stake_accounts, max_live_slots ) ) ) {
233 0 : FD_LOG_WARNING(( "fd_stake_delegations_new: bad layout" ));
234 0 : return NULL;
235 0 : }
236 :
237 129 : fd_stake_delegation_t * root_pool = root_pool_join( root_pool_new( pool_mem, max_stake_accounts ) );
238 129 : if( FD_UNLIKELY( !root_pool ) ) {
239 0 : FD_LOG_WARNING(( "Failed to create stake delegations pool" ));
240 0 : return NULL;
241 0 : }
242 :
243 129 : root_map_t * root_map = root_map_join( root_map_new( map_mem, map_chain_cnt, seed ) );
244 129 : if( FD_UNLIKELY( !root_map ) ) {
245 0 : FD_LOG_WARNING(( "Failed to create stake delegations map" ));
246 0 : return NULL;
247 0 : }
248 :
249 129 : fd_stake_delegation_t * delta_pool = delta_pool_join( delta_pool_new( delta_pool_mem, max_stake_accounts ) );
250 129 : if( FD_UNLIKELY( !delta_pool ) ) {
251 0 : FD_LOG_WARNING(( "Failed to create stake delegation delta pool" ));
252 0 : return NULL;
253 0 : }
254 :
255 129 : fork_pool_ele_t * fork_pool = fork_pool_join( fork_pool_new( fork_pool_mem, max_live_slots ) );
256 129 : if( FD_UNLIKELY( !fork_pool ) ) {
257 0 : FD_LOG_WARNING(( "Failed to create fork pool" ));
258 0 : return NULL;
259 0 : }
260 :
261 129 : fd_stake_delegation_ref_t * pubkey_pool = pubkey_pool_join( pubkey_pool_new( pubkey_pool_mem, pubkey_max ) );
262 129 : if( FD_UNLIKELY( !pubkey_pool ) ) {
263 0 : FD_LOG_WARNING(( "Failed to create stake delegation pubkey pool" ));
264 0 : return NULL;
265 0 : }
266 :
267 129 : pubkey_map_t * pubkey_map = pubkey_map_join( pubkey_map_new( pubkey_map_mem, pubkey_chain_cnt, seed ) );
268 129 : if( FD_UNLIKELY( !pubkey_map ) ) {
269 0 : FD_LOG_WARNING(( "Failed to create stake delegation pubkey map" ));
270 0 : return NULL;
271 0 : }
272 :
273 129 : stake_delegations->max_stake_accounts_ = max_stake_accounts;
274 129 : stake_delegations->expected_stake_accounts_ = expected_stake_accounts;
275 129 : stake_delegations->pool_offset_ = (ulong)root_pool - (ulong)mem;
276 129 : stake_delegations->map_offset_ = (ulong)root_map - (ulong)mem;
277 129 : stake_delegations->delta_pool_offset_ = (ulong)delta_pool - (ulong)mem;
278 129 : stake_delegations->fork_pool_offset_ = (ulong)fork_pool - (ulong)mem;
279 129 : stake_delegations->fork_map_offset_ = (ulong)fork_map_mem - (ulong)mem;
280 129 : stake_delegations->pubkey_pool_offset_ = (ulong)pubkey_pool - (ulong)mem;
281 129 : stake_delegations->pubkey_map_offset_ = (ulong)pubkey_map - (ulong)mem;
282 129 : stake_delegations->max_pubkeys_ = pubkey_max;
283 129 : stake_delegations->pubkey_idx_wmk_ = 0UL;
284 :
285 129 : stake_delegations->effective_stake = 0UL;
286 129 : stake_delegations->activating_stake = 0UL;
287 129 : stake_delegations->deactivating_stake = 0UL;
288 129 : stake_delegations->frontier_query_epoch = ULONG_MAX;
289 129 : stake_delegations->pool_idx_wmk_ = 0UL;
290 129 : stake_delegations->pubkey_fallback = 0;
291 129 : stake_delegations->fp_warmed_awarded = 0;
292 :
293 129 : fd_rwlock_new( &stake_delegations->lock );
294 :
295 129 : FD_COMPILER_MFENCE();
296 129 : FD_VOLATILE( stake_delegations->magic ) = FD_STAKE_DELEGATIONS_MAGIC;
297 129 : FD_COMPILER_MFENCE();
298 :
299 129 : return mem;
300 129 : }
301 :
302 : fd_stake_delegations_t *
303 135 : fd_stake_delegations_join( void * mem ) {
304 135 : if( FD_UNLIKELY( !mem ) ) {
305 3 : FD_LOG_WARNING(( "NULL mem" ));
306 3 : return NULL;
307 3 : }
308 :
309 132 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)mem, fd_stake_delegations_align() ) ) ) {
310 0 : FD_LOG_WARNING(( "misaligned mem" ));
311 0 : return NULL;
312 0 : }
313 :
314 132 : fd_stake_delegations_t * stake_delegations = (fd_stake_delegations_t *)mem;
315 :
316 132 : if( FD_UNLIKELY( stake_delegations->magic!=FD_STAKE_DELEGATIONS_MAGIC ) ) {
317 3 : FD_LOG_WARNING(( "Invalid stake delegations magic" ));
318 3 : return NULL;
319 3 : }
320 :
321 129 : return stake_delegations;
322 132 : }
323 :
324 : void
325 3996 : fd_stake_delegations_reset( fd_stake_delegations_t * stake_delegations ) {
326 3996 : fd_rwlock_write( &stake_delegations->lock );
327 3996 : root_pool_reset( get_root_pool( stake_delegations ) );
328 3996 : root_map_reset( get_root_map( stake_delegations ) );
329 3996 : delta_pool_reset( get_delta_pool( stake_delegations ) );
330 3996 : fork_pool_ele_t * fork_pool = get_fork_pool( stake_delegations );
331 3996 : ulong max_forks = fork_pool_max( fork_pool );
332 68328 : for( ulong i=0UL; i<max_forks; i++ ) {
333 64332 : fork_map_reset( get_fork_map( stake_delegations, (ushort)i ) );
334 64332 : }
335 3996 : fork_pool_reset( fork_pool );
336 3996 : pubkey_pool_reset( get_pubkey_pool( stake_delegations ) );
337 3996 : pubkey_map_reset( get_pubkey_map( stake_delegations ) );
338 3996 : stake_delegations->effective_stake = 0UL;
339 3996 : stake_delegations->activating_stake = 0UL;
340 3996 : stake_delegations->deactivating_stake = 0UL;
341 3996 : stake_delegations->frontier_query_epoch = ULONG_MAX;
342 3996 : stake_delegations->pool_idx_wmk_ = 0UL;
343 3996 : stake_delegations->pubkey_idx_wmk_ = 0UL;
344 3996 : stake_delegations->pubkey_fallback = 0;
345 3996 : stake_delegations->fp_warmed_awarded = 0;
346 3996 : fd_rwlock_unwrite( &stake_delegations->lock );
347 3996 : }
348 :
349 : fd_stake_delegation_t const *
350 : fd_stake_delegation_root_query( fd_stake_delegations_t const * stake_delegations,
351 168 : fd_pubkey_t const * stake_account ) {
352 168 : fd_stake_delegation_t * pool = get_root_pool( stake_delegations );
353 168 : root_map_t * map = get_root_map( stake_delegations );
354 :
355 168 : return root_map_ele_query_const( map, stake_account, NULL, pool );
356 168 : }
357 :
358 : /* root_update is the unlocked core of fd_stake_delegations_root_update.
359 : Callers that already hold the lock use it directly. Returns the root
360 : pool element holding the delegation, or NULL if the root pool was
361 : exhausted and the delegation got pushed into the pubkey fallback tier
362 : instead. */
363 :
364 : static fd_stake_delegation_t *
365 : root_update( fd_stake_delegations_t * stake_delegations,
366 : fd_pubkey_t const * stake_account,
367 : fd_pubkey_t const * vote_account,
368 : ulong stake,
369 : ulong activation_epoch,
370 : ulong deactivation_epoch,
371 : ulong credits_observed,
372 : ulong lamports,
373 : uint acc_dlen,
374 4242 : uchar warmup_cooldown_rate ) {
375 4242 : fd_stake_delegation_t * pool = get_root_pool( stake_delegations );
376 4242 : root_map_t * map = get_root_map( stake_delegations );
377 :
378 4242 : fd_stake_delegation_t * stake_delegation = root_map_ele_query( map, stake_account, NULL, pool );
379 4242 : if( FD_LIKELY( !stake_delegation ) ) {
380 4137 : if( FD_UNLIKELY( !root_pool_free( pool ) ) ) {
381 6 : pubkey_fallback_enter( stake_delegations, stake_account );
382 6 : return NULL;
383 6 : }
384 4131 : stake_delegation = root_pool_ele_acquire( pool );
385 4131 : stake_delegation->stake_account = *stake_account;
386 4131 : stake_delegations->pool_idx_wmk_ = fd_ulong_max( stake_delegations->pool_idx_wmk_, root_pool_idx( pool, stake_delegation )+1UL );
387 4131 : FD_CHECK_CRIT( root_map_ele_insert( map, stake_delegation, pool ), "unable to insert stake delegation into map" );
388 4131 : pubkey_ref_acquire( stake_delegations, stake_account );
389 4131 : }
390 :
391 4236 : FD_CHECK_ERR( (long)activation_epoch <USHORT_MAX, "activation_epoch overflow" );
392 4236 : FD_CHECK_ERR( (long)deactivation_epoch<USHORT_MAX, "deactivation_epoch overflow" );
393 :
394 4236 : stake_delegation->vote_account = *vote_account;
395 4236 : stake_delegation->stake = stake;
396 4236 : stake_delegation->lamports = lamports;
397 4236 : stake_delegation->acc_dlen = acc_dlen;
398 4236 : stake_delegation->activation_epoch = (ushort)activation_epoch;
399 4236 : stake_delegation->deactivation_epoch = (ushort)deactivation_epoch;
400 4236 : stake_delegation->credits_observed = credits_observed;
401 4236 : stake_delegation->warmup_cooldown_rate = warmup_cooldown_rate;
402 4236 : stake_delegation->dne_in_root = 0;
403 4236 : stake_delegation->delta_idx = UINT_MAX;
404 4236 : stake_delegation->in_use = 1;
405 4236 : stake_delegation->state = FD_STAKE_DELEGATION_STATE_UNKNOWN;
406 :
407 4236 : return stake_delegation;
408 4236 : }
409 :
410 : void
411 : fd_stake_delegations_root_update( fd_stake_delegations_t * stake_delegations,
412 : fd_pubkey_t const * stake_account,
413 : fd_pubkey_t const * vote_account,
414 : ulong stake,
415 : ulong activation_epoch,
416 : ulong deactivation_epoch,
417 : ulong credits_observed,
418 : ulong lamports,
419 : uint acc_dlen,
420 4134 : uchar warmup_cooldown_rate ) {
421 4134 : fd_rwlock_write( &stake_delegations->lock );
422 4134 : root_update( stake_delegations, stake_account, vote_account, stake, activation_epoch,
423 4134 : deactivation_epoch, credits_observed, lamports, acc_dlen, warmup_cooldown_rate );
424 4134 : fd_rwlock_unwrite( &stake_delegations->lock );
425 4134 : }
426 :
427 : #if FD_HAS_DOUBLE
428 :
429 : ulong
430 : fd_stake_delegations_prune_inactive_root( fd_stake_delegations_t * stake_delegations,
431 : ulong epoch,
432 : fd_stake_history_t const * stake_history,
433 : ulong * warmup_cooldown_rate_epoch,
434 : int use_fixed_point_stake_math,
435 9 : fd_bank_t const * emit_bank ) {
436 9 : fd_rwlock_write( &stake_delegations->lock );
437 :
438 9 : root_map_t * map = get_root_map( stake_delegations );
439 9 : fd_stake_delegation_t * pool = get_root_pool( stake_delegations );
440 9 : ulong prev_epoch = epoch ? epoch-1UL : 0UL;
441 9 : ulong pruned = 0UL;
442 :
443 27 : for( ulong idx=0UL; idx<stake_delegations->pool_idx_wmk_; idx++ ) {
444 18 : fd_stake_delegation_t * delegation = pool+idx;
445 18 : if( FD_LIKELY( !delegation->in_use ) ) continue;
446 :
447 18 : if( FD_LIKELY( !fd_stake_delegation_is_inactive( delegation, epoch, stake_history, warmup_cooldown_rate_epoch, use_fixed_point_stake_math ) ||
448 18 : !fd_stake_delegation_is_inactive( delegation, prev_epoch, stake_history, warmup_cooldown_rate_epoch, use_fixed_point_stake_math ) ) ) continue;
449 :
450 9 : if( FD_UNLIKELY( emit_bank ) ) fd_event_runtime_stake_delegation_remove_emit( emit_bank, delegation->stake_account.uc );
451 9 : pruned++;
452 :
453 9 : fd_pubkey_t stake_account = delegation->stake_account;
454 9 : root_map_idx_remove( map, &stake_account, (uint)idx, pool );
455 9 : delegation->in_use = 0;
456 9 : root_pool_idx_release( pool, (uint)idx );
457 9 : pubkey_ref_release( stake_delegations, &stake_account );
458 9 : }
459 :
460 9 : fd_rwlock_unwrite( &stake_delegations->lock );
461 :
462 9 : return pruned;
463 9 : }
464 :
465 : static void
466 : refresh_remove( root_map_t * map,
467 : fd_stake_delegation_t * pool,
468 : pubkey_map_t * ref_map,
469 : fd_stake_delegation_ref_t * ref_pool,
470 : fd_pubkey_t const * stake_account,
471 9 : ulong ref_idx ) {
472 9 : fd_stake_delegation_t * delegation = root_map_ele_query( map, stake_account, NULL, pool );
473 9 : if( FD_LIKELY( delegation ) ) {
474 6 : root_map_idx_remove( map, stake_account, UINT_MAX, pool );
475 6 : delegation->in_use = 0;
476 6 : root_pool_ele_release( pool, delegation );
477 6 : }
478 9 : pubkey_map_ele_remove( ref_map, stake_account, NULL, ref_pool );
479 9 : ref_pool[ ref_idx ].refcnt = 0U;
480 9 : pubkey_pool_ele_release( ref_pool, &ref_pool[ ref_idx ] );
481 9 : }
482 :
483 : void
484 : fd_stake_delegations_refresh( fd_stake_delegations_t * stake_delegations,
485 : ulong epoch,
486 : fd_stake_history_t const * stake_history,
487 : ulong * warmup_cooldown_rate_epoch,
488 : int use_fixed_point_stake_math,
489 : int remove_inactive_stakes,
490 : fd_accdb_t * accdb,
491 9 : fd_accdb_fork_id_t fork_id ) {
492 9 : fd_rwlock_write( &stake_delegations->lock );
493 :
494 9 : int history_contiguous = fd_sysvar_stake_history_is_contiguous( stake_history );
495 :
496 9 : stake_delegations->effective_stake = 0UL;
497 9 : stake_delegations->activating_stake = 0UL;
498 9 : stake_delegations->deactivating_stake = 0UL;
499 :
500 9 : root_map_t * map = get_root_map( stake_delegations );
501 9 : fd_stake_delegation_t * pool = get_root_pool( stake_delegations );
502 9 : pubkey_map_t * ref_map = get_pubkey_map( stake_delegations );
503 9 : fd_stake_delegation_ref_t * ref_pool = get_pubkey_pool( stake_delegations );
504 :
505 : /* Drive the refresh off the pubkey tier rather than the root pool. The
506 : tier is a superset of the root by construction, so in the normal case
507 : this visits exactly the same accounts, and in fallback mode it also
508 : visits the accounts that never made it into the root map. Refresh
509 : runs at boot before any fork exists, which is what makes it safe to
510 : rebuild both tiers in place and to reclaim entries whose refcounts
511 : fallback mode left meaningless. */
512 :
513 9 : ulong const wmk = stake_delegations->pubkey_idx_wmk_;
514 :
515 12 : #define BATCH 64UL
516 9 : uchar const * pubkeys[ BATCH ];
517 9 : int writable[ BATCH ];
518 9 : fd_acc_t accs[ BATCH ];
519 9 : ulong ref_idx[ BATCH ];
520 :
521 9 : ulong i = 0UL;
522 18 : while( i<wmk ) {
523 9 : ulong batch_n = 0UL;
524 21 : while( i<wmk && batch_n<BATCH ) {
525 12 : if( FD_LIKELY( ref_pool[ i ].refcnt ) ) {
526 12 : pubkeys[ batch_n ] = ref_pool[ i ].stake_account.uc;
527 12 : writable[ batch_n ] = 0;
528 12 : ref_idx[ batch_n ] = i;
529 12 : batch_n++;
530 12 : }
531 12 : i++;
532 12 : }
533 9 : if( FD_UNLIKELY( !batch_n ) ) continue;
534 :
535 9 : fd_accdb_acquire( accdb, fork_id, batch_n, pubkeys, writable, accs );
536 :
537 21 : for( ulong j=0UL; j<batch_n; j++ ) {
538 12 : fd_pubkey_t const * stake_account = (fd_pubkey_t const *)pubkeys[ j ];
539 12 : fd_stake_state_t const * stake = accs[ j ].lamports ? fd_stakes_get_state( &accs[ j ] ) : NULL;
540 :
541 12 : if( FD_UNLIKELY( !stake || stake->stake_type!=FD_STAKE_STATE_STAKE ) ) {
542 0 : refresh_remove( map, pool, ref_map, ref_pool, stake_account, ref_idx[ j ] );
543 0 : continue;
544 0 : }
545 :
546 12 : fd_delegation_t const * account_delegation = &stake->stake.stake.delegation;
547 12 : ulong prev_epoch = epoch ? epoch-1UL : 0UL;
548 12 : if( FD_UNLIKELY( remove_inactive_stakes &&
549 12 : fd_delegation_is_inactive( account_delegation, epoch, stake_history, warmup_cooldown_rate_epoch, use_fixed_point_stake_math ) &&
550 12 : fd_delegation_is_inactive( account_delegation, prev_epoch, stake_history, warmup_cooldown_rate_epoch, use_fixed_point_stake_math ) ) ) {
551 9 : refresh_remove( map, pool, ref_map, ref_pool, stake_account, ref_idx[ j ] );
552 9 : continue;
553 9 : }
554 :
555 3 : fd_stake_delegation_t * delegation = root_update(
556 3 : stake_delegations,
557 3 : stake_account,
558 3 : &stake->stake.stake.delegation.voter_pubkey,
559 3 : stake->stake.stake.delegation.stake,
560 3 : stake->stake.stake.delegation.activation_epoch,
561 3 : stake->stake.stake.delegation.deactivation_epoch,
562 3 : stake->stake.stake.credits_observed,
563 3 : accs[ j ].lamports,
564 3 : (uint)accs[ j ].data_len,
565 3 : fd_stake_warmup_cooldown_rate( epoch, warmup_cooldown_rate_epoch ) );
566 :
567 3 : fd_stake_history_entry_t history = fd_delegation_activation_status( &stake->stake.stake.delegation, epoch, stake_history, warmup_cooldown_rate_epoch, use_fixed_point_stake_math );
568 3 : stake_delegations->effective_stake += history.effective;
569 3 : stake_delegations->activating_stake += history.activating;
570 3 : stake_delegations->deactivating_stake += history.deactivating;
571 :
572 : /* A delegation that the root pool couldn't take has no element to
573 : tag. The fallback tier resolves it from the accounts database
574 : at the boundary and hands out an untagged copy. */
575 3 : if( FD_LIKELY( delegation ) ) {
576 3 : uchar state = fd_stake_delegation_classify( delegation, history, epoch );
577 3 : delegation->state = !history_contiguous ? FD_STAKE_DELEGATION_STATE_UNKNOWN : state;
578 3 : if( FD_LIKELY( delegation->state==FD_STAKE_DELEGATION_STATE_WARMED && !use_fixed_point_stake_math ) ) {
579 0 : stake_delegations->fp_warmed_awarded = 1;
580 0 : }
581 3 : }
582 3 : }
583 :
584 9 : fd_accdb_release( accdb, batch_n, accs );
585 9 : }
586 9 : #undef BATCH
587 :
588 : /* Every surviving entry now holds exactly one reference, the root map's,
589 : because there are no fork deltas at boot. Rewriting the refcounts
590 : repairs any that fallback mode left unpaired. */
591 21 : for( ulong idx=0UL; idx<stake_delegations->pubkey_idx_wmk_; idx++ ) {
592 12 : if( FD_LIKELY( ref_pool[ idx ].refcnt ) ) ref_pool[ idx ].refcnt = 1U;
593 12 : }
594 :
595 : /* If pruning freed enough room for every account to sit in the root map,
596 : the fallback is no longer needed. This is the only place the sticky
597 : flag is cleared. */
598 9 : if( FD_UNLIKELY( stake_delegations->pubkey_fallback ) &&
599 9 : pubkey_pool_used( ref_pool )==root_pool_used( pool ) ) {
600 3 : FD_LOG_NOTICE(( "stake delegations no longer need the pubkey fallback; %lu stake accounts fit in the root map",
601 3 : root_pool_used( pool ) ));
602 3 : stake_delegations->pubkey_fallback = 0;
603 3 : }
604 :
605 9 : fd_rwlock_unwrite( &stake_delegations->lock );
606 9 : }
607 :
608 : #endif
609 :
610 : ulong
611 99 : fd_stake_delegations_base_cnt( fd_stake_delegations_t const * stake_delegations ) {
612 99 : return root_pool_used( get_root_pool( stake_delegations ) );
613 99 : }
614 :
615 : ulong
616 57 : fd_stake_delegations_pubkey_cnt( fd_stake_delegations_t const * stake_delegations ) {
617 57 : return pubkey_pool_used( get_pubkey_pool( stake_delegations ) );
618 57 : }
619 :
620 : /* Fork-aware delta operations */
621 :
622 : ushort
623 4833 : fd_stake_delegations_new_fork( fd_stake_delegations_t * stake_delegations ) {
624 4833 : fd_rwlock_write( &stake_delegations->lock );
625 4833 : fork_pool_ele_t * fork_pool = get_fork_pool( stake_delegations );
626 4833 : FD_CHECK_CRIT( fork_pool_free( fork_pool ), "no free forks in pool. The system has forked too wide." );
627 4833 : ushort fork_idx = (ushort)fork_pool_idx_acquire( fork_pool );
628 4833 : fd_rwlock_unwrite( &stake_delegations->lock );
629 :
630 4833 : return fork_idx;
631 4833 : }
632 :
633 : void
634 : fd_stake_delegations_fork_update( fd_stake_delegations_t * stake_delegations,
635 : ushort fork_idx,
636 : fd_pubkey_t const * stake_account,
637 : fd_pubkey_t const * vote_account,
638 : ulong stake,
639 : ulong activation_epoch,
640 : ulong deactivation_epoch,
641 : ulong credits_observed,
642 : ulong lamports,
643 : uint acc_dlen,
644 468 : uchar warmup_cooldown_rate ) {
645 468 : fd_rwlock_write( &stake_delegations->lock );
646 :
647 468 : fd_stake_delegation_t * delta_pool = get_delta_pool( stake_delegations );
648 468 : fork_map_t * map = get_fork_map( stake_delegations, fork_idx );
649 468 : fd_stake_delegation_t * stake_delegation = fork_map_ele_query( map, stake_account, NULL, delta_pool );
650 468 : if( FD_LIKELY( !stake_delegation ) ) {
651 408 : if( FD_UNLIKELY( !delta_pool_free( delta_pool ) ) ) {
652 : /* The delta pool cannot take this stake account. Record it in the
653 : pubkey fallback tier and drop the delegation state on the floor:
654 : the epoch boundary will read it back out of the accounts
655 : database. */
656 3 : pubkey_fallback_enter( stake_delegations, stake_account );
657 405 : } else {
658 405 : stake_delegation = delta_pool_ele_acquire( delta_pool );
659 405 : stake_delegation->stake_account = *stake_account;
660 405 : fork_map_ele_insert( map, stake_delegation, delta_pool );
661 405 : pubkey_ref_acquire( stake_delegations, stake_account );
662 405 : }
663 408 : }
664 :
665 468 : if( FD_LIKELY( stake_delegation ) ) {
666 465 : FD_CHECK_ERR( (long)activation_epoch <USHORT_MAX, "activation_epoch overflow" );
667 465 : FD_CHECK_ERR( (long)deactivation_epoch<USHORT_MAX, "deactivation_epoch overflow" );
668 465 : stake_delegation->vote_account = *vote_account;
669 465 : stake_delegation->stake = stake;
670 465 : stake_delegation->lamports = lamports;
671 465 : stake_delegation->acc_dlen = acc_dlen;
672 465 : stake_delegation->activation_epoch = (ushort)activation_epoch;
673 465 : stake_delegation->deactivation_epoch = (ushort)deactivation_epoch;
674 465 : stake_delegation->credits_observed = credits_observed;
675 465 : stake_delegation->warmup_cooldown_rate = warmup_cooldown_rate;
676 465 : stake_delegation->is_tombstone = 0;
677 465 : stake_delegation->state = FD_STAKE_DELEGATION_STATE_UNKNOWN;
678 465 : }
679 :
680 468 : fd_rwlock_unwrite( &stake_delegations->lock );
681 468 : }
682 :
683 : void
684 : fd_stake_delegations_fork_remove( fd_stake_delegations_t * stake_delegations,
685 : ushort fork_idx,
686 87 : fd_pubkey_t const * stake_account ) {
687 87 : fd_rwlock_write( &stake_delegations->lock );
688 :
689 87 : fd_stake_delegation_t * delta_pool = get_delta_pool( stake_delegations );
690 87 : fork_map_t * map = get_fork_map( stake_delegations, fork_idx );
691 87 : fd_stake_delegation_t * stake_delegation = fork_map_ele_query( map, stake_account, NULL, delta_pool );
692 87 : if( FD_LIKELY( !stake_delegation ) ) {
693 45 : if( FD_UNLIKELY( !delta_pool_free( delta_pool ) ) ) {
694 0 : pubkey_fallback_enter( stake_delegations, stake_account );
695 45 : } else {
696 45 : stake_delegation = delta_pool_ele_acquire( delta_pool );
697 45 : stake_delegation->stake_account = *stake_account;
698 45 : fork_map_ele_insert( map, stake_delegation, delta_pool );
699 45 : pubkey_ref_acquire( stake_delegations, stake_account );
700 45 : }
701 45 : }
702 :
703 87 : if( FD_LIKELY( stake_delegation ) ) {
704 87 : stake_delegation->lamports = 0UL;
705 87 : stake_delegation->acc_dlen = 0U;
706 87 : stake_delegation->is_tombstone = 1;
707 87 : stake_delegation->state = FD_STAKE_DELEGATION_STATE_UNKNOWN;
708 :
709 87 : FD_BASE58_ENCODE_32_BYTES( stake_delegation->stake_account.uc, stake_account_out );
710 87 : FD_LOG_DEBUG(( "fork_remove: stake_account=%s", stake_account_out ));
711 87 : }
712 :
713 87 : fd_rwlock_unwrite( &stake_delegations->lock );
714 87 : }
715 :
716 : void
717 : fd_stake_delegations_evict_fork( fd_stake_delegations_t * stake_delegations,
718 741 : ushort fork_idx ) {
719 741 : if( fork_idx==USHORT_MAX ) return;
720 :
721 732 : fd_rwlock_write( &stake_delegations->lock );
722 :
723 732 : fd_stake_delegation_t * delta_pool = get_delta_pool( stake_delegations );
724 732 : fork_map_t * fork_map = get_fork_map( stake_delegations, fork_idx );
725 :
726 732 : fork_map_iter_t iter = fork_map_iter_init( fork_map, delta_pool );
727 1014 : while( !fork_map_iter_done( iter, fork_map, delta_pool ) ) {
728 282 : fd_stake_delegation_t * ele = fork_map_iter_ele( iter, fork_map, delta_pool );
729 282 : iter = fork_map_iter_next( iter, fork_map, delta_pool );
730 282 : pubkey_ref_release( stake_delegations, &ele->stake_account );
731 282 : delta_pool_ele_release( delta_pool, ele );
732 282 : }
733 732 : fork_map_reset( fork_map );
734 :
735 732 : fork_pool_idx_release( get_fork_pool( stake_delegations ), fork_idx );
736 :
737 732 : fd_rwlock_unwrite( &stake_delegations->lock );
738 732 : }
739 :
740 : void
741 : fd_stake_delegations_apply_fork_delta( ulong epoch,
742 : fd_stake_history_t const * stake_history,
743 : ulong * warmup_cooldown_rate_epoch,
744 : int use_fixed_point_stake_math,
745 : fd_stake_delegations_t * stake_delegations,
746 : ushort fork_idx,
747 555 : fd_stake_delegations_delta_stats_t * stake_delegations_delta_stats ) {
748 :
749 555 : ulong upserts = 0UL;
750 555 : ulong removes = 0UL;
751 :
752 555 : fd_rwlock_write( &stake_delegations->lock );
753 :
754 555 : int history_contiguous = fd_sysvar_stake_history_is_contiguous( stake_history );
755 :
756 555 : fd_stake_delegation_t * delta_pool = get_delta_pool( stake_delegations );
757 555 : fork_map_t * fork_map = get_fork_map( stake_delegations, fork_idx );
758 :
759 555 : for( fork_map_iter_t iter = fork_map_iter_init( fork_map, delta_pool );
760 675 : !fork_map_iter_done( iter, fork_map, delta_pool );
761 555 : iter = fork_map_iter_next( iter, fork_map, delta_pool ) ) {
762 120 : fd_stake_delegation_t * stake_delegation = fork_map_iter_ele( iter, fork_map, delta_pool );
763 120 : if( FD_LIKELY( !stake_delegation->is_tombstone ) ) {
764 105 : upserts++;
765 : /* If the acc in the delta is an update:
766 : - If the acc already exists, subtract the old version's stake
767 : - Insert/update the new version
768 : - Add the new version's stake to the totals */
769 105 : fd_stake_delegation_t const * old_delegation = fd_stake_delegation_root_query( stake_delegations, &stake_delegation->stake_account );
770 105 : if( FD_LIKELY( old_delegation ) ) {
771 66 : fd_stake_history_entry_t old_entry = fd_stake_delegation_activation_status( old_delegation, epoch, stake_history, warmup_cooldown_rate_epoch, use_fixed_point_stake_math );
772 66 : stake_delegations->effective_stake -= old_entry.effective;
773 66 : stake_delegations->activating_stake -= old_entry.activating;
774 66 : stake_delegations->deactivating_stake -= old_entry.deactivating;
775 66 : }
776 :
777 105 : fd_stake_delegation_t * root_ele = root_update(
778 105 : stake_delegations,
779 105 : &stake_delegation->stake_account,
780 105 : &stake_delegation->vote_account,
781 105 : stake_delegation->stake,
782 105 : stake_delegation->activation_epoch==(ushort)USHORT_MAX ? ULONG_MAX : stake_delegation->activation_epoch,
783 105 : stake_delegation->deactivation_epoch==(ushort)USHORT_MAX ? ULONG_MAX : stake_delegation->deactivation_epoch,
784 105 : stake_delegation->credits_observed,
785 105 : stake_delegation->lamports,
786 105 : stake_delegation->acc_dlen,
787 105 : stake_delegation->warmup_cooldown_rate );
788 :
789 105 : fd_stake_history_entry_t new_acc = fd_stake_delegation_activation_status( stake_delegation, epoch, stake_history, warmup_cooldown_rate_epoch, use_fixed_point_stake_math );
790 105 : stake_delegations->effective_stake += new_acc.effective;
791 105 : stake_delegations->activating_stake += new_acc.activating;
792 105 : stake_delegations->deactivating_stake += new_acc.deactivating;
793 :
794 : /* A delegation that the root pool couldn't take has no element to
795 : tag. The fallback tier resolves it from the accounts database
796 : at the boundary and hands out an untagged copy. */
797 105 : if( FD_LIKELY( root_ele ) ) {
798 105 : uchar state = fd_stake_delegation_classify( root_ele, new_acc, epoch );
799 105 : root_ele->state = !history_contiguous ? FD_STAKE_DELEGATION_STATE_UNKNOWN : state;
800 105 : if( FD_LIKELY( root_ele->state==FD_STAKE_DELEGATION_STATE_WARMED && !use_fixed_point_stake_math ) ) {
801 57 : stake_delegations->fp_warmed_awarded = 1;
802 57 : }
803 105 : }
804 105 : } else {
805 15 : removes++;
806 : /* If the stake delegation in the delta is a tombstone, just
807 : remove the stake delegation from the root map and subtract
808 : its stake from the totals. */
809 15 : fd_stake_delegation_t * root_pool = get_root_pool( stake_delegations );
810 15 : root_map_t * root_map = get_root_map( stake_delegations );
811 15 : ulong delegation_idx = root_map_idx_query( root_map, &stake_delegation->stake_account, UINT_MAX, root_pool );
812 15 : if( FD_LIKELY( delegation_idx!=UINT_MAX ) ) {
813 12 : fd_stake_delegation_t * old_delegation = root_pool + delegation_idx;
814 12 : fd_stake_history_entry_t old_entry = fd_stake_delegation_activation_status( old_delegation, epoch, stake_history, warmup_cooldown_rate_epoch, use_fixed_point_stake_math );
815 12 : stake_delegations->effective_stake -= old_entry.effective;
816 12 : stake_delegations->activating_stake -= old_entry.activating;
817 12 : stake_delegations->deactivating_stake -= old_entry.deactivating;
818 12 : root_map_idx_remove( root_map, &stake_delegation->stake_account, delegation_idx, root_pool );
819 12 : old_delegation->in_use = 0;
820 12 : root_pool_idx_release( root_pool, delegation_idx );
821 12 : pubkey_ref_release( stake_delegations, &stake_delegation->stake_account );
822 12 : }
823 15 : }
824 120 : }
825 555 : FD_LOG_DEBUG(( "effective_stake=%lu, activating_stake=%lu, deactivating_stake=%lu", stake_delegations->effective_stake, stake_delegations->activating_stake, stake_delegations->deactivating_stake ));
826 :
827 555 : if( FD_UNLIKELY( stake_delegations_delta_stats ) ) {
828 531 : stake_delegations_delta_stats->upserts += upserts;
829 531 : stake_delegations_delta_stats->removes += removes;
830 531 : }
831 :
832 555 : fd_rwlock_unwrite( &stake_delegations->lock );
833 555 : }
834 :
835 : void
836 0 : fd_stake_delegations_iter_advance_fallback( fd_stake_delegations_iter_t * iter ) {
837 0 : fd_stake_delegations_t const * stake_delegations = iter->stake_delegations;
838 0 : fd_stake_delegation_ref_t * pool = get_pubkey_pool( stake_delegations );
839 :
840 0 : for(;;) {
841 0 : if( FD_LIKELY( iter->batch_idx<iter->batch_cnt ) ) {
842 0 : iter->ele = &iter->batch[ iter->batch_idx ];
843 0 : iter->idx = iter->batch_pool_idx[ iter->batch_idx ];
844 0 : return;
845 0 : }
846 :
847 0 : if( FD_UNLIKELY( iter->scan_idx>=iter->wmk ) ) {
848 0 : iter->ele = NULL;
849 0 : return;
850 0 : }
851 :
852 0 : uchar const * pubkeys [ FD_STAKE_DELEGATIONS_ITER_BATCH ];
853 0 : int writable[ FD_STAKE_DELEGATIONS_ITER_BATCH ];
854 0 : ulong pool_idx[ FD_STAKE_DELEGATIONS_ITER_BATCH ];
855 0 : fd_acc_t accs [ FD_STAKE_DELEGATIONS_ITER_BATCH ];
856 :
857 0 : ulong batch_n = 0UL;
858 0 : while( iter->scan_idx<iter->wmk && batch_n<FD_STAKE_DELEGATIONS_ITER_BATCH ) {
859 0 : fd_stake_delegation_ref_t * ref = pool + iter->scan_idx;
860 0 : if( FD_LIKELY( ref->refcnt ) ) {
861 0 : pubkeys [ batch_n ] = ref->stake_account.uc;
862 0 : writable[ batch_n ] = 0;
863 0 : pool_idx[ batch_n ] = iter->scan_idx;
864 0 : batch_n++;
865 0 : }
866 0 : iter->scan_idx++;
867 0 : }
868 0 : if( FD_UNLIKELY( !batch_n ) ) continue;
869 :
870 0 : fd_accdb_acquire( iter->accdb, iter->accdb_fork_id, batch_n, pubkeys, writable, accs );
871 :
872 0 : ulong out = 0UL;
873 0 : for( ulong j=0UL; j<batch_n; j++ ) {
874 0 : if( FD_UNLIKELY( !accs[ j ].lamports ) ) continue;
875 :
876 0 : fd_stake_state_t const * stake = fd_stakes_get_state( &accs[ j ] );
877 0 : if( FD_UNLIKELY( !stake || stake->stake_type!=FD_STAKE_STATE_STAKE ) ) continue;
878 :
879 0 : fd_delegation_t const * delegation = &stake->stake.stake.delegation;
880 0 : fd_stake_delegation_t * ele = &iter->batch[ out ];
881 :
882 0 : FD_CHECK_ERR( (long)delegation->activation_epoch <USHORT_MAX, "activation_epoch overflow" );
883 0 : FD_CHECK_ERR( (long)delegation->deactivation_epoch<USHORT_MAX, "deactivation_epoch overflow" );
884 0 : ele->stake_account = *(fd_pubkey_t const *)pubkeys[ j ];
885 0 : ele->vote_account = delegation->voter_pubkey;
886 0 : ele->stake = delegation->stake;
887 0 : ele->lamports = accs[ j ].lamports;
888 0 : ele->credits_observed = stake->stake.stake.credits_observed;
889 0 : ele->acc_dlen = (uint)accs[ j ].data_len;
890 0 : ele->next_ = UINT_MAX;
891 0 : ele->delta_idx = UINT_MAX;
892 0 : ele->activation_epoch = (ushort)fd_ulong_min( delegation->activation_epoch, USHORT_MAX );
893 0 : ele->deactivation_epoch = (ushort)fd_ulong_min( delegation->deactivation_epoch, USHORT_MAX );
894 0 : ele->is_tombstone = 0;
895 0 : ele->warmup_cooldown_rate = fd_stake_warmup_cooldown_rate( iter->epoch, iter->warmup_cooldown_rate_epoch );
896 0 : ele->in_use = 1;
897 0 : ele->state = FD_STAKE_DELEGATION_STATE_UNKNOWN; /* Resolved copies are never tagged. */
898 :
899 0 : iter->batch_pool_idx[ out ] = pool_idx[ j ];
900 0 : out++;
901 0 : }
902 :
903 0 : fd_accdb_release( iter->accdb, batch_n, accs );
904 :
905 0 : iter->batch_cnt = out;
906 0 : iter->batch_idx = 0UL;
907 0 : }
908 0 : }
909 :
910 : fd_stake_delegations_iter_t *
911 : fd_stake_delegations_iter_init( fd_stake_delegations_iter_t * iter,
912 : fd_stake_delegations_t const * stake_delegations,
913 : fd_accdb_t * accdb,
914 : fd_accdb_fork_id_t accdb_fork_id,
915 : ulong epoch,
916 1065 : ulong * warmup_cooldown_rate_epoch ) {
917 1065 : if( FD_UNLIKELY( !stake_delegations ) ) {
918 0 : FD_LOG_CRIT(( "NULL stake_delegations" ));
919 0 : }
920 :
921 1065 : iter->root_pool = get_root_pool( stake_delegations );
922 1065 : iter->delta_pool = get_delta_pool( stake_delegations );
923 1065 : iter->stake_delegations = stake_delegations;
924 1065 : iter->idx = 0UL;
925 1065 : iter->scan_idx = 0UL;
926 1065 : iter->batch_cnt = 0UL;
927 1065 : iter->batch_idx = 0UL;
928 1065 : iter->fallback = stake_delegations->pubkey_fallback;
929 :
930 1065 : if( FD_UNLIKELY( iter->fallback ) ) {
931 0 : if( FD_UNLIKELY( !accdb ) ) {
932 0 : FD_LOG_CRIT(( "stake delegations are in pubkey fallback mode but no accounts database was "
933 0 : "supplied to resolve them; iterating the root map alone would silently drop "
934 0 : "stake accounts" ));
935 0 : }
936 0 : iter->accdb = accdb;
937 0 : iter->accdb_fork_id = accdb_fork_id;
938 0 : iter->epoch = epoch;
939 0 : iter->warmup_cooldown_rate_epoch = warmup_cooldown_rate_epoch;
940 0 : iter->wmk = stake_delegations->pubkey_idx_wmk_;
941 0 : fd_stake_delegations_iter_advance_fallback( iter );
942 0 : return iter;
943 0 : }
944 :
945 1065 : iter->wmk = stake_delegations->pool_idx_wmk_;
946 1065 : fd_stake_delegations_iter_advance_private( iter );
947 :
948 1065 : return iter;
949 1065 : }
950 :
951 : static void
952 : fd_stake_delegations_mark_delta( fd_stake_delegations_t * stake_delegations,
953 : ulong epoch,
954 : fd_stake_history_t const * stake_history,
955 : ulong * warmup_cooldown_rate_epoch,
956 : int use_fixed_point_stake_math,
957 498 : ushort fork_idx ) {
958 498 : root_map_t * root_map = get_root_map( stake_delegations );
959 498 : fd_stake_delegation_t * root_pool = get_root_pool( stake_delegations );
960 498 : fd_stake_delegation_t * delta_pool = get_delta_pool( stake_delegations );
961 498 : fork_map_t * fork_map = get_fork_map( stake_delegations, fork_idx );
962 :
963 498 : for( fork_map_iter_t iter = fork_map_iter_init( fork_map, delta_pool );
964 687 : !fork_map_iter_done( iter, fork_map, delta_pool );
965 498 : iter = fork_map_iter_next( iter, fork_map, delta_pool ) ) {
966 189 : fd_stake_delegation_t * delta_delegation = fork_map_iter_ele( iter, fork_map, delta_pool );
967 189 : fd_stake_delegation_t * base_delegation = root_map_ele_query( root_map, &delta_delegation->stake_account, NULL, root_pool);
968 189 : if( FD_UNLIKELY( !base_delegation ) ) {
969 75 : if( FD_UNLIKELY( !root_pool_free( root_pool ) ) ) {
970 : /* No room to project this delta into the root for the duration of
971 : the iteration. The stake totals are recomputed from scratch in
972 : fallback mode, so skipping the bookkeeping below is safe, and
973 : the boundary sweep picks the account up from the fallback
974 : tier. */
975 0 : pubkey_fallback_enter( stake_delegations, &delta_delegation->stake_account );
976 0 : continue;
977 0 : }
978 : /* No pubkey tier reference is taken for this projection, and
979 : correspondingly none is dropped when unmark_delta releases it: the
980 : delta entry driving this loop already holds one. */
981 75 : base_delegation = root_pool_ele_acquire( root_pool );
982 75 : base_delegation->stake_account = delta_delegation->stake_account;
983 75 : base_delegation->lamports = 0UL;
984 75 : base_delegation->acc_dlen = 0U;
985 75 : base_delegation->dne_in_root = 1;
986 75 : base_delegation->delta_idx = (uint)delta_pool_idx( delta_pool, delta_delegation );
987 75 : base_delegation->in_use = 1;
988 75 : base_delegation->state = FD_STAKE_DELEGATION_STATE_UNKNOWN;
989 75 : stake_delegations->pool_idx_wmk_ = fd_ulong_max( stake_delegations->pool_idx_wmk_, root_pool_idx( root_pool, base_delegation )+1UL );
990 75 : root_map_ele_insert( root_map, base_delegation, root_pool );
991 114 : } else {
992 : /* Subtract the old version's stake if it's not a tombstone. */
993 114 : fd_stake_delegation_t * old_delegation = base_delegation->delta_idx==UINT_MAX ? base_delegation : delta_pool_ele( delta_pool, base_delegation->delta_idx );
994 114 : if( FD_LIKELY( base_delegation->delta_idx==UINT_MAX || !old_delegation->is_tombstone ) ) {
995 111 : fd_stake_history_entry_t old_entry = fd_stake_delegation_activation_status( old_delegation, epoch, stake_history, warmup_cooldown_rate_epoch, use_fixed_point_stake_math );
996 111 : stake_delegations->effective_stake -= old_entry.effective;
997 111 : stake_delegations->activating_stake -= old_entry.activating;
998 111 : stake_delegations->deactivating_stake -= old_entry.deactivating;
999 111 : }
1000 : /* Update the base delegation to point to the new version. */
1001 114 : base_delegation->delta_idx = (uint)delta_pool_idx( delta_pool, delta_delegation );
1002 114 : }
1003 :
1004 : /* Add the new version's stake to the totals (as long as it's not a
1005 : tombstone).*/
1006 189 : if( FD_LIKELY( !delta_delegation->is_tombstone ) ) {
1007 156 : fd_stake_history_entry_t new_acc = fd_stake_delegation_activation_status( delta_delegation, epoch, stake_history, warmup_cooldown_rate_epoch, use_fixed_point_stake_math );
1008 156 : stake_delegations->effective_stake += new_acc.effective;
1009 156 : stake_delegations->activating_stake += new_acc.activating;
1010 156 : stake_delegations->deactivating_stake += new_acc.deactivating;
1011 156 : }
1012 189 : }
1013 498 : }
1014 :
1015 : static void
1016 : fd_stake_delegations_unmark_delta( fd_stake_delegations_t * stake_delegations,
1017 : ulong epoch,
1018 : fd_stake_history_t const * stake_history,
1019 : ulong * warmup_cooldown_rate_epoch,
1020 : int use_fixed_point_stake_math,
1021 498 : ushort fork_idx ) {
1022 498 : root_map_t * root_map = get_root_map( stake_delegations );
1023 498 : fd_stake_delegation_t * root_pool = get_root_pool( stake_delegations );
1024 498 : fd_stake_delegation_t * delta_pool = get_delta_pool( stake_delegations );
1025 498 : fork_map_t * fork_map = get_fork_map( stake_delegations, fork_idx );
1026 :
1027 498 : for( fork_map_iter_t iter = fork_map_iter_init( fork_map, delta_pool );
1028 687 : !fork_map_iter_done( iter, fork_map, delta_pool );
1029 498 : iter = fork_map_iter_next( iter, fork_map, delta_pool ) ) {
1030 189 : fd_stake_delegation_t * delta_delegation = fork_map_iter_ele( iter, fork_map, delta_pool );
1031 189 : fd_stake_delegation_t * base_delegation = root_map_ele_query( root_map, &delta_delegation->stake_account, NULL, root_pool );
1032 189 : if( FD_UNLIKELY( !base_delegation ) ) continue;
1033 :
1034 189 : uint delta_idx = (uint)delta_pool_idx( delta_pool, delta_delegation );
1035 189 : if( FD_UNLIKELY( base_delegation->delta_idx!=delta_idx ) ) continue;
1036 :
1037 174 : if( FD_UNLIKELY( base_delegation->dne_in_root )) {
1038 75 : if( FD_LIKELY( !delta_delegation->is_tombstone ) ) {
1039 72 : fd_stake_history_entry_t acc = fd_stake_delegation_activation_status( delta_delegation, epoch, stake_history, warmup_cooldown_rate_epoch, use_fixed_point_stake_math );
1040 72 : stake_delegations->effective_stake -= acc.effective;
1041 72 : stake_delegations->activating_stake -= acc.activating;
1042 72 : stake_delegations->deactivating_stake -= acc.deactivating;
1043 72 : }
1044 :
1045 75 : base_delegation->dne_in_root = 0;
1046 75 : base_delegation->delta_idx = UINT_MAX;
1047 75 : base_delegation->in_use = 0;
1048 75 : root_map_ele_remove( root_map, &delta_delegation->stake_account, NULL, root_pool );
1049 75 : root_pool_ele_release( root_pool, base_delegation );
1050 :
1051 99 : } else {
1052 99 : if( FD_LIKELY( !delta_delegation->is_tombstone ) ) {
1053 72 : fd_stake_history_entry_t acc = fd_stake_delegation_activation_status( delta_delegation, epoch, stake_history, warmup_cooldown_rate_epoch, use_fixed_point_stake_math );
1054 72 : stake_delegations->effective_stake -= acc.effective;
1055 72 : stake_delegations->activating_stake -= acc.activating;
1056 72 : stake_delegations->deactivating_stake -= acc.deactivating;
1057 72 : }
1058 :
1059 99 : base_delegation->delta_idx = UINT_MAX;
1060 :
1061 99 : fd_stake_history_entry_t acc = fd_stake_delegation_activation_status( base_delegation, epoch, stake_history, warmup_cooldown_rate_epoch, use_fixed_point_stake_math );
1062 99 : stake_delegations->effective_stake += acc.effective;
1063 99 : stake_delegations->activating_stake += acc.activating;
1064 99 : stake_delegations->deactivating_stake += acc.deactivating;
1065 99 : }
1066 174 : }
1067 498 : }
1068 :
1069 : void
1070 : fd_stake_delegations_frontier_query_begin( fd_stake_delegations_t * stake_delegations,
1071 : ulong epoch,
1072 : fd_stake_history_t const * stake_history,
1073 : ulong * warmup_cooldown_rate_epoch,
1074 : int use_fixed_point_stake_math,
1075 : ushort const * fork_ids,
1076 456 : ulong fork_id_cnt ) {
1077 456 : fd_rwlock_write( &stake_delegations->lock );
1078 456 : stake_delegations->frontier_query_epoch = epoch;
1079 954 : for( ulong i=0UL; i<fork_id_cnt; i++ ) {
1080 498 : fd_stake_delegations_mark_delta( stake_delegations,
1081 498 : epoch,
1082 498 : stake_history,
1083 498 : warmup_cooldown_rate_epoch,
1084 498 : use_fixed_point_stake_math,
1085 498 : fork_ids[ i ] );
1086 498 : }
1087 456 : }
1088 :
1089 : void
1090 : fd_stake_delegations_frontier_query_end( fd_stake_delegations_t * stake_delegations,
1091 : fd_stake_history_t const * stake_history,
1092 : ulong * warmup_cooldown_rate_epoch,
1093 : int use_fixed_point_stake_math,
1094 : ushort const * fork_ids,
1095 456 : ulong fork_id_cnt ) {
1096 954 : for( ulong i=0UL; i<fork_id_cnt; i++ ) {
1097 498 : fd_stake_delegations_unmark_delta( stake_delegations,
1098 498 : stake_delegations->frontier_query_epoch,
1099 498 : stake_history,
1100 498 : warmup_cooldown_rate_epoch,
1101 498 : use_fixed_point_stake_math,
1102 498 : fork_ids[ i ] );
1103 498 : }
1104 456 : stake_delegations->frontier_query_epoch = ULONG_MAX;
1105 456 : fd_rwlock_unwrite( &stake_delegations->lock );
1106 456 : }
1107 :
1108 : void
1109 6 : fd_stake_delegations_invalidate_warmed( fd_stake_delegations_t * stake_delegations ) {
1110 6 : fd_stake_delegation_t * root_pool = get_root_pool( stake_delegations );
1111 12 : for( ulong i=0UL; i<stake_delegations->pool_idx_wmk_; i++ ) {
1112 6 : fd_stake_delegation_t * delegation = &root_pool[ i ];
1113 6 : if( FD_LIKELY( delegation->in_use && delegation->state==FD_STAKE_DELEGATION_STATE_WARMED ) ) {
1114 6 : delegation->state = FD_STAKE_DELEGATION_STATE_UNKNOWN;
1115 6 : }
1116 6 : }
1117 6 : stake_delegations->fp_warmed_awarded = 0;
1118 6 : }
|