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 : }