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

Generated by: LCOV version 1.14