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