diff --git a/Cargo.lock b/Cargo.lock index 7ff77ca54f..39b3f78d70 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -175,6 +175,7 @@ dependencies = [ "fixed-hash", "generic-array", "hex-literal", + "lazy_static", "merkletree", "parity-scale-codec", "parity-scale-codec-derive", diff --git a/common/Cargo.toml b/common/Cargo.toml index 226e9770d0..7923af47a4 100644 --- a/common/Cargo.toml +++ b/common/Cargo.toml @@ -22,3 +22,4 @@ anyhow = "1.0.51" parity-scale-codec = {version = "2.3.1", features = ["derive", "chain-error"]} parity-scale-codec-derive = "2.3.1" sscanf = "0.1.4" +lazy_static = "1.4.0" diff --git a/common/src/primitives/mod.rs b/common/src/primitives/mod.rs index 70758b383a..bb3dfe9828 100644 --- a/common/src/primitives/mod.rs +++ b/common/src/primitives/mod.rs @@ -21,6 +21,7 @@ pub mod error; pub mod height; pub mod id; pub mod merkle; +pub mod time; pub mod version; pub use amount::Amount; diff --git a/common/src/primitives/time.rs b/common/src/primitives/time.rs new file mode 100644 index 0000000000..c314bbe85a --- /dev/null +++ b/common/src/primitives/time.rs @@ -0,0 +1,113 @@ +// Copyright (c) 2021 RBB S.r.l +// opensource@mintlayer.org +// SPDX-License-Identifier: MIT +// Licensed under the MIT License; +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://spdx.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Author(s): A. Altonen +#![allow(unused, dead_code)] +use lazy_static::lazy_static; +use std::sync::atomic::{AtomicI64, Ordering}; +use std::time::SystemTime; + +lazy_static! { + static ref TIME_SOURCE: AtomicI64 = Default::default(); +} + +/// Either gets the current time or panics +pub fn get() -> i64 { + let value = TIME_SOURCE.load(Ordering::SeqCst); + if value == 0 { + return SystemTime::now() + .duration_since(SystemTime::UNIX_EPOCH) + .expect("Time went backwards") + .as_secs() as i64; + } + + value +} + +/// Return mocked time if set, otherwise return `None` +pub fn get_mocked() -> Option { + let value = TIME_SOURCE.load(Ordering::SeqCst); + if value == 0 { + return None; + } + + Some(value) +} + +/// Reset time source to use `SystemTime::UNIX_EPOCH` +pub fn reset() { + TIME_SOURCE.store(0i64, Ordering::SeqCst); +} + +/// Set current time +pub fn set(now: i64) -> Result<(), &'static str> { + if now <= 0 { + return Err("Invalid time given"); + } + + TIME_SOURCE.store(now, Ordering::SeqCst); + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_time() { + let handle = std::thread::spawn(move || { + println!("p2p time: {}", get()); + std::thread::sleep(std::time::Duration::from_secs(1)); + + println!("p2p time: {}", get()); + assert_eq!(get(), 1337); + std::thread::sleep(std::time::Duration::from_secs(1)); + + println!("p2p time: {}", get()); + assert_ne!(get(), 1337); + }); + + std::thread::spawn(move || { + println!("rpc time: {}", get()); + std::thread::sleep(std::time::Duration::from_millis(500)); + + set(1337); + assert_eq!(get(), 1337); + println!("rpc time: {}", get()); + std::thread::sleep(std::time::Duration::from_millis(500)); + + reset(); + assert_ne!(get(), 1337); + println!("rpc time: {}", get()); + }); + + handle.join(); + } + + #[test] + fn test_mocked() { + assert_eq!(get_mocked(), None); + + assert_eq!(set(1337), Ok(())); + assert_eq!(get(), 1337); + assert_eq!(get_mocked(), Some(1337)); + + reset(); + assert_eq!(get_mocked(), None); + + assert_eq!(set(0), Err("Invalid time given")); + assert_eq!(set(-17), Err("Invalid time given")); + } +} diff --git a/p2p/src/message.rs b/p2p/src/message.rs index 085b233e0b..d3a0ec6ba3 100644 --- a/p2p/src/message.rs +++ b/p2p/src/message.rs @@ -26,7 +26,7 @@ pub enum HandshakeMessage { /// Services that the local node supports services: u32, /// Unix timestamp - timestamp: u64, + timestamp: i64, }, HelloAck { /// Software version of local node @@ -34,7 +34,7 @@ pub enum HandshakeMessage { /// Services that the local node supports services: u32, /// Unix timestamp - timestamp: u64, + timestamp: i64, }, } @@ -57,13 +57,13 @@ pub struct Message { mod tests { use super::*; use common::chain::config; - use std::time::SystemTime; + use common::primitives::time; #[test] fn hello_test() { let config = config::create_mainnet(); let serv = 0u32; - let ts: u64 = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap().as_secs(); + let ts = time::get(); let msg = Message { magic: *config.magic_bytes(), @@ -109,7 +109,7 @@ mod tests { fn hello_ack_test() { let config = config::create_mainnet(); let serv = 0u32; - let ts: u64 = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap().as_secs(); + let ts = time::get(); let msg = Message { magic: *config.magic_bytes(), diff --git a/p2p/src/peer.rs b/p2p/src/peer.rs index eed79f6ef5..ff77d85ce8 100644 --- a/p2p/src/peer.rs +++ b/p2p/src/peer.rs @@ -20,10 +20,11 @@ use crate::message::{HandshakeMessage, Message, MessageType}; use crate::net::{NetworkService, SocketService}; use crate::proto::handshake::*; use common::chain::ChainConfig; +use common::primitives::time; use futures::{stream::FuturesUnordered, FutureExt, StreamExt}; use futures_timer::Delay; use std::sync::Arc; -use std::time::{Duration, SystemTime}; +use std::time::Duration; pub type PeerId = u64; pub type TaskId = u64; @@ -215,7 +216,7 @@ where msg: MessageType::Handshake(HandshakeMessage::Hello { version: *self.config.version(), services: 0u32, - timestamp: SystemTime::now().duration_since(SystemTime::UNIX_EPOCH)?.as_secs(), + timestamp: time::get(), }), })) .await?; diff --git a/p2p/src/proto/handshake.rs b/p2p/src/proto/handshake.rs index 1341ae9d83..39b2149c1b 100644 --- a/p2p/src/proto/handshake.rs +++ b/p2p/src/proto/handshake.rs @@ -18,7 +18,7 @@ use crate::error::{self, P2pError, ProtocolError}; use crate::message::{HandshakeMessage, Message, MessageType}; use crate::net::{NetworkService, SocketService}; use crate::peer::{Peer, PeerState}; -use std::time::SystemTime; +use common::primitives::time; #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum InboundHandshakeState { @@ -76,9 +76,7 @@ where msg: MessageType::Handshake(HandshakeMessage::HelloAck { version: *self.config.version(), services: 0u32, - timestamp: SystemTime::now() - .duration_since(SystemTime::UNIX_EPOCH)? - .as_secs(), + timestamp: time::get(), }), }; @@ -175,6 +173,7 @@ mod tests { use crate::net::mock::{MockService, MockSocket}; use crate::peer::PeerRole; use common::chain::{config, ChainConfig}; + use common::primitives::time; use common::primitives::version::SemVer; use std::sync::Arc; use tokio::net::TcpStream; @@ -244,10 +243,7 @@ mod tests { HandshakeMessage::Hello { version: *config.version(), services: 0u32, - timestamp: SystemTime::now() - .duration_since(SystemTime::UNIX_EPOCH) - .unwrap() - .as_secs(), + timestamp: time::get(), }, ) .await; @@ -319,10 +315,7 @@ mod tests { msg: MessageType::Handshake(HandshakeMessage::Hello { version: *config.version(), services: 0u32, - timestamp: SystemTime::now() - .duration_since(SystemTime::UNIX_EPOCH) - .unwrap() - .as_secs(), + timestamp: time::get(), }), }) .await @@ -379,10 +372,7 @@ mod tests { msg: MessageType::Handshake(HandshakeMessage::Hello { version: SemVer::new(13, 37, 1338), services: 0u32, - timestamp: SystemTime::now() - .duration_since(SystemTime::UNIX_EPOCH) - .unwrap() - .as_secs(), + timestamp: time::get(), }), }) .await @@ -439,10 +429,7 @@ mod tests { msg: MessageType::Handshake(HandshakeMessage::Hello { version: SemVer::new(13, 37, 1338), services: 0u32, - timestamp: SystemTime::now() - .duration_since(SystemTime::UNIX_EPOCH) - .unwrap() - .as_secs(), + timestamp: time::get(), }), }) .await @@ -497,10 +484,7 @@ mod tests { HandshakeMessage::Hello { version: *config.version(), services: 0u32, - timestamp: SystemTime::now() - .duration_since(SystemTime::UNIX_EPOCH) - .unwrap() - .as_secs(), + timestamp: time::get(), }, ) .await; @@ -527,10 +511,7 @@ mod tests { msg: MessageType::Handshake(HandshakeMessage::Hello { version: *config.version(), services: 0u32, - timestamp: SystemTime::now() - .duration_since(SystemTime::UNIX_EPOCH) - .unwrap() - .as_secs(), + timestamp: time::get(), }), }) .await @@ -575,10 +556,7 @@ mod tests { msg: MessageType::Handshake(HandshakeMessage::HelloAck { version: *config.version(), services: 0u32, - timestamp: SystemTime::now() - .duration_since(SystemTime::UNIX_EPOCH) - .unwrap() - .as_secs(), + timestamp: time::get(), }), }) .await @@ -623,10 +601,7 @@ mod tests { msg: MessageType::Handshake(HandshakeMessage::HelloAck { version: *config.version(), services: 0u32, - timestamp: SystemTime::now() - .duration_since(SystemTime::UNIX_EPOCH) - .unwrap() - .as_secs(), + timestamp: time::get(), }), })) .await; @@ -655,10 +630,7 @@ mod tests { msg: MessageType::Handshake(HandshakeMessage::HelloAck { version: *config.version(), services: 0u32, - timestamp: SystemTime::now() - .duration_since(SystemTime::UNIX_EPOCH) - .unwrap() - .as_secs(), + timestamp: time::get(), }), })) .await; @@ -687,10 +659,7 @@ mod tests { msg: MessageType::Handshake(HandshakeMessage::Hello { version: *config.version(), services: 0u32, - timestamp: SystemTime::now() - .duration_since(SystemTime::UNIX_EPOCH) - .unwrap() - .as_secs(), + timestamp: time::get(), }), })) .await;