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 0 : int warmup ) { 10 : 11 0 : 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 0 : *schedule = (fd_epoch_schedule_t) { 17 0 : .slots_per_epoch = epoch_len, 18 0 : .leader_schedule_slot_offset = leader_schedule_slot_offset, 19 0 : .warmup = !!warmup 20 0 : }; 21 : 22 0 : if( warmup ) { 23 0 : ulong ceil_log2_epoch = (ulong)fd_ulong_find_msb( epoch_len-1UL ) + 1UL; 24 : 25 0 : if( FD_UNLIKELY( ceil_log2_epoch>=64UL ) ) { 26 0 : FD_LOG_WARNING(( "epoch_len too large (ceil_log2_epoch %lu)", ceil_log2_epoch )); 27 0 : return NULL; 28 0 : } 29 : 30 0 : ulong ceil_log2_len_min = (ulong)fd_ulong_find_msb( FD_EPOCH_LEN_MIN ); 31 : 32 0 : schedule->first_normal_epoch = fd_ulong_sat_sub( ceil_log2_epoch, ceil_log2_len_min ); 33 0 : schedule->first_normal_slot = (1UL << ceil_log2_epoch) - FD_EPOCH_LEN_MIN; 34 0 : } 35 : 36 0 : return schedule; 37 0 : } 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 0 : fd_epoch_schedule_t const * epoch_schedule ) { 44 0 : fd_sysvar_account_update( bank, accdb, capture_ctx, &fd_sysvar_epoch_schedule_id, epoch_schedule, sizeof(fd_epoch_schedule_t) ); 45 0 : } 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 1478 : ulong epoch ) { 60 1478 : if( FD_UNLIKELY( epoch < schedule->first_normal_epoch ) ) { 61 36 : ulong exp = fd_ulong_sat_add( epoch, (ulong)fd_ulong_find_lsb( FD_EPOCH_LEN_MIN ) ); 62 36 : return ( exp<64UL ? 1UL<<exp : ULONG_MAX ); // saturating_pow 63 36 : } 64 : 65 1442 : return schedule->slots_per_epoch; 66 1478 : } 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 2543 : ulong epoch ) { 73 2543 : if( FD_UNLIKELY( epoch <= schedule->first_normal_epoch ) ) { 74 464 : ulong power = epoch<64UL ? 1UL<<epoch : ULONG_MAX; 75 464 : return fd_ulong_sat_mul( power-1UL, FD_EPOCH_LEN_MIN ); 76 464 : } 77 : 78 2079 : return fd_ulong_sat_add( 79 2079 : fd_ulong_sat_mul( 80 2079 : fd_ulong_sat_sub( 81 2079 : epoch, 82 2079 : schedule->first_normal_epoch), 83 2079 : schedule->slots_per_epoch), 84 2079 : schedule->first_normal_slot); 85 2543 : } 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 14820 : ulong * out_offset_opt ) { 93 14820 : ulong epoch; 94 14820 : ulong offset; 95 : 96 14820 : 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 4191 : 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 4191 : int exp = fd_ulong_find_msb( s0-1UL ) + 1; 108 4191 : int min_exp = fd_ulong_find_msb( FD_EPOCH_LEN_MIN ); 109 4191 : epoch = (ulong)( exp - min_exp - 1 ); 110 4191 : ulong epoch_len = 1UL<<( epoch + (ulong)fd_uint_find_lsb( FD_EPOCH_LEN_MIN ) ); 111 4191 : offset = slot - ( epoch_len - FD_EPOCH_LEN_MIN ); 112 10629 : } else { 113 10629 : if( FD_UNLIKELY( schedule->slots_per_epoch == 0UL ) ) { 114 0 : FD_LOG_WARNING(( "zero slots_per_epoch returning first_normal_epoch %lu", schedule->first_normal_epoch )); 115 0 : if( out_offset_opt ) *out_offset_opt = 0UL; 116 0 : return schedule->first_normal_epoch; 117 0 : } 118 10629 : ulong n_slot = slot - schedule->first_normal_slot; 119 10629 : ulong n_epoch = n_slot / schedule->slots_per_epoch; 120 10629 : epoch = schedule->first_normal_epoch + n_epoch; 121 10629 : offset = n_slot % schedule->slots_per_epoch; 122 10629 : } 123 : 124 14820 : ulong dummy_out; 125 14820 : ulong * out_offset = out_offset_opt ? out_offset_opt : &dummy_out; 126 : 127 14820 : *out_offset = offset; 128 14820 : return epoch; 129 14820 : } 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 615 : ulong slot ) { 136 : 137 615 : if( FD_UNLIKELY( slot<schedule->first_normal_slot ) ) { 138 12 : return fd_slot_to_epoch( schedule, slot, NULL ) + 1UL; 139 12 : } 140 : 141 603 : if( FD_UNLIKELY( schedule->slots_per_epoch == 0UL ) ) { 142 0 : FD_LOG_WARNING(( "zero slots_per_epoch returning first_normal_epoch %lu", schedule->first_normal_epoch )); 143 0 : return schedule->first_normal_epoch; 144 0 : } 145 : 146 603 : ulong new_slots_since_first_normal_slot = 147 603 : slot - schedule->first_normal_slot; 148 603 : ulong new_first_normal_leader_schedule_slot = 149 603 : new_slots_since_first_normal_slot + schedule->leader_schedule_slot_offset; 150 603 : ulong new_epochs_since_first_normal_leader_schedule = 151 603 : new_first_normal_leader_schedule_slot / schedule->slots_per_epoch; 152 : 153 603 : return schedule->first_normal_epoch + new_epochs_since_first_normal_leader_schedule; 154 603 : }