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