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