LCOV - code coverage report
Current view: top level - flamenco/runtime/sysvar - fd_sysvar_epoch_schedule.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 87 91 95.6 %
Date: 2026-07-10 05:48:38 Functions: 6 7 85.7 %

          Line data    Source code
       1             : #include "fd_sysvar_epoch_schedule.h"
       2             : #include "fd_sysvar.h"
       3             : #include "../fd_system_ids.h"
       4             : 
       5             : fd_epoch_schedule_t *
       6             : fd_epoch_schedule_derive( fd_epoch_schedule_t * schedule,
       7             :                           ulong                 epoch_len,
       8             :                           ulong                 leader_schedule_slot_offset,
       9       12267 :                           int                   warmup ) {
      10             : 
      11       12267 :   if( FD_UNLIKELY( epoch_len < FD_EPOCH_LEN_MIN ) ) {
      12           6 :     FD_LOG_WARNING(( "epoch_len too small" ));
      13           6 :     return NULL;
      14           6 :   }
      15             : 
      16       12261 :   *schedule = (fd_epoch_schedule_t) {
      17       12261 :     .slots_per_epoch             = epoch_len,
      18       12261 :     .leader_schedule_slot_offset = leader_schedule_slot_offset,
      19       12261 :     .warmup                      = !!warmup
      20       12261 :   };
      21             : 
      22       12261 :   if( warmup ) {
      23        6081 :     ulong ceil_log2_epoch   = (ulong)fd_ulong_find_msb( epoch_len-1UL ) + 1UL;
      24             : 
      25        6081 :     if( FD_UNLIKELY( ceil_log2_epoch>=64UL ) ) {
      26           3 :       FD_LOG_WARNING(( "epoch_len too large (ceil_log2_epoch %lu)", ceil_log2_epoch ));
      27           3 :       return NULL;
      28           3 :     }
      29             : 
      30        6078 :     ulong ceil_log2_len_min = (ulong)fd_ulong_find_msb( FD_EPOCH_LEN_MIN );
      31             : 
      32        6078 :     schedule->first_normal_epoch = fd_ulong_sat_sub( ceil_log2_epoch, ceil_log2_len_min );
      33        6078 :     schedule->first_normal_slot  = (1UL << ceil_log2_epoch) - FD_EPOCH_LEN_MIN;
      34        6078 :   }
      35             : 
      36       12258 :   return schedule;
      37       12261 : }
      38             : 
      39             : void
      40             : fd_sysvar_epoch_schedule_write( fd_bank_t *                 bank,
      41             :                                 fd_accdb_t *                accdb,
      42             :                                 fd_capture_ctx_t *          capture_ctx,
      43           9 :                                 fd_epoch_schedule_t const * epoch_schedule ) {
      44           9 :   fd_sysvar_account_update( bank, accdb, capture_ctx, &fd_sysvar_epoch_schedule_id, epoch_schedule, sizeof(fd_epoch_schedule_t) );
      45           9 : }
      46             : 
      47             : void
      48             : fd_sysvar_epoch_schedule_init( fd_bank_t *        bank,
      49             :                                fd_accdb_t *       accdb,
      50           0 :                                fd_capture_ctx_t * capture_ctx ) {
      51           0 :   fd_epoch_schedule_t const * epoch_schedule = &bank->f.epoch_schedule;
      52           0 :   fd_sysvar_epoch_schedule_write( bank, accdb, capture_ctx, epoch_schedule );
      53           0 : }
      54             : 
      55             : /* https://github.com/solana-labs/solana/blob/88aeaa82a856fc807234e7da0b31b89f2dc0e091/sdk/program/src/epoch_schedule.rs#L105 */
      56             : 
      57             : ulong
      58             : fd_epoch_slot_cnt( fd_epoch_schedule_t const * schedule,
      59         252 :                    ulong                       epoch ) {
      60         252 :   if( FD_UNLIKELY( epoch < schedule->first_normal_epoch ) ) {
      61          63 :     ulong exp = fd_ulong_sat_add( epoch, (ulong)fd_ulong_find_lsb( FD_EPOCH_LEN_MIN ) );
      62          63 :     return ( exp<64UL ? 1UL<<exp : ULONG_MAX ); // saturating_pow
      63          63 :   }
      64             : 
      65         189 :   return schedule->slots_per_epoch;
      66         252 : }
      67             : 
      68             : /* https://github.com/solana-labs/solana/blob/88aeaa82a856fc807234e7da0b31b89f2dc0e091/sdk/program/src/epoch_schedule.rs#L170 */
      69             : 
      70             : ulong
      71             : fd_epoch_slot0( fd_epoch_schedule_t const * schedule,
      72        9252 :                 ulong                       epoch ) {
      73        9252 :   if( FD_UNLIKELY( epoch <= schedule->first_normal_epoch ) ) {
      74        7809 :     ulong power = fd_ulong_if( epoch<64UL, 1UL<<epoch, ULONG_MAX );
      75        7809 :     return fd_ulong_sat_mul( power-1UL, FD_EPOCH_LEN_MIN );
      76        7809 :   }
      77             : 
      78        1443 :   return fd_ulong_sat_add(
      79        1443 :           fd_ulong_sat_mul(
      80        1443 :             fd_ulong_sat_sub(
      81        1443 :               epoch,
      82        1443 :               schedule->first_normal_epoch),
      83        1443 :             schedule->slots_per_epoch),
      84        1443 :           schedule->first_normal_slot);
      85        9252 : }
      86             : 
      87             : /* https://github.com/solana-labs/solana/blob/88aeaa82a856fc807234e7da0b31b89f2dc0e091/sdk/program/src/epoch_schedule.rs#L140 */
      88             : 
      89             : ulong
      90             : fd_slot_to_epoch( fd_epoch_schedule_t const * schedule,
      91             :                   ulong                       slot,
      92     1355472 :                   ulong *                     out_offset_opt ) {
      93     1355472 :   ulong epoch;
      94     1355472 :   ulong offset;
      95             : 
      96     1355472 :   if( FD_UNLIKELY( slot < schedule->first_normal_slot ) ) {
      97             :     /* step0 = ceil(log2(FD_EPOCH_LEN_MIN))
      98             :        step1 = ceil(log2(FD_EPOCH_LEN_MIN + slot + 1))
      99             :        epoch = step1 - step0 - 1 */
     100             : 
     101       23727 :     ulong s0 = FD_EPOCH_LEN_MIN + slot + 1UL;
     102             :     /* Invariant: s0 > 1UL */
     103             :     /* Invariant: s0 > FD_EPOCH_LEN_MIN */
     104             : 
     105             :     /* Find lowest exp where (2^exp) >= s0
     106             :        (Only valid for s0 > 1UL and FD_EPOCH_LEN_MIN > 1UL) */
     107       23727 :     int   exp       = fd_ulong_find_msb( s0-1UL ) + 1;
     108       23727 :     int   min_exp   = fd_ulong_find_msb( FD_EPOCH_LEN_MIN );
     109       23727 :           epoch     = (ulong)( exp - min_exp - 1 );
     110       23727 :     ulong epoch_len = 1UL<<( epoch + (ulong)fd_uint_find_lsb( FD_EPOCH_LEN_MIN ) );
     111       23727 :           offset    = slot - ( epoch_len - FD_EPOCH_LEN_MIN );
     112     1331745 :   } else {
     113     1331745 :     if( FD_UNLIKELY( schedule->slots_per_epoch == 0UL ) ) {
     114          12 :       FD_LOG_WARNING(( "zero slots_per_epoch returning first_normal_epoch %lu", schedule->first_normal_epoch ));
     115          12 :       if( out_offset_opt ) *out_offset_opt = 0UL;
     116          12 :       return schedule->first_normal_epoch;
     117          12 :     }
     118     1331733 :     ulong n_slot  = slot - schedule->first_normal_slot;
     119     1331733 :     ulong n_epoch = n_slot / schedule->slots_per_epoch;
     120     1331733 :           epoch   = schedule->first_normal_epoch + n_epoch;
     121     1331733 :           offset  = n_slot % schedule->slots_per_epoch;
     122     1331733 :   }
     123             : 
     124     1355460 :   ulong   dummy_out;
     125     1355460 :   ulong * out_offset = out_offset_opt ? out_offset_opt : &dummy_out;
     126             : 
     127     1355460 :   *out_offset = offset;
     128     1355460 :   return epoch;
     129     1355472 : }
     130             : 
     131             : /* https://docs.rs/solana-epoch-schedule/2.2.1/src/solana_epoch_schedule/lib.rs.html#136-151 */
     132             : 
     133             : ulong
     134             : fd_slot_to_leader_schedule_epoch( fd_epoch_schedule_t const * schedule,
     135        4176 :                                   ulong                       slot ) {
     136             : 
     137        4176 :   if( FD_UNLIKELY( slot<schedule->first_normal_slot ) ) {
     138           3 :     return fd_slot_to_epoch( schedule, slot, NULL ) + 1UL;
     139           3 :   }
     140             : 
     141        4173 :   if( FD_UNLIKELY( schedule->slots_per_epoch == 0UL ) ) {
     142          12 :     FD_LOG_WARNING(( "zero slots_per_epoch returning first_normal_epoch %lu", schedule->first_normal_epoch ));
     143          12 :     return schedule->first_normal_epoch;
     144          12 :   }
     145             : 
     146        4161 :   ulong new_slots_since_first_normal_slot =
     147        4161 :       slot - schedule->first_normal_slot;
     148        4161 :   ulong new_first_normal_leader_schedule_slot =
     149        4161 :       new_slots_since_first_normal_slot + schedule->leader_schedule_slot_offset;
     150        4161 :   ulong new_epochs_since_first_normal_leader_schedule =
     151        4161 :       new_first_normal_leader_schedule_slot / schedule->slots_per_epoch;
     152             : 
     153        4161 :   return schedule->first_normal_epoch + new_epochs_since_first_normal_leader_schedule;
     154        4173 : }

Generated by: LCOV version 1.14