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 304 0.0 %
Date: 2025-07-01 05:00:49 Functions: 0 9 0.0 %

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

Generated by: LCOV version 1.14