LCOV - code coverage report
Current view: top level - flamenco/runtime/sysvar - fd_sysvar_clock.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 148 251 59.0 %
Date: 2024-11-13 11:58:15 Functions: 8 9 88.9 %

          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_sysvar.h"
       5             : #include "../fd_executor.h"
       6             : #include "../fd_acc_mgr.h"
       7             : #include "../fd_system_ids.h"
       8             : #include "../context/fd_exec_epoch_ctx.h"
       9             : #include "../context/fd_exec_slot_ctx.h"
      10             : 
      11             : /* https://github.com/solana-labs/solana/blob/8f2c8b8388a495d2728909e30460aa40dcc5d733/runtime/src/stake_weighted_timestamp.rs#L14 */
      12        8634 : #define MAX_ALLOWABLE_DRIFT_FAST ( 25 )
      13             : 
      14             : /* https://github.com/solana-labs/solana/blob/8f2c8b8388a495d2728909e30460aa40dcc5d733/runtime/src/stake_weighted_timestamp.rs#L16 */
      15        8634 : #define MAX_ALLOWABLE_DRIFT_SLOW ( 150 )
      16             : 
      17             : /* Do all intermediate calculations at nanosecond precision, to mirror Solana's behaviour. */
      18       25902 : #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       17268 : timestamp_from_genesis( fd_exec_slot_ctx_t * slot_ctx ) {
      28       17268 :   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       17268 :   FD_LOG_INFO(("slot %lu", slot_ctx->slot_bank.slot));
      31       17268 :   return (long)( epoch_bank->genesis_creation_time + ( ( slot_ctx->slot_bank.slot * epoch_bank->ns_per_slot ) / NS_IN_S ) );
      32       17268 : }
      33             : 
      34             : static void
      35             : write_clock( fd_exec_slot_ctx_t *    slot_ctx,
      36        8634 :              fd_sol_sysvar_clock_t * clock ) {
      37        8634 :   ulong sz = fd_sol_sysvar_clock_size( clock );
      38        8634 :   uchar enc[sz];
      39        8634 :   memset( enc, 0, sz );
      40        8634 :   fd_bincode_encode_ctx_t ctx;
      41        8634 :   ctx.data = enc;
      42        8634 :   ctx.dataend = enc + sz;
      43        8634 :   if( fd_sol_sysvar_clock_encode( clock, &ctx ) )
      44           0 :     FD_LOG_ERR(("fd_sol_sysvar_clock_encode failed"));
      45             : 
      46        8634 :   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        8634 : }
      48             : 
      49             : 
      50             : fd_sol_sysvar_clock_t *
      51             : fd_sysvar_clock_read( fd_sol_sysvar_clock_t *    result,
      52       16116 :                       fd_exec_slot_ctx_t const * slot_ctx  ) {
      53       16116 :   fd_sol_sysvar_clock_t const * ret = fd_sysvar_cache_clock( slot_ctx->sysvar_cache );
      54       16116 :   if( NULL != ret ) {
      55        2190 :     fd_memcpy(result, ret, sizeof(fd_sol_sysvar_clock_t));
      56        2190 :     return result;
      57        2190 :   }
      58             : 
      59       13926 :   FD_BORROWED_ACCOUNT_DECL(acc);
      60       13926 :   int rc = fd_acc_mgr_view( slot_ctx->acc_mgr, slot_ctx->funk_txn, &fd_sysvar_clock_id, acc );
      61       13926 :   if( FD_UNLIKELY( rc!=FD_ACC_MGR_SUCCESS ) )
      62           0 :     return NULL;
      63             : 
      64       13926 :   fd_bincode_decode_ctx_t ctx =
      65       13926 :     { .data    = acc->const_data,
      66       13926 :       .dataend = acc->const_data + acc->const_meta->dlen,
      67       13926 :       .valloc  = {0}  /* valloc not required */ };
      68             : 
      69       13926 :   if( FD_UNLIKELY( fd_sol_sysvar_clock_decode( result, &ctx )!=FD_BINCODE_SUCCESS ) )
      70           0 :     return NULL;
      71       13926 :   return result;
      72       13926 : }
      73             : 
      74             : void
      75        8634 : fd_sysvar_clock_init( fd_exec_slot_ctx_t * slot_ctx ) {
      76        8634 :   long timestamp = timestamp_from_genesis( slot_ctx );
      77             : 
      78        8634 :   fd_sol_sysvar_clock_t clock = {
      79        8634 :     .slot = slot_ctx->slot_bank.slot,
      80        8634 :     .epoch = 0,
      81        8634 :     .epoch_start_timestamp = timestamp,
      82        8634 :     .leader_schedule_epoch = 1,
      83        8634 :     .unix_timestamp = timestamp,
      84        8634 :   };
      85        8634 :   write_clock( slot_ctx, &clock );
      86        8634 : }
      87             : 
      88             : /* Bounds the timestamp estimate by the max allowable drift from the expected PoH slot duration.
      89             : 
      90             : https://github.com/solana-labs/solana/blob/8f2c8b8388a495d2728909e30460aa40dcc5d733/runtime/src/stake_weighted_timestamp.rs#L67 */
      91             : static long
      92             : bound_timestamp_estimate( fd_exec_slot_ctx_t * slot_ctx,
      93             :                           long                 estimate,
      94        8634 :                           long                 epoch_start_timestamp ) {
      95             : 
      96             :   /* Determine offsets from start of epoch */
      97             :   /* TODO: handle epoch boundary case */
      98        8634 :   fd_epoch_bank_t * epoch_bank = fd_exec_epoch_ctx_epoch_bank( slot_ctx->epoch_ctx );
      99        8634 :   uint128 poh_estimate_offset = epoch_bank->ns_per_slot * slot_ctx->slot_bank.slot;
     100        8634 :   uint128 estimate_offset = (uint128)( ( estimate - epoch_start_timestamp ) * NS_IN_S );
     101             : 
     102        8634 :   uint128 max_delta_fast = ( poh_estimate_offset * MAX_ALLOWABLE_DRIFT_FAST ) / 100;
     103        8634 :   uint128 max_delta_slow = ( poh_estimate_offset * MAX_ALLOWABLE_DRIFT_SLOW ) / 100;
     104             : 
     105        8634 :   if ( ( estimate_offset > poh_estimate_offset ) && ( ( estimate_offset - poh_estimate_offset ) > max_delta_slow ) ) {
     106           0 :     return epoch_start_timestamp + (long)( poh_estimate_offset / NS_IN_S ) + (long)( max_delta_slow / NS_IN_S );
     107        8634 :   } else if ( ( estimate_offset < poh_estimate_offset ) && ( ( poh_estimate_offset - estimate_offset ) > max_delta_fast ) ) {
     108           0 :     return epoch_start_timestamp + (long)( poh_estimate_offset / NS_IN_S ) + (long)( max_delta_fast / NS_IN_S );
     109           0 :   }
     110             : 
     111        8634 :   return estimate;
     112        8634 : }
     113             : 
     114             : /* Estimates the current timestamp, using the stake-weighted median of the latest validator timestamp oracle votes received
     115             :    from each voting node:
     116             :    https://github.com/solana-labs/solana/blob/8f2c8b8388a495d2728909e30460aa40dcc5d733/runtime/src/bank.rs#L2927
     117             : 
     118             :    Linear interpolation, using the target duration of a slot, is used to calculate the timestamp estimate for the current slot:
     119             : 
     120             :     timestamp = (stake-weighted median of vote timestamps) + ((target slot duration) * (slots since median timestamp vote was received))
     121             :  */
     122             : static long
     123        8634 : estimate_timestamp( fd_exec_slot_ctx_t * slot_ctx ) {
     124             :   /* TODO: bound the estimate to ensure it stays within a certain range of the expected PoH clock:
     125             :   https://github.com/solana-labs/solana/blob/8f2c8b8388a495d2728909e30460aa40dcc5d733/runtime/src/stake_weighted_timestamp.rs#L13 */
     126             : 
     127        8634 :   fd_clock_timestamp_vote_t_mapnode_t * votes = slot_ctx->slot_bank.timestamp_votes.votes_root;
     128        8634 :   if ( NULL == votes ) {
     129        8634 :     return timestamp_from_genesis( slot_ctx );
     130        8634 :   }
     131             : 
     132             :   /* TODO: actually take the stake-weighted median. For now, just use the root node. */
     133           0 :   fd_clock_timestamp_vote_t * head = &votes->elem;
     134           0 :   ulong slots = slot_ctx->slot_bank.slot - head->slot;
     135           0 :   fd_epoch_bank_t * epoch_bank = fd_exec_epoch_ctx_epoch_bank( slot_ctx->epoch_ctx );
     136           0 :   uint128 ns_correction = epoch_bank->ns_per_slot * slots;
     137           0 :   return head->timestamp  + (long) (ns_correction / NS_IN_S) ;
     138        8634 : }
     139             : 
     140           0 : #define CIDX_T ulong
     141             : #define VAL_T  long
     142             : struct stake_ts_ele {
     143             :   CIDX_T parent_cidx;
     144             :   CIDX_T left_cidx;
     145             :   CIDX_T right_cidx;
     146             :   CIDX_T prio_cidx;
     147             :   VAL_T timestamp;
     148             :   unsigned long stake;
     149             : };
     150             : 
     151             : typedef struct stake_ts_ele stake_ts_ele_t;
     152             : 
     153             : #define POOL_NAME  stake_ts_pool
     154       27852 : #define POOL_T     stake_ts_ele_t
     155             : #define POOL_IDX_T CIDX_T
     156   142602240 : #define POOL_NEXT  parent_cidx
     157             : #include "../../../util/tmpl/fd_pool.c"
     158             : 
     159           0 : FD_FN_CONST static inline int valcmp (VAL_T a, VAL_T b) {
     160           0 :   int val = (a < b) ? -1 : 1;
     161           0 :   return (a == b) ? 0 : val;
     162           0 : }
     163             : 
     164             : #define TREAP_NAME       stake_ts_treap
     165             : #define TREAP_T          stake_ts_ele_t
     166             : #define TREAP_QUERY_T    VAL_T
     167           0 : #define TREAP_CMP(q,e)   valcmp(q, e->timestamp)
     168           0 : #define TREAP_LT(e0,e1)  (((VAL_T)((e0)->timestamp)) < ((VAL_T)((e1)->timestamp)))
     169           0 : #define TREAP_IDX_T      CIDX_T
     170           0 : #define TREAP_PARENT     parent_cidx
     171           0 : #define TREAP_LEFT       left_cidx
     172           0 : #define TREAP_RIGHT      right_cidx
     173           0 : #define TREAP_PRIO       prio_cidx
     174             : #define TREAP_IMPL_STYLE 0
     175             : #include "../../../util/tmpl/fd_treap.c"
     176             : 
     177             : /* https://github.com/solana-labs/solana/blob/c091fd3da8014c0ef83b626318018f238f506435/runtime/src/bank.rs#L3600 */
     178             : static void
     179             : fd_calculate_stake_weighted_timestamp(
     180             :   fd_exec_slot_ctx_t * slot_ctx,
     181             :   long * result_timestamp,
     182             :   uint fix_estimate_into_u64
     183       13926 :  ) {
     184       13926 :   FD_SCRATCH_SCOPE_BEGIN {
     185       13926 :   fd_epoch_bank_t * epoch_bank = fd_exec_epoch_ctx_epoch_bank( slot_ctx->epoch_ctx );
     186       13926 :   ulong slot_duration = (ulong)( epoch_bank->ns_per_slot );
     187       13926 :   fd_sol_sysvar_clock_t clock;
     188       13926 :   fd_sysvar_clock_read( &clock, slot_ctx );
     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       13926 :   stake_ts_treap_t   _treap[1];
     195       13926 :   stake_ts_treap_t * treap    = stake_ts_treap_join( stake_ts_treap_new( _treap, 10240UL ) );
     196       13926 :   uchar *            pool_mem = fd_scratch_alloc( stake_ts_pool_align(), stake_ts_pool_footprint( 10240UL ) );
     197       13926 :   stake_ts_ele_t *   pool     = stake_ts_pool_join( stake_ts_pool_new( pool_mem, 10240UL ) );
     198       13926 :   fd_rng_t           _rng[1];
     199       13926 :   fd_rng_t *         rng      = fd_rng_join( fd_rng_new( _rng, (uint)slot_ctx->slot_bank.transaction_count, 0UL ) );
     200             : 
     201       13926 :   ulong total_stake = 0;
     202             : 
     203       13926 :   fd_clock_timestamp_vote_t_mapnode_t * timestamp_votes_root = slot_ctx->slot_bank.timestamp_votes.votes_root;
     204       13926 :   fd_clock_timestamp_vote_t_mapnode_t * timestamp_votes_pool = slot_ctx->slot_bank.timestamp_votes.votes_pool;
     205       13926 :   fd_vote_accounts_pair_t_mapnode_t * vote_acc_root = slot_ctx->slot_bank.epoch_stakes.vote_accounts_root;
     206       13926 :   fd_vote_accounts_pair_t_mapnode_t * vote_acc_pool = slot_ctx->slot_bank.epoch_stakes.vote_accounts_pool;
     207       13926 :   for (
     208       13926 :     fd_vote_accounts_pair_t_mapnode_t* n = fd_vote_accounts_pair_t_map_minimum(vote_acc_pool, vote_acc_root);
     209       13926 :     n;
     210       13926 :     n = fd_vote_accounts_pair_t_map_successor(vote_acc_pool, n)
     211       13926 :   ) {
     212             : 
     213             :     /* get timestamp */
     214           0 :     fd_pubkey_t const * vote_pubkey = &n->elem.key;
     215             : 
     216           0 :     if( timestamp_votes_pool == NULL ) {
     217           0 :       continue;
     218           0 :     } else {
     219           0 :       fd_clock_timestamp_vote_t_mapnode_t query_vote_acc_node;
     220           0 :       query_vote_acc_node.elem.pubkey = *vote_pubkey;
     221           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);
     222           0 :       ulong vote_timestamp;
     223           0 :       ulong vote_slot;
     224           0 :       if( vote_acc_node == NULL ) {
     225           0 :         vote_timestamp = (ulong)n->elem.value.last_timestamp_ts;
     226           0 :         vote_slot = n->elem.value.last_timestamp_slot;
     227           0 :       } else {
     228           0 :         vote_timestamp = (ulong)vote_acc_node->elem.timestamp;
     229           0 :         vote_slot = vote_acc_node->elem.slot;
     230           0 :       }
     231             : 
     232           0 :       ulong slot_delta = fd_ulong_sat_sub(slot_ctx->slot_bank.slot, vote_slot);
     233           0 :       fd_epoch_bank_t * epoch_bank = fd_exec_epoch_ctx_epoch_bank( slot_ctx->epoch_ctx );
     234           0 :       if (slot_delta > epoch_bank->epoch_schedule.slots_per_epoch) {
     235           0 :         continue;
     236           0 :       }
     237             : 
     238           0 :       ulong offset = fd_ulong_sat_mul(slot_duration, slot_delta);
     239           0 :       long estimate = (long)vote_timestamp + (long)(offset / NS_IN_S);
     240             :       /* get stake */
     241           0 :       total_stake += n->elem.stake;
     242           0 :       ulong treap_idx = stake_ts_treap_idx_query( treap, estimate, pool );
     243           0 :       if ( FD_LIKELY( treap_idx < ULONG_MAX ) ) {
     244           0 :         pool[ treap_idx ].stake += n->elem.stake;
     245           0 :       } else {
     246           0 :         if( 0 == stake_ts_pool_free( pool ) ) {
     247           0 :           FD_LOG_ERR(( "stake_ts_pool is empty" ));
     248           0 :         }
     249           0 :         ulong idx = stake_ts_pool_idx_acquire( pool );
     250           0 :         pool[ idx ].prio_cidx = fd_rng_ulong( rng );
     251           0 :         pool[ idx ].timestamp = estimate;
     252           0 :         pool[ idx ].stake     = n->elem.stake;
     253           0 :         stake_ts_treap_idx_insert( treap, idx, pool );
     254           0 :       }
     255           0 :     }
     256           0 :   }
     257             : 
     258       13926 :   *result_timestamp = 0;
     259       13926 :   if (total_stake == 0) {
     260       13926 :     return;
     261       13926 :   }
     262             : 
     263             :   // FIXME: this should be a uint128
     264           0 :   ulong stake_accumulator = 0;
     265           0 :   for (stake_ts_treap_fwd_iter_t iter = stake_ts_treap_fwd_iter_init ( treap, pool);
     266           0 :        !stake_ts_treap_fwd_iter_done( iter );
     267           0 :        iter = stake_ts_treap_fwd_iter_next( iter, pool ) ) {
     268           0 :     ulong idx = stake_ts_treap_fwd_iter_idx( iter );
     269           0 :     stake_accumulator = fd_ulong_sat_add(stake_accumulator, pool[ idx ].stake);
     270           0 :     if (stake_accumulator > (total_stake / 2)) {
     271           0 :       *result_timestamp = pool[ idx ].timestamp;
     272           0 :       break;
     273           0 :     }
     274           0 :   }
     275             : 
     276           0 :   FD_LOG_DEBUG(( "stake weighted timestamp: %ld total stake %lu", *result_timestamp, total_stake ));
     277             : 
     278             :   // Bound estimate by `max_allowable_drift` since the start of the epoch
     279           0 :   fd_epoch_schedule_t schedule = slot_ctx->epoch_ctx->epoch_bank.epoch_schedule;
     280           0 :   ulong epoch_start_slot = fd_epoch_slot0( &schedule, clock.epoch );
     281           0 :   FD_LOG_DEBUG(("Epoch start slot %lu", epoch_start_slot));
     282           0 :   ulong poh_estimate_offset = fd_ulong_sat_mul(slot_duration, fd_ulong_sat_sub(slot_ctx->slot_bank.slot, epoch_start_slot));
     283           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));
     284           0 :   ulong max_delta_fast = fd_ulong_sat_mul(poh_estimate_offset, MAX_ALLOWABLE_DRIFT_FAST) / 100;
     285           0 :   ulong max_delta_slow = fd_ulong_sat_mul(poh_estimate_offset, MAX_ALLOWABLE_DRIFT_SLOW) / 100;
     286           0 :   FD_LOG_DEBUG(("poh offset %lu estimate %lu fast %lu slow %lu", poh_estimate_offset, estimate_offset, max_delta_fast, max_delta_slow));
     287           0 :   if (estimate_offset > poh_estimate_offset && fd_ulong_sat_sub(estimate_offset, poh_estimate_offset) > max_delta_slow) {
     288           0 :     *result_timestamp = clock.epoch_start_timestamp + (long)poh_estimate_offset / NS_IN_S + (long)max_delta_slow / NS_IN_S;
     289           0 :   } else if (estimate_offset < poh_estimate_offset && fd_ulong_sat_sub(poh_estimate_offset, estimate_offset) > max_delta_fast) {
     290           0 :     *result_timestamp = clock.epoch_start_timestamp + (long)poh_estimate_offset / NS_IN_S - (long)max_delta_fast / NS_IN_S;
     291           0 :   }
     292             : 
     293           0 :   FD_LOG_DEBUG(( "corrected stake weighted timestamp: %ld", *result_timestamp ));
     294             : 
     295           0 :   if (*result_timestamp < clock.unix_timestamp) {
     296           0 :     FD_LOG_DEBUG(( "updated timestamp to ancestor" ));
     297           0 :     *result_timestamp = clock.unix_timestamp;
     298           0 :   }
     299           0 :   return;
     300             : 
     301       13926 :   }
     302       13926 :   FD_SCRATCH_SCOPE_END;
     303       13926 : }
     304             : 
     305             : int
     306        8634 : fd_sysvar_clock_update( fd_exec_slot_ctx_t * slot_ctx ) {
     307             : 
     308        8634 :   fd_pubkey_t const * key = &fd_sysvar_clock_id;
     309             : 
     310        8634 :   FD_BORROWED_ACCOUNT_DECL(rec);
     311        8634 :   int err = fd_acc_mgr_view( slot_ctx->acc_mgr, slot_ctx->funk_txn, key, rec);
     312        8634 :   if (err)
     313           0 :     FD_LOG_CRIT(( "fd_acc_mgr_view(clock) failed: %d", err ));
     314             : 
     315        8634 :   fd_bincode_decode_ctx_t ctx;
     316        8634 :   ctx.data    = rec->const_data;
     317        8634 :   ctx.dataend = rec->const_data + rec->const_meta->dlen;
     318        8634 :   ctx.valloc  = slot_ctx->valloc;
     319        8634 :   fd_sol_sysvar_clock_t clock;
     320        8634 :   if( fd_sol_sysvar_clock_decode( &clock, &ctx ) )
     321           0 :     FD_LOG_ERR(("fd_sol_sysvar_clock_decode failed"));
     322             : 
     323        8634 :   long ancestor_timestamp = clock.unix_timestamp;
     324             : 
     325        8634 :   if (slot_ctx->slot_bank.slot != 0) {
     326        8634 :     fd_calculate_stake_weighted_timestamp(slot_ctx, &clock.unix_timestamp, FD_FEATURE_ACTIVE( slot_ctx, warp_timestamp_again ) );
     327        8634 :   }
     328             : 
     329        8634 :   if (0 == clock.unix_timestamp) {
     330             :     /* generate timestamp for genesis */
     331        8634 :     long timestamp_estimate         = estimate_timestamp( slot_ctx );
     332        8634 :     long bounded_timestamp_estimate = bound_timestamp_estimate( slot_ctx, timestamp_estimate, clock.epoch_start_timestamp );
     333        8634 :     if ( timestamp_estimate != bounded_timestamp_estimate ) {
     334           0 :       FD_LOG_INFO(( "corrected timestamp_estimate %ld to %ld", timestamp_estimate, bounded_timestamp_estimate ));
     335           0 :     }
     336             :     /*  if let Some(timestamp_estimate) =
     337             :             self.get_timestamp_estimate(max_allowable_drift, epoch_start_timestamp)
     338             :         {
     339             :             unix_timestamp = timestamp_estimate;
     340             :             if timestamp_estimate < ancestor_timestamp {
     341             :                 unix_timestamp = ancestor_timestamp;
     342             :             }
     343             :         } */
     344        8634 :     if( bounded_timestamp_estimate < ancestor_timestamp ) {
     345           0 :       FD_LOG_DEBUG(( "clock rewind detected: %ld -> %ld", ancestor_timestamp, bounded_timestamp_estimate ));
     346           0 :       bounded_timestamp_estimate = ancestor_timestamp;
     347           0 :     }
     348        8634 :     clock.unix_timestamp = bounded_timestamp_estimate;
     349        8634 :   }
     350             : 
     351        8634 :   clock.slot  = slot_ctx->slot_bank.slot;
     352             : 
     353        8634 :   ulong epoch_old = clock.epoch;
     354        8634 :   fd_epoch_bank_t * epoch_bank = fd_exec_epoch_ctx_epoch_bank( slot_ctx->epoch_ctx );
     355        8634 :   ulong epoch_new = fd_slot_to_epoch( &epoch_bank->epoch_schedule, clock.slot, NULL );
     356        8634 :   FD_LOG_DEBUG(("Epoch old %lu new %lu slot %lu", epoch_old, epoch_new, clock.slot));
     357        8634 :   clock.epoch = epoch_new;
     358        8634 :   if( epoch_old != epoch_new ) {
     359        5292 :     long timestamp_estimate = 0L;
     360        5292 :     fd_calculate_stake_weighted_timestamp( slot_ctx, &timestamp_estimate, FD_FEATURE_ACTIVE( slot_ctx, warp_timestamp_again ) );
     361        5292 :     clock.unix_timestamp = fd_long_max( timestamp_estimate, ancestor_timestamp );
     362        5292 :     clock.epoch_start_timestamp = clock.unix_timestamp;
     363        5292 :     clock.leader_schedule_epoch = fd_slot_to_leader_schedule_epoch( &epoch_bank->epoch_schedule, slot_ctx->slot_bank.slot );
     364        5292 :   }
     365             : 
     366        8634 :   FD_LOG_DEBUG(( "Updated clock at slot %lu", slot_ctx->slot_bank.slot ));
     367        8634 :   FD_LOG_DEBUG(( "clock.slot: %lu", clock.slot ));
     368        8634 :   FD_LOG_DEBUG(( "clock.epoch_start_timestamp: %ld", clock.epoch_start_timestamp ));
     369        8634 :   FD_LOG_DEBUG(( "clock.epoch: %lu", clock.epoch ));
     370        8634 :   FD_LOG_DEBUG(( "clock.leader_schedule_epoch: %lu", clock.leader_schedule_epoch ));
     371        8634 :   FD_LOG_DEBUG(( "clock.unix_timestamp: %ld", clock.unix_timestamp ));
     372             : 
     373        8634 :   ulong               sz       = fd_sol_sysvar_clock_size(&clock);
     374        8634 :   FD_BORROWED_ACCOUNT_DECL(acc);
     375        8634 :   err = fd_acc_mgr_modify( slot_ctx->acc_mgr, slot_ctx->funk_txn, key, 1, sz, acc);
     376        8634 :   if (err)
     377           0 :     FD_LOG_CRIT(( "fd_acc_mgr_modify(clock) failed: %d", err ));
     378             : 
     379        8634 :   fd_bincode_encode_ctx_t e_ctx = {
     380        8634 :     .data    = acc->data,
     381        8634 :     .dataend = acc->data+sz,
     382        8634 :   };
     383        8634 :   if( fd_sol_sysvar_clock_encode( &clock, &e_ctx ) )
     384           0 :     return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
     385             : 
     386        8634 :   ulong lamps = fd_rent_exempt_minimum_balance( &epoch_bank->rent, sz );
     387        8634 :   if( acc->meta->info.lamports < lamps )
     388           0 :     acc->meta->info.lamports = lamps;
     389             : 
     390        8634 :   acc->meta->dlen = sz;
     391        8634 :   fd_memcpy( acc->meta->info.owner, fd_sysvar_owner_id.key, 32 );
     392             : 
     393        8634 :   return 0;
     394        8634 : }

Generated by: LCOV version 1.14