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: 49 107 45.8 %
Date: 2025-07-01 05:00:49 Functions: 4 8 50.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 "../context/fd_exec_slot_ctx.h"
       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       12102 :                           int                   warmup ) {
      10             : 
      11       12102 :   if( FD_UNLIKELY( epoch_len < FD_EPOCH_LEN_MIN ) ) {
      12           0 :     FD_LOG_WARNING(( "epoch_len too small" ));
      13           0 :     return NULL;
      14           0 :   }
      15             : 
      16       12102 :   *schedule = (fd_epoch_schedule_t) {
      17       12102 :     .slots_per_epoch             = epoch_len,
      18       12102 :     .leader_schedule_slot_offset = leader_schedule_slot_offset,
      19       12102 :     .warmup                      = !!warmup
      20       12102 :   };
      21             : 
      22       12102 :   if( warmup ) {
      23        6051 :     ulong ceil_log2_epoch   = (ulong)fd_ulong_find_msb( epoch_len-1UL ) + 1UL;
      24        6051 :     ulong ceil_log2_len_min = (ulong)fd_ulong_find_msb( FD_EPOCH_LEN_MIN );
      25             : 
      26        6051 :     schedule->first_normal_epoch = fd_ulong_sat_sub( ceil_log2_epoch, ceil_log2_len_min );
      27        6051 :     schedule->first_normal_slot  = (1UL << ceil_log2_epoch) - FD_EPOCH_LEN_MIN;
      28        6051 :   }
      29             : 
      30       12102 :   return schedule;
      31       12102 : }
      32             : 
      33             : void
      34             : fd_sysvar_epoch_schedule_write( fd_exec_slot_ctx_t *        slot_ctx,
      35           0 :                                 fd_epoch_schedule_t const * epoch_schedule ) {
      36           0 :   ulong sz = fd_epoch_schedule_size( epoch_schedule );
      37           0 :   FD_LOG_INFO(("Writing epoch schedule size %lu", sz));
      38             :   /* TODO remove alloca */
      39           0 :   uchar enc[ sz ];
      40           0 :   memset( enc, 0, sz );
      41           0 :   fd_bincode_encode_ctx_t ctx = {
      42           0 :     .data    = enc,
      43           0 :     .dataend = enc + sz
      44           0 :   };
      45           0 :   if( fd_epoch_schedule_encode( epoch_schedule, &ctx ) ) {
      46           0 :     FD_LOG_ERR(("fd_epoch_schedule_encode failed"));
      47           0 :   }
      48             : 
      49           0 :   fd_sysvar_set( slot_ctx->bank, slot_ctx->funk, slot_ctx->funk_txn, &fd_sysvar_owner_id, &fd_sysvar_epoch_schedule_id, enc, sz, slot_ctx->slot );
      50           0 : }
      51             : 
      52             : fd_epoch_schedule_t *
      53             : fd_sysvar_epoch_schedule_read( fd_funk_t *     funk,
      54             :                                fd_funk_txn_t * funk_txn,
      55           0 :                                fd_spad_t *     spad ) {
      56             : 
      57           0 :   FD_TXN_ACCOUNT_DECL( acc );
      58           0 :   int err = fd_txn_account_init_from_funk_readonly( acc, &fd_sysvar_epoch_schedule_id, funk, funk_txn );
      59           0 :   if( FD_UNLIKELY( err != FD_ACC_MGR_SUCCESS ) ) {
      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( acc->vt->get_lamports( acc ) == 0UL ) ) {
      68           0 :     return NULL;
      69           0 :   }
      70             : 
      71           0 :   return fd_bincode_decode_spad(
      72           0 :       epoch_schedule, spad,
      73           0 :       acc->vt->get_data( acc ),
      74           0 :       acc->vt->get_data_len( acc ),
      75           0 :       &err );
      76           0 : }
      77             : 
      78             : void
      79           0 : fd_sysvar_epoch_schedule_init( fd_exec_slot_ctx_t * slot_ctx ) {
      80           0 :   fd_epoch_schedule_t const * epoch_schedule = fd_bank_epoch_schedule_query( slot_ctx->bank );
      81           0 :   fd_sysvar_epoch_schedule_write( slot_ctx, epoch_schedule );
      82           0 : }
      83             : 
      84             : /* https://github.com/solana-labs/solana/blob/88aeaa82a856fc807234e7da0b31b89f2dc0e091/sdk/program/src/epoch_schedule.rs#L105 */
      85             : 
      86             : ulong
      87             : fd_epoch_slot_cnt( fd_epoch_schedule_t const * schedule,
      88          63 :                    ulong                       epoch ) {
      89             : 
      90          63 :   if( FD_UNLIKELY( epoch < schedule->first_normal_epoch ) ) {
      91          63 :     ulong exp = fd_ulong_sat_add( epoch, (ulong)fd_ulong_find_lsb( FD_EPOCH_LEN_MIN ) );
      92          63 :     return ( exp<64UL ? 1UL<<exp : ULONG_MAX ); // saturating_pow
      93          63 :   }
      94             : 
      95           0 :   return schedule->slots_per_epoch;
      96          63 : }
      97             : 
      98             : /* https://github.com/solana-labs/solana/blob/88aeaa82a856fc807234e7da0b31b89f2dc0e091/sdk/program/src/epoch_schedule.rs#L170 */
      99             : 
     100             : ulong
     101             : fd_epoch_slot0( fd_epoch_schedule_t const * schedule,
     102         189 :                 ulong                       epoch ) {
     103         189 :   if( FD_UNLIKELY( epoch <= schedule->first_normal_epoch ) ) {
     104         189 :     ulong power = fd_ulong_if( epoch<64UL, 1UL<<epoch, ULONG_MAX );
     105         189 :     return fd_ulong_sat_mul( power-1UL, FD_EPOCH_LEN_MIN );
     106         189 :   }
     107             : 
     108           0 :   return fd_ulong_sat_add(
     109           0 :           fd_ulong_sat_mul(
     110           0 :             fd_ulong_sat_sub(
     111           0 :               epoch,
     112           0 :               schedule->first_normal_epoch),
     113           0 :             schedule->slots_per_epoch),
     114           0 :           schedule->first_normal_slot);
     115         189 : }
     116             : 
     117             : /* https://github.com/solana-labs/solana/blob/88aeaa82a856fc807234e7da0b31b89f2dc0e091/sdk/program/src/epoch_schedule.rs#L140 */
     118             : 
     119             : ulong
     120             : fd_slot_to_epoch( fd_epoch_schedule_t const * schedule,
     121             :                   ulong                       slot,
     122       23724 :                   ulong *                     out_offset_opt ) {
     123             : 
     124       23724 :   if( FD_UNLIKELY( schedule->slots_per_epoch == 0UL ) ) {
     125           0 :     FD_LOG_WARNING(( "zero slots_per_epoch" ));
     126           0 :     return 0UL;
     127           0 :   }
     128             : 
     129       23724 :   ulong epoch;
     130       23724 :   ulong offset;
     131             : 
     132       23724 :   if( FD_UNLIKELY( slot < schedule->first_normal_slot ) ) {
     133             :     /* step0 = ceil(log2(FD_EPOCH_LEN_MIN))
     134             :        step1 = ceil(log2(FD_EPOCH_LEN_MIN + slot + 1))
     135             :        epoch = step1 - step0 - 1 */
     136             : 
     137       23712 :     ulong s0 = FD_EPOCH_LEN_MIN + slot + 1UL;
     138             :     /* Invariant: s0 > 1UL */
     139             :     /* Invariant: s0 > FD_EPOCH_LEN_MIN */
     140             : 
     141             :     /* Find lowest exp where (2^exp) >= s0
     142             :        (Only valid for s0 > 1UL and FD_EPOCH_LEN_MIN > 1UL) */
     143       23712 :     int   exp       = fd_ulong_find_msb( s0-1UL ) + 1;
     144       23712 :     int   min_exp   = fd_ulong_find_msb( FD_EPOCH_LEN_MIN );
     145       23712 :           epoch     = (ulong)( exp - min_exp - 1 );
     146       23712 :     ulong epoch_len = 1UL<<( epoch + (ulong)fd_uint_find_lsb( FD_EPOCH_LEN_MIN ) );
     147       23712 :           offset    = slot - ( epoch_len - FD_EPOCH_LEN_MIN );
     148       23712 :   } else {
     149             :     // FD_LOG_WARNING(("First %lu slots per epoch %lu", schedule->first_normal_slot, schedule->slots_per_epoch));
     150          12 :     ulong n_slot  = slot - schedule->first_normal_slot;
     151          12 :     ulong n_epoch = n_slot / schedule->slots_per_epoch;
     152          12 :           epoch   = schedule->first_normal_epoch + n_epoch;
     153          12 :           offset  = n_slot % schedule->slots_per_epoch;
     154          12 :   }
     155             : 
     156       23724 :   ulong   dummy_out;
     157       23724 :   ulong * out_offset = out_offset_opt ? out_offset_opt : &dummy_out;
     158             : 
     159       23724 :   *out_offset = offset;
     160       23724 :   return epoch;
     161       23724 : }
     162             : 
     163             : /* https://github.com/firedancer-io/solana/blob/dab3da8e7b667d7527565bddbdbecf7ec1fb868e/sdk/program/src/epoch_schedule.rs#L114 */
     164             : 
     165             : ulong
     166             : fd_slot_to_leader_schedule_epoch( fd_epoch_schedule_t const * schedule,
     167           0 :                                   ulong                       slot ) {
     168             : 
     169           0 :   if( slot < schedule->first_normal_slot )
     170           0 :     return fd_slot_to_epoch( schedule, slot, NULL ) + 1UL;
     171             : 
     172             :   /* These variable names ... sigh */
     173             : 
     174           0 :   ulong new_slots_since_first_normal_slot =
     175           0 :     slot - schedule->first_normal_slot;
     176           0 :   ulong new_first_normal_leader_schedule_slot =
     177           0 :     new_slots_since_first_normal_slot + schedule->leader_schedule_slot_offset;
     178           0 :   ulong new_epochs_since_first_normal_leader_schedule =
     179           0 :     new_first_normal_leader_schedule_slot / schedule->slots_per_epoch;
     180             : 
     181           0 :   return schedule->first_normal_epoch + new_epochs_since_first_normal_leader_schedule;
     182           0 : }

Generated by: LCOV version 1.14