LCOV - code coverage report
Current view: top level - flamenco/leaders - fd_multi_epoch_leaders.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 113 125 90.4 %
Date: 2026-10-24 04:27:19 Functions: 14 14 100.0 %

          Line data    Source code
       1             : #include "fd_multi_epoch_leaders.h"
       2             : 
       3             : void *
       4          51 : fd_multi_epoch_leaders_new( void * shmem ) {
       5          51 :   if( FD_UNLIKELY( !shmem ) ) {
       6           0 :     FD_LOG_WARNING(( "NULL shmem" ));
       7           0 :     return NULL;
       8           0 :   }
       9             : 
      10          51 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)shmem, fd_multi_epoch_leaders_align() ) ) ) {
      11           0 :     FD_LOG_WARNING(( "misaligned shmem" ));
      12           0 :     return NULL;
      13           0 :   }
      14             : 
      15          51 :   fd_multi_epoch_leaders_t * leaders = (fd_multi_epoch_leaders_t *)shmem;
      16             : 
      17             :   /* Initialize all epochs to satisfy invariants */
      18          51 :   fd_vote_stake_weight_t dummy_stakes[ 1 ] = {{ .vote_key = {{0}}, .id_key = {{0}}, .stake = 1UL }};
      19         153 :   for( ulong i=0UL; i<MULTI_EPOCH_LEADERS_EPOCH_CNT; i++ ) {
      20         102 :     leaders->lsched[i] = fd_epoch_leaders_join( fd_epoch_leaders_new( leaders->_lsched[i], i, 0UL, 1UL, 1UL, dummy_stakes, 0 ) );
      21         102 :     FD_TEST( leaders->lsched[i] );
      22         102 :     leaders->init_done[i] = 0;
      23         102 :   }
      24             : 
      25          51 :   return shmem;
      26          51 : }
      27             : 
      28             : fd_multi_epoch_leaders_t *
      29          51 : fd_multi_epoch_leaders_join( void * shleaders ) { return shleaders; }
      30             : 
      31             : void *
      32          27 : fd_multi_epoch_leaders_leave( fd_multi_epoch_leaders_t * mleaders ) { return mleaders; }
      33             : 
      34             : void *
      35          27 : fd_multi_epoch_leaders_delete( void * shleaders ) { return shleaders; }
      36             : 
      37             : fd_epoch_leaders_t const *
      38             : fd_multi_epoch_leaders_get_lsched_for_epoch( fd_multi_epoch_leaders_t const * mleaders,
      39           9 :                                              ulong                            epoch ) {
      40           9 :   fd_epoch_leaders_t const * even_lsched = fd_ptr_if( mleaders->init_done[0] & !!(mleaders->lsched[0]->epoch==epoch), mleaders->lsched[0], NULL );
      41           9 :   fd_epoch_leaders_t const * odd_lsched  = fd_ptr_if( mleaders->init_done[1] & !!(mleaders->lsched[1]->epoch==epoch), mleaders->lsched[1], NULL );
      42           9 :   return fd_ptr_if( !!even_lsched, even_lsched, odd_lsched );
      43           9 : }
      44             : 
      45             : static inline ulong
      46             : fd_multi_epoch_leaders_get_epoch_idx( fd_multi_epoch_leaders_t const * mleaders,
      47       69207 :                                       ulong                            slot ) {
      48       69207 :   fd_epoch_leaders_t const * even_lsched = mleaders->lsched[0];
      49       69207 :   fd_epoch_leaders_t const * odd_lsched  = mleaders->lsched[1];
      50             : 
      51       69207 :   ulong even_match = fd_ulong_if( mleaders->init_done[0] & !!(even_lsched->slot0<=slot) & !!(slot<even_lsched->slot0+even_lsched->slot_cnt), 0UL, ULONG_MAX );
      52       69207 :   ulong odd_match  = fd_ulong_if( mleaders->init_done[1] & !!(odd_lsched->slot0<=slot) & !!(slot<odd_lsched->slot0+odd_lsched->slot_cnt), 1UL, ULONG_MAX );
      53             : 
      54       69207 :   return fd_ulong_if( even_match!=ULONG_MAX, even_match, odd_match );
      55       69207 : }
      56             : 
      57             : fd_epoch_leaders_t const *
      58             : fd_multi_epoch_leaders_get_lsched_for_slot( fd_multi_epoch_leaders_t const *  mleaders,
      59         153 :                                              ulong                            slot ) {
      60         153 :   const ulong epoch_idx = fd_multi_epoch_leaders_get_epoch_idx( mleaders, slot );
      61         153 :   if( FD_UNLIKELY( epoch_idx==ULONG_MAX ) ) return NULL;
      62         147 :   return mleaders->lsched[epoch_idx];
      63         153 : }
      64             : 
      65             : ulong
      66             : fd_multi_epoch_leaders_get_next_slot( fd_multi_epoch_leaders_t const * mleaders,
      67             :                                       ulong                            start_slot,
      68          36 :                                       fd_pubkey_t              const * leader_q ) {
      69             : 
      70             :   /* Find epoch containing start_slot */
      71          36 :   ulong epoch_idx = fd_multi_epoch_leaders_get_epoch_idx( mleaders, start_slot );
      72          36 :   if( FD_UNLIKELY( epoch_idx==ULONG_MAX ) ) return ULONG_MAX;
      73             : 
      74             :   /* Start at epoch_idx and seek next slot (across epochs)  */
      75          36 :   for( ulong i=0; i<MULTI_EPOCH_LEADERS_EPOCH_CNT; i++ ) {
      76          33 :     ulong epoch_i = (epoch_idx + i) % MULTI_EPOCH_LEADERS_EPOCH_CNT;
      77             : 
      78          33 :     fd_epoch_leaders_t const * epoch_lsched  = mleaders->lsched[ epoch_i ];
      79          33 :     ulong                      slot0         = epoch_lsched->slot0;
      80          33 :     ulong                      slot_end      = slot0 + epoch_lsched->slot_cnt;
      81             : 
      82             :     /* skip older epochs */
      83          33 :     if( FD_UNLIKELY( !mleaders->init_done[epoch_i] ) ) continue;
      84             : 
      85          33 :     ulong start_slot_it = fd_ulong_max( start_slot, slot0 );
      86        9177 :     for( ulong slot=start_slot_it; slot<slot_end; slot++ ) {
      87        9168 :       fd_pubkey_t const * leader = fd_epoch_leaders_get( epoch_lsched, slot );
      88        9168 :       if( FD_UNLIKELY( !memcmp( leader->key, leader_q->key, 32UL ) ) ) return slot;
      89        9168 :     }
      90          33 :   }
      91             : 
      92           3 :   return ULONG_MAX;
      93          27 : }
      94             : 
      95             : void
      96             : fd_multi_epoch_leaders_stake_msg_init( fd_multi_epoch_leaders_t   * mleaders,
      97          75 :                                        fd_stake_weight_msg_t const * msg ) {
      98          75 :   if( FD_UNLIKELY( msg->staked_vote_cnt > MAX_STAKE_WEIGHTS ) )
      99           0 :     FD_LOG_ERR(( "Multi-epoch leaders received a malformed update with %lu stakes in it,"
     100          75 :                  " but the maximum allowed is %lu", msg->staked_vote_cnt, MAX_STAKE_WEIGHTS ));
     101          75 :   if( FD_UNLIKELY( msg->slot_cnt > MAX_SLOTS_PER_EPOCH ) )
     102           0 :     FD_LOG_ERR(( "Multi-epoch leaders received a malformed update with %lu slots in it,"
     103          75 :                  " but the maximum allowed is %lu", msg->slot_cnt, MAX_SLOTS_PER_EPOCH ));
     104             : 
     105          75 :   mleaders->scratch->epoch             = msg->epoch;
     106          75 :   mleaders->scratch->start_slot        = msg->start_slot;
     107          75 :   mleaders->scratch->slot_cnt          = msg->slot_cnt;
     108          75 :   mleaders->scratch->staked_cnt        = msg->staked_vote_cnt;
     109          75 :   mleaders->scratch->geo_aware_lsched  = 0;
     110             : 
     111          75 :   fd_memcpy( mleaders->vote_stake_weight, fd_stake_weight_msg_stake_weights( msg ), msg->staked_vote_cnt*sizeof(fd_vote_stake_weight_t) );
     112          75 : }
     113             : 
     114             : void
     115             : fd_multi_epoch_leaders_epoch_msg_init( fd_multi_epoch_leaders_t   * mleaders,
     116           6 :                                        fd_epoch_info_msg_t const  * msg ) {
     117           6 :   if( FD_UNLIKELY( msg->staked_vote_cnt > MAX_STAKE_WEIGHTS ) )
     118           0 :     FD_LOG_ERR(( "Multi-epoch leaders received a malformed update with %lu stakes in it,"
     119           6 :                  " but the maximum allowed is %lu", msg->staked_vote_cnt, MAX_STAKE_WEIGHTS ));
     120           6 :   if( FD_UNLIKELY( msg->slot_cnt > MAX_SLOTS_PER_EPOCH ) )
     121           0 :     FD_LOG_ERR(( "Multi-epoch leaders received a malformed update with %lu slots in it,"
     122           6 :                  " but the maximum allowed is %lu", msg->slot_cnt, MAX_SLOTS_PER_EPOCH ));
     123             : 
     124           6 :   mleaders->scratch->epoch             = msg->epoch;
     125           6 :   mleaders->scratch->start_slot        = msg->start_slot;
     126           6 :   mleaders->scratch->slot_cnt          = msg->slot_cnt;
     127           6 :   mleaders->scratch->staked_cnt        = msg->staked_vote_cnt;
     128           6 :   mleaders->scratch->geo_aware_lsched  = fd_epoch_leaders_use_geo_aware( &msg->features, &msg->epoch_schedule, msg->epoch );
     129             : 
     130           6 :   fd_vote_stake_weight_t const * weights = fd_epoch_info_msg_stake_weights( msg );
     131           6 :   fd_memcpy( mleaders->vote_stake_weight, weights, msg->staked_vote_cnt*sizeof(fd_vote_stake_weight_t) );
     132           6 : }
     133             : 
     134             : void
     135          78 : fd_multi_epoch_leaders_stake_msg_fini( fd_multi_epoch_leaders_t * mleaders ) {
     136          78 :   const ulong epoch          = mleaders->scratch->epoch;
     137          78 :   const ulong slot0          = mleaders->scratch->start_slot;
     138          78 :   const ulong slot_cnt       = mleaders->scratch->slot_cnt;
     139          78 :   const ulong pub_cnt        = mleaders->scratch->staked_cnt;
     140          78 :   const int   geo_aware      = mleaders->scratch->geo_aware_lsched;
     141          78 :   const ulong epoch_idx      = epoch % MULTI_EPOCH_LEADERS_EPOCH_CNT;
     142             : 
     143          78 :   fd_vote_stake_weight_t * stakes = mleaders->vote_stake_weight;
     144             : 
     145          78 :   if( FD_UNLIKELY( slot_cnt>MAX_SLOTS_PER_EPOCH ) )
     146           0 :     FD_LOG_ERR(( "Multi-epoch leaders received a malformed update with %lu slots in it,"
     147          78 :                  " but the maximum allowed is %lu", slot_cnt, MAX_SLOTS_PER_EPOCH ));
     148          78 :   if( FD_UNLIKELY( pub_cnt>MAX_STAKE_WEIGHTS ) )
     149           0 :     FD_LOG_ERR(( "Multi-epoch leaders received a malformed update with %lu stakes in it,"
     150          78 :                  " but the maximum allowed is %lu", pub_cnt, MAX_STAKE_WEIGHTS ));
     151             : 
     152             :   /* Clear old data */
     153          78 :   fd_epoch_leaders_delete( fd_epoch_leaders_leave( mleaders->lsched[epoch_idx] ) );
     154             : 
     155             :   /* Populate new lsched */
     156          78 :   uchar *  lsched_mem        = mleaders->_lsched[epoch_idx];
     157          78 :   mleaders->lsched[epoch_idx] = fd_epoch_leaders_join( fd_epoch_leaders_new(
     158          78 :                                     lsched_mem, epoch, slot0, slot_cnt,
     159          78 :                                     pub_cnt, stakes, geo_aware ) );
     160          78 :   mleaders->init_done[epoch_idx] = 1;
     161          78 : }
     162             : 
     163             : void
     164           6 : fd_multi_epoch_leaders_epoch_msg_fini( fd_multi_epoch_leaders_t * mleaders ) {
     165           6 :   fd_multi_epoch_leaders_stake_msg_fini( mleaders );
     166           6 : }
     167             : 
     168             : fd_pubkey_t const *
     169             : fd_multi_epoch_leaders_get_leader_for_slot( fd_multi_epoch_leaders_t const * mleaders,
     170       69018 :                                             ulong                            slot ) {
     171       69018 :   const ulong epoch_idx = fd_multi_epoch_leaders_get_epoch_idx( mleaders, slot );
     172       69018 :   if( FD_UNLIKELY( epoch_idx==ULONG_MAX ) ) return NULL;
     173       69012 :   return fd_epoch_leaders_get( mleaders->lsched[epoch_idx], slot );
     174       69018 : }
     175             : 
     176             : fd_multi_epoch_leaders_lsched_sorted_t
     177          15 : fd_multi_epoch_leaders_get_sorted_lscheds( fd_multi_epoch_leaders_t const * mleaders ) {
     178          15 :   fd_multi_epoch_leaders_lsched_sorted_t ret = { .lscheds = { NULL, NULL } };
     179          15 :   fd_epoch_leaders_t * even_option = fd_ptr_if( mleaders->init_done[0], mleaders->lsched[0], NULL );
     180          15 :   fd_epoch_leaders_t * odd_option  = fd_ptr_if( mleaders->init_done[1], mleaders->lsched[1], NULL );
     181             : 
     182             :   /* Sort by epoch if both non-null, null comes first */
     183          15 :   if( even_option && odd_option ) {
     184           9 :     ret.lscheds[0] = fd_ptr_if( even_option->epoch < odd_option->epoch, even_option, odd_option );
     185           9 :     ret.lscheds[1] = fd_ptr_if( even_option->epoch < odd_option->epoch, odd_option, even_option );
     186           9 :   } else {
     187             :     /* if one non-null, this will pick it up. Else, both null and this no-ops */
     188           6 :     ret.lscheds[0] = fd_ptr_if( !!even_option, even_option, odd_option );
     189           6 :   }
     190             : 
     191          15 :   return ret;
     192          15 : }

Generated by: LCOV version 1.14