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