diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 079b8ed8f..217c49126 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -9,10 +9,9 @@ variables: GIT_STRATEGY: fetch GIT_SUBMODULE_STRATEGY: recursive CI_SERVER_NAME: "GitLab CI" - CARGO_HOME: "${CI_PROJECT_DIR}/.cargo" CARGO_TARGET: x86_64-unknown-linux-gnu -.no_git: &no_git #disable git strategy +.no_git: &no_git # disable git strategy variables: GIT_STRATEGY: none GIT_SUBMODULE_STRATEGY: none @@ -32,12 +31,17 @@ variables: paths: - artifacts/ -.docker-cache-status: &docker-cache-status - dependencies: [] +.docker-cache-status: &docker-cache-status + variables: + CARGO_HOME: "/cargo/${CI_JOB_NAME}" before_script: + - SCCACHE_ERROR_LOG=/builds/parity/parity-ethereum/sccache_error.log RUST_LOG=sccache::server=debug sccache --start-server - sccache -s after_script: - - sccache -s + - echo "All crate-types:" + - grep 'parse_arguments.*--crate-type' sccache_error.log | sed -re 's/.*"--crate-type", "([^"]+)".*/\1/' | sort | uniq -c + - echo "Non-cacheable reasons:" + - grep CannotCache sccache_error.log | sed -re 's/.*CannotCache\((.+)\).*/\1/' | sort | uniq -c tags: - linux-docker @@ -47,43 +51,60 @@ cargo-check 0 3: <<: *docker-cache-status script: - time cargo check --target $CARGO_TARGET --locked --no-default-features + - sccache -s cargo-check 1 3: stage: test <<: *docker-cache-status script: - time cargo check --target $CARGO_TARGET --locked --manifest-path util/io/Cargo.toml --no-default-features + - sccache -s cargo-check 2 3: stage: test <<: *docker-cache-status script: - time cargo check --target $CARGO_TARGET --locked --manifest-path util/io/Cargo.toml --features "mio" + - sccache -s cargo-audit: stage: test + <<: *docker-cache-status script: - cargo audit - tags: - - linux-docker + - sccache -s validate-chainspecs: stage: test <<: *docker-cache-status script: - ./scripts/gitlab/validate-chainspecs.sh + - sccache -s test-cpp: stage: build <<: *docker-cache-status script: - ./scripts/gitlab/test-cpp.sh + - sccache -s test-linux: stage: build <<: *docker-cache-status script: - ./scripts/gitlab/test-linux.sh + - sccache -s + +build-android: + stage: build + image: parity/rust-android:gitlab-ci + variables: + CARGO_TARGET: armv7-linux-androideabi + script: + - scripts/gitlab/build-linux.sh + tags: + - linux-docker + <<: *collect_artifacts build-linux: &build-linux stage: build @@ -91,6 +112,7 @@ build-linux: &build-linux <<: *docker-cache-status script: - scripts/gitlab/build-linux.sh + - sccache -s <<: *collect_artifacts build-linux-i386: @@ -116,6 +138,7 @@ build-darwin: only: *releaseable_branches variables: CARGO_TARGET: x86_64-apple-darwin + CARGO_HOME: "${CI_PROJECT_DIR}/.cargo" CC: gcc CXX: g++ script: @@ -129,6 +152,7 @@ build-windows: only: *releaseable_branches variables: CARGO_TARGET: x86_64-pc-windows-msvc + CARGO_HOME: "${CI_PROJECT_DIR}/.cargo" script: - sh scripts/gitlab/build-windows.sh tags: @@ -138,7 +162,7 @@ build-windows: publish-docker: stage: publish only: *releaseable_branches - cache: {} + cache: {} dependencies: - build-linux tags: @@ -152,7 +176,7 @@ publish-snap: &publish-snap image: snapcore/snapcraft variables: BUILD_ARCH: amd64 - cache: {} + cache: {} dependencies: - build-linux tags: @@ -227,19 +251,9 @@ publish-docs: - tags except: - nightly - cache: {} + cache: {} script: - scripts/gitlab/publish-docs.sh tags: - linux-docker -build-android: - stage: build - image: parity/rust-android:gitlab-ci - variables: - CARGO_TARGET: armv7-linux-androideabi - script: - - scripts/gitlab/build-linux.sh - tags: - - linux-docker - <<: *collect_artifacts diff --git a/Cargo.lock b/Cargo.lock index 48e0d5303..b2551b339 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -742,6 +742,7 @@ dependencies = [ "serde_derive 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", "stats 0.1.0", "tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", + "time-utils 0.1.0", "trace-time 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "trie-standardmap 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "triehash-ethereum 0.2.0", @@ -2393,7 +2394,7 @@ dependencies = [ "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", "jni 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", "panic_hook 0.1.0", - "parity-ethereum 2.3.6", + "parity-ethereum 2.3.7", "tokio 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-current-thread 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2411,7 +2412,7 @@ dependencies = [ [[package]] name = "parity-ethereum" -version = "2.3.6" +version = "2.3.7" dependencies = [ "ansi_term 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", "atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2462,7 +2463,7 @@ dependencies = [ "parity-rpc 1.12.0", "parity-runtime 0.1.0", "parity-updater 1.12.0", - "parity-version 2.3.6", + "parity-version 2.3.7", "parity-whisper 0.1.0", "parking_lot 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "pretty_assertions 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2614,7 +2615,7 @@ dependencies = [ "parity-crypto 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "parity-runtime 0.1.0", "parity-updater 1.12.0", - "parity-version 2.3.6", + "parity-version 2.3.7", "parking_lot 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "patricia-trie 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "pretty_assertions 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2712,7 +2713,7 @@ dependencies = [ "parity-bytes 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "parity-hash-fetch 1.12.0", "parity-path 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-version 2.3.6", + "parity-version 2.3.7", "parking_lot 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2722,7 +2723,7 @@ dependencies = [ [[package]] name = "parity-version" -version = "2.3.6" +version = "2.3.7" dependencies = [ "parity-bytes 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "rlp 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2765,6 +2766,7 @@ dependencies = [ "serde_json 1.0.32 (registry+https://github.com/rust-lang/crates.io-index)", "slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", + "time-utils 0.1.0", "tiny-keccak 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -3713,6 +3715,10 @@ dependencies = [ "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "time-utils" +version = "0.1.0" + [[package]] name = "timer" version = "0.2.0" diff --git a/Cargo.toml b/Cargo.toml index 2cea0afde..965308db6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ description = "Parity Ethereum client" name = "parity-ethereum" # NOTE Make sure to update util/version/Cargo.toml as well -version = "2.3.6" +version = "2.3.7" license = "GPL-3.0" authors = ["Parity Technologies "] @@ -136,7 +136,8 @@ members = [ "util/triehash-ethereum", "util/keccak-hasher", "util/patricia-trie-ethereum", - "util/fastmap" + "util/fastmap", + "util/time-utils" ] [patch.crates-io] diff --git a/ethcore/Cargo.toml b/ethcore/Cargo.toml index 6adb69f67..e94c270c2 100644 --- a/ethcore/Cargo.toml +++ b/ethcore/Cargo.toml @@ -65,6 +65,7 @@ serde = "1.0" serde_derive = "1.0" stats = { path = "../util/stats" } tempdir = {version="0.3", optional = true} +time-utils = { path = "../util/time-utils" } trace-time = "0.1" triehash-ethereum = { version = "0.2", path = "../util/triehash-ethereum" } unexpected = { path = "../util/unexpected" } diff --git a/ethcore/src/engines/authority_round/mod.rs b/ethcore/src/engines/authority_round/mod.rs index 3de0d3fde..8e7000ef2 100644 --- a/ethcore/src/engines/authority_round/mod.rs +++ b/ethcore/src/engines/authority_round/mod.rs @@ -48,6 +48,9 @@ use types::header::{Header, ExtendedHeader}; use types::ancestry_action::AncestryAction; use unexpected::{Mismatch, OutOfBounds}; +#[cfg(not(time_checked_add))] +use time_utils::CheckedSystemTime; + mod finality; /// `AuthorityRound` params. @@ -571,8 +574,15 @@ fn verify_timestamp(step: &Step, header_step: u64) -> Result<(), BlockError> { // NOTE This error might be returned only in early stage of verification (Stage 1). // Returning it further won't recover the sync process. trace!(target: "engine", "verify_timestamp: block too early"); - let oob = oob.map(|n| SystemTime::now() + Duration::from_secs(n)); - Err(BlockError::TemporarilyInvalid(oob).into()) + + let now = SystemTime::now(); + let found = now.checked_add(Duration::from_secs(oob.found)).ok_or(BlockError::TimestampOverflow)?; + let max = oob.max.and_then(|m| now.checked_add(Duration::from_secs(m))); + let min = oob.min.and_then(|m| now.checked_add(Duration::from_secs(m))); + + let new_oob = OutOfBounds { min, max, found }; + + Err(BlockError::TemporarilyInvalid(new_oob).into()) }, Ok(_) => Ok(()), } @@ -608,6 +618,7 @@ fn combine_proofs(signal_number: BlockNumber, set_proof: &[u8], finality_proof: stream.out() } + fn destructure_proofs(combined: &[u8]) -> Result<(BlockNumber, &[u8], &[u8]), Error> { let rlp = Rlp::new(combined); Ok(( @@ -623,7 +634,7 @@ trait AsMillis { impl AsMillis for Duration { fn as_millis(&self) -> u64 { - self.as_secs()*1_000 + (self.subsec_nanos()/1_000_000) as u64 + self.as_secs() * 1_000 + (self.subsec_nanos() / 1_000_000) as u64 } } diff --git a/ethcore/src/lib.rs b/ethcore/src/lib.rs index 2e291aecf..00045ce1f 100644 --- a/ethcore/src/lib.rs +++ b/ethcore/src/lib.rs @@ -15,6 +15,7 @@ // along with Parity Ethereum. If not, see . #![warn(missing_docs, unused_extern_crates)] +#![cfg_attr(feature = "time_checked_add", feature(time_checked_add))] //! Ethcore library //! @@ -115,7 +116,6 @@ extern crate tempdir; extern crate kvdb_rocksdb; #[cfg(any(test, feature = "blooms-db"))] extern crate blooms_db; - #[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows", target_os = "android"))] extern crate hardware_wallet; @@ -149,6 +149,9 @@ extern crate env_logger; #[cfg(test)] extern crate rlp_compress; +#[cfg(not(time_checked_add))] +extern crate time_utils; + pub mod account_provider; pub mod block; pub mod builtin; diff --git a/ethcore/src/verification/verification.rs b/ethcore/src/verification/verification.rs index 3f5008a2b..331672602 100644 --- a/ethcore/src/verification/verification.rs +++ b/ethcore/src/verification/verification.rs @@ -40,24 +40,8 @@ use types::{BlockNumber, header::Header}; use types::transaction::SignedTransaction; use verification::queue::kind::blocks::Unverified; - -/// Returns `Ok` when the result less or equal to `i32::max_value` to prevent `SystemTime` to panic because -/// it is platform specific, may be i32 or i64. -/// -/// `Err Result { - let d1 = sys.duration_since(UNIX_EPOCH).map_err(|_| BlockError::TimestampOverflow)?; - let total_time = d1.checked_add(d2).ok_or(BlockError::TimestampOverflow)?; - - if total_time.as_secs() <= i32::max_value() as u64 { - Ok(sys + d2) - } else { - Err(BlockError::TimestampOverflow) - } -} +#[cfg(not(time_checked_add))] +use time_utils::CheckedSystemTime; /// Preprocessed block data gathered in `verify_block_unordered` call pub struct PreverifiedBlock { @@ -323,9 +307,11 @@ pub fn verify_header_params(header: &Header, engine: &EthEngine, is_full: bool, if is_full { const ACCEPTABLE_DRIFT: Duration = Duration::from_secs(15); + // this will resist overflow until `year 2037` let max_time = SystemTime::now() + ACCEPTABLE_DRIFT; let invalid_threshold = max_time + ACCEPTABLE_DRIFT * 9; - let timestamp = timestamp_checked_add(UNIX_EPOCH, Duration::from_secs(header.timestamp()))?; + let timestamp = UNIX_EPOCH.checked_add(Duration::from_secs(header.timestamp())) + .ok_or(BlockError::TimestampOverflow)?; if timestamp > invalid_threshold { return Err(From::from(BlockError::InvalidTimestamp(OutOfBounds { max: Some(max_time), min: None, found: timestamp }))) @@ -347,8 +333,11 @@ fn verify_parent(header: &Header, parent: &Header, engine: &EthEngine) -> Result let gas_limit_divisor = engine.params().gas_limit_bound_divisor; if !engine.is_timestamp_valid(header.timestamp(), parent.timestamp()) { - let min = timestamp_checked_add(SystemTime::now(), Duration::from_secs(parent.timestamp().saturating_add(1)))?; - let found = timestamp_checked_add(SystemTime::now(), Duration::from_secs(header.timestamp()))?; + let now = SystemTime::now(); + let min = now.checked_add(Duration::from_secs(parent.timestamp().saturating_add(1))) + .ok_or(BlockError::TimestampOverflow)?; + let found = now.checked_add(Duration::from_secs(header.timestamp())) + .ok_or(BlockError::TimestampOverflow)?; return Err(From::from(BlockError::InvalidTimestamp(OutOfBounds { max: None, min: Some(min), found }))) } if header.number() != parent.number() + 1 { @@ -835,11 +824,4 @@ mod tests { check_fail(unordered_test(&create_test_block_with_data(&header, &bad_transactions, &[]), &engine), TooManyTransactions(keypair.address())); unordered_test(&create_test_block_with_data(&header, &good_transactions, &[]), &engine).unwrap(); } - - #[test] - fn checked_add_systime_dur() { - assert!(timestamp_checked_add(UNIX_EPOCH, Duration::new(i32::max_value() as u64 + 1, 0)).is_err()); - assert!(timestamp_checked_add(UNIX_EPOCH, Duration::new(i32::max_value() as u64, 0)).is_ok()); - assert!(timestamp_checked_add(UNIX_EPOCH, Duration::new(i32::max_value() as u64 - 1, 1_000_000_000)).is_ok()); - } } diff --git a/util/time-utils/Cargo.toml b/util/time-utils/Cargo.toml new file mode 100644 index 000000000..38e7dd02c --- /dev/null +++ b/util/time-utils/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "time-utils" +version = "0.1.0" +authors = ["Parity Technologies "] +description = "Time utilities for checked arithmetic" +license = "GPL3" +edition = "2018" + +[dependencies] diff --git a/util/time-utils/src/lib.rs b/util/time-utils/src/lib.rs new file mode 100644 index 000000000..ff9f159cf --- /dev/null +++ b/util/time-utils/src/lib.rs @@ -0,0 +1,50 @@ +use std::time::{Duration, SystemTime, UNIX_EPOCH}; + +/// Temporary trait for `checked operations` on SystemTime until these are available in standard library +pub trait CheckedSystemTime { + /// Returns `Some` when the result less or equal to `i32::max_value` to prevent `SystemTime` to panic because + /// it is platform specific, possible representations are i32, i64, u64 and Duration. `None` otherwise + fn checked_add(self, _d: Duration) -> Option; + /// Returns `Some` when the result is successful and `None` when it is not + fn checked_sub(self, _d: Duration) -> Option; +} + +impl CheckedSystemTime for SystemTime { + fn checked_add(self, dur: Duration) -> Option { + let this_dur = self.duration_since(UNIX_EPOCH).ok()?; + let total_time = this_dur.checked_add(dur)?; + + if total_time.as_secs() <= i32::max_value() as u64 { + Some(self + dur) + } else { + None + } + } + + fn checked_sub(self, dur: Duration) -> Option { + let this_dur = self.duration_since(UNIX_EPOCH).ok()?; + let total_time = this_dur.checked_sub(dur)?; + + if total_time.as_secs() <= i32::max_value() as u64 { + Some(self - dur) + } else { + None + } + } +} + +#[cfg(test)] +mod tests { + #[test] + fn it_works() { + use super::CheckedSystemTime; + use std::time::{Duration, SystemTime, UNIX_EPOCH}; + + assert!(CheckedSystemTime::checked_add(UNIX_EPOCH, Duration::new(i32::max_value() as u64 + 1, 0)).is_none()); + assert!(CheckedSystemTime::checked_add(UNIX_EPOCH, Duration::new(i32::max_value() as u64, 0)).is_some()); + assert!(CheckedSystemTime::checked_add(UNIX_EPOCH, Duration::new(i32::max_value() as u64 - 1, 1_000_000_000)).is_some()); + + assert!(CheckedSystemTime::checked_sub(UNIX_EPOCH, Duration::from_secs(120)).is_none()); + assert!(CheckedSystemTime::checked_sub(SystemTime::now(), Duration::from_secs(1000)).is_some()); + } +} diff --git a/util/version/Cargo.toml b/util/version/Cargo.toml index 61b822b19..d27c141b6 100644 --- a/util/version/Cargo.toml +++ b/util/version/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "parity-version" # NOTE: this value is used for Parity Ethereum version string (via env CARGO_PKG_VERSION) -version = "2.3.6" +version = "2.3.7" authors = ["Parity Technologies "] build = "build.rs" diff --git a/whisper/Cargo.toml b/whisper/Cargo.toml index fec601586..85c6376fa 100644 --- a/whisper/Cargo.toml +++ b/whisper/Cargo.toml @@ -24,6 +24,7 @@ serde_json = "1.0" slab = "0.3" smallvec = "0.6" tiny-keccak = "1.4" +time-utils = { path = "../util/time-utils" } jsonrpc-core = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-2.2" } jsonrpc-macros = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-2.2" } diff --git a/whisper/src/lib.rs b/whisper/src/lib.rs index 2db7108f7..2d14a5947 100644 --- a/whisper/src/lib.rs +++ b/whisper/src/lib.rs @@ -17,6 +17,8 @@ //! Whisper P2P messaging system as a DevP2P subprotocol, with RPC and Rust //! interface. +#![cfg_attr(feature = "time_checked_add", feature(time_checked_add))] + extern crate byteorder; extern crate parity_crypto as crypto; extern crate ethcore_network as network; @@ -48,6 +50,9 @@ extern crate jsonrpc_macros; #[macro_use] extern crate serde_derive; +#[cfg(not(time_checked_add))] +extern crate time_utils; + #[cfg(test)] extern crate serde_json; diff --git a/whisper/src/message.rs b/whisper/src/message.rs index 14a6fcf9c..2d5a4fac1 100644 --- a/whisper/src/message.rs +++ b/whisper/src/message.rs @@ -24,6 +24,9 @@ use rlp::{self, DecoderError, RlpStream, Rlp}; use smallvec::SmallVec; use tiny_keccak::{keccak256, Keccak}; +#[cfg(not(time_checked_add))] +use time_utils::CheckedSystemTime; + /// Work-factor proved. Takes 3 parameters: size of message, time to live, /// and hash. /// @@ -116,6 +119,7 @@ pub enum Error { EmptyTopics, LivesTooLong, IssuedInFuture, + TimestampOverflow, ZeroTTL, } @@ -132,6 +136,7 @@ impl fmt::Display for Error { Error::LivesTooLong => write!(f, "Message claims to be issued before the unix epoch."), Error::IssuedInFuture => write!(f, "Message issued in future."), Error::ZeroTTL => write!(f, "Message live for zero time."), + Error::TimestampOverflow => write!(f, "Timestamp overflow"), Error::EmptyTopics => write!(f, "Message has no topics."), } } @@ -225,10 +230,6 @@ impl rlp::Decodable for Envelope { } } -/// Error indicating no topics. -#[derive(Debug, Copy, Clone)] -pub struct EmptyTopics; - /// Message creation parameters. /// Pass this to `Message::create` to make a message. pub struct CreateParams { @@ -254,11 +255,11 @@ pub struct Message { impl Message { /// Create a message from creation parameters. /// Panics if TTL is 0. - pub fn create(params: CreateParams) -> Result { + pub fn create(params: CreateParams) -> Result { use byteorder::{BigEndian, ByteOrder}; use rand::{Rng, SeedableRng, XorShiftRng}; - if params.topics.is_empty() { return Err(EmptyTopics) } + if params.topics.is_empty() { return Err(Error::EmptyTopics) } let mut rng = { let mut thread_rng = ::rand::thread_rng(); @@ -269,7 +270,8 @@ impl Message { assert!(params.ttl > 0); let expiry = { - let after_mining = SystemTime::now() + Duration::from_millis(params.work); + let after_mining = SystemTime::now().checked_sub(Duration::from_millis(params.work)) + .ok_or(Error::TimestampOverflow)?; let since_epoch = after_mining.duration_since(time::UNIX_EPOCH) .expect("time after now is after unix epoch; qed"); @@ -356,7 +358,10 @@ impl Message { (envelope.expiry - envelope.ttl).saturating_sub(LEEWAY_SECONDS) ); - if time::UNIX_EPOCH + issue_time_adjusted > now { + let issue_time_adjusted = time::UNIX_EPOCH.checked_add(issue_time_adjusted) + .ok_or(Error::TimestampOverflow)?; + + if issue_time_adjusted > now { return Err(Error::IssuedInFuture); } @@ -399,8 +404,8 @@ impl Message { } /// Get the expiry time. - pub fn expiry(&self) -> SystemTime { - time::UNIX_EPOCH + Duration::from_secs(self.envelope.expiry) + pub fn expiry(&self) -> Option { + time::UNIX_EPOCH.checked_add(Duration::from_secs(self.envelope.expiry)) } /// Get the topics. diff --git a/whisper/src/net/mod.rs b/whisper/src/net/mod.rs index 64431d142..d263f6cfb 100644 --- a/whisper/src/net/mod.rs +++ b/whisper/src/net/mod.rs @@ -223,7 +223,10 @@ impl Messages { } } - let expiry = message.expiry(); + let expiry = match message.expiry() { + Some(time) => time, + _ => return false, + }; self.cumulative_size += message.encoded_size(); @@ -232,8 +235,8 @@ impl Messages { let sorted_entry = SortedEntry { slab_id: id, - work_proved: work_proved, - expiry: expiry, + work_proved, + expiry, }; match self.sorted.binary_search(&sorted_entry) {