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_bank_t * bank,
37 : fd_funk_t * funk,
38 : fd_funk_txn_xid_t const * xid,
39 : fd_capture_ctx_t * capture_ctx,
40 0 : fd_epoch_schedule_t const * epoch_schedule ) {
41 0 : ulong sz = fd_epoch_schedule_size( epoch_schedule );
42 : /* TODO remove alloca */
43 0 : uchar enc[ sz ];
44 0 : memset( enc, 0, sz );
45 0 : fd_bincode_encode_ctx_t ctx = {
46 0 : .data = enc,
47 0 : .dataend = enc + sz
48 0 : };
49 0 : if( fd_epoch_schedule_encode( epoch_schedule, &ctx ) ) {
50 0 : FD_LOG_ERR(("fd_epoch_schedule_encode failed"));
51 0 : }
52 :
53 0 : fd_sysvar_account_update( bank, funk, xid, capture_ctx, &fd_sysvar_epoch_schedule_id, enc, sz );
54 0 : }
55 :
56 : fd_epoch_schedule_t *
57 : fd_sysvar_epoch_schedule_read( fd_funk_t * funk,
58 : fd_funk_txn_xid_t const * xid,
59 0 : fd_epoch_schedule_t * out ) {
60 :
61 0 : FD_TXN_ACCOUNT_DECL( acc );
62 0 : int err = fd_txn_account_init_from_funk_readonly( acc, &fd_sysvar_epoch_schedule_id, funk, xid );
63 0 : if( FD_UNLIKELY( err != FD_ACC_MGR_SUCCESS ) ) {
64 0 : return NULL;
65 0 : }
66 :
67 : /* This check is needed as a quirk of the fuzzer. If a sysvar account
68 : exists in the accounts database, but doesn't have any lamports,
69 : this means that the account does not exist. This wouldn't happen
70 : in a real execution environment. */
71 0 : if( FD_UNLIKELY( fd_txn_account_get_lamports( acc )==0UL ) ) {
72 0 : return NULL;
73 0 : }
74 :
75 0 : return fd_bincode_decode_static(
76 0 : epoch_schedule, out,
77 0 : fd_txn_account_get_data( acc ),
78 0 : fd_txn_account_get_data_len( acc ),
79 0 : &err );
80 0 : }
81 :
82 : void
83 : fd_sysvar_epoch_schedule_init( fd_bank_t * bank,
84 : fd_funk_t * funk,
85 : fd_funk_txn_xid_t const * xid,
86 0 : fd_capture_ctx_t * capture_ctx ) {
87 0 : fd_epoch_schedule_t const * epoch_schedule = fd_bank_epoch_schedule_query( bank );
88 0 : fd_sysvar_epoch_schedule_write( bank, funk, xid, capture_ctx, epoch_schedule );
89 0 : }
90 :
91 : /* https://github.com/solana-labs/solana/blob/88aeaa82a856fc807234e7da0b31b89f2dc0e091/sdk/program/src/epoch_schedule.rs#L105 */
92 :
93 : ulong
94 : fd_epoch_slot_cnt( fd_epoch_schedule_t const * schedule,
95 66 : ulong epoch ) {
96 :
97 66 : if( FD_UNLIKELY( epoch < schedule->first_normal_epoch ) ) {
98 63 : ulong exp = fd_ulong_sat_add( epoch, (ulong)fd_ulong_find_lsb( FD_EPOCH_LEN_MIN ) );
99 63 : return ( exp<64UL ? 1UL<<exp : ULONG_MAX ); // saturating_pow
100 63 : }
101 :
102 3 : return schedule->slots_per_epoch;
103 66 : }
104 :
105 : /* https://github.com/solana-labs/solana/blob/88aeaa82a856fc807234e7da0b31b89f2dc0e091/sdk/program/src/epoch_schedule.rs#L170 */
106 :
107 : ulong
108 : fd_epoch_slot0( fd_epoch_schedule_t const * schedule,
109 201 : ulong epoch ) {
110 201 : if( FD_UNLIKELY( epoch <= schedule->first_normal_epoch ) ) {
111 198 : ulong power = fd_ulong_if( epoch<64UL, 1UL<<epoch, ULONG_MAX );
112 198 : return fd_ulong_sat_mul( power-1UL, FD_EPOCH_LEN_MIN );
113 198 : }
114 :
115 3 : return fd_ulong_sat_add(
116 3 : fd_ulong_sat_mul(
117 3 : fd_ulong_sat_sub(
118 3 : epoch,
119 3 : schedule->first_normal_epoch),
120 3 : schedule->slots_per_epoch),
121 3 : schedule->first_normal_slot);
122 201 : }
123 :
124 : /* https://github.com/solana-labs/solana/blob/88aeaa82a856fc807234e7da0b31b89f2dc0e091/sdk/program/src/epoch_schedule.rs#L140 */
125 :
126 : ulong
127 : fd_slot_to_epoch( fd_epoch_schedule_t const * schedule,
128 : ulong slot,
129 1319745 : ulong * out_offset_opt ) {
130 :
131 1319745 : if( FD_UNLIKELY( schedule->slots_per_epoch == 0UL ) ) {
132 3 : FD_LOG_WARNING(( "zero slots_per_epoch" ));
133 3 : return 0UL;
134 3 : }
135 :
136 1319742 : ulong epoch;
137 1319742 : ulong offset;
138 :
139 1319742 : if( FD_UNLIKELY( slot < schedule->first_normal_slot ) ) {
140 : /* step0 = ceil(log2(FD_EPOCH_LEN_MIN))
141 : step1 = ceil(log2(FD_EPOCH_LEN_MIN + slot + 1))
142 : epoch = step1 - step0 - 1 */
143 :
144 23715 : ulong s0 = FD_EPOCH_LEN_MIN + slot + 1UL;
145 : /* Invariant: s0 > 1UL */
146 : /* Invariant: s0 > FD_EPOCH_LEN_MIN */
147 :
148 : /* Find lowest exp where (2^exp) >= s0
149 : (Only valid for s0 > 1UL and FD_EPOCH_LEN_MIN > 1UL) */
150 23715 : int exp = fd_ulong_find_msb( s0-1UL ) + 1;
151 23715 : int min_exp = fd_ulong_find_msb( FD_EPOCH_LEN_MIN );
152 23715 : epoch = (ulong)( exp - min_exp - 1 );
153 23715 : ulong epoch_len = 1UL<<( epoch + (ulong)fd_uint_find_lsb( FD_EPOCH_LEN_MIN ) );
154 23715 : offset = slot - ( epoch_len - FD_EPOCH_LEN_MIN );
155 1296027 : } else {
156 : // FD_LOG_WARNING(("First %lu slots per epoch %lu", schedule->first_normal_slot, schedule->slots_per_epoch));
157 1296027 : ulong n_slot = slot - schedule->first_normal_slot;
158 1296027 : ulong n_epoch = n_slot / schedule->slots_per_epoch;
159 1296027 : epoch = schedule->first_normal_epoch + n_epoch;
160 1296027 : offset = n_slot % schedule->slots_per_epoch;
161 1296027 : }
162 :
163 1319742 : ulong dummy_out;
164 1319742 : ulong * out_offset = out_offset_opt ? out_offset_opt : &dummy_out;
165 :
166 1319742 : *out_offset = offset;
167 1319742 : return epoch;
168 1319745 : }
169 :
170 : /* https://docs.rs/solana-epoch-schedule/2.2.1/src/solana_epoch_schedule/lib.rs.html#136-151 */
171 :
172 : ulong
173 : fd_slot_to_leader_schedule_epoch( fd_epoch_schedule_t const * schedule,
174 12 : ulong slot ) {
175 :
176 12 : if( FD_UNLIKELY( slot<schedule->first_normal_slot ) ) {
177 3 : return fd_slot_to_epoch( schedule, slot, NULL ) + 1UL;
178 3 : }
179 :
180 9 : ulong new_slots_since_first_normal_slot =
181 9 : slot - schedule->first_normal_slot;
182 9 : ulong new_first_normal_leader_schedule_slot =
183 9 : new_slots_since_first_normal_slot + schedule->leader_schedule_slot_offset;
184 9 : ulong new_epochs_since_first_normal_leader_schedule =
185 9 : new_first_normal_leader_schedule_slot / schedule->slots_per_epoch;
186 :
187 9 : return schedule->first_normal_epoch + new_epochs_since_first_normal_leader_schedule;
188 12 : }
|