LCOV - code coverage report
Current view: top level - flamenco/rewards - fd_stake_rewards.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 317 392 80.9 %
Date: 2026-10-23 17:13:25 Functions: 34 38 89.5 %

          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 : }

Generated by: LCOV version 1.14