From 28c10371f2304cfc7097f88e7dbbcafb18873b8e Mon Sep 17 00:00:00 2001 From: Yordan Madzhunkov <52652109+yordanmadzhunkov@users.noreply.github.com> Date: Thu, 11 Nov 2021 02:04:34 +0200 Subject: [PATCH] Mock time for tests (#5123) Co-authored-by: Yordan Madzhunkov Co-authored-by: Bowen Wang --- chain/chain-primitives/src/error.rs | 4 +- chain/chain/src/chain.rs | 8 +- chain/chain/src/doomslug.rs | 68 ++++++-- chain/chain/src/store.rs | 2 +- chain/chain/src/store_validator.rs | 5 +- chain/chain/src/test_utils.rs | 16 +- chain/chain/src/types.rs | 5 +- chain/chain/tests/doomslug.rs | 6 +- chain/chain/tests/simple_chain.rs | 50 ++++++ chain/chunks/src/lib.rs | 21 ++- chain/chunks/src/test_utils.rs | 4 +- chain/client-primitives/src/types.rs | 3 +- chain/client/src/client.rs | 19 +-- chain/client/src/client_actor.rs | 12 +- chain/client/src/info.rs | 7 +- chain/client/src/sync.rs | 20 +-- chain/client/src/test_utils.rs | 13 +- chain/client/src/view_client.rs | 5 +- chain/client/tests/query_client.rs | 2 +- chain/network-primitives/src/types.rs | 7 +- chain/network/src/peer/peer_actor.rs | 11 +- .../src/peer_manager/peer_manager_actor.rs | 16 +- chain/network/src/peer_manager/peer_store.rs | 2 +- chain/network/src/routing/route_back_cache.rs | 11 +- chain/network/src/routing/routing.rs | 9 +- chain/network/src/types.rs | 2 +- chain/network/tests/cache_edges.rs | 3 +- core/primitives/benches/serialization.rs | 4 +- core/primitives/src/block.rs | 6 +- core/primitives/src/block_header.rs | 3 +- core/primitives/src/lib.rs | 1 + core/primitives/src/time.rs | 155 ++++++++++++++++++ core/primitives/src/utils.rs | 9 +- core/primitives/src/views.rs | 6 +- .../genesis-csv-to-json/src/csv_parser.rs | 3 +- .../tests/client/chunks_management.rs | 2 +- .../tests/network/infinite_loop.rs | 2 +- integration-tests/tests/network/runner.rs | 3 +- integration-tests/tests/test_simple.rs | 5 +- nearcore/src/config.rs | 6 +- 40 files changed, 401 insertions(+), 135 deletions(-) create mode 100644 core/primitives/src/time.rs diff --git a/chain/chain-primitives/src/error.rs b/chain/chain-primitives/src/error.rs index 78428aa8d6c..fcba26373e3 100644 --- a/chain/chain-primitives/src/error.rs +++ b/chain/chain-primitives/src/error.rs @@ -1,7 +1,9 @@ use std::fmt::{self, Display}; use std::io; -use chrono::{DateTime, Utc}; +use chrono::DateTime; +use near_primitives::time::Utc; + use failure::{Backtrace, Context, Fail}; use log::error; diff --git a/chain/chain/src/chain.rs b/chain/chain/src/chain.rs index c78c098655f..d16c0543930 100644 --- a/chain/chain/src/chain.rs +++ b/chain/chain/src/chain.rs @@ -4,8 +4,8 @@ use std::time::{Duration as TimeDuration, Instant}; use borsh::BorshSerialize; use chrono::Duration; -use chrono::Utc; use itertools::Itertools; +use near_primitives::time::{Clock, Utc}; use rand::rngs::StdRng; use rand::seq::SliceRandom; use rand::SeedableRng; @@ -476,7 +476,7 @@ impl Chain { self.orphans.add(Orphan { block: block.clone(), provenance: Provenance::NONE, - added: Instant::now(), + added: Clock::instant(), }); Ok(()) } @@ -1145,7 +1145,7 @@ impl Chain { // we only add blocks that couldn't have been gc'ed to the orphan pool. if block_height >= tail_height { let block_hash = *block.hash(); - let orphan = Orphan { block, provenance, added: Instant::now() }; + let orphan = Orphan { block, provenance, added: Clock::instant() }; self.orphans.add(orphan); @@ -1165,7 +1165,7 @@ impl Chain { ErrorKind::ChunksMissing(missing_chunks) => { let block_hash = *block.hash(); block_misses_chunks(missing_chunks.clone()); - let orphan = Orphan { block, provenance, added: Instant::now() }; + let orphan = Orphan { block, provenance, added: Clock::instant() }; self.blocks_with_missing_chunks.add_block_with_missing_chunks( orphan, diff --git a/chain/chain/src/doomslug.rs b/chain/chain/src/doomslug.rs index 547eb21a4f5..9728324344e 100644 --- a/chain/chain/src/doomslug.rs +++ b/chain/chain/src/doomslug.rs @@ -5,6 +5,7 @@ use std::time::{Duration, Instant}; use near_crypto::Signature; use near_primitives::block::{Approval, ApprovalInner}; use near_primitives::hash::CryptoHash; +use near_primitives::time::Clock; use near_primitives::types::{AccountId, ApprovalStake, Balance, BlockHeight, BlockHeightDelta}; use near_primitives::validator_signer::ValidatorSigner; @@ -296,8 +297,8 @@ impl Doomslug { tip: DoomslugTip { block_hash: CryptoHash::default(), height: 0 }, endorsement_pending: false, timer: DoomslugTimer { - started: Instant::now(), - last_endorsement_sent: Instant::now(), + started: Clock::instant(), + last_endorsement_sent: Clock::instant(), height: 0, endorsement_delay, min_delay, @@ -596,11 +597,12 @@ impl Doomslug { #[cfg(test)] mod tests { use std::sync::Arc; - use std::time::{Duration, Instant}; + use std::time::Duration; use near_crypto::{KeyType, SecretKey}; use near_primitives::block::{Approval, ApprovalInner}; use near_primitives::hash::hash; + use near_primitives::time::Clock; use near_primitives::types::{AccountId, ApprovalStake}; use near_primitives::validator_signer::InMemoryValidatorSigner; @@ -625,7 +627,7 @@ mod tests { DoomslugThresholdMode::TwoThirds, ); - let mut now = Instant::now(); // For the test purposes the absolute value of the initial instant doesn't matter + let mut now = Clock::instant(); // For the test purposes the absolute value of the initial instant doesn't matter // Set a new tip, must produce an endorsement ds.set_tip(now, hash(&[1]), 1, 1); @@ -781,7 +783,7 @@ mod tests { DoomslugThresholdMode::TwoThirds, ); - let mut now = Instant::now(); + let mut now = Clock::instant(); // In the comments below the format is // account, height -> approved stake @@ -914,7 +916,12 @@ mod tests { let a2_3 = Approval::new(hash(&[3]), 3, 4, &signers[2]); // Process first approval, and then process it again and make sure it works - tracker.process_approval(Instant::now(), &a1_1, &stakes, DoomslugThresholdMode::TwoThirds); + tracker.process_approval( + Clock::instant(), + &a1_1, + &stakes, + DoomslugThresholdMode::TwoThirds, + ); assert_eq!( tracker @@ -934,7 +941,12 @@ mod tests { 0 ); - tracker.process_approval(Instant::now(), &a1_1, &stakes, DoomslugThresholdMode::TwoThirds); + tracker.process_approval( + Clock::instant(), + &a1_1, + &stakes, + DoomslugThresholdMode::TwoThirds, + ); assert_eq!( tracker @@ -955,8 +967,18 @@ mod tests { ); // Process the remaining two approvals on the first block - tracker.process_approval(Instant::now(), &a1_2, &stakes, DoomslugThresholdMode::TwoThirds); - tracker.process_approval(Instant::now(), &a1_3, &stakes, DoomslugThresholdMode::TwoThirds); + tracker.process_approval( + Clock::instant(), + &a1_2, + &stakes, + DoomslugThresholdMode::TwoThirds, + ); + tracker.process_approval( + Clock::instant(), + &a1_3, + &stakes, + DoomslugThresholdMode::TwoThirds, + ); assert_eq!( tracker @@ -977,7 +999,12 @@ mod tests { ); // Process new approvals one by one, expect the approved and endorsed stake to slowly decrease - tracker.process_approval(Instant::now(), &a2_1, &stakes, DoomslugThresholdMode::TwoThirds); + tracker.process_approval( + Clock::instant(), + &a2_1, + &stakes, + DoomslugThresholdMode::TwoThirds, + ); assert_eq!( tracker @@ -997,7 +1024,12 @@ mod tests { 5 ); - tracker.process_approval(Instant::now(), &a2_2, &stakes, DoomslugThresholdMode::TwoThirds); + tracker.process_approval( + Clock::instant(), + &a2_2, + &stakes, + DoomslugThresholdMode::TwoThirds, + ); assert_eq!( tracker @@ -1018,7 +1050,12 @@ mod tests { ); // As we update the last of the three approvals, the tracker for the first block should be completely removed - tracker.process_approval(Instant::now(), &a2_3, &stakes, DoomslugThresholdMode::TwoThirds); + tracker.process_approval( + Clock::instant(), + &a2_3, + &stakes, + DoomslugThresholdMode::TwoThirds, + ); assert!(tracker.approval_trackers.get(&ApprovalInner::Skip(1)).is_none()); @@ -1043,7 +1080,12 @@ mod tests { 5 ); - tracker.process_approval(Instant::now(), &a2_3, &stakes, DoomslugThresholdMode::TwoThirds); + tracker.process_approval( + Clock::instant(), + &a2_3, + &stakes, + DoomslugThresholdMode::TwoThirds, + ); assert_eq!( tracker diff --git a/chain/chain/src/store.rs b/chain/chain/src/store.rs index 167a787fe5c..a40e2191bda 100644 --- a/chain/chain/src/store.rs +++ b/chain/chain/src/store.rs @@ -5,7 +5,7 @@ use std::sync::Arc; use borsh::{BorshDeserialize, BorshSerialize}; use cached::{Cached, SizedCache}; -use chrono::Utc; +use near_primitives::time::Utc; use near_chain_primitives::error::{Error, ErrorKind}; use near_primitives::block::{Approval, Tip}; diff --git a/chain/chain/src/store_validator.rs b/chain/chain/src/store_validator.rs index 6c37c3659e3..7fa0dfc4dad 100644 --- a/chain/chain/src/store_validator.rs +++ b/chain/chain/src/store_validator.rs @@ -26,6 +26,7 @@ use validate::StoreValidatorError; use crate::RuntimeAdapter; use near_primitives::shard_layout::get_block_shard_uid_rev; +use near_primitives::time::Clock; mod validate; @@ -98,7 +99,7 @@ impl StoreValidator { store: store.clone(), inner: StoreValidatorCache::new(), timeout: None, - start_time: Instant::now(), + start_time: Clock::instant(), errors: vec![], tests: 0, } @@ -345,7 +346,7 @@ impl StoreValidator { Ok(()) } pub fn validate(&mut self) { - self.start_time = Instant::now(); + self.start_time = Clock::instant(); // Init checks // Check Head-Tail validity and fill cache with their values diff --git a/chain/chain/src/test_utils.rs b/chain/chain/src/test_utils.rs index 89b9fb7bd46..ed2c84280a8 100644 --- a/chain/chain/src/test_utils.rs +++ b/chain/chain/src/test_utils.rs @@ -3,7 +3,7 @@ use std::collections::{HashMap, HashSet}; use std::sync::{Arc, RwLock}; use borsh::{BorshDeserialize, BorshSerialize}; -use chrono::Utc; + use num_rational::Rational; use tracing::debug; @@ -56,6 +56,7 @@ use crate::types::{ use crate::Doomslug; use crate::{BlockHeader, DoomslugThresholdMode, RuntimeAdapter}; use near_primitives::epoch_manager::ShardConfig; +use near_primitives::time::Clock; #[derive(BorshSerialize, BorshDeserialize, Hash, PartialEq, Eq, Ord, PartialOrd, Clone, Debug)] struct AccountNonce(AccountId, Nonce); @@ -1220,7 +1221,7 @@ pub fn setup_with_tx_validity_period( let chain = Chain::new( runtime.clone(), &ChainGenesis { - time: Utc::now(), + time: Clock::utc(), height: 0, gas_limit: 1_000_000, min_gas_price: 100, @@ -1267,7 +1268,7 @@ pub fn setup_with_validators( let chain = Chain::new( runtime.clone(), &ChainGenesis { - time: Utc::now(), + time: Clock::utc(), height: 0, gas_limit: 1_000_000, min_gas_price: 100, @@ -1386,7 +1387,7 @@ pub fn display_chain(me: &Option, chain: &mut Chain, tail: bool) { impl ChainGenesis { pub fn test() -> Self { ChainGenesis { - time: Utc::now(), + time: Clock::utc(), height: 0, gas_limit: 1_000_000, min_gas_price: 0, @@ -1402,7 +1403,7 @@ impl ChainGenesis { #[cfg(test)] mod test { - use std::time::Instant; + use std::convert::TryFrom; use borsh::BorshSerialize; use rand::Rng; @@ -1410,6 +1411,7 @@ mod test { use near_primitives::hash::{hash, CryptoHash}; use near_primitives::receipt::Receipt; use near_primitives::sharding::ReceiptList; + use near_primitives::time::Clock; use near_primitives::types::{AccountId, EpochId, NumShards}; use near_store::test_utils::create_test_store; @@ -1459,10 +1461,10 @@ mod test { ) }) .collect::>(); - let start = Instant::now(); + let start = Clock::instant(); let naive_result = runtime_adapter.naive_build_receipt_hashes(&receipts); let naive_duration = start.elapsed(); - let start = Instant::now(); + let start = Clock::instant(); let shard_layout = runtime_adapter.get_shard_layout(&EpochId::default()).unwrap(); let prod_result = runtime_adapter.build_receipts_hashes(&receipts, &shard_layout); let prod_duration = start.elapsed(); diff --git a/chain/chain/src/types.rs b/chain/chain/src/types.rs index f3cdcb55387..dfdd502ef00 100644 --- a/chain/chain/src/types.rs +++ b/chain/chain/src/types.rs @@ -3,7 +3,8 @@ use std::collections::HashMap; use std::sync::Arc; use borsh::{BorshDeserialize, BorshSerialize}; -use chrono::{DateTime, Utc}; +use chrono::DateTime; +use near_primitives::time::Utc; use num_rational::Rational; use serde::Serialize; @@ -800,7 +801,7 @@ pub enum ValidatorInfoIdentifier { #[cfg(test)] mod tests { - use chrono::Utc; + use near_primitives::time::Utc; use near_crypto::KeyType; use near_primitives::block::{genesis_chunks, Approval}; diff --git a/chain/chain/tests/doomslug.rs b/chain/chain/tests/doomslug.rs index 362ab73747e..9c256292df8 100644 --- a/chain/chain/tests/doomslug.rs +++ b/chain/chain/tests/doomslug.rs @@ -1,12 +1,12 @@ #[cfg(test)] #[cfg(feature = "expensive_tests")] mod tests { + use near_primitives::time::Clock; + use rand::{thread_rng, Rng}; use std::collections::{HashMap, HashSet}; use std::sync::Arc; use std::time::{Duration, Instant}; - use rand::{thread_rng, Rng}; - use near_chain::{Doomslug, DoomslugThresholdMode}; use near_crypto::{KeyType, SecretKey}; use near_primitives::block::Approval; @@ -75,7 +75,7 @@ mod tests { }) .collect::>(); - let mut now = Instant::now(); + let mut now = Clock::instant(); let started = now; let gst = now + time_to_gst; diff --git a/chain/chain/tests/simple_chain.rs b/chain/chain/tests/simple_chain.rs index 3e2f4186ef2..42f3d49f69a 100644 --- a/chain/chain/tests/simple_chain.rs +++ b/chain/chain/tests/simple_chain.rs @@ -1,21 +1,57 @@ +use chrono; +use chrono::TimeZone; use near_chain::test_utils::setup; use near_chain::{Block, ChainStoreAccess, ErrorKind, Provenance}; use near_logger_utils::init_test_logger; use near_primitives::hash::CryptoHash; +use near_primitives::time::{Clock, MockClockGuard}; use near_primitives::version::PROTOCOL_VERSION; use num_rational::Rational; +use std::str::FromStr; #[test] fn empty_chain() { init_test_logger(); + let _mock_clock_guard = MockClockGuard::default(); + let now = chrono::Utc.ymd(2020, 10, 1).and_hms_milli(0, 0, 1, 444); + Clock::add_utc(now); + let (chain, _, _) = setup(); + let count_instant = { Clock::instant_call_count() }; + let count_utc = { Clock::utc_call_count() }; + assert_eq!(chain.head().unwrap().height, 0); + let hash = chain.head().unwrap().last_block_hash; + #[cfg(feature = "protocol_feature_block_header_v3")] + assert_eq!(hash, CryptoHash::from_str("ED2pukQzADa3rPKzmSkVV4vA3J6DLASBsU9WRbesKywZ").unwrap()); + #[cfg(not(feature = "protocol_feature_block_header_v3"))] + assert_eq!(hash, CryptoHash::from_str("4Fgb9xxzLcWuY7atxSB9yXxF4BKzTHvXMHrAkk88mEUp").unwrap()); + assert_eq!(count_utc, 1); + assert_eq!(count_instant, 0); } #[test] fn build_chain() { init_test_logger(); + let _mock_clock_guard = MockClockGuard::default(); + for i in 0..5 { + Clock::add_utc(chrono::Utc.ymd(2020, 10, 1).and_hms_milli(0, 0, 3, 444 + i)); + } + let (mut chain, _, signer) = setup(); + + let prev_hash = *chain.head_header().unwrap().hash(); + #[cfg(feature = "protocol_feature_block_header_v3")] + assert_eq!( + prev_hash, + CryptoHash::from_str("Ax1E4j9Yq7AVECEtXrhjEvnMe6XGG1FXYg8jhAZrZrmK").unwrap() + ); + #[cfg(not(feature = "protocol_feature_block_header_v3"))] + assert_eq!( + prev_hash, + CryptoHash::from_str("GZyFPBDNEaowcZRPGm3biD9Yh9rdWuYdR8cRjZBAZwVp").unwrap() + ); + for i in 0..4 { let prev_hash = *chain.head_header().unwrap().hash(); let prev = chain.get_block(&prev_hash).unwrap(); @@ -26,6 +62,20 @@ fn build_chain() { assert_eq!(tip.unwrap().height, i + 1); } assert_eq!(chain.head().unwrap().height, 4); + let count_instant = Clock::instant_call_count(); + let count_utc = Clock::utc_call_count(); + assert_eq!(count_utc, 5); + assert_eq!(count_instant, 0); + #[cfg(feature = "protocol_feature_block_header_v3")] + assert_eq!( + chain.head().unwrap().last_block_hash, + CryptoHash::from_str("8qbYy7tVXGVFBaqoxbVbeQFBJpQBs9iPRCqHdsz6c3Ws").unwrap() + ); + #[cfg(not(feature = "protocol_feature_block_header_v3"))] + assert_eq!( + chain.head().unwrap().last_block_hash, + CryptoHash::from_str("2V3sj9CRWkv1n2q8ByuQdrbMSuVKkcBwsYMJupG6XcDG").unwrap() + ); } #[test] diff --git a/chain/chunks/src/lib.rs b/chain/chunks/src/lib.rs index 1ad3b40a880..0d20529054e 100644 --- a/chain/chunks/src/lib.rs +++ b/chain/chunks/src/lib.rs @@ -5,8 +5,9 @@ use std::time::{Duration, Instant}; use borsh::BorshSerialize; use cached::{Cached, SizedCache}; -use chrono::{DateTime, Utc}; +use chrono::DateTime; use log::{debug, error, warn}; +use near_primitives::time::Utc; use rand::seq::SliceRandom; use near_chain::validate::validate_chunk_proofs; @@ -29,6 +30,7 @@ use near_primitives::sharding::{ PartialEncodedChunkV1, PartialEncodedChunkV2, ReceiptList, ReceiptProof, ReedSolomonWrapper, ShardChunkHeader, ShardProof, }; +use near_primitives::time::Clock; use near_primitives::transaction::SignedTransaction; use near_primitives::types::validator_stake::ValidatorStake; use near_primitives::types::{ @@ -134,7 +136,7 @@ impl RequestPool { continue; } if chunk_request.last_requested.elapsed() > self.retry_duration { - chunk_request.last_requested = Instant::now(); + chunk_request.last_requested = Clock::instant(); requests.push((chunk_hash.clone(), chunk_request.clone())); } } @@ -621,8 +623,8 @@ impl ShardsManager { height, parent_hash, shard_id, - last_requested: Instant::now(), - added: Instant::now(), + last_requested: Clock::instant(), + added: Clock::instant(), }, ); @@ -1722,7 +1724,7 @@ mod test { use near_primitives::version::PROTOCOL_VERSION; use near_store::test_utils::create_test_store; use std::sync::Arc; - use std::time::{Duration, Instant}; + use std::time::Duration; use near_network::NetworkRequests; use near_primitives::block::Tip; @@ -1745,14 +1747,15 @@ mod test { runtime_adapter, network_adapter.clone(), ); + let added = Clock::instant(); shards_manager.requested_partial_encoded_chunks.insert( ChunkHash(hash(&[1])), ChunkRequestInfo { height: 0, parent_hash: Default::default(), shard_id: 0, - added: Instant::now(), - last_requested: Instant::now(), + added: added, + last_requested: added, }, ); std::thread::sleep(Duration::from_millis(2 * CHUNK_REQUEST_RETRY_MS)); @@ -1830,8 +1833,8 @@ mod test { height: header.height_created(), parent_hash: header.prev_block_hash(), shard_id: header.shard_id(), - last_requested: Instant::now(), - added: Instant::now(), + last_requested: Clock::instant(), + added: Clock::instant(), }, ); shards_manager diff --git a/chain/chunks/src/test_utils.rs b/chain/chunks/src/test_utils.rs index c4c369cbe19..009d61f5275 100644 --- a/chain/chunks/src/test_utils.rs +++ b/chain/chunks/src/test_utils.rs @@ -1,6 +1,6 @@ use std::sync::Arc; -use chrono::Utc; +use near_primitives::time::Clock; use near_chain::test_utils::KeyValueRuntime; use near_chain::types::RuntimeAdapter; @@ -67,7 +67,7 @@ impl Default for SealsManagerTestFixture { Default::default(), Default::default(), Default::default(), - Utc::now(), + Clock::utc(), Default::default(), Default::default(), Default::default(), diff --git a/chain/client-primitives/src/types.rs b/chain/client-primitives/src/types.rs index 38b43840f3e..b8c06c02a72 100644 --- a/chain/client-primitives/src/types.rs +++ b/chain/client-primitives/src/types.rs @@ -3,7 +3,8 @@ use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; use actix::Message; -use chrono::{DateTime, Utc}; +use chrono::DateTime; +use near_primitives::time::Utc; use serde::{Deserialize, Serialize}; use near_chain_configs::ProtocolConfigView; diff --git a/chain/client/src/client.rs b/chain/client/src/client.rs index 672db6bf87c..ea2e0372a25 100644 --- a/chain/client/src/client.rs +++ b/chain/client/src/client.rs @@ -7,8 +7,8 @@ use std::sync::{Arc, RwLock}; use std::time::{Duration, Instant}; use cached::{Cached, SizedCache}; -use chrono::Utc; use log::{debug, error, info, warn}; +use near_primitives::time::Clock; use near_chain::chain::{ ApplyStatePartsRequest, BlockCatchUpRequest, BlocksCatchUpState, StateSplitRequest, @@ -158,7 +158,6 @@ impl Client { validator_signer.clone(), doomslug_threshold_mode, ); - Ok(Self { #[cfg(feature = "test_features")] adv_produce_blocks: false, @@ -181,21 +180,21 @@ impl Client { challenges: Default::default(), rs: ReedSolomonWrapper::new(data_parts, parity_parts), rebroadcasted_blocks: SizedCache::with_size(NUM_REBROADCAST_BLOCKS), - last_time_head_progress_made: Instant::now(), + last_time_head_progress_made: Clock::instant(), }) } // Checks if it's been at least `stall_timeout` since the last time the head was updated, or // this method was called. If yes, rebroadcasts the current head. pub fn check_head_progress_stalled(&mut self, stall_timeout: Duration) -> Result<(), Error> { - if Instant::now() > self.last_time_head_progress_made + stall_timeout + if Clock::instant() > self.last_time_head_progress_made + stall_timeout && !self.sync_status.is_syncing() { let block = self.chain.get_block(&self.chain.head()?.last_block_hash)?; self.network_adapter.do_send(PeerManagerMessageRequest::NetworkRequests( NetworkRequests::Block { block: block.clone() }, )); - self.last_time_head_progress_made = Instant::now(); + self.last_time_head_progress_made = Clock::instant(); } Ok(()) } @@ -505,7 +504,7 @@ impl Client { // Update latest known even before returning block out, to prevent race conditions. self.chain.mut_store().save_latest_known(LatestKnown { height: next_height, - seen: to_timestamp(Utc::now()), + seen: to_timestamp(Clock::utc()), })?; near_metrics::inc_counter(&metrics::BLOCK_PRODUCED_TOTAL); @@ -759,7 +758,7 @@ impl Client { } if let Ok(Some(_)) = result { - self.last_time_head_progress_made = Instant::now(); + self.last_time_head_progress_made = Clock::instant(); } let protocol_version = self @@ -926,9 +925,8 @@ impl Client { } else { self.chain.get_block_header(&last_final_hash)?.height() }; - self.doomslug.set_tip( - Instant::now(), + Clock::instant(), tip.last_block_hash, tip.height, last_final_height, @@ -1374,8 +1372,7 @@ impl Client { return; } }; - - self.doomslug.on_approval_message(Instant::now(), &approval, &block_producer_stakes); + self.doomslug.on_approval_message(Clock::instant(), &approval, &block_producer_stakes); } /// Forwards given transaction to upcoming validators. diff --git a/chain/client/src/client_actor.rs b/chain/client/src/client_actor.rs index 3b48ec8a4ce..f1d2aadd8c8 100644 --- a/chain/client/src/client_actor.rs +++ b/chain/client/src/client_actor.rs @@ -9,9 +9,10 @@ use actix::dev::ToEnvelope; use actix::{Actor, Addr, Arbiter, AsyncContext, Context, Handler, Message}; use actix_rt::ArbiterHandle; use borsh::BorshSerialize; +use chrono::DateTime; use chrono::Duration as OldDuration; -use chrono::{DateTime, Utc}; use log::{debug, error, info, trace, warn}; +use near_primitives::time::{Clock, Utc}; #[cfg(feature = "delay_detector")] use delay_detector::DelayDetector; @@ -108,7 +109,7 @@ pub struct ClientActor { fn wait_until_genesis(genesis_time: &DateTime) { loop { // Get chrono::Duration::num_seconds() by deducting genesis_time from now. - let duration = genesis_time.signed_duration_since(Utc::now()); + let duration = genesis_time.signed_duration_since(Clock::utc()); let chrono_seconds = duration.num_seconds(); // Check if number of seconds in chrono::Duration larger than zero. if chrono_seconds <= 0 { @@ -738,7 +739,7 @@ impl ClientActor { Some(signer) => signer, }; - let now = Instant::now(); + let now = Clock::instant(); // Check that we haven't announced it too recently if let Some(last_validator_announce_time) = self.last_validator_announce_time { // Don't make announcement if have passed less than half of the time in which other peers @@ -809,7 +810,7 @@ impl ClientActor { || num_chunks == self.client.runtime_adapter.num_shards(&epoch_id).unwrap(); if self.client.doomslug.ready_to_produce_block( - Instant::now(), + Clock::instant(), height, have_all_chunks, ) { @@ -908,8 +909,7 @@ impl ClientActor { fn try_doomslug_timer(&mut self, _: &mut Context) { let _ = self.client.check_and_update_doomslug_tip(); - - let approvals = self.client.doomslug.process_timer(Instant::now()); + let approvals = self.client.doomslug.process_timer(Clock::instant()); // Important to save the largest approval target height before sending approvals, so // that if the node crashes in the meantime, we cannot get slashed on recovery diff --git a/chain/client/src/info.rs b/chain/client/src/info.rs index f4edd19ad80..1dfdb7188b7 100644 --- a/chain/client/src/info.rs +++ b/chain/client/src/info.rs @@ -1,6 +1,6 @@ +use near_primitives::time::Instant; use std::cmp::min; use std::sync::Arc; -use std::time::Instant; use actix::Addr; use ansi_term::Color::{Blue, Cyan, Green, White, Yellow}; @@ -24,6 +24,7 @@ use near_telemetry::{telemetry, TelemetryActor}; use crate::metrics; use crate::SyncStatus; use near_client_primitives::types::ShardSyncStatus; +use near_primitives::time::Clock; pub struct ValidatorInfoHelper { pub is_validator: bool, @@ -63,7 +64,7 @@ impl InfoHelper { nearcore_version: client_config.version.clone(), sys: System::new(), pid: get_current_pid().ok(), - started: Instant::now(), + started: Clock::instant(), num_blocks_processed: 0, gas_used: 0, telemetry_actor, @@ -160,7 +161,7 @@ impl InfoHelper { let teragas = 1_000_000_000_000u64; set_gauge(&metrics::AVG_TGAS_USAGE, (avg_gas_used as f64 / teragas as f64).round() as i64); - self.started = Instant::now(); + self.started = Clock::instant(); self.num_blocks_processed = 0; self.gas_used = 0; diff --git a/chain/client/src/sync.rs b/chain/client/src/sync.rs index e395ac44e7e..e927434b800 100644 --- a/chain/client/src/sync.rs +++ b/chain/client/src/sync.rs @@ -6,7 +6,7 @@ use std::sync::Arc; use std::{ops::Add, time::Duration as TimeDuration}; use ansi_term::Color::{Purple, Yellow}; -use chrono::{DateTime, Duration, Utc}; +use chrono::{DateTime, Duration}; use futures::{future, FutureExt}; use log::{debug, error, info, warn}; use rand::seq::{IteratorRandom, SliceRandom}; @@ -21,6 +21,7 @@ use near_primitives::block::Tip; use near_primitives::hash::CryptoHash; use near_primitives::network::PeerId; use near_primitives::syncing::get_num_state_parts; +use near_primitives::time::{Clock, Utc}; use near_primitives::types::validator_stake::ValidatorStake; use near_primitives::types::{ AccountId, BlockHeight, BlockHeightDelta, EpochId, ShardId, StateRoot, @@ -121,7 +122,7 @@ impl EpochSync { next_epoch_id: genesis_next_epoch_id.clone(), next_block_producers: first_epoch_block_producers, requested_epoch_id: genesis_epoch_id, - last_request_time: Utc::now(), + last_request_time: Clock::utc(), last_request_peer_id: None, request_timeout: Duration::from_std(request_timeout).unwrap(), peer_timeout: Duration::from_std(peer_timeout).unwrap(), @@ -160,7 +161,7 @@ impl HeaderSync { HeaderSync { network_adapter, history_locator: vec![], - prev_header_sync: (Utc::now(), 0, 0, 0), + prev_header_sync: (Clock::utc(), 0, 0, 0), syncing_peer: None, stalling_ts: None, initial_timeout: Duration::from_std(initial_timeout).unwrap(), @@ -227,7 +228,7 @@ impl HeaderSync { header_head: &Tip, highest_height: BlockHeight, ) -> bool { - let now = Utc::now(); + let now = Clock::utc(); let (timeout, old_expected_height, prev_height, prev_highest_height) = self.prev_header_sync; @@ -537,8 +538,7 @@ impl BlockSync { }, }; let next_height = chain.get_block_header(&next_hash)?.height(); - - let request = BlockSyncRequest { height: next_height, hash: next_hash, when: Utc::now() }; + let request = BlockSyncRequest { height: next_height, hash: next_hash, when: Clock::utc() }; let head = chain.head()?; let header_head = chain.header_head()?; @@ -576,7 +576,7 @@ impl BlockSync { None => Ok(true), Some(request) => Ok(chain.head()?.height >= request.height || chain.is_chunk_orphan(&request.hash) - || Utc::now() - request.when > Duration::seconds(BLOCK_REQUEST_TIMEOUT)), + || Clock::utc() - request.when > Duration::seconds(BLOCK_REQUEST_TIMEOUT)), } } } @@ -598,10 +598,10 @@ struct PendingRequestStatus { impl PendingRequestStatus { fn new(timeout: Duration) -> Self { - Self { missing_parts: 1, wait_until: Utc::now().add(timeout) } + Self { missing_parts: 1, wait_until: Clock::utc().add(timeout) } } fn expired(&self) -> bool { - Utc::now() > self.wait_until + Clock::utc() > self.wait_until } } @@ -1173,7 +1173,7 @@ impl StateSync { state_split_scheduler: &dyn Fn(StateSplitRequest), ) -> Result { let prev_hash = chain.get_block_header(&sync_hash)?.prev_hash().clone(); - let now = Utc::now(); + let now = Clock::utc(); let (request_block, have_block) = self.sync_block_status(&prev_hash, chain, now)?; diff --git a/chain/client/src/test_utils.rs b/chain/client/src/test_utils.rs index 5cb28f5e6cf..14932f7f22e 100644 --- a/chain/client/src/test_utils.rs +++ b/chain/client/src/test_utils.rs @@ -5,12 +5,12 @@ use std::mem::swap; use std::ops::DerefMut; use std::sync::{Arc, RwLock}; use std::time::Duration; -use std::time::Instant; use actix::actors::mocker::Mocker; use actix::{Actor, Addr, AsyncContext, Context}; -use chrono::{DateTime, Utc}; +use chrono::DateTime; use futures::{future, FutureExt}; +use near_primitives::time::Utc; use num_rational::Rational; use rand::{thread_rng, Rng}; @@ -55,6 +55,7 @@ use near_chain::chain::{do_apply_chunks, BlockCatchUpRequest, StateSplitRequest} use near_chain::types::AcceptedBlock; use near_client_primitives::types::Error; use near_primitives::runtime::config::RuntimeConfig; +use near_primitives::time::{Clock, Instant}; use near_primitives::utils::MaybeValidated; pub type PeerManagerMock = Mocker; @@ -282,7 +283,7 @@ pub fn setup_mock_with_validity_period_and_no_epoch_sync( false, network_adapter.clone(), transaction_validity_period, - Utc::now(), + Clock::utc(), ctx, ); vca = Some(view_client_addr); @@ -322,7 +323,7 @@ impl BlockStats { hash2depth: HashMap::new(), num_blocks: 0, max_chain_length: 0, - last_check: Instant::now(), + last_check: Clock::instant(), max_divergence: 0, last_hash: None, parent: HashMap::new(), @@ -371,7 +372,7 @@ impl BlockStats { } pub fn check_stats(&mut self, force: bool) { - let now = Instant::now(); + let now = Clock::instant(); let diff = now.duration_since(self.last_check); if !force && diff.lt(&Duration::from_secs(60)) { return; @@ -485,7 +486,7 @@ pub fn setup_mock_all_validators( let key_pairs = key_pairs; let addresses: Vec<_> = (0..key_pairs.len()).map(|i| hash(vec![i as u8].as_ref())).collect(); - let genesis_time = Utc::now(); + let genesis_time = Clock::utc(); let mut ret = vec![]; let connectors: Arc, Addr)>>> = diff --git a/chain/client/src/view_client.rs b/chain/client/src/view_client.rs index 4450dbb8651..28fdfd44946 100644 --- a/chain/client/src/view_client.rs +++ b/chain/client/src/view_client.rs @@ -1,6 +1,7 @@ //! Readonly view of the chain and state of the database. //! Useful for querying from RPC. +use near_primitives::time::Clock; use std::cmp::Ordering; use std::collections::HashMap; use std::collections::VecDeque; @@ -161,7 +162,7 @@ impl ViewClientActor { } fn need_request(key: K, cache: &mut SizedCache) -> bool { - let now = Instant::now(); + let now = Clock::instant(); let need_request = match cache.cache_get(&key) { Some(time) => now - *time > Duration::from_millis(REQUEST_WAIT_TIME), None => true, @@ -510,7 +511,7 @@ impl ViewClientActor { fn check_state_sync_request(&self) -> bool { let mut cache = self.state_request_cache.lock().expect(POISONED_LOCK_ERR); - let now = Instant::now(); + let now = Clock::instant(); let cutoff = now - self.config.view_client_throttle_period; // Assume that time is linear. While in different threads there might be some small differences, // it should not matter in practice. diff --git a/chain/client/tests/query_client.rs b/chain/client/tests/query_client.rs index 6e30e9d6842..021c1bfa780 100644 --- a/chain/client/tests/query_client.rs +++ b/chain/client/tests/query_client.rs @@ -1,7 +1,6 @@ use actix::System; use futures::{future, FutureExt}; -use chrono::Utc; use near_actix_test_utils::run_actix; use near_client::test_utils::{setup_no_network, setup_only_view}; use near_client::{ @@ -13,6 +12,7 @@ use near_network::test_utils::MockPeerManagerAdapter; use near_network::types::{NetworkViewClientMessages, NetworkViewClientResponses}; use near_network::{NetworkClientMessages, NetworkClientResponses, PeerInfo}; use near_primitives::block::{Block, BlockHeader}; +use near_primitives::time::Utc; use near_primitives::transaction::SignedTransaction; use near_primitives::types::{BlockId, BlockReference, EpochId}; use near_primitives::utils::to_timestamp; diff --git a/chain/network-primitives/src/types.rs b/chain/network-primitives/src/types.rs index 88b2f40b221..837887f9339 100644 --- a/chain/network-primitives/src/types.rs +++ b/chain/network-primitives/src/types.rs @@ -9,7 +9,8 @@ use std::time::Duration; use actix::dev::{MessageResponse, ResponseChannel}; use actix::{Actor, Message}; use borsh::{BorshDeserialize, BorshSerialize}; -use chrono::{DateTime, Utc}; +use chrono::DateTime; +use near_primitives::time::{Clock, Utc}; use serde::{Deserialize, Serialize}; use strum::AsStaticStr; use tokio::net::TcpStream; @@ -664,8 +665,8 @@ impl KnownPeerState { KnownPeerState { peer_info, status: KnownPeerStatus::Unknown, - first_seen: to_timestamp(Utc::now()), - last_seen: to_timestamp(Utc::now()), + first_seen: to_timestamp(Clock::utc()), + last_seen: to_timestamp(Clock::utc()), } } diff --git a/chain/network/src/peer/peer_actor.rs b/chain/network/src/peer/peer_actor.rs index c6b117ab8bb..b761b8c12eb 100644 --- a/chain/network/src/peer/peer_actor.rs +++ b/chain/network/src/peer/peer_actor.rs @@ -28,6 +28,7 @@ use near_primitives::hash::CryptoHash; use near_primitives::logging; use near_primitives::network::PeerId; use near_primitives::sharding::PartialEncodedChunk; +use near_primitives::time::Clock; use near_primitives::unwrap_option_or_return; use near_primitives::utils::DisplayOption; use near_primitives::version::{ @@ -241,11 +242,11 @@ impl PeerActor { genesis_id: Default::default(), chain_info: Default::default(), edge_info, - last_time_received_message_update: Instant::now(), + last_time_received_message_update: Clock::instant(), network_metrics, txns_since_last_block, peer_counter, - last_time_received_epoch_sync_request: Instant::now() + last_time_received_epoch_sync_request: Clock::instant() - Duration::from_millis(EPOCH_SYNC_PEER_TIMEOUT_MS), routed_message_cache: SizedCache::with_size(ROUTED_MESSAGE_CACHE_SIZE), } @@ -416,7 +417,7 @@ impl PeerActor { NetworkViewClientMessages::BlockHeadersRequest(hashes) } PeerMessage::EpochSyncRequest(epoch_id) => { - self.last_time_received_epoch_sync_request = Instant::now(); + self.last_time_received_epoch_sync_request = Clock::instant(); NetworkViewClientMessages::EpochSyncRequest { epoch_id } } PeerMessage::EpochSyncFinalizationRequest(epoch_id) => { @@ -628,7 +629,7 @@ impl PeerActor { if self.last_time_received_message_update.elapsed() > UPDATE_INTERVAL_LAST_TIME_RECEIVED_MESSAGE { - self.last_time_received_message_update = Instant::now(); + self.last_time_received_message_update = Clock::instant(); self.peer_manager_addr.do_send(PeerManagerMessageRequest::PeerRequest( PeerRequest::ReceivedMessage(peer_id, self.last_time_received_message_update), )); @@ -750,7 +751,7 @@ impl StreamHandler, ReasonForBan>> for PeerActor { // Drop duplicated messages routed within DROP_DUPLICATED_MESSAGES_PERIOD ms if let PeerMessage::Routed(msg) = &peer_msg { let key = (msg.author.clone(), msg.target.clone(), msg.signature.clone()); - let now = Instant::now(); + let now = Clock::instant(); if let Some(time) = self.routed_message_cache.cache_get(&key) { if now.duration_since(*time) <= DROP_DUPLICATED_MESSAGES_PERIOD { debug!(target: "network", "Dropping duplicated message from {} to {:?}", msg.author, msg.target); diff --git a/chain/network/src/peer_manager/peer_manager_actor.rs b/chain/network/src/peer_manager/peer_manager_actor.rs index 52b0aeb618c..c44962d282e 100644 --- a/chain/network/src/peer_manager/peer_manager_actor.rs +++ b/chain/network/src/peer_manager/peer_manager_actor.rs @@ -13,9 +13,9 @@ use actix::{ Actor, ActorFuture, Addr, Arbiter, AsyncContext, Context, ContextFutureSpawner, Handler, Recipient, Running, StreamHandler, SyncArbiter, WrapFuture, }; -use chrono::Utc; use futures::task::Poll; use futures::{future, Stream, StreamExt}; +use near_primitives::time::Clock; use tokio::net::{TcpListener, TcpStream}; use tokio_util::codec::FramedRead; use tracing::{debug, error, info, trace, warn}; @@ -372,7 +372,7 @@ impl PeerManagerActor { /// Receives list of edges that were verified, in a trigger every 20ms, and adds them to /// the routing table. fn broadcast_edges_trigger(&mut self, ctx: &mut Context) { - let start = Instant::now(); + let start = Clock::instant(); let mut new_edges = Vec::new(); while let Some(edge) = self.routing_table_exchange_helper.edges_to_add_receiver.pop() { new_edges.push(edge); @@ -535,9 +535,9 @@ impl PeerManagerActor { full_peer_info, sent_bytes_per_sec: 0, received_bytes_per_sec: 0, - last_time_peer_requested: Instant::now(), - last_time_received_message: Instant::now(), - connection_established_time: Instant::now(), + last_time_peer_requested: Clock::instant(), + last_time_received_message: Clock::instant(), + connection_established_time: Clock::instant(), peer_type, }, ); @@ -602,7 +602,7 @@ impl PeerManagerActor { // Ask for peers list on connection. let _ = addr.do_send(SendMessage { message: PeerMessage::PeersRequest }); if let Some(active_peer) = act.active_peers.get_mut(&target_peer_id) { - active_peer.last_time_peer_requested = Instant::now(); + active_peer.last_time_peer_requested = Clock::instant(); } if peer_type == PeerType::Outbound { @@ -874,7 +874,7 @@ impl PeerManagerActor { let msg = SendMessage { message: PeerMessage::PeersRequest }; for (_, active_peer) in self.active_peers.iter_mut() { if active_peer.last_time_peer_requested.elapsed() > REQUEST_PEERS_INTERVAL { - active_peer.last_time_peer_requested = Instant::now(); + active_peer.last_time_peer_requested = Clock::instant(); requests.push(active_peer.addr.send(msg.clone())); } } @@ -1115,7 +1115,7 @@ impl PeerManagerActor { for (peer_id, peer_state) in self.peer_store.iter() { if let KnownPeerStatus::Banned(_, last_banned) = peer_state.status { let interval = unwrap_or_error!( - (Utc::now() - from_timestamp(last_banned)).to_std(), + (Clock::utc() - from_timestamp(last_banned)).to_std(), "Failed to convert time" ); if interval > self.config.ban_window { diff --git a/chain/network/src/peer_manager/peer_store.rs b/chain/network/src/peer_manager/peer_store.rs index 6f31d5db763..b4e2ff5578c 100644 --- a/chain/network/src/peer_manager/peer_store.rs +++ b/chain/network/src/peer_manager/peer_store.rs @@ -6,7 +6,7 @@ use std::net::SocketAddr; use std::sync::Arc; use borsh::BorshSerialize; -use chrono::Utc; +use near_primitives::time::Utc; use rand::seq::SliceRandom; use rand::thread_rng; use tracing::{debug, error}; diff --git a/chain/network/src/routing/route_back_cache.rs b/chain/network/src/routing/route_back_cache.rs index b3dec2d7475..bce877dd17a 100644 --- a/chain/network/src/routing/route_back_cache.rs +++ b/chain/network/src/routing/route_back_cache.rs @@ -1,10 +1,10 @@ +use near_primitives::hash::CryptoHash; +use near_primitives::network::PeerId; +use near_primitives::time::Clock; use std::collections::btree_map; use std::collections::{BTreeMap, BTreeSet, HashMap}; use std::time::{Duration, Instant}; -use near_primitives::hash::CryptoHash; -use near_primitives::network::PeerId; - type Size = u64; /// Cache to store route back messages. @@ -115,9 +115,8 @@ impl RouteBackCache { if self.is_full() { self.remove_frequent(); - let now = Instant::now(); + let now = Clock::instant(); let remove_until = now - self.evict_timeout; - let mut remove_empty = vec![]; for (key, value) in self.record_per_target.iter_mut() { @@ -207,7 +206,7 @@ impl RouteBackCache { self.remove_evicted(); - let now = Instant::now(); + let now = Clock::instant(); self.main.insert(hash, (now, target.clone())); diff --git a/chain/network/src/routing/routing.rs b/chain/network/src/routing/routing.rs index 3c31095ddbf..0e06f05bb09 100644 --- a/chain/network/src/routing/routing.rs +++ b/chain/network/src/routing/routing.rs @@ -1,3 +1,4 @@ +use near_primitives::time::Clock; use std::collections::{hash_map::Entry, HashMap, VecDeque}; use std::sync::{Arc, Mutex}; use std::time::{Duration, Instant}; @@ -538,9 +539,9 @@ impl RoutingTableView { let mut res = None; if let Some(nonces) = self.waiting_pong.cache_get_mut(&pong.source) { - res = nonces - .cache_remove(&(pong.nonce as usize)) - .and_then(|sent| Some(Instant::now().duration_since(sent).as_secs_f64() * 1000f64)); + res = nonces.cache_remove(&(pong.nonce as usize)).and_then(|sent| { + Some(Clock::instant().duration_since(sent).as_secs_f64() * 1000f64) + }); } let cnt = self.pong_info.cache_get(&(pong.nonce as usize)).map(|v| v.1).unwrap_or(0); @@ -559,7 +560,7 @@ impl RoutingTableView { self.waiting_pong.cache_get_mut(&target).unwrap() }; - entry.cache_set(nonce, Instant::now()); + entry.cache_set(nonce, Clock::instant()); } pub fn get_ping(&mut self, peer_id: PeerId) -> usize { diff --git a/chain/network/src/types.rs b/chain/network/src/types.rs index 13d80aad753..dc9eba05e70 100644 --- a/chain/network/src/types.rs +++ b/chain/network/src/types.rs @@ -1,9 +1,9 @@ +use near_primitives::time::Instant; use std::collections::HashMap; use std::fmt; use std::fmt::{Debug, Formatter}; use std::io; use std::sync::{Arc, Mutex, RwLock}; -use std::time::Instant; use actix::dev::{MessageResponse, ResponseChannel}; use actix::{Actor, Addr, MailboxError, Message, Recipient}; diff --git a/chain/network/tests/cache_edges.rs b/chain/network/tests/cache_edges.rs index efeae09d7af..998f8ca6906 100644 --- a/chain/network/tests/cache_edges.rs +++ b/chain/network/tests/cache_edges.rs @@ -3,6 +3,7 @@ use std::sync::Arc; use std::time::Instant; use borsh::de::BorshDeserialize; +use near_primitives::time::Clock; use near_crypto::Signature; use near_network::routing::routing::{ @@ -37,7 +38,7 @@ impl RoutingTableTest { fn new() -> Self { let me = random_peer_id(); let store = create_test_store(); - let now = Instant::now(); + let now = Clock::instant(); Self { routing_table: RoutingTableActor::new(me.clone(), store.clone()), diff --git a/core/primitives/benches/serialization.rs b/core/primitives/benches/serialization.rs index e16689bb330..edb65ba028a 100644 --- a/core/primitives/benches/serialization.rs +++ b/core/primitives/benches/serialization.rs @@ -3,7 +3,7 @@ extern crate bencher; use bencher::Bencher; use borsh::{BorshDeserialize, BorshSerialize}; -use chrono::Utc; +use near_primitives::time::Clock; use near_crypto::{KeyType, PublicKey, Signature}; use near_primitives::account::Account; @@ -39,7 +39,7 @@ fn create_block() -> Block { let genesis = Block::genesis( PROTOCOL_VERSION, genesis_chunks.into_iter().map(|chunk| chunk.take_header()).collect(), - Utc::now(), + Clock::utc(), 0, 1_000, 1_000, diff --git a/core/primitives/src/block.rs b/core/primitives/src/block.rs index bf0788186ba..9008ad692a4 100644 --- a/core/primitives/src/block.rs +++ b/core/primitives/src/block.rs @@ -1,7 +1,8 @@ use std::cmp::max; +use crate::time::{Clock, Utc}; use borsh::{BorshDeserialize, BorshSerialize}; -use chrono::{DateTime, Utc}; +use chrono::DateTime; use near_crypto::Signature; use num_rational::Rational; use primitive_types::U256; @@ -240,8 +241,7 @@ impl Block { ); let new_total_supply = prev.total_supply() + minted_amount.unwrap_or(0) - balance_burnt; - - let now = to_timestamp(Utc::now()); + let now = to_timestamp(Clock::utc()); let time = if now <= prev.raw_timestamp() { prev.raw_timestamp() + 1 } else { now }; let (vrf_value, vrf_proof) = signer.compute_vrf_with_proof(prev.random_value().as_ref()); diff --git a/core/primitives/src/block_header.rs b/core/primitives/src/block_header.rs index 2f7eccd06e5..a3996f9ebd0 100644 --- a/core/primitives/src/block_header.rs +++ b/core/primitives/src/block_header.rs @@ -1,5 +1,6 @@ +use crate::time::Utc; use borsh::{BorshDeserialize, BorshSerialize}; -use chrono::{DateTime, Utc}; +use chrono::DateTime; use serde::Serialize; use near_crypto::{KeyType, PublicKey, Signature}; diff --git a/core/primitives/src/lib.rs b/core/primitives/src/lib.rs index c974fb1244f..19ffa37f6f8 100644 --- a/core/primitives/src/lib.rs +++ b/core/primitives/src/lib.rs @@ -25,6 +25,7 @@ pub mod state_record; pub mod syncing; pub mod telemetry; pub mod test_utils; +pub mod time; pub mod transaction; pub mod trie_key; pub mod types; diff --git a/core/primitives/src/time.rs b/core/primitives/src/time.rs new file mode 100644 index 00000000000..6e3180f5764 --- /dev/null +++ b/core/primitives/src/time.rs @@ -0,0 +1,155 @@ +use std::default::Default; + +use chrono; + +pub use chrono::Utc; +pub use std::time::{Duration, Instant}; + +use chrono::DateTime; +use std::cell::RefCell; +use std::collections::VecDeque; + +struct MockClockPerThread { + utc: VecDeque>, + durations: VecDeque, + utc_call_count: u64, + instant_call_count: u64, + instant: Instant, + is_mock: bool, +} + +pub struct Clock {} + +impl MockClockPerThread { + pub fn reset(&mut self) { + self.utc.clear(); + self.durations.clear(); + self.utc_call_count = 0; + self.instant_call_count = 0; + self.instant = Instant::now(); + self.is_mock = false; + } + + fn with(f: F) -> T + where + F: FnOnce(&mut MockClockPerThread) -> T, + { + thread_local! { + static INSTANCE: RefCell = RefCell::default() + } + INSTANCE.with(|it| f(&mut *it.borrow_mut())) + } + + fn pop_utc(&mut self) -> Option> { + self.utc_call_count += 1; + self.utc.pop_front() + } + fn pop_instant(&mut self) -> Option { + self.instant_call_count += 1; + let x = self.durations.pop_front(); + match x { + Some(t) => self.instant.checked_add(t), + None => None, + } + } +} + +impl Default for MockClockPerThread { + fn default() -> Self { + Self { + utc: VecDeque::with_capacity(16), + durations: VecDeque::with_capacity(16), + utc_call_count: 0, + instant_call_count: 0, + instant: Instant::now(), + is_mock: false, + } + } +} + +pub struct MockClockGuard {} + +impl Default for MockClockGuard { + fn default() -> Self { + Clock::set_mock(); + Self {} + } +} + +impl Drop for MockClockGuard { + fn drop(&mut self) { + Clock::reset(); + } +} + +impl Clock { + pub fn set_mock() { + MockClockPerThread::with(|clock| { + clock.is_mock = true; + }); + } + pub fn reset() { + MockClockPerThread::with(|clock| { + clock.reset(); + }); + } + pub fn add_utc(mock_date: DateTime) { + MockClockPerThread::with(|clock| { + if clock.is_mock { + clock.utc.push_back(mock_date); + } else { + panic!("Use MockClockGuard in your test"); + } + }); + } + + pub fn add_instant(mock_instant: Duration) { + MockClockPerThread::with(|clock| { + if clock.is_mock { + clock.durations.push_back(mock_instant); + } else { + panic!("Use MockClockGuard in your test"); + } + }); + } + + pub fn utc() -> DateTime { + MockClockPerThread::with(|clock| { + if clock.is_mock { + let x = clock.pop_utc(); + match x { + Some(t) => t, + None => { + panic!("Mock clock run out of samples"); + } + } + } else { + chrono::Utc::now() + } + }) + } + + pub fn instant() -> Instant { + MockClockPerThread::with(|clock| { + if clock.is_mock { + let x = clock.pop_instant(); + match x { + Some(t) => t, + None => { + panic!("Mock clock run out of samples"); + } + } + } else { + Instant::now() + } + }) + } + + pub fn instant_call_count() -> u64 { + MockClockPerThread::with(|clock| clock.instant_call_count) + } + + pub fn utc_call_count() -> u64 { + MockClockPerThread::with(|clock| clock.utc_call_count) + } +} diff --git a/core/primitives/src/utils.rs b/core/primitives/src/utils.rs index 6eb81386143..5d4484fb355 100644 --- a/core/primitives/src/utils.rs +++ b/core/primitives/src/utils.rs @@ -3,7 +3,8 @@ use std::convert::AsRef; use std::fmt; use byteorder::{LittleEndian, WriteBytesExt}; -use chrono::{DateTime, NaiveDateTime, Utc}; +use chrono; +use chrono::{DateTime, NaiveDateTime}; use rand::distributions::Alphanumeric; use rand::{thread_rng, Rng}; use serde; @@ -313,18 +314,18 @@ macro_rules! unwrap_option_or_return { } /// Converts timestamp in ns into DateTime UTC time. -pub fn from_timestamp(timestamp: u64) -> DateTime { +pub fn from_timestamp(timestamp: u64) -> DateTime { DateTime::from_utc( NaiveDateTime::from_timestamp( (timestamp / NS_IN_SECOND) as i64, (timestamp % NS_IN_SECOND) as u32, ), - Utc, + chrono::Utc, ) } /// Converts DateTime UTC time into timestamp in ns. -pub fn to_timestamp(time: DateTime) -> u64 { +pub fn to_timestamp(time: DateTime) -> u64 { time.timestamp_nanos() as u64 } diff --git a/core/primitives/src/views.rs b/core/primitives/src/views.rs index 509a6afbf0d..e597eb5f42f 100644 --- a/core/primitives/src/views.rs +++ b/core/primitives/src/views.rs @@ -7,7 +7,7 @@ use std::fmt; use std::sync::Arc; use borsh::{BorshDeserialize, BorshSerialize}; -use chrono::{DateTime, Utc}; +use chrono::DateTime; use serde::{Deserialize, Serialize}; use near_crypto::{PublicKey, Signature}; @@ -294,11 +294,11 @@ pub struct StatusSyncInfo { pub latest_block_hash: CryptoHash, pub latest_block_height: BlockHeight, pub latest_state_root: CryptoHash, - pub latest_block_time: DateTime, + pub latest_block_time: DateTime, pub syncing: bool, pub earliest_block_hash: Option, pub earliest_block_height: Option, - pub earliest_block_time: Option>, + pub earliest_block_time: Option>, } // TODO: add more information to ValidatorInfo diff --git a/genesis-tools/genesis-csv-to-json/src/csv_parser.rs b/genesis-tools/genesis-csv-to-json/src/csv_parser.rs index 5ba632da738..7f6bc15c4b6 100644 --- a/genesis-tools/genesis-csv-to-json/src/csv_parser.rs +++ b/genesis-tools/genesis-csv-to-json/src/csv_parser.rs @@ -3,8 +3,9 @@ use std::fs::File; use std::io::Read; use std::path::PathBuf; -use chrono::{DateTime, Utc}; +use chrono::DateTime; use csv::ReaderBuilder; +use near_primitives::time::Utc; use serde::{Deserialize, Serialize}; use near_crypto::{KeyType, PublicKey}; diff --git a/integration-tests/tests/client/chunks_management.rs b/integration-tests/tests/client/chunks_management.rs index 9b8ce669634..5a8980a8d80 100644 --- a/integration-tests/tests/client/chunks_management.rs +++ b/integration-tests/tests/client/chunks_management.rs @@ -1,7 +1,7 @@ +use near_primitives::time::Instant; use std::collections::hash_map::Entry; use std::collections::HashMap; use std::sync::{Arc, RwLock}; -use std::time::Instant; use actix::{Addr, System}; use futures::{future, FutureExt}; diff --git a/integration-tests/tests/network/infinite_loop.rs b/integration-tests/tests/network/infinite_loop.rs index f2333e2e9ac..bef92f20847 100644 --- a/integration-tests/tests/network/infinite_loop.rs +++ b/integration-tests/tests/network/infinite_loop.rs @@ -1,6 +1,6 @@ +use near_primitives::time::Instant; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::Arc; -use std::time::Instant; use actix::actors::mocker::Mocker; use actix::{Actor, System}; diff --git a/integration-tests/tests/network/runner.rs b/integration-tests/tests/network/runner.rs index 5a407c3a3b9..e28182bc835 100644 --- a/integration-tests/tests/network/runner.rs +++ b/integration-tests/tests/network/runner.rs @@ -5,8 +5,9 @@ use std::sync::{Arc, RwLock}; use std::time::Duration; use actix::{Actor, Addr, AsyncContext, Context, Handler, Message, System}; -use chrono::{DateTime, Utc}; +use chrono::DateTime; use futures::{future, FutureExt, TryFutureExt}; +use near_primitives::time::Utc; use tracing::debug; use near_actix_test_utils::run_actix; diff --git a/integration-tests/tests/test_simple.rs b/integration-tests/tests/test_simple.rs index 1812e651ccc..0de48c16a35 100644 --- a/integration-tests/tests/test_simple.rs +++ b/integration-tests/tests/test_simple.rs @@ -5,8 +5,9 @@ mod test { use integration_tests::node::{create_nodes, sample_two_nodes, Node}; use integration_tests::test_helpers::{heavy_test, wait}; use near_logger_utils::init_integration_logger; + use near_primitives::time::Clock; use near_primitives::transaction::SignedTransaction; - use std::time::{Duration, Instant}; + use std::time::Duration; fn run_multiple_nodes(num_nodes: usize, num_trials: usize, test_prefix: &str) { init_integration_logger(); @@ -21,7 +22,7 @@ mod test { } // waiting for nodes to be synced - let started = Instant::now(); + let started = Clock::instant(); loop { if started.elapsed() > Duration::from_secs(10) { panic!("nodes are not synced in 10s"); diff --git a/nearcore/src/config.rs b/nearcore/src/config.rs index 8c48a526baa..79d7abb6fbd 100644 --- a/nearcore/src/config.rs +++ b/nearcore/src/config.rs @@ -6,7 +6,7 @@ use std::sync::Arc; use std::time::Duration; use actix; -use chrono::Utc; +use near_primitives::time::Clock; use num_rational::Rational; use serde::{Deserialize, Serialize}; use tracing::info; @@ -535,7 +535,7 @@ impl Genesis { add_protocol_account(&mut records); let config = GenesisConfig { protocol_version: PROTOCOL_VERSION, - genesis_time: Utc::now(), + genesis_time: Clock::utc(), chain_id: random_chain_id(), num_block_producer_seats: num_validator_seats, num_block_producer_seats_per_shard: num_validator_seats_per_shard.clone(), @@ -945,7 +945,7 @@ pub fn init_configs( let genesis_config = GenesisConfig { protocol_version: PROTOCOL_VERSION, - genesis_time: Utc::now(), + genesis_time: Clock::utc(), chain_id, genesis_height: 0, num_block_producer_seats: NUM_BLOCK_PRODUCER_SEATS,