LCOV - code coverage report
Current view: top level - flamenco/runtime/sysvar - fd_sysvar_clock.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 194 210 92.4 %
Date: 2026-06-25 05:49:44 Functions: 8 8 100.0 %

          Line data    Source code
       1             : #include "fd_sysvar_clock.h"
       2             : #include "fd_sysvar_epoch_schedule.h"
       3             : #include "../fd_runtime_stack.h"
       4             : #include "../fd_system_ids.h"
       5             : #include "../sysvar/fd_sysvar.h"
       6             : #include "../program/vote/fd_vote_state_versioned.h"
       7             : 
       8             : /* Syvar Clock Possible Values:
       9             :   slot:
      10             :   [0, ULONG_MAX]
      11             : 
      12             :   epoch:
      13             :   [0, slot/432000UL]
      14             : 
      15             :   epoch_start_timestamp:
      16             :   [0, ULONG_MAX]
      17             : 
      18             :   unix_timestamp:
      19             :   This value is bounded by the slot distance from the
      20             :   epoch_start_timestamp.
      21             :   The protocol allows for a maximum drift (either fast or slow) from the
      22             :   start of the epoch's timestamp.  The expected time is called the PoH
      23             :   offset.  This offset is calculated by (epoch_start_timestamp + slots
      24             :   since epoch * slot_duration). The drift is then bounded by the
      25             :   max_allowable_drift_{slow,fast}.  The stake weighted offset can be
      26             :   150% more than the PoH offset and 25% less than the PoH offset.
      27             :   So, the bounds for the unix_timestamp can be calculated by:
      28             :   upper bound = epoch_start_timestamp + (slots since epoch * slot_duration) * 2.5
      29             :   lower bound = epoch_start_timestamp + (slots since epoch * slot_duration) * 0.75
      30             : 
      31             :   leader_schedule_epoch:
      32             :   This is the value of the epoch used for the leader schedule.  It is
      33             :   computed based on the values of the epoch schedule (first_normal_slot,
      34             :   leader_schedule_slot_offset, slots_per_epoch).  It is always equal to
      35             :   ((slot - first_normal_slot) + leader_schedule_slot_offset) / schedule->slots_per_epoch
      36             : */
      37             : 
      38             : /* https://github.com/anza-xyz/agave/blob/v2.3.7/runtime/src/stake_weighted_timestamp.rs#L14 */
      39        3597 : #define MAX_ALLOWABLE_DRIFT_FAST_PERCENT ( 25U )
      40             : 
      41             : /* https://github.com/anza-xyz/agave/blob/v2.3.7/runtime/src/stake_weighted_timestamp.rs#L15 */
      42        3597 : #define MAX_ALLOWABLE_DRIFT_SLOW_PERCENT ( 150U )
      43             : 
      44             : /* Do all intermediate calculations at nanosecond precision, to mirror
      45             :    Solana's behavior. */
      46        7488 : #define NS_IN_S ((long)1e9)
      47             : 
      48             : /* FD_SYSVAR_CLOCK_STAKE_WEIGHTS_MAX specifies the max number of stake
      49             :    weights processed in a clock update. */
      50             : 
      51             : #define FD_SYSVAR_CLOCK_STAKE_WEIGHTS_MAX (10240UL)
      52             : 
      53             : /* https://github.com/anza-xyz/agave/blob/v2.3.7/runtime/src/bank.rs#L2110-L2117 */
      54             : static inline long
      55           9 : unix_timestamp_from_genesis( fd_bank_t * bank ) {
      56             :   /* TODO: genesis_creation_time needs to be a long in the bank. */
      57           9 :   return fd_long_sat_add(
      58           9 :       (long)bank->f.genesis_creation_time,
      59           9 :       (long)( fd_uint128_sat_mul( bank->f.slot, bank->f.ns_per_slot.ud ) / NS_IN_S ) );
      60           9 : }
      61             : 
      62             : static void
      63             : fd_sysvar_clock_write( fd_bank_t *                   bank,
      64             :                        fd_accdb_t *                  accdb,
      65             :                        fd_capture_ctx_t *            capture_ctx,
      66        3801 :                        fd_sol_sysvar_clock_t const * clock ) {
      67        3801 :   fd_sysvar_account_update( bank, accdb, capture_ctx, &fd_sysvar_clock_id, clock, sizeof(fd_sol_sysvar_clock_t) );
      68        3801 : }
      69             : 
      70             : fd_sol_sysvar_clock_t *
      71             : fd_sysvar_clock_read( fd_accdb_t *            accdb,
      72             :                       fd_accdb_fork_id_t      fork_id,
      73        3828 :                       fd_sol_sysvar_clock_t * clock ) {
      74        3828 :   fd_acc_t acc = fd_accdb_read_one( accdb, fork_id, fd_sysvar_clock_id.uc );
      75        3828 :   if( FD_UNLIKELY( !acc.lamports || acc.data_len<sizeof(fd_sol_sysvar_clock_t) ) ) {
      76             :     /* This check is needed as a quirk of the fuzzer. If a sysvar
      77             :        account exists in the accounts database, but doesn't have any
      78             :        lamports, this means that the account does not exist.  This
      79             :        wouldn't happen in a real execution environment. */
      80           0 :     fd_accdb_unread_one( accdb, &acc );
      81           0 :     return NULL;
      82           0 :   }
      83             : 
      84        3828 :   fd_memcpy( clock, acc.data, sizeof(fd_sol_sysvar_clock_t) );
      85        3828 :   fd_accdb_unread_one( accdb, &acc );
      86        3828 :   return clock;
      87        3828 : }
      88             : 
      89             : void
      90             : fd_sysvar_clock_init( fd_bank_t *        bank,
      91             :                       fd_accdb_t *       accdb,
      92           9 :                       fd_capture_ctx_t * capture_ctx ) {
      93           9 :   long timestamp = unix_timestamp_from_genesis( bank );
      94             : 
      95           9 :   fd_sol_sysvar_clock_t clock = {
      96           9 :     .slot                  = bank->f.slot,
      97           9 :     .epoch                 = 0,
      98           9 :     .epoch_start_timestamp = timestamp,
      99           9 :     .leader_schedule_epoch = 1,
     100           9 :     .unix_timestamp        = timestamp,
     101           9 :   };
     102           9 :   fd_sysvar_clock_write( bank, accdb, capture_ctx, &clock );
     103           9 : }
     104             : 
     105             : #define SORT_NAME  sort_stake_ts
     106         303 : #define SORT_KEY_T ts_est_ele_t
     107         174 : #define SORT_BEFORE(a,b) ( (a).timestamp < (b).timestamp )
     108             : #include "../../../util/tmpl/fd_sort.c"
     109             : 
     110             : static void
     111             : accum_vote_stakes_no_vat( fd_bank_t *               bank,
     112             :                           fd_accdb_t *              accdb,
     113             :                           fd_runtime_stack_t *      runtime_stack,
     114             :                           uint128 *                 total_stake_out,
     115        3744 :                           ulong *                   ts_ele_cnt_out ) {
     116             : 
     117        3744 :   ts_est_ele_t * ts_eles = runtime_stack->clock_ts.staked_ts;
     118        3744 :   ulong ts_ele_cnt = 0UL;
     119             : 
     120        3744 :   uint128 total_stake = 0UL;
     121             : 
     122        3744 :   fd_epoch_schedule_t const * epoch_schedule = &bank->f.epoch_schedule;
     123        3744 :   ulong                       slot_duration  = bank->f.ns_per_slot.ul[0];
     124        3744 :   ulong                       current_slot   = bank->f.slot;
     125             : 
     126        3744 :   fd_vote_stakes_t * vote_stakes = fd_bank_vote_stakes( bank );
     127        3744 :   ushort             fork_idx    = bank->vote_stakes_fork_id;
     128             : 
     129        3744 :   fd_top_votes_t const * top_votes = fd_bank_top_votes_t_2_query( bank );
     130             : 
     131        3744 :   uchar __attribute__((aligned(FD_VOTE_STAKES_ITER_ALIGN))) iter_mem[ FD_VOTE_STAKES_ITER_FOOTPRINT ];
     132        3744 :   for( fd_vote_stakes_iter_t * iter = fd_vote_stakes_fork_iter_init( vote_stakes, fork_idx, iter_mem );
     133        7611 :         !fd_vote_stakes_fork_iter_done( vote_stakes, fork_idx, iter );
     134        3867 :         fd_vote_stakes_fork_iter_next( vote_stakes, fork_idx, iter ) ) {
     135        3867 :     fd_pubkey_t pubkey;
     136        3867 :     ulong       stake_t_2;
     137        3867 :     fd_vote_stakes_fork_iter_ele( vote_stakes, fork_idx, iter, &pubkey, NULL, &stake_t_2, NULL, NULL, NULL, NULL );
     138        3867 :     if( FD_UNLIKELY( !stake_t_2 ) ) continue;
     139             : 
     140        3867 :     ulong last_vote_slot;
     141        3867 :     long  last_vote_timestamp;
     142        3867 :     uchar is_valid = 1;
     143        3867 :     int   found = fd_top_votes_query( top_votes, &pubkey, NULL, NULL, &last_vote_slot, &last_vote_timestamp, NULL, &is_valid );
     144        3867 :     if( FD_UNLIKELY( !found ) ) {
     145          84 :       fd_acc_t acc = fd_accdb_read_one( accdb, bank->accdb_fork_id, pubkey.uc );
     146          84 :       if( FD_UNLIKELY( !acc.lamports || !fd_vsv_is_correct_size_owner_and_init( acc.owner, acc.data, acc.data_len ) ) ) {
     147           0 :         fd_accdb_unread_one( accdb, &acc );
     148           0 :         continue;
     149           0 :       }
     150             : 
     151          84 :       fd_vote_block_timestamp_t last_vote;
     152          84 :       FD_TEST( !fd_vote_account_last_timestamp( acc.data, acc.data_len, &last_vote ) );
     153          84 :       fd_accdb_unread_one( accdb, &acc );
     154          84 :       last_vote_slot      = last_vote.slot;
     155          84 :       last_vote_timestamp = last_vote.timestamp;
     156          84 :     }
     157        3867 :     if( FD_UNLIKELY( !is_valid ) ) continue;
     158             : 
     159             :     /* https://github.com/anza-xyz/agave/blob/v3.0.0/runtime/src/bank.rs#L2445 */
     160        3867 :     ulong slot_delta;
     161        3867 :     int err = fd_ulong_checked_sub( current_slot, last_vote_slot, &slot_delta );
     162        3867 :     if( FD_UNLIKELY( err ) ) {
     163             :       /* Don't count vote accounts with a last vote slot that is greater
     164             :           than the current slot. */
     165           0 :       continue;
     166           0 :     }
     167             : 
     168             :     /* Don't count vote accounts that haven't voted in the past 432k
     169             :         slots (length of an epoch).
     170             :         https://github.com/anza-xyz/agave/blob/v3.0.0/runtime/src/bank.rs#L2446-L2447 */
     171        3867 :     if( FD_UNLIKELY( slot_delta>epoch_schedule->slots_per_epoch ) ) {
     172         252 :       continue;
     173         252 :     }
     174             : 
     175             :     /* Calculate the timestamp estimate by taking the last vote
     176             :         timestamp and adding the estimated time since the last vote
     177             :         (delta from last vote slot to current slot * slot duration).
     178             :         https://github.com/anza-xyz/agave/blob/v2.3.7/runtime/src/stake_weighted_timestamp.rs#L44-L45 */
     179        3615 :     ulong offset   = fd_ulong_sat_mul( slot_duration, slot_delta );
     180        3615 :     long  estimate = fd_long_sat_add( last_vote_timestamp, (long)(offset / NS_IN_S) );
     181             : 
     182             :     /* For each timestamp, accumulate the stake from E-2.  If the acc
     183             :         for the timestamp doesn't exist yet, insert it.  Otherwise,
     184             :         update the existing acc.
     185             :         https://github.com/anza-xyz/agave/blob/v2.3.7/runtime/src/stake_weighted_timestamp.rs#L46-L53 */
     186        3615 :     ts_eles[ ts_ele_cnt ] = (ts_est_ele_t){
     187        3615 :       .timestamp = estimate,
     188        3615 :       .stake     = { .ud=stake_t_2 },
     189        3615 :     };
     190        3615 :     ts_ele_cnt++;
     191             : 
     192             :     /* https://github.com/anza-xyz/agave/blob/v2.3.7/runtime/src/stake_weighted_timestamp.rs#L54 */
     193        3615 :     total_stake += stake_t_2;
     194        3615 :   }
     195        3744 :   fd_vote_stakes_fork_iter_fini( vote_stakes );
     196             : 
     197        3744 :   *total_stake_out = total_stake;
     198        3744 :   *ts_ele_cnt_out  = ts_ele_cnt;
     199        3744 : }
     200             : 
     201             : static void
     202             : accum_vote_stakes_vat( fd_bank_t *          bank,
     203             :                        fd_runtime_stack_t * runtime_stack,
     204             :                        uint128 *            total_stake_out,
     205          48 :                        ulong *              ts_ele_cnt_out ) {
     206             : 
     207          48 :   ts_est_ele_t * ts_eles = runtime_stack->clock_ts.staked_ts;
     208          48 :   ulong ts_ele_cnt = 0UL;
     209             : 
     210          48 :   uint128 total_stake = 0UL;
     211             : 
     212          48 :   fd_epoch_schedule_t const * epoch_schedule = &bank->f.epoch_schedule;
     213          48 :   ulong                       slot_duration  = bank->f.ns_per_slot.ul[0];
     214          48 :   ulong                       current_slot   = bank->f.slot;
     215             : 
     216          48 :   fd_top_votes_t const * top_votes = fd_bank_top_votes_t_2_query( bank );
     217             : 
     218          48 :   uchar __attribute__((aligned(FD_TOP_VOTES_ITER_ALIGN))) iter_mem[ FD_TOP_VOTES_ITER_FOOTPRINT ];
     219          48 :   for( fd_top_votes_iter_t * iter = fd_top_votes_iter_init( top_votes, iter_mem );
     220         168 :        !fd_top_votes_iter_done( top_votes, iter );
     221         120 :        fd_top_votes_iter_next( top_votes, iter ) ) {
     222         120 :     fd_pubkey_t pubkey;
     223         120 :     ulong       stake_t_2;
     224         120 :     ulong       last_vote_slot;
     225         120 :     long        last_vote_timestamp;
     226         120 :     uchar       is_valid;
     227         120 :     fd_top_votes_iter_ele( top_votes, iter, &pubkey, NULL, &stake_t_2, NULL, &last_vote_slot, &last_vote_timestamp, &is_valid );
     228         120 :     if( FD_UNLIKELY( !is_valid ) ) continue;
     229             : 
     230             :     /* https://github.com/anza-xyz/agave/blob/v3.0.0/runtime/src/bank.rs#L2445 */
     231         111 :     ulong slot_delta;
     232         111 :     int err = fd_ulong_checked_sub( current_slot, last_vote_slot, &slot_delta );
     233         111 :     if( FD_UNLIKELY( err ) ) {
     234             :       /* Don't count vote accounts with a last vote slot that is greater
     235             :          than the current slot. */
     236           0 :       continue;
     237           0 :     }
     238             : 
     239             :     /* Don't count vote accounts that haven't voted in the past 432k
     240             :         slots (length of an epoch).
     241             :         https://github.com/anza-xyz/agave/blob/v3.0.0/runtime/src/bank.rs#L2446-L2447 */
     242         111 :     if( FD_UNLIKELY( slot_delta>epoch_schedule->slots_per_epoch ) ) {
     243           0 :       continue;
     244           0 :     }
     245             : 
     246             :     /* Calculate the timestamp estimate by taking the last vote
     247             :         timestamp and adding the estimated time since the last vote
     248             :         (delta from last vote slot to current slot * slot duration).
     249             :         https://github.com/anza-xyz/agave/blob/v2.3.7/runtime/src/stake_weighted_timestamp.rs#L44-L45 */
     250         111 :     ulong offset   = fd_ulong_sat_mul( slot_duration, slot_delta );
     251         111 :     long  estimate = fd_long_sat_add( last_vote_timestamp, (long)(offset / NS_IN_S) );
     252             : 
     253             :     /* For each timestamp, accumulate the stake from E-2.  If the entry
     254             :         for the timestamp doesn't exist yet, insert it.  Otherwise,
     255             :         update the existing entry.
     256             :         https://github.com/anza-xyz/agave/blob/v2.3.7/runtime/src/stake_weighted_timestamp.rs#L46-L53 */
     257         111 :     ts_eles[ ts_ele_cnt ] = (ts_est_ele_t){
     258         111 :       .timestamp = estimate,
     259         111 :       .stake     = { .ud=stake_t_2 },
     260         111 :     };
     261         111 :     ts_ele_cnt++;
     262             : 
     263             :     /* https://github.com/anza-xyz/agave/blob/v2.3.7/runtime/src/stake_weighted_timestamp.rs#L54 */
     264         111 :     total_stake += stake_t_2;
     265         111 :   }
     266             : 
     267          48 :   *total_stake_out = total_stake;
     268          48 :   *ts_ele_cnt_out  = ts_ele_cnt;
     269          48 : }
     270             : 
     271             : /* get_timestamp_estimate calculates a timestamp estimate.  Does not
     272             :    modify the slot context.  Walks all cached vote accounts (from the
     273             :    "bank") and calculates a unix timestamp estimate. Returns the
     274             :    timestamp estimate.  spad is used for scratch allocations (allocates
     275             :    a treap of size FD_SYSVAR_CLOCK_STAKE_WEIGHTS_MAX). Crashes the
     276             :    process with FD_LOG_ERR on failure (e.g. too many vote accounts).
     277             : 
     278             :   https://github.com/anza-xyz/agave/blob/v2.3.7/runtime/src/bank.rs#L2563-L2601 */
     279             : static long
     280             : get_timestamp_estimate( fd_bank_t *             bank,
     281             :                         fd_accdb_t *            accdb,
     282             :                         fd_sol_sysvar_clock_t * clock,
     283             :                         fd_runtime_stack_t *    runtime_stack,
     284             :                         ulong const *           parent_epoch,
     285        3792 :                         int *                   out_estimate_present ) {
     286        3792 :   fd_epoch_schedule_t const * epoch_schedule = &bank->f.epoch_schedule;
     287        3792 :   ulong                       slot_duration  = bank->f.ns_per_slot.ul[0];
     288        3792 :   ulong                       current_slot   = bank->f.slot;
     289             : 
     290        3792 :   ts_est_ele_t * ts_eles = runtime_stack->clock_ts.staked_ts;
     291             : 
     292             :   /* https://github.com/anza-xyz/agave/blob/v2.3.7/runtime/src/stake_weighted_timestamp.rs#L41 */
     293        3792 :   ulong  ts_ele_cnt   = 0UL;
     294        3792 :   uint128 total_stake = 0UL;
     295             : 
     296             :   /* A timestamp estimate is calculated at every slot using the most
     297             :      recent vote states of voting validators. This estimated is based on
     298             :      a stake weighted median using the stake as of the end of epoch E-2
     299             :      if we are currently in epoch E. We do not count vote accounts that
     300             :      have not voted in an epoch's worth of slots (432k). */
     301             : 
     302        3792 :   ulong curr_epoch = fd_slot_to_epoch( epoch_schedule, bank->f.slot, NULL );
     303        3792 :   ulong vat_epoch  = fd_slot_to_epoch( epoch_schedule, bank->f.features.validator_admission_ticket, NULL );
     304             : 
     305        3792 :   if( curr_epoch>=vat_epoch+1UL ) {
     306          48 :     accum_vote_stakes_vat( bank, runtime_stack, &total_stake, &ts_ele_cnt );
     307        3744 :   } else {
     308        3744 :     accum_vote_stakes_no_vat( bank, accdb, runtime_stack, &total_stake, &ts_ele_cnt );
     309        3744 :   }
     310             : 
     311             :   /* https://github.com/anza-xyz/agave/blob/v2.3.7/runtime/src/stake_weighted_timestamp.rs#L56-L58 */
     312        3792 :   if( FD_UNLIKELY( total_stake==0UL ) ) {
     313         195 :     *out_estimate_present = 0;
     314         195 :     return 0L;
     315         195 :   }
     316             : 
     317        3597 :   sort_stake_ts_inplace( ts_eles, ts_ele_cnt );
     318             : 
     319             :   /* Populate estimate with the stake-weighted median timestamp.
     320             :      https://github.com/anza-xyz/agave/blob/v2.3.7/runtime/src/stake_weighted_timestamp.rs#L59-L68 */
     321        3597 :   uint128 stake_accumulator = 0;
     322        3597 :   long    estimate          = 0L;
     323        3726 :   for( ulong i=0UL; i<ts_ele_cnt; i++ ) {
     324        3726 :     stake_accumulator = fd_uint128_sat_add( stake_accumulator, ts_eles[i].stake.ud );
     325        3726 :     if( stake_accumulator>(total_stake/2UL) ) {
     326        3597 :       estimate = ts_eles[ i ].timestamp;
     327        3597 :       break;
     328        3597 :     }
     329        3726 :   }
     330             : 
     331             :   /* Bound estimate by `max_allowable_drift` since the start of the
     332             :      epoch.
     333             :      https://github.com/anza-xyz/agave/blob/v2.3.7/runtime/src/stake_weighted_timestamp.rs#L69-L99 */
     334        3597 :   ulong epoch_for_start_slot  = parent_epoch ? *parent_epoch : curr_epoch;
     335        3597 :   ulong epoch_start_slot      = fd_epoch_slot0( epoch_schedule, epoch_for_start_slot );
     336        3597 :   long  epoch_start_timestamp = clock->epoch_start_timestamp;
     337             : 
     338             :   /* https://github.com/anza-xyz/agave/blob/v2.3.7/runtime/src/stake_weighted_timestamp.rs#L71-L72 */
     339        3597 :   ulong poh_estimate_offset = fd_ulong_sat_mul( slot_duration, fd_ulong_sat_sub( current_slot, epoch_start_slot ) );
     340             : 
     341             :   /* https://github.com/anza-xyz/agave/blob/v2.3.7/runtime/src/stake_weighted_timestamp.rs#L73-L77 */
     342        3597 :   ulong estimate_offset = fd_ulong_sat_mul( NS_IN_S, fd_ulong_sat_sub( (ulong)estimate, (ulong)epoch_start_timestamp ) );
     343             : 
     344             :   /* https://github.com/anza-xyz/agave/blob/v2.3.7/runtime/src/stake_weighted_timestamp.rs#L78-L81 */
     345        3597 :   ulong max_allowable_drift_fast = fd_ulong_sat_mul( poh_estimate_offset, MAX_ALLOWABLE_DRIFT_FAST_PERCENT ) / 100UL;
     346        3597 :   ulong max_allowable_drift_slow = fd_ulong_sat_mul( poh_estimate_offset, MAX_ALLOWABLE_DRIFT_SLOW_PERCENT ) / 100UL;
     347             : 
     348             :   /* https://github.com/anza-xyz/agave/blob/v2.3.7/runtime/src/stake_weighted_timestamp.rs#L82-L98 */
     349        3597 :   if( estimate_offset>poh_estimate_offset && fd_ulong_sat_sub( estimate_offset, poh_estimate_offset )>max_allowable_drift_slow ) {
     350          45 :     estimate = fd_long_sat_add(
     351          45 :         epoch_start_timestamp,
     352          45 :         fd_long_sat_add( (long)poh_estimate_offset / NS_IN_S, (long)max_allowable_drift_slow / NS_IN_S ) );
     353        3552 :   } else if( estimate_offset<poh_estimate_offset && fd_ulong_sat_sub( poh_estimate_offset, estimate_offset )>max_allowable_drift_fast ) {
     354          33 :     estimate = fd_long_sat_sub(
     355          33 :         fd_long_sat_add( epoch_start_timestamp, (long)poh_estimate_offset / NS_IN_S ),
     356          33 :         (long)max_allowable_drift_fast / NS_IN_S );
     357          33 :   }
     358             : 
     359        3597 :   *out_estimate_present = 1;
     360        3597 :   return estimate;
     361        3792 : }
     362             : 
     363             : /* TODO: This function should be called from genesis bootup as well with
     364             :    parent_epoch = NULL
     365             :    https://github.com/anza-xyz/agave/blob/v2.3.7/runtime/src/bank.rs#L2158-L2215 */
     366             : void
     367             : fd_sysvar_clock_update( fd_bank_t *          bank,
     368             :                         fd_accdb_t *         accdb,
     369             :                         fd_capture_ctx_t *   capture_ctx,
     370             :                         fd_runtime_stack_t * runtime_stack,
     371        3792 :                         ulong const *        parent_epoch ) {
     372        3792 :   fd_sol_sysvar_clock_t clock_[1];
     373        3792 :   fd_sol_sysvar_clock_t * clock = fd_sysvar_clock_read( accdb, bank->accdb_fork_id, clock_ );
     374        3792 :   if( FD_UNLIKELY( !clock ) ) FD_LOG_ERR(( "fd_sysvar_clock_read failed" ));
     375             : 
     376        3792 :   fd_epoch_schedule_t const * epoch_schedule = &bank->f.epoch_schedule;
     377        3792 :   ulong                       current_slot   = bank->f.slot;
     378        3792 :   ulong                       current_epoch  = fd_slot_to_epoch( epoch_schedule, current_slot, NULL );
     379             : 
     380             :   /* https://github.com/anza-xyz/agave/blob/v2.3.7/runtime/src/bank.rs#L2159 */
     381        3792 :   long unix_timestamp = clock->unix_timestamp;
     382             : 
     383             :   /* https://github.com/anza-xyz/agave/blob/v2.3.7/runtime/src/bank.rs#L2175 */
     384        3792 :   long ancestor_timestamp = clock->unix_timestamp;
     385             : 
     386             :   /* TODO: Are we handling slot 0 correctly?
     387             :      https://github.com/anza-xyz/agave/blob/v2.3.7/runtime/src/bank.rs#L2176-L2183 */
     388        3792 :   int  estimate_present   = 0;
     389        3792 :   long timestamp_estimate = get_timestamp_estimate( bank, accdb, clock, runtime_stack, parent_epoch, &estimate_present );
     390             : 
     391             :   /* If the timestamp was successfully calculated, use it. Otherwise,
     392             :      keep the old one. */
     393        3792 :   if( FD_LIKELY( estimate_present ) ) {
     394        3597 :     unix_timestamp = timestamp_estimate;
     395             : 
     396             :     /* https://github.com/anza-xyz/agave/blob/v2.3.7/runtime/src/bank.rs#L2180-L2182 */
     397        3597 :     if( timestamp_estimate<ancestor_timestamp ) {
     398           6 :       unix_timestamp = ancestor_timestamp;
     399           6 :     }
     400        3597 :   }
     401             : 
     402             :   /* https://github.com/anza-xyz/agave/blob/v2.3.7/runtime/src/bank.rs#L2191-L2197 */
     403        3792 :   long epoch_start_timestamp = (parent_epoch!=NULL && *parent_epoch!=current_epoch) ?
     404         195 :       unix_timestamp :
     405        3792 :       clock->epoch_start_timestamp;
     406             : 
     407             :   /* https://github.com/anza-xyz/agave/blob/v2.3.7/runtime/src/bank.rs#L2198-L2201 */
     408        3792 :   if( FD_UNLIKELY( current_slot==0UL ) ) {
     409           0 :     long timestamp_from_genesis = unix_timestamp_from_genesis( bank );
     410           0 :     unix_timestamp              = timestamp_from_genesis;
     411           0 :     epoch_start_timestamp       = timestamp_from_genesis;
     412           0 :   }
     413             : 
     414             :   /* https://github.com/anza-xyz/agave/blob/v2.3.7/runtime/src/bank.rs#L2202-L2208 */
     415        3792 :   *clock = (fd_sol_sysvar_clock_t){
     416        3792 :     .slot                  = current_slot,
     417        3792 :     .epoch_start_timestamp = epoch_start_timestamp,
     418        3792 :     .epoch                 = current_epoch,
     419        3792 :     .leader_schedule_epoch = fd_slot_to_leader_schedule_epoch( epoch_schedule, current_slot ),
     420        3792 :     .unix_timestamp        = unix_timestamp,
     421        3792 :   };
     422             : 
     423             :   /* https://github.com/anza-xyz/agave/blob/v2.3.7/runtime/src/bank.rs#L2209-L2214 */
     424        3792 :   fd_sysvar_clock_write( bank, accdb, capture_ctx, clock );
     425        3792 : }

Generated by: LCOV version 1.14