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