Line data Source code
1 : #include "fd_sysvar_clock.h"
2 : #include "fd_sysvar_epoch_schedule.h"
3 : #include "fd_sysvar_rent.h"
4 : #include "../fd_executor.h"
5 : #include "../fd_acc_mgr.h"
6 : #include "../fd_system_ids.h"
7 : #include "../../fd_flamenco_base.h"
8 :
9 : /* https://github.com/solana-labs/solana/blob/8f2c8b8388a495d2728909e30460aa40dcc5d733/runtime/src/stake_weighted_timestamp.rs#L14 */
10 0 : #define MAX_ALLOWABLE_DRIFT_FAST ( 25 )
11 :
12 : /* https://github.com/solana-labs/solana/blob/8f2c8b8388a495d2728909e30460aa40dcc5d733/runtime/src/stake_weighted_timestamp.rs#L16 */
13 0 : #define MAX_ALLOWABLE_DRIFT_SLOW ( 150 )
14 :
15 : /* Do all intermediate calculations at nanosecond precision, to mirror Solana's behaviour. */
16 0 : #define NS_IN_S ( 1000000000 )
17 :
18 : /* The target tick duration, derived from the target tick rate.
19 : https://github.com/solana-labs/solana/blob/8f2c8b8388a495d2728909e30460aa40dcc5d733/sdk/src/poh_config.rs#L32
20 : */
21 : #define DEFAULT_TARGET_TICK_DURATION_NS ( NS_IN_S / FD_SYSVAR_CLOCK_DEFAULT_HASHES_PER_TICK )
22 :
23 : /* https://github.com/solana-labs/solana/blob/8f2c8b8388a495d2728909e30460aa40dcc5d733/runtime/src/bank.rs#L2200 */
24 : static long
25 0 : timestamp_from_genesis( fd_bank_t * bank ) {
26 : /* TODO: maybe make types of timestamps the same throughout the runtime codebase. as Solana uses a signed representation */
27 :
28 0 : return (long)(fd_bank_genesis_creation_time_get( bank ) + ((bank->slot * fd_bank_ns_per_slot_get( bank )) / NS_IN_S));
29 0 : }
30 :
31 : void
32 : fd_sysvar_clock_write( fd_bank_t * bank,
33 : fd_funk_t * funk,
34 : fd_funk_txn_t * funk_txn,
35 0 : fd_sol_sysvar_clock_t * clock ) {
36 0 : ulong sz = fd_sol_sysvar_clock_size( clock );
37 0 : uchar enc[sz];
38 0 : memset( enc, 0, sz );
39 0 : fd_bincode_encode_ctx_t ctx;
40 0 : ctx.data = enc;
41 0 : ctx.dataend = enc + sz;
42 0 : if( fd_sol_sysvar_clock_encode( clock, &ctx ) )
43 0 : FD_LOG_ERR(("fd_sol_sysvar_clock_encode failed"));
44 :
45 0 : fd_sysvar_set( bank, funk, funk_txn, &fd_sysvar_owner_id, (fd_pubkey_t *) &fd_sysvar_clock_id, enc, sz, bank->slot );
46 0 : }
47 :
48 :
49 : fd_sol_sysvar_clock_t const *
50 : fd_sysvar_clock_read( fd_funk_t * funk,
51 : fd_funk_txn_t * funk_txn,
52 0 : fd_spad_t * spad ) {
53 0 : FD_TXN_ACCOUNT_DECL( acc );
54 0 : int rc = fd_txn_account_init_from_funk_readonly( acc, &fd_sysvar_clock_id, funk, funk_txn );
55 0 : if( FD_UNLIKELY( rc!=FD_ACC_MGR_SUCCESS ) ) {
56 0 : return NULL;
57 0 : }
58 :
59 : /* This check is needed as a quirk of the fuzzer. If a sysvar account
60 : exists in the accounts database, but doesn't have any lamports,
61 : this means that the account does not exist. This wouldn't happen
62 : in a real execution environment. */
63 0 : if( FD_UNLIKELY( acc->vt->get_lamports( acc )==0 ) ) {
64 0 : return NULL;
65 0 : }
66 :
67 0 : int err;
68 0 : return fd_bincode_decode_spad(
69 0 : sol_sysvar_clock, spad,
70 0 : acc->vt->get_data( acc ),
71 0 : acc->vt->get_data_len( acc ),
72 0 : &err );
73 0 : }
74 :
75 : void
76 : fd_sysvar_clock_init( fd_bank_t * bank,
77 : fd_funk_t * funk,
78 0 : fd_funk_txn_t * funk_txn ) {
79 0 : long timestamp = timestamp_from_genesis( bank );
80 :
81 0 : fd_sol_sysvar_clock_t clock = {
82 0 : .slot = bank->slot,
83 0 : .epoch = 0,
84 0 : .epoch_start_timestamp = timestamp,
85 0 : .leader_schedule_epoch = 1,
86 0 : .unix_timestamp = timestamp,
87 0 : };
88 0 : fd_sysvar_clock_write( bank, funk, funk_txn, &clock );
89 0 : }
90 :
91 : /* Bounds the timestamp estimate by the max allowable drift from the expected PoH slot duration.
92 :
93 : https://github.com/solana-labs/solana/blob/8f2c8b8388a495d2728909e30460aa40dcc5d733/runtime/src/stake_weighted_timestamp.rs#L67 */
94 : static long
95 : bound_timestamp_estimate( fd_bank_t * bank,
96 : long estimate,
97 0 : long epoch_start_timestamp ) {
98 :
99 : /* Determine offsets from start of epoch */
100 : /* TODO: handle epoch boundary case */
101 0 : uint128 poh_estimate_offset = fd_bank_ns_per_slot_get( bank ) * bank->slot;
102 0 : uint128 estimate_offset = (uint128)( ( estimate - epoch_start_timestamp ) * NS_IN_S );
103 :
104 0 : uint128 max_delta_fast = ( poh_estimate_offset * MAX_ALLOWABLE_DRIFT_FAST ) / 100;
105 0 : uint128 max_delta_slow = ( poh_estimate_offset * MAX_ALLOWABLE_DRIFT_SLOW ) / 100;
106 :
107 0 : if ( ( estimate_offset > poh_estimate_offset ) && ( ( estimate_offset - poh_estimate_offset ) > max_delta_slow ) ) {
108 0 : return epoch_start_timestamp + (long)( poh_estimate_offset / NS_IN_S ) + (long)( max_delta_slow / NS_IN_S );
109 0 : } else if ( ( estimate_offset < poh_estimate_offset ) && ( ( poh_estimate_offset - estimate_offset ) > max_delta_fast ) ) {
110 0 : return epoch_start_timestamp + (long)( poh_estimate_offset / NS_IN_S ) - (long)( max_delta_fast / NS_IN_S );
111 0 : }
112 :
113 0 : return estimate;
114 0 : }
115 :
116 : /* Estimates the current timestamp, using the stake-weighted median of the latest validator timestamp oracle votes received
117 : from each voting node:
118 : https://github.com/solana-labs/solana/blob/8f2c8b8388a495d2728909e30460aa40dcc5d733/runtime/src/bank.rs#L2927
119 :
120 : Linear interpolation, using the target duration of a slot, is used to calculate the timestamp estimate for the current slot:
121 :
122 : timestamp = (stake-weighted median of vote timestamps) + ((target slot duration) * (slots since median timestamp vote was received))
123 : */
124 : static long
125 0 : estimate_timestamp( fd_bank_t * bank ) {
126 : /* TODO: bound the estimate to ensure it stays within a certain range of the expected PoH clock:
127 : https://github.com/solana-labs/solana/blob/8f2c8b8388a495d2728909e30460aa40dcc5d733/runtime/src/stake_weighted_timestamp.rs#L13 */
128 :
129 0 : fd_clock_timestamp_votes_global_t const * clock_timestamp_votes = fd_bank_clock_timestamp_votes_locking_query( bank );
130 0 : fd_clock_timestamp_vote_t_mapnode_t * votes = !!clock_timestamp_votes ? fd_clock_timestamp_votes_votes_root_join( clock_timestamp_votes ) : NULL;
131 0 : if( NULL==votes ) {
132 0 : fd_bank_clock_timestamp_votes_end_locking_query( bank );
133 0 : return timestamp_from_genesis( bank );
134 0 : }
135 :
136 : /* TODO: actually take the stake-weighted median. For now, just use the root node. */
137 0 : fd_clock_timestamp_vote_t * head = &votes->elem;
138 0 : ulong slots = bank->slot - head->slot;
139 0 : uint128 ns_correction = fd_bank_ns_per_slot_get( bank ) * slots;
140 0 : fd_bank_clock_timestamp_votes_end_locking_query( bank );
141 0 : return head->timestamp + (long) (ns_correction / NS_IN_S) ;
142 0 : }
143 :
144 0 : #define CIDX_T ulong
145 : #define VAL_T long
146 : struct stake_ts_ele {
147 : CIDX_T parent_cidx;
148 : CIDX_T left_cidx;
149 : CIDX_T right_cidx;
150 : CIDX_T prio_cidx;
151 : VAL_T timestamp;
152 : unsigned long stake;
153 : };
154 :
155 : typedef struct stake_ts_ele stake_ts_ele_t;
156 :
157 : #define POOL_NAME stake_ts_pool
158 0 : #define POOL_T stake_ts_ele_t
159 : #define POOL_IDX_T CIDX_T
160 0 : #define POOL_NEXT parent_cidx
161 : #include "../../../util/tmpl/fd_pool.c"
162 :
163 0 : FD_FN_CONST static inline int valcmp (VAL_T a, VAL_T b) {
164 0 : int val = (a < b) ? -1 : 1;
165 0 : return (a == b) ? 0 : val;
166 0 : }
167 :
168 : #define TREAP_NAME stake_ts_treap
169 : #define TREAP_T stake_ts_ele_t
170 : #define TREAP_QUERY_T VAL_T
171 0 : #define TREAP_CMP(q,e) valcmp(q, e->timestamp)
172 0 : #define TREAP_LT(e0,e1) (((VAL_T)((e0)->timestamp)) < ((VAL_T)((e1)->timestamp)))
173 0 : #define TREAP_IDX_T CIDX_T
174 0 : #define TREAP_PARENT parent_cidx
175 0 : #define TREAP_LEFT left_cidx
176 0 : #define TREAP_RIGHT right_cidx
177 0 : #define TREAP_PRIO prio_cidx
178 : #define TREAP_IMPL_STYLE 0
179 : #include "../../../util/tmpl/fd_treap.c"
180 :
181 : /* https://github.com/solana-labs/solana/blob/c091fd3da8014c0ef83b626318018f238f506435/runtime/src/bank.rs#L3600 */
182 : static void
183 : fd_calculate_stake_weighted_timestamp( fd_bank_t * bank,
184 : fd_funk_t * funk,
185 : fd_funk_txn_t * funk_txn,
186 : long * result_timestamp,
187 : uint fix_estimate_into_u64,
188 0 : fd_spad_t * runtime_spad ) {
189 0 : FD_SPAD_FRAME_BEGIN( runtime_spad ) {
190 :
191 0 : ulong slot_duration = (ulong)fd_bank_ns_per_slot_get( bank );
192 0 : fd_sol_sysvar_clock_t const * clock = fd_sysvar_clock_read( funk,
193 0 : funk_txn,
194 0 : runtime_spad );
195 : // get the unique timestamps
196 : /* stake per timestamp */
197 :
198 : /* Set up a temporary treap, pool, and rng (required for treap prio) */
199 : /* FIXME: Hardcoded constant */
200 0 : stake_ts_treap_t _treap[1];
201 0 : stake_ts_treap_t * treap = stake_ts_treap_join( stake_ts_treap_new( _treap, 10240UL ) );
202 0 : uchar * pool_mem = fd_spad_alloc( runtime_spad, stake_ts_pool_align(), stake_ts_pool_footprint( 10240UL ) );
203 0 : stake_ts_ele_t * pool = stake_ts_pool_join( stake_ts_pool_new( pool_mem, 10240UL ) );
204 0 : uint txn_cnt = (uint)fd_bank_transaction_count_get( bank );
205 :
206 0 : fd_rng_t _rng[1];
207 0 : fd_rng_t * rng = fd_rng_join( fd_rng_new( _rng, txn_cnt, 0UL ) );
208 :
209 0 : ulong total_stake = 0;
210 :
211 0 : fd_clock_timestamp_votes_global_t const * clock_timestamp_votes = fd_bank_clock_timestamp_votes_locking_query( bank );
212 0 : if( FD_UNLIKELY( !clock_timestamp_votes ) ) {
213 0 : fd_bank_clock_timestamp_votes_end_locking_query( bank );
214 0 : *result_timestamp = 0;
215 0 : return;
216 0 : }
217 :
218 0 : fd_clock_timestamp_vote_t_mapnode_t * timestamp_votes_pool = fd_clock_timestamp_votes_votes_pool_join( clock_timestamp_votes );
219 0 : fd_clock_timestamp_vote_t_mapnode_t * timestamp_votes_root = fd_clock_timestamp_votes_votes_root_join( clock_timestamp_votes );
220 :
221 0 : fd_vote_accounts_global_t const * epoch_stakes = fd_bank_epoch_stakes_locking_query( bank );
222 0 : fd_vote_accounts_pair_global_t_mapnode_t * vote_acc_pool = fd_vote_accounts_vote_accounts_pool_join( epoch_stakes );
223 0 : fd_vote_accounts_pair_global_t_mapnode_t * vote_acc_root = fd_vote_accounts_vote_accounts_root_join( epoch_stakes );
224 :
225 0 : for( fd_vote_accounts_pair_global_t_mapnode_t * n = fd_vote_accounts_pair_global_t_map_minimum(vote_acc_pool, vote_acc_root);
226 0 : n;
227 0 : n = fd_vote_accounts_pair_global_t_map_successor( vote_acc_pool, n ) ) {
228 :
229 : /* get timestamp */
230 0 : fd_pubkey_t const * vote_pubkey = &n->elem.key;
231 :
232 0 : if( timestamp_votes_pool == NULL ) {
233 0 : continue;
234 0 : } else {
235 0 : fd_clock_timestamp_vote_t_mapnode_t query_vote_acc_node;
236 0 : query_vote_acc_node.elem.pubkey = *vote_pubkey;
237 0 : fd_clock_timestamp_vote_t_mapnode_t * vote_acc_node = fd_clock_timestamp_vote_t_map_find( timestamp_votes_pool,
238 0 : timestamp_votes_root,
239 0 : &query_vote_acc_node );
240 0 : ulong vote_timestamp = 0;
241 0 : ulong vote_slot = 0;
242 0 : if( vote_acc_node == NULL ) {
243 0 : int err;
244 :
245 0 : uchar * data = fd_solana_account_data_join( &n->elem.value );
246 0 : ulong data_len = n->elem.value.data_len;
247 :
248 0 : fd_vote_state_versioned_t * vsv = fd_bincode_decode_spad(
249 0 : vote_state_versioned, runtime_spad,
250 0 : data,
251 0 : data_len,
252 0 : &err );
253 0 : if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) {
254 0 : FD_LOG_WARNING(( "Vote state versioned decode failed" ));
255 0 : continue;
256 0 : }
257 :
258 0 : switch( vsv->discriminant ) {
259 0 : case fd_vote_state_versioned_enum_v0_23_5:
260 0 : vote_timestamp = (ulong)vsv->inner.v0_23_5.last_timestamp.timestamp;
261 0 : vote_slot = vsv->inner.v0_23_5.last_timestamp.slot;
262 0 : break;
263 0 : case fd_vote_state_versioned_enum_v1_14_11:
264 0 : vote_timestamp = (ulong)vsv->inner.v1_14_11.last_timestamp.timestamp;
265 0 : vote_slot = vsv->inner.v1_14_11.last_timestamp.slot;
266 0 : break;
267 0 : case fd_vote_state_versioned_enum_current:
268 0 : vote_timestamp = (ulong)vsv->inner.current.last_timestamp.timestamp;
269 0 : vote_slot = vsv->inner.current.last_timestamp.slot;
270 0 : break;
271 0 : default:
272 0 : __builtin_unreachable();
273 0 : }
274 :
275 0 : } else {
276 0 : vote_timestamp = (ulong)vote_acc_node->elem.timestamp;
277 0 : vote_slot = vote_acc_node->elem.slot;
278 0 : }
279 :
280 0 : ulong slot_delta = fd_ulong_sat_sub(bank->slot, vote_slot);
281 0 : fd_epoch_schedule_t const * epoch_schedule = fd_bank_epoch_schedule_query( bank );
282 0 : if( slot_delta > epoch_schedule->slots_per_epoch ) {
283 0 : continue;
284 0 : }
285 :
286 0 : ulong offset = fd_ulong_sat_mul(slot_duration, slot_delta);
287 0 : long estimate = (long)vote_timestamp + (long)(offset / NS_IN_S);
288 : /* get stake */
289 0 : total_stake += n->elem.stake;
290 0 : ulong treap_idx = stake_ts_treap_idx_query( treap, estimate, pool );
291 0 : if ( FD_LIKELY( treap_idx < ULONG_MAX ) ) {
292 0 : pool[ treap_idx ].stake += n->elem.stake;
293 0 : } else {
294 0 : if( 0 == stake_ts_pool_free( pool ) ) {
295 0 : FD_LOG_ERR(( "stake_ts_pool is empty" ));
296 0 : }
297 0 : ulong idx = stake_ts_pool_idx_acquire( pool );
298 0 : pool[ idx ].prio_cidx = fd_rng_ulong( rng );
299 0 : pool[ idx ].timestamp = estimate;
300 0 : pool[ idx ].stake = n->elem.stake;
301 0 : stake_ts_treap_idx_insert( treap, idx, pool );
302 0 : }
303 0 : }
304 0 : }
305 0 : fd_bank_epoch_stakes_end_locking_query( bank );
306 0 : fd_bank_clock_timestamp_votes_end_locking_query( bank );
307 :
308 0 : *result_timestamp = 0;
309 0 : if( total_stake == 0 ) {
310 0 : return;
311 0 : }
312 :
313 : // FIXME: this should be a uint128
314 0 : ulong stake_accumulator = 0;
315 0 : for( stake_ts_treap_fwd_iter_t iter = stake_ts_treap_fwd_iter_init( treap, pool );
316 0 : !stake_ts_treap_fwd_iter_done( iter );
317 0 : iter = stake_ts_treap_fwd_iter_next( iter, pool ) ) {
318 0 : ulong idx = stake_ts_treap_fwd_iter_idx( iter );
319 0 : stake_accumulator = fd_ulong_sat_add(stake_accumulator, pool[ idx ].stake);
320 0 : if( stake_accumulator > (total_stake / 2) ) {
321 0 : *result_timestamp = pool[ idx ].timestamp;
322 0 : break;
323 0 : }
324 0 : }
325 :
326 0 : FD_LOG_DEBUG(( "stake weighted timestamp: %ld total stake %lu", *result_timestamp, total_stake ));
327 :
328 : // Bound estimate by `max_allowable_drift` since the start of the epoch
329 0 : fd_epoch_schedule_t const * epoch_schedule = fd_bank_epoch_schedule_query( bank );
330 0 : ulong epoch_start_slot = fd_epoch_slot0( epoch_schedule, clock->epoch );
331 0 : FD_LOG_DEBUG(( "Epoch start slot %lu", epoch_start_slot ));
332 0 : ulong poh_estimate_offset = fd_ulong_sat_mul( slot_duration, fd_ulong_sat_sub( bank->slot, epoch_start_slot ) );
333 0 : ulong estimate_offset = fd_ulong_sat_mul( NS_IN_S, (fix_estimate_into_u64) ? fd_ulong_sat_sub( (ulong)*result_timestamp, (ulong)clock->epoch_start_timestamp ) : (ulong)(*result_timestamp - clock->epoch_start_timestamp));
334 0 : ulong max_delta_fast = fd_ulong_sat_mul( poh_estimate_offset, MAX_ALLOWABLE_DRIFT_FAST ) / 100;
335 0 : ulong max_delta_slow = fd_ulong_sat_mul( poh_estimate_offset, MAX_ALLOWABLE_DRIFT_SLOW ) / 100;
336 0 : FD_LOG_DEBUG(( "poh offset %lu estimate %lu fast %lu slow %lu", poh_estimate_offset, estimate_offset, max_delta_fast, max_delta_slow ));
337 0 : if( estimate_offset > poh_estimate_offset && fd_ulong_sat_sub(estimate_offset, poh_estimate_offset) > max_delta_slow ) {
338 0 : *result_timestamp = clock->epoch_start_timestamp + (long)poh_estimate_offset / NS_IN_S + (long)max_delta_slow / NS_IN_S;
339 0 : } else if( estimate_offset < poh_estimate_offset && fd_ulong_sat_sub(poh_estimate_offset, estimate_offset) > max_delta_fast ) {
340 0 : *result_timestamp = clock->epoch_start_timestamp + (long)poh_estimate_offset / NS_IN_S - (long)max_delta_fast / NS_IN_S;
341 0 : }
342 :
343 0 : if (*result_timestamp < clock->unix_timestamp) {
344 0 : FD_LOG_DEBUG(( "updated timestamp to ancestor" ));
345 0 : *result_timestamp = clock->unix_timestamp;
346 0 : }
347 0 : return;
348 :
349 0 : } FD_SPAD_FRAME_END;
350 0 : }
351 :
352 : int
353 : fd_sysvar_clock_update( fd_bank_t * bank,
354 : fd_funk_t * funk,
355 : fd_funk_txn_t * funk_txn,
356 0 : fd_spad_t * runtime_spad ) {
357 :
358 0 : fd_pubkey_t const * key = &fd_sysvar_clock_id;
359 :
360 0 : FD_TXN_ACCOUNT_DECL( rec );
361 0 : int err = fd_txn_account_init_from_funk_readonly( rec, key, funk, funk_txn );
362 0 : if( FD_UNLIKELY( err ) ) {
363 0 : FD_LOG_ERR(( "fd_txn_account_init_from_funk_readonly(clock) failed: %d", err ));
364 0 : }
365 :
366 0 : fd_sol_sysvar_clock_t * clock = fd_bincode_decode_spad(
367 0 : sol_sysvar_clock, runtime_spad,
368 0 : rec->vt->get_data( rec ),
369 0 : rec->vt->get_data_len( rec ),
370 0 : &err );
371 0 : if( FD_UNLIKELY( err ) ) {
372 0 : FD_LOG_ERR(( "fd_sol_sysvar_clock_decode failed" ));
373 0 : }
374 :
375 0 : long ancestor_timestamp = clock->unix_timestamp;
376 :
377 0 : if( bank->slot != 0 ) {
378 0 : long new_timestamp = 0L;
379 0 : fd_calculate_stake_weighted_timestamp( bank,
380 0 : funk,
381 0 : funk_txn,
382 0 : &new_timestamp,
383 0 : FD_FEATURE_ACTIVE_BANK( bank, warp_timestamp_again ),
384 0 : runtime_spad );
385 :
386 : /* If the timestamp was successfully calculated, use it. It not keep the old one.
387 : https://github.com/anza-xyz/agave/blob/v2.1.14/runtime/src/bank.rs#L1947-L1954 */
388 0 : if( FD_LIKELY( new_timestamp ) ) {
389 0 : clock->unix_timestamp = new_timestamp;
390 0 : }
391 0 : }
392 :
393 0 : if( FD_UNLIKELY( !clock->unix_timestamp ) ) {
394 : /* generate timestamp for genesis */
395 0 : long timestamp_estimate = estimate_timestamp( bank );
396 0 : long bounded_timestamp_estimate = bound_timestamp_estimate( bank,
397 0 : timestamp_estimate,
398 0 : clock->epoch_start_timestamp );
399 0 : if( timestamp_estimate != bounded_timestamp_estimate ) {
400 0 : FD_LOG_INFO(( "corrected timestamp_estimate %ld to %ld", timestamp_estimate, bounded_timestamp_estimate ));
401 0 : }
402 : /* if let Some(timestamp_estimate) =
403 : self.get_timestamp_estimate(max_allowable_drift, epoch_start_timestamp)
404 : {
405 : unix_timestamp = timestamp_estimate;
406 : if timestamp_estimate < ancestor_timestamp {
407 : unix_timestamp = ancestor_timestamp;
408 : }
409 : } */
410 0 : if( bounded_timestamp_estimate < ancestor_timestamp ) {
411 0 : FD_LOG_DEBUG(( "clock rewind detected: %ld -> %ld", ancestor_timestamp, bounded_timestamp_estimate ));
412 0 : bounded_timestamp_estimate = ancestor_timestamp;
413 0 : }
414 0 : clock->unix_timestamp = bounded_timestamp_estimate;
415 0 : }
416 :
417 0 : clock->slot = bank->slot;
418 :
419 0 : fd_epoch_schedule_t const * epoch_schedule = fd_bank_epoch_schedule_query( bank );
420 :
421 0 : ulong epoch_old = clock->epoch;
422 0 : ulong epoch_new = fd_slot_to_epoch( epoch_schedule, clock->slot, NULL );
423 0 : FD_LOG_DEBUG(("Epoch old %lu new %lu slot %lu", epoch_old, epoch_new, clock->slot));
424 0 : clock->epoch = epoch_new;
425 0 : if( epoch_old != epoch_new ) {
426 0 : long timestamp_estimate = 0L;
427 0 : fd_calculate_stake_weighted_timestamp( bank,
428 0 : funk,
429 0 : funk_txn,
430 0 : ×tamp_estimate,
431 0 : FD_FEATURE_ACTIVE_BANK( bank, warp_timestamp_again ),
432 0 : runtime_spad );
433 0 : clock->unix_timestamp = fd_long_max( timestamp_estimate, ancestor_timestamp );
434 0 : clock->epoch_start_timestamp = clock->unix_timestamp;
435 0 : clock->leader_schedule_epoch = fd_slot_to_leader_schedule_epoch( epoch_schedule, bank->slot );
436 0 : }
437 :
438 0 : FD_LOG_DEBUG(( "clock->slot: %lu", clock->slot ));
439 0 : FD_LOG_DEBUG(( "clock->epoch_start_timestamp: %ld", clock->epoch_start_timestamp ));
440 0 : FD_LOG_DEBUG(( "clock->epoch: %lu", clock->epoch ));
441 0 : FD_LOG_DEBUG(( "clock->leader_schedule_epoch: %lu", clock->leader_schedule_epoch ));
442 0 : FD_LOG_DEBUG(( "clock->unix_timestamp: %ld", clock->unix_timestamp ));
443 :
444 0 : ulong sz = fd_sol_sysvar_clock_size( clock );
445 0 : FD_TXN_ACCOUNT_DECL( acc );
446 0 : err = fd_txn_account_init_from_funk_mutable( acc, key, funk, funk_txn, 1, sz );
447 0 : if( err ) {
448 0 : FD_LOG_CRIT(( "fd_txn_account_init_from_funk_mutable(clock) failed: %d", err ));
449 0 : }
450 :
451 0 : fd_bincode_encode_ctx_t e_ctx = {
452 0 : .data = acc->vt->get_data_mut( acc ),
453 0 : .dataend = acc->vt->get_data_mut( acc )+sz,
454 0 : };
455 0 : if( fd_sol_sysvar_clock_encode( clock, &e_ctx ) ) {
456 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
457 0 : }
458 :
459 0 : fd_rent_t const * rent = fd_bank_rent_query( bank );
460 0 : ulong lamps = fd_rent_exempt_minimum_balance( rent, sz );
461 0 : if( acc->vt->get_lamports( acc ) < lamps ) {
462 0 : acc->vt->set_lamports( acc, lamps );
463 0 : }
464 :
465 0 : acc->vt->set_data_len( acc, sz );
466 0 : acc->vt->set_owner( acc, &fd_sysvar_owner_id );
467 :
468 0 : fd_txn_account_mutable_fini( acc, funk, funk_txn );
469 :
470 0 : return 0;
471 0 : }
|