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: 84 107 78.5 %
Date: 2026-02-22 05:44:04 Functions: 6 8 75.0 %

          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             : fd_epoch_schedule_t *
       7             : fd_epoch_schedule_derive( fd_epoch_schedule_t * schedule,
       8             :                           ulong                 epoch_len,
       9             :                           ulong                 leader_schedule_slot_offset,
      10       12105 :                           int                   warmup ) {
      11             : 
      12       12105 :   if( FD_UNLIKELY( epoch_len < FD_EPOCH_LEN_MIN ) ) {
      13           3 :     FD_LOG_WARNING(( "epoch_len too small" ));
      14           3 :     return NULL;
      15           3 :   }
      16             : 
      17       12102 :   *schedule = (fd_epoch_schedule_t) {
      18       12102 :     .slots_per_epoch             = epoch_len,
      19       12102 :     .leader_schedule_slot_offset = leader_schedule_slot_offset,
      20       12102 :     .warmup                      = !!warmup
      21       12102 :   };
      22             : 
      23       12102 :   if( warmup ) {
      24        6051 :     ulong ceil_log2_epoch   = (ulong)fd_ulong_find_msb( epoch_len-1UL ) + 1UL;
      25        6051 :     ulong ceil_log2_len_min = (ulong)fd_ulong_find_msb( FD_EPOCH_LEN_MIN );
      26             : 
      27        6051 :     schedule->first_normal_epoch = fd_ulong_sat_sub( ceil_log2_epoch, ceil_log2_len_min );
      28        6051 :     schedule->first_normal_slot  = (1UL << ceil_log2_epoch) - FD_EPOCH_LEN_MIN;
      29        6051 :   }
      30             : 
      31       12102 :   return schedule;
      32       12105 : }
      33             : 
      34             : void
      35             : fd_sysvar_epoch_schedule_write( fd_bank_t *                 bank,
      36             :                                 fd_accdb_user_t *           accdb,
      37             :                                 fd_funk_txn_xid_t const *   xid,
      38             :                                 fd_capture_ctx_t *          capture_ctx,
      39          51 :                                 fd_epoch_schedule_t const * epoch_schedule ) {
      40             : 
      41          51 :   uchar enc[ FD_SYSVAR_EPOCH_SCHEDULE_BINCODE_SZ ] = {0};
      42             : 
      43          51 :   fd_bincode_encode_ctx_t ctx = {
      44          51 :     .data    = enc,
      45          51 :     .dataend = enc + FD_SYSVAR_EPOCH_SCHEDULE_BINCODE_SZ
      46          51 :   };
      47          51 :   if( FD_UNLIKELY( fd_epoch_schedule_encode( epoch_schedule, &ctx ) ) ) {
      48           0 :     FD_LOG_ERR(( "fd_epoch_schedule_encode failed" ));
      49           0 :   }
      50             : 
      51          51 :   fd_sysvar_account_update( bank, accdb, xid, capture_ctx, &fd_sysvar_epoch_schedule_id, enc, FD_SYSVAR_EPOCH_SCHEDULE_BINCODE_SZ );
      52          51 : }
      53             : 
      54             : fd_epoch_schedule_t *
      55             : fd_sysvar_epoch_schedule_read( fd_accdb_user_t *         accdb,
      56             :                                fd_funk_txn_xid_t const * xid,
      57           0 :                                fd_epoch_schedule_t *     out ) {
      58           0 :   fd_accdb_ro_t ro[1];
      59           0 :   if( FD_UNLIKELY( !fd_accdb_open_ro( accdb, ro, xid, &fd_sysvar_epoch_schedule_id ) ) ) {
      60           0 :     return NULL;
      61           0 :   }
      62             : 
      63             :   /* This check is needed as a quirk of the fuzzer. If a sysvar account
      64             :      exists in the accounts database, but doesn't have any lamports,
      65             :      this means that the account does not exist. This wouldn't happen
      66             :      in a real execution environment. */
      67           0 :   if( FD_UNLIKELY( fd_accdb_ref_lamports( ro )==0UL ) ) {
      68           0 :     fd_accdb_close_ro( accdb, ro );
      69           0 :     return NULL;
      70           0 :   }
      71             : 
      72           0 :   fd_epoch_schedule_t * rc = fd_bincode_decode_static(
      73           0 :       epoch_schedule, out,
      74           0 :       fd_accdb_ref_data_const( ro ),
      75           0 :       fd_accdb_ref_data_sz   ( ro ),
      76           0 :       NULL );
      77           0 :   fd_accdb_close_ro( accdb, ro );
      78           0 :   return rc;
      79           0 : }
      80             : 
      81             : void
      82             : fd_sysvar_epoch_schedule_init( fd_bank_t *               bank,
      83             :                                fd_accdb_user_t *         accdb,
      84             :                                fd_funk_txn_xid_t const * xid,
      85           0 :                                fd_capture_ctx_t *        capture_ctx ) {
      86           0 :   fd_epoch_schedule_t const * epoch_schedule = fd_bank_epoch_schedule_query( bank );
      87           0 :   fd_sysvar_epoch_schedule_write( bank, accdb, xid, capture_ctx, epoch_schedule );
      88           0 : }
      89             : 
      90             : /* https://github.com/solana-labs/solana/blob/88aeaa82a856fc807234e7da0b31b89f2dc0e091/sdk/program/src/epoch_schedule.rs#L105 */
      91             : 
      92             : ulong
      93             : fd_epoch_slot_cnt( fd_epoch_schedule_t const * schedule,
      94         111 :                    ulong                       epoch ) {
      95             : 
      96         111 :   if( FD_UNLIKELY( epoch < schedule->first_normal_epoch ) ) {
      97          63 :     ulong exp = fd_ulong_sat_add( epoch, (ulong)fd_ulong_find_lsb( FD_EPOCH_LEN_MIN ) );
      98          63 :     return ( exp<64UL ? 1UL<<exp : ULONG_MAX ); // saturating_pow
      99          63 :   }
     100             : 
     101          48 :   return schedule->slots_per_epoch;
     102         111 : }
     103             : 
     104             : /* https://github.com/solana-labs/solana/blob/88aeaa82a856fc807234e7da0b31b89f2dc0e091/sdk/program/src/epoch_schedule.rs#L170 */
     105             : 
     106             : ulong
     107             : fd_epoch_slot0( fd_epoch_schedule_t const * schedule,
     108         342 :                 ulong                       epoch ) {
     109         342 :   if( FD_UNLIKELY( epoch <= schedule->first_normal_epoch ) ) {
     110         249 :     ulong power = fd_ulong_if( epoch<64UL, 1UL<<epoch, ULONG_MAX );
     111         249 :     return fd_ulong_sat_mul( power-1UL, FD_EPOCH_LEN_MIN );
     112         249 :   }
     113             : 
     114          93 :   return fd_ulong_sat_add(
     115          93 :           fd_ulong_sat_mul(
     116          93 :             fd_ulong_sat_sub(
     117          93 :               epoch,
     118          93 :               schedule->first_normal_epoch),
     119          93 :             schedule->slots_per_epoch),
     120          93 :           schedule->first_normal_slot);
     121         342 : }
     122             : 
     123             : /* https://github.com/solana-labs/solana/blob/88aeaa82a856fc807234e7da0b31b89f2dc0e091/sdk/program/src/epoch_schedule.rs#L140 */
     124             : 
     125             : ulong
     126             : fd_slot_to_epoch( fd_epoch_schedule_t const * schedule,
     127             :                   ulong                       slot,
     128     1320312 :                   ulong *                     out_offset_opt ) {
     129             : 
     130     1320312 :   if( FD_UNLIKELY( schedule->slots_per_epoch == 0UL ) ) {
     131           3 :     FD_LOG_WARNING(( "zero slots_per_epoch" ));
     132           3 :     return 0UL;
     133           3 :   }
     134             : 
     135     1320309 :   ulong epoch;
     136     1320309 :   ulong offset;
     137             : 
     138     1320309 :   if( FD_UNLIKELY( slot < schedule->first_normal_slot ) ) {
     139             :     /* step0 = ceil(log2(FD_EPOCH_LEN_MIN))
     140             :        step1 = ceil(log2(FD_EPOCH_LEN_MIN + slot + 1))
     141             :        epoch = step1 - step0 - 1 */
     142             : 
     143       23715 :     ulong s0 = FD_EPOCH_LEN_MIN + slot + 1UL;
     144             :     /* Invariant: s0 > 1UL */
     145             :     /* Invariant: s0 > FD_EPOCH_LEN_MIN */
     146             : 
     147             :     /* Find lowest exp where (2^exp) >= s0
     148             :        (Only valid for s0 > 1UL and FD_EPOCH_LEN_MIN > 1UL) */
     149       23715 :     int   exp       = fd_ulong_find_msb( s0-1UL ) + 1;
     150       23715 :     int   min_exp   = fd_ulong_find_msb( FD_EPOCH_LEN_MIN );
     151       23715 :           epoch     = (ulong)( exp - min_exp - 1 );
     152       23715 :     ulong epoch_len = 1UL<<( epoch + (ulong)fd_uint_find_lsb( FD_EPOCH_LEN_MIN ) );
     153       23715 :           offset    = slot - ( epoch_len - FD_EPOCH_LEN_MIN );
     154     1296594 :   } else {
     155             :     // FD_LOG_WARNING(("First %lu slots per epoch %lu", schedule->first_normal_slot, schedule->slots_per_epoch));
     156     1296594 :     ulong n_slot  = slot - schedule->first_normal_slot;
     157     1296594 :     ulong n_epoch = n_slot / schedule->slots_per_epoch;
     158     1296594 :           epoch   = schedule->first_normal_epoch + n_epoch;
     159     1296594 :           offset  = n_slot % schedule->slots_per_epoch;
     160     1296594 :   }
     161             : 
     162     1320309 :   ulong   dummy_out;
     163     1320309 :   ulong * out_offset = out_offset_opt ? out_offset_opt : &dummy_out;
     164             : 
     165     1320309 :   *out_offset = offset;
     166     1320309 :   return epoch;
     167     1320312 : }
     168             : 
     169             : /* https://docs.rs/solana-epoch-schedule/2.2.1/src/solana_epoch_schedule/lib.rs.html#136-151 */
     170             : 
     171             : ulong
     172             : fd_slot_to_leader_schedule_epoch( fd_epoch_schedule_t const * schedule,
     173          75 :                                   ulong                       slot ) {
     174             : 
     175          75 :   if( FD_UNLIKELY( slot<schedule->first_normal_slot ) ) {
     176           3 :     return fd_slot_to_epoch( schedule, slot, NULL ) + 1UL;
     177           3 :   }
     178             : 
     179          72 :   ulong new_slots_since_first_normal_slot =
     180          72 :       slot - schedule->first_normal_slot;
     181          72 :   ulong new_first_normal_leader_schedule_slot =
     182          72 :       new_slots_since_first_normal_slot + schedule->leader_schedule_slot_offset;
     183          72 :   ulong new_epochs_since_first_normal_leader_schedule =
     184          72 :       new_first_normal_leader_schedule_slot / schedule->slots_per_epoch;
     185             : 
     186          72 :   return schedule->first_normal_epoch + new_epochs_since_first_normal_leader_schedule;
     187          75 : }

Generated by: LCOV version 1.14