Line data Source code
1 : #include "fd_stake_delegations.h"
2 : #include "../accdb/fd_accdb_pipe.h"
3 : #include "fd_stakes.h"
4 :
5 : #define POOL_NAME root_pool
6 1188 : #define POOL_T fd_stake_delegation_t
7 129 : #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 3531 : #define MAP_KEY stake_account
16 414 : #define MAP_KEY_EQ(k0,k1) (fd_pubkey_eq( k0, k1 ))
17 7455 : #define MAP_KEY_HASH(key,seed) (fd_funk_rec_key_hash1( key->uc, seed ))
18 4401 : #define MAP_NEXT next_
19 16719 : #define MAP_IDX_T uint
20 : #include "../../util/tmpl/fd_map_chain.c"
21 :
22 : #define POOL_NAME delta_pool
23 1188 : #define POOL_T fd_stake_delegation_t
24 216 : #define POOL_NEXT next_
25 : #define POOL_IDX_T uint
26 : #define POOL_LAZY 1
27 : #include "../../util/tmpl/fd_pool.c"
28 :
29 : #define DLIST_NAME fork_dlist
30 : #define DLIST_ELE_T fd_stake_delegation_t
31 198 : #define DLIST_PREV prev_
32 693 : #define DLIST_NEXT next_
33 : #define DLIST_IDX_T uint
34 : #include "../../util/tmpl/fd_dlist.c"
35 :
36 : struct fork_pool_ele { ushort next; };
37 : typedef struct fork_pool_ele fork_pool_ele_t;
38 :
39 : #define POOL_NAME fork_pool
40 1188 : #define POOL_T fork_pool_ele_t
41 : #define POOL_IDX_T ushort
42 : #include "../../util/tmpl/fd_pool.c"
43 :
44 : /* Internal getters for base map + pool */
45 :
46 : static inline fd_stake_delegation_t *
47 8265 : get_root_pool( fd_stake_delegations_t const * stake_delegations ) {
48 8265 : return fd_type_pun( (uchar *)stake_delegations + stake_delegations->pool_offset_ );
49 8265 : }
50 :
51 : static inline root_map_t *
52 8196 : get_root_map( fd_stake_delegations_t const * stake_delegations ) {
53 8196 : return fd_type_pun( (uchar *)stake_delegations + stake_delegations->map_offset_ );
54 8196 : }
55 :
56 : /* Internal getters for delta pool + fork structures */
57 :
58 : static inline fd_stake_delegation_t *
59 8628 : get_delta_pool( fd_stake_delegations_t const * stake_delegations ) {
60 8628 : return fd_type_pun( (uchar *)stake_delegations + stake_delegations->delta_pool_offset_ );
61 8628 : }
62 :
63 : static inline fork_pool_ele_t *
64 7413 : get_fork_pool( fd_stake_delegations_t const * stake_delegations ) {
65 7413 : return fd_type_pun( (uchar *)stake_delegations + stake_delegations->fork_pool_offset_ );
66 7413 : }
67 :
68 : static inline fork_dlist_t *
69 : get_fork_dlist( fd_stake_delegations_t const * stake_delegations,
70 56142 : ushort fork_idx ) {
71 56142 : return fd_type_pun( (uchar *)stake_delegations + stake_delegations->dlist_offsets_[ fork_idx ] );
72 56142 : }
73 :
74 : ulong
75 18438 : fd_stake_delegations_align( void ) {
76 18438 : return FD_STAKE_DELEGATIONS_ALIGN;
77 18438 : }
78 :
79 : ulong
80 : fd_stake_delegations_footprint( ulong max_stake_accounts,
81 : ulong expected_stake_accounts,
82 2973 : ulong max_live_slots ) {
83 :
84 2973 : ulong map_chain_cnt = root_map_chain_cnt_est( expected_stake_accounts );
85 :
86 2973 : ulong l = FD_LAYOUT_INIT;
87 2973 : l = FD_LAYOUT_APPEND( l, fd_stake_delegations_align(), sizeof(fd_stake_delegations_t) );
88 2973 : l = FD_LAYOUT_APPEND( l, root_pool_align(), root_pool_footprint( max_stake_accounts ) );
89 2973 : l = FD_LAYOUT_APPEND( l, root_map_align(), root_map_footprint( map_chain_cnt ) );
90 2973 : l = FD_LAYOUT_APPEND( l, delta_pool_align(), delta_pool_footprint( max_stake_accounts ) );
91 2973 : l = FD_LAYOUT_APPEND( l, fork_pool_align(), fork_pool_footprint( max_live_slots ) );
92 10482 : for( ulong i=0UL; i<max_live_slots; i++ ) {
93 7509 : l = FD_LAYOUT_APPEND( l, fork_dlist_align(), fork_dlist_footprint() );
94 7509 : }
95 :
96 2973 : return FD_LAYOUT_FINI( l, fd_stake_delegations_align() );
97 2973 : }
98 :
99 : void *
100 : fd_stake_delegations_new( void * mem,
101 : ulong seed,
102 : ulong max_stake_accounts,
103 : ulong expected_stake_accounts,
104 600 : ulong max_live_slots ) {
105 600 : if( FD_UNLIKELY( !mem ) ) {
106 3 : FD_LOG_WARNING(( "NULL mem" ));
107 3 : return NULL;
108 3 : }
109 :
110 597 : if( FD_UNLIKELY( !max_stake_accounts ) ) {
111 3 : FD_LOG_WARNING(( "max_stake_accounts is 0" ));
112 3 : return NULL;
113 3 : }
114 :
115 594 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)mem, fd_stake_delegations_align() ) ) ) {
116 0 : FD_LOG_WARNING(( "misaligned mem" ));
117 0 : return NULL;
118 0 : }
119 :
120 594 : if( FD_UNLIKELY( max_live_slots>FD_STAKE_DELEGATIONS_FORK_MAX ) ) {
121 0 : FD_LOG_WARNING(( "max_live_slots is too large" ));
122 0 : return NULL;
123 0 : }
124 :
125 594 : ulong map_chain_cnt = root_map_chain_cnt_est( expected_stake_accounts );
126 :
127 594 : FD_SCRATCH_ALLOC_INIT( l, mem );
128 594 : fd_stake_delegations_t * stake_delegations = FD_SCRATCH_ALLOC_APPEND( l, fd_stake_delegations_align(), sizeof(fd_stake_delegations_t) );
129 594 : void * pool_mem = FD_SCRATCH_ALLOC_APPEND( l, root_pool_align(), root_pool_footprint( max_stake_accounts ) );
130 594 : void * map_mem = FD_SCRATCH_ALLOC_APPEND( l, root_map_align(), root_map_footprint( map_chain_cnt ) );
131 594 : void * delta_pool_mem = FD_SCRATCH_ALLOC_APPEND( l, delta_pool_align(), delta_pool_footprint( max_stake_accounts ) );
132 594 : void * fork_pool_mem = FD_SCRATCH_ALLOC_APPEND( l, fork_pool_align(), fork_pool_footprint( max_live_slots ) );
133 2115 : for( ushort i=0; i<(ushort)max_live_slots; i++ ) {
134 1521 : void * fork_dlist_mem = FD_SCRATCH_ALLOC_APPEND( l, fork_dlist_align(), fork_dlist_footprint() );
135 0 : fork_dlist_t * dlist = fork_dlist_join( fork_dlist_new( fork_dlist_mem ) );
136 1521 : if( FD_UNLIKELY( !dlist ) ) {
137 0 : FD_LOG_WARNING(( "Failed to create fork dlist" ));
138 0 : return NULL;
139 0 : }
140 1521 : stake_delegations->dlist_offsets_[ i ] = (ulong)dlist - (ulong)mem;
141 1521 : }
142 :
143 594 : if( FD_UNLIKELY( FD_SCRATCH_ALLOC_FINI( l, fd_stake_delegations_align() )!=(ulong)mem+fd_stake_delegations_footprint( max_stake_accounts, expected_stake_accounts, max_live_slots ) ) ) {
144 0 : FD_LOG_WARNING(( "fd_stake_delegations_new: bad layout" ));
145 0 : return NULL;
146 0 : }
147 :
148 594 : fd_stake_delegation_t * root_pool = root_pool_join( root_pool_new( pool_mem, max_stake_accounts ) );
149 594 : if( FD_UNLIKELY( !root_pool ) ) {
150 0 : FD_LOG_WARNING(( "Failed to create stake delegations pool" ));
151 0 : return NULL;
152 0 : }
153 :
154 594 : root_map_t * root_map = root_map_join( root_map_new( map_mem, map_chain_cnt, seed ) );
155 594 : if( FD_UNLIKELY( !root_map ) ) {
156 0 : FD_LOG_WARNING(( "Failed to create stake delegations map" ));
157 0 : return NULL;
158 0 : }
159 :
160 594 : fd_stake_delegation_t * delta_pool = delta_pool_join( delta_pool_new( delta_pool_mem, max_stake_accounts ) );
161 594 : if( FD_UNLIKELY( !delta_pool ) ) {
162 0 : FD_LOG_WARNING(( "Failed to create stake delegation delta pool" ));
163 0 : return NULL;
164 0 : }
165 :
166 594 : fork_pool_ele_t * fork_pool = fork_pool_join( fork_pool_new( fork_pool_mem, max_live_slots ) );
167 594 : if( FD_UNLIKELY( !fork_pool ) ) {
168 0 : FD_LOG_WARNING(( "Failed to create fork pool" ));
169 0 : return NULL;
170 0 : }
171 :
172 594 : stake_delegations->max_stake_accounts_ = max_stake_accounts;
173 594 : stake_delegations->expected_stake_accounts_ = expected_stake_accounts;
174 594 : stake_delegations->pool_offset_ = (ulong)root_pool - (ulong)mem;
175 594 : stake_delegations->map_offset_ = (ulong)root_map - (ulong)mem;
176 594 : stake_delegations->delta_pool_offset_ = (ulong)delta_pool - (ulong)mem;
177 594 : stake_delegations->fork_pool_offset_ = (ulong)fork_pool - (ulong)mem;
178 :
179 594 : stake_delegations->effective_stake = 0UL;
180 594 : stake_delegations->activating_stake = 0UL;
181 594 : stake_delegations->deactivating_stake = 0UL;
182 :
183 594 : fd_rwlock_new( &stake_delegations->delta_lock );
184 :
185 594 : FD_COMPILER_MFENCE();
186 594 : FD_VOLATILE( stake_delegations->magic ) = FD_STAKE_DELEGATIONS_MAGIC;
187 594 : FD_COMPILER_MFENCE();
188 :
189 594 : return mem;
190 594 : }
191 :
192 : fd_stake_delegations_t *
193 600 : fd_stake_delegations_join( void * mem ) {
194 600 : if( FD_UNLIKELY( !mem ) ) {
195 3 : FD_LOG_WARNING(( "NULL mem" ));
196 3 : return NULL;
197 3 : }
198 :
199 597 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)mem, fd_stake_delegations_align() ) ) ) {
200 0 : FD_LOG_WARNING(( "misaligned mem" ));
201 0 : return NULL;
202 0 : }
203 :
204 597 : fd_stake_delegations_t * stake_delegations = (fd_stake_delegations_t *)mem;
205 :
206 597 : if( FD_UNLIKELY( stake_delegations->magic!=FD_STAKE_DELEGATIONS_MAGIC ) ) {
207 3 : FD_LOG_WARNING(( "Invalid stake delegations magic" ));
208 3 : return NULL;
209 3 : }
210 :
211 594 : return stake_delegations;
212 597 : }
213 :
214 : void
215 3441 : fd_stake_delegations_reset( fd_stake_delegations_t * stake_delegations ) {
216 3441 : root_pool_reset ( get_root_pool ( stake_delegations ) );
217 3441 : root_map_reset ( get_root_map ( stake_delegations ) );
218 3441 : delta_pool_reset( get_delta_pool( stake_delegations ) );
219 3441 : fork_pool_ele_t * fork_pool = get_fork_pool( stake_delegations );
220 3441 : fd_stake_delegation_t * delta_pool = get_delta_pool( stake_delegations );
221 3441 : ulong max_forks = fork_pool_max( fork_pool );
222 58545 : for( ulong i=0UL; i<max_forks; i++ ) {
223 55104 : fork_dlist_remove_all( get_fork_dlist( stake_delegations, (ushort)i ), delta_pool );
224 55104 : }
225 3441 : fork_pool_reset( fork_pool );
226 3441 : stake_delegations->effective_stake = 0UL;
227 3441 : stake_delegations->activating_stake = 0UL;
228 3441 : stake_delegations->deactivating_stake = 0UL;
229 3441 : }
230 :
231 : fd_stake_delegation_t const *
232 : fd_stake_delegation_root_query( fd_stake_delegations_t const * stake_delegations,
233 30 : fd_pubkey_t const * stake_account ) {
234 30 : fd_stake_delegation_t * pool = get_root_pool( stake_delegations );
235 30 : root_map_t * map = get_root_map( stake_delegations );
236 :
237 30 : return root_map_ele_query_const( map, stake_account, NULL, pool );
238 30 : }
239 :
240 : void
241 : fd_stake_delegations_root_update( fd_stake_delegations_t * stake_delegations,
242 : fd_pubkey_t const * stake_account,
243 : fd_pubkey_t const * vote_account,
244 : ulong stake,
245 : ulong activation_epoch,
246 : ulong deactivation_epoch,
247 : ulong credits_observed,
248 3486 : uchar warmup_cooldown_rate ) {
249 3486 : fd_stake_delegation_t * pool = get_root_pool( stake_delegations );
250 3486 : root_map_t * map = get_root_map( stake_delegations );
251 :
252 3486 : fd_stake_delegation_t * stake_delegation = root_map_ele_query( map, stake_account, NULL, pool );
253 3486 : if( !stake_delegation ) {
254 3465 : FD_CRIT( root_pool_free( pool ), "no free stake delegations in pool" );
255 3465 : stake_delegation = root_pool_ele_acquire( pool );
256 3465 : stake_delegation->stake_account = *stake_account;
257 3465 : FD_CRIT( root_map_ele_insert( map, stake_delegation, pool ), "unable to insert stake delegation into map" );
258 3465 : }
259 :
260 3486 : stake_delegation->vote_account = *vote_account;
261 3486 : stake_delegation->stake = stake;
262 3486 : stake_delegation->activation_epoch = (ushort)fd_ulong_min( activation_epoch, USHORT_MAX );
263 3486 : stake_delegation->deactivation_epoch = (ushort)fd_ulong_min( deactivation_epoch, USHORT_MAX );
264 3486 : stake_delegation->credits_observed = credits_observed;
265 3486 : stake_delegation->warmup_cooldown_rate = warmup_cooldown_rate;
266 3486 : stake_delegation->dne_in_root = 0;
267 3486 : stake_delegation->delta_idx = UINT_MAX;
268 3486 : }
269 :
270 : static inline void
271 : fd_stake_delegations_remove( fd_stake_delegations_t * stake_delegations,
272 3 : fd_pubkey_t const * stake_account ) {
273 3 : fd_stake_delegation_t * pool = get_root_pool( stake_delegations );
274 3 : root_map_t * map = get_root_map( stake_delegations );
275 :
276 3 : ulong delegation_idx = root_map_idx_query( map, stake_account, UINT_MAX, pool );
277 3 : if( FD_UNLIKELY( delegation_idx==UINT_MAX ) ) return;
278 :
279 3 : root_map_idx_remove( map, stake_account, delegation_idx, pool );
280 3 : root_pool_idx_release( pool, delegation_idx );
281 3 : }
282 :
283 : #if FD_HAS_DOUBLE
284 :
285 : void
286 : fd_stake_delegations_refresh( fd_stake_delegations_t * stake_delegations,
287 : ulong epoch,
288 : fd_stake_history_t const * stake_history,
289 : ulong * warmup_cooldown_rate_epoch,
290 : fd_accdb_user_t * accdb,
291 0 : fd_funk_txn_xid_t const * xid ) {
292 :
293 0 : stake_delegations->effective_stake = 0UL;
294 0 : stake_delegations->activating_stake = 0UL;
295 0 : stake_delegations->deactivating_stake = 0UL;
296 :
297 0 : root_map_t * map = get_root_map( stake_delegations );
298 0 : fd_stake_delegation_t * pool = get_root_pool( stake_delegations );
299 :
300 0 : fd_accdb_ro_pipe_t ro_pipe[1];
301 0 : fd_accdb_ro_pipe_init( ro_pipe, accdb, xid );
302 0 : ulong const job_cnt = fd_stake_delegations_cnt( stake_delegations );
303 0 : for( ulong i=0UL; i<job_cnt; i++ ) {
304 :
305 : /* stream out read requests */
306 0 : fd_accdb_ro_pipe_enqueue( ro_pipe, &pool[ i ].stake_account );
307 0 : if( FD_UNLIKELY( i+1UL==job_cnt ) ) {
308 0 : fd_accdb_ro_pipe_flush( ro_pipe );
309 0 : }
310 :
311 : /* handle a batch of completions */
312 0 : fd_accdb_ro_t * ro;
313 0 : while( (ro = fd_accdb_ro_pipe_poll( ro_pipe )) ) {
314 0 : fd_pubkey_t const * address = fd_accdb_ref_address( ro );
315 0 : fd_stake_delegation_t * delegation = root_map_ele_query( map, address, NULL, pool );
316 0 : if( FD_UNLIKELY( !delegation ) ) continue;
317 :
318 0 : fd_stake_state_t const * stake = fd_stakes_get_state( ro->meta );
319 0 : if( FD_UNLIKELY( !stake ) ) goto remove;
320 0 : if( FD_UNLIKELY( stake->stake_type != FD_STAKE_STATE_STAKE ) ) goto remove;
321 :
322 0 : fd_stake_delegations_root_update(
323 0 : stake_delegations,
324 0 : address,
325 0 : &stake->stake.stake.delegation.voter_pubkey,
326 0 : stake->stake.stake.delegation.stake,
327 0 : stake->stake.stake.delegation.activation_epoch,
328 0 : stake->stake.stake.delegation.deactivation_epoch,
329 0 : stake->stake.stake.credits_observed,
330 0 : fd_stake_warmup_cooldown_rate( epoch, warmup_cooldown_rate_epoch ) );
331 :
332 0 : fd_stake_history_entry_t entry = stake_activating_and_deactivating( &stake->stake.stake.delegation, epoch, stake_history, warmup_cooldown_rate_epoch );
333 0 : stake_delegations->effective_stake += entry.effective;
334 0 : stake_delegations->activating_stake += entry.activating;
335 0 : stake_delegations->deactivating_stake += entry.deactivating;
336 0 : continue; /* ok */
337 :
338 0 : remove:
339 0 : root_map_idx_remove( map, address, UINT_MAX, pool );
340 0 : root_pool_ele_release( pool, delegation );
341 0 : }
342 0 : }
343 0 : fd_accdb_ro_pipe_fini( ro_pipe );
344 0 : }
345 :
346 : #endif
347 :
348 : ulong
349 69 : fd_stake_delegations_cnt( fd_stake_delegations_t const * stake_delegations ) {
350 69 : return root_pool_used( get_root_pool( stake_delegations ) );
351 69 : }
352 :
353 : /* Fork-aware delta operations */
354 :
355 : ushort
356 3753 : fd_stake_delegations_new_fork( fd_stake_delegations_t * stake_delegations ) {
357 3753 : fork_pool_ele_t * fork_pool = get_fork_pool( stake_delegations );
358 3753 : FD_CRIT( fork_pool_free( fork_pool ), "no free forks in pool. The system has forked too wide." );
359 3753 : ushort fork_idx = (ushort)fork_pool_idx_acquire( fork_pool );
360 :
361 3753 : return fork_idx;
362 3753 : }
363 :
364 : void
365 : fd_stake_delegations_fork_update( fd_stake_delegations_t * stake_delegations,
366 : ushort fork_idx,
367 : fd_pubkey_t const * stake_account,
368 : fd_pubkey_t const * vote_account,
369 : ulong stake,
370 : ulong activation_epoch,
371 : ulong deactivation_epoch,
372 : ulong credits_observed,
373 168 : uchar warmup_cooldown_rate ) {
374 168 : fd_rwlock_write( &stake_delegations->delta_lock );
375 :
376 168 : fd_stake_delegation_t * delta_pool = get_delta_pool( stake_delegations );
377 168 : FD_CRIT( delta_pool_free( delta_pool ), "no free stake delegations in pool" );
378 :
379 168 : fork_dlist_t * dlist = get_fork_dlist( stake_delegations, fork_idx );
380 :
381 168 : fd_stake_delegation_t * stake_delegation = delta_pool_ele_acquire( delta_pool );
382 :
383 168 : fork_dlist_ele_push_tail( dlist, stake_delegation, delta_pool );
384 :
385 168 : stake_delegation->stake_account = *stake_account;
386 168 : stake_delegation->vote_account = *vote_account;
387 168 : stake_delegation->stake = stake;
388 168 : stake_delegation->activation_epoch = (ushort)fd_ulong_min( activation_epoch, USHORT_MAX );
389 168 : stake_delegation->deactivation_epoch = (ushort)fd_ulong_min( deactivation_epoch, USHORT_MAX );
390 168 : stake_delegation->credits_observed = credits_observed;
391 168 : stake_delegation->warmup_cooldown_rate = warmup_cooldown_rate;
392 168 : stake_delegation->is_tombstone = 0;
393 :
394 168 : FD_BASE58_ENCODE_32_BYTES( stake_delegation->stake_account.uc, stake_account_out );
395 168 : FD_LOG_DEBUG(( "fork_update: stake_account=%s, stake=%lu, activation_epoch=%u, deactivation_epoch=%u",
396 168 : stake_account_out, stake_delegation->stake, stake_delegation->activation_epoch, stake_delegation->deactivation_epoch ));
397 :
398 168 : fd_rwlock_unwrite( &stake_delegations->delta_lock );
399 168 : }
400 :
401 : void
402 : fd_stake_delegations_fork_remove( fd_stake_delegations_t * stake_delegations,
403 : ushort fork_idx,
404 30 : fd_pubkey_t const * stake_account ) {
405 30 : fd_rwlock_write( &stake_delegations->delta_lock );
406 :
407 30 : fd_stake_delegation_t * delta_pool = get_delta_pool( stake_delegations );
408 30 : FD_CRIT( delta_pool_free( delta_pool ), "no free stake delegations in pool" );
409 :
410 30 : fd_stake_delegation_t * stake_delegation = delta_pool_ele_acquire( delta_pool );
411 :
412 30 : fork_dlist_t * dlist = get_fork_dlist( stake_delegations, fork_idx );
413 30 : fork_dlist_ele_push_tail( dlist, stake_delegation, delta_pool );
414 :
415 30 : stake_delegation->stake_account = *stake_account;
416 30 : stake_delegation->is_tombstone = 1;
417 :
418 30 : FD_BASE58_ENCODE_32_BYTES( stake_delegation->stake_account.uc, stake_account_out );
419 30 : FD_LOG_DEBUG(( "fork_remove: stake_account=%s", stake_account_out ));
420 :
421 30 : fd_rwlock_unwrite( &stake_delegations->delta_lock );
422 30 : }
423 :
424 : void
425 : fd_stake_delegations_evict_fork( fd_stake_delegations_t * stake_delegations,
426 228 : ushort fork_idx ) {
427 228 : if( fork_idx==USHORT_MAX ) return;
428 :
429 219 : fd_rwlock_write( &stake_delegations->delta_lock );
430 :
431 219 : fd_stake_delegation_t * delta_pool = get_delta_pool( stake_delegations );
432 :
433 219 : fork_dlist_t * dlist = get_fork_dlist( stake_delegations, fork_idx );
434 348 : while( !fork_dlist_is_empty( dlist, delta_pool ) ) {
435 129 : fd_stake_delegation_t * ele = fork_dlist_ele_pop_head( dlist, delta_pool );
436 129 : delta_pool_ele_release( delta_pool, ele );
437 129 : }
438 :
439 219 : fork_pool_idx_release( get_fork_pool( stake_delegations ), fork_idx );
440 :
441 219 : fd_rwlock_unwrite( &stake_delegations->delta_lock );
442 219 : }
443 :
444 : void
445 : fd_stake_delegations_apply_fork_delta( ulong epoch,
446 : fd_stake_history_t const * stake_history,
447 : ulong * warmup_cooldown_rate_epoch,
448 : fd_stake_delegations_t * stake_delegations,
449 93 : ushort fork_idx ) {
450 :
451 93 : fork_dlist_t * dlist = get_fork_dlist( stake_delegations, fork_idx );
452 93 : fd_stake_delegation_t * delta_pool = get_delta_pool( stake_delegations );
453 :
454 93 : for( fork_dlist_iter_t iter = fork_dlist_iter_fwd_init( dlist, delta_pool );
455 123 : !fork_dlist_iter_done( iter, dlist, delta_pool );
456 93 : iter = fork_dlist_iter_fwd_next( iter, dlist, delta_pool ) ) {
457 30 : fd_stake_delegation_t * stake_delegation = fork_dlist_iter_ele( iter, dlist, delta_pool );
458 30 : if( FD_LIKELY( !stake_delegation->is_tombstone ) ) {
459 : /* If the entry in the delta is an update:
460 : - If the entry already exists, subtract the old version's stake
461 : - Insert/update the new version
462 : - Add the new version's stake to the totals */
463 27 : fd_stake_delegation_t const * old_delegation = fd_stake_delegation_root_query( stake_delegations, &stake_delegation->stake_account );
464 27 : if( FD_LIKELY( old_delegation ) ) {
465 12 : fd_stake_history_entry_t old_entry = fd_stakes_activating_and_deactivating( old_delegation, epoch, stake_history, warmup_cooldown_rate_epoch );
466 12 : stake_delegations->effective_stake -= old_entry.effective;
467 12 : stake_delegations->activating_stake -= old_entry.activating;
468 12 : stake_delegations->deactivating_stake -= old_entry.deactivating;
469 12 : }
470 :
471 27 : fd_stake_delegations_root_update(
472 27 : stake_delegations,
473 27 : &stake_delegation->stake_account,
474 27 : &stake_delegation->vote_account,
475 27 : stake_delegation->stake,
476 27 : stake_delegation->activation_epoch,
477 27 : stake_delegation->deactivation_epoch,
478 27 : stake_delegation->credits_observed,
479 27 : stake_delegation->warmup_cooldown_rate );
480 :
481 27 : fd_stake_history_entry_t new_entry = fd_stakes_activating_and_deactivating( stake_delegation, epoch, stake_history, warmup_cooldown_rate_epoch );
482 27 : stake_delegations->effective_stake += new_entry.effective;
483 27 : stake_delegations->activating_stake += new_entry.activating;
484 27 : stake_delegations->deactivating_stake += new_entry.deactivating;
485 27 : } else {
486 : /* If the stake delegation in the delta is a tombstone, just
487 : remove the stake delegation from the root map and subtract
488 : it's stake from the totals. */
489 3 : fd_stake_delegation_t const * old_delegation = fd_stake_delegation_root_query( stake_delegations, &stake_delegation->stake_account );
490 3 : if( FD_LIKELY( old_delegation ) ) {
491 3 : fd_stake_history_entry_t old_entry = fd_stakes_activating_and_deactivating( old_delegation, epoch, stake_history, warmup_cooldown_rate_epoch );
492 3 : stake_delegations->effective_stake -= old_entry.effective;
493 3 : stake_delegations->activating_stake -= old_entry.activating;
494 3 : stake_delegations->deactivating_stake -= old_entry.deactivating;
495 3 : }
496 3 : fd_stake_delegations_remove( stake_delegations, &stake_delegation->stake_account );
497 3 : }
498 30 : }
499 93 : FD_LOG_DEBUG(( "effective_stake=%lu, activating_stake=%lu, deactivating_stake=%lu", stake_delegations->effective_stake, stake_delegations->activating_stake, stake_delegations->deactivating_stake ));
500 93 : }
501 :
502 : /* Combined base+delta iterator */
503 :
504 : fd_stake_delegation_t const *
505 876 : fd_stake_delegations_iter_ele( fd_stake_delegations_iter_t * iter ) {
506 876 : ulong idx = root_map_iter_idx( iter->iter, iter->root_map, iter->root_pool );
507 876 : fd_stake_delegation_t * stake_delegation = root_pool_ele( iter->root_pool, idx );
508 876 : if( FD_UNLIKELY( stake_delegation->delta_idx!=UINT_MAX ) ) {
509 132 : return (fd_stake_delegation_t *)delta_pool_ele( iter->delta_pool, stake_delegation->delta_idx );
510 132 : }
511 744 : return stake_delegation;
512 876 : }
513 :
514 : ulong
515 405 : fd_stake_delegations_iter_idx( fd_stake_delegations_iter_t * iter ) {
516 405 : return root_map_iter_idx( iter->iter, iter->root_map, iter->root_pool );
517 405 : }
518 :
519 : static void
520 1440 : skip_tombstones( fd_stake_delegations_iter_t * iter ) {
521 1455 : while( !root_map_iter_done( iter->iter, iter->root_map, iter->root_pool ) ) {
522 891 : fd_stake_delegation_t * root_delegation = root_map_iter_ele( iter->iter, iter->root_map, iter->root_pool );
523 891 : fd_stake_delegation_t const * ele = (root_delegation->delta_idx != UINT_MAX)
524 891 : ? (fd_stake_delegation_t const *)delta_pool_ele( iter->delta_pool, root_delegation->delta_idx )
525 891 : : (fd_stake_delegation_t const *)root_delegation;
526 891 : if( FD_LIKELY( !ele->is_tombstone ) ) return;
527 15 : iter->iter = root_map_iter_next( iter->iter, iter->root_map, iter->root_pool );
528 15 : }
529 1440 : }
530 :
531 : fd_stake_delegations_iter_t *
532 : fd_stake_delegations_iter_init( fd_stake_delegations_iter_t * iter,
533 708 : fd_stake_delegations_t const * stake_delegations ) {
534 708 : if( FD_UNLIKELY( !stake_delegations ) ) {
535 0 : FD_LOG_CRIT(( "NULL stake_delegations" ));
536 0 : }
537 :
538 708 : iter->root_map = get_root_map( stake_delegations );
539 708 : iter->root_pool = get_root_pool( stake_delegations );
540 708 : iter->iter = root_map_iter_init( iter->root_map, iter->root_pool );
541 708 : iter->delta_pool = get_delta_pool( stake_delegations );
542 :
543 708 : skip_tombstones( iter );
544 :
545 708 : return iter;
546 708 : }
547 :
548 : void
549 732 : fd_stake_delegations_iter_next( fd_stake_delegations_iter_t * iter ) {
550 732 : iter->iter = root_map_iter_next( iter->iter, iter->root_map, iter->root_pool );
551 732 : skip_tombstones( iter );
552 732 : }
553 :
554 : int
555 1440 : fd_stake_delegations_iter_done( fd_stake_delegations_iter_t * iter ) {
556 1440 : return root_map_iter_done( iter->iter, iter->root_map, iter->root_pool );
557 1440 : }
558 :
559 : void
560 : fd_stake_delegations_mark_delta( fd_stake_delegations_t * stake_delegations,
561 : ulong epoch,
562 : fd_stake_history_t const * stake_history,
563 : ulong * warmup_cooldown_rate_epoch,
564 264 : ushort fork_idx ) {
565 :
566 264 : root_map_t * root_map = get_root_map( stake_delegations );
567 264 : fd_stake_delegation_t * root_pool = get_root_pool( stake_delegations );
568 264 : fd_stake_delegation_t * delta_pool = get_delta_pool( stake_delegations );
569 264 : fork_dlist_t * fork_dlist = get_fork_dlist( stake_delegations, fork_idx );
570 :
571 264 : for( fork_dlist_iter_t iter = fork_dlist_iter_fwd_init( fork_dlist, delta_pool );
572 432 : !fork_dlist_iter_done( iter, fork_dlist, delta_pool );
573 264 : iter = fork_dlist_iter_fwd_next( iter, fork_dlist, delta_pool ) ) {
574 168 : fd_stake_delegation_t * delta_delegation = fork_dlist_iter_ele( iter, fork_dlist, delta_pool );
575 :
576 168 : fd_stake_delegation_t * base_delegation = root_map_ele_query( root_map, &delta_delegation->stake_account, NULL, root_pool);
577 168 : if( FD_UNLIKELY( !base_delegation ) ) {
578 66 : base_delegation = root_pool_ele_acquire( root_pool );
579 66 : base_delegation->stake_account = delta_delegation->stake_account;
580 66 : base_delegation->dne_in_root = 1;
581 66 : base_delegation->delta_idx = (uint)delta_pool_idx( delta_pool, delta_delegation );
582 66 : root_map_ele_insert( root_map, base_delegation, root_pool );
583 102 : } else {
584 : /* Only subtract the old version's stake if it's not a tombstone.*/
585 102 : fd_stake_delegation_t * old_delegation = base_delegation->delta_idx==UINT_MAX ? base_delegation : delta_pool_ele( delta_pool, base_delegation->delta_idx );
586 102 : if( FD_LIKELY( base_delegation->delta_idx==UINT_MAX || !old_delegation->is_tombstone ) ) {
587 93 : fd_stake_history_entry_t old_entry = fd_stakes_activating_and_deactivating( old_delegation, epoch, stake_history, warmup_cooldown_rate_epoch );
588 93 : stake_delegations->effective_stake -= old_entry.effective;
589 93 : stake_delegations->activating_stake -= old_entry.activating;
590 93 : stake_delegations->deactivating_stake -= old_entry.deactivating;
591 93 : }
592 : /* Update the base delegation to point to the new version. */
593 102 : base_delegation->delta_idx = (uint)delta_pool_idx( delta_pool, delta_delegation );
594 102 : }
595 :
596 : /* Add the new version's stake to the totals (as long as it's not a
597 : tombstone).*/
598 168 : if( FD_LIKELY( !delta_delegation->is_tombstone ) ) {
599 141 : fd_stake_history_entry_t new_entry = fd_stakes_activating_and_deactivating( delta_delegation, epoch, stake_history, warmup_cooldown_rate_epoch );
600 141 : stake_delegations->effective_stake += new_entry.effective;
601 141 : stake_delegations->activating_stake += new_entry.activating;
602 141 : stake_delegations->deactivating_stake += new_entry.deactivating;
603 141 : }
604 168 : }
605 264 : }
606 :
607 : void
608 : fd_stake_delegations_unmark_delta( fd_stake_delegations_t * stake_delegations,
609 : ulong epoch,
610 : fd_stake_history_t const * stake_history,
611 : ulong * warmup_cooldown_rate_epoch,
612 264 : ushort fork_idx ) {
613 :
614 264 : root_map_t * root_map = get_root_map( stake_delegations );
615 264 : fd_stake_delegation_t * root_pool = get_root_pool( stake_delegations );
616 264 : fork_dlist_t * fork_dlist = get_fork_dlist( stake_delegations, fork_idx );
617 264 : fd_stake_delegation_t * delta_pool = get_delta_pool( stake_delegations );
618 :
619 264 : for( fork_dlist_iter_t iter = fork_dlist_iter_fwd_init( fork_dlist, delta_pool );
620 432 : !fork_dlist_iter_done( iter, fork_dlist, delta_pool );
621 264 : iter = fork_dlist_iter_fwd_next( iter, fork_dlist, delta_pool ) ) {
622 168 : fd_stake_delegation_t * delta_delegation = fork_dlist_iter_ele( iter, fork_dlist, delta_pool );
623 :
624 168 : fd_stake_delegation_t * base_delegation = root_map_ele_query( root_map, &delta_delegation->stake_account, NULL, root_pool );
625 168 : if( FD_UNLIKELY( !base_delegation ) ) {
626 0 : continue;
627 0 : }
628 :
629 168 : uint delta_idx = (uint)delta_pool_idx( delta_pool, delta_delegation );
630 168 : if( FD_UNLIKELY( base_delegation->delta_idx!=delta_idx ) ) continue;
631 :
632 120 : if( FD_UNLIKELY( base_delegation->dne_in_root )) {
633 66 : if( FD_LIKELY( !delta_delegation->is_tombstone ) ) {
634 63 : fd_stake_history_entry_t entry = fd_stakes_activating_and_deactivating( delta_delegation, epoch, stake_history, warmup_cooldown_rate_epoch );
635 63 : stake_delegations->effective_stake -= entry.effective;
636 63 : stake_delegations->activating_stake -= entry.activating;
637 63 : stake_delegations->deactivating_stake -= entry.deactivating;
638 63 : }
639 :
640 66 : base_delegation->dne_in_root = 0;
641 66 : base_delegation->delta_idx = UINT_MAX;
642 66 : root_map_ele_remove( root_map, &delta_delegation->stake_account, NULL, root_pool );
643 66 : root_pool_ele_release( root_pool, base_delegation );
644 :
645 66 : } else {
646 54 : if( FD_LIKELY( !delta_delegation->is_tombstone ) ) {
647 39 : fd_stake_history_entry_t entry = fd_stakes_activating_and_deactivating( delta_delegation, epoch, stake_history, warmup_cooldown_rate_epoch );
648 39 : stake_delegations->effective_stake -= entry.effective;
649 39 : stake_delegations->activating_stake -= entry.activating;
650 39 : stake_delegations->deactivating_stake -= entry.deactivating;
651 39 : }
652 :
653 54 : base_delegation->delta_idx = UINT_MAX;
654 :
655 54 : fd_stake_history_entry_t entry = fd_stakes_activating_and_deactivating( base_delegation, epoch, stake_history, warmup_cooldown_rate_epoch );
656 54 : stake_delegations->effective_stake += entry.effective;
657 54 : stake_delegations->activating_stake += entry.activating;
658 54 : stake_delegations->deactivating_stake += entry.deactivating;
659 54 : }
660 120 : }
661 264 : }
|