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 111 78.4 %
Date: 2026-05-25 08:51:51 Functions: 6 9 66.7 %

          Line data    Source code
       1             : #include "fd_sysvar_epoch_schedule.h"
       2             : #include "fd_sysvar.h"
       3             : #include "../fd_system_ids.h"
       4             : #include "../../accdb/fd_accdb_sync.h"
       5             : 
       6             : static int
       7           0 : validate( fd_epoch_schedule_t const * schedule ) {
       8           0 :   return schedule->warmup!=0 && schedule->warmup!=1;
       9           0 : }
      10             : 
      11             : fd_epoch_schedule_t *
      12             : fd_epoch_schedule_derive( fd_epoch_schedule_t * schedule,
      13             :                           ulong                 epoch_len,
      14             :                           ulong                 leader_schedule_slot_offset,
      15       12264 :                           int                   warmup ) {
      16             : 
      17       12264 :   if( FD_UNLIKELY( epoch_len < FD_EPOCH_LEN_MIN ) ) {
      18           6 :     FD_LOG_WARNING(( "epoch_len too small" ));
      19           6 :     return NULL;
      20           6 :   }
      21             : 
      22       12258 :   *schedule = (fd_epoch_schedule_t) {
      23       12258 :     .slots_per_epoch             = epoch_len,
      24       12258 :     .leader_schedule_slot_offset = leader_schedule_slot_offset,
      25       12258 :     .warmup                      = !!warmup
      26       12258 :   };
      27             : 
      28       12258 :   if( warmup ) {
      29        6084 :     ulong ceil_log2_epoch   = (ulong)fd_ulong_find_msb( epoch_len-1UL ) + 1UL;
      30             : 
      31        6084 :     if( FD_UNLIKELY( ceil_log2_epoch>=64UL ) ) {
      32           3 :       FD_LOG_WARNING(( "epoch_len too large (ceil_log2_epoch %lu)", ceil_log2_epoch ));
      33           3 :       return NULL;
      34           3 :     }
      35             : 
      36        6081 :     ulong ceil_log2_len_min = (ulong)fd_ulong_find_msb( FD_EPOCH_LEN_MIN );
      37             : 
      38        6081 :     schedule->first_normal_epoch = fd_ulong_sat_sub( ceil_log2_epoch, ceil_log2_len_min );
      39        6081 :     schedule->first_normal_slot  = (1UL << ceil_log2_epoch) - FD_EPOCH_LEN_MIN;
      40        6081 :   }
      41             : 
      42       12255 :   return schedule;
      43       12258 : }
      44             : 
      45             : void
      46             : fd_sysvar_epoch_schedule_write( fd_bank_t *                 bank,
      47             :                                 fd_accdb_user_t *           accdb,
      48             :                                 fd_funk_txn_xid_t const *   xid,
      49             :                                 fd_capture_ctx_t *          capture_ctx,
      50          21 :                                 fd_epoch_schedule_t const * epoch_schedule ) {
      51             : 
      52          21 :   fd_sysvar_account_update( bank, accdb, xid, capture_ctx, &fd_sysvar_epoch_schedule_id, epoch_schedule, sizeof(fd_epoch_schedule_t) );
      53          21 : }
      54             : 
      55             : fd_epoch_schedule_t *
      56             : fd_sysvar_epoch_schedule_read( fd_accdb_user_t *         accdb,
      57             :                                fd_funk_txn_xid_t const * xid,
      58           0 :                                fd_epoch_schedule_t *     out ) {
      59           0 :   fd_accdb_ro_t ro[1];
      60           0 :   if( FD_UNLIKELY( !fd_accdb_open_ro( accdb, ro, xid, &fd_sysvar_epoch_schedule_id ) ) ) {
      61           0 :     return NULL;
      62           0 :   }
      63             : 
      64           0 :   if( FD_UNLIKELY( fd_accdb_ref_data_sz( ro )!=FD_SYSVAR_EPOCH_SCHEDULE_BINCODE_SZ ) ) {
      65           0 :     fd_accdb_close_ro( accdb, ro );
      66           0 :     return NULL;
      67           0 :   }
      68             : 
      69           0 :   memcpy( out, fd_accdb_ref_data_const( ro ), sizeof(fd_epoch_schedule_t) );
      70             : 
      71           0 :   if( FD_UNLIKELY( validate( out ) ) ) {
      72           0 :     fd_accdb_close_ro( accdb, ro );
      73           0 :     return NULL;
      74           0 :   }
      75             : 
      76           0 :   fd_accdb_close_ro( accdb, ro );
      77           0 :   return out;
      78           0 : }
      79             : 
      80             : void
      81             : fd_sysvar_epoch_schedule_init( fd_bank_t *               bank,
      82             :                                fd_accdb_user_t *         accdb,
      83             :                                fd_funk_txn_xid_t const * xid,
      84           0 :                                fd_capture_ctx_t *        capture_ctx ) {
      85           0 :   fd_epoch_schedule_t const * epoch_schedule = &bank->f.epoch_schedule;
      86           0 :   fd_sysvar_epoch_schedule_write( bank, accdb, xid, capture_ctx, epoch_schedule );
      87           0 : }
      88             : 
      89             : /* https://github.com/solana-labs/solana/blob/88aeaa82a856fc807234e7da0b31b89f2dc0e091/sdk/program/src/epoch_schedule.rs#L105 */
      90             : 
      91             : ulong
      92             : fd_epoch_slot_cnt( fd_epoch_schedule_t const * schedule,
      93         195 :                    ulong                       epoch ) {
      94             : 
      95         195 :   if( FD_UNLIKELY( epoch < schedule->first_normal_epoch ) ) {
      96          63 :     ulong exp = fd_ulong_sat_add( epoch, (ulong)fd_ulong_find_lsb( FD_EPOCH_LEN_MIN ) );
      97          63 :     return ( exp<64UL ? 1UL<<exp : ULONG_MAX ); // saturating_pow
      98          63 :   }
      99             : 
     100         132 :   return schedule->slots_per_epoch;
     101         195 : }
     102             : 
     103             : /* https://github.com/solana-labs/solana/blob/88aeaa82a856fc807234e7da0b31b89f2dc0e091/sdk/program/src/epoch_schedule.rs#L170 */
     104             : 
     105             : ulong
     106             : fd_epoch_slot0( fd_epoch_schedule_t const * schedule,
     107        9846 :                 ulong                       epoch ) {
     108        9846 :   if( FD_UNLIKELY( epoch <= schedule->first_normal_epoch ) ) {
     109        9585 :     ulong power = fd_ulong_if( epoch<64UL, 1UL<<epoch, ULONG_MAX );
     110        9585 :     return fd_ulong_sat_mul( power-1UL, FD_EPOCH_LEN_MIN );
     111        9585 :   }
     112             : 
     113         261 :   return fd_ulong_sat_add(
     114         261 :           fd_ulong_sat_mul(
     115         261 :             fd_ulong_sat_sub(
     116         261 :               epoch,
     117         261 :               schedule->first_normal_epoch),
     118         261 :             schedule->slots_per_epoch),
     119         261 :           schedule->first_normal_slot);
     120        9846 : }
     121             : 
     122             : /* https://github.com/solana-labs/solana/blob/88aeaa82a856fc807234e7da0b31b89f2dc0e091/sdk/program/src/epoch_schedule.rs#L140 */
     123             : 
     124             : ulong
     125             : fd_slot_to_epoch( fd_epoch_schedule_t const * schedule,
     126             :                   ulong                       slot,
     127     1348845 :                   ulong *                     out_offset_opt ) {
     128     1348845 :   ulong epoch;
     129     1348845 :   ulong offset;
     130             : 
     131     1348845 :   if( FD_UNLIKELY( slot < schedule->first_normal_slot ) ) {
     132             :     /* step0 = ceil(log2(FD_EPOCH_LEN_MIN))
     133             :        step1 = ceil(log2(FD_EPOCH_LEN_MIN + slot + 1))
     134             :        epoch = step1 - step0 - 1 */
     135             : 
     136       23727 :     ulong s0 = FD_EPOCH_LEN_MIN + slot + 1UL;
     137             :     /* Invariant: s0 > 1UL */
     138             :     /* Invariant: s0 > FD_EPOCH_LEN_MIN */
     139             : 
     140             :     /* Find lowest exp where (2^exp) >= s0
     141             :        (Only valid for s0 > 1UL and FD_EPOCH_LEN_MIN > 1UL) */
     142       23727 :     int   exp       = fd_ulong_find_msb( s0-1UL ) + 1;
     143       23727 :     int   min_exp   = fd_ulong_find_msb( FD_EPOCH_LEN_MIN );
     144       23727 :           epoch     = (ulong)( exp - min_exp - 1 );
     145       23727 :     ulong epoch_len = 1UL<<( epoch + (ulong)fd_uint_find_lsb( FD_EPOCH_LEN_MIN ) );
     146       23727 :           offset    = slot - ( epoch_len - FD_EPOCH_LEN_MIN );
     147     1325118 :   } else {
     148     1325118 :     if( FD_UNLIKELY( schedule->slots_per_epoch == 0UL ) ) {
     149          12 :       FD_LOG_WARNING(( "zero slots_per_epoch returning first_normal_epoch %lu", schedule->first_normal_epoch ));
     150          12 :       if( out_offset_opt ) *out_offset_opt = 0UL;
     151          12 :       return schedule->first_normal_epoch;
     152          12 :     }
     153     1325106 :     ulong n_slot  = slot - schedule->first_normal_slot;
     154     1325106 :     ulong n_epoch = n_slot / schedule->slots_per_epoch;
     155     1325106 :           epoch   = schedule->first_normal_epoch + n_epoch;
     156     1325106 :           offset  = n_slot % schedule->slots_per_epoch;
     157     1325106 :   }
     158             : 
     159     1348833 :   ulong   dummy_out;
     160     1348833 :   ulong * out_offset = out_offset_opt ? out_offset_opt : &dummy_out;
     161             : 
     162     1348833 :   *out_offset = offset;
     163     1348833 :   return epoch;
     164     1348845 : }
     165             : 
     166             : /* https://docs.rs/solana-epoch-schedule/2.2.1/src/solana_epoch_schedule/lib.rs.html#136-151 */
     167             : 
     168             : ulong
     169             : fd_slot_to_leader_schedule_epoch( fd_epoch_schedule_t const * schedule,
     170        3552 :                                   ulong                       slot ) {
     171             : 
     172        3552 :   if( FD_UNLIKELY( slot<schedule->first_normal_slot ) ) {
     173           3 :     return fd_slot_to_epoch( schedule, slot, NULL ) + 1UL;
     174           3 :   }
     175             : 
     176        3549 :   if( FD_UNLIKELY( schedule->slots_per_epoch == 0UL ) ) {
     177          12 :     FD_LOG_WARNING(( "zero slots_per_epoch returning first_normal_epoch %lu", schedule->first_normal_epoch ));
     178          12 :     return schedule->first_normal_epoch;
     179          12 :   }
     180             : 
     181        3537 :   ulong new_slots_since_first_normal_slot =
     182        3537 :       slot - schedule->first_normal_slot;
     183        3537 :   ulong new_first_normal_leader_schedule_slot =
     184        3537 :       new_slots_since_first_normal_slot + schedule->leader_schedule_slot_offset;
     185        3537 :   ulong new_epochs_since_first_normal_leader_schedule =
     186        3537 :       new_first_normal_leader_schedule_slot / schedule->slots_per_epoch;
     187             : 
     188        3537 :   return schedule->first_normal_epoch + new_epochs_since_first_normal_leader_schedule;
     189        3549 : }

Generated by: LCOV version 1.14