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