Line data Source code
1 : #include "fd_stake_rewards.h"
2 : #include "fd_rewards_base.h"
3 : #include "../../ballet/siphash13/fd_siphash13.h"
4 : #include "../runtime/fd_bank.h"
5 :
6 12 : #define FD_STAKE_REWARDS_MAGIC (0xF17EDA2CE757A4E0) /* FIREDANCER STAKE V0 */
7 :
8 : FD_STATIC_ASSERT( FD_BANKS_MAX_BANKS<USHORT_MAX, fork_idx_width );
9 :
10 : struct fork {
11 : int next;
12 : };
13 : typedef struct fork fork_t;
14 :
15 : #define POOL_NAME fork_pool
16 24 : #define POOL_T fork_t
17 2033 : #define POOL_NEXT next
18 : #define POOL_IDX_T int
19 : #include "../../util/tmpl/fd_pool.c"
20 :
21 : struct __attribute__((packed, aligned(4UL))) partition_ele {
22 : fd_pubkey_t pubkey;
23 : ulong lamports;
24 : ulong block_reward; /* SIMD-0123 block revenue share, lamports only */
25 : ulong credits_observed;
26 : uint next;
27 : };
28 : typedef struct partition_ele partition_ele_t;
29 :
30 : struct fork_info {
31 : uint ele_cnt;
32 : uint partition_cnt;
33 : uint win_lo;
34 : uint win_hi;
35 : uint buf_idx; /* UINT_MAX for empty or evicted windows */
36 : uint ready; /* finalized and cached, including empty windows */
37 : ulong starting_block_height;
38 : ulong total_stake_rewards;
39 : ulong total_block_rewards;
40 : ulong refcnt;
41 : };
42 : typedef struct fork_info fork_info_t;
43 :
44 : struct fd_stake_rewards {
45 : ulong magic;
46 : ulong max_stake_accounts; /* entries each buffer can hold */
47 : ulong fork_cnt; /* max bank count plus replacement */
48 : ulong fork_info_offset;
49 : ulong fork_pool_offset;
50 : ulong buf_fork_offset;
51 : ulong buf_seq_offset;
52 : ulong buf_partition_heads_offset;
53 : ulong buf_offset;
54 : ulong finish_seq;
55 :
56 : uint cache_cnt;
57 : uint staging_fork;
58 :
59 : uint staging_partition_idxs_tail[ MAX_PARTITIONS_PER_EPOCH ];
60 :
61 : /* Temporary storage for the current stake reward being computed. */
62 : fd_siphash13_t primed_hasher[ 1 ];
63 :
64 : uint iter_fork;
65 : uint iter_buf;
66 : uint iter_idx;
67 : };
68 : typedef struct fd_stake_rewards fd_stake_rewards_t;
69 :
70 : static inline fork_t *
71 743 : get_fork_pool( fd_stake_rewards_t const * stake_rewards ) {
72 743 : return fd_type_pun( (uchar *)stake_rewards + stake_rewards->fork_pool_offset );
73 743 : }
74 :
75 : static inline fork_info_t *
76 : get_fork_info( fd_stake_rewards_t const * stake_rewards,
77 2866 : ushort fork_idx ) {
78 2866 : fork_info_t * fork_info = fd_type_pun( (uchar *)stake_rewards + stake_rewards->fork_info_offset );
79 2866 : return fork_info + fork_idx;
80 2866 : }
81 :
82 : static inline uint
83 2808 : get_buf_cnt( fd_stake_rewards_t const * stake_rewards ) {
84 2808 : return stake_rewards->cache_cnt+1U;
85 2808 : }
86 :
87 : static inline uint *
88 927 : get_buf_forks( fd_stake_rewards_t const * stake_rewards ) {
89 927 : return fd_type_pun( (uchar *)stake_rewards + stake_rewards->buf_fork_offset );
90 927 : }
91 :
92 : static inline ulong *
93 886 : get_buf_seqs( fd_stake_rewards_t const * stake_rewards ) {
94 886 : return fd_type_pun( (uchar *)stake_rewards + stake_rewards->buf_seq_offset );
95 886 : }
96 :
97 : static inline uint *
98 : get_buf_partition_heads( fd_stake_rewards_t const * stake_rewards,
99 370 : uint buf_idx ) {
100 370 : uint * heads = fd_type_pun(
101 370 : (uchar *)stake_rewards + stake_rewards->buf_partition_heads_offset );
102 370 : return heads + (ulong)buf_idx*MAX_PARTITIONS_PER_EPOCH;
103 370 : }
104 :
105 : static inline partition_ele_t *
106 : get_buf( fd_stake_rewards_t const * stake_rewards,
107 527 : uint buf_idx ) {
108 527 : partition_ele_t * buf = fd_type_pun(
109 527 : (uchar *)stake_rewards + stake_rewards->buf_offset );
110 527 : return buf + (ulong)buf_idx*stake_rewards->max_stake_accounts;
111 527 : }
112 :
113 : static uint
114 : window_sz( ulong capacity,
115 : uint partitions_cnt,
116 74 : ulong max_rewards_cnt ) {
117 :
118 : /* Percentage of the window capacity left unused when the rewards do
119 : not all fit. Rewards are scattered uniformly over the partitions,
120 : so the entry count of a window of W partitions has a mean of at
121 : most W*max_rewards_cnt/partitions_cnt and a sd of the square root
122 : of that mean. Reserving a hundredth of the capacity puts the
123 : overflow threshold at sqrt(capacity)/100 deviations above the mean,
124 : which is over thirteen deviations at the production capacity of
125 : 2150000 stake accounts. */
126 74 : if( FD_LIKELY( max_rewards_cnt<=capacity ) ) return partitions_cnt;
127 :
128 0 : ulong usable = fd_ulong_max( fd_ulong_sat_sub( capacity, fd_ulong_max( capacity*1UL/100UL, 1UL ) ), 1UL );
129 0 : ulong sz = fd_ulong_max( usable*(ulong)partitions_cnt/max_rewards_cnt, 1UL );
130 0 : return (uint)fd_ulong_min( sz, (ulong)partitions_cnt );
131 74 : }
132 :
133 : static void
134 : fork_drop_buf( fd_stake_rewards_t * stake_rewards,
135 47 : ushort fork_idx ) {
136 47 : fork_info_t * fork_info = get_fork_info( stake_rewards, fork_idx );
137 47 : if( fork_info->buf_idx==UINT_MAX ) return;
138 :
139 47 : uint buf_idx = fork_info->buf_idx;
140 47 : uint * buf_forks = get_buf_forks( stake_rewards );
141 47 : ulong * buf_seqs = get_buf_seqs( stake_rewards );
142 47 : FD_CHECK_CRIT( buf_idx<get_buf_cnt( stake_rewards ), "invalid reward buffer" );
143 47 : FD_CHECK_CRIT( buf_forks[buf_idx]==(uint)fork_idx,
144 47 : "reward buffer owner mismatch" );
145 47 : buf_forks[buf_idx] = UINT_MAX;
146 47 : buf_seqs[buf_idx] = 0UL;
147 47 : fork_info->buf_idx = UINT_MAX;
148 47 : }
149 :
150 : static uint
151 : buf_acquire( fd_stake_rewards_t * stake_rewards,
152 74 : ushort fork_idx ) {
153 74 : uint * buf_forks = get_buf_forks( stake_rewards );
154 74 : uint buf_cnt = get_buf_cnt( stake_rewards );
155 74 : for( uint buf_idx=0U; buf_idx<buf_cnt; buf_idx++ ) {
156 74 : if( FD_LIKELY( buf_forks[buf_idx]==UINT_MAX ) ) {
157 74 : buf_forks[buf_idx] = (uint)fork_idx;
158 74 : get_buf_seqs( stake_rewards )[buf_idx] = 0UL;
159 74 : return buf_idx;
160 74 : }
161 74 : }
162 0 : FD_LOG_CRIT(( "invariant violation: no free stake rewards construction buffer" ));
163 0 : }
164 :
165 : static void
166 : window_init( fd_stake_rewards_t * stake_rewards,
167 : ushort fork_idx,
168 : uint win_lo,
169 74 : ulong max_rewards_cnt ) {
170 : /* The win_hi is either the end of rewards or the end of the
171 : partition window, whichever is smaller. */
172 :
173 74 : fork_info_t * fork_info = get_fork_info( stake_rewards, fork_idx );
174 :
175 74 : uint remaining_cnt = fd_uint_sat_sub( fork_info->partition_cnt, win_lo );
176 74 : uint win_sz = window_sz( stake_rewards->max_stake_accounts, remaining_cnt, max_rewards_cnt );
177 74 : uint win_end = fd_uint_min( win_lo+win_sz, fork_info->partition_cnt );
178 74 : fork_info->win_lo = win_lo;
179 74 : fork_info->win_hi = fd_uint_max( fd_uint_sat_sub( win_end, 1UL ), win_lo );
180 74 : fork_info->ele_cnt = 0U;
181 74 : fork_info->ready = 0U;
182 74 : fork_info->total_stake_rewards = 0UL;
183 74 : memset( get_buf_partition_heads( stake_rewards, fork_info->buf_idx ), 0xFF,
184 74 : (ulong)fork_info->partition_cnt*sizeof(uint) );
185 74 : memset( stake_rewards->staging_partition_idxs_tail, 0xFF,
186 74 : (ulong)fork_info->partition_cnt*sizeof(uint) );
187 74 : stake_rewards->staging_fork = (uint)fork_idx;
188 74 : }
189 :
190 : static void
191 : prime_hasher( fd_stake_rewards_t * stake_rewards,
192 74 : fd_hash_t const * parent_blockhash ) {
193 74 : fd_siphash13_init( stake_rewards->primed_hasher, 0UL, 0UL );
194 74 : fd_siphash13_append( stake_rewards->primed_hasher, parent_blockhash->hash, sizeof(fd_hash_t) );
195 74 : }
196 :
197 : ulong
198 312 : fd_stake_rewards_align( void ) {
199 312 : return FD_STAKE_REWARDS_ALIGN;
200 312 : }
201 :
202 : ulong
203 : fd_stake_rewards_footprint( ulong max_stake_accounts,
204 : ulong max_bank_cnt,
205 48 : ulong cache_cnt ) {
206 48 : if( FD_UNLIKELY( max_stake_accounts>=(ulong)UINT_MAX ) ) return 0UL;
207 48 : if( FD_UNLIKELY( !max_bank_cnt || max_bank_cnt>FD_BANKS_MAX_BANKS ) ) return 0UL;
208 48 : if( FD_UNLIKELY( !cache_cnt || cache_cnt>max_bank_cnt+1UL ) ) return 0UL;
209 48 : ulong fork_cnt = max_bank_cnt+1UL;
210 48 : ulong buf_cnt = cache_cnt+1UL;
211 :
212 48 : ulong l = FD_LAYOUT_INIT;
213 48 : l = FD_LAYOUT_APPEND( l, fd_stake_rewards_align(), sizeof(fd_stake_rewards_t) );
214 48 : l = FD_LAYOUT_APPEND( l, alignof(fork_info_t), fd_ulong_sat_mul( fork_cnt, sizeof(fork_info_t) ) );
215 48 : l = FD_LAYOUT_APPEND( l, fork_pool_align(), fork_pool_footprint( fork_cnt ) );
216 48 : l = FD_LAYOUT_APPEND( l, alignof(uint), fd_ulong_sat_mul( buf_cnt, sizeof(uint) ) );
217 48 : l = FD_LAYOUT_APPEND( l, alignof(ulong), fd_ulong_sat_mul( buf_cnt, sizeof(ulong) ) );
218 48 : l = FD_LAYOUT_APPEND( l, alignof(uint), fd_ulong_sat_mul( buf_cnt*MAX_PARTITIONS_PER_EPOCH,
219 48 : sizeof(uint) ) );
220 48 : l = FD_LAYOUT_APPEND( l, alignof(partition_ele_t), fd_ulong_sat_mul( fd_ulong_sat_mul( buf_cnt, max_stake_accounts ),
221 48 : sizeof(partition_ele_t) ) );
222 48 : return FD_LAYOUT_FINI( l, fd_stake_rewards_align() );
223 48 : }
224 :
225 : void *
226 : fd_stake_rewards_new( void * shmem,
227 : ulong max_stake_accounts,
228 : ulong max_bank_cnt,
229 12 : ulong cache_cnt ) {
230 12 : if( FD_UNLIKELY( !shmem ) ) {
231 0 : FD_LOG_WARNING(( "NULL shmem" ));
232 0 : return NULL;
233 0 : }
234 12 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)shmem, fd_stake_rewards_align() ) ) ) {
235 0 : FD_LOG_WARNING(( "misaligned shmem" ));
236 0 : return NULL;
237 0 : }
238 :
239 : /* Entries are addressed by uint indices within a buffer. */
240 12 : if( FD_UNLIKELY( max_stake_accounts>=(ulong)UINT_MAX ) ) {
241 0 : FD_LOG_WARNING(( "max_stake_accounts is too large" ));
242 0 : return NULL;
243 0 : }
244 12 : if( FD_UNLIKELY( !max_bank_cnt || max_bank_cnt>FD_BANKS_MAX_BANKS ) ) {
245 0 : FD_LOG_WARNING(( "max_bank_cnt must be in [1,%lu]", FD_BANKS_MAX_BANKS ));
246 0 : return NULL;
247 0 : }
248 12 : if( FD_UNLIKELY( !cache_cnt || cache_cnt>max_bank_cnt+1UL ) ) {
249 0 : FD_LOG_WARNING(( "cache_cnt must be in [1,max_bank_cnt+1]" ));
250 0 : return NULL;
251 0 : }
252 12 : ulong fork_cnt = max_bank_cnt+1UL;
253 12 : ulong buf_cnt = cache_cnt+1UL;
254 :
255 12 : FD_SCRATCH_ALLOC_INIT( l, shmem );
256 12 : fd_stake_rewards_t * stake_rewards = FD_SCRATCH_ALLOC_APPEND( l, fd_stake_rewards_align(), sizeof(fd_stake_rewards_t) );
257 12 : void * fork_info_mem = FD_SCRATCH_ALLOC_APPEND( l, alignof(fork_info_t), fd_ulong_sat_mul( fork_cnt, sizeof(fork_info_t) ) );
258 12 : void * fork_pool_mem = FD_SCRATCH_ALLOC_APPEND( l, fork_pool_align(), fork_pool_footprint( fork_cnt ) );
259 12 : void * buf_fork_mem = FD_SCRATCH_ALLOC_APPEND( l, alignof(uint), fd_ulong_sat_mul( buf_cnt, sizeof(uint) ) );
260 12 : void * buf_seq_mem = FD_SCRATCH_ALLOC_APPEND( l, alignof(ulong), fd_ulong_sat_mul( buf_cnt, sizeof(ulong) ) );
261 12 : void * buf_heads_mem = FD_SCRATCH_ALLOC_APPEND( l, alignof(uint), fd_ulong_sat_mul( buf_cnt*MAX_PARTITIONS_PER_EPOCH,
262 12 : sizeof(uint) ) );
263 12 : void * buf_mem = FD_SCRATCH_ALLOC_APPEND( l, alignof(partition_ele_t), fd_ulong_sat_mul( fd_ulong_sat_mul( buf_cnt, max_stake_accounts ),
264 12 : sizeof(partition_ele_t) ) );
265 :
266 0 : fork_t * fork_pool = fork_pool_join( fork_pool_new( fork_pool_mem, fork_cnt ) );
267 12 : if( FD_UNLIKELY( !fork_pool ) ) {
268 0 : FD_LOG_WARNING(( "Failed to create fork pool" ));
269 0 : return NULL;
270 0 : }
271 12 : stake_rewards->fork_info_offset = (ulong)fork_info_mem - (ulong)shmem;
272 12 : stake_rewards->fork_pool_offset = (ulong)fork_pool - (ulong)shmem;
273 12 : stake_rewards->buf_fork_offset = (ulong)buf_fork_mem - (ulong)shmem;
274 12 : stake_rewards->buf_seq_offset = (ulong)buf_seq_mem - (ulong)shmem;
275 12 : stake_rewards->buf_partition_heads_offset = (ulong)buf_heads_mem - (ulong)shmem;
276 12 : stake_rewards->buf_offset = (ulong)buf_mem - (ulong)shmem;
277 12 : stake_rewards->max_stake_accounts = max_stake_accounts;
278 12 : stake_rewards->fork_cnt = fork_cnt;
279 12 : stake_rewards->cache_cnt = (uint)cache_cnt;
280 :
281 12 : fd_stake_rewards_clear( stake_rewards );
282 :
283 12 : FD_COMPILER_MFENCE();
284 12 : FD_VOLATILE( stake_rewards->magic ) = FD_STAKE_REWARDS_MAGIC;
285 12 : FD_COMPILER_MFENCE();
286 :
287 12 : return shmem;
288 12 : }
289 :
290 : fd_stake_rewards_t *
291 24 : fd_stake_rewards_join( void * shmem ) {
292 24 : if( FD_UNLIKELY( !shmem ) ) {
293 0 : FD_LOG_WARNING(( "NULL shmem" ));
294 0 : return NULL;
295 0 : }
296 :
297 24 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)shmem, fd_stake_rewards_align() ) ) ) {
298 0 : FD_LOG_WARNING(( "misaligned shmem" ));
299 0 : return NULL;
300 0 : }
301 :
302 24 : fd_stake_rewards_t * stake_rewards = (fd_stake_rewards_t *)shmem;
303 24 : if( FD_UNLIKELY( stake_rewards->magic != FD_STAKE_REWARDS_MAGIC ) ) {
304 0 : FD_LOG_WARNING(( "Invalid stake rewards magic" ));
305 0 : return NULL;
306 0 : }
307 24 : return stake_rewards;
308 24 : }
309 :
310 : static void
311 1997 : fork_reset_meta( fork_info_t * fork_info ) {
312 1997 : fork_info->ele_cnt = 0U;
313 1997 : fork_info->partition_cnt = 0U;
314 1997 : fork_info->win_lo = UINT_MAX;
315 1997 : fork_info->win_hi = UINT_MAX;
316 1997 : fork_info->buf_idx = UINT_MAX;
317 1997 : fork_info->ready = 0U;
318 1997 : fork_info->starting_block_height = 0UL;
319 1997 : fork_info->total_stake_rewards = 0UL;
320 1997 : fork_info->total_block_rewards = 0UL;
321 1997 : fork_info->refcnt = 0UL;
322 1997 : }
323 :
324 : void
325 627 : fd_stake_rewards_clear( fd_stake_rewards_t * stake_rewards ) {
326 627 : fork_pool_reset( get_fork_pool( stake_rewards ) );
327 2508 : for( ulong i=0UL; i<stake_rewards->fork_cnt; i++ )
328 1881 : fork_reset_meta( get_fork_info( stake_rewards, (ushort)i ) );
329 627 : uint * buf_forks = get_buf_forks( stake_rewards );
330 627 : ulong * buf_seqs = get_buf_seqs( stake_rewards );
331 2508 : for( uint i=0U; i<get_buf_cnt( stake_rewards ); i++ ) {
332 1881 : buf_forks[i] = UINT_MAX;
333 1881 : buf_seqs[i] = 0UL;
334 1881 : }
335 627 : stake_rewards->staging_fork = UINT_MAX;
336 627 : stake_rewards->finish_seq = 0UL;
337 627 : stake_rewards->iter_idx = UINT_MAX;
338 627 : }
339 :
340 : static void
341 : fork_purge( fd_stake_rewards_t * stake_rewards,
342 42 : ushort fork_idx ) {
343 42 : fork_info_t * fork_info = get_fork_info( stake_rewards, fork_idx );
344 42 : if( stake_rewards->staging_fork==(uint)fork_idx )
345 0 : stake_rewards->staging_fork = UINT_MAX;
346 42 : fork_drop_buf( stake_rewards, fork_idx );
347 42 : fork_reset_meta( fork_info );
348 42 : fork_pool_idx_release( get_fork_pool( stake_rewards ), (ulong)fork_idx );
349 42 : }
350 :
351 : void
352 : fd_stake_rewards_acquire( fd_stake_rewards_t * stake_rewards,
353 0 : ushort fork_idx ) {
354 0 : get_fork_info( stake_rewards, fork_idx )->refcnt++;
355 0 : }
356 :
357 : void
358 : fd_stake_rewards_release( fd_stake_rewards_t * stake_rewards,
359 42 : ushort fork_idx ) {
360 42 : fork_info_t * fork_info = get_fork_info( stake_rewards, fork_idx );
361 42 : ulong refcnt = fork_info->refcnt;
362 42 : FD_CHECK_CRIT( refcnt, "releasing stake rewards fork with zero refcount" );
363 42 : if( FD_UNLIKELY( refcnt==1UL ) ) fork_purge( stake_rewards, fork_idx );
364 0 : else fork_info->refcnt = refcnt-1UL;
365 42 : }
366 :
367 : ulong
368 : fd_stake_rewards_refcnt( fd_stake_rewards_t const * stake_rewards,
369 0 : ushort fork_idx ) {
370 0 : return get_fork_info( stake_rewards, fork_idx )->refcnt;
371 0 : }
372 :
373 : ulong
374 0 : fd_stake_rewards_free_cnt( fd_stake_rewards_t const * stake_rewards ) {
375 0 : return (ulong)fork_pool_free( get_fork_pool( stake_rewards ) );
376 0 : }
377 :
378 : ushort
379 : fd_stake_rewards_init( fd_stake_rewards_t * stake_rewards,
380 : fd_hash_t const * parent_blockhash,
381 : ulong starting_block_height,
382 : uint partitions_cnt,
383 : uint win_lo,
384 74 : ulong max_rewards_cnt ) {
385 74 : FD_CHECK_CRIT( stake_rewards->staging_fork==UINT_MAX,
386 74 : "initializing while another fork is staged" );
387 74 : FD_CHECK_CRIT( partitions_cnt && partitions_cnt<=MAX_PARTITIONS_PER_EPOCH,
388 74 : "invalid partition count" );
389 74 : FD_CHECK_CRIT( win_lo<partitions_cnt, "invalid window start" );
390 74 : FD_CHECK_CRIT( starting_block_height<=ULONG_MAX-(ulong)partitions_cnt,
391 74 : "partition block height overflow" );
392 :
393 74 : fork_t * fork_pool = get_fork_pool( stake_rewards );
394 :
395 74 : if( FD_UNLIKELY( !fork_pool_free( fork_pool ) ) ) {
396 0 : FD_LOG_ERR(( "No free forks in the stake rewards pool. This likely occurred due to extremely degenerate "
397 0 : "network conditions. Please report this crash to the Firedancer team." ));
398 0 : }
399 74 : ushort fork_idx = (ushort)fork_pool_idx_acquire( fork_pool );
400 74 : fork_info_t * fork_info = get_fork_info( stake_rewards, fork_idx );
401 74 : fork_reset_meta( fork_info );
402 74 : fork_info->refcnt = 1UL;
403 74 : fork_info->buf_idx = buf_acquire( stake_rewards, fork_idx );
404 :
405 74 : prime_hasher( stake_rewards, parent_blockhash );
406 :
407 74 : fork_info->partition_cnt = partitions_cnt;
408 74 : fork_info->starting_block_height = starting_block_height;
409 74 : window_init( stake_rewards, fork_idx, win_lo, max_rewards_cnt );
410 :
411 74 : return fork_idx;
412 74 : }
413 :
414 : uint
415 : fd_stake_rewards_window_lo( fd_stake_rewards_t const * stake_rewards,
416 41 : ushort fork_idx ) {
417 41 : fork_info_t const * fork_info = get_fork_info( stake_rewards, fork_idx );
418 41 : return fd_uint_if( fork_info->ready ||
419 41 : stake_rewards->staging_fork==(uint)fork_idx,
420 41 : fork_info->win_lo,
421 41 : UINT_MAX );
422 41 : }
423 :
424 : uint
425 : fd_stake_rewards_window_hi( fd_stake_rewards_t const * stake_rewards,
426 41 : ushort fork_idx ) {
427 41 : fork_info_t const * fork_info = get_fork_info( stake_rewards, fork_idx );
428 41 : return fd_uint_if( fork_info->ready ||
429 41 : stake_rewards->staging_fork==(uint)fork_idx,
430 41 : fork_info->win_hi,
431 41 : UINT_MAX );
432 41 : }
433 :
434 : void
435 : fd_stake_rewards_insert( fd_stake_rewards_t * stake_rewards,
436 : ushort fork_idx,
437 : fd_pubkey_t const * pubkey,
438 : ulong lamports,
439 : ulong credits_observed,
440 255 : ulong block_reward ) {
441 :
442 255 : FD_STATIC_ASSERT( sizeof(fd_pubkey_t)==32UL, partition_hash_size );
443 255 : FD_CHECK_CRIT( stake_rewards->staging_fork==(uint)fork_idx,
444 255 : "insert into a fork that is not staged" );
445 255 : ulong hash64 = fd_siphash13_fini_x32( stake_rewards->primed_hasher, pubkey->uc );
446 :
447 255 : fork_info_t * fork_info = get_fork_info( stake_rewards, fork_idx );
448 255 : ulong partition_index = (ulong)((uint128)fork_info->partition_cnt * (uint128) hash64 / ((uint128)ULONG_MAX + 1));
449 :
450 : /* The total covers the whole epoch, not just the window, so that it
451 : does not depend on where the window happens to sit. */
452 255 : fork_info->total_stake_rewards += lamports;
453 255 : fork_info->total_block_rewards += block_reward;
454 :
455 255 : if( FD_UNLIKELY( partition_index<fork_info->win_lo || partition_index>fork_info->win_hi ) ) return;
456 :
457 255 : uint curr_fork_len = fork_info->ele_cnt;
458 255 : if( FD_UNLIKELY( curr_fork_len>=stake_rewards->max_stake_accounts ) ) {
459 0 : FD_LOG_CRIT(( "invariant violation: curr_fork_len>=stake_rewards->max_stake_accounts" ));
460 0 : }
461 :
462 255 : partition_ele_t * buf = get_buf( stake_rewards, fork_info->buf_idx );
463 255 : partition_ele_t * partition_ele = buf + curr_fork_len;
464 255 : partition_ele->pubkey = *pubkey;
465 255 : partition_ele->lamports = lamports;
466 255 : partition_ele->block_reward = block_reward;
467 255 : partition_ele->credits_observed = credits_observed;
468 255 : partition_ele->next = UINT_MAX;
469 :
470 255 : uint * head = get_buf_partition_heads( stake_rewards, fork_info->buf_idx ) + partition_index;
471 255 : uint * tail = stake_rewards->staging_partition_idxs_tail + partition_index;
472 :
473 255 : if( FD_LIKELY( *head!=UINT_MAX ) ) buf[ *tail ].next = curr_fork_len;
474 69 : else *head = curr_fork_len;
475 255 : *tail = curr_fork_len;
476 :
477 255 : fork_info->ele_cnt++;
478 255 : }
479 :
480 : static void
481 : buf_trim( fd_stake_rewards_t * stake_rewards,
482 69 : uint keep_buf_idx ) {
483 69 : uint * buf_forks = get_buf_forks( stake_rewards );
484 69 : ulong * buf_seqs = get_buf_seqs( stake_rewards );
485 69 : uint buf_cnt = get_buf_cnt( stake_rewards );
486 :
487 69 : uint resident_cnt = 0U;
488 69 : uint victim_idx = UINT_MAX;
489 276 : for( uint buf_idx=0U; buf_idx<buf_cnt; buf_idx++ ) {
490 207 : if( buf_forks[buf_idx]==UINT_MAX ) continue;
491 69 : resident_cnt++;
492 69 : if( buf_idx==keep_buf_idx ) continue;
493 0 : if( victim_idx==UINT_MAX || buf_seqs[buf_idx]<buf_seqs[victim_idx] )
494 0 : victim_idx = buf_idx;
495 0 : }
496 69 : if( FD_LIKELY( resident_cnt<=stake_rewards->cache_cnt ) ) return;
497 :
498 0 : FD_CHECK_CRIT( resident_cnt==stake_rewards->cache_cnt+1U,
499 0 : "invalid resident stake rewards buffer count" );
500 0 : FD_CHECK_CRIT( victim_idx!=UINT_MAX,
501 0 : "missing resident stake rewards buffer eviction candidate" );
502 0 : ushort victim_fork = (ushort)buf_forks[victim_idx];
503 0 : fork_info_t * fork_info = get_fork_info( stake_rewards, victim_fork );
504 0 : FD_CHECK_CRIT( fork_info->ready && fork_info->buf_idx==victim_idx,
505 0 : "reward buffer owner mismatch" );
506 0 : fork_info->buf_idx = UINT_MAX;
507 0 : fork_info->ready = 0U;
508 0 : buf_forks[victim_idx] = UINT_MAX;
509 0 : buf_seqs[victim_idx] = 0UL;
510 0 : }
511 :
512 : void
513 : fd_stake_rewards_fini( fd_stake_rewards_t * stake_rewards,
514 74 : ushort fork_idx ) {
515 74 : FD_CHECK_CRIT( stake_rewards->staging_fork==(uint)fork_idx,
516 74 : "finishing a fork that is not staged" );
517 :
518 74 : fork_info_t * fork_info = get_fork_info( stake_rewards, fork_idx );
519 74 : if( FD_UNLIKELY( !fork_info->ele_cnt ) ) {
520 5 : fork_drop_buf( stake_rewards, fork_idx );
521 5 : fork_info->ready = 1U;
522 5 : stake_rewards->staging_fork = UINT_MAX;
523 5 : return;
524 5 : }
525 :
526 69 : uint buf_idx = fork_info->buf_idx;
527 69 : FD_CHECK_CRIT( buf_idx<get_buf_cnt( stake_rewards ), "invalid reward buffer" );
528 69 : FD_CHECK_CRIT( get_buf_forks( stake_rewards )[buf_idx]==(uint)fork_idx,
529 69 : "reward buffer owner mismatch" );
530 69 : get_buf_seqs( stake_rewards )[buf_idx] = stake_rewards->finish_seq++;
531 69 : fork_info->ready = 1U;
532 69 : stake_rewards->staging_fork = UINT_MAX;
533 69 : buf_trim( stake_rewards, buf_idx );
534 69 : }
535 :
536 : void
537 : fd_stake_rewards_iter_init( fd_stake_rewards_t * stake_rewards,
538 : ushort fork_idx,
539 41 : uint partition_idx ) {
540 41 : fork_info_t const * fork_info = get_fork_info( stake_rewards, fork_idx );
541 41 : FD_CHECK_CRIT( fork_info->ready, "stake rewards window is not resident" );
542 41 : if( FD_UNLIKELY( partition_idx<fork_info->win_lo || partition_idx>fork_info->win_hi ) ) {
543 0 : FD_LOG_CRIT(( "partition %u is outside of the valid window [%u,%u]", partition_idx, fork_info->win_lo, fork_info->win_hi ));
544 0 : }
545 41 : if( FD_UNLIKELY( fork_info->buf_idx==UINT_MAX ) ) {
546 0 : FD_CHECK_CRIT( !fork_info->ele_cnt, "stake rewards window is not resident" );
547 0 : stake_rewards->iter_fork = (uint)fork_idx;
548 0 : stake_rewards->iter_idx = UINT_MAX;
549 0 : return;
550 0 : }
551 41 : uint buf_idx = fork_info->buf_idx;
552 41 : FD_CHECK_CRIT( buf_idx<get_buf_cnt( stake_rewards ), "invalid reward buffer" );
553 41 : FD_CHECK_CRIT( get_buf_forks( stake_rewards )[buf_idx]==(uint)fork_idx,
554 41 : "reward buffer owner mismatch" );
555 41 : stake_rewards->iter_fork = (uint)fork_idx;
556 41 : stake_rewards->iter_buf = buf_idx;
557 41 : stake_rewards->iter_idx = get_buf_partition_heads( stake_rewards, buf_idx )[partition_idx];
558 41 : }
559 :
560 : void
561 : fd_stake_rewards_iter_next( fd_stake_rewards_t * stake_rewards,
562 136 : ushort fork_idx ) {
563 136 : FD_CHECK_CRIT( stake_rewards->iter_fork==(uint)fork_idx,
564 136 : "iterator fork mismatch" );
565 136 : FD_CHECK_CRIT( stake_rewards->iter_idx!=UINT_MAX,
566 136 : "advancing a finished iterator" );
567 136 : partition_ele_t const * buf = get_buf( stake_rewards, stake_rewards->iter_buf );
568 136 : stake_rewards->iter_idx = buf[stake_rewards->iter_idx].next;
569 136 : }
570 :
571 : int
572 259 : fd_stake_rewards_iter_done( fd_stake_rewards_t * stake_rewards ) {
573 259 : return stake_rewards->iter_idx==UINT_MAX;
574 259 : }
575 :
576 : void
577 : fd_stake_rewards_iter_ele( fd_stake_rewards_t * stake_rewards,
578 : ushort fork_idx,
579 : fd_pubkey_t * pubkey_out,
580 : ulong * lamports_out,
581 : ulong * credits_observed_out,
582 136 : ulong * block_reward_out ) {
583 136 : FD_CHECK_CRIT( stake_rewards->iter_fork==(uint)fork_idx,
584 136 : "iterator fork mismatch" );
585 136 : FD_CHECK_CRIT( stake_rewards->iter_idx!=UINT_MAX,
586 136 : "accessing a finished iterator" );
587 136 : partition_ele_t const * ele =
588 136 : get_buf( stake_rewards, stake_rewards->iter_buf ) +
589 136 : stake_rewards->iter_idx;
590 :
591 136 : *pubkey_out = ele->pubkey;
592 136 : *lamports_out = ele->lamports;
593 136 : *credits_observed_out = ele->credits_observed;
594 136 : if( block_reward_out ) *block_reward_out = ele->block_reward;
595 136 : }
596 :
597 : ulong
598 : fd_stake_rewards_total_rewards( fd_stake_rewards_t const * stake_rewards,
599 32 : ushort fork_idx ) {
600 32 : return get_fork_info( stake_rewards, fork_idx )->total_stake_rewards;
601 32 : }
602 :
603 : ulong
604 : fd_stake_rewards_total_block_rewards( fd_stake_rewards_t const * stake_rewards,
605 0 : ushort fork_idx ) {
606 0 : return get_fork_info( stake_rewards, fork_idx )->total_block_rewards;
607 0 : }
608 :
609 : uint
610 : fd_stake_rewards_num_partitions( fd_stake_rewards_t const * stake_rewards,
611 74 : ushort fork_idx ) {
612 74 : return get_fork_info( stake_rewards, fork_idx )->partition_cnt;
613 74 : }
614 :
615 : ulong
616 : fd_stake_rewards_starting_block_height( fd_stake_rewards_t const * stake_rewards,
617 74 : ushort fork_idx ) {
618 74 : return get_fork_info( stake_rewards, fork_idx )->starting_block_height;
619 74 : }
620 :
621 : ulong
622 : fd_stake_rewards_exclusive_ending_block_height( fd_stake_rewards_t const * stake_rewards,
623 74 : ushort fork_idx ) {
624 74 : fork_info_t const * fork_info = get_fork_info( stake_rewards, fork_idx );
625 74 : return fork_info->starting_block_height + fork_info->partition_cnt;
626 74 : }
|