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_epoch_ctx.h" 5 : #include "../context/fd_exec_slot_ctx.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 12102 : int warmup ) { 12 : 13 12102 : if( FD_UNLIKELY( epoch_len < FD_EPOCH_LEN_MIN ) ) { 14 0 : FD_LOG_WARNING(( "epoch_len too small" )); 15 0 : return NULL; 16 0 : } 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 12102 : } 34 : 35 : static void 36 : write_epoch_schedule( fd_exec_slot_ctx_t * slot_ctx, 37 0 : fd_epoch_schedule_t * epoch_schedule ) { 38 0 : ulong sz = fd_epoch_schedule_size( epoch_schedule ); 39 0 : FD_LOG_INFO(("Writing epoch schedule size %lu", sz)); 40 : /* TODO remove alloca */ 41 0 : uchar enc[ sz ]; 42 0 : memset( enc, 0, sz ); 43 0 : fd_bincode_encode_ctx_t ctx; 44 0 : ctx.data = enc; 45 0 : ctx.dataend = enc + sz; 46 0 : if ( fd_epoch_schedule_encode( epoch_schedule, &ctx ) ) 47 0 : FD_LOG_ERR(("fd_epoch_schedule_encode failed")); 48 : 49 0 : fd_sysvar_set( slot_ctx, fd_sysvar_owner_id.key, &fd_sysvar_epoch_schedule_id, enc, sz, slot_ctx->slot_bank.slot ); 50 0 : } 51 : 52 : fd_epoch_schedule_t * 53 : fd_sysvar_epoch_schedule_read( fd_epoch_schedule_t * result, 54 : fd_sysvar_cache_t const * sysvar_cache, 55 : fd_acc_mgr_t * acc_mgr, 56 0 : fd_funk_txn_t * funk_txn ) { 57 0 : fd_epoch_schedule_t const * ret = (fd_epoch_schedule_t const *)fd_sysvar_cache_epoch_schedule( sysvar_cache ); 58 0 : if( FD_UNLIKELY( NULL != ret ) ) { 59 0 : fd_memcpy(result, ret, sizeof(fd_epoch_schedule_t)); 60 0 : return result; 61 0 : } 62 : 63 0 : FD_TXN_ACCOUNT_DECL( acc ); 64 0 : int err = fd_acc_mgr_view( acc_mgr, funk_txn, &fd_sysvar_epoch_schedule_id, acc ); 65 0 : if( FD_UNLIKELY( err != FD_ACC_MGR_SUCCESS ) ) 66 0 : return NULL; 67 : 68 0 : fd_bincode_decode_ctx_t decode = { 69 0 : .data = acc->const_data, 70 0 : .dataend = acc->const_data + acc->const_meta->dlen 71 0 : }; 72 : 73 0 : ulong total_sz = 0UL; 74 0 : err = fd_epoch_schedule_decode_footprint( &decode, &total_sz ); 75 0 : if( FD_UNLIKELY( err ) ) { 76 0 : return NULL; 77 0 : } 78 : 79 : /* Assumes that result has been properly allocated and setup by the caller. */ 80 : 81 0 : fd_epoch_schedule_decode( result, &decode ); 82 : 83 0 : return result; 84 0 : } 85 : 86 : void 87 0 : fd_sysvar_epoch_schedule_init( fd_exec_slot_ctx_t * slot_ctx ) { 88 0 : fd_epoch_bank_t * epoch_bank = fd_exec_epoch_ctx_epoch_bank( slot_ctx->epoch_ctx ); 89 0 : write_epoch_schedule( slot_ctx, &epoch_bank->epoch_schedule ); 90 0 : } 91 : 92 : /* https://github.com/solana-labs/solana/blob/88aeaa82a856fc807234e7da0b31b89f2dc0e091/sdk/program/src/epoch_schedule.rs#L105 */ 93 : 94 : ulong 95 : fd_epoch_slot_cnt( fd_epoch_schedule_t const * schedule, 96 63 : ulong epoch ) { 97 : 98 63 : if( FD_UNLIKELY( epoch < schedule->first_normal_epoch ) ) { 99 63 : ulong exp = fd_ulong_sat_add( epoch, (ulong)fd_ulong_find_lsb( FD_EPOCH_LEN_MIN ) ); 100 63 : return ( exp<64UL ? 1UL<<exp : ULONG_MAX ); // saturating_pow 101 63 : } 102 : 103 0 : return schedule->slots_per_epoch; 104 63 : } 105 : 106 : /* https://github.com/solana-labs/solana/blob/88aeaa82a856fc807234e7da0b31b89f2dc0e091/sdk/program/src/epoch_schedule.rs#L170 */ 107 : 108 : ulong 109 : fd_epoch_slot0( fd_epoch_schedule_t const * schedule, 110 126 : ulong epoch ) { 111 126 : if( FD_UNLIKELY( epoch <= schedule->first_normal_epoch ) ) { 112 126 : ulong power = fd_ulong_if( epoch<64UL, 1UL<<epoch, ULONG_MAX ); 113 126 : return fd_ulong_sat_mul( power-1UL, FD_EPOCH_LEN_MIN ); 114 126 : } 115 : 116 0 : return fd_ulong_sat_add( 117 0 : fd_ulong_sat_mul( 118 0 : fd_ulong_sat_sub( 119 0 : epoch, 120 0 : schedule->first_normal_epoch), 121 0 : schedule->slots_per_epoch), 122 0 : schedule->first_normal_slot); 123 126 : } 124 : 125 : /* https://github.com/solana-labs/solana/blob/88aeaa82a856fc807234e7da0b31b89f2dc0e091/sdk/program/src/epoch_schedule.rs#L140 */ 126 : 127 : ulong 128 : fd_slot_to_epoch( fd_epoch_schedule_t const * schedule, 129 : ulong slot, 130 23712 : ulong * out_offset_opt ) { 131 : 132 23712 : if( FD_UNLIKELY( schedule->slots_per_epoch == 0UL ) ) { 133 0 : FD_LOG_WARNING(( "zero slots_per_epoch" )); 134 0 : return 0UL; 135 0 : } 136 : 137 23712 : ulong epoch; 138 23712 : ulong offset; 139 : 140 23712 : if( FD_UNLIKELY( slot < schedule->first_normal_slot ) ) { 141 : /* step0 = ceil(log2(FD_EPOCH_LEN_MIN)) 142 : step1 = ceil(log2(FD_EPOCH_LEN_MIN + slot + 1)) 143 : epoch = step1 - step0 - 1 */ 144 : 145 23712 : ulong s0 = FD_EPOCH_LEN_MIN + slot + 1UL; 146 : /* Invariant: s0 > 1UL */ 147 : /* Invariant: s0 > FD_EPOCH_LEN_MIN */ 148 : 149 : /* Find lowest exp where (2^exp) >= s0 150 : (Only valid for s0 > 1UL and FD_EPOCH_LEN_MIN > 1UL) */ 151 23712 : int exp = fd_ulong_find_msb( s0-1UL ) + 1; 152 23712 : int min_exp = fd_ulong_find_msb( FD_EPOCH_LEN_MIN ); 153 23712 : epoch = (ulong)( exp - min_exp - 1 ); 154 23712 : ulong epoch_len = 1UL<<( epoch + (ulong)fd_uint_find_lsb( FD_EPOCH_LEN_MIN ) ); 155 23712 : offset = slot - ( epoch_len - FD_EPOCH_LEN_MIN ); 156 23712 : } else { 157 : // FD_LOG_WARNING(("First %lu slots per epoch %lu", schedule->first_normal_slot, schedule->slots_per_epoch)); 158 0 : ulong n_slot = slot - schedule->first_normal_slot; 159 0 : ulong n_epoch = n_slot / schedule->slots_per_epoch; 160 0 : epoch = schedule->first_normal_epoch + n_epoch; 161 0 : offset = n_slot % schedule->slots_per_epoch; 162 0 : } 163 : 164 23712 : ulong dummy_out; 165 23712 : ulong * out_offset = out_offset_opt ? out_offset_opt : &dummy_out; 166 : 167 23712 : *out_offset = offset; 168 23712 : return epoch; 169 23712 : } 170 : 171 : /* https://github.com/firedancer-io/solana/blob/dab3da8e7b667d7527565bddbdbecf7ec1fb868e/sdk/program/src/epoch_schedule.rs#L114 */ 172 : 173 : ulong 174 : fd_slot_to_leader_schedule_epoch( fd_epoch_schedule_t const * schedule, 175 0 : ulong slot ) { 176 : 177 0 : if( slot < schedule->first_normal_slot ) 178 0 : return fd_slot_to_epoch( schedule, slot, NULL ) + 1UL; 179 : 180 : /* These variable names ... sigh */ 181 : 182 0 : ulong new_slots_since_first_normal_slot = 183 0 : slot - schedule->first_normal_slot; 184 0 : ulong new_first_normal_leader_schedule_slot = 185 0 : new_slots_since_first_normal_slot + schedule->leader_schedule_slot_offset; 186 0 : ulong new_epochs_since_first_normal_leader_schedule = 187 0 : new_first_normal_leader_schedule_slot / schedule->slots_per_epoch; 188 : 189 0 : return schedule->first_normal_epoch + new_epochs_since_first_normal_leader_schedule; 190 0 : }