LCOV - code coverage report
Current view: top level - flamenco/runtime/sysvar - fd_sysvar_clock.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 269 0.0 %
Date: 2025-08-05 05:04:49 Functions: 0 9 0.0 %

          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 :                                            &timestamp_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 : }

Generated by: LCOV version 1.14