Line data Source code
1 : #include "fd_sysvar_epoch_schedule.h"
2 : #include "fd_sysvar.h"
3 : #include "../fd_system_ids.h"
4 : #include "../../accdb/fd_accdb_sync.h"
5 :
6 : static int
7 0 : validate( fd_epoch_schedule_t const * schedule ) {
8 0 : return schedule->warmup!=0 && schedule->warmup!=1;
9 0 : }
10 :
11 : fd_epoch_schedule_t *
12 : fd_epoch_schedule_derive( fd_epoch_schedule_t * schedule,
13 : ulong epoch_len,
14 : ulong leader_schedule_slot_offset,
15 12264 : int warmup ) {
16 :
17 12264 : if( FD_UNLIKELY( epoch_len < FD_EPOCH_LEN_MIN ) ) {
18 6 : FD_LOG_WARNING(( "epoch_len too small" ));
19 6 : return NULL;
20 6 : }
21 :
22 12258 : *schedule = (fd_epoch_schedule_t) {
23 12258 : .slots_per_epoch = epoch_len,
24 12258 : .leader_schedule_slot_offset = leader_schedule_slot_offset,
25 12258 : .warmup = !!warmup
26 12258 : };
27 :
28 12258 : if( warmup ) {
29 6084 : ulong ceil_log2_epoch = (ulong)fd_ulong_find_msb( epoch_len-1UL ) + 1UL;
30 :
31 6084 : if( FD_UNLIKELY( ceil_log2_epoch>=64UL ) ) {
32 3 : FD_LOG_WARNING(( "epoch_len too large (ceil_log2_epoch %lu)", ceil_log2_epoch ));
33 3 : return NULL;
34 3 : }
35 :
36 6081 : ulong ceil_log2_len_min = (ulong)fd_ulong_find_msb( FD_EPOCH_LEN_MIN );
37 :
38 6081 : schedule->first_normal_epoch = fd_ulong_sat_sub( ceil_log2_epoch, ceil_log2_len_min );
39 6081 : schedule->first_normal_slot = (1UL << ceil_log2_epoch) - FD_EPOCH_LEN_MIN;
40 6081 : }
41 :
42 12255 : return schedule;
43 12258 : }
44 :
45 : void
46 : fd_sysvar_epoch_schedule_write( fd_bank_t * bank,
47 : fd_accdb_user_t * accdb,
48 : fd_funk_txn_xid_t const * xid,
49 : fd_capture_ctx_t * capture_ctx,
50 21 : fd_epoch_schedule_t const * epoch_schedule ) {
51 :
52 21 : fd_sysvar_account_update( bank, accdb, xid, capture_ctx, &fd_sysvar_epoch_schedule_id, epoch_schedule, sizeof(fd_epoch_schedule_t) );
53 21 : }
54 :
55 : fd_epoch_schedule_t *
56 : fd_sysvar_epoch_schedule_read( fd_accdb_user_t * accdb,
57 : fd_funk_txn_xid_t const * xid,
58 0 : fd_epoch_schedule_t * out ) {
59 0 : fd_accdb_ro_t ro[1];
60 0 : if( FD_UNLIKELY( !fd_accdb_open_ro( accdb, ro, xid, &fd_sysvar_epoch_schedule_id ) ) ) {
61 0 : return NULL;
62 0 : }
63 :
64 0 : if( FD_UNLIKELY( fd_accdb_ref_data_sz( ro )!=FD_SYSVAR_EPOCH_SCHEDULE_BINCODE_SZ ) ) {
65 0 : fd_accdb_close_ro( accdb, ro );
66 0 : return NULL;
67 0 : }
68 :
69 0 : memcpy( out, fd_accdb_ref_data_const( ro ), sizeof(fd_epoch_schedule_t) );
70 :
71 0 : if( FD_UNLIKELY( validate( out ) ) ) {
72 0 : fd_accdb_close_ro( accdb, ro );
73 0 : return NULL;
74 0 : }
75 :
76 0 : fd_accdb_close_ro( accdb, ro );
77 0 : return out;
78 0 : }
79 :
80 : void
81 : fd_sysvar_epoch_schedule_init( fd_bank_t * bank,
82 : fd_accdb_user_t * accdb,
83 : fd_funk_txn_xid_t const * xid,
84 0 : fd_capture_ctx_t * capture_ctx ) {
85 0 : fd_epoch_schedule_t const * epoch_schedule = &bank->f.epoch_schedule;
86 0 : fd_sysvar_epoch_schedule_write( bank, accdb, xid, capture_ctx, epoch_schedule );
87 0 : }
88 :
89 : /* https://github.com/solana-labs/solana/blob/88aeaa82a856fc807234e7da0b31b89f2dc0e091/sdk/program/src/epoch_schedule.rs#L105 */
90 :
91 : ulong
92 : fd_epoch_slot_cnt( fd_epoch_schedule_t const * schedule,
93 195 : ulong epoch ) {
94 :
95 195 : if( FD_UNLIKELY( epoch < schedule->first_normal_epoch ) ) {
96 63 : ulong exp = fd_ulong_sat_add( epoch, (ulong)fd_ulong_find_lsb( FD_EPOCH_LEN_MIN ) );
97 63 : return ( exp<64UL ? 1UL<<exp : ULONG_MAX ); // saturating_pow
98 63 : }
99 :
100 132 : return schedule->slots_per_epoch;
101 195 : }
102 :
103 : /* https://github.com/solana-labs/solana/blob/88aeaa82a856fc807234e7da0b31b89f2dc0e091/sdk/program/src/epoch_schedule.rs#L170 */
104 :
105 : ulong
106 : fd_epoch_slot0( fd_epoch_schedule_t const * schedule,
107 9846 : ulong epoch ) {
108 9846 : if( FD_UNLIKELY( epoch <= schedule->first_normal_epoch ) ) {
109 9585 : ulong power = fd_ulong_if( epoch<64UL, 1UL<<epoch, ULONG_MAX );
110 9585 : return fd_ulong_sat_mul( power-1UL, FD_EPOCH_LEN_MIN );
111 9585 : }
112 :
113 261 : return fd_ulong_sat_add(
114 261 : fd_ulong_sat_mul(
115 261 : fd_ulong_sat_sub(
116 261 : epoch,
117 261 : schedule->first_normal_epoch),
118 261 : schedule->slots_per_epoch),
119 261 : schedule->first_normal_slot);
120 9846 : }
121 :
122 : /* https://github.com/solana-labs/solana/blob/88aeaa82a856fc807234e7da0b31b89f2dc0e091/sdk/program/src/epoch_schedule.rs#L140 */
123 :
124 : ulong
125 : fd_slot_to_epoch( fd_epoch_schedule_t const * schedule,
126 : ulong slot,
127 1348845 : ulong * out_offset_opt ) {
128 1348845 : ulong epoch;
129 1348845 : ulong offset;
130 :
131 1348845 : if( FD_UNLIKELY( slot < schedule->first_normal_slot ) ) {
132 : /* step0 = ceil(log2(FD_EPOCH_LEN_MIN))
133 : step1 = ceil(log2(FD_EPOCH_LEN_MIN + slot + 1))
134 : epoch = step1 - step0 - 1 */
135 :
136 23727 : ulong s0 = FD_EPOCH_LEN_MIN + slot + 1UL;
137 : /* Invariant: s0 > 1UL */
138 : /* Invariant: s0 > FD_EPOCH_LEN_MIN */
139 :
140 : /* Find lowest exp where (2^exp) >= s0
141 : (Only valid for s0 > 1UL and FD_EPOCH_LEN_MIN > 1UL) */
142 23727 : int exp = fd_ulong_find_msb( s0-1UL ) + 1;
143 23727 : int min_exp = fd_ulong_find_msb( FD_EPOCH_LEN_MIN );
144 23727 : epoch = (ulong)( exp - min_exp - 1 );
145 23727 : ulong epoch_len = 1UL<<( epoch + (ulong)fd_uint_find_lsb( FD_EPOCH_LEN_MIN ) );
146 23727 : offset = slot - ( epoch_len - FD_EPOCH_LEN_MIN );
147 1325118 : } else {
148 1325118 : if( FD_UNLIKELY( schedule->slots_per_epoch == 0UL ) ) {
149 12 : FD_LOG_WARNING(( "zero slots_per_epoch returning first_normal_epoch %lu", schedule->first_normal_epoch ));
150 12 : if( out_offset_opt ) *out_offset_opt = 0UL;
151 12 : return schedule->first_normal_epoch;
152 12 : }
153 1325106 : ulong n_slot = slot - schedule->first_normal_slot;
154 1325106 : ulong n_epoch = n_slot / schedule->slots_per_epoch;
155 1325106 : epoch = schedule->first_normal_epoch + n_epoch;
156 1325106 : offset = n_slot % schedule->slots_per_epoch;
157 1325106 : }
158 :
159 1348833 : ulong dummy_out;
160 1348833 : ulong * out_offset = out_offset_opt ? out_offset_opt : &dummy_out;
161 :
162 1348833 : *out_offset = offset;
163 1348833 : return epoch;
164 1348845 : }
165 :
166 : /* https://docs.rs/solana-epoch-schedule/2.2.1/src/solana_epoch_schedule/lib.rs.html#136-151 */
167 :
168 : ulong
169 : fd_slot_to_leader_schedule_epoch( fd_epoch_schedule_t const * schedule,
170 3552 : ulong slot ) {
171 :
172 3552 : if( FD_UNLIKELY( slot<schedule->first_normal_slot ) ) {
173 3 : return fd_slot_to_epoch( schedule, slot, NULL ) + 1UL;
174 3 : }
175 :
176 3549 : if( FD_UNLIKELY( schedule->slots_per_epoch == 0UL ) ) {
177 12 : FD_LOG_WARNING(( "zero slots_per_epoch returning first_normal_epoch %lu", schedule->first_normal_epoch ));
178 12 : return schedule->first_normal_epoch;
179 12 : }
180 :
181 3537 : ulong new_slots_since_first_normal_slot =
182 3537 : slot - schedule->first_normal_slot;
183 3537 : ulong new_first_normal_leader_schedule_slot =
184 3537 : new_slots_since_first_normal_slot + schedule->leader_schedule_slot_offset;
185 3537 : ulong new_epochs_since_first_normal_leader_schedule =
186 3537 : new_first_normal_leader_schedule_slot / schedule->slots_per_epoch;
187 :
188 3537 : return schedule->first_normal_epoch + new_epochs_since_first_normal_leader_schedule;
189 3549 : }
|