diff --git a/Cargo.lock b/Cargo.lock index 9fe1c5854d3..bde4784c881 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -429,6 +429,16 @@ dependencies = [ "rpassword 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "client-traits" +version = "0.1.0" +dependencies = [ + "common-types 0.1.0", + "derive_more 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ethereum-types 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rlp 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "cloudabi" version = "0.0.3" @@ -461,6 +471,8 @@ dependencies = [ name = "common-types" version = "0.1.0" dependencies = [ + "derive_more 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ethbloom 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", "ethereum-types 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "ethjson 0.1.0", "ethkey 0.3.0", @@ -470,7 +482,9 @@ dependencies = [ "rlp 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "rlp_derive 0.1.0", "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-hex 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "unexpected 0.1.0", + "vm 0.1.0", ] [[package]] @@ -520,7 +534,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cast 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", "thread-scoped 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -858,6 +872,7 @@ dependencies = [ "account-state 0.1.0", "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "blooms-db 0.1.0", + "client-traits 0.1.0", "common-types 0.1.0", "criterion 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", @@ -896,7 +911,7 @@ dependencies = [ "lru-cache 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "macros 0.1.0", "memory-cache 0.1.0", - "num_cpus 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)", "parity-bytes 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "parity-runtime 0.1.0", "parity-snappy 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -926,6 +941,7 @@ dependencies = [ "triehash-ethereum 0.2.0", "unexpected 0.1.0", "using_queue 0.1.0", + "verification 0.1.0", "vm 0.1.0", ] @@ -1027,7 +1043,7 @@ dependencies = [ "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "slab 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1432,7 +1448,7 @@ dependencies = [ "docopt 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.5.13 (registry+https://github.com/rust-lang/crates.io-index)", "ethstore 0.2.1", - "num_cpus 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)", "panic_hook 0.1.0", "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1627,7 +1643,7 @@ version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2082,7 +2098,7 @@ dependencies = [ "jsonrpc-core 12.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)", "tokio 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "unicase 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2179,7 +2195,7 @@ dependencies = [ "interleaved-ordered 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "kvdb 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)", "parity-rocksdb 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", "regex 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2562,7 +2578,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "num_cpus" -version = "1.10.0" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2723,7 +2739,7 @@ dependencies = [ "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "migration-rocksdb 0.1.0", "node-filter 1.12.0", - "num_cpus 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)", "number_prefix 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "panic_hook 0.1.0", "parity-bytes 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3522,7 +3538,7 @@ dependencies = [ "crossbeam-queue 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -4139,7 +4155,7 @@ name = "threadpool" version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "num_cpus 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -4262,7 +4278,7 @@ dependencies = [ "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", "slab 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-executor 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", @@ -4310,7 +4326,7 @@ dependencies = [ "crossbeam-utils 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-executor 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -4637,6 +4653,30 @@ dependencies = [ "time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "verification" +version = "0.1.0" +dependencies = [ + "client-traits 0.1.0", + "common-types 0.1.0", + "derive_more 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ethcore-blockchain 0.1.0", + "ethcore-call-contract 0.1.0", + "ethcore-io 1.12.0", + "ethereum-types 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "keccak-hash 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "len-caching-lock 0.1.1", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-bytes 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-util-mem 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rlp 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "time-utils 0.1.0", + "triehash-ethereum 0.2.0", + "unexpected 0.1.0", +] + [[package]] name = "version_check" version = "0.1.5" @@ -5049,7 +5089,7 @@ dependencies = [ "checksum num-iter 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)" = "af3fdbbc3291a5464dc57b03860ec37ca6bf915ed6ee385e7c6c052c422b2124" "checksum num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)" = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31" "checksum num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0b3a5d7cc97d6d30d8b9bc8fa19bf45349ffe46241e8816f50f62f6d6aaabee1" -"checksum num_cpus 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1a23f0ed30a54abaa0c7e83b1d2d87ada7c3c23078d1d87815af3e3b6385fbba" +"checksum num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bcef43580c035376c0705c42792c294b66974abbfd2789b511784023f71f3273" "checksum number_prefix 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "dbf9993e59c894e3c08aa1c2712914e9e6bf1fcbfc6bef283e2183df345a4fee" "checksum ole32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5d2c49021782e5233cd243168edfa8037574afed4eba4bbaf538b3d8d1789d8c" "checksum opaque-debug 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "93f5bb2e8e8dec81642920ccff6b61f1eb94fa3020c5a325c9851ff604152409" diff --git a/Cargo.toml b/Cargo.toml index f85a72d6e86..3af087e8582 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -140,4 +140,7 @@ members = [ "util/patricia-trie-ethereum", "util/fastmap", "util/time-utils", + + "ethcore/verification", + "ethcore/client-traits", ] diff --git a/ethcore/Cargo.toml b/ethcore/Cargo.toml index bfe330bcacf..abfd5bcc84e 100644 --- a/ethcore/Cargo.toml +++ b/ethcore/Cargo.toml @@ -74,6 +74,9 @@ using_queue = { path = "../miner/using-queue" } vm = { path = "vm" } rand_xorshift = "0.1.1" +client-traits = { path = "client-traits" } +verification = { path = "verification" } + [dev-dependencies] blooms-db = { path = "../util/blooms-db" } criterion = "0.2" diff --git a/ethcore/account-db/Cargo.toml b/ethcore/account-db/Cargo.toml index e3d0dea2121..850b55905fb 100644 --- a/ethcore/account-db/Cargo.toml +++ b/ethcore/account-db/Cargo.toml @@ -1,8 +1,9 @@ [package] -description = "DB backend wrapper for Account trie" name = "account-db" -version = "0.1.0" +description = "DB backend wrapper for Account trie" authors = ["Parity Technologies "] +license = "GPL-3.0" +version = "0.1.0" edition = "2018" [dependencies] diff --git a/ethcore/account-state/Cargo.toml b/ethcore/account-state/Cargo.toml index 2b07cc76987..1e04a7922c5 100644 --- a/ethcore/account-state/Cargo.toml +++ b/ethcore/account-state/Cargo.toml @@ -1,8 +1,9 @@ [package] -description = "Ethereum accounts, keeps track of changes to the code and storage" name = "account-state" -version = "0.1.0" +description = "Ethereum accounts, keeps track of changes to the code and storage" authors = ["Parity Technologies "] +license = "GPL-3.0" +version = "0.1.0" edition = "2018" [dependencies] diff --git a/ethcore/account-state/src/account.rs b/ethcore/account-state/src/account.rs index c0bab08be89..48d52801419 100644 --- a/ethcore/account-state/src/account.rs +++ b/ethcore/account-state/src/account.rs @@ -29,7 +29,6 @@ use lru_cache::LruCache; use parity_bytes::{Bytes, ToPretty}; use rlp::{DecoderError, encode}; use trie_db::{Recorder, Trie}; - use common_types::basic_account::BasicAccount; use ethtrie::{Result as TrieResult, SecTrieDB, TrieDB, TrieFactory}; use keccak_hasher::KeccakHasher; diff --git a/ethcore/account-state/src/backend.rs b/ethcore/account-state/src/backend.rs index 563aa1023dc..5aa675ff929 100644 --- a/ethcore/account-state/src/backend.rs +++ b/ethcore/account-state/src/backend.rs @@ -29,7 +29,6 @@ use hash_db::{AsHashDB, EMPTY_PREFIX, HashDB, Prefix}; use kvdb::DBValue; use memory_db::{HashKey, MemoryDB}; use parking_lot::Mutex; - use journaldb::AsKeyedHashDB; use keccak_hasher::KeccakHasher; @@ -222,7 +221,7 @@ impl> Proving { /// This will store all values ever fetched from that base. pub fn new(base: H) -> Self { Proving { - base: base, + base, changed: journaldb::new_memory_db(), proof: Mutex::new(HashSet::new()), } diff --git a/ethcore/account-state/src/lib.rs b/ethcore/account-state/src/lib.rs index 3ec3d291ec5..60b1982f400 100644 --- a/ethcore/account-state/src/lib.rs +++ b/ethcore/account-state/src/lib.rs @@ -26,12 +26,10 @@ pub mod account; pub mod backend; pub mod substate; pub mod state; -pub mod error; pub use { account::Account, backend::Backend, - error::Error, substate::Substate, state::{State, CleanupMode}, }; diff --git a/ethcore/account-state/src/state.rs b/ethcore/account-state/src/state.rs index 0b2775ff6c1..e3ced835099 100644 --- a/ethcore/account-state/src/state.rs +++ b/ethcore/account-state/src/state.rs @@ -32,6 +32,7 @@ use std::{ use common_types::{ state_diff::StateDiff, basic_account::BasicAccount, + errors::EthcoreError as Error, }; use ethereum_types::{Address, H256, U256}; use ethtrie::{TrieDB, Result as TrieResult}; @@ -46,7 +47,6 @@ use pod::{self, PodAccount, PodState}; use trie_db::{Trie, TrieError, Recorder}; use crate::{ - Error, account::Account, backend::Backend, }; diff --git a/ethcore/client-traits/Cargo.toml b/ethcore/client-traits/Cargo.toml new file mode 100644 index 00000000000..a19d447bf6c --- /dev/null +++ b/ethcore/client-traits/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "client-traits" +version = "0.1.0" +edition = "2018" + + +[dependencies] +call-contract = { package = "ethcore-call-contract", path = "../call-contract" } +common-types = { path = "../types" } +derive_more = "0.15.0" +ethereum-types = "0.6" +rlp = "0.4.0" # todo: only needed for `DecoderError` – fix diff --git a/ethcore/client-traits/src/error.rs b/ethcore/client-traits/src/error.rs new file mode 100644 index 00000000000..d99d1c28dde --- /dev/null +++ b/ethcore/client-traits/src/error.rs @@ -0,0 +1,57 @@ +// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// This file is part of Parity Ethereum. + +// Parity Ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Ethereum. If not, see . + +//! State related errors + +use derive_more::{Display, From}; +use common_types::{ + engines::EngineError, + block::{BlockError, ImportError}, + transaction::Error as TransactionError, +}; +use rlp::DecoderError; + +#[derive(Debug, Display, From)] +pub enum Error { + /// todo: what errors do I need? + Block(BlockError), + Import(ImportError), + Engine(EngineError), + Decoder(DecoderError), + Transaction(TransactionError), + State(String), // todo: move account-state errors to `common_types`? + Other(String) +} + +impl std::error::Error for Error {} + +impl From<&str> for Error { + fn from(s: &str) -> Self { + Error::Other(s.into()) + } +} + +impl From for Error { + fn from(s: String) -> Self { + Error::Other(s) + } +} + +impl From> for Error where Error: From { + fn from(err: Box) -> Self { + Error::from(*err) + } +} diff --git a/ethcore/client-traits/src/lib.rs b/ethcore/client-traits/src/lib.rs new file mode 100644 index 00000000000..3ed2e3f3c0d --- /dev/null +++ b/ethcore/client-traits/src/lib.rs @@ -0,0 +1,106 @@ +// todo: module docs + +use call_contract::CallContract; +use common_types::{ + BlockNumber, + ids::BlockId, + header::Header, + encoded, + transaction::{self, UnverifiedTransaction, SignedTransaction}, + engines::{ + EthashExtensions, + params::CommonParams + }, + errors::EthcoreError, +}; +use ethereum_types::{H256, U256, Address}; + +/// Provides various information on a block by it's ID +pub trait BlockInfo { + /// Get raw block header data by block id. + fn block_header(&self, id: BlockId) -> Option; + + /// Get the best block header. + fn best_block_header(&self) -> Header; + + /// Get raw block data by block header hash. + fn block(&self, id: BlockId) -> Option; + + /// Get address code hash at given block's state. + fn code_hash(&self, address: &Address, id: BlockId) -> Option; +} + +pub trait VerifyingClient: BlockInfo + CallContract {} + +/// todo rewrite docs: A consensus mechanism for the chain. Generally either proof-of-work or proof-of-stake-based. +/// Provides hooks into each of the major parts of block import. +pub trait VerifyingEngine: Sync + Send { + /// The number of additional header fields required for this engine. + fn seal_fields(&self, _header: &Header) -> usize { 0 } + + /// Maximum number of uncles a block is allowed to declare. + fn maximum_uncle_count(&self, _block: BlockNumber) -> usize { 0 } + + /// Optional maximum gas limit. + fn maximum_gas_limit(&self) -> Option { None } + + /// Some intrinsic operation parameters; by default they take their value from the `spec()`'s `engine_params`. + fn maximum_extra_data_size(&self) -> usize { self.params().maximum_extra_data_size } + + /// Get the general parameters of the chain. + fn params(&self) -> &CommonParams; + + /// Get a reference to the ethash-specific extensions. + fn ethash_extensions(&self) -> Option<&EthashExtensions> { None } + + /// Phase 1 quick block verification. Only does checks that are cheap. Returns either a null `Ok` or a general error detailing the problem with import. + /// The verification module can optionally avoid checking the seal (`check_seal`), if seal verification is disabled this method won't be called. + fn verify_block_basic(&self, _header: &Header) -> Result<(), EthcoreError> { Ok(()) } + + /// Phase 2 verification. Perform costly checks such as transaction signatures. Returns either a null `Ok` or a general error detailing the problem with import. + /// The verification module can optionally avoid checking the seal (`check_seal`), if seal verification is disabled this method won't be called. + fn verify_block_unordered(&self, _header: &Header) -> Result<(), EthcoreError> { Ok(()) } + + /// Phase 3 verification. Check block information against parent. Returns either a null `Ok` or a general error detailing the problem with import. + fn verify_block_family(&self, _header: &Header, _parent: &Header) -> Result<(), EthcoreError> { Ok(()) } + + /// Phase 4 verification. Verify block header against potentially external data. + /// Should only be called when `register_client` has been called previously. + fn verify_block_external(&self, _header: &Header) -> Result<(), EthcoreError> { Ok(()) } + + /// Perform basic/cheap transaction verification. + /// + /// This should include all cheap checks that can be done before + /// actually checking the signature, like chain-replay protection. + /// + /// NOTE This is done before the signature is recovered so avoid + /// doing any state-touching checks that might be expensive. + /// + /// TODO: Add flags for which bits of the transaction to check. + /// TODO: consider including State in the params. + fn verify_transaction_basic(&self, _t: &UnverifiedTransaction, _header: &Header) -> Result<(), transaction::Error>; + + /// Verify a particular transaction is valid. + /// + /// Unordered verification doesn't rely on the transaction execution order, + /// i.e. it should only verify stuff that doesn't assume any previous transactions + /// has already been verified and executed. + /// + /// NOTE This function consumes an `UnverifiedTransaction` and produces `SignedTransaction` + /// which implies that a heavy check of the signature is performed here. + fn verify_transaction_unordered(&self, _t: UnverifiedTransaction, _header: &Header) -> Result; + + // Transactions are verified against the parent header since the current + // state wasn't available when the tx was created + fn verify_transactions( + &self, + txs: &Vec, + parent: &Header, + client: &dyn VerifyingClient, + ) -> Result<(), transaction::Error>; + + /// Check whether the parent timestamp is valid. + fn is_timestamp_valid(&self, header_timestamp: u64, parent_timestamp: u64) -> bool { + header_timestamp > parent_timestamp + } +} diff --git a/ethcore/pod/Cargo.toml b/ethcore/pod/Cargo.toml index cef7d8542e0..f9bc2b3ad5e 100644 --- a/ethcore/pod/Cargo.toml +++ b/ethcore/pod/Cargo.toml @@ -1,8 +1,9 @@ [package] -description = "State/Account system expressed in Plain Old Data." name = "pod" -version = "0.1.0" +description = "State/Account system expressed in Plain Old Data." authors = ["Parity Technologies "] +license = "GPL-3.0" +version = "0.1.0" edition = "2018" [dependencies] diff --git a/ethcore/src/block.rs b/ethcore/src/block.rs index ef8e27db15f..19a4a70b6a6 100644 --- a/ethcore/src/block.rs +++ b/ethcore/src/block.rs @@ -39,21 +39,23 @@ use bytes::Bytes; use ethereum_types::{H256, U256, Address, Bloom}; use engines::Engine; -use error::{Error, BlockError}; use trie_vm_factories::Factories; use state_db::StateDB; use account_state::State; use trace::Tracing; use triehash::ordered_trie_root; use unexpected::{Mismatch, OutOfBounds}; -use verification::PreverifiedBlock; use vm::{EnvInfo, LastHashes}; use hash::keccak; use rlp::{RlpStream, Encodable, encode_list}; -use types::transaction::{SignedTransaction, Error as TransactionError}; -use types::header::Header; -use types::receipt::{Receipt, TransactionOutcome}; +use types::{ + block::PreverifiedBlock, + errors::{EthcoreError as Error, BlockError}, + transaction::{SignedTransaction, Error as TransactionError}, + header::Header, + receipt::{Receipt, TransactionOutcome}, +}; use executive_state::ExecutiveState; /// Block that is ready for transactions to be added. @@ -550,16 +552,16 @@ mod tests { use super::*; use engines::Engine; use vm::LastHashes; - use error::Error; use trie_vm_factories::Factories; use state_db::StateDB; use ethereum_types::Address; use std::sync::Arc; use verification::queue::kind::blocks::Unverified; use types::transaction::SignedTransaction; - use types::header::Header; - use types::view; - use types::views::BlockView; + use types::{ + header::Header, view, views::BlockView, + errors::EthcoreError as Error, + }; use hash_db::EMPTY_PREFIX; /// Enact the block given by `block_bytes` using `engine` on the database `db` with given `parent` block header diff --git a/ethcore/src/client/ancient_import.rs b/ethcore/src/client/ancient_import.rs index f89afa13a3f..22f13ff5388 100644 --- a/ethcore/src/client/ancient_import.rs +++ b/ethcore/src/client/ancient_import.rs @@ -23,7 +23,10 @@ use engines::{Engine, EpochVerifier}; use blockchain::BlockChain; use parking_lot::RwLock; use rand::Rng; -use types::header::Header; +use types::{ + header::Header, + errors::EthcoreError, +}; // do "heavy" verification on ~1/50 blocks, randomly sampled. const HEAVY_VERIFY_RATE: f32 = 0.02; @@ -51,7 +54,7 @@ impl AncientVerifier { rng: &mut R, header: &Header, chain: &BlockChain, - ) -> Result<(), ::error::Error> { + ) -> Result<(), EthcoreError> { // perform verification let verified = if let Some(ref cur_verifier) = *self.cur_verifier.read() { match rng.gen::() <= HEAVY_VERIFY_RATE { @@ -86,7 +89,7 @@ impl AncientVerifier { } fn initial_verifier(&self, header: &Header, chain: &BlockChain) - -> Result, ::error::Error> + -> Result, EthcoreError> { trace!(target: "client", "Initializing ancient block restoration."); let current_epoch_data = chain.epoch_transitions() diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index 3026290b504..14942ffc43d 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -33,24 +33,17 @@ use journaldb; use kvdb::{DBValue, KeyValueDB, DBTransaction}; use parking_lot::{Mutex, RwLock}; use rand::rngs::OsRng; -use types::transaction::{self, LocalizedTransaction, UnverifiedTransaction, SignedTransaction, Action}; use trie::{TrieSpec, TrieFactory, Trie}; -use types::ancestry_action::AncestryAction; -use types::encoded; -use types::filter::Filter; -use types::log_entry::LocalizedLogEntry; -use types::receipt::{Receipt, LocalizedReceipt}; -use types::{BlockNumber, header::Header}; use vm::{EnvInfo, LastHashes}; use hash_db::EMPTY_PREFIX; use block::{LockedBlock, Drain, ClosedBlock, OpenBlock, enact_verified, SealedBlock}; use client::ancient_import::AncientVerifier; use client::{ - Nonce, Balance, ChainInfo, BlockInfo, TransactionInfo, + Nonce, Balance, ChainInfo, TransactionInfo, ReopenBlock, PrepareOpenBlock, ScheduleInfo, ImportSealedBlock, BroadcastProposalBlock, ImportBlock, StateOrBlock, StateInfo, StateClient, Call, AccountData, BlockChain as BlockChainTrait, BlockProducer, SealedBlockImporter, - ClientIoMessage, BlockChainReset + BlockChainReset }; use client::{ BlockId, TransactionId, UncleId, TraceId, ClientConfig, BlockChainClient, @@ -59,12 +52,10 @@ use client::{ IoClient, BadBlocks, }; use client::bad_blocks; -use engines::{MAX_UNCLE_AGE, Engine, EpochTransition, ForkChoice, EngineError, SealingState}; +use client_traits::{BlockInfo, VerifyingEngine}; +use engines::{MAX_UNCLE_AGE, Engine, EpochTransition, ForkChoice, SealingState}; use engines::epoch::PendingTransition; -use error::{ - ImportError, ExecutionError, CallError, BlockError, - Error as EthcoreError, EthcoreResult, -}; +use error::{CallError, EthcoreResult}; use executive::{Executive, Executed, TransactOptions, contract_address}; use trie_vm_factories::{Factories, VmFactory}; use miner::{Miner, MinerService}; @@ -75,18 +66,32 @@ use executive_state; use state_db::StateDB; use trace::{self, TraceDB, ImportRequest as TraceImportRequest, LocalizedTrace, Database as TraceDatabase}; use transaction_ext::Transaction; +use types::{ + ancestry_action::AncestryAction, + BlockNumber, + block::PreverifiedBlock, + encoded, + errors::{EngineError, ExecutionError, BlockError, EthcoreError, SnapshotError, ImportError}, + transaction::{self, LocalizedTransaction, UnverifiedTransaction, SignedTransaction, Action}, + filter::Filter, + log_entry::LocalizedLogEntry, + receipt::{Receipt, LocalizedReceipt}, + header::Header, + client_io_message::ClientIoMessage, +}; + use verification::queue::kind::BlockLike; use verification::queue::kind::blocks::Unverified; -use verification::{PreverifiedBlock, Verifier, BlockQueue}; +use verification::{Verifier, BlockQueue}; use verification; use ansi_term::Colour; // re-export -pub use types::blockchain_info::BlockChainInfo; -pub use types::block_status::BlockStatus; pub use blockchain::CacheSize as BlockChainCacheSize; -pub use verification::QueueInfo as BlockQueueInfo; use db::{Writable, Readable, keys::BlockDetails}; +pub use types::blockchain_info::BlockChainInfo; +pub use types::block_status::BlockStatus; +pub use types::verification_queue_info::VerificationQueueInfo as BlockQueueInfo; use_contract!(registry, "res/contracts/registrar.json"); @@ -248,8 +253,13 @@ impl Importer { engine: Arc, message_channel: IoChannel, miner: Arc, - ) -> Result { - let block_queue = BlockQueue::new(config.queue.clone(), engine.clone(), message_channel.clone(), config.verifier_type.verifying_seal()); + ) -> Result { + let block_queue = BlockQueue::new( + config.queue.clone(), + engine.clone(), + message_channel.clone(), + config.verifier_type.verifying_seal() + ); Ok(Importer { import_lock: Mutex::new(()), @@ -384,7 +394,7 @@ impl Importer { if let Err(e) = verify_family_result { warn!(target: "client", "Stage 3 block verification failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e); - return Err(e.into()); + return Err(e); }; let verify_external_result = self.verifier.verify_block_external(&header, engine); @@ -704,7 +714,7 @@ impl Client { db: Arc, miner: Arc, message_channel: IoChannel, - ) -> Result, ::error::Error> { + ) -> Result, EthcoreError> { let trie_spec = match config.fat_db { true => TrieSpec::Fat, false => TrieSpec::Secure, @@ -950,7 +960,7 @@ impl Client { } // prune ancient states until below the memory limit or only the minimum amount remain. - fn prune_ancient(&self, mut state_db: StateDB, chain: &BlockChain) -> Result<(), ::error::Error> { + fn prune_ancient(&self, mut state_db: StateDB, chain: &BlockChain) -> Result<(), EthcoreError> { let number = match state_db.journal_db().latest_era() { Some(n) => n, None => return Ok(()), @@ -1145,10 +1155,10 @@ impl Client { ) -> Result<(), EthcoreError> { let db = self.state_db.read().journal_db().boxed_clone(); let best_block_number = self.chain_info().best_block_number; - let block_number = self.block_number(at).ok_or_else(|| snapshot::Error::InvalidStartingBlock(at))?; + let block_number = self.block_number(at).ok_or_else(|| SnapshotError::InvalidStartingBlock(at))?; if db.is_pruned() && self.pruning_info().earliest_state > block_number { - return Err(snapshot::Error::OldBlockPrunedDB.into()); + return Err(SnapshotError::OldBlockPrunedDB.into()); } let history = ::std::cmp::min(self.history, 1000); @@ -1162,17 +1172,17 @@ impl Client { match self.block_hash(BlockId::Number(start_num)) { Some(h) => h, - None => return Err(snapshot::Error::InvalidStartingBlock(at).into()), + None => return Err(SnapshotError::InvalidStartingBlock(at).into()), } } _ => match self.block_hash(at) { Some(hash) => hash, - None => return Err(snapshot::Error::InvalidStartingBlock(at).into()), + None => return Err(SnapshotError::InvalidStartingBlock(at).into()), }, }; let processing_threads = self.config.snapshot.processing_threads; - let chunker = self.engine.snapshot_components().ok_or(snapshot::Error::SnapshotsUnsupported)?; + let chunker = self.engine.snapshot_components().ok_or(SnapshotError::SnapshotsUnsupported)?; snapshot::take_snapshot( chunker, &self.chain.read(), diff --git a/ethcore/src/client/mod.rs b/ethcore/src/client/mod.rs index a7260b82509..9aca6c26a98 100644 --- a/ethcore/src/client/mod.rs +++ b/ethcore/src/client/mod.rs @@ -35,7 +35,7 @@ pub use self::io_message::ClientIoMessage; pub use self::test_client::{TestBlockChainClient, EachBlockWith, TestState}; pub use self::chain_notify::{ChainNotify, NewBlocks, ChainRoute, ChainRouteType, ChainMessageType}; pub use self::traits::{ - Nonce, Balance, ChainInfo, BlockInfo, ReopenBlock, PrepareOpenBlock, TransactionInfo, ScheduleInfo, ImportSealedBlock, BroadcastProposalBlock, ImportBlock, + Nonce, Balance, ChainInfo, ReopenBlock, PrepareOpenBlock, TransactionInfo, ScheduleInfo, ImportSealedBlock, BroadcastProposalBlock, ImportBlock, StateOrBlock, StateClient, Call, EngineInfo, AccountData, BlockChain, BlockProducer, SealedBlockImporter, BadBlocks, BlockChainReset }; diff --git a/ethcore/src/client/test_client.rs b/ethcore/src/client/test_client.rs index 693fdad01bd..59cd1c69160 100644 --- a/ethcore/src/client/test_client.rs +++ b/ethcore/src/client/test_client.rs @@ -40,6 +40,7 @@ use types::transaction::{self, Transaction, LocalizedTransaction, SignedTransact use types::BlockNumber; use types::basic_account::BasicAccount; use types::encoded; +use types::errors::EthcoreError as Error; use types::filter::Filter; use types::header::Header; use types::log_entry::LocalizedLogEntry; @@ -52,15 +53,16 @@ use vm::Schedule; use block::{OpenBlock, SealedBlock, ClosedBlock}; use call_contract::{CallContract, RegistryInfo}; use client::{ - Nonce, Balance, ChainInfo, BlockInfo, ReopenBlock, TransactionInfo, + Nonce, Balance, ChainInfo, ReopenBlock, TransactionInfo, PrepareOpenBlock, BlockChainClient, BlockChainInfo, BlockStatus, BlockId, Mode, TransactionId, UncleId, TraceId, TraceFilter, LastHashes, CallAnalytics, ProvingBlockChainClient, ScheduleInfo, ImportSealedBlock, BroadcastProposalBlock, ImportBlock, StateOrBlock, Call, StateClient, EngineInfo, AccountData, BlockChain, BlockProducer, SealedBlockImporter, IoClient, BadBlocks }; +use client_traits::BlockInfo; use engines::Engine; -use error::{Error, EthcoreResult}; +use error::EthcoreResult; use executed::CallError; use executive::Executed; use journaldb; diff --git a/ethcore/src/client/traits.rs b/ethcore/src/client/traits.rs index 50fe75a699b..2f99f7eedbd 100644 --- a/ethcore/src/client/traits.rs +++ b/ethcore/src/client/traits.rs @@ -25,32 +25,36 @@ use ethereum_types::{H256, U256, Address}; use evm::Schedule; use itertools::Itertools; use kvdb::DBValue; -use types::transaction::{self, LocalizedTransaction, SignedTransaction}; -use types::BlockNumber; -use types::basic_account::BasicAccount; -use types::block_status::BlockStatus; -use types::blockchain_info::BlockChainInfo; -use types::call_analytics::CallAnalytics; -use types::encoded; -use types::filter::Filter; -use types::header::Header; -use types::ids::*; -use types::log_entry::LocalizedLogEntry; -use types::pruning_info::PruningInfo; -use types::receipt::LocalizedReceipt; -use types::trace_filter::Filter as TraceFilter; +use types::{ + transaction::{self, LocalizedTransaction, SignedTransaction}, + BlockNumber, + basic_account::BasicAccount, + block_status::BlockStatus, + blockchain_info::BlockChainInfo, + call_analytics::CallAnalytics, + encoded, + errors::EthcoreError as Error, + filter::Filter, + header::Header, + ids::*, + log_entry::LocalizedLogEntry, + pruning_info::PruningInfo, + receipt::LocalizedReceipt, + trace_filter::Filter as TraceFilter, + verification_queue_info::VerificationQueueInfo as BlockQueueInfo, +}; use vm::LastHashes; use block::{OpenBlock, SealedBlock, ClosedBlock}; use client::Mode; +use client_traits::BlockInfo; use engines::Engine; -use error::{Error, EthcoreResult}; +use error::EthcoreResult; use executed::CallError; use executive::Executed; use account_state::state::StateInfo; use trace::LocalizedTrace; -use verification::queue::QueueInfo as BlockQueueInfo; -use verification::queue::kind::blocks::Unverified; +use verification::queue::kind::blocks::Unverified; // todo this is reexported from common_types /// State information to be used during client query pub enum StateOrBlock { @@ -112,21 +116,6 @@ pub trait ChainInfo { fn chain_info(&self) -> BlockChainInfo; } -/// Provides various information on a block by it's ID -pub trait BlockInfo { - /// Get raw block header data by block id. - fn block_header(&self, id: BlockId) -> Option; - - /// Get the best block header. - fn best_block_header(&self) -> Header; - - /// Get raw block data by block header hash. - fn block(&self, id: BlockId) -> Option; - - /// Get address code hash at given block's state. - fn code_hash(&self, address: &Address, id: BlockId) -> Option; -} - /// Provides various information on a transaction by it's ID pub trait TransactionInfo { /// Get the hash of block that contains the transaction, if any. diff --git a/ethcore/src/engines/authority_round/mod.rs b/ethcore/src/engines/authority_round/mod.rs index 360ec0443ec..440efca57fb 100644 --- a/ethcore/src/engines/authority_round/mod.rs +++ b/ethcore/src/engines/authority_round/mod.rs @@ -26,10 +26,10 @@ use std::time::{UNIX_EPOCH, Duration}; use block::*; use client::EngineClient; -use engines::{Engine, Seal, SealingState, EngineError, ConstructedVerifier}; +use client_traits::VerifyingEngine; +use engines::{Engine, Seal, SealingState, ConstructedVerifier}; use engines::block_reward; use engines::block_reward::{BlockRewardContract, RewardKind}; -use error::{Error, BlockError}; use ethjson; use machine::{AuxiliaryData, Call, Machine}; use hash::keccak; @@ -43,9 +43,14 @@ use rlp::{encode, Decodable, DecoderError, Encodable, RlpStream, Rlp}; use ethereum_types::{H256, H520, Address, U128, U256}; use parking_lot::{Mutex, RwLock}; use time_utils::CheckedSystemTime; -use types::BlockNumber; -use types::header::{Header, ExtendedHeader}; -use types::ancestry_action::AncestryAction; +use types::{ + ancestry_action::AncestryAction, + BlockNumber, + header::{Header, ExtendedHeader}, + engines::{params::CommonParams}, + errors::{BlockError, EthcoreError as Error, EngineError}, + transaction::{self, UnverifiedTransaction, SignedTransaction}, +}; use unexpected::{Mismatch, OutOfBounds}; mod finality; @@ -941,26 +946,168 @@ impl IoHandler<()> for TransitionHandler { } } -impl Engine for AuthorityRound { - fn name(&self) -> &str { "AuthorityRound" } - - fn machine(&self) -> &Machine { &self.machine } - +impl VerifyingEngine for AuthorityRound { /// Three fields - consensus step and the corresponding proposer signature, and a list of empty /// step messages (which should be empty if no steps are skipped) fn seal_fields(&self, header: &Header) -> usize { header_expected_seal_fields(header, self.empty_steps_transition) } - fn step(&self) { - self.step.inner.increment(); - self.step.can_propose.store(true, AtomicOrdering::SeqCst); - if let Some(ref weak) = *self.client.read() { - if let Some(c) = weak.upgrade() { - c.update_sealing(); + fn params(&self) -> &CommonParams { + self.machine.params() + } + + /// Check the number of seal fields. + fn verify_block_basic(&self, header: &Header) -> Result<(), Error> { + if header.number() >= self.validate_score_transition && *header.difficulty() >= U256::from(U128::max_value()) { + return Err(From::from(BlockError::DifficultyOutOfBounds( + OutOfBounds { min: None, max: Some(U256::from(U128::max_value())), found: *header.difficulty() } + ))); + } + + match verify_timestamp(&self.step.inner, header_step(header, self.empty_steps_transition)?) { + Err(BlockError::InvalidSeal) => { + // This check runs in Phase 1 where there is no guarantee that the parent block is + // already imported, therefore the call to `epoch_set` may fail. In that case we + // won't report the misbehavior but this is not a concern because: + // - Authorities will have a signing key available to report and it's expected that + // they'll be up-to-date and importing, therefore the parent header will most likely + // be available + // - Even if you are an authority that is syncing the chain, the contract will most + // likely ignore old reports + // - This specific check is only relevant if you're importing (since it checks + // against wall clock) + if let Ok((_, set_number)) = self.epoch_set(header) { + trace!(target: "engine", "Reporting benign misbehaviour (cause: InvalidSeal) at block #{}, epoch set number {}. Own address: {}", + header.number(), set_number, self.address().unwrap_or_default()); + self.validators.report_benign(header.author(), set_number, header.number()); + } + + Err(BlockError::InvalidSeal.into()) + } + Err(e) => Err(e.into()), + Ok(()) => Ok(()), + } + } + + /// Do the step and gas limit validation. + fn verify_block_family(&self, header: &Header, parent: &Header) -> Result<(), Error> { + let step = header_step(header, self.empty_steps_transition)?; + let parent_step = header_step(parent, self.empty_steps_transition)?; + + let (validators, set_number) = self.epoch_set(header).map_err(Into::into)?; + + // Ensure header is from the step after parent. + if step == parent_step + || (header.number() >= self.validate_step_transition && step <= parent_step) { + warn!(target: "engine", "Multiple blocks proposed for step {}.", parent_step); + + self.validators.report_malicious(header.author(), set_number, header.number(), Default::default()); + Err(EngineError::DoubleVote(*header.author()))?; + } + + // If empty step messages are enabled we will validate the messages in the seal, missing messages are not + // reported as there's no way to tell whether the empty step message was never sent or simply not included. + let empty_steps_len = if header.number() >= self.empty_steps_transition { + let validate_empty_steps = || -> Result { + let strict_empty_steps = header.number() >= self.strict_empty_steps_transition; + let empty_steps = header_empty_steps(header)?; + let empty_steps_len = empty_steps.len(); + let mut prev_empty_step = 0; + + for empty_step in empty_steps { + if empty_step.step <= parent_step || empty_step.step >= step { + Err(EngineError::InsufficientProof( + format!("empty step proof for invalid step: {:?}", empty_step.step)))?; + } + + if empty_step.parent_hash != *header.parent_hash() { + Err(EngineError::InsufficientProof( + format!("empty step proof for invalid parent hash: {:?}", empty_step.parent_hash)))?; + } + + if !empty_step.verify(&*validators).unwrap_or(false) { + Err(EngineError::InsufficientProof( + format!("invalid empty step proof: {:?}", empty_step)))?; + } + + if strict_empty_steps { + if empty_step.step <= prev_empty_step { + Err(EngineError::InsufficientProof(format!( + "{} empty step: {:?}", + if empty_step.step == prev_empty_step { "duplicate" } else { "unordered" }, + empty_step + )))?; + } + + prev_empty_step = empty_step.step; + } + } + + Ok(empty_steps_len) + }; + + match validate_empty_steps() { + Ok(len) => len, + Err(err) => { + trace!(target: "engine", "Reporting benign misbehaviour (cause: invalid empty steps) at block #{}, epoch set number {}. Own address: {}", + header.number(), set_number, self.address().unwrap_or_default()); + self.validators.report_benign(header.author(), set_number, header.number()); + return Err(err); + }, + } + } else { + self.report_skipped(header, step, parent_step, &*validators, set_number); + + 0 + }; + + if header.number() >= self.validate_score_transition { + let expected_difficulty = calculate_score(parent_step.into(), step.into(), empty_steps_len.into()); + if header.difficulty() != &expected_difficulty { + return Err(From::from(BlockError::InvalidDifficulty(Mismatch { expected: expected_difficulty, found: header.difficulty().clone() }))); } } + + Ok(()) + } + + // Check the validators. + fn verify_block_external(&self, header: &Header) -> Result<(), Error> { + let (validators, set_number) = self.epoch_set(header).map_err(Into::into)?; + + // verify signature against fixed list, but reports should go to the + // contract itself. + let res = verify_external(header, &*validators, self.empty_steps_transition); + match res { + Err(Error::Engine(EngineError::NotProposer(_))) => { + trace!(target: "engine", "Reporting benign misbehaviour (cause: block from incorrect proposer) at block #{}, epoch set number {}. Own address: {}", + header.number(), set_number, self.address().unwrap_or_default()); + self.validators.report_benign(header.author(), set_number, header.number()); + }, + Ok(_) => { + // we can drop all accumulated empty step messages that are older than this header's step + let header_step = header_step(header, self.empty_steps_transition)?; + self.clear_empty_steps(header_step.into()); + }, + _ => {}, + } + res.map_err(Into::into) + } + + fn verify_transaction_basic(&self, t: &UnverifiedTransaction, header: &Header) -> Result<(), transaction::Error> { + self.machine().verify_transaction_basic(t, header) + } + + fn verify_transaction_unordered(&self, t: UnverifiedTransaction, header: &Header) -> Result { + self.machine().verify_transaction_unordered(t, header) } +} + +impl Engine for AuthorityRound { + fn name(&self) -> &str { "AuthorityRound" } + + fn machine(&self) -> &Machine { &self.machine } /// Additional engine-specific information for the user/developer concerning `header`. fn extra_info(&self, header: &Header) -> BTreeMap { @@ -1007,18 +1154,73 @@ impl Engine for AuthorityRound { } } - fn populate_from_parent(&self, header: &mut Header, parent: &Header) { - let parent_step = header_step(parent, self.empty_steps_transition).expect("Header has been verified; qed"); - let current_step = self.step.inner.load(); + fn on_new_block( + &self, + block: &mut ExecutedBlock, + epoch_begin: bool, + ) -> Result<(), Error> { + // with immediate transitions, we don't use the epoch mechanism anyway. + // the genesis is always considered an epoch, but we ignore it intentionally. + if self.immediate_transitions || !epoch_begin { return Ok(()) } - let current_empty_steps_len = if header.number() >= self.empty_steps_transition { - self.empty_steps(parent_step, current_step, parent.hash()).len() - } else { - 0 + // genesis is never a new block, but might as well check. + let header = block.header.clone(); + let first = header.number() == 0; + + let mut call = |to, data| { + let result = self.machine.execute_as_system( + block, + to, + U256::max_value(), // unbounded gas? maybe make configurable. + Some(data), + ); + + result.map_err(|e| format!("{}", e)) }; - let score = calculate_score(parent_step, current_step, current_empty_steps_len); - header.set_difficulty(score); + self.validators.on_epoch_begin(first, &header, &mut call) + } + + /// Apply the block reward on finalisation of the block. + fn on_close_block( + &self, + block: &mut ExecutedBlock, + parent: &Header, + ) -> Result<(), Error> { + let mut beneficiaries = Vec::new(); + if block.header.number() >= self.empty_steps_transition { + let empty_steps = if block.header.seal().is_empty() { + // this is a new block, calculate rewards based on the empty steps messages we have accumulated + let parent_step = header_step(parent, self.empty_steps_transition)?; + let current_step = self.step.inner.load(); + self.empty_steps(parent_step, current_step, parent.hash()) + } else { + // we're verifying a block, extract empty steps from the seal + header_empty_steps(&block.header)? + }; + + for empty_step in empty_steps { + let author = empty_step.author()?; + beneficiaries.push((author, RewardKind::EmptyStep)); + } + } + + let author = *block.header.author(); + beneficiaries.push((author, RewardKind::Author)); + + let rewards: Vec<_> = match self.block_reward_contract { + Some(ref c) if block.header.number() >= self.block_reward_contract_transition => { + let mut call = super::default_system_or_code_call(&self.machine, block); + + let rewards = c.reward(&beneficiaries, &mut call)?; + rewards.into_iter().map(|(author, amount)| (author, RewardKind::External, amount)).collect() + }, + _ => { + beneficiaries.into_iter().map(|(author, reward_kind)| (author, reward_kind, self.block_reward)).collect() + }, + }; + + block_reward::apply_block_rewards(&rewards, block, &self.machine) } fn sealing_state(&self) -> SealingState { @@ -1068,28 +1270,6 @@ impl Engine for AuthorityRound { SealingState::Ready } - fn handle_message(&self, rlp: &[u8]) -> Result<(), EngineError> { - fn fmt_err(x: T) -> EngineError { - EngineError::MalformedMessage(format!("{:?}", x)) - } - - let rlp = Rlp::new(rlp); - let empty_step: EmptyStep = rlp.as_val().map_err(fmt_err)?; - - if empty_step.verify(&*self.validators).unwrap_or(false) { - if self.step.inner.check_future(empty_step.step).is_ok() { - trace!(target: "engine", "handle_message: received empty step message {:?}", empty_step); - self.handle_empty_step_message(empty_step); - } else { - trace!(target: "engine", "handle_message: empty step message from the future {:?}", empty_step); - } - } else { - trace!(target: "engine", "handle_message: received invalid step message {:?}", empty_step); - }; - - Ok(()) - } - /// Attempt to seal the block internally. /// /// This operation is synchronous and may (quite reasonably) not be available, in which case @@ -1208,213 +1388,6 @@ impl Engine for AuthorityRound { Ok(()) } - fn on_new_block( - &self, - block: &mut ExecutedBlock, - epoch_begin: bool, - ) -> Result<(), Error> { - // with immediate transitions, we don't use the epoch mechanism anyway. - // the genesis is always considered an epoch, but we ignore it intentionally. - if self.immediate_transitions || !epoch_begin { return Ok(()) } - - // genesis is never a new block, but might as well check. - let header = block.header.clone(); - let first = header.number() == 0; - - let mut call = |to, data| { - let result = self.machine.execute_as_system( - block, - to, - U256::max_value(), // unbounded gas? maybe make configurable. - Some(data), - ); - - result.map_err(|e| format!("{}", e)) - }; - - self.validators.on_epoch_begin(first, &header, &mut call) - } - - /// Apply the block reward on finalisation of the block. - fn on_close_block( - &self, - block: &mut ExecutedBlock, - parent: &Header, - ) -> Result<(), Error> { - let mut beneficiaries = Vec::new(); - if block.header.number() >= self.empty_steps_transition { - let empty_steps = if block.header.seal().is_empty() { - // this is a new block, calculate rewards based on the empty steps messages we have accumulated - let parent_step = header_step(parent, self.empty_steps_transition)?; - let current_step = self.step.inner.load(); - self.empty_steps(parent_step, current_step, parent.hash()) - } else { - // we're verifying a block, extract empty steps from the seal - header_empty_steps(&block.header)? - }; - - for empty_step in empty_steps { - let author = empty_step.author()?; - beneficiaries.push((author, RewardKind::EmptyStep)); - } - } - - let author = *block.header.author(); - beneficiaries.push((author, RewardKind::Author)); - - let rewards: Vec<_> = match self.block_reward_contract { - Some(ref c) if block.header.number() >= self.block_reward_contract_transition => { - let mut call = super::default_system_or_code_call(&self.machine, block); - - let rewards = c.reward(&beneficiaries, &mut call)?; - rewards.into_iter().map(|(author, amount)| (author, RewardKind::External, amount)).collect() - }, - _ => { - beneficiaries.into_iter().map(|(author, reward_kind)| (author, reward_kind, self.block_reward)).collect() - }, - }; - - block_reward::apply_block_rewards(&rewards, block, &self.machine) - } - - /// Check the number of seal fields. - fn verify_block_basic(&self, header: &Header) -> Result<(), Error> { - if header.number() >= self.validate_score_transition && *header.difficulty() >= U256::from(U128::max_value()) { - return Err(From::from(BlockError::DifficultyOutOfBounds( - OutOfBounds { min: None, max: Some(U256::from(U128::max_value())), found: *header.difficulty() } - ))); - } - - match verify_timestamp(&self.step.inner, header_step(header, self.empty_steps_transition)?) { - Err(BlockError::InvalidSeal) => { - // This check runs in Phase 1 where there is no guarantee that the parent block is - // already imported, therefore the call to `epoch_set` may fail. In that case we - // won't report the misbehavior but this is not a concern because: - // - Authorities will have a signing key available to report and it's expected that - // they'll be up-to-date and importing, therefore the parent header will most likely - // be available - // - Even if you are an authority that is syncing the chain, the contract will most - // likely ignore old reports - // - This specific check is only relevant if you're importing (since it checks - // against wall clock) - if let Ok((_, set_number)) = self.epoch_set(header) { - trace!(target: "engine", "Reporting benign misbehaviour (cause: InvalidSeal) at block #{}, epoch set number {}. Own address: {}", - header.number(), set_number, self.address().unwrap_or_default()); - self.validators.report_benign(header.author(), set_number, header.number()); - } - - Err(BlockError::InvalidSeal.into()) - } - Err(e) => Err(e.into()), - Ok(()) => Ok(()), - } - } - - /// Do the step and gas limit validation. - fn verify_block_family(&self, header: &Header, parent: &Header) -> Result<(), Error> { - let step = header_step(header, self.empty_steps_transition)?; - let parent_step = header_step(parent, self.empty_steps_transition)?; - - let (validators, set_number) = self.epoch_set(header)?; - - // Ensure header is from the step after parent. - if step == parent_step - || (header.number() >= self.validate_step_transition && step <= parent_step) { - warn!(target: "engine", "Multiple blocks proposed for step {}.", parent_step); - - self.validators.report_malicious(header.author(), set_number, header.number(), Default::default()); - Err(EngineError::DoubleVote(*header.author()))?; - } - - // If empty step messages are enabled we will validate the messages in the seal, missing messages are not - // reported as there's no way to tell whether the empty step message was never sent or simply not included. - let empty_steps_len = if header.number() >= self.empty_steps_transition { - let validate_empty_steps = || -> Result { - let strict_empty_steps = header.number() >= self.strict_empty_steps_transition; - let empty_steps = header_empty_steps(header)?; - let empty_steps_len = empty_steps.len(); - let mut prev_empty_step = 0; - - for empty_step in empty_steps { - if empty_step.step <= parent_step || empty_step.step >= step { - Err(EngineError::InsufficientProof( - format!("empty step proof for invalid step: {:?}", empty_step.step)))?; - } - - if empty_step.parent_hash != *header.parent_hash() { - Err(EngineError::InsufficientProof( - format!("empty step proof for invalid parent hash: {:?}", empty_step.parent_hash)))?; - } - - if !empty_step.verify(&*validators).unwrap_or(false) { - Err(EngineError::InsufficientProof( - format!("invalid empty step proof: {:?}", empty_step)))?; - } - - if strict_empty_steps { - if empty_step.step <= prev_empty_step { - Err(EngineError::InsufficientProof(format!( - "{} empty step: {:?}", - if empty_step.step == prev_empty_step { "duplicate" } else { "unordered" }, - empty_step - )))?; - } - - prev_empty_step = empty_step.step; - } - } - - Ok(empty_steps_len) - }; - - match validate_empty_steps() { - Ok(len) => len, - Err(err) => { - trace!(target: "engine", "Reporting benign misbehaviour (cause: invalid empty steps) at block #{}, epoch set number {}. Own address: {}", - header.number(), set_number, self.address().unwrap_or_default()); - self.validators.report_benign(header.author(), set_number, header.number()); - return Err(err); - }, - } - } else { - self.report_skipped(header, step, parent_step, &*validators, set_number); - - 0 - }; - - if header.number() >= self.validate_score_transition { - let expected_difficulty = calculate_score(parent_step.into(), step.into(), empty_steps_len.into()); - if header.difficulty() != &expected_difficulty { - return Err(From::from(BlockError::InvalidDifficulty(Mismatch { expected: expected_difficulty, found: header.difficulty().clone() }))); - } - } - - Ok(()) - } - - // Check the validators. - fn verify_block_external(&self, header: &Header) -> Result<(), Error> { - let (validators, set_number) = self.epoch_set(header)?; - - // verify signature against fixed list, but reports should go to the - // contract itself. - let res = verify_external(header, &*validators, self.empty_steps_transition); - match res { - Err(Error::Engine(EngineError::NotProposer(_))) => { - trace!(target: "engine", "Reporting benign misbehaviour (cause: block from incorrect proposer) at block #{}, epoch set number {}. Own address: {}", - header.number(), set_number, self.address().unwrap_or_default()); - self.validators.report_benign(header.author(), set_number, header.number()); - }, - Ok(_) => { - // we can drop all accumulated empty step messages that are older than this header's step - let header_step = header_step(header, self.empty_steps_transition)?; - self.clear_empty_steps(header_step.into()); - }, - _ => {}, - } - res - } - fn genesis_epoch_data(&self, header: &Header, call: &Call) -> Result, String> { self.validators.genesis_epoch_data(header, call) .map(|set_proof| combine_proofs(0, &set_proof, &[])) @@ -1429,49 +1402,6 @@ impl Engine for AuthorityRound { self.validators.signals_epoch_end(first, header, aux) } - fn is_epoch_end_light( - &self, - chain_head: &Header, - chain: &super::Headers
, - transition_store: &super::PendingTransitionStore, - ) -> Option> { - // epochs only matter if we want to support light clients. - if self.immediate_transitions { return None } - - let epoch_transition_hash = { - let client = match self.client.read().as_ref().and_then(|weak| weak.upgrade()) { - Some(client) => client, - None => { - warn!(target: "engine", "Unable to check for epoch end: missing client ref."); - return None; - } - }; - - let mut epoch_manager = self.epoch_manager.lock(); - if !epoch_manager.zoom_to_after(&*client, &self.machine, &*self.validators, *chain_head.parent_hash()) { - return None; - } - - epoch_manager.epoch_transition_hash - }; - - let mut hash = *chain_head.parent_hash(); - - let mut ancestry = itertools::repeat_call(move || { - chain(hash).and_then(|header| { - if header.number() == 0 { return None } - hash = *header.parent_hash(); - Some(header) - }) - }) - .while_some() - .take_while(|header| header.hash() != epoch_transition_hash); - - let finalized = self.build_finality(chain_head, &mut ancestry); - - self.is_epoch_end(chain_head, &finalized, chain, transition_store) - } - fn is_epoch_end( &self, chain_head: &Header, @@ -1543,6 +1473,49 @@ impl Engine for AuthorityRound { None } + fn is_epoch_end_light( + &self, + chain_head: &Header, + chain: &super::Headers
, + transition_store: &super::PendingTransitionStore, + ) -> Option> { + // epochs only matter if we want to support light clients. + if self.immediate_transitions { return None } + + let epoch_transition_hash = { + let client = match self.client.read().as_ref().and_then(|weak| weak.upgrade()) { + Some(client) => client, + None => { + warn!(target: "engine", "Unable to check for epoch end: missing client ref."); + return None; + } + }; + + let mut epoch_manager = self.epoch_manager.lock(); + if !epoch_manager.zoom_to_after(&*client, &self.machine, &*self.validators, *chain_head.parent_hash()) { + return None; + } + + epoch_manager.epoch_transition_hash + }; + + let mut hash = *chain_head.parent_hash(); + + let mut ancestry = itertools::repeat_call(move || { + chain(hash).and_then(|header| { + if header.number() == 0 { return None } + hash = *header.parent_hash(); + Some(header) + }) + }) + .while_some() + .take_while(|header| header.hash() != epoch_transition_hash); + + let finalized = self.build_finality(chain_head, &mut ancestry); + + self.is_epoch_end(chain_head, &finalized, chain, transition_store) + } + fn epoch_verifier<'a>(&self, _header: &Header, proof: &'a [u8]) -> ConstructedVerifier<'a> { let (signal_number, set_proof, finality_proof) = match destructure_proofs(proof) { Ok(x) => x, @@ -1567,9 +1540,40 @@ impl Engine for AuthorityRound { } } - fn register_client(&self, client: Weak) { - *self.client.write() = Some(client.clone()); - self.validators.register_client(client); + fn populate_from_parent(&self, header: &mut Header, parent: &Header) { + let parent_step = header_step(parent, self.empty_steps_transition).expect("Header has been verified; qed"); + let current_step = self.step.inner.load(); + + let current_empty_steps_len = if header.number() >= self.empty_steps_transition { + self.empty_steps(parent_step, current_step, parent.hash()).len() + } else { + 0 + }; + + let score = calculate_score(parent_step, current_step, current_empty_steps_len); + header.set_difficulty(score); + } + + fn handle_message(&self, rlp: &[u8]) -> Result<(), EngineError> { + fn fmt_err(x: T) -> EngineError { + EngineError::MalformedMessage(format!("{:?}", x)) + } + + let rlp = Rlp::new(rlp); + let empty_step: EmptyStep = rlp.as_val().map_err(fmt_err)?; + + if empty_step.verify(&*self.validators).unwrap_or(false) { + if self.step.inner.check_future(empty_step.step).is_ok() { + trace!(target: "engine", "handle_message: received empty step message {:?}", empty_step); + self.handle_empty_step_message(empty_step); + } else { + trace!(target: "engine", "handle_message: empty step message from the future {:?}", empty_step); + } + } else { + trace!(target: "engine", "handle_message: received invalid step message {:?}", empty_step); + }; + + Ok(()) } fn set_signer(&self, signer: Box) { @@ -1584,6 +1588,21 @@ impl Engine for AuthorityRound { ) } + fn register_client(&self, client: Weak) { + *self.client.write() = Some(client.clone()); + self.validators.register_client(client); + } + + fn step(&self) { + self.step.inner.increment(); + self.step.can_propose.store(true, AtomicOrdering::SeqCst); + if let Some(ref weak) = *self.client.read() { + if let Some(c) = weak.upgrade() { + c.update_sealing(); + } + } + } + fn snapshot_components(&self) -> Option> { if self.immediate_transitions { None @@ -1615,7 +1634,12 @@ mod tests { use accounts::AccountProvider; use ethereum_types::{Address, H520, H256, U256}; use ethkey::Signature; - use types::header::Header; + use types::{ + header::Header, + engines::params::CommonParams, + errors::{EthcoreError as Error, EngineError}, + transaction::{Action, Transaction}, + }; use rlp::encode; use block::*; use test_helpers::{ @@ -1623,10 +1647,8 @@ mod tests { TestNotify }; use spec::Spec; - use types::transaction::{Action, Transaction}; - use engines::{Seal, Engine, EngineError}; + use engines::{Seal, Engine}; use engines::validator_set::{TestSet, SimpleList}; - use error::Error; use super::{AuthorityRoundParams, AuthorityRound, EmptyStep, SealedEmptyStep, calculate_score}; use machine::Machine; @@ -1653,7 +1675,7 @@ mod tests { // mutate aura params f(&mut params); // create engine - let mut c_params = ::spec::CommonParams::default(); + let mut c_params = CommonParams::default(); c_params.gas_limit_bound_divisor = 5.into(); let machine = Machine::regular(c_params, Default::default()); AuthorityRound::new(params, machine).unwrap() diff --git a/ethcore/src/engines/basic_authority.rs b/ethcore/src/engines/basic_authority.rs index 8232c0ff260..b6d9ac0191b 100644 --- a/ethcore/src/engines/basic_authority.rs +++ b/ethcore/src/engines/basic_authority.rs @@ -21,13 +21,19 @@ use ethereum_types::{H256, H520}; use parking_lot::RwLock; use ethkey::{self, Signature}; use block::*; -use engines::{Engine, Seal, SealingState, ConstructedVerifier, EngineError}; +use engines::{Engine, Seal, SealingState, ConstructedVerifier}; use engines::signer::EngineSigner; -use error::{BlockError, Error}; use ethjson; use client::EngineClient; +use client_traits::VerifyingEngine; use machine::{AuxiliaryData, Call, Machine}; -use types::header::Header; +use types::{ + header::Header, + engines::params::CommonParams, + transaction::{self, UnverifiedTransaction, SignedTransaction}, + errors::{EngineError, BlockError, EthcoreError as Error} +}; + use super::validator_set::{ValidatorSet, SimpleList, new_validator_set}; /// `BasicAuthority` params. @@ -90,14 +96,32 @@ impl BasicAuthority { } } +impl VerifyingEngine for BasicAuthority { + // One field - the signature + fn seal_fields(&self, _header: &Header) -> usize { 1 } + + fn params(&self) -> &CommonParams { + self.machine.params() + } + + fn verify_block_external(&self, header: &Header) -> Result<(), Error> { + verify_external(header, &*self.validators).map_err(Into::into) + } + + fn verify_transaction_basic(&self, t: &UnverifiedTransaction, header: &Header) -> Result<(), transaction::Error> { + self.machine().verify_transaction_basic(t, header) + } + + fn verify_transaction_unordered(&self, t: UnverifiedTransaction, header: &Header) -> Result { + self.machine().verify_transaction_unordered(t, header) + } +} + impl Engine for BasicAuthority { fn name(&self) -> &str { "BasicAuthority" } fn machine(&self) -> &Machine { &self.machine } - // One field - the signature - fn seal_fields(&self, _header: &Header) -> usize { 1 } - fn sealing_state(&self) -> SealingState { if self.signer.read().is_some() { SealingState::Ready @@ -125,10 +149,6 @@ impl Engine for BasicAuthority { Ok(()) } - fn verify_block_external(&self, header: &Header) -> Result<(), Error> { - verify_external(header, &*self.validators) - } - fn genesis_epoch_data(&self, header: &Header, call: &Call) -> Result, String> { self.validators.genesis_epoch_data(header, call) } @@ -189,10 +209,6 @@ impl Engine for BasicAuthority { } } - fn register_client(&self, client: Weak) { - self.validators.register_client(client); - } - fn set_signer(&self, signer: Box) { *self.signer.write() = Some(signer); } @@ -205,6 +221,10 @@ impl Engine for BasicAuthority { ) } + fn register_client(&self, client: Weak) { + self.validators.register_client(client); + } + fn snapshot_components(&self) -> Option> { None } diff --git a/ethcore/src/engines/block_reward.rs b/ethcore/src/engines/block_reward.rs index c7266368df8..908fc9ceb87 100644 --- a/ethcore/src/engines/block_reward.rs +++ b/ethcore/src/engines/block_reward.rs @@ -23,10 +23,12 @@ use ethereum_types::{H160, Address, U256}; use std::sync::Arc; use hash::keccak; -use error::Error; use machine::Machine; use trace; -use types::BlockNumber; +use types::{ + BlockNumber, + errors::{EngineError, EthcoreError as Error}, +}; use super::{SystemOrCodeCall, SystemOrCodeCallKind}; use trace::{Tracer, ExecutiveTracer, Tracing}; use block::ExecutedBlock; @@ -120,7 +122,7 @@ impl BlockRewardContract { let output = caller(self.kind.clone(), input) .map_err(Into::into) - .map_err(::engines::EngineError::FailedSystemCall)?; + .map_err(EngineError::FailedSystemCall)?; // since this is a non-constant call we can't use ethabi's function output // deserialization, sadness ensues. @@ -131,7 +133,7 @@ impl BlockRewardContract { let tokens = ethabi::decode(types, &output) .map_err(|err| err.to_string()) - .map_err(::engines::EngineError::FailedSystemCall)?; + .map_err(EngineError::FailedSystemCall)?; assert!(tokens.len() == 2); @@ -139,7 +141,7 @@ impl BlockRewardContract { let rewards = tokens[1].clone().to_array().expect("type checked by ethabi::decode; qed"); if addresses.len() != rewards.len() { - return Err(::engines::EngineError::FailedSystemCall( + return Err(EngineError::FailedSystemCall( "invalid data returned by reward contract: both arrays must have the same size".into() ).into()); } diff --git a/ethcore/src/engines/clique/block_state.rs b/ethcore/src/engines/clique/block_state.rs index 913053eaa57..787dd53dc12 100644 --- a/ethcore/src/engines/clique/block_state.rs +++ b/ethcore/src/engines/clique/block_state.rs @@ -18,15 +18,16 @@ use std::collections::{HashMap, BTreeSet, VecDeque}; use std::fmt; use std::time::{Duration, SystemTime, UNIX_EPOCH}; -use engines::EngineError; use engines::clique::util::{extract_signers, recover_creator}; use engines::clique::{VoteType, DIFF_INTURN, DIFF_NOTURN, NULL_AUTHOR, SIGNING_DELAY_NOTURN_MS}; -use error::{Error, BlockError}; use ethereum_types::{Address, H64}; use rand::Rng; use time_utils::CheckedSystemTime; -use types::BlockNumber; -use types::header::Header; +use types::{ + BlockNumber, + header::Header, + errors::{BlockError, EthcoreError as Error, EngineError}, +}; use unexpected::Mismatch; /// Type that keeps track of the state for a given vote @@ -90,7 +91,7 @@ pub struct CliqueBlockState { } impl fmt::Display for CliqueBlockState { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let signers: Vec = self.signers.iter() .map(|s| format!("{} {:?}", @@ -107,11 +108,11 @@ impl fmt::Display for CliqueBlockState { let rm_votes = self.votes_history.iter().filter(|v| v.kind == VoteType::Remove).count(); let reverted_votes = self.votes_history.iter().filter(|v| v.reverted).count(); - write!(f, + write!(f, "Votes {{ \n signers: {:?} \n recent_signers: {:?} \n number of votes: {} \n number of add votes {} \r number of remove votes {} \n number of reverted votes: {}}}", signers, recent_signers, num_votes, add_votes, rm_votes, reverted_votes) - } + } } impl CliqueBlockState { diff --git a/ethcore/src/engines/clique/mod.rs b/ethcore/src/engines/clique/mod.rs index adfcf88f01d..b107216aece 100644 --- a/ethcore/src/engines/clique/mod.rs +++ b/ethcore/src/engines/clique/mod.rs @@ -68,9 +68,9 @@ use std::time::{Instant, Duration, SystemTime, UNIX_EPOCH}; use block::ExecutedBlock; use client::{BlockId, EngineClient}; +use client_traits::VerifyingEngine; use engines::clique::util::{extract_signers, recover_creator}; -use engines::{Engine, EngineError, Seal, SealingState}; -use error::{BlockError, Error}; +use engines::{Engine, Seal, SealingState}; use ethereum_types::{Address, H64, H160, H256, U256}; use ethkey::Signature; use hash::KECCAK_EMPTY_LIST_RLP; @@ -82,9 +82,13 @@ use rand::Rng; use super::signer::EngineSigner; use unexpected::{Mismatch, OutOfBounds}; use time_utils::CheckedSystemTime; -use types::BlockNumber; -use types::header::Header; - +use types::{ + BlockNumber, + header::Header, + engines::params::CommonParams, + errors::{BlockError, EthcoreError as Error, EngineError}, + transaction::{self, SignedTransaction, UnverifiedTransaction}, +}; use self::block_state::CliqueBlockState; use self::params::CliqueParams; @@ -255,8 +259,7 @@ impl Clique { fn new_checkpoint_state(&self, header: &Header) -> Result { debug_assert_eq!(header.number() % self.epoch_length, 0); - let mut state = CliqueBlockState::new( - extract_signers(header)?); + let mut state = CliqueBlockState::new(extract_signers(header)?); // TODO(niklasad1): refactor to perform this check in the `CliqueBlockState` constructor instead state.calc_next_timestamp(header.timestamp(), self.period)?; @@ -354,14 +357,187 @@ impl Clique { } } +impl VerifyingEngine for Clique { + // Clique use same fields, nonce + mixHash + fn seal_fields(&self, _header: &Header) -> usize { 2 } + + fn params(&self) -> &CommonParams { + self.machine.params() + } + + fn verify_block_basic(&self, header: &Header) -> Result<(), Error> { + // Largely same as https://github.com/ethereum/go-ethereum/blob/master/consensus/clique/clique.go#L275 + + // Ignore genesis block. + if header.number() == 0 { + return Ok(()); + } + + // Don't waste time checking blocks from the future + { + let limit = CheckedSystemTime::checked_add(SystemTime::now(), Duration::from_secs(self.period)) + .ok_or(BlockError::TimestampOverflow)?; + + // This should succeed under the contraints that the system clock works + let limit_as_dur = limit.duration_since(UNIX_EPOCH).map_err(|e| { + // todo can use String directly? + Box::new(format!("Converting SystemTime to Duration failed: {}", e)) + })?; + + let hdr = Duration::from_secs(header.timestamp()); + if hdr > limit_as_dur { + let found = CheckedSystemTime::checked_add(UNIX_EPOCH, hdr).ok_or(BlockError::TimestampOverflow)?; + + Err(BlockError::TemporarilyInvalid(OutOfBounds { + min: None, + max: Some(limit), + found, + }.into()))? + } + } + + let is_checkpoint = header.number() % self.epoch_length == 0; + + if is_checkpoint && *header.author() != NULL_AUTHOR { + return Err(EngineError::CliqueWrongAuthorCheckpoint(Mismatch { + expected: H160::zero(), + found: *header.author(), + }))?; + } + + let seal_fields = header.decode_seal::>()?; + if seal_fields.len() != 2 { + Err(BlockError::InvalidSealArity(Mismatch { + expected: 2, + found: seal_fields.len(), + }))? + } + + let mixhash = H256::from_slice(seal_fields[0]); + let nonce = H64::from_slice(seal_fields[1]); + + // Nonce must be 0x00..0 or 0xff..f + if nonce != NONCE_DROP_VOTE && nonce != NONCE_AUTH_VOTE { + Err(EngineError::CliqueInvalidNonce(nonce))?; + } + + if is_checkpoint && nonce != NULL_NONCE { + Err(EngineError::CliqueInvalidNonce(nonce))?; + } + + // Ensure that the mix digest is zero as Clique don't have fork protection currently + if mixhash != NULL_MIXHASH { + Err(BlockError::MismatchedH256SealElement(Mismatch { + expected: NULL_MIXHASH, + found: mixhash, + }))? + } + + let extra_data_len = header.extra_data().len(); + + if extra_data_len < VANITY_LENGTH { + Err(EngineError::CliqueMissingVanity)? + } + + if extra_data_len < VANITY_LENGTH + SIGNATURE_LENGTH { + Err(EngineError::CliqueMissingSignature)? + } + + let signers = extra_data_len - (VANITY_LENGTH + SIGNATURE_LENGTH); + + // Checkpoint blocks must at least contain one signer + if is_checkpoint && signers == 0 { + Err(EngineError::CliqueCheckpointNoSigner)? + } + + // Addresses must be be divisable by 20 + if is_checkpoint && signers % ADDRESS_LENGTH != 0 { + Err(EngineError::CliqueCheckpointInvalidSigners(signers))? + } + + // Ensure that the block doesn't contain any uncles which are meaningless in PoA + if *header.uncles_hash() != NULL_UNCLES_HASH { + Err(BlockError::InvalidUnclesHash(Mismatch { + expected: NULL_UNCLES_HASH, + found: *header.uncles_hash(), + }))? + } + + // Ensure that the block's difficulty is meaningful (may not be correct at this point) + if *header.difficulty() != DIFF_INTURN && *header.difficulty() != DIFF_NOTURN { + Err(BlockError::DifficultyOutOfBounds(OutOfBounds { + min: Some(DIFF_NOTURN), + max: Some(DIFF_INTURN), + found: *header.difficulty(), + }))? + } + + // All basic checks passed, continue to next phase + Ok(()) + } + + /// Verify block family by looking up parent state (backfill if needed), then try to apply current header. + /// see https://github.com/ethereum/go-ethereum/blob/master/consensus/clique/clique.go#L338 + fn verify_block_family(&self, header: &Header, parent: &Header) -> Result<(), Error> { + // Ignore genesis block. + if header.number() == 0 { + return Ok(()); + } + + // parent sanity check + if parent.hash() != *header.parent_hash() || header.number() != parent.number() + 1 { + Err(BlockError::UnknownParent(parent.hash()))? + } + + // Ensure that the block's timestamp isn't too close to it's parent + let limit = parent.timestamp().saturating_add(self.period); + if limit > header.timestamp() { + let max = CheckedSystemTime::checked_add(UNIX_EPOCH, Duration::from_secs(header.timestamp())); + let found = CheckedSystemTime::checked_add(UNIX_EPOCH, Duration::from_secs(limit)) + .ok_or(BlockError::TimestampOverflow)?; + + Err(BlockError::InvalidTimestamp(OutOfBounds { + min: None, + max, + found, + }.into()))? + } + + // Retrieve the parent state + let parent_state = match self.state(&parent) { + Ok(parent_state) => parent_state, + Err(e) => { + // todo: these can be BlockError, EngineError and probably some others too + match e { + Error::Block(err) => return Err(Error::Block(err)), + Error::Engine(err) => return Err(Error::Engine(err)), + _ => return Err(Error::Other(e.to_string())), + } + } + }; + // Try to apply current state, apply() will further check signer and recent signer. + let mut new_state = parent_state.clone(); +// new_state.apply(header, header.number() % self.epoch_length == 0)?; +// new_state.calc_next_timestamp(header.timestamp(), self.period)?; +// self.block_state_by_hash.write().insert(header.hash(), new_state); + + Ok(()) + } + + fn verify_transaction_basic(&self, t: &UnverifiedTransaction, header: &Header) -> Result<(), transaction::Error> { + self.machine().verify_transaction_basic(t, header) + } + + fn verify_transaction_unordered(&self, t: UnverifiedTransaction, header: &Header) -> Result { + self.machine().verify_transaction_unordered(t, header) + } +} + impl Engine for Clique { fn name(&self) -> &str { "Clique" } fn machine(&self) -> &Machine { &self.machine } - // Clique use same fields, nonce + mixHash - fn seal_fields(&self, _header: &Header) -> usize { 2 } - fn maximum_uncle_count(&self, _block: BlockNumber) -> usize { 0 } fn on_new_block( @@ -536,159 +712,159 @@ impl Engine for Clique { fn verify_local_seal(&self, _header: &Header) -> Result<(), Error> { Ok(()) } - fn verify_block_basic(&self, header: &Header) -> Result<(), Error> { - // Largely same as https://github.com/ethereum/go-ethereum/blob/master/consensus/clique/clique.go#L275 - - // Ignore genesis block. - if header.number() == 0 { - return Ok(()); - } - - // Don't waste time checking blocks from the future - { - let limit = CheckedSystemTime::checked_add(SystemTime::now(), Duration::from_secs(self.period)) - .ok_or(BlockError::TimestampOverflow)?; - - // This should succeed under the contraints that the system clock works - let limit_as_dur = limit.duration_since(UNIX_EPOCH).map_err(|e| { - Box::new(format!("Converting SystemTime to Duration failed: {}", e)) - })?; - - let hdr = Duration::from_secs(header.timestamp()); - if hdr > limit_as_dur { - let found = CheckedSystemTime::checked_add(UNIX_EPOCH, hdr).ok_or(BlockError::TimestampOverflow)?; - - Err(BlockError::TemporarilyInvalid(OutOfBounds { - min: None, - max: Some(limit), - found, - }.into()))? - } - } - - let is_checkpoint = header.number() % self.epoch_length == 0; - - if is_checkpoint && *header.author() != NULL_AUTHOR { - return Err(EngineError::CliqueWrongAuthorCheckpoint(Mismatch { - expected: H160::zero(), - found: *header.author(), - }))?; - } - - let seal_fields = header.decode_seal::>()?; - if seal_fields.len() != 2 { - Err(BlockError::InvalidSealArity(Mismatch { - expected: 2, - found: seal_fields.len(), - }))? - } - - let mixhash = H256::from_slice(seal_fields[0]); - let nonce = H64::from_slice(seal_fields[1]); - - // Nonce must be 0x00..0 or 0xff..f - if nonce != NONCE_DROP_VOTE && nonce != NONCE_AUTH_VOTE { - Err(EngineError::CliqueInvalidNonce(nonce))?; - } - - if is_checkpoint && nonce != NULL_NONCE { - Err(EngineError::CliqueInvalidNonce(nonce))?; - } - - // Ensure that the mix digest is zero as Clique don't have fork protection currently - if mixhash != NULL_MIXHASH { - Err(BlockError::MismatchedH256SealElement(Mismatch { - expected: NULL_MIXHASH, - found: mixhash, - }))? - } - - let extra_data_len = header.extra_data().len(); - - if extra_data_len < VANITY_LENGTH { - Err(EngineError::CliqueMissingVanity)? - } - - if extra_data_len < VANITY_LENGTH + SIGNATURE_LENGTH { - Err(EngineError::CliqueMissingSignature)? - } - - let signers = extra_data_len - (VANITY_LENGTH + SIGNATURE_LENGTH); - - // Checkpoint blocks must at least contain one signer - if is_checkpoint && signers == 0 { - Err(EngineError::CliqueCheckpointNoSigner)? - } - - // Addresses must be be divisable by 20 - if is_checkpoint && signers % ADDRESS_LENGTH != 0 { - Err(EngineError::CliqueCheckpointInvalidSigners(signers))? - } - - // Ensure that the block doesn't contain any uncles which are meaningless in PoA - if *header.uncles_hash() != NULL_UNCLES_HASH { - Err(BlockError::InvalidUnclesHash(Mismatch { - expected: NULL_UNCLES_HASH, - found: *header.uncles_hash(), - }))? - } - - // Ensure that the block's difficulty is meaningful (may not be correct at this point) - if *header.difficulty() != DIFF_INTURN && *header.difficulty() != DIFF_NOTURN { - Err(BlockError::DifficultyOutOfBounds(OutOfBounds { - min: Some(DIFF_NOTURN), - max: Some(DIFF_INTURN), - found: *header.difficulty(), - }))? - } - - // All basic checks passed, continue to next phase - Ok(()) - } - - fn verify_block_unordered(&self, _header: &Header) -> Result<(), Error> { - // Nothing to check here. - Ok(()) - } - - /// Verify block family by looking up parent state (backfill if needed), then try to apply current header. - /// see https://github.com/ethereum/go-ethereum/blob/master/consensus/clique/clique.go#L338 - fn verify_block_family(&self, header: &Header, parent: &Header) -> Result<(), Error> { - // Ignore genesis block. - if header.number() == 0 { - return Ok(()); - } - - // parent sanity check - if parent.hash() != *header.parent_hash() || header.number() != parent.number() + 1 { - Err(BlockError::UnknownParent(parent.hash()))? - } - - // Ensure that the block's timestamp isn't too close to it's parent - let limit = parent.timestamp().saturating_add(self.period); - if limit > header.timestamp() { - let max = CheckedSystemTime::checked_add(UNIX_EPOCH, Duration::from_secs(header.timestamp())); - let found = CheckedSystemTime::checked_add(UNIX_EPOCH, Duration::from_secs(limit)) - .ok_or(BlockError::TimestampOverflow)?; - - Err(BlockError::InvalidTimestamp(OutOfBounds { - min: None, - max, - found, - }.into()))? - } - - // Retrieve the parent state - let parent_state = self.state(&parent)?; - // Try to apply current state, apply() will further check signer and recent signer. - let mut new_state = parent_state.clone(); - new_state.apply(header, header.number() % self.epoch_length == 0)?; - new_state.calc_next_timestamp(header.timestamp(), self.period)?; - self.block_state_by_hash.write().insert(header.hash(), new_state); - - Ok(()) - } - +// fn verify_block_basic(&self, header: &Header) -> Result<(), Error> { +// // Largely same as https://github.com/ethereum/go-ethereum/blob/master/consensus/clique/clique.go#L275 +// +// // Ignore genesis block. +// if header.number() == 0 { +// return Ok(()); +// } +// +// // Don't waste time checking blocks from the future +// { +// let limit = CheckedSystemTime::checked_add(SystemTime::now(), Duration::from_secs(self.period)) +// .ok_or(BlockError::TimestampOverflow)?; +// +// // This should succeed under the contraints that the system clock works +// let limit_as_dur = limit.duration_since(UNIX_EPOCH).map_err(|e| { +// Box::new(format!("Converting SystemTime to Duration failed: {}", e)) +// })?; +// +// let hdr = Duration::from_secs(header.timestamp()); +// if hdr > limit_as_dur { +// let found = CheckedSystemTime::checked_add(UNIX_EPOCH, hdr).ok_or(BlockError::TimestampOverflow)?; +// +// Err(BlockError::TemporarilyInvalid(OutOfBounds { +// min: None, +// max: Some(limit), +// found, +// }.into()))? +// } +// } +// +// let is_checkpoint = header.number() % self.epoch_length == 0; +// +// if is_checkpoint && *header.author() != NULL_AUTHOR { +// return Err(EngineError::CliqueWrongAuthorCheckpoint(Mismatch { +// expected: H160::zero(), +// found: *header.author(), +// }))?; +// } +// +// let seal_fields = header.decode_seal::>()?; +// if seal_fields.len() != 2 { +// Err(BlockError::InvalidSealArity(Mismatch { +// expected: 2, +// found: seal_fields.len(), +// }))? +// } +// +// let mixhash = H256::from_slice(seal_fields[0]); +// let nonce = H64::from_slice(seal_fields[1]); +// +// // Nonce must be 0x00..0 or 0xff..f +// if nonce != NONCE_DROP_VOTE && nonce != NONCE_AUTH_VOTE { +// Err(EngineError::CliqueInvalidNonce(nonce))?; +// } +// +// if is_checkpoint && nonce != NULL_NONCE { +// Err(EngineError::CliqueInvalidNonce(nonce))?; +// } +// +// // Ensure that the mix digest is zero as Clique don't have fork protection currently +// if mixhash != NULL_MIXHASH { +// Err(BlockError::MismatchedH256SealElement(Mismatch { +// expected: NULL_MIXHASH, +// found: mixhash, +// }))? +// } +// +// let extra_data_len = header.extra_data().len(); +// +// if extra_data_len < VANITY_LENGTH { +// Err(EngineError::CliqueMissingVanity)? +// } +// +// if extra_data_len < VANITY_LENGTH + SIGNATURE_LENGTH { +// Err(EngineError::CliqueMissingSignature)? +// } +// +// let signers = extra_data_len - (VANITY_LENGTH + SIGNATURE_LENGTH); +// +// // Checkpoint blocks must at least contain one signer +// if is_checkpoint && signers == 0 { +// Err(EngineError::CliqueCheckpointNoSigner)? +// } +// +// // Addresses must be be divisable by 20 +// if is_checkpoint && signers % ADDRESS_LENGTH != 0 { +// Err(EngineError::CliqueCheckpointInvalidSigners(signers))? +// } +// +// // Ensure that the block doesn't contain any uncles which are meaningless in PoA +// if *header.uncles_hash() != NULL_UNCLES_HASH { +// Err(BlockError::InvalidUnclesHash(Mismatch { +// expected: NULL_UNCLES_HASH, +// found: *header.uncles_hash(), +// }))? +// } +// +// // Ensure that the block's difficulty is meaningful (may not be correct at this point) +// if *header.difficulty() != DIFF_INTURN && *header.difficulty() != DIFF_NOTURN { +// Err(BlockError::DifficultyOutOfBounds(OutOfBounds { +// min: Some(DIFF_NOTURN), +// max: Some(DIFF_INTURN), +// found: *header.difficulty(), +// }))? +// } +// +// // All basic checks passed, continue to next phase +// Ok(()) +// } +// +// fn verify_block_unordered(&self, _header: &Header) -> Result<(), Error> { +// // Nothing to check here. +// Ok(()) +// } +// +// /// Verify block family by looking up parent state (backfill if needed), then try to apply current header. +// /// see https://github.com/ethereum/go-ethereum/blob/master/consensus/clique/clique.go#L338 +// fn verify_block_family(&self, header: &Header, parent: &Header) -> Result<(), Error> { +// // Ignore genesis block. +// if header.number() == 0 { +// return Ok(()); +// } +// +// // parent sanity check +// if parent.hash() != *header.parent_hash() || header.number() != parent.number() + 1 { +// Err(BlockError::UnknownParent(parent.hash()))? +// } +// +// // Ensure that the block's timestamp isn't too close to it's parent +// let limit = parent.timestamp().saturating_add(self.period); +// if limit > header.timestamp() { +// let max = CheckedSystemTime::checked_add(UNIX_EPOCH, Duration::from_secs(header.timestamp())); +// let found = CheckedSystemTime::checked_add(UNIX_EPOCH, Duration::from_secs(limit)) +// .ok_or(BlockError::TimestampOverflow)?; +// +// Err(BlockError::InvalidTimestamp(OutOfBounds { +// min: None, +// max, +// found, +// }.into()))? +// } +// +// // Retrieve the parent state +// let parent_state = self.state(&parent)?; +// // Try to apply current state, apply() will further check signer and recent signer. +// let mut new_state = parent_state.clone(); +// new_state.apply(header, header.number() % self.epoch_length == 0)?; +// new_state.calc_next_timestamp(header.timestamp(), self.period)?; +// self.block_state_by_hash.write().insert(header.hash(), new_state); +// +// Ok(()) +// } +// fn genesis_epoch_data(&self, header: &Header, _call: &Call) -> Result, String> { let mut state = self.new_checkpoint_state(header).expect("Unable to parse genesis data."); state.calc_next_timestamp(header.timestamp(), self.period).map_err(|e| format!("{}", e))?; @@ -710,7 +886,7 @@ impl Engine for Clique { // it's just to ignore setting a correct difficulty here, we will check authorization in next step in generate_seal anyway. if let Some(signer) = self.signer.read().as_ref() { let state = match self.state(&parent) { - Err(e) => { + Err(e) => { trace!(target: "engine", "populate_from_parent: Unable to find parent state: {}, ignored.", e); return; } diff --git a/ethcore/src/engines/clique/tests.rs b/ethcore/src/engines/clique/tests.rs index 97e218a05ce..ae965cabd1f 100644 --- a/ethcore/src/engines/clique/tests.rs +++ b/ethcore/src/engines/clique/tests.rs @@ -18,12 +18,14 @@ use block::*; use engines::Engine; -use error::Error; use ethereum_types::{Address, H256}; use ethkey::{Secret, KeyPair}; use state_db::StateDB; use super::*; use test_helpers::get_temp_state_db; +use types::{ + errors::{EthcoreError as Error, EngineError}, +}; use std::sync::Arc; use std::collections::HashMap; diff --git a/ethcore/src/engines/clique/util.rs b/ethcore/src/engines/clique/util.rs index 79b74bb7365..afa0a73bf4f 100644 --- a/ethcore/src/engines/clique/util.rs +++ b/ethcore/src/engines/clique/util.rs @@ -16,15 +16,16 @@ use std::collections::BTreeSet; -use engines::EngineError; use engines::clique::{ADDRESS_LENGTH, SIGNATURE_LENGTH, VANITY_LENGTH, NULL_NONCE, NULL_MIXHASH}; -use error::Error; use ethereum_types::{Address, H256}; use ethkey::{public_to_address, recover as ec_recover, Signature}; use lru_cache::LruCache; use parking_lot::RwLock; use rlp::encode; -use types::header::Header; +use types::{ + header::Header, + errors::{EthcoreError as Error, EngineError}, +}; /// How many recovered signature to cache in the memory. pub const CREATOR_CACHE_NUM: usize = 4096; diff --git a/ethcore/src/engines/instant_seal.rs b/ethcore/src/engines/instant_seal.rs index 125a4edc23b..6a70d0175ed 100644 --- a/ethcore/src/engines/instant_seal.rs +++ b/ethcore/src/engines/instant_seal.rs @@ -15,10 +15,16 @@ // along with Parity Ethereum. If not, see . use engines::{Engine, Seal, SealingState}; +use client_traits::VerifyingEngine; use machine::Machine; -use types::header::Header; +use types::{ + header::Header, + engines::params::CommonParams, + errors::EthcoreError as Error, + transaction::{self, UnverifiedTransaction, SignedTransaction}, +}; + use block::ExecutedBlock; -use error::Error; /// `InstantSeal` params. #[derive(Default, Debug, PartialEq)] @@ -52,6 +58,20 @@ impl InstantSeal { } } +impl VerifyingEngine for InstantSeal { + fn params(&self) -> &CommonParams { + self.machine.params() + } + + fn verify_transaction_basic(&self, t: &UnverifiedTransaction, header: &Header) -> Result<(), transaction::Error> { + self.machine().verify_transaction_basic(t, header) + } + + fn verify_transaction_unordered(&self, t: UnverifiedTransaction, header: &Header) -> Result { + self.machine().verify_transaction_unordered(t, header) + } +} + impl Engine for InstantSeal { fn name(&self) -> &str { "InstantSeal" diff --git a/ethcore/src/engines/mod.rs b/ethcore/src/engines/mod.rs index 9cad9bdce02..1cefbad433f 100644 --- a/ethcore/src/engines/mod.rs +++ b/ethcore/src/engines/mod.rs @@ -39,24 +39,24 @@ pub use types::engines::epoch::{self, Transition as EpochTransition}; use std::sync::{Weak, Arc}; use std::collections::BTreeMap; -use std::{fmt, error}; use builtin::Builtin; use vm::{EnvInfo, Schedule, CreateContractAddress, CallType, ActionValue}; -use error::Error; -use types::BlockNumber; -use types::header::{Header, ExtendedHeader}; +use types::{ + BlockNumber, + ancestry_action::AncestryAction, + header::{Header, ExtendedHeader}, + errors::{EthcoreError as Error, EngineError}, + transaction::{self, UnverifiedTransaction}, +}; use snapshot::SnapshotComponents; -use spec::CommonParams; -use types::transaction::{self, UnverifiedTransaction, SignedTransaction}; use client::EngineClient; +use client_traits::VerifyingEngine; use ethkey::{Signature}; use machine::{self, Machine, AuxiliaryRequest, AuxiliaryData}; -use ethereum_types::{H64, H256, U256, Address}; -use unexpected::{Mismatch, OutOfBounds}; +use ethereum_types::{H256, U256, Address}; use bytes::Bytes; -use types::ancestry_action::AncestryAction; use block::ExecutedBlock; /// Default EIP-210 contract code. @@ -65,94 +65,6 @@ pub const DEFAULT_BLOCKHASH_CONTRACT: &'static str = "73ffffffffffffffffffffffff /// The number of generations back that uncles can be. pub const MAX_UNCLE_AGE: usize = 6; -/// Voting errors. -#[derive(Debug)] -pub enum EngineError { - /// Signature or author field does not belong to an authority. - NotAuthorized(Address), - /// The same author issued different votes at the same step. - DoubleVote(Address), - /// The received block is from an incorrect proposer. - NotProposer(Mismatch
), - /// Message was not expected. - UnexpectedMessage, - /// Seal field has an unexpected size. - BadSealFieldSize(OutOfBounds), - /// Validation proof insufficient. - InsufficientProof(String), - /// Failed system call. - FailedSystemCall(String), - /// Malformed consensus message. - MalformedMessage(String), - /// Requires client ref, but none registered. - RequiresClient, - /// Invalid engine specification or implementation. - InvalidEngine, - /// Requires signer ref, but none registered. - RequiresSigner, - /// Missing Parent Epoch - MissingParent(H256), - /// Checkpoint is missing - CliqueMissingCheckpoint(H256), - /// Missing vanity data - CliqueMissingVanity, - /// Missing signature - CliqueMissingSignature, - /// Missing signers - CliqueCheckpointNoSigner, - /// List of signers is invalid - CliqueCheckpointInvalidSigners(usize), - /// Wrong author on a checkpoint - CliqueWrongAuthorCheckpoint(Mismatch
), - /// Wrong checkpoint authors recovered - CliqueFaultyRecoveredSigners(Vec), - /// Invalid nonce (should contain vote) - CliqueInvalidNonce(H64), - /// The signer signed a block to recently - CliqueTooRecentlySigned(Address), - /// Custom - Custom(String), -} - -impl fmt::Display for EngineError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - use self::EngineError::*; - let msg = match *self { - CliqueMissingCheckpoint(ref hash) => format!("Missing checkpoint block: {}", hash), - CliqueMissingVanity => format!("Extra data is missing vanity data"), - CliqueMissingSignature => format!("Extra data is missing signature"), - CliqueCheckpointInvalidSigners(len) => format!("Checkpoint block list was of length: {} of checkpoint but - it needs to be bigger than zero and a divisible by 20", len), - CliqueCheckpointNoSigner => format!("Checkpoint block list of signers was empty"), - CliqueInvalidNonce(ref mis) => format!("Unexpected nonce {} expected {} or {}", mis, 0_u64, u64::max_value()), - CliqueWrongAuthorCheckpoint(ref oob) => format!("Unexpected checkpoint author: {}", oob), - CliqueFaultyRecoveredSigners(ref mis) => format!("Faulty recovered signers {:?}", mis), - CliqueTooRecentlySigned(ref address) => format!("The signer: {} has signed a block too recently", address), - Custom(ref s) => s.clone(), - DoubleVote(ref address) => format!("Author {} issued too many blocks.", address), - NotProposer(ref mis) => format!("Author is not a current proposer: {}", mis), - NotAuthorized(ref address) => format!("Signer {} is not authorized.", address), - UnexpectedMessage => "This Engine should not be fed messages.".into(), - BadSealFieldSize(ref oob) => format!("Seal field has an unexpected length: {}", oob), - InsufficientProof(ref msg) => format!("Insufficient validation proof: {}", msg), - FailedSystemCall(ref msg) => format!("Failed to make system call: {}", msg), - MalformedMessage(ref msg) => format!("Received malformed consensus message: {}", msg), - RequiresClient => format!("Call requires client but none registered"), - RequiresSigner => format!("Call requires signer but none registered"), - InvalidEngine => format!("Invalid engine specification or implementation"), - MissingParent(ref hash) => format!("Parent Epoch is missing from database: {}", hash), - }; - - f.write_fmt(format_args!("Engine error ({})", msg)) - } -} - -impl error::Error for EngineError { - fn description(&self) -> &str { - "Engine error" - } -} - /// Seal type. #[derive(Debug, PartialEq, Eq)] pub enum Seal { @@ -276,7 +188,7 @@ pub enum EpochChange { /// A consensus mechanism for the chain. Generally either proof-of-work or proof-of-stake-based. /// Provides hooks into each of the major parts of block import. -pub trait Engine: Sync + Send { +pub trait Engine: VerifyingEngine + Sync + Send { /// The name of this engine. fn name(&self) -> &str; @@ -285,14 +197,11 @@ pub trait Engine: Sync + Send { fn machine(&self) -> &Machine; /// The number of additional header fields required for this engine. - fn seal_fields(&self, _header: &Header) -> usize { 0 } +// fn seal_fields(&self, _header: &Header) -> usize { 0 } /// Additional engine-specific information for the user/developer concerning `header`. fn extra_info(&self, _header: &Header) -> BTreeMap { BTreeMap::new() } - /// Maximum number of uncles a block is allowed to declare. - fn maximum_uncle_count(&self, _block: BlockNumber) -> usize { 0 } - /// Optional maximum gas limit. fn maximum_gas_limit(&self) -> Option { None } @@ -346,18 +255,18 @@ pub trait Engine: Sync + Send { /// Phase 1 quick block verification. Only does checks that are cheap. Returns either a null `Ok` or a general error detailing the problem with import. /// The verification module can optionally avoid checking the seal (`check_seal`), if seal verification is disabled this method won't be called. - fn verify_block_basic(&self, _header: &Header) -> Result<(), Error> { Ok(()) } +// fn verify_block_basic(&self, _header: &Header) -> Result<(), Error> { Ok(()) } /// Phase 2 verification. Perform costly checks such as transaction signatures. Returns either a null `Ok` or a general error detailing the problem with import. /// The verification module can optionally avoid checking the seal (`check_seal`), if seal verification is disabled this method won't be called. - fn verify_block_unordered(&self, _header: &Header) -> Result<(), Error> { Ok(()) } +// fn verify_block_unordered(&self, _header: &Header) -> Result<(), Error> { Ok(()) } /// Phase 3 verification. Check block information against parent. Returns either a null `Ok` or a general error detailing the problem with import. - fn verify_block_family(&self, _header: &Header, _parent: &Header) -> Result<(), Error> { Ok(()) } +// fn verify_block_family(&self, _header: &Header, _parent: &Header) -> Result<(), Error> { Ok(()) } /// Phase 4 verification. Verify block header against potentially external data. /// Should only be called when `register_client` has been called previously. - fn verify_block_external(&self, _header: &Header) -> Result<(), Error> { Ok(()) } +// fn verify_block_external(&self, _header: &Header) -> Result<(), Error> { Ok(()) } /// Genesis epoch data. fn genesis_epoch_data<'a>(&self, _header: &Header, _state: &machine::Call) -> Result, String> { Ok(Vec::new()) } @@ -458,11 +367,6 @@ pub trait Engine: Sync + Send { cmp::max(now.as_secs() as u64, parent_timestamp + 1) } - /// Check whether the parent timestamp is valid. - fn is_timestamp_valid(&self, header_timestamp: u64, parent_timestamp: u64) -> bool { - header_timestamp > parent_timestamp - } - /// Gather all ancestry actions. Called at the last stage when a block is committed. The Engine must guarantee that /// the ancestry exists. fn ancestry_actions(&self, _header: &Header, _ancestry: &mut dyn Iterator) -> Vec { @@ -474,11 +378,6 @@ pub trait Engine: Sync + Send { Ok(*header.author()) } - /// Get the general parameters of the chain. - fn params(&self) -> &CommonParams { - self.machine().params() - } - /// Get the EVM schedule for the given block number. fn schedule(&self, block_number: BlockNumber) -> Schedule { self.machine().schedule(block_number) @@ -495,11 +394,6 @@ pub trait Engine: Sync + Send { self.machine().builtin(a, block_number) } - /// Some intrinsic operation parameters; by default they take their value from the `spec()`'s `engine_params`. - fn maximum_extra_data_size(&self) -> usize { - self.machine().maximum_extra_data_size() - } - /// The nonce with which accounts begin at given block. fn account_start_nonce(&self, block: BlockNumber) -> U256 { self.machine().account_start_nonce(block) @@ -523,9 +417,9 @@ pub trait Engine: Sync + Send { /// /// NOTE This function consumes an `UnverifiedTransaction` and produces `SignedTransaction` /// which implies that a heavy check of the signature is performed here. - fn verify_transaction_unordered(&self, t: UnverifiedTransaction, header: &Header) -> Result { - self.machine().verify_transaction_unordered(t, header) - } +// fn verify_transaction_unordered(&self, t: UnverifiedTransaction, header: &Header) -> Result { +// self.machine().verify_transaction_unordered(t, header) +// } /// Perform basic/cheap transaction verification. /// @@ -537,9 +431,9 @@ pub trait Engine: Sync + Send { /// /// TODO: Add flags for which bits of the transaction to check. /// TODO: consider including State in the params. - fn verify_transaction_basic(&self, t: &UnverifiedTransaction, header: &Header) -> Result<(), transaction::Error> { - self.machine().verify_transaction_basic(t, header) - } +// fn verify_transaction_basic(&self, t: &UnverifiedTransaction, header: &Header) -> Result<(), transaction::Error> { +// self.machine().verify_transaction_basic(t, header) +// } /// Performs pre-validation of RLP decoded transaction before other processing fn decode_transaction(&self, transaction: &[u8]) -> Result { diff --git a/ethcore/src/engines/null_engine.rs b/ethcore/src/engines/null_engine.rs index 2d81e4400fc..aad44f057e8 100644 --- a/ethcore/src/engines/null_engine.rs +++ b/ethcore/src/engines/null_engine.rs @@ -15,13 +15,18 @@ // along with Parity Ethereum. If not, see . use engines::Engine; +use client_traits::VerifyingEngine; use engines::block_reward::{self, RewardKind}; use ethereum_types::U256; use machine::Machine; -use types::BlockNumber; -use types::header::Header; +use types::{ + BlockNumber, + header::Header, + engines::params::CommonParams, + errors::EthcoreError as Error, + transaction::{self, UnverifiedTransaction, SignedTransaction}, +}; use block::ExecutedBlock; -use error::Error; /// Params for a null engine. #[derive(Clone, Default)] @@ -54,6 +59,20 @@ impl NullEngine { } } +impl VerifyingEngine for NullEngine { + fn params(&self) -> &CommonParams { + self.machine.params() + } + + fn verify_transaction_basic(&self, t: &UnverifiedTransaction, header: &Header) -> Result<(), transaction::Error> { + self.machine().verify_transaction_basic(t, header) + } + + fn verify_transaction_unordered(&self, t: UnverifiedTransaction, header: &Header) -> Result { + self.machine().verify_transaction_unordered(t, header) + } +} + impl Engine for NullEngine { fn name(&self) -> &str { "NullEngine" @@ -61,6 +80,8 @@ impl Engine for NullEngine { fn machine(&self) -> &Machine { &self.machine } + fn maximum_uncle_count(&self, _block: BlockNumber) -> usize { 2 } + fn on_close_block( &self, block: &mut ExecutedBlock, @@ -92,8 +113,6 @@ impl Engine for NullEngine { block_reward::apply_block_rewards(&rewards, block, &self.machine) } - fn maximum_uncle_count(&self, _block: BlockNumber) -> usize { 2 } - fn verify_local_seal(&self, _header: &Header) -> Result<(), Error> { Ok(()) } diff --git a/ethcore/src/engines/validator_set/contract.rs b/ethcore/src/engines/validator_set/contract.rs index 5b695525dc5..2dd18a2b476 100644 --- a/ethcore/src/engines/validator_set/contract.rs +++ b/ethcore/src/engines/validator_set/contract.rs @@ -23,8 +23,11 @@ use bytes::Bytes; use ethereum_types::{H256, Address}; use machine::{AuxiliaryData, Call, Machine}; use parking_lot::RwLock; -use types::BlockNumber; -use types::header::Header; +use types::{ + BlockNumber, + header::Header, + errors::EthcoreError, +}; use client::EngineClient; @@ -72,7 +75,7 @@ impl ValidatorSet for ValidatorContract { self.validators.default_caller(id) } - fn on_epoch_begin(&self, first: bool, header: &Header, call: &mut SystemCall) -> Result<(), ::error::Error> { + fn on_epoch_begin(&self, first: bool, header: &Header, call: &mut SystemCall) -> Result<(), EthcoreError> { self.validators.on_epoch_begin(first, header, call) } @@ -93,7 +96,7 @@ impl ValidatorSet for ValidatorContract { self.validators.signals_epoch_end(first, header, aux) } - fn epoch_set(&self, first: bool, machine: &Machine, number: BlockNumber, proof: &[u8]) -> Result<(SimpleList, Option), ::error::Error> { + fn epoch_set(&self, first: bool, machine: &Machine, number: BlockNumber, proof: &[u8]) -> Result<(SimpleList, Option), EthcoreError> { self.validators.epoch_set(first, machine, number, proof) } @@ -147,7 +150,8 @@ mod tests { use types::ids::BlockId; use test_helpers::generate_dummy_client_with_spec; use call_contract::CallContract; - use client::{BlockChainClient, ChainInfo, BlockInfo}; + use client::{BlockChainClient, ChainInfo}; + use client_traits::BlockInfo; use super::super::ValidatorSet; use super::ValidatorContract; diff --git a/ethcore/src/engines/validator_set/mod.rs b/ethcore/src/engines/validator_set/mod.rs index 865c45484fa..17a012e52d4 100644 --- a/ethcore/src/engines/validator_set/mod.rs +++ b/ethcore/src/engines/validator_set/mod.rs @@ -29,9 +29,12 @@ use bytes::Bytes; use ethereum_types::{H256, Address}; use ethjson::spec::ValidatorSet as ValidatorSpec; use machine::{AuxiliaryData, Call, Machine}; -use types::BlockNumber; -use types::header::Header; -use types::ids::BlockId; +use types::{ + BlockNumber, + header::Header, + ids::BlockId, + errors::EthcoreError, +}; use client::EngineClient; @@ -88,7 +91,7 @@ pub trait ValidatorSet: Send + Sync + 'static { /// The caller provided here may not generate proofs. /// /// `first` is true if this is the first block in the set. - fn on_epoch_begin(&self, _first: bool, _header: &Header, _call: &mut SystemCall) -> Result<(), ::error::Error> { + fn on_epoch_begin(&self, _first: bool, _header: &Header, _call: &mut SystemCall) -> Result<(), EthcoreError> { Ok(()) } @@ -124,7 +127,7 @@ pub trait ValidatorSet: Send + Sync + 'static { /// Returns the set, along with a flag indicating whether finality of a specific /// hash should be proven. fn epoch_set(&self, first: bool, machine: &Machine, number: BlockNumber, proof: &[u8]) - -> Result<(SimpleList, Option), ::error::Error>; + -> Result<(SimpleList, Option), EthcoreError>; /// Checks if a given address is a validator, with the given function /// for executing synchronous calls to contracts. diff --git a/ethcore/src/engines/validator_set/multi.rs b/ethcore/src/engines/validator_set/multi.rs index 0f6dd41afec..0a3aa780f50 100644 --- a/ethcore/src/engines/validator_set/multi.rs +++ b/ethcore/src/engines/validator_set/multi.rs @@ -22,9 +22,12 @@ use std::sync::Weak; use bytes::Bytes; use ethereum_types::{H256, Address}; use parking_lot::RwLock; -use types::BlockNumber; -use types::header::Header; -use types::ids::BlockId; +use types::{ + BlockNumber, + header::Header, + ids::BlockId, + errors::EthcoreError, +}; use client::EngineClient; use machine::{AuxiliaryData, Call, Machine}; @@ -77,7 +80,7 @@ impl ValidatorSet for Multi { .unwrap_or_else(|| Box::new(|_, _| Err("No validator set for given ID.".into()))) } - fn on_epoch_begin(&self, _first: bool, header: &Header, call: &mut SystemCall) -> Result<(), ::error::Error> { + fn on_epoch_begin(&self, _first: bool, header: &Header, call: &mut SystemCall) -> Result<(), EthcoreError> { let (set_block, set) = self.correct_set_by_number(header.number()); let first = set_block == header.number(); @@ -104,7 +107,7 @@ impl ValidatorSet for Multi { set.signals_epoch_end(first, header, aux) } - fn epoch_set(&self, _first: bool, machine: &Machine, number: BlockNumber, proof: &[u8]) -> Result<(super::SimpleList, Option), ::error::Error> { + fn epoch_set(&self, _first: bool, machine: &Machine, number: BlockNumber, proof: &[u8]) -> Result<(super::SimpleList, Option), EthcoreError> { let (set_block, set) = self.correct_set_by_number(number); let first = set_block == number; @@ -151,7 +154,8 @@ mod tests { use std::collections::BTreeMap; use hash::keccak; use accounts::AccountProvider; - use client::{BlockChainClient, ChainInfo, BlockInfo, ImportBlock}; + use client::{BlockChainClient, ChainInfo, ImportBlock}; + use client_traits::BlockInfo; use engines::EpochChange; use engines::validator_set::ValidatorSet; use ethkey::Secret; diff --git a/ethcore/src/engines/validator_set/safe_contract.rs b/ethcore/src/engines/validator_set/safe_contract.rs index 50ee598dc73..7ca55aafc97 100644 --- a/ethcore/src/engines/validator_set/safe_contract.rs +++ b/ethcore/src/engines/validator_set/safe_contract.rs @@ -26,10 +26,13 @@ use kvdb::DBValue; use memory_cache::MemoryLruCache; use parking_lot::RwLock; use rlp::{Rlp, RlpStream}; -use types::header::Header; -use types::ids::BlockId; -use types::log_entry::LogEntry; -use types::receipt::Receipt; +use types::{ + header::Header, + errors::{EngineError, EthcoreError, BlockError}, + ids::BlockId, + log_entry::LogEntry, + receipt::Receipt, +}; use unexpected::Mismatch; use client::EngineClient; @@ -144,13 +147,13 @@ fn check_first_proof(machine: &Machine, contract_address: Address, old_header: H } } -fn decode_first_proof(rlp: &Rlp) -> Result<(Header, Vec), ::error::Error> { +fn decode_first_proof(rlp: &Rlp) -> Result<(Header, Vec), EthcoreError> { let header = rlp.val_at(0)?; let state_items = rlp.at(1)?.iter().map(|x| { let mut val = DBValue::new(); val.append_slice(x.data()?); Ok(val) - }).collect::>()?; + }).collect::>()?; Ok((header, state_items)) } @@ -164,7 +167,7 @@ fn encode_proof(header: &Header, receipts: &[Receipt]) -> Bytes { stream.drain() } -fn decode_proof(rlp: &Rlp) -> Result<(Header, Vec), ::error::Error> { +fn decode_proof(rlp: &Rlp) -> Result<(Header, Vec), EthcoreError> { Ok((rlp.val_at(0)?, rlp.list_at(1)?)) } @@ -293,11 +296,11 @@ impl ValidatorSet for ValidatorSafeContract { .map(|out| (out, Vec::new()))) // generate no proofs in general } - fn on_epoch_begin(&self, _first: bool, _header: &Header, caller: &mut SystemCall) -> Result<(), ::error::Error> { + fn on_epoch_begin(&self, _first: bool, _header: &Header, caller: &mut SystemCall) -> Result<(), EthcoreError> { let data = validator_set::functions::finalize_change::encode_input(); caller(self.contract_address, data) .map(|_| ()) - .map_err(::engines::EngineError::FailedSystemCall) + .map_err(EngineError::FailedSystemCall) .map_err(Into::into) } @@ -348,7 +351,7 @@ impl ValidatorSet for ValidatorSafeContract { } fn epoch_set(&self, first: bool, machine: &Machine, _number: ::types::BlockNumber, proof: &[u8]) - -> Result<(SimpleList, Option), ::error::Error> + -> Result<(SimpleList, Option), EthcoreError> { let rlp = Rlp::new(proof); @@ -359,7 +362,7 @@ impl ValidatorSet for ValidatorSafeContract { let number = old_header.number(); let old_hash = old_header.hash(); let addresses = check_first_proof(machine, self.contract_address, old_header, &state_items) - .map_err(::engines::EngineError::InsufficientProof)?; + .map_err(EngineError::InsufficientProof)?; trace!(target: "engine", "Extracted epoch validator set at block #{}: {} addresses", number, addresses.len()); @@ -374,7 +377,7 @@ impl ValidatorSet for ValidatorSafeContract { receipts.iter().map(::rlp::encode) ); if found_root != *old_header.receipts_root() { - return Err(::error::BlockError::InvalidReceiptsRoot( + return Err(BlockError::InvalidReceiptsRoot( Mismatch { expected: *old_header.receipts_root(), found: found_root } ).into()); } @@ -387,7 +390,7 @@ impl ValidatorSet for ValidatorSafeContract { Ok((list, Some(old_header.hash()))) }, - None => Err(::engines::EngineError::InsufficientProof("No log event in proof.".into()).into()), + None => Err(EngineError::InsufficientProof("No log event in proof.".into()).into()), } } } @@ -453,7 +456,8 @@ mod tests { use spec::Spec; use accounts::AccountProvider; use types::transaction::{Transaction, Action}; - use client::{ChainInfo, BlockInfo, ImportBlock}; + use client::{ChainInfo, ImportBlock}; + use client_traits::BlockInfo; use ethkey::Secret; use miner::{self, MinerService}; use test_helpers::{generate_dummy_client_with_spec, generate_dummy_client_with_spec_and_data}; diff --git a/ethcore/src/engines/validator_set/simple_list.rs b/ethcore/src/engines/validator_set/simple_list.rs index 613b108804b..efd0cec878a 100644 --- a/ethcore/src/engines/validator_set/simple_list.rs +++ b/ethcore/src/engines/validator_set/simple_list.rs @@ -20,8 +20,11 @@ use parity_util_mem::MallocSizeOf; use ethereum_types::{H256, Address}; use machine::{AuxiliaryData, Call, Machine}; -use types::BlockNumber; -use types::header::Header; +use types::{ + BlockNumber, + header::Header, + errors::EthcoreError, +}; use super::ValidatorSet; /// Validator set containing a known set of addresses. @@ -78,7 +81,7 @@ impl ValidatorSet for SimpleList { ::engines::EpochChange::No } - fn epoch_set(&self, _first: bool, _: &Machine, _: BlockNumber, _: &[u8]) -> Result<(SimpleList, Option), ::error::Error> { + fn epoch_set(&self, _first: bool, _: &Machine, _: BlockNumber, _: &[u8]) -> Result<(SimpleList, Option), EthcoreError> { Ok((self.clone(), None)) } diff --git a/ethcore/src/error.rs b/ethcore/src/error.rs index 8affe6a9757..8003612389f 100644 --- a/ethcore/src/error.rs +++ b/ethcore/src/error.rs @@ -16,243 +16,10 @@ //! General error types for use in ethcore. -use std::{fmt, error}; -use std::time::SystemTime; +use types::errors::EthcoreError; -use derive_more::{Display, From}; -use ethereum_types::{H256, U256, Address, Bloom}; -use ethkey::Error as EthkeyError; -use ethtrie::TrieError; -use rlp; -use snappy::InvalidInput; -use snapshot::Error as SnapshotError; -use types::BlockNumber; -use types::transaction::Error as TransactionError; -use unexpected::{Mismatch, OutOfBounds}; - -use engines::EngineError; - -pub use executed::{ExecutionError, CallError}; - -/// Errors concerning block processing. -#[derive(Debug, Display, PartialEq, Clone, Eq)] -pub enum BlockError { - /// Block has too many uncles. - #[display(fmt = "Block has too many uncles. {}", _0)] - TooManyUncles(OutOfBounds), - /// Extra data is of an invalid length. - #[display(fmt = "Extra block data too long. {}", _0)] - ExtraDataOutOfBounds(OutOfBounds), - /// Seal is incorrect format. - #[display(fmt = "Block seal in incorrect format: {}", _0)] - InvalidSealArity(Mismatch), - /// Block has too much gas used. - #[display(fmt = "Block has too much gas used. {}", _0)] - TooMuchGasUsed(OutOfBounds), - /// Uncles hash in header is invalid. - #[display(fmt = "Block has invalid uncles hash: {}", _0)] - InvalidUnclesHash(Mismatch), - /// An uncle is from a generation too old. - #[display(fmt = "Uncle block is too old. {}", _0)] - UncleTooOld(OutOfBounds), - /// An uncle is from the same generation as the block. - #[display(fmt = "Uncle from same generation as block. {}", _0)] - UncleIsBrother(OutOfBounds), - /// An uncle is already in the chain. - #[display(fmt = "Uncle {} already in chain", _0)] - UncleInChain(H256), - /// An uncle is included twice. - #[display(fmt = "Uncle {} already in the header", _0)] - DuplicateUncle(H256), - /// An uncle has a parent not in the chain. - #[display(fmt = "Uncle {} has a parent not in the chain", _0)] - UncleParentNotInChain(H256), - /// State root header field is invalid. - #[display(fmt = "Invalid state root in header: {}", _0)] - InvalidStateRoot(Mismatch), - /// Gas used header field is invalid. - #[display(fmt = "Invalid gas used in header: {}", _0)] - InvalidGasUsed(Mismatch), - /// Transactions root header field is invalid. - #[display(fmt = "Invalid transactions root in header: {}", _0)] - InvalidTransactionsRoot(Mismatch), - /// Difficulty is out of range; this can be used as an looser error prior to getting a definitive - /// value for difficulty. This error needs only provide bounds of which it is out. - #[display(fmt = "Difficulty out of bounds: {}", _0)] - DifficultyOutOfBounds(OutOfBounds), - /// Difficulty header field is invalid; this is a strong error used after getting a definitive - /// value for difficulty (which is provided). - #[display(fmt = "Invalid block difficulty: {}", _0)] - InvalidDifficulty(Mismatch), - /// Seal element of type H256 (max_hash for Ethash, but could be something else for - /// other seal engines) is out of bounds. - #[display(fmt = "Seal element out of bounds: {}", _0)] - MismatchedH256SealElement(Mismatch), - /// Proof-of-work aspect of seal, which we assume is a 256-bit value, is invalid. - #[display(fmt = "Block has invalid PoW: {}", _0)] - InvalidProofOfWork(OutOfBounds), - /// Some low-level aspect of the seal is incorrect. - #[display(fmt = "Block has invalid seal.")] - InvalidSeal, - /// Gas limit header field is invalid. - #[display(fmt = "Invalid gas limit: {}", _0)] - InvalidGasLimit(OutOfBounds), - /// Receipts trie root header field is invalid. - #[display(fmt = "Invalid receipts trie root in header: {}", _0)] - InvalidReceiptsRoot(Mismatch), - /// Timestamp header field is invalid. - #[display(fmt = "Invalid timestamp in header: {}", _0)] - InvalidTimestamp(OutOfBoundsTime), - /// Timestamp header field is too far in future. - #[display(fmt = "Future timestamp in header: {}", _0)] - TemporarilyInvalid(OutOfBoundsTime), - /// Log bloom header field is invalid. - #[display(fmt = "Invalid log bloom in header: {}", _0)] - InvalidLogBloom(Box>), - /// Number field of header is invalid. - #[display(fmt = "Invalid number in header: {}", _0)] - InvalidNumber(Mismatch), - /// Block number isn't sensible. - #[display(fmt = "Implausible block number. {}", _0)] - RidiculousNumber(OutOfBounds), - /// Timestamp header overflowed - #[display(fmt = "Timestamp overflow")] - TimestampOverflow, - /// Too many transactions from a particular address. - #[display(fmt = "Too many transactions from: {}", _0)] - TooManyTransactions(Address), - /// Parent given is unknown. - #[display(fmt = "Unknown parent: {}", _0)] - UnknownParent(H256), - /// Uncle parent given is unknown. - #[display(fmt = "Unknown uncle parent: {}", _0)] - UnknownUncleParent(H256), - /// No transition to epoch number. - #[display(fmt = "Unknown transition to epoch number: {}", _0)] - UnknownEpochTransition(u64), -} - -/// Newtype for Display impl to show seconds -#[derive(Debug, Clone, From, PartialEq, Eq)] -pub struct OutOfBoundsTime(OutOfBounds); - -impl fmt::Display for OutOfBoundsTime { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let seconds = self.0 - .map(|st| st.elapsed().unwrap_or_default().as_secs()); - f.write_fmt(format_args!("{}", seconds)) - } -} - -impl error::Error for BlockError { - fn description(&self) -> &str { - "Block error" - } -} - -/// Block import Error -#[derive(Debug, Display)] -pub enum ImportError { - /// Already in the block chain. - #[display(fmt = "Block already in chain")] - AlreadyInChain, - /// Already in the block queue - #[display(fmt = "block already in the block queue")] - AlreadyQueued, - /// Already marked as bad from a previous import (could mean parent is bad) - #[display(fmt = "block known to be bad")] - KnownBad, -} - -impl error::Error for ImportError {} +pub use executed::CallError; // todo: move to common_types /// Ethcore Result -pub type EthcoreResult = Result; - -/// Ethcore Error -#[derive(Debug, Display, From)] -pub enum Error { - /// Error concerning block import. - #[display(fmt = "Import error: {}", _0)] - Import(ImportError), - /// Io channel queue error - #[display(fmt = "Queue is full: {}", _0)] - FullQueue(usize), - /// Io create error - #[display(fmt = "Io error: {}", _0)] - Io(::io::IoError), - /// Error concerning the Rust standard library's IO subsystem. - #[display(fmt = "Std Io error: {}", _0)] - StdIo(::std::io::Error), - /// Error concerning TrieDBs. - #[display(fmt = "Trie error: {}", _0)] - Trie(TrieError), - /// Error concerning EVM code execution. - #[display(fmt = "Execution error: {}", _0)] - Execution(ExecutionError), - /// Error concerning block processing. - #[display(fmt = "Block error: {}", _0)] - Block(BlockError), - /// Error concerning transaction processing. - #[display(fmt = "Transaction error: {}", _0)] - Transaction(TransactionError), - /// Snappy error - #[display(fmt = "Snappy error: {}", _0)] - Snappy(InvalidInput), - /// Consensus vote error. - #[display(fmt = "Engine error: {}", _0)] - Engine(EngineError), - /// Ethkey error." - #[display(fmt = "Ethkey error: {}", _0)] - Ethkey(EthkeyError), - /// RLP decoding errors - #[display(fmt = "Decoder error: {}", _0)] - Decoder(rlp::DecoderError), - /// Snapshot error. - #[display(fmt = "Snapshot error {}", _0)] - Snapshot(SnapshotError), - /// PoW hash is invalid or out of date. - #[display(fmt = "PoW hash is invalid or out of date.")] - PowHashInvalid, - /// The value of the nonce or mishash is invalid. - #[display(fmt = "The value of the nonce or mishash is invalid.")] - PowInvalid, - /// A convenient variant for String. - #[display(fmt = "{}", _0)] - Msg(String), - /// State errors - #[display(fmt = "State error ({})", _0)] - State(account_state::Error), -} - -impl error::Error for Error { - fn source(&self) -> Option<&(dyn error::Error + 'static)> { - match self { - Error::Io(e) => Some(e), - Error::StdIo(e) => Some(e), - Error::Trie(e) => Some(e), - Error::Execution(e) => Some(e), - Error::Block(e) => Some(e), - Error::Transaction(e) => Some(e), - Error::Snappy(e) => Some(e), - Error::Engine(e) => Some(e), - Error::Ethkey(e) => Some(e), - Error::Decoder(e) => Some(e), - Error::Snapshot(e) => Some(e), - Error::State(e) => Some(e), - _ => None, - } - } -} - -impl From<&str> for Error { - fn from(s: &str) -> Self { - Error::Msg(s.into()) - } -} +pub type EthcoreResult = Result; -impl From> for Error where Error: From { - fn from(err: Box) -> Error { - Error::from(*err) - } -} diff --git a/ethcore/src/ethereum/ethash.rs b/ethcore/src/ethereum/ethash_eng.rs similarity index 95% rename from ethcore/src/ethereum/ethash.rs rename to ethcore/src/ethereum/ethash_eng.rs index 91964a11e95..6dfe0609135 100644 --- a/ethcore/src/ethereum/ethash.rs +++ b/ethcore/src/ethereum/ethash_eng.rs @@ -19,18 +19,24 @@ use std::collections::BTreeMap; use std::path::Path; use std::sync::Arc; +use client_traits::VerifyingEngine; use ethereum_types::{H256, H64, U256}; use ethjson; use hash::{KECCAK_EMPTY_LIST_RLP}; use rlp::Rlp; -use types::header::Header; -use types::BlockNumber; +use types::{ + BlockNumber, + header::Header, + engines::{EthashExtensions, params::CommonParams}, + transaction::{self, UnverifiedTransaction, SignedTransaction}, + errors::{BlockError, EthcoreError as Error}, +}; + use unexpected::{OutOfBounds, Mismatch}; use block::ExecutedBlock; use engines::block_reward::{self, BlockRewardContract, RewardKind}; use engines::{self, Engine}; -use error::{BlockError, Error}; use ethash::{self, quick_get_difficulty, slow_hash_block_number, EthashManager, OptimizeFor}; use machine::Machine; @@ -184,14 +190,14 @@ impl Ethash { ethash_params: EthashParams, machine: Machine, optimize_for: T, - ) -> Arc { + ) -> Self { let progpow_transition = ethash_params.progpow_transition; - Arc::new(Ethash { + Ethash { ethash_params, machine, pow: EthashManager::new(cache_dir.as_ref(), optimize_for.into(), progpow_transition), - }) + } } } @@ -203,20 +209,100 @@ impl Ethash { // for any block in the chain. // in the future, we might move the Ethash epoch // caching onto this mechanism as well. -impl engines::EpochVerifier for Arc { +impl engines::EpochVerifier for Ethash { fn verify_light(&self, _header: &Header) -> Result<(), Error> { Ok(()) } fn verify_heavy(&self, header: &Header) -> Result<(), Error> { - self.verify_block_unordered(header) + self.verify_block_unordered(header).into() } } -impl Engine for Arc { - fn name(&self) -> &str { "Ethash" } - fn machine(&self) -> &Machine { &self.machine } - +impl VerifyingEngine for Ethash { // Two fields - nonce and mix. fn seal_fields(&self, _header: &Header) -> usize { 2 } + fn maximum_gas_limit(&self) -> Option { Some(0x7fff_ffff_ffff_ffffu64.into()) } + + fn params(&self) -> &CommonParams { self.machine.params() } + + /// Get a reference to the ethash-specific extensions. + fn ethash_extensions(&self) -> Option<&EthashExtensions> { + self.machine.ethash_extensions() + } + + fn verify_block_basic(&self, header: &Header) -> Result<(), Error> { + // check the seal fields. + let seal = Seal::parse_seal(header.seal())?; + + // TODO: consider removing these lines. + let min_difficulty = self.ethash_params.minimum_difficulty; + if header.difficulty() < &min_difficulty { + return Err(From::from(BlockError::DifficultyOutOfBounds(OutOfBounds { min: Some(min_difficulty), max: None, found: header.difficulty().clone() }))) + } + + let difficulty = ethash::boundary_to_difficulty(&H256(quick_get_difficulty( + &header.bare_hash().0, + seal.nonce.to_low_u64_be(), + &seal.mix_hash.0, + header.number() >= self.ethash_params.progpow_transition + ))); + + if &difficulty < header.difficulty() { + return Err(From::from(BlockError::InvalidProofOfWork(OutOfBounds { min: Some(header.difficulty().clone()), max: None, found: difficulty }))); + } + + Ok(()) + } + + fn verify_block_unordered(&self, header: &Header) -> Result<(), Error> { + let seal = Seal::parse_seal(header.seal())?; + + let result = self.pow.compute_light(header.number() as u64, &header.bare_hash().0, seal.nonce.to_low_u64_be()); + let mix = H256(result.mix_hash); + let difficulty = ethash::boundary_to_difficulty(&H256(result.value)); + trace!(target: "miner", "num: {num}, seed: {seed}, h: {h}, non: {non}, mix: {mix}, res: {res}", + num = header.number() as u64, + seed = H256(slow_hash_block_number(header.number() as u64)), + h = header.bare_hash(), + non = seal.nonce.to_low_u64_be(), + mix = H256(result.mix_hash), + res = H256(result.value)); + if mix != seal.mix_hash { + return Err(From::from(BlockError::MismatchedH256SealElement(Mismatch { expected: mix, found: seal.mix_hash }))); + } + if &difficulty < header.difficulty() { + return Err(From::from(BlockError::InvalidProofOfWork(OutOfBounds { min: Some(header.difficulty().clone()), max: None, found: difficulty }))); + } + Ok(()) + } + + fn verify_block_family(&self, header: &Header, parent: &Header) -> Result<(), Error> { + // we should not calculate difficulty for genesis blocks + if header.number() == 0 { + return Err(From::from(BlockError::RidiculousNumber(OutOfBounds { min: Some(1), max: None, found: header.number() }))); + } + + // Check difficulty is correct given the two timestamps. + let expected_difficulty = self.calculate_difficulty(header, parent); + if header.difficulty() != &expected_difficulty { + return Err(From::from(BlockError::InvalidDifficulty(Mismatch { expected: expected_difficulty, found: header.difficulty().clone() }))) + } + + Ok(()) + } + + fn verify_transaction_basic(&self, t: &UnverifiedTransaction, header: &Header) -> Result<(), transaction::Error> { + self.machine().verify_transaction_basic(t, header) + } + + fn verify_transaction_unordered(&self, t: UnverifiedTransaction, header: &Header) -> Result { + self.machine().verify_transaction_unordered(t, header) + } +} + +impl Engine for Ethash { + fn name(&self) -> &str { "Ethash" } + fn machine(&self) -> &Machine { &self.machine } + /// Additional engine-specific information for the user/developer concerning `header`. fn extra_info(&self, header: &Header) -> BTreeMap { match Seal::parse_seal(header.seal()) { @@ -230,13 +316,6 @@ impl Engine for Arc { fn maximum_uncle_count(&self, _block: BlockNumber) -> usize { 2 } - fn maximum_gas_limit(&self) -> Option { Some(0x7fff_ffff_ffff_ffffu64.into()) } - - fn populate_from_parent(&self, header: &mut Header, parent: &Header) { - let difficulty = self.calculate_difficulty(header, parent); - header.set_difficulty(difficulty); - } - /// Apply the block reward on finalisation of the block. /// This assumes that all uncles are valid uncles (i.e. of at least one generation before the current). fn on_close_block(&self, block: &mut ExecutedBlock, _parent_header: &Header) -> Result<(), Error> { @@ -303,80 +382,19 @@ impl Engine for Arc { #[cfg(not(feature = "miner-debug"))] fn verify_local_seal(&self, header: &Header) -> Result<(), Error> { self.verify_block_basic(header) - .and_then(|_| self.verify_block_unordered(header)) - } - - #[cfg(feature = "miner-debug")] - fn verify_local_seal(&self, _header: &Header) -> Result<(), Error> { - warn!("Skipping seal verification, running in miner testing mode."); - Ok(()) - } - - fn verify_block_basic(&self, header: &Header) -> Result<(), Error> { - // check the seal fields. - let seal = Seal::parse_seal(header.seal())?; - - // TODO: consider removing these lines. - let min_difficulty = self.ethash_params.minimum_difficulty; - if header.difficulty() < &min_difficulty { - return Err(From::from(BlockError::DifficultyOutOfBounds(OutOfBounds { min: Some(min_difficulty), max: None, found: header.difficulty().clone() }))) - } - - let difficulty = ethash::boundary_to_difficulty(&H256(quick_get_difficulty( - &header.bare_hash().0, - seal.nonce.to_low_u64_be(), - &seal.mix_hash.0, - header.number() >= self.ethash_params.progpow_transition - ))); - - if &difficulty < header.difficulty() { - return Err(From::from(BlockError::InvalidProofOfWork(OutOfBounds { min: Some(header.difficulty().clone()), max: None, found: difficulty }))); - } - - Ok(()) - } - - fn verify_block_unordered(&self, header: &Header) -> Result<(), Error> { - let seal = Seal::parse_seal(header.seal())?; - - let result = self.pow.compute_light(header.number() as u64, &header.bare_hash().0, seal.nonce.to_low_u64_be()); - let mix = H256(result.mix_hash); - let difficulty = ethash::boundary_to_difficulty(&H256(result.value)); - trace!(target: "miner", "num: {num}, seed: {seed}, h: {h}, non: {non}, mix: {mix}, res: {res}", - num = header.number() as u64, - seed = H256(slow_hash_block_number(header.number() as u64)), - h = header.bare_hash(), - non = seal.nonce.to_low_u64_be(), - mix = H256(result.mix_hash), - res = H256(result.value)); - if mix != seal.mix_hash { - return Err(From::from(BlockError::MismatchedH256SealElement(Mismatch { expected: mix, found: seal.mix_hash }))); - } - if &difficulty < header.difficulty() { - return Err(From::from(BlockError::InvalidProofOfWork(OutOfBounds { min: Some(header.difficulty().clone()), max: None, found: difficulty }))); - } - Ok(()) - } - - fn verify_block_family(&self, header: &Header, parent: &Header) -> Result<(), Error> { - // we should not calculate difficulty for genesis blocks - if header.number() == 0 { - return Err(From::from(BlockError::RidiculousNumber(OutOfBounds { min: Some(1), max: None, found: header.number() }))); - } - - // Check difficulty is correct given the two timestamps. - let expected_difficulty = self.calculate_difficulty(header, parent); - if header.difficulty() != &expected_difficulty { - return Err(From::from(BlockError::InvalidDifficulty(Mismatch { expected: expected_difficulty, found: header.difficulty().clone() }))) - } - - Ok(()) + .map_err(Into::into) + .and_then(|_| self.verify_block_unordered(header).map_err(Into::into)) } fn epoch_verifier<'a>(&self, _header: &Header, _proof: &'a [u8]) -> engines::ConstructedVerifier<'a> { engines::ConstructedVerifier::Trusted(Box::new(self.clone())) } + fn populate_from_parent(&self, header: &mut Header, parent: &Header) { + let difficulty = self.calculate_difficulty(header, parent); + header.set_difficulty(difficulty); + } + fn snapshot_components(&self) -> Option> { Some(Box::new(::snapshot::PowSnapshot::new(SNAPSHOT_BLOCKS, MAX_SNAPSHOT_BLOCKS))) } @@ -486,8 +504,10 @@ mod tests { use ethereum_types::{H64, H256, U256, Address}; use block::*; use test_helpers::get_temp_state_db; - use error::{BlockError, Error}; - use types::header::Header; + use types::{ + header::Header, + errors::{BlockError, EthcoreError as Error}, + }; use spec::Spec; use engines::Engine; use super::super::{new_morden, new_mcip3_test, new_homestead_test_machine}; diff --git a/ethcore/src/ethereum/mod.rs b/ethcore/src/ethereum/mod.rs index af2114a1bd4..2666208cabf 100644 --- a/ethcore/src/ethereum/mod.rs +++ b/ethcore/src/ethereum/mod.rs @@ -20,11 +20,11 @@ //! consensus specifications. /// Export the ethash module. -pub mod ethash; +pub mod ethash_eng; /// Export the denominations module. pub mod denominations; -pub use self::ethash::{Ethash}; +pub use self::ethash_eng::{Ethash}; pub use self::denominations::*; use machine::Machine; diff --git a/ethcore/src/executed.rs b/ethcore/src/executed.rs index 10e06fd05e2..7f58dae41c3 100644 --- a/ethcore/src/executed.rs +++ b/ethcore/src/executed.rs @@ -16,15 +16,17 @@ //! Transaction execution format module. -use ethereum_types::{U256, U512, Address}; +use std::fmt; + +use ethereum_types::{U256, Address}; use bytes::Bytes; -use ethtrie; use vm; use trace::{VMTrace, FlatTrace}; -use types::state_diff::StateDiff; -use types::log_entry::LogEntry; - -use std::{fmt, error}; +use types::{ + state_diff::StateDiff, + log_entry::LogEntry, + errors::ExecutionError, +}; /// Transaction execution receipt. #[derive(Debug, PartialEq, Clone)] @@ -69,96 +71,6 @@ pub struct Executed { pub state_diff: Option, } -/// Result of executing the transaction. -#[derive(PartialEq, Debug, Clone)] -pub enum ExecutionError { - /// Returned when there gas paid for transaction execution is - /// lower than base gas required. - NotEnoughBaseGas { - /// Absolute minimum gas required. - required: U256, - /// Gas provided. - got: U256 - }, - /// Returned when block (gas_used + gas) > gas_limit. - /// - /// If gas =< gas_limit, upstream may try to execute the transaction - /// in next block. - BlockGasLimitReached { - /// Gas limit of block for transaction. - gas_limit: U256, - /// Gas used in block prior to transaction. - gas_used: U256, - /// Amount of gas in block. - gas: U256 - }, - /// Returned when transaction nonce does not match state nonce. - InvalidNonce { - /// Nonce expected. - expected: U256, - /// Nonce found. - got: U256 - }, - /// Returned when cost of transaction (value + gas_price * gas) exceeds - /// current sender balance. - NotEnoughCash { - /// Minimum required balance. - required: U512, - /// Actual balance. - got: U512 - }, - /// When execution tries to modify the state in static context - MutableCallInStaticContext, - /// Returned when transacting from a non-existing account with dust protection enabled. - SenderMustExist, - /// Returned when internal evm error occurs. - Internal(String), - /// Returned when generic transaction occurs - TransactionMalformed(String), -} - -impl From> for ExecutionError { - fn from(err: Box) -> Self { - ExecutionError::Internal(format!("{:?}", err)) - } -} -impl From for ExecutionError { - fn from(err: ethtrie::TrieError) -> Self { - ExecutionError::Internal(format!("{:?}", err)) - } -} - -impl fmt::Display for ExecutionError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - use self::ExecutionError::*; - - let msg = match *self { - NotEnoughBaseGas { ref required, ref got } => - format!("Not enough base gas. {} is required, but only {} paid", required, got), - BlockGasLimitReached { ref gas_limit, ref gas_used, ref gas } => - format!("Block gas limit reached. The limit is {}, {} has \ - already been used, and {} more is required", gas_limit, gas_used, gas), - InvalidNonce { ref expected, ref got } => - format!("Invalid transaction nonce: expected {}, found {}", expected, got), - NotEnoughCash { ref required, ref got } => - format!("Cost of transaction exceeds sender balance. {} is required \ - but the sender only has {}", required, got), - MutableCallInStaticContext => "Mutable Call in static context".to_owned(), - SenderMustExist => "Transacting from an empty account".to_owned(), - Internal(ref msg) => msg.clone(), - TransactionMalformed(ref err) => format!("Malformed transaction: {}", err), - }; - - f.write_fmt(format_args!("Transaction execution error ({}).", msg)) - } -} - -impl error::Error for ExecutionError { - fn description(&self) -> &str { - "Transaction execution error" - } -} - /// Result of executing the transaction. #[derive(PartialEq, Debug, Clone)] pub enum CallError { @@ -183,7 +95,6 @@ impl From for CallError { impl fmt::Display for CallError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { use self::CallError::*; - let msg = match *self { TransactionNotFound => "Transaction couldn't be found in the chain".into(), StatePruned => "Couldn't find the transaction block's state in the chain".into(), diff --git a/ethcore/src/executive.rs b/ethcore/src/executive.rs index 4ff2ed26d9d..b32b15ea149 100644 --- a/ethcore/src/executive.rs +++ b/ethcore/src/executive.rs @@ -22,7 +22,6 @@ use hash::keccak; use ethereum_types::{H256, U256, U512, Address}; use bytes::{Bytes, BytesRef}; use account_state::{Backend as StateBackend, State, Substate, CleanupMode}; -use executed::ExecutionError; use machine::Machine; use evm::{CallType, Finalize, FinalizationResult}; use vm::{ @@ -32,7 +31,10 @@ use vm::{ use trie_vm_factories::VmFactory; use externalities::*; use trace::{self, Tracer, VMTracer}; -use types::transaction::{Action, SignedTransaction}; +use types::{ + errors::ExecutionError, + transaction::{Action, SignedTransaction}, +}; use transaction_ext::Transaction; use crossbeam_utils::thread; pub use executed::{Executed, ExecutionResult}; @@ -1200,14 +1202,16 @@ mod tests { use ethereum_types::{H256, U256, U512, Address, BigEndianHash}; use vm::{ActionParams, ActionValue, CallType, EnvInfo, CreateContractAddress}; use evm::{Factory, VMType}; - use error::ExecutionError; use machine::Machine; use account_state::{Substate, CleanupMode}; use test_helpers::{get_temp_state_with_factory, get_temp_state}; use trace::trace; use trace::{FlatTrace, Tracer, NoopTracer, ExecutiveTracer}; use trace::{VMTrace, VMOperation, VMExecutedOperation, MemoryDiff, StorageDiff, VMTracer, NoopVMTracer, ExecutiveVMTracer}; - use types::transaction::{Action, Transaction}; + use types::{ + errors::ExecutionError, + transaction::{Action, Transaction}, + }; fn make_frontier_machine(max_depth: usize) -> Machine { let mut machine = ::ethereum::new_frontier_test_machine(); diff --git a/ethcore/src/executive_state.rs b/ethcore/src/executive_state.rs index f0f89f8bc6d..0931c45811b 100644 --- a/ethcore/src/executive_state.rs +++ b/ethcore/src/executive_state.rs @@ -21,8 +21,9 @@ use machine::Machine; use vm::EnvInfo; use executive::{Executive, TransactOptions}; -use executed::{Executed, ExecutionError}; +use executed::Executed; use types::{ + errors::{ExecutionError, EthcoreError as Error}, transaction::SignedTransaction, receipt::{TransactionOutcome, Receipt}, }; @@ -38,8 +39,6 @@ use keccak_hasher::KeccakHasher; use kvdb::DBValue; use hash_db::AsHashDB; -use error::Error; - /// Return type of proof validity check. #[derive(Debug, Clone)] pub enum ProvedExecution { diff --git a/ethcore/src/externalities.rs b/ethcore/src/externalities.rs index b992fc157a1..01cc978ef25 100644 --- a/ethcore/src/externalities.rs +++ b/ethcore/src/externalities.rs @@ -17,18 +17,19 @@ //! Transaction Execution environment. use std::cmp; use std::sync::Arc; -use ethereum_types::{H256, U256, Address, BigEndianHash}; -use bytes::Bytes; + use account_state::{Backend as StateBackend, State, Substate, CleanupMode}; -use machine::Machine; +use bytes::Bytes; +use ethereum_types::{H256, U256, Address, BigEndianHash}; use executive::*; +use machine::Machine; +use types::transaction::UNSIGNED_SENDER; +use trace::{Tracer, VMTracer}; use vm::{ self, ActionParams, ActionValue, EnvInfo, CallType, Schedule, Ext, ContractCreateResult, MessageCallResult, CreateContractAddress, ReturnData, TrapKind }; -use types::transaction::UNSIGNED_SENDER; -use trace::{Tracer, VMTracer}; /// Policy for handling output data on `RETURN` opcode. pub enum OutputPolicy { diff --git a/ethcore/src/lib.rs b/ethcore/src/lib.rs index 10caf9b0539..61db6e92997 100644 --- a/ethcore/src/lib.rs +++ b/ethcore/src/lib.rs @@ -57,6 +57,7 @@ extern crate account_db; extern crate account_state; extern crate ansi_term; extern crate common_types as types; +extern crate client_traits; extern crate crossbeam_utils; extern crate ethabi; extern crate ethash; @@ -139,6 +140,7 @@ extern crate macros; extern crate rlp_derive; #[macro_use] extern crate trace_time; +pub extern crate verification; // todo doesn't need to be public #[cfg_attr(test, macro_use)] extern crate evm; @@ -152,7 +154,7 @@ extern crate parity_runtime; pub mod block; pub mod client; pub mod engines; -pub mod error; +//pub mod error; // todo pub mod ethereum; pub mod executed; pub mod executive; @@ -161,11 +163,12 @@ pub mod machine; pub mod miner; pub mod snapshot; pub mod spec; -pub mod verification; +//pub mod verification; mod externalities; mod transaction_ext; mod tx_filter; +mod error; // todo: CallError is still there but after it's gone, rename module #[cfg(test)] mod tests; diff --git a/ethcore/src/machine.rs b/ethcore/src/machine.rs index eb0ffd384cf..bdf799034c6 100644 --- a/ethcore/src/machine.rs +++ b/ethcore/src/machine.rs @@ -22,19 +22,24 @@ use std::sync::Arc; use ethereum_types::{U256, H256, Address}; use rlp::Rlp; -use types::transaction::{self, SYSTEM_ADDRESS, UNSIGNED_SENDER, UnverifiedTransaction, SignedTransaction}; -use types::BlockNumber; -use types::header::Header; +use types::{ + BlockNumber, + header::Header, + engines::{ + EthashExtensions, + params::CommonParams, + }, + errors::{EngineError, EthcoreError as Error}, + transaction::{self, SYSTEM_ADDRESS, UNSIGNED_SENDER, UnverifiedTransaction, SignedTransaction}, +}; use vm::{CallType, ActionParams, ActionValue, ParamsType}; use vm::{EnvInfo, Schedule, CreateContractAddress}; use block::ExecutedBlock; use builtin::Builtin; use call_contract::CallContract; -use client::BlockInfo; -use error::Error; +use client_traits::BlockInfo; use executive::Executive; -use spec::CommonParams; use account_state::{CleanupMode, Substate}; use trace::{NoopTracer, NoopVMTracer}; use tx_filter::TransactionFilter; @@ -42,30 +47,6 @@ use tx_filter::TransactionFilter; /// Parity tries to round block.gas_limit to multiple of this constant pub const PARITY_GAS_LIMIT_DETERMINANT: U256 = U256([37, 0, 0, 0]); -/// Ethash-specific extensions. -#[derive(Debug, Clone)] -pub struct EthashExtensions { - /// Homestead transition block number. - pub homestead_transition: BlockNumber, - /// DAO hard-fork transition block (X). - pub dao_hardfork_transition: u64, - /// DAO hard-fork refund contract address (C). - pub dao_hardfork_beneficiary: Address, - /// DAO hard-fork DAO accounts list (L) - pub dao_hardfork_accounts: Vec
, -} - -impl From<::ethjson::spec::EthashParams> for EthashExtensions { - fn from(p: ::ethjson::spec::EthashParams) -> Self { - EthashExtensions { - homestead_transition: p.homestead_transition.map_or(0, Into::into), - dao_hardfork_transition: p.dao_hardfork_transition.map_or(u64::max_value(), Into::into), - dao_hardfork_beneficiary: p.dao_hardfork_beneficiary.map_or_else(Address::zero, Into::into), - dao_hardfork_accounts: p.dao_hardfork_accounts.unwrap_or_else(Vec::new).into_iter().map(Into::into).collect(), - } - } -} - /// Special rules to be applied to the schedule. pub type ScheduleCreationRules = dyn Fn(&mut Schedule, BlockNumber) + Sync + Send; @@ -183,7 +164,7 @@ impl Machine { let mut ex = Executive::new(&mut state, &env_info, self, &schedule); let mut substate = Substate::new(); - let res = ex.call(params, &mut substate, &mut NoopTracer, &mut NoopVMTracer).map_err(|e| ::engines::EngineError::FailedSystemCall(format!("{}", e)))?; + let res = ex.call(params, &mut substate, &mut NoopTracer, &mut NoopVMTracer).map_err(|e| EngineError::FailedSystemCall(format!("{}", e)))?; let output = res.return_data.to_vec(); Ok(output) @@ -372,9 +353,12 @@ impl Machine { } /// Does verification of the transaction against the parent state. - pub fn verify_transaction(&self, t: &SignedTransaction, parent: &Header, client: &C) - -> Result<(), transaction::Error> - { + pub fn verify_transaction( + &self, + t: &SignedTransaction, + parent: &Header, + client: &C + ) -> Result<(), transaction::Error> { if let Some(ref filter) = self.tx_filter.as_ref() { if !filter.transaction_allowed(&parent.hash(), parent.number() + 1, t, client) { return Err(transaction::Error::NotAllowed.into()) diff --git a/ethcore/src/miner/miner.rs b/ethcore/src/miner/miner.rs index 33c33883672..3b42364384e 100644 --- a/ethcore/src/miner/miner.rs +++ b/ethcore/src/miner/miner.rs @@ -43,20 +43,22 @@ use types::transaction::{ SignedTransaction, PendingTransaction, }; -use types::BlockNumber; -use types::block::Block; -use types::header::Header; -use types::receipt::RichReceipt; +use types::{ + BlockNumber, + block::Block, + header::Header, + errors::{EthcoreError as Error, ExecutionError}, + receipt::RichReceipt, + client_io_message::ClientIoMessage, +}; use using_queue::{UsingQueue, GetAction}; use block::{ClosedBlock, SealedBlock}; use client::{ BlockChain, ChainInfo, BlockProducer, SealedBlockImporter, Nonce, TransactionInfo, TransactionId }; -use client::{BlockId, ClientIoMessage}; +use client::BlockId; use engines::{Engine, Seal, SealingState, EngineSigner}; -use error::Error; -use executed::ExecutionError; use executive::contract_address; use spec::Spec; use account_state::State; diff --git a/ethcore/src/miner/mod.rs b/ethcore/src/miner/mod.rs index 10a101ee5d4..c9f4c0d1f04 100644 --- a/ethcore/src/miner/mod.rs +++ b/ethcore/src/miner/mod.rs @@ -37,10 +37,13 @@ use bytes::Bytes; use ethcore_miner::pool::{VerifiedTransaction, QueueStatus, local_transactions}; use ethereum_types::{H256, U256, Address}; use types::transaction::{self, UnverifiedTransaction, SignedTransaction, PendingTransaction}; -use types::BlockNumber; -use types::block::Block; -use types::header::Header; -use types::receipt::RichReceipt; +use types::{ + BlockNumber, + errors::EthcoreError as Error, + block::Block, + header::Header, + receipt::RichReceipt, +}; use block::SealedBlock; use call_contract::{CallContract, RegistryInfo}; @@ -49,7 +52,6 @@ use client::{ BlockChain, BlockProducer, SealedBlockImporter, ChainInfo, AccountData, Nonce, }; -use error::Error; use account_state::state::StateInfo; /// Provides methods to verify incoming external transactions diff --git a/ethcore/src/miner/pool_client.rs b/ethcore/src/miner/pool_client.rs index b197e56f6c4..802b8e90068 100644 --- a/ethcore/src/miner/pool_client.rs +++ b/ethcore/src/miner/pool_client.rs @@ -36,7 +36,8 @@ use types::header::Header; use parking_lot::RwLock; use call_contract::CallContract; -use client::{TransactionId, BlockInfo, Nonce}; +use client::{TransactionId, Nonce}; +use client_traits::BlockInfo; use engines::Engine; use miner; use transaction_ext::Transaction; diff --git a/ethcore/src/snapshot/consensus/authority.rs b/ethcore/src/snapshot/consensus/authority.rs index 4f65d42909e..341c70d34c5 100644 --- a/ethcore/src/snapshot/consensus/authority.rs +++ b/ethcore/src/snapshot/consensus/authority.rs @@ -25,7 +25,7 @@ use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; use engines::{Engine, EpochVerifier, EpochTransition}; -use snapshot::{Error, ManifestData, Progress}; +use snapshot::{ManifestData, Progress}; use blockchain::{BlockChain, BlockChainDB, BlockProvider}; use bytes::Bytes; @@ -33,10 +33,13 @@ use ethereum_types::{H256, U256}; use itertools::{Position, Itertools}; use kvdb::KeyValueDB; use rlp::{RlpStream, Rlp}; -use types::encoded; -use types::header::Header; -use types::ids::BlockId; -use types::receipt::Receipt; +use types::{ + encoded, + header::Header, + ids::BlockId, + errors::{SnapshotError, EthcoreError}, + receipt::Receipt, +}; /// Snapshot creation and restoration for PoA chains. /// Chunk format: @@ -59,9 +62,9 @@ impl SnapshotComponents for PoaSnapshot { sink: &mut ChunkSink, _progress: &Progress, preferred_size: usize, - ) -> Result<(), Error> { + ) -> Result<(), SnapshotError> { let number = chain.block_number(&block_at) - .ok_or_else(|| Error::InvalidStartingBlock(BlockId::Hash(block_at)))?; + .ok_or_else(||SnapshotError::InvalidStartingBlock(BlockId::Hash(block_at)))?; let mut pending_size = 0; let mut rlps = Vec::new(); @@ -75,7 +78,7 @@ impl SnapshotComponents for PoaSnapshot { } let header = chain.block_header_data(&transition.block_hash) - .ok_or_else(|| Error::BlockNotFound(transition.block_hash))?; + .ok_or_else(||SnapshotError::BlockNotFound(transition.block_hash))?; let entry = { let mut entry_stream = RlpStream::new_list(2); @@ -100,12 +103,12 @@ impl SnapshotComponents for PoaSnapshot { let (block, receipts) = chain.block(&block_at) .and_then(|b| chain.block_receipts(&block_at).map(|r| (b, r))) - .ok_or_else(|| Error::BlockNotFound(block_at))?; + .ok_or_else(||SnapshotError::BlockNotFound(block_at))?; let block = block.decode()?; let parent_td = chain.block_details(block.header.parent_hash()) .map(|d| d.total_difficulty) - .ok_or_else(|| Error::BlockNotFound(block_at))?; + .ok_or_else(||SnapshotError::BlockNotFound(block_at))?; rlps.push({ let mut stream = RlpStream::new_list(5); @@ -128,11 +131,11 @@ impl SnapshotComponents for PoaSnapshot { chain: BlockChain, db: Arc, manifest: &ManifestData, - ) -> Result, ::error::Error> { + ) -> Result, EthcoreError> { Ok(Box::new(ChunkRebuilder { manifest: manifest.clone(), warp_target: None, - chain: chain, + chain, db: db.key_value().clone(), had_genesis: false, unverified_firsts: Vec::new(), @@ -146,7 +149,7 @@ impl SnapshotComponents for PoaSnapshot { // writes a chunk composed of the inner RLPs here. // flag indicates whether the chunk is the last chunk. -fn write_chunk(last: bool, chunk_data: &mut Vec, sink: &mut ChunkSink) -> Result<(), Error> { +fn write_chunk(last: bool, chunk_data: &mut Vec, sink: &mut ChunkSink) -> Result<(), SnapshotError> { let mut stream = RlpStream::new_list(1 + chunk_data.len()); stream.append(&last); @@ -185,7 +188,7 @@ impl ChunkRebuilder { last_verifier: &mut Option>, transition_rlp: Rlp, engine: &dyn Engine, - ) -> Result { + ) -> Result { use engines::ConstructedVerifier; // decode. @@ -202,7 +205,7 @@ impl ChunkRebuilder { Some(ref last) => if last.check_finality_proof(finality_proof).map_or(true, |hashes| !hashes.contains(&hash)) { - return Err(Error::BadEpochProof(header.number()).into()); + return Err(SnapshotError::BadEpochProof(header.number()).into()); }, None if header.number() != 0 => { // genesis never requires additional validation. @@ -231,7 +234,7 @@ impl ChunkRebuilder { block_number: header.number(), proof: epoch_data, }, - header: header, + header, }) } } @@ -242,7 +245,7 @@ impl Rebuilder for ChunkRebuilder { chunk: &[u8], engine: &dyn Engine, abort_flag: &AtomicBool, - ) -> Result<(), ::error::Error> { + ) -> Result<(), EthcoreError> { let rlp = Rlp::new(chunk); let is_last_chunk: bool = rlp.val_at(0)?; let num_items = rlp.item_count()?; @@ -255,13 +258,13 @@ impl Rebuilder for ChunkRebuilder { }; if num_transitions == 0 && !is_last_chunk { - return Err(Error::WrongChunkFormat("Found non-last chunk without any data.".into()).into()); + return Err(SnapshotError::WrongChunkFormat("Found non-last chunk without any data.".into()).into()); } let mut last_verifier = None; let mut last_number = None; for transition_rlp in rlp.iter().skip(1).take(num_transitions).with_position() { - if !abort_flag.load(Ordering::SeqCst) { return Err(Error::RestorationAborted.into()) } + if !abort_flag.load(Ordering::SeqCst) { return Err(SnapshotError::RestorationAborted.into()) } let (is_first, is_last) = match transition_rlp { Position::First(_) => (true, false), @@ -278,7 +281,7 @@ impl Rebuilder for ChunkRebuilder { )?; if last_number.map_or(false, |num| verified.header.number() <= num) { - return Err(Error::WrongChunkFormat("Later epoch transition in earlier or same block.".into()).into()); + return Err(SnapshotError::WrongChunkFormat("Later epoch transition in earlier or same block.".into()).into()); } last_number = Some(verified.header.number()); @@ -289,7 +292,7 @@ impl Rebuilder for ChunkRebuilder { // but it doesn't need verification later. if verified.header.number() == 0 { if verified.header.hash() != self.chain.genesis_hash() { - return Err(Error::WrongBlockHash(0, verified.header.hash(), self.chain.genesis_hash()).into()); + return Err(SnapshotError::WrongBlockHash(0, verified.header.hash(), self.chain.genesis_hash()).into()); } self.had_genesis = true; @@ -332,7 +335,7 @@ impl Rebuilder for ChunkRebuilder { let hash = block.header.hash(); let best_hash = self.manifest.block_hash; if hash != best_hash { - return Err(Error::WrongBlockHash(block.header.number(), best_hash, hash).into()) + return Err(SnapshotError::WrongBlockHash(block.header.number(), best_hash, hash).into()) } } @@ -348,14 +351,14 @@ impl Rebuilder for ChunkRebuilder { Ok(()) } - fn finalize(&mut self) -> Result<(), ::error::Error> { + fn finalize(&mut self) -> Result<(), EthcoreError> { if !self.had_genesis { - return Err(Error::WrongChunkFormat("No genesis transition included.".into()).into()); + return Err(SnapshotError::WrongChunkFormat("No genesis transition included.".into()).into()); } let target_header = match self.warp_target.take() { Some(x) => x, - None => return Err(Error::WrongChunkFormat("Warp target block not included.".into()).into()), + None => return Err(SnapshotError::WrongChunkFormat("Warp target block not included.".into()).into()), }; trace!(target: "snapshot", "rebuilder, finalize: verifying {} unverified first blocks", self.unverified_firsts.len()); @@ -368,7 +371,7 @@ impl Rebuilder for ChunkRebuilder { while let Some(&(ref last_header, ref last_verifier)) = lasts_reversed.next() { if last_header.number() < header.number() { if last_verifier.check_finality_proof(&finality_proof).map_or(true, |hashes| !hashes.contains(&hash)) { - return Err(Error::BadEpochProof(header.number()).into()); + return Err(SnapshotError::BadEpochProof(header.number()).into()); } found = true; break; @@ -376,7 +379,7 @@ impl Rebuilder for ChunkRebuilder { } if !found { - return Err(Error::WrongChunkFormat("Inconsistent chunk ordering.".into()).into()); + return Err(SnapshotError::WrongChunkFormat("Inconsistent chunk ordering.".into()).into()); } } diff --git a/ethcore/src/snapshot/consensus/mod.rs b/ethcore/src/snapshot/consensus/mod.rs index d6f317538ba..102acdeca2a 100644 --- a/ethcore/src/snapshot/consensus/mod.rs +++ b/ethcore/src/snapshot/consensus/mod.rs @@ -22,7 +22,8 @@ use std::sync::Arc; use blockchain::{BlockChain, BlockChainDB}; use engines::Engine; -use snapshot::{Error, ManifestData, Progress}; +use snapshot::{ManifestData, Progress}; +use types::errors::{SnapshotError, EthcoreError}; use ethereum_types::H256; @@ -51,7 +52,7 @@ pub trait SnapshotComponents: Send { chunk_sink: &mut ChunkSink, progress: &Progress, preferred_size: usize, - ) -> Result<(), Error>; + ) -> Result<(), SnapshotError>; /// Create a rebuilder, which will have chunks fed into it in aribtrary /// order and then be finalized. @@ -65,7 +66,7 @@ pub trait SnapshotComponents: Send { chain: BlockChain, db: Arc, manifest: &ManifestData, - ) -> Result, ::error::Error>; + ) -> Result, EthcoreError>; /// Minimum supported snapshot version number. fn min_supported_version(&self) -> u64; @@ -85,12 +86,12 @@ pub trait Rebuilder: Send { chunk: &[u8], engine: &dyn Engine, abort_flag: &AtomicBool, - ) -> Result<(), ::error::Error>; + ) -> Result<(), EthcoreError>; /// Finalize the restoration. Will be done after all chunks have been /// fed successfully. /// /// This should apply the necessary "glue" between chunks, /// and verify against the restored state. - fn finalize(&mut self) -> Result<(), ::error::Error>; + fn finalize(&mut self) -> Result<(), EthcoreError>; } diff --git a/ethcore/src/snapshot/consensus/work.rs b/ethcore/src/snapshot/consensus/work.rs index eda200d6671..f286006a921 100644 --- a/ethcore/src/snapshot/consensus/work.rs +++ b/ethcore/src/snapshot/consensus/work.rs @@ -28,14 +28,17 @@ use std::sync::Arc; use blockchain::{BlockChain, BlockChainDB, BlockProvider}; use engines::Engine; -use snapshot::{Error, ManifestData, Progress}; +use snapshot::{ManifestData, Progress}; use snapshot::block::AbridgedBlock; use ethereum_types::H256; use kvdb::KeyValueDB; use bytes::Bytes; use rlp::{RlpStream, Rlp}; use rand::rngs::OsRng; -use types::encoded; +use types::{ + encoded, + errors::{SnapshotError, EthcoreError}, +}; /// Snapshot creation and restoration for PoW chains. /// This includes blocks from the head of the chain as a @@ -52,10 +55,7 @@ pub struct PowSnapshot { impl PowSnapshot { /// Create a new instance. pub fn new(blocks: u64, max_restore_blocks: u64) -> PowSnapshot { - PowSnapshot { - blocks: blocks, - max_restore_blocks: max_restore_blocks, - } + PowSnapshot { blocks, max_restore_blocks } } } @@ -67,14 +67,14 @@ impl SnapshotComponents for PowSnapshot { chunk_sink: &mut ChunkSink, progress: &Progress, preferred_size: usize, - ) -> Result<(), Error> { + ) -> Result<(), SnapshotError> { PowWorker { - chain: chain, + chain, rlps: VecDeque::new(), current_hash: block_at, writer: chunk_sink, - progress: progress, - preferred_size: preferred_size, + progress, + preferred_size, }.chunk_all(self.blocks) } @@ -83,7 +83,7 @@ impl SnapshotComponents for PowSnapshot { chain: BlockChain, db: Arc, manifest: &ManifestData, - ) -> Result, ::error::Error> { + ) -> Result, EthcoreError> { PowRebuilder::new(chain, db.key_value().clone(), manifest, self.max_restore_blocks).map(|r| Box::new(r) as Box<_>) } @@ -105,7 +105,7 @@ struct PowWorker<'a> { impl<'a> PowWorker<'a> { // Repeatedly fill the buffers and writes out chunks, moving backwards from starting block hash. // Loops until we reach the first desired block, and writes out the remainder. - fn chunk_all(&mut self, snapshot_blocks: u64) -> Result<(), Error> { + fn chunk_all(&mut self, snapshot_blocks: u64) -> Result<(), SnapshotError> { let mut loaded_size = 0; let mut last = self.current_hash; @@ -116,7 +116,7 @@ impl<'a> PowWorker<'a> { let (block, receipts) = self.chain.block(&self.current_hash) .and_then(|b| self.chain.block_receipts(&self.current_hash).map(|r| (b, r))) - .ok_or_else(|| Error::BlockNotFound(self.current_hash))?; + .ok_or_else(||SnapshotError::BlockNotFound(self.current_hash))?; let abridged_rlp = AbridgedBlock::from_block_view(&block.view()).into_inner(); @@ -155,12 +155,12 @@ impl<'a> PowWorker<'a> { // // we preface each chunk with the parent of the first block's details, // obtained from the details of the last block written. - fn write_chunk(&mut self, last: H256) -> Result<(), Error> { + fn write_chunk(&mut self, last: H256) -> Result<(), SnapshotError> { trace!(target: "snapshot", "prepared block chunk with {} blocks", self.rlps.len()); let (last_header, last_details) = self.chain.block_header_data(&last) .and_then(|n| self.chain.block_details(&last).map(|d| (n, d))) - .ok_or_else(|| Error::BlockNotFound(last))?; + .ok_or_else(||SnapshotError::BlockNotFound(last))?; let parent_number = last_header.number() - 1; let parent_hash = last_header.parent_hash(); @@ -206,7 +206,7 @@ pub struct PowRebuilder { impl PowRebuilder { /// Create a new PowRebuilder. - fn new(chain: BlockChain, db: Arc, manifest: &ManifestData, snapshot_blocks: u64) -> Result { + fn new(chain: BlockChain, db: Arc, manifest: &ManifestData, snapshot_blocks: u64) -> Result { Ok(PowRebuilder { chain, db, @@ -224,7 +224,7 @@ impl PowRebuilder { impl Rebuilder for PowRebuilder { /// Feed the rebuilder an uncompressed block chunk. /// Returns the number of blocks fed or any errors. - fn feed(&mut self, chunk: &[u8], engine: &dyn Engine, abort_flag: &AtomicBool) -> Result<(), ::error::Error> { + fn feed(&mut self, chunk: &[u8], engine: &dyn Engine, abort_flag: &AtomicBool) -> Result<(), EthcoreError> { use snapshot::verify_old_block; use ethereum_types::U256; use triehash::ordered_trie_root; @@ -236,7 +236,7 @@ impl Rebuilder for PowRebuilder { trace!(target: "snapshot", "restoring block chunk with {} blocks.", num_blocks); if self.fed_blocks + num_blocks > self.snapshot_blocks { - return Err(Error::TooManyBlocks(self.snapshot_blocks, self.fed_blocks + num_blocks).into()) + return Err(SnapshotError::TooManyBlocks(self.snapshot_blocks, self.fed_blocks + num_blocks).into()) } // todo: assert here that these values are consistent with chunks being in order. @@ -245,7 +245,7 @@ impl Rebuilder for PowRebuilder { let parent_total_difficulty = rlp.val_at::(2)?; for idx in 3..item_count { - if !abort_flag.load(Ordering::SeqCst) { return Err(Error::RestorationAborted.into()) } + if !abort_flag.load(Ordering::SeqCst) { return Err(SnapshotError::RestorationAborted.into()) } let pair = rlp.at(idx)?; let abridged_rlp = pair.at(0)?.as_raw().to_owned(); @@ -259,11 +259,11 @@ impl Rebuilder for PowRebuilder { if is_best { if block.header.hash() != self.best_hash { - return Err(Error::WrongBlockHash(cur_number, self.best_hash, block.header.hash()).into()) + return Err(SnapshotError::WrongBlockHash(cur_number, self.best_hash, block.header.hash()).into()) } if block.header.state_root() != &self.best_root { - return Err(Error::WrongStateRoot(self.best_root, *block.header.state_root()).into()) + return Err(SnapshotError::WrongStateRoot(self.best_root, *block.header.state_root()).into()) } } @@ -298,7 +298,7 @@ impl Rebuilder for PowRebuilder { } /// Glue together any disconnected chunks and check that the chain is complete. - fn finalize(&mut self) -> Result<(), ::error::Error> { + fn finalize(&mut self) -> Result<(), EthcoreError> { let mut batch = self.db.transaction(); trace!(target: "snapshot", "rebuilder, finalize: inserting {} disconnected chunks", self.disconnected.len()); for (first_num, first_hash) in self.disconnected.drain(..) { diff --git a/ethcore/src/snapshot/io.rs b/ethcore/src/snapshot/io.rs index 536862e7bee..cf1316827d2 100644 --- a/ethcore/src/snapshot/io.rs +++ b/ethcore/src/snapshot/io.rs @@ -28,6 +28,7 @@ use std::path::{Path, PathBuf}; use bytes::Bytes; use ethereum_types::H256; use rlp::{RlpStream, Rlp}; +use types::errors::{SnapshotError, EthcoreError}; use super::ManifestData; @@ -206,7 +207,7 @@ impl PackedReader { /// Create a new `PackedReader` for the file at the given path. /// This will fail if any io errors are encountered or the file /// is not a valid packed snapshot. - pub fn new(path: &Path) -> Result, ::snapshot::error::Error> { + pub fn new(path: &Path) -> Result, SnapshotError> { let mut file = File::open(path)?; let file_len = file.metadata()?.len(); if file_len < 8 { @@ -246,7 +247,7 @@ impl PackedReader { }; if version > SNAPSHOT_VERSION { - return Err(::snapshot::error::Error::VersionNotSupported(version)); + return Err(SnapshotError::VersionNotSupported(version)); } let state: Vec = rlp.list_at(0 + start)?; @@ -299,7 +300,7 @@ pub struct LooseReader { impl LooseReader { /// Create a new `LooseReader` which will read the manifest and chunk data from /// the given directory. - pub fn new(mut dir: PathBuf) -> Result { + pub fn new(mut dir: PathBuf) -> Result { let mut manifest_buf = Vec::new(); dir.push("MANIFEST"); diff --git a/ethcore/src/snapshot/mod.rs b/ethcore/src/snapshot/mod.rs index 4ded83efef1..779a8b8668e 100644 --- a/ethcore/src/snapshot/mod.rs +++ b/ethcore/src/snapshot/mod.rs @@ -28,9 +28,11 @@ use hash::{keccak, KECCAK_NULL_RLP, KECCAK_EMPTY}; use account_db::{AccountDB, AccountDBMut}; use blockchain::{BlockChain, BlockProvider}; use engines::Engine; -use types::header::Header; -use types::ids::BlockId; - +use types::{ + ids::BlockId, + header::Header, + errors::{SnapshotError as Error, EthcoreError}, +}; use ethereum_types::{H256, U256}; use hash_db::HashDB; use keccak_hasher::KeccakHasher; @@ -53,7 +55,7 @@ use account_state::Account as StateAccount; use crossbeam_utils::thread; use rand::{Rng, rngs::OsRng}; -pub use self::error::Error; +//pub use self::error::Error; // todo sort out what we need to reexport pub use self::consensus::*; pub use self::service::{SnapshotClient, Service, DatabaseRestore}; @@ -69,7 +71,6 @@ pub mod service; mod account; mod block; mod consensus; -mod error; mod watcher; #[cfg(test)] @@ -423,7 +424,7 @@ impl StateRebuilder { } /// Feed an uncompressed state chunk into the rebuilder. - pub fn feed(&mut self, chunk: &[u8], flag: &AtomicBool) -> Result<(), ::error::Error> { + pub fn feed(&mut self, chunk: &[u8], flag: &AtomicBool) -> Result<(), EthcoreError> { let rlp = Rlp::new(chunk); let empty_rlp = StateAccount::new_basic(U256::zero(), U256::zero()).rlp(); let mut pairs = Vec::with_capacity(rlp.item_count()?); @@ -486,7 +487,7 @@ impl StateRebuilder { /// Finalize the restoration. Check for accounts missing code and make a dummy /// journal entry. /// Once all chunks have been fed, there should be nothing missing. - pub fn finalize(mut self, era: u64, id: H256) -> Result, ::error::Error> { + pub fn finalize(mut self, era: u64, id: H256) -> Result, EthcoreError> { let missing = self.missing_code.keys().cloned().collect::>(); if !missing.is_empty() { return Err(Error::MissingCode(missing).into()) } @@ -517,7 +518,7 @@ fn rebuild_accounts( known_code: &HashMap, known_storage_roots: &mut HashMap, abort_flag: &AtomicBool, -) -> Result { +) -> Result { let mut status = RebuiltStatus::default(); for (account_rlp, out) in account_fat_rlps.into_iter().zip(out_chunk.iter_mut()) { if !abort_flag.load(Ordering::SeqCst) { return Err(Error::RestorationAborted.into()) } @@ -578,13 +579,13 @@ const POW_VERIFY_RATE: f32 = 0.02; /// Verify an old block with the given header, engine, blockchain, body. If `always` is set, it will perform /// the fullest verification possible. If not, it will take a random sample to determine whether it will /// do heavy or light verification. -pub fn verify_old_block(rng: &mut OsRng, header: &Header, engine: &dyn Engine, chain: &BlockChain, always: bool) -> Result<(), ::error::Error> { - engine.verify_block_basic(header)?; +pub fn verify_old_block(rng: &mut OsRng, header: &Header, engine: &dyn Engine, chain: &BlockChain, always: bool) -> Result<(), EthcoreError> { + engine.verify_block_basic(header).map_err(Into::into)?; if always || rng.gen::() <= POW_VERIFY_RATE { - engine.verify_block_unordered(header)?; + engine.verify_block_unordered(header).map_err(Into::into)?; match chain.block_header_data(header.parent_hash()) { - Some(parent) => engine.verify_block_family(header, &parent.decode()?), + Some(parent) => engine.verify_block_family(header, &parent.decode()?).map_err(Into::into), None => Ok(()), } } else { diff --git a/ethcore/src/snapshot/service.rs b/ethcore/src/snapshot/service.rs index 5e1efe13824..3e9823e3ff0 100644 --- a/ethcore/src/snapshot/service.rs +++ b/ethcore/src/snapshot/service.rs @@ -28,12 +28,15 @@ use super::{ManifestData, StateRebuilder, Rebuilder, RestorationStatus, Snapshot use super::io::{SnapshotReader, LooseReader, SnapshotWriter, LooseWriter}; use blockchain::{BlockChain, BlockChainDB, BlockChainDBHandler}; -use client::{BlockInfo, BlockChainClient, Client, ChainInfo, ClientIoMessage}; +use client_traits::BlockInfo; +use client::{BlockChainClient, Client, ChainInfo}; use engines::Engine; -use error::Error; -use snapshot::{Error as SnapshotError}; use hash::keccak; -use types::ids::BlockId; +use types::{ + client_io_message::ClientIoMessage, + errors::{EthcoreError as Error, SnapshotError, SnapshotError::UnlinkedAncientBlockChain}, + ids::BlockId, +}; use io::IoChannel; @@ -43,7 +46,6 @@ use bytes::Bytes; use journaldb::Algorithm; use kvdb::DBTransaction; use snappy; -use snapshot::error::Error::UnlinkedAncientBlockChain; /// Helper for removing directories in case of error. struct Guard(bool, PathBuf); diff --git a/ethcore/src/snapshot/tests/proof_of_work.rs b/ethcore/src/snapshot/tests/proof_of_work.rs index 4aa444229a2..9be48ab06fa 100644 --- a/ethcore/src/snapshot/tests/proof_of_work.rs +++ b/ethcore/src/snapshot/tests/proof_of_work.rs @@ -18,7 +18,7 @@ use std::sync::atomic::AtomicBool; use tempdir::TempDir; -use error::Error; +use types::errors::EthcoreError as Error; use blockchain::generator::{BlockGenerator, BlockBuilder}; use blockchain::{BlockChain, ExtrasInsert}; diff --git a/ethcore/src/snapshot/tests/service.rs b/ethcore/src/snapshot/tests/service.rs index 515e5992ffa..dd1e905dbb8 100644 --- a/ethcore/src/snapshot/tests/service.rs +++ b/ethcore/src/snapshot/tests/service.rs @@ -21,7 +21,8 @@ use std::sync::Arc; use tempdir::TempDir; use blockchain::BlockProvider; -use client::{Client, ClientConfig, ImportBlock, BlockInfo}; +use client::{Client, ClientConfig, ImportBlock}; +use client_traits::BlockInfo; use types::ids::BlockId; use snapshot::io::{PackedReader, PackedWriter, SnapshotReader, SnapshotWriter}; use snapshot::service::{Service, ServiceParams}; diff --git a/ethcore/src/snapshot/tests/state.rs b/ethcore/src/snapshot/tests/state.rs index b62775c547e..d78f33d8d52 100644 --- a/ethcore/src/snapshot/tests/state.rs +++ b/ethcore/src/snapshot/tests/state.rs @@ -20,14 +20,14 @@ use std::sync::Arc; use std::sync::atomic::AtomicBool; use hash::{KECCAK_NULL_RLP, keccak}; -use types::basic_account::BasicAccount; +use types::{ + basic_account::BasicAccount, + errors::EthcoreError as Error, +}; use snapshot::account; use snapshot::{chunk_state, Error as SnapshotError, Progress, StateRebuilder, SNAPSHOT_SUBPARTS}; use snapshot::io::{PackedReader, PackedWriter, SnapshotReader, SnapshotWriter}; use super::helpers::StateProducer; - -use error::Error; - use rand::SeedableRng; use rand_xorshift::XorShiftRng; use ethereum_types::H256; diff --git a/ethcore/src/snapshot/watcher.rs b/ethcore/src/snapshot/watcher.rs index 06df0c9cc42..9ba7f601031 100644 --- a/ethcore/src/snapshot/watcher.rs +++ b/ethcore/src/snapshot/watcher.rs @@ -17,8 +17,12 @@ //! Watcher for snapshot-related chain events. use parking_lot::Mutex; -use client::{BlockInfo, Client, ChainNotify, NewBlocks, ClientIoMessage}; -use types::ids::BlockId; +use client::{Client, ChainNotify, NewBlocks}; +use client_traits::BlockInfo; +use types::{ + client_io_message::ClientIoMessage, + ids::BlockId +}; use io::IoChannel; use ethereum_types::H256; diff --git a/ethcore/src/spec/mod.rs b/ethcore/src/spec/mod.rs index 5d90b5fbf89..b3710a91b67 100644 --- a/ethcore/src/spec/mod.rs +++ b/ethcore/src/spec/mod.rs @@ -21,4 +21,4 @@ mod seal; mod spec; pub use self::genesis::Genesis; -pub use self::spec::{Spec, SpecHardcodedSync, SpecParams, CommonParams, OptimizeFor}; +pub use self::spec::{Spec, SpecHardcodedSync, SpecParams, OptimizeFor}; diff --git a/ethcore/src/spec/spec.rs b/ethcore/src/spec/spec.rs index e31e1e25d16..8b8dc2f12d4 100644 --- a/ethcore/src/spec/spec.rs +++ b/ethcore/src/spec/spec.rs @@ -28,17 +28,20 @@ use hash::{KECCAK_NULL_RLP, keccak}; use parking_lot::RwLock; use rlp::{Rlp, RlpStream}; use rustc_hex::{FromHex, ToHex}; -use types::BlockNumber; -use types::encoded; -use types::header::Header; -use vm::{EnvInfo, CallType, ActionValue, ActionParams, ParamsType, VersionedSchedule}; +use types::{ + BlockNumber, + header::Header, + encoded, + engines::params::CommonParams, + errors::EthcoreError as Error, +}; +use vm::{EnvInfo, CallType, ActionValue, ActionParams, ParamsType}; use builtin::Builtin; use engines::{ Engine, NullEngine, InstantSeal, InstantSealParams, BasicAuthority, Clique, - AuthorityRound, DEFAULT_BLOCKHASH_CONTRACT + AuthorityRound }; -use error::Error; use executive::Executive; use trie_vm_factories::Factories; use machine::Machine; @@ -50,301 +53,11 @@ use trace::{NoopTracer, NoopVMTracer}; pub use ethash::OptimizeFor; -const MAX_TRANSACTION_SIZE: usize = 300 * 1024; - // helper for formatting errors. fn fmt_err(f: F) -> String { format!("Spec json is invalid: {}", f) } -/// Parameters common to ethereum-like blockchains. -/// NOTE: when adding bugfix hard-fork parameters, -/// add to `nonzero_bugfix_hard_fork` -/// -/// we define a "bugfix" hard fork as any hard fork which -/// you would put on-by-default in a new chain. -#[derive(Debug, PartialEq, Default)] -#[cfg_attr(test, derive(Clone))] -pub struct CommonParams { - /// Account start nonce. - pub account_start_nonce: U256, - /// Maximum size of extra data. - pub maximum_extra_data_size: usize, - /// Network id. - pub network_id: u64, - /// Chain id. - pub chain_id: u64, - /// Main subprotocol name. - pub subprotocol_name: String, - /// Minimum gas limit. - pub min_gas_limit: U256, - /// Fork block to check. - pub fork_block: Option<(BlockNumber, H256)>, - /// EIP150 transition block number. - pub eip150_transition: BlockNumber, - /// Number of first block where EIP-160 rules begin. - pub eip160_transition: BlockNumber, - /// Number of first block where EIP-161.abc begin. - pub eip161abc_transition: BlockNumber, - /// Number of first block where EIP-161.d begins. - pub eip161d_transition: BlockNumber, - /// Number of first block where EIP-98 rules begin. - pub eip98_transition: BlockNumber, - /// Number of first block where EIP-658 rules begin. - pub eip658_transition: BlockNumber, - /// Number of first block where EIP-155 rules begin. - pub eip155_transition: BlockNumber, - /// Validate block receipts root. - pub validate_receipts_transition: BlockNumber, - /// Validate transaction chain id. - pub validate_chain_id_transition: BlockNumber, - /// Number of first block where EIP-140 rules begin. - pub eip140_transition: BlockNumber, - /// Number of first block where EIP-210 rules begin. - pub eip210_transition: BlockNumber, - /// EIP-210 Blockhash contract address. - pub eip210_contract_address: Address, - /// EIP-210 Blockhash contract code. - pub eip210_contract_code: Bytes, - /// Gas allocated for EIP-210 blockhash update. - pub eip210_contract_gas: U256, - /// Number of first block where EIP-211 rules begin. - pub eip211_transition: BlockNumber, - /// Number of first block where EIP-214 rules begin. - pub eip214_transition: BlockNumber, - /// Number of first block where EIP-145 rules begin. - pub eip145_transition: BlockNumber, - /// Number of first block where EIP-1052 rules begin. - pub eip1052_transition: BlockNumber, - /// Number of first block where EIP-1283 rules begin. - pub eip1283_transition: BlockNumber, - /// Number of first block where EIP-1283 rules end. - pub eip1283_disable_transition: BlockNumber, - /// Number of first block where EIP-1014 rules begin. - pub eip1014_transition: BlockNumber, - /// Number of first block where dust cleanup rules (EIP-168 and EIP169) begin. - pub dust_protection_transition: BlockNumber, - /// Nonce cap increase per block. Nonce cap is only checked if dust protection is enabled. - pub nonce_cap_increment: u64, - /// Enable dust cleanup for contracts. - pub remove_dust_contracts: bool, - /// Wasm activation blocknumber, if any disabled initially. - pub wasm_activation_transition: BlockNumber, - /// Wasm account version, activated after `wasm_activation_transition`. If this field is defined, do not use code - /// prefix to determine VM to execute. - pub wasm_version: Option, - /// Number of first block where KIP-4 rules begin. Only has effect if Wasm is activated. - pub kip4_transition: BlockNumber, - /// Number of first block where KIP-6 rules begin. Only has effect if Wasm is activated. - pub kip6_transition: BlockNumber, - /// Gas limit bound divisor (how much gas limit can change per block) - pub gas_limit_bound_divisor: U256, - /// Registrar contract address. - pub registrar: Option
, - /// Node permission managing contract address. - pub node_permission_contract: Option
, - /// Maximum contract code size that can be deployed. - pub max_code_size: u64, - /// Number of first block where max code size limit is active. - pub max_code_size_transition: BlockNumber, - /// Transaction permission managing contract address. - pub transaction_permission_contract: Option
, - /// Block at which the transaction permission contract should start being used. - pub transaction_permission_contract_transition: BlockNumber, - /// Maximum size of transaction's RLP payload - pub max_transaction_size: usize, -} - -impl CommonParams { - /// Schedule for an EVM in the post-EIP-150-era of the Ethereum main net. - pub fn schedule(&self, block_number: u64) -> ::vm::Schedule { - if block_number < self.eip150_transition { - ::vm::Schedule::new_homestead() - } else { - let max_code_size = self.max_code_size(block_number); - let mut schedule = ::vm::Schedule::new_post_eip150( - max_code_size as _, - block_number >= self.eip160_transition, - block_number >= self.eip161abc_transition, - block_number >= self.eip161d_transition - ); - - self.update_schedule(block_number, &mut schedule); - schedule - } - } - - /// Returns max code size at given block. - pub fn max_code_size(&self, block_number: u64) -> u64 { - if block_number >= self.max_code_size_transition { - self.max_code_size - } else { - u64::max_value() - } - } - - /// Apply common spec config parameters to the schedule. - pub fn update_schedule(&self, block_number: u64, schedule: &mut ::vm::Schedule) { - schedule.have_create2 = block_number >= self.eip1014_transition; - schedule.have_revert = block_number >= self.eip140_transition; - schedule.have_static_call = block_number >= self.eip214_transition; - schedule.have_return_data = block_number >= self.eip211_transition; - schedule.have_bitwise_shifting = block_number >= self.eip145_transition; - schedule.have_extcodehash = block_number >= self.eip1052_transition; - schedule.eip1283 = block_number >= self.eip1283_transition && !(block_number >= self.eip1283_disable_transition); - if block_number >= self.eip210_transition { - schedule.blockhash_gas = 800; - } - if block_number >= self.dust_protection_transition { - schedule.kill_dust = match self.remove_dust_contracts { - true => ::vm::CleanDustMode::WithCodeAndStorage, - false => ::vm::CleanDustMode::BasicOnly, - }; - } - if block_number >= self.wasm_activation_transition { - let mut wasm = ::vm::WasmCosts::default(); - if block_number >= self.kip4_transition { - wasm.have_create2 = true; - } - if block_number >= self.kip6_transition { - wasm.have_gasleft = true; - } - schedule.wasm = Some(wasm); - if let Some(version) = self.wasm_version { - schedule.versions.insert(version, VersionedSchedule::PWasm); - } - } - } - - /// Return Some if the current parameters contain a bugfix hard fork not on block 0. - pub fn nonzero_bugfix_hard_fork(&self) -> Option<&str> { - if self.eip155_transition != 0 { - return Some("eip155Transition"); - } - - if self.validate_receipts_transition != 0 { - return Some("validateReceiptsTransition"); - } - - if self.validate_chain_id_transition != 0 { - return Some("validateChainIdTransition"); - } - - None - } -} - -impl From for CommonParams { - fn from(p: ethjson::spec::Params) -> Self { - CommonParams { - account_start_nonce: p.account_start_nonce.map_or_else(U256::zero, Into::into), - maximum_extra_data_size: p.maximum_extra_data_size.into(), - network_id: p.network_id.into(), - chain_id: if let Some(n) = p.chain_id { - n.into() - } else { - p.network_id.into() - }, - subprotocol_name: p.subprotocol_name.unwrap_or_else(|| "eth".to_owned()), - min_gas_limit: p.min_gas_limit.into(), - fork_block: if let (Some(n), Some(h)) = (p.fork_block, p.fork_hash) { - Some((n.into(), h.into())) - } else { - None - }, - eip150_transition: p.eip150_transition.map_or(0, Into::into), - eip160_transition: p.eip160_transition.map_or(0, Into::into), - eip161abc_transition: p.eip161abc_transition.map_or(0, Into::into), - eip161d_transition: p.eip161d_transition.map_or(0, Into::into), - eip98_transition: p.eip98_transition.map_or_else( - BlockNumber::max_value, - Into::into, - ), - eip155_transition: p.eip155_transition.map_or(0, Into::into), - validate_receipts_transition: p.validate_receipts_transition.map_or(0, Into::into), - validate_chain_id_transition: p.validate_chain_id_transition.map_or(0, Into::into), - eip140_transition: p.eip140_transition.map_or_else( - BlockNumber::max_value, - Into::into, - ), - eip210_transition: p.eip210_transition.map_or_else( - BlockNumber::max_value, - Into::into, - ), - eip210_contract_address: p.eip210_contract_address.map_or(Address::from_low_u64_be(0xf0), Into::into), - eip210_contract_code: p.eip210_contract_code.map_or_else( - || { - DEFAULT_BLOCKHASH_CONTRACT.from_hex().expect( - "Default BLOCKHASH contract is valid", - ) - }, - Into::into, - ), - eip210_contract_gas: p.eip210_contract_gas.map_or(1000000.into(), Into::into), - eip211_transition: p.eip211_transition.map_or_else( - BlockNumber::max_value, - Into::into, - ), - eip145_transition: p.eip145_transition.map_or_else( - BlockNumber::max_value, - Into::into, - ), - eip214_transition: p.eip214_transition.map_or_else( - BlockNumber::max_value, - Into::into, - ), - eip658_transition: p.eip658_transition.map_or_else( - BlockNumber::max_value, - Into::into, - ), - eip1052_transition: p.eip1052_transition.map_or_else( - BlockNumber::max_value, - Into::into, - ), - eip1283_transition: p.eip1283_transition.map_or_else( - BlockNumber::max_value, - Into::into, - ), - eip1283_disable_transition: p.eip1283_disable_transition.map_or_else( - BlockNumber::max_value, - Into::into, - ), - eip1014_transition: p.eip1014_transition.map_or_else( - BlockNumber::max_value, - Into::into, - ), - dust_protection_transition: p.dust_protection_transition.map_or_else( - BlockNumber::max_value, - Into::into, - ), - nonce_cap_increment: p.nonce_cap_increment.map_or(64, Into::into), - remove_dust_contracts: p.remove_dust_contracts.unwrap_or(false), - gas_limit_bound_divisor: p.gas_limit_bound_divisor.into(), - registrar: p.registrar.map(Into::into), - node_permission_contract: p.node_permission_contract.map(Into::into), - max_code_size: p.max_code_size.map_or(u64::max_value(), Into::into), - max_transaction_size: p.max_transaction_size.map_or(MAX_TRANSACTION_SIZE, Into::into), - max_code_size_transition: p.max_code_size_transition.map_or(0, Into::into), - transaction_permission_contract: p.transaction_permission_contract.map(Into::into), - transaction_permission_contract_transition: - p.transaction_permission_contract_transition.map_or(0, Into::into), - wasm_activation_transition: p.wasm_activation_transition.map_or_else( - BlockNumber::max_value, - Into::into - ), - wasm_version: p.wasm_version.map(Into::into), - kip4_transition: p.kip4_transition.map_or_else( - BlockNumber::max_value, - Into::into - ), - kip6_transition: p.kip6_transition.map_or_else( - BlockNumber::max_value, - Into::into - ), - } - } -} - /// Runtime parameters for the spec that are related to how the software should run the chain, /// rather than integral properties of the chain itself. #[derive(Debug, Clone, Copy)] diff --git a/ethcore/src/tests/client.rs b/ethcore/src/tests/client.rs index 9063df3aef1..297ba2dc9ad 100644 --- a/ethcore/src/tests/client.rs +++ b/ethcore/src/tests/client.rs @@ -27,7 +27,8 @@ use types::filter::Filter; use types::view; use types::views::BlockView; -use client::{BlockChainClient, BlockChainReset, Client, ClientConfig, BlockId, ChainInfo, BlockInfo, PrepareOpenBlock, ImportSealedBlock, ImportBlock}; +use client::{BlockChainClient, BlockChainReset, Client, ClientConfig, BlockId, ChainInfo, PrepareOpenBlock, ImportSealedBlock, ImportBlock}; +use client_traits::BlockInfo; use ethereum; use executive::{Executive, TransactOptions}; use miner::{Miner, PendingOrdering, MinerService}; diff --git a/ethcore/src/tx_filter.rs b/ethcore/src/tx_filter.rs index 2b9a7c94198..65a25fe9fb1 100644 --- a/ethcore/src/tx_filter.rs +++ b/ethcore/src/tx_filter.rs @@ -21,11 +21,14 @@ use lru_cache::LruCache; use ethabi::FunctionOutputDecoder; use call_contract::CallContract; -use client::{BlockInfo, BlockId}; +use client::BlockId; +use client_traits::BlockInfo; use parking_lot::Mutex; -use spec::CommonParams; -use types::transaction::{Action, SignedTransaction}; -use types::BlockNumber; +use types::{ + BlockNumber, + engines::params::CommonParams, + transaction::{Action, SignedTransaction} +}; use hash::KECCAK_EMPTY; use_contract!(transact_acl_deprecated, "res/contracts/tx_acl_deprecated.json"); diff --git a/ethcore/src/verification/canon_verifier.rs b/ethcore/src/verification/canon_verifier.rs index 230521c305b..2f2f397f071 100644 --- a/ethcore/src/verification/canon_verifier.rs +++ b/ethcore/src/verification/canon_verifier.rs @@ -17,7 +17,7 @@ //! Canonical verifier. use call_contract::CallContract; -use client::BlockInfo; +use client_traits::BlockInfo; use engines::Engine; use error::Error; use types::header::Header; diff --git a/ethcore/src/verification/mod.rs b/ethcore/src/verification/mod.rs index 37c721ab1b8..b95ef1be2ec 100644 --- a/ethcore/src/verification/mod.rs +++ b/ethcore/src/verification/mod.rs @@ -29,7 +29,7 @@ pub use self::noop_verifier::NoopVerifier; pub use self::queue::{BlockQueue, Config as QueueConfig, VerificationQueue, QueueInfo}; use call_contract::CallContract; -use client::BlockInfo; +use client_traits::BlockInfo; /// Verifier type. #[derive(Debug, PartialEq, Clone)] diff --git a/ethcore/src/verification/noop_verifier.rs b/ethcore/src/verification/noop_verifier.rs index f503caa2f16..3af8ae9c968 100644 --- a/ethcore/src/verification/noop_verifier.rs +++ b/ethcore/src/verification/noop_verifier.rs @@ -17,7 +17,7 @@ //! No-op verifier. use call_contract::CallContract; -use client::BlockInfo; +use client_traits::BlockInfo; use engines::Engine; use error::Error; use types::header::Header; diff --git a/ethcore/src/verification/queue/kind.rs b/ethcore/src/verification/queue/kind.rs index 0ac0b4f34fc..8607b3ac4ad 100644 --- a/ethcore/src/verification/queue/kind.rs +++ b/ethcore/src/verification/queue/kind.rs @@ -69,10 +69,13 @@ pub mod blocks { use super::{Kind, BlockLike}; use engines::Engine; - use error::{Error, BlockError}; - use types::header::Header; - use verification::{PreverifiedBlock, verify_block_basic, verify_block_unordered}; - use types::transaction::UnverifiedTransaction; + use error::Error; + use types::{ + block::{PreverifiedBlock, BlockError}, + header::Header, + transaction::UnverifiedTransaction + }; + use verification::{verify_block_basic, verify_block_unordered}; use parity_util_mem::MallocSizeOf; use ethereum_types::{H256, U256}; diff --git a/ethcore/src/verification/queue/mod.rs b/ethcore/src/verification/queue/mod.rs index 21e9f92ef90..b057e4eeb1e 100644 --- a/ethcore/src/verification/queue/mod.rs +++ b/ethcore/src/verification/queue/mod.rs @@ -26,14 +26,17 @@ use parity_util_mem::{MallocSizeOf, MallocSizeOfExt}; use ethereum_types::{H256, U256}; use parking_lot::{Condvar, Mutex, RwLock}; use io::*; -use error::{BlockError, ImportError, Error}; +use error::{BlockError, Error}; use engines::Engine; use client::ClientIoMessage; use len_caching_lock::LenCachingMutex; use self::kind::{BlockLike, Kind}; -pub use types::verification_queue_info::VerificationQueueInfo as QueueInfo; +pub use types::{ + block::ImportError, + verification_queue_info::VerificationQueueInfo as QueueInfo, +}; pub mod kind; diff --git a/ethcore/src/verification/verification.rs b/ethcore/src/verification/verification.rs index 664926374f4..d1930246169 100644 --- a/ethcore/src/verification/verification.rs +++ b/ethcore/src/verification/verification.rs @@ -24,37 +24,25 @@ use std::collections::HashSet; use std::time::{Duration, SystemTime, UNIX_EPOCH}; -use bytes::Bytes; use hash::keccak; -use parity_util_mem::MallocSizeOf; use rlp::Rlp; use triehash::ordered_trie_root; use unexpected::{Mismatch, OutOfBounds}; use blockchain::*; use call_contract::CallContract; -use client::BlockInfo; +use client_traits::BlockInfo; use engines::{Engine, MAX_UNCLE_AGE}; -use error::{BlockError, Error}; -use types::{BlockNumber, header::Header}; -use types::transaction::SignedTransaction; +use error::Error; +use types::{ + BlockNumber, + header::Header, + block::{BlockError, PreverifiedBlock} +}; use verification::queue::kind::blocks::Unverified; use time_utils::CheckedSystemTime; -/// Preprocessed block data gathered in `verify_block_unordered` call -#[derive(MallocSizeOf)] -pub struct PreverifiedBlock { - /// Populated block header - pub header: Header, - /// Populated block transactions - pub transactions: Vec, - /// Populated block uncles - pub uncles: Vec
, - /// Block bytes - pub bytes: Bytes, -} - /// Phase 1 quick block verification. Only does checks that are cheap. Operates on a single block pub fn verify_block_basic(block: &Unverified, engine: &dyn Engine, check_seal: bool) -> Result<(), Error> { verify_header_params(&block.header, engine, true, check_seal)?; @@ -379,15 +367,19 @@ mod tests { use std::time::{SystemTime, UNIX_EPOCH}; use ethereum_types::{H256, BloomRef, U256, Address}; use blockchain::{BlockDetails, TransactionAddress, BlockReceipts}; - use types::encoded; + use bytes::Bytes; use hash::keccak; use engines::Engine; use error::BlockError::*; use ethkey::{Random, Generator}; - use spec::{CommonParams, Spec}; + use spec::Spec; use test_helpers::{create_test_block_with_data, create_test_block}; - use types::transaction::{SignedTransaction, Transaction, UnverifiedTransaction, Action}; - use types::log_entry::{LogEntry, LocalizedLogEntry}; + use types::{ + encoded, + engines::params::CommonParams, + transaction::{SignedTransaction, Transaction, UnverifiedTransaction, Action}, + log_entry::{LogEntry, LocalizedLogEntry}, + }; use rlp; use triehash::ordered_trie_root; diff --git a/ethcore/src/verification/verifier.rs b/ethcore/src/verification/verifier.rs index b5c2782a6f4..ea5844fa4b0 100644 --- a/ethcore/src/verification/verifier.rs +++ b/ethcore/src/verification/verifier.rs @@ -17,7 +17,7 @@ //! A generic verifier trait. use call_contract::CallContract; -use client::BlockInfo; +use client_traits::BlockInfo; use engines::Engine; use error::Error; use types::header::Header; diff --git a/ethcore/state-db/Cargo.toml b/ethcore/state-db/Cargo.toml index 2742522db20..547c398dbc2 100644 --- a/ethcore/state-db/Cargo.toml +++ b/ethcore/state-db/Cargo.toml @@ -1,9 +1,9 @@ [package] -description = "State database" name = "state-db" -version = "0.1.0" +description = "State database" authors = ["Parity Technologies "] license = "GPL-3.0" +version = "0.1.0" edition = "2018" [dependencies] diff --git a/ethcore/trace/Cargo.toml b/ethcore/trace/Cargo.toml index dffc6c8bf70..fcf06624e86 100644 --- a/ethcore/trace/Cargo.toml +++ b/ethcore/trace/Cargo.toml @@ -1,8 +1,9 @@ [package] -description = "Transaction tracing" name = "trace" +description = "Transaction tracing" version = "0.1.0" authors = ["Parity Technologies "] +license = "GPL-3.0" edition = "2018" [dependencies] diff --git a/ethcore/types/Cargo.toml b/ethcore/types/Cargo.toml index 083205d717a..a7897033f15 100644 --- a/ethcore/types/Cargo.toml +++ b/ethcore/types/Cargo.toml @@ -5,15 +5,19 @@ version = "0.1.0" authors = ["Parity Technologies "] [dependencies] +derive_more = "0.15.0" +ethbloom = "0.6" ethereum-types = "0.6.0" ethjson = { path = "../../json" } -parity-util-mem = "0.1" ethkey = { path = "../../accounts/ethkey" } keccak-hash = "0.2.0" parity-bytes = "0.1" +parity-util-mem = "0.1" +parity-snappy = "0.1" +patricia-trie-ethereum = { path = "../../util/patricia-trie-ethereum" } rlp = "0.4.0" rlp_derive = { path = "../../util/rlp-derive" } +rustc-hex= "1.0" # todo: only needed for CommonParams. Remove. unexpected = { path = "../../util/unexpected" } +vm = { path = "../vm"} # todo: this is irritating -[dev-dependencies] -rustc-hex= "1.0" diff --git a/ethcore/types/src/block.rs b/ethcore/types/src/block.rs index a423fb1ed1b..7be08da984d 100644 --- a/ethcore/types/src/block.rs +++ b/ethcore/types/src/block.rs @@ -22,20 +22,23 @@ //! and can be appended to with transactions and uncles. //! //! When ready, `OpenBlock` can be closed and turned into a `ClosedBlock`. A `ClosedBlock` can -//! be reopend again by a miner under certain circumstances. On block close, state commit is +//! be re-opend again by a miner under certain circumstances. On block close, state commit is //! performed. //! //! `LockedBlock` is a version of a `ClosedBlock` that cannot be reopened. It can be sealed //! using an engine. //! -//! `ExecutedBlock` is an underlaying data structure used by all structs above to store block +//! `ExecutedBlock` is an underlying data structure used by all structs above to store block //! related info. +// todo: fix module docs + use bytes::Bytes; +use parity_util_mem::MallocSizeOf; use header::Header; use rlp::{Rlp, RlpStream, Decodable, DecoderError}; -use transaction::UnverifiedTransaction; +use transaction::{UnverifiedTransaction, SignedTransaction}; /// A block, encoded as it is on the block chain. #[derive(Default, Debug, Clone, PartialEq)] @@ -74,3 +77,16 @@ impl Decodable for Block { }) } } + +/// Preprocessed block data gathered in `verify_block_unordered` call +#[derive(MallocSizeOf)] +pub struct PreverifiedBlock { + /// Populated block header + pub header: Header, + /// Populated block transactions + pub transactions: Vec, + /// Populated block uncles + pub uncles: Vec
, + /// Block bytes + pub bytes: Bytes, +} diff --git a/ethcore/types/src/blockchain_info.rs b/ethcore/types/src/blockchain_info.rs index 42158638dae..c303775b9df 100644 --- a/ethcore/types/src/blockchain_info.rs +++ b/ethcore/types/src/blockchain_info.rs @@ -20,7 +20,7 @@ use std::fmt; use ethereum_types::{U256, H256}; use security_level::SecurityLevel; -use {BlockNumber}; +use BlockNumber; /// Information about the blockchain gathered together. #[derive(Clone, Debug)] diff --git a/ethcore/types/src/client_io_message.rs b/ethcore/types/src/client_io_message.rs new file mode 100644 index 00000000000..f403621b36f --- /dev/null +++ b/ethcore/types/src/client_io_message.rs @@ -0,0 +1,42 @@ +// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// This file is part of Parity Ethereum. + +// Parity Ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Ethereum. If not, see . + +//! Client IO message + +use ethereum_types::H256; +use bytes::Bytes; +use crate::snapshot_manifest::ManifestData; + +/// Message type for external and internal events +#[derive(Debug)] +pub enum ClientIoMessage { + /// Best Block Hash in chain has been changed + NewChainHead, + /// A block is ready + BlockVerified, + /// Begin snapshot restoration + BeginRestoration(ManifestData), + /// Feed a state chunk to the snapshot service + FeedStateChunk(H256, Bytes), + /// Feed a block chunk to the snapshot service + FeedBlockChunk(H256, Bytes), + /// Take a snapshot for the block with given number. + TakeSnapshot(u64), + // todo: hopefully we dont need this on in verifiers – gonna be tricky to wire up to ethcore + ///// Execute wrapped closure + //Execute(Callback), +} + diff --git a/ethcore/types/src/engines/mod.rs b/ethcore/types/src/engines/mod.rs index cc622bbe65b..33a6e78145b 100644 --- a/ethcore/types/src/engines/mod.rs +++ b/ethcore/types/src/engines/mod.rs @@ -16,7 +16,22 @@ //! Engine-specific types. +use ethereum_types::Address; +use ethjson; + +use crate::BlockNumber; + pub mod epoch; +pub mod params; + +/// The number of generations back that uncles can be. +// todo: hook up in ethcore +pub const MAX_UNCLE_AGE: usize = 6; + +/// Default EIP-210 contract code. +/// As defined in https://github.com/ethereum/EIPs/pull/210 +// todo: why can't I pre-hex this so we don't have to import rustc-hex? +pub const DEFAULT_BLOCKHASH_CONTRACT: &'static str = "73fffffffffffffffffffffffffffffffffffffffe33141561006a5760014303600035610100820755610100810715156100455760003561010061010083050761010001555b6201000081071515610064576000356101006201000083050761020001555b5061013e565b4360003512151561008457600060405260206040f361013d565b61010060003543031315156100a857610100600035075460605260206060f361013c565b6101006000350715156100c55762010000600035430313156100c8565b60005b156100ea576101006101006000350507610100015460805260206080f361013b565b620100006000350715156101095763010000006000354303131561010c565b60005b1561012f57610100620100006000350507610200015460a052602060a0f361013a565b600060c052602060c0f35b5b5b5b5b"; /// Fork choice. #[derive(Debug, PartialEq, Eq)] @@ -26,3 +41,27 @@ pub enum ForkChoice { /// Choose the current best block. Old, } + +/// Ethash-specific extensions. +#[derive(Debug, Clone)] +pub struct EthashExtensions { + /// Homestead transition block number. + pub homestead_transition: BlockNumber, + /// DAO hard-fork transition block (X). + pub dao_hardfork_transition: u64, + /// DAO hard-fork refund contract address (C). + pub dao_hardfork_beneficiary: Address, + /// DAO hard-fork DAO accounts list (L) + pub dao_hardfork_accounts: Vec
, +} + +impl From for EthashExtensions { + fn from(p: ::ethjson::spec::EthashParams) -> Self { + EthashExtensions { + homestead_transition: p.homestead_transition.map_or(0, Into::into), + dao_hardfork_transition: p.dao_hardfork_transition.map_or(u64::max_value(), Into::into), + dao_hardfork_beneficiary: p.dao_hardfork_beneficiary.map_or_else(Address::zero, Into::into), + dao_hardfork_accounts: p.dao_hardfork_accounts.unwrap_or_else(Vec::new).into_iter().map(Into::into).collect(), + } + } +} diff --git a/ethcore/types/src/engines/params.rs b/ethcore/types/src/engines/params.rs new file mode 100644 index 00000000000..919393824c0 --- /dev/null +++ b/ethcore/types/src/engines/params.rs @@ -0,0 +1,316 @@ +// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// This file is part of Parity Ethereum. + +// Parity Ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Ethereum. If not, see . + +//! Engine-specific parameter types. + +use ethereum_types::{Address, U256, H256}; +use bytes::Bytes; +use ethjson; +// todo: used only to `.from_hex()` a const which is dumb. Remove. +use rustc_hex::FromHex; + +use BlockNumber; +use engines::DEFAULT_BLOCKHASH_CONTRACT; + +const MAX_TRANSACTION_SIZE: usize = 300 * 1024; + +/// Parameters common to ethereum-like blockchains. +/// NOTE: when adding bugfix hard-fork parameters, +/// add to `nonzero_bugfix_hard_fork` +/// +/// we define a "bugfix" hard fork as any hard fork which +/// you would put on-by-default in a new chain. +#[derive(Debug, PartialEq, Default, Clone)] +//#[cfg_attr(test, derive(Clone))] // todo: this will not work across crate boundaries I think +pub struct CommonParams { + /// Account start nonce. + pub account_start_nonce: U256, + /// Maximum size of extra data. + pub maximum_extra_data_size: usize, + /// Network id. + pub network_id: u64, + /// Chain id. + pub chain_id: u64, + /// Main subprotocol name. + pub subprotocol_name: String, + /// Minimum gas limit. + pub min_gas_limit: U256, + /// Fork block to check. + pub fork_block: Option<(BlockNumber, H256)>, + /// EIP150 transition block number. + pub eip150_transition: BlockNumber, + /// Number of first block where EIP-160 rules begin. + pub eip160_transition: BlockNumber, + /// Number of first block where EIP-161.abc begin. + pub eip161abc_transition: BlockNumber, + /// Number of first block where EIP-161.d begins. + pub eip161d_transition: BlockNumber, + /// Number of first block where EIP-98 rules begin. + pub eip98_transition: BlockNumber, + /// Number of first block where EIP-658 rules begin. + pub eip658_transition: BlockNumber, + /// Number of first block where EIP-155 rules begin. + pub eip155_transition: BlockNumber, + /// Validate block receipts root. + pub validate_receipts_transition: BlockNumber, + /// Validate transaction chain id. + pub validate_chain_id_transition: BlockNumber, + /// Number of first block where EIP-140 rules begin. + pub eip140_transition: BlockNumber, + /// Number of first block where EIP-210 rules begin. + pub eip210_transition: BlockNumber, + /// EIP-210 Blockhash contract address. + pub eip210_contract_address: Address, + /// EIP-210 Blockhash contract code. + pub eip210_contract_code: Bytes, + /// Gas allocated for EIP-210 blockhash update. + pub eip210_contract_gas: U256, + /// Number of first block where EIP-211 rules begin. + pub eip211_transition: BlockNumber, + /// Number of first block where EIP-214 rules begin. + pub eip214_transition: BlockNumber, + /// Number of first block where EIP-145 rules begin. + pub eip145_transition: BlockNumber, + /// Number of first block where EIP-1052 rules begin. + pub eip1052_transition: BlockNumber, + /// Number of first block where EIP-1283 rules begin. + pub eip1283_transition: BlockNumber, + /// Number of first block where EIP-1283 rules end. + pub eip1283_disable_transition: BlockNumber, + /// Number of first block where EIP-1014 rules begin. + pub eip1014_transition: BlockNumber, + /// Number of first block where dust cleanup rules (EIP-168 and EIP169) begin. + pub dust_protection_transition: BlockNumber, + /// Nonce cap increase per block. Nonce cap is only checked if dust protection is enabled. + pub nonce_cap_increment: u64, + /// Enable dust cleanup for contracts. + pub remove_dust_contracts: bool, + /// Wasm activation blocknumber, if any disabled initially. + pub wasm_activation_transition: BlockNumber, + /// Wasm account version, activated after `wasm_activation_transition`. If this field is defined, do not use code + /// prefix to determine VM to execute. + pub wasm_version: Option, + /// Number of first block where KIP-4 rules begin. Only has effect if Wasm is activated. + pub kip4_transition: BlockNumber, + /// Number of first block where KIP-6 rules begin. Only has effect if Wasm is activated. + pub kip6_transition: BlockNumber, + /// Gas limit bound divisor (how much gas limit can change per block) + pub gas_limit_bound_divisor: U256, + /// Registrar contract address. + pub registrar: Option
, + /// Node permission managing contract address. + pub node_permission_contract: Option
, + /// Maximum contract code size that can be deployed. + pub max_code_size: u64, + /// Number of first block where max code size limit is active. + pub max_code_size_transition: BlockNumber, + /// Transaction permission managing contract address. + pub transaction_permission_contract: Option
, + /// Block at which the transaction permission contract should start being used. + pub transaction_permission_contract_transition: BlockNumber, + /// Maximum size of transaction's RLP payload + pub max_transaction_size: usize, +} + +impl CommonParams { + /// Schedule for an EVM in the post-EIP-150-era of the Ethereum main net. + pub fn schedule(&self, block_number: u64) -> vm::Schedule { + if block_number < self.eip150_transition { + vm::Schedule::new_homestead() + } else { + let max_code_size = self.max_code_size(block_number); + let mut schedule = vm::Schedule::new_post_eip150( + max_code_size as _, + block_number >= self.eip160_transition, + block_number >= self.eip161abc_transition, + block_number >= self.eip161d_transition + ); + + self.update_schedule(block_number, &mut schedule); + schedule + } + } + + /// Returns max code size at given block. + pub fn max_code_size(&self, block_number: u64) -> u64 { + if block_number >= self.max_code_size_transition { + self.max_code_size + } else { + u64::max_value() + } + } + + /// Apply common spec config parameters to the schedule. + pub fn update_schedule(&self, block_number: u64, schedule: &mut vm::Schedule) { + schedule.have_create2 = block_number >= self.eip1014_transition; + schedule.have_revert = block_number >= self.eip140_transition; + schedule.have_static_call = block_number >= self.eip214_transition; + schedule.have_return_data = block_number >= self.eip211_transition; + schedule.have_bitwise_shifting = block_number >= self.eip145_transition; + schedule.have_extcodehash = block_number >= self.eip1052_transition; + schedule.eip1283 = block_number >= self.eip1283_transition && !(block_number >= self.eip1283_disable_transition); + if block_number >= self.eip210_transition { + schedule.blockhash_gas = 800; + } + if block_number >= self.dust_protection_transition { + schedule.kill_dust = match self.remove_dust_contracts { + true => vm::CleanDustMode::WithCodeAndStorage, + false => vm::CleanDustMode::BasicOnly, + }; + } + if block_number >= self.wasm_activation_transition { + let mut wasm = vm::WasmCosts::default(); + if block_number >= self.kip4_transition { + wasm.have_create2 = true; + } + if block_number >= self.kip6_transition { + wasm.have_gasleft = true; + } + schedule.wasm = Some(wasm); + if let Some(version) = self.wasm_version { + schedule.versions.insert(version, vm::VersionedSchedule::PWasm); + } + } + } + + /// Return Some if the current parameters contain a bugfix hard fork not on block 0. + pub fn nonzero_bugfix_hard_fork(&self) -> Option<&str> { + if self.eip155_transition != 0 { + return Some("eip155Transition"); + } + + if self.validate_receipts_transition != 0 { + return Some("validateReceiptsTransition"); + } + + if self.validate_chain_id_transition != 0 { + return Some("validateChainIdTransition"); + } + + None + } +} + +impl From for CommonParams { + fn from(p: ethjson::spec::Params) -> Self { + CommonParams { + account_start_nonce: p.account_start_nonce.map_or_else(U256::zero, Into::into), + maximum_extra_data_size: p.maximum_extra_data_size.into(), + network_id: p.network_id.into(), + chain_id: if let Some(n) = p.chain_id { + n.into() + } else { + p.network_id.into() + }, + subprotocol_name: p.subprotocol_name.unwrap_or_else(|| "eth".to_owned()), + min_gas_limit: p.min_gas_limit.into(), + fork_block: if let (Some(n), Some(h)) = (p.fork_block, p.fork_hash) { + Some((n.into(), h.into())) + } else { + None + }, + eip150_transition: p.eip150_transition.map_or(0, Into::into), + eip160_transition: p.eip160_transition.map_or(0, Into::into), + eip161abc_transition: p.eip161abc_transition.map_or(0, Into::into), + eip161d_transition: p.eip161d_transition.map_or(0, Into::into), + eip98_transition: p.eip98_transition.map_or_else( + BlockNumber::max_value, + Into::into, + ), + eip155_transition: p.eip155_transition.map_or(0, Into::into), + validate_receipts_transition: p.validate_receipts_transition.map_or(0, Into::into), + validate_chain_id_transition: p.validate_chain_id_transition.map_or(0, Into::into), + eip140_transition: p.eip140_transition.map_or_else( + BlockNumber::max_value, + Into::into, + ), + eip210_transition: p.eip210_transition.map_or_else( + BlockNumber::max_value, + Into::into, + ), + eip210_contract_address: p.eip210_contract_address.map_or(Address::from_low_u64_be(0xf0), Into::into), + eip210_contract_code: p.eip210_contract_code.map_or_else( + || { + DEFAULT_BLOCKHASH_CONTRACT.from_hex().expect( + "Default BLOCKHASH contract is valid", + ) + }, + Into::into, + ), + eip210_contract_gas: p.eip210_contract_gas.map_or(1000000.into(), Into::into), + eip211_transition: p.eip211_transition.map_or_else( + BlockNumber::max_value, + Into::into, + ), + eip145_transition: p.eip145_transition.map_or_else( + BlockNumber::max_value, + Into::into, + ), + eip214_transition: p.eip214_transition.map_or_else( + BlockNumber::max_value, + Into::into, + ), + eip658_transition: p.eip658_transition.map_or_else( + BlockNumber::max_value, + Into::into, + ), + eip1052_transition: p.eip1052_transition.map_or_else( + BlockNumber::max_value, + Into::into, + ), + eip1283_transition: p.eip1283_transition.map_or_else( + BlockNumber::max_value, + Into::into, + ), + eip1283_disable_transition: p.eip1283_disable_transition.map_or_else( + BlockNumber::max_value, + Into::into, + ), + eip1014_transition: p.eip1014_transition.map_or_else( + BlockNumber::max_value, + Into::into, + ), + dust_protection_transition: p.dust_protection_transition.map_or_else( + BlockNumber::max_value, + Into::into, + ), + nonce_cap_increment: p.nonce_cap_increment.map_or(64, Into::into), + remove_dust_contracts: p.remove_dust_contracts.unwrap_or(false), + gas_limit_bound_divisor: p.gas_limit_bound_divisor.into(), + registrar: p.registrar.map(Into::into), + node_permission_contract: p.node_permission_contract.map(Into::into), + max_code_size: p.max_code_size.map_or(u64::max_value(), Into::into), + max_transaction_size: p.max_transaction_size.map_or(MAX_TRANSACTION_SIZE, Into::into), + max_code_size_transition: p.max_code_size_transition.map_or(0, Into::into), + transaction_permission_contract: p.transaction_permission_contract.map(Into::into), + transaction_permission_contract_transition: + p.transaction_permission_contract_transition.map_or(0, Into::into), + wasm_activation_transition: p.wasm_activation_transition.map_or_else( + BlockNumber::max_value, + Into::into + ), + wasm_version: p.wasm_version.map(Into::into), + kip4_transition: p.kip4_transition.map_or_else( + BlockNumber::max_value, + Into::into + ), + kip6_transition: p.kip6_transition.map_or_else( + BlockNumber::max_value, + Into::into + ), + } + } +} diff --git a/ethcore/types/src/errors/block_error.rs b/ethcore/types/src/errors/block_error.rs new file mode 100644 index 00000000000..261542f34a5 --- /dev/null +++ b/ethcore/types/src/errors/block_error.rs @@ -0,0 +1,143 @@ +use std::{ + fmt, + error, + time::SystemTime +}; + +use ethbloom::Bloom; +use ethereum_types::{H256, U256, Address}; +use unexpected::{Mismatch, OutOfBounds}; + +use BlockNumber; + +/// Errors concerning block processing. +#[derive(Debug, Display, PartialEq, Clone, Eq)] +pub enum BlockError { + /// Block has too many uncles. + #[display(fmt = "Block has too many uncles. {}", _0)] + TooManyUncles(OutOfBounds), + /// Extra data is of an invalid length. + #[display(fmt = "Extra block data too long. {}", _0)] + ExtraDataOutOfBounds(OutOfBounds), + /// Seal is incorrect format. + #[display(fmt = "Block seal in incorrect format: {}", _0)] + InvalidSealArity(Mismatch), + /// Block has too much gas used. + #[display(fmt = "Block has too much gas used. {}", _0)] + TooMuchGasUsed(OutOfBounds), + /// Uncles hash in header is invalid. + #[display(fmt = "Block has invalid uncles hash: {}", _0)] + InvalidUnclesHash(Mismatch), + /// An uncle is from a generation too old. + #[display(fmt = "Uncle block is too old. {}", _0)] + UncleTooOld(OutOfBounds), + /// An uncle is from the same generation as the block. + #[display(fmt = "Uncle from same generation as block. {}", _0)] + UncleIsBrother(OutOfBounds), + /// An uncle is already in the chain. + #[display(fmt = "Uncle {} already in chain", _0)] + UncleInChain(H256), + /// An uncle is included twice. + #[display(fmt = "Uncle {} already in the header", _0)] + DuplicateUncle(H256), + /// An uncle has a parent not in the chain. + #[display(fmt = "Uncle {} has a parent not in the chain", _0)] + UncleParentNotInChain(H256), + /// State root header field is invalid. + #[display(fmt = "Invalid state root in header: {}", _0)] + InvalidStateRoot(Mismatch), + /// Gas used header field is invalid. + #[display(fmt = "Invalid gas used in header: {}", _0)] + InvalidGasUsed(Mismatch), + /// Transactions root header field is invalid. + #[display(fmt = "Invalid transactions root in header: {}", _0)] + InvalidTransactionsRoot(Mismatch), + /// Difficulty is out of range; this can be used as an looser error prior to getting a definitive + /// value for difficulty. This error needs only provide bounds of which it is out. + #[display(fmt = "Difficulty out of bounds: {}", _0)] + DifficultyOutOfBounds(OutOfBounds), + /// Difficulty header field is invalid; this is a strong error used after getting a definitive + /// value for difficulty (which is provided). + #[display(fmt = "Invalid block difficulty: {}", _0)] + InvalidDifficulty(Mismatch), + /// Seal element of type H256 (max_hash for Ethash, but could be something else for + /// other seal engines) is out of bounds. + #[display(fmt = "Seal element out of bounds: {}", _0)] + MismatchedH256SealElement(Mismatch), + /// Proof-of-work aspect of seal, which we assume is a 256-bit value, is invalid. + #[display(fmt = "Block has invalid PoW: {}", _0)] + InvalidProofOfWork(OutOfBounds), + /// Some low-level aspect of the seal is incorrect. + #[display(fmt = "Block has invalid seal.")] + InvalidSeal, + /// Gas limit header field is invalid. + #[display(fmt = "Invalid gas limit: {}", _0)] + InvalidGasLimit(OutOfBounds), + /// Receipts trie root header field is invalid. + #[display(fmt = "Invalid receipts trie root in header: {}", _0)] + InvalidReceiptsRoot(Mismatch), + /// Timestamp header field is invalid. + #[display(fmt = "Invalid timestamp in header: {}", _0)] + InvalidTimestamp(OutOfBoundsTime), + /// Timestamp header field is too far in future. + #[display(fmt = "Future timestamp in header: {}", _0)] + TemporarilyInvalid(OutOfBoundsTime), + /// Log bloom header field is invalid. + #[display(fmt = "Invalid log bloom in header: {}", _0)] + InvalidLogBloom(Box>), + /// Number field of header is invalid. + #[display(fmt = "Invalid number in header: {}", _0)] + InvalidNumber(Mismatch), + /// Block number isn't sensible. + #[display(fmt = "Implausible block number. {}", _0)] + RidiculousNumber(OutOfBounds), + /// Timestamp header overflowed + #[display(fmt = "Timestamp overflow")] + TimestampOverflow, + /// Too many transactions from a particular address. + #[display(fmt = "Too many transactions from: {}", _0)] + TooManyTransactions(Address), + /// Parent given is unknown. + #[display(fmt = "Unknown parent: {}", _0)] + UnknownParent(H256), + /// Uncle parent given is unknown. + #[display(fmt = "Unknown uncle parent: {}", _0)] + UnknownUncleParent(H256), + /// No transition to epoch number. + #[display(fmt = "Unknown transition to epoch number: {}", _0)] + UnknownEpochTransition(u64), +} + +/// Newtype for Display impl to show seconds +#[derive(Debug, Clone, From, PartialEq, Eq)] +pub struct OutOfBoundsTime(OutOfBounds); + +impl fmt::Display for OutOfBoundsTime { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let seconds = self.0 + .map(|st| st.elapsed().unwrap_or_default().as_secs()); + f.write_fmt(format_args!("{}", seconds)) + } +} + +impl error::Error for BlockError { + fn description(&self) -> &str { + "Block error" + } +} + +/// Block import Error +#[derive(Debug, Display)] +pub enum ImportError { + /// Already in the block chain. + #[display(fmt = "Block already in chain")] + AlreadyInChain, + /// Already in the block queue + #[display(fmt = "block already in the block queue")] + AlreadyQueued, + /// Already marked as bad from a previous import (could mean parent is bad) + #[display(fmt = "block known to be bad")] + KnownBad, +} + +impl error::Error for ImportError {} diff --git a/ethcore/types/src/errors/engine_error.rs b/ethcore/types/src/errors/engine_error.rs new file mode 100644 index 00000000000..7023954a222 --- /dev/null +++ b/ethcore/types/src/errors/engine_error.rs @@ -0,0 +1,94 @@ +use std::fmt; + +use derive_more::From; +use ethereum_types::{Address, H64, H256}; +use unexpected::{Mismatch, OutOfBounds}; + +/// Voting errors. +#[derive(Debug, From)] +pub enum EngineError { + /// Signature or author field does not belong to an authority. + NotAuthorized(Address), + /// The same author issued different votes at the same step. + DoubleVote(Address), + /// The received block is from an incorrect proposer. + NotProposer(Mismatch
), + /// Message was not expected. + UnexpectedMessage, + /// Seal field has an unexpected size. + BadSealFieldSize(OutOfBounds), + /// Validation proof insufficient. + InsufficientProof(String), + /// Failed system call. + FailedSystemCall(String), + /// Malformed consensus message. + MalformedMessage(String), + /// Requires client ref, but none registered. + RequiresClient, + /// Invalid engine specification or implementation. + InvalidEngine, + /// Requires signer ref, but none registered. + RequiresSigner, + /// Missing Parent Epoch + MissingParent(H256), + /// Checkpoint is missing + CliqueMissingCheckpoint(H256), + /// Missing vanity data + CliqueMissingVanity, + /// Missing signature + CliqueMissingSignature, + /// Missing signers + CliqueCheckpointNoSigner, + /// List of signers is invalid + CliqueCheckpointInvalidSigners(usize), + /// Wrong author on a checkpoint + CliqueWrongAuthorCheckpoint(Mismatch
), + /// Wrong checkpoint authors recovered + CliqueFaultyRecoveredSigners(Vec), + /// Invalid nonce (should contain vote) + CliqueInvalidNonce(H64), + /// The signer signed a block to recently + CliqueTooRecentlySigned(Address), + /// Custom + Custom(String), +} + +impl fmt::Display for EngineError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use self::EngineError::*; + let msg = match *self { + CliqueMissingCheckpoint(ref hash) => format!("Missing checkpoint block: {}", hash), + CliqueMissingVanity => format!("Extra data is missing vanity data"), + CliqueMissingSignature => format!("Extra data is missing signature"), + CliqueCheckpointInvalidSigners(len) => format!("Checkpoint block list was of length: {} of checkpoint but + it needs to be bigger than zero and a divisible by 20", len), + CliqueCheckpointNoSigner => format!("Checkpoint block list of signers was empty"), + CliqueInvalidNonce(ref mis) => format!("Unexpected nonce {} expected {} or {}", mis, 0_u64, u64::max_value()), + CliqueWrongAuthorCheckpoint(ref oob) => format!("Unexpected checkpoint author: {}", oob), + CliqueFaultyRecoveredSigners(ref mis) => format!("Faulty recovered signers {:?}", mis), + CliqueTooRecentlySigned(ref address) => format!("The signer: {} has signed a block too recently", address), + Custom(ref s) => s.clone(), + DoubleVote(ref address) => format!("Author {} issued too many blocks.", address), + NotProposer(ref mis) => format!("Author is not a current proposer: {}", mis), + NotAuthorized(ref address) => format!("Signer {} is not authorized.", address), + UnexpectedMessage => "This Engine should not be fed messages.".into(), + BadSealFieldSize(ref oob) => format!("Seal field has an unexpected length: {}", oob), + InsufficientProof(ref msg) => format!("Insufficient validation proof: {}", msg), + FailedSystemCall(ref msg) => format!("Failed to make system call: {}", msg), + MalformedMessage(ref msg) => format!("Received malformed consensus message: {}", msg), + RequiresClient => format!("Call requires client but none registered"), + RequiresSigner => format!("Call requires signer but none registered"), + InvalidEngine => format!("Invalid engine specification or implementation"), + MissingParent(ref hash) => format!("Parent Epoch is missing from database: {}", hash), + }; + + f.write_fmt(format_args!("Engine error ({})", msg)) + } +} + +// TODO: wth is this? Use derive_more. +impl std::error::Error for EngineError { + fn description(&self) -> &str { + "Engine error" + } +} diff --git a/ethcore/types/src/errors/ethcore_error.rs b/ethcore/types/src/errors/ethcore_error.rs new file mode 100644 index 00000000000..49ad9c88325 --- /dev/null +++ b/ethcore/types/src/errors/ethcore_error.rs @@ -0,0 +1,207 @@ +// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// This file is part of Parity Ethereum. + +// Parity Ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Ethereum. If not, see . + +//! General error types for use in ethcore. + +use std::{ + error, + fmt, + //io::Error as IoError, +}; + +use derive_more::{Display, From}; +use ethereum_types::{U256, U512}; +use ethtrie::TrieError; +use parity_snappy::InvalidInput; +use ethkey::Error as EthkeyError; + +use errors::{BlockError, EngineError, ImportError, SnapshotError}; +use transaction::Error as TransactionError; + +/// Ethcore Error +#[derive(Debug, Display, From)] +pub enum EthcoreError { + /// Error concerning block import. + #[display(fmt = "Import error: {}", _0)] + Import(ImportError), + /// Io channel queue error + #[display(fmt = "Queue is full: {}", _0)] + FullQueue(usize), + /// Io create error + #[display(fmt = "Io error: {}", _0)] + Io(::std::io::Error), + /// Error concerning the Rust standard library's IO subsystem. + #[display(fmt = "Std Io error: {}", _0)] + StdIo(::std::io::Error), + /// Error concerning TrieDBs. + #[display(fmt = "Trie error: {}", _0)] + Trie(TrieError), + /// Error concerning EVM code execution. + #[display(fmt = "Execution error: {}", _0)] + Execution(ExecutionError), + /// Error concerning block processing. + #[display(fmt = "Block error: {}", _0)] + Block(BlockError), + /// Error concerning transaction processing. + #[display(fmt = "Transaction error: {}", _0)] + Transaction(TransactionError), + /// Snappy error + #[display(fmt = "Snappy error: {}", _0)] + Snappy(InvalidInput), + /// Consensus vote error. + #[display(fmt = "Engine error: {}", _0)] + Engine(EngineError), + /// Ethkey error." + #[display(fmt = "Ethkey error: {}", _0)] + Ethkey(EthkeyError), + /// RLP decoding errors + #[display(fmt = "Decoder error: {}", _0)] + Decoder(rlp::DecoderError), + /// Snapshot error. + #[display(fmt = "Snapshot error {}", _0)] + Snapshot(SnapshotError), + /// PoW hash is invalid or out of date. + #[display(fmt = "PoW hash is invalid or out of date.")] + PowHashInvalid, + /// The value of the nonce or mishash is invalid. + #[display(fmt = "The value of the nonce or mishash is invalid.")] + PowInvalid, + /// A convenient variant for String. + #[display(fmt = "{}", _0)] + Msg(String), +} + +impl error::Error for EthcoreError { + fn source(&self) -> Option<&(dyn error::Error + 'static)> { + use self::EthcoreError::*; + match self { + Io(e) => Some(e), + StdIo(e) => Some(e), + Trie(e) => Some(e), + Execution(e) => Some(e), + Block(e) => Some(e), + Transaction(e) => Some(e), + Snappy(e) => Some(e), + Engine(e) => Some(e), + Ethkey(e) => Some(e), + Decoder(e) => Some(e), + Snapshot(e) => Some(e), + _ => None, + } + } +} + +impl From<&str> for EthcoreError { + fn from(s: &str) -> Self { + EthcoreError::Msg(s.into()) + } +} + +impl From> for EthcoreError where EthcoreError: From { + fn from(err: Box) -> EthcoreError { + EthcoreError::from(*err) + } +} + +/// Error type for executing a transaction. +#[derive(PartialEq, Debug, Clone)] +pub enum ExecutionError { + /// Returned when there gas paid for transaction execution is + /// lower than base gas required. + NotEnoughBaseGas { + /// Absolute minimum gas required. + required: U256, + /// Gas provided. + got: U256 + }, + /// Returned when block (gas_used + gas) > gas_limit. + /// + /// If gas =< gas_limit, upstream may try to execute the transaction + /// in next block. + BlockGasLimitReached { + /// Gas limit of block for transaction. + gas_limit: U256, + /// Gas used in block prior to transaction. + gas_used: U256, + /// Amount of gas in block. + gas: U256 + }, + /// Returned when transaction nonce does not match state nonce. + InvalidNonce { + /// Nonce expected. + expected: U256, + /// Nonce found. + got: U256 + }, + /// Returned when cost of transaction (value + gas_price * gas) exceeds + /// current sender balance. + NotEnoughCash { + /// Minimum required balance. + required: U512, + /// Actual balance. + got: U512 + }, + /// When execution tries to modify the state in static context + MutableCallInStaticContext, + /// Returned when transacting from a non-existing account with dust protection enabled. + SenderMustExist, + /// Returned when internal evm error occurs. + Internal(String), + /// Returned when generic transaction occurs + TransactionMalformed(String), +} + +impl error::Error for ExecutionError { + fn description(&self) -> &str { + "Transaction execution error" + } +} + +impl From> for ExecutionError { + fn from(err: Box) -> Self { + ExecutionError::Internal(format!("{:?}", err)) + } +} +impl From for ExecutionError { + fn from(err: TrieError) -> Self { + ExecutionError::Internal(format!("{:?}", err)) + } +} + +impl fmt::Display for ExecutionError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use self::ExecutionError::*; + + let msg = match *self { + NotEnoughBaseGas { ref required, ref got } => + format!("Not enough base gas. {} is required, but only {} paid", required, got), + BlockGasLimitReached { ref gas_limit, ref gas_used, ref gas } => + format!("Block gas limit reached. The limit is {}, {} has \ + already been used, and {} more is required", gas_limit, gas_used, gas), + InvalidNonce { ref expected, ref got } => + format!("Invalid transaction nonce: expected {}, found {}", expected, got), + NotEnoughCash { ref required, ref got } => + format!("Cost of transaction exceeds sender balance. {} is required \ + but the sender only has {}", required, got), + MutableCallInStaticContext => "Mutable Call in static context".to_owned(), + SenderMustExist => "Transacting from an empty account".to_owned(), + Internal(ref msg) => msg.clone(), + TransactionMalformed(ref err) => format!("Malformed transaction: {}", err), + }; + + f.write_fmt(format_args!("Transaction execution error ({}).", msg)) + } +} diff --git a/ethcore/types/src/errors/mod.rs b/ethcore/types/src/errors/mod.rs new file mode 100644 index 00000000000..5c27e76fffb --- /dev/null +++ b/ethcore/types/src/errors/mod.rs @@ -0,0 +1,29 @@ +// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// This file is part of Parity Ethereum. + +// Parity Ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Ethereum. If not, see . + +//! General error types for use in parity-ethereum. + +mod block_error; +mod engine_error; +mod ethcore_error; +mod snapshot_error; + +pub use self::{ + block_error::{BlockError, ImportError}, + engine_error::EngineError, + ethcore_error::{EthcoreError, ExecutionError}, + snapshot_error::SnapshotError, +}; diff --git a/ethcore/src/snapshot/error.rs b/ethcore/types/src/errors/snapshot_error.rs similarity index 54% rename from ethcore/src/snapshot/error.rs rename to ethcore/types/src/errors/snapshot_error.rs index 68742e2e178..ae4048fd2a3 100644 --- a/ethcore/src/snapshot/error.rs +++ b/ethcore/types/src/errors/snapshot_error.rs @@ -19,15 +19,15 @@ use std::error; use std::fmt; -use types::ids::BlockId; - use ethereum_types::H256; use ethtrie::TrieError; use rlp::DecoderError; +use ids::BlockId; + /// Snapshot-related errors. #[derive(Debug)] -pub enum Error { +pub enum SnapshotError { /// Invalid starting block for snapshot. InvalidStartingBlock(BlockId), /// Block not found. @@ -72,67 +72,69 @@ pub enum Error { UnlinkedAncientBlockChain(H256), } -impl error::Error for Error { +impl error::Error for SnapshotError { fn source(&self) -> Option<&(dyn error::Error + 'static)> { + use self::SnapshotError::*; match self { - Error::Trie(e) => Some(e), - Error::Decoder(e) => Some(e), - Error::Io(e) => Some(e), + Trie(e) => Some(e), + Decoder(e) => Some(e), + Io(e) => Some(e), _ => None, } } } -impl fmt::Display for Error { +impl fmt::Display for SnapshotError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use self::SnapshotError::*; match *self { - Error::InvalidStartingBlock(ref id) => write!(f, "Invalid starting block: {:?}", id), - Error::BlockNotFound(ref hash) => write!(f, "Block not found in chain: {}", hash), - Error::IncompleteChain => write!(f, "Incomplete blockchain."), - Error::WrongStateRoot(ref expected, ref found) => write!(f, "Final block has wrong state root. Expected {:?}, got {:?}", expected, found), - Error::WrongBlockHash(ref num, ref expected, ref found) => + InvalidStartingBlock(ref id) => write!(f, "Invalid starting block: {:?}", id), + BlockNotFound(ref hash) => write!(f, "Block not found in chain: {}", hash), + IncompleteChain => write!(f, "Incomplete blockchain."), + WrongStateRoot(ref expected, ref found) => write!(f, "Final block has wrong state root. Expected {:?}, got {:?}", expected, found), + WrongBlockHash(ref num, ref expected, ref found) => write!(f, "Block {} had wrong hash. expected {:?}, got {:?}", num, expected, found), - Error::TooManyBlocks(ref expected, ref found) => write!(f, "Snapshot contained too many blocks. Expected {}, got {}", expected, found), - Error::OldBlockPrunedDB => write!(f, "Attempted to create a snapshot at an old block while using \ + TooManyBlocks(ref expected, ref found) => write!(f, "Snapshot contained too many blocks. Expected {}, got {}", expected, found), + OldBlockPrunedDB => write!(f, "Attempted to create a snapshot at an old block while using \ a pruned database. Please re-run with the --pruning archive flag."), - Error::MissingCode(ref missing) => write!(f, "Incomplete snapshot: {} contract codes not found.", missing.len()), - Error::UnrecognizedCodeState(state) => write!(f, "Unrecognized code encoding ({})", state), - Error::RestorationAborted => write!(f, "Snapshot restoration aborted."), - Error::Io(ref err) => err.fmt(f), - Error::Decoder(ref err) => err.fmt(f), - Error::Trie(ref err) => err.fmt(f), - Error::VersionNotSupported(ref ver) => write!(f, "Snapshot version {} is not supprted.", ver), - Error::ChunkTooSmall => write!(f, "Chunk size is too small."), - Error::ChunkTooLarge => write!(f, "Chunk size is too large."), - Error::SnapshotsUnsupported => write!(f, "Snapshots unsupported by consensus engine."), - Error::SnapshotAborted => write!(f, "Snapshot was aborted."), - Error::BadEpochProof(i) => write!(f, "Bad epoch proof for transition to epoch {}", i), - Error::WrongChunkFormat(ref msg) => write!(f, "Wrong chunk format: {}", msg), - Error::UnlinkedAncientBlockChain(parent_hash) => write!(f, "Unlinked ancient blocks chain at parent_hash={:#x}", parent_hash), + MissingCode(ref missing) => write!(f, "Incomplete snapshot: {} contract codes not found.", missing.len()), + UnrecognizedCodeState(state) => write!(f, "Unrecognized code encoding ({})", state), + RestorationAborted => write!(f, "Snapshot restoration aborted."), + Io(ref err) => err.fmt(f), + Decoder(ref err) => err.fmt(f), + Trie(ref err) => err.fmt(f), + VersionNotSupported(ref ver) => write!(f, "Snapshot version {} is not supprted.", ver), + ChunkTooSmall => write!(f, "Chunk size is too small."), + ChunkTooLarge => write!(f, "Chunk size is too large."), + SnapshotsUnsupported => write!(f, "Snapshots unsupported by consensus engine."), + SnapshotAborted => write!(f, "Snapshot was aborted."), + BadEpochProof(i) => write!(f, "Bad epoch proof for transition to epoch {}", i), + WrongChunkFormat(ref msg) => write!(f, "Wrong chunk format: {}", msg), + UnlinkedAncientBlockChain(parent_hash) => write!(f, "Unlinked ancient blocks chain at parent_hash={:#x}", parent_hash), } } } -impl From<::std::io::Error> for Error { +impl From<::std::io::Error> for SnapshotError { fn from(err: ::std::io::Error) -> Self { - Error::Io(err) + SnapshotError::Io(err) } } -impl From for Error { +impl From for SnapshotError { fn from(err: TrieError) -> Self { - Error::Trie(err) + SnapshotError::Trie(err) } } -impl From for Error { +impl From for SnapshotError { fn from(err: DecoderError) -> Self { - Error::Decoder(err) + SnapshotError::Decoder(err) } } -impl From> for Error where Error: From { +impl From> for SnapshotError where SnapshotError: From { fn from(err: Box) -> Self { - Error::from(*err) + SnapshotError::from(*err) } } diff --git a/ethcore/types/src/lib.rs b/ethcore/types/src/lib.rs index f8c5087bd36..d89a5055b3f 100644 --- a/ethcore/types/src/lib.rs +++ b/ethcore/types/src/lib.rs @@ -33,23 +33,25 @@ #![warn(missing_docs, unused_extern_crates)] +extern crate ethbloom; extern crate ethereum_types; extern crate ethjson; extern crate ethkey; +#[macro_use] +extern crate derive_more; extern crate keccak_hash as hash; extern crate parity_bytes as bytes; +extern crate patricia_trie_ethereum as ethtrie; extern crate rlp; +extern crate rustc_hex; // todo: used only to `.from_hex()` a const which is dumb. Remove. +extern crate parity_snappy; extern crate unexpected; #[macro_use] extern crate rlp_derive; extern crate parity_util_mem; - extern crate parity_util_mem as malloc_size_of; -#[cfg(test)] -extern crate rustc_hex; - #[macro_use] pub mod views; @@ -60,8 +62,10 @@ pub mod block; pub mod block_status; pub mod blockchain_info; pub mod call_analytics; +pub mod client_io_message; pub mod encoded; pub mod engines; +pub mod errors; pub mod filter; pub mod header; pub mod ids; diff --git a/ethcore/verification/Cargo.toml b/ethcore/verification/Cargo.toml new file mode 100644 index 00000000000..be15d87ccd5 --- /dev/null +++ b/ethcore/verification/Cargo.toml @@ -0,0 +1,27 @@ +[package] +name = "verification" +description = "Block verification utilities." +authors = ["Parity Technologies "] +license = "GPL-3.0" +version = "0.1.0" +edition = "2018" + +[dependencies] +call-contract = { path = "../call-contract", package = "ethcore-call-contract" } +common-types = { path = "../types" } +client-traits = { path = "../client-traits" } +derive_more = "0.15.0" +keccak-hash = "0.2.0" +rlp = "0.4" +triehash-ethereum = { version = "0.2", path = "../../util/triehash-ethereum" } +unexpected = { path = "../../util/unexpected" } +ethcore-blockchain = { path = "../blockchain" } +time-utils = { path = "../../util/time-utils" } +parking_lot = "0.8.0" +parity-util-mem = "0.1.0" +ethereum-types = "0.6" +ethcore-io = { path = "../../util/io" } +len-caching-lock = { path = "../../util/len-caching-lock" } +num_cpus = "1.10.1" +log = "0.4.6" +parity-bytes = "0.1.0" diff --git a/ethcore/verification/src/canon_verifier.rs b/ethcore/verification/src/canon_verifier.rs new file mode 100644 index 00000000000..31f7f57aefc --- /dev/null +++ b/ethcore/verification/src/canon_verifier.rs @@ -0,0 +1,50 @@ +// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// This file is part of Parity Ethereum. + +// Parity Ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Ethereum. If not, see . + +//! Canonical verifier. + +use client_traits::{VerifyingEngine, VerifyingClient}; +use crate::{ + Verifier, + verification::{self, FullFamilyParams}, +}; +use common_types::{ + errors::EthcoreError as Error, + header::Header, +}; + +/// A canonical verifier -- this does full verification. +pub struct CanonVerifier; + +impl Verifier for CanonVerifier { + fn verify_block_family( + &self, + header: &Header, + parent: &Header, + engine: &dyn VerifyingEngine, + do_full: Option>, + ) -> Result<(), Error> { + verification::verify_block_family(header, parent, engine, do_full) + } + + fn verify_block_final(&self, expected: &Header, got: &Header) -> Result<(), Error> { + verification::verify_block_final(expected, got) + } + + fn verify_block_external(&self, header: &Header, engine: &dyn VerifyingEngine) -> Result<(), Error> { + engine.verify_block_external(header).map_err(Into::into) + } +} diff --git a/ethcore/account-state/src/error.rs b/ethcore/verification/src/error.rs similarity index 77% rename from ethcore/account-state/src/error.rs rename to ethcore/verification/src/error.rs index 0a2911f7913..aca56a11441 100644 --- a/ethcore/account-state/src/error.rs +++ b/ethcore/verification/src/error.rs @@ -18,12 +18,20 @@ use derive_more::{Display, From}; +use client_traits::error::Error as ClientError; +use common_types::{ + block::{BlockError, ImportError}, + transaction::Error as TransactionError, +}; +use rlp::DecoderError; + #[derive(Debug, Display, From)] pub enum Error { - /// Trie error. - Trie(ethtrie::TrieError), - /// Decoder error. - Decoder(rlp::DecoderError), + Block(BlockError), + Import(ImportError), + Client(ClientError), + Transaction(TransactionError), + Decoder(DecoderError), } impl std::error::Error for Error {} diff --git a/ethcore/verification/src/lib.rs b/ethcore/verification/src/lib.rs new file mode 100644 index 00000000000..8eb0fcf0c30 --- /dev/null +++ b/ethcore/verification/src/lib.rs @@ -0,0 +1,63 @@ +// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// This file is part of Parity Ethereum. + +// Parity Ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Ethereum. If not, see . + +//! Block verification utilities. + +use parity_util_mem as malloc_size_of; + +mod verification; +mod verifier; +pub mod queue; +mod canon_verifier; +mod noop_verifier; + +pub use self::verification::*; +pub use self::verifier::Verifier; +pub use self::canon_verifier::CanonVerifier; +pub use self::noop_verifier::NoopVerifier; +pub use self::queue::{BlockQueue, Config as QueueConfig, VerificationQueue}; + +use client_traits::VerifyingClient; + +/// Verifier type. +#[derive(Debug, PartialEq, Clone)] +pub enum VerifierType { + /// Verifies block normally. + Canon, + /// Verifies block normally, but skips seal verification. + CanonNoSeal, + /// Does not verify block at all. + /// Used in tests. + Noop, +} + +/// Create a new verifier based on type. +pub fn new(v: VerifierType) -> Box> { + match v { + VerifierType::Canon | VerifierType::CanonNoSeal => Box::new(CanonVerifier), + VerifierType::Noop => Box::new(NoopVerifier), + } +} + +impl VerifierType { + /// Check if seal verification is enabled for this verifier type. + pub fn verifying_seal(&self) -> bool { + match *self { + VerifierType::Canon => true, + VerifierType::Noop | VerifierType::CanonNoSeal => false, + } + } +} diff --git a/ethcore/verification/src/noop_verifier.rs b/ethcore/verification/src/noop_verifier.rs new file mode 100644 index 00000000000..29f0f7dd03d --- /dev/null +++ b/ethcore/verification/src/noop_verifier.rs @@ -0,0 +1,51 @@ +// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// This file is part of Parity Ethereum. + +// Parity Ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Ethereum. If not, see . + +//! No-op verifier. + +use call_contract::CallContract; +use client_traits::{BlockInfo, VerifyingEngine}; +use crate::{ + Verifier, verification::FullFamilyParams +}; +use common_types::{ + errors::EthcoreError, + header::Header, +}; + +/// A no-op verifier -- this will verify everything it's given immediately. +#[allow(dead_code)] +pub struct NoopVerifier; + +impl Verifier for NoopVerifier { + fn verify_block_family( + &self, + _: &Header, + _t: &Header, + _: &dyn VerifyingEngine, + _: Option> + ) -> Result<(), EthcoreError> { + Ok(()) + } + + fn verify_block_final(&self, _expected: &Header, _got: &Header) -> Result<(), EthcoreError> { + Ok(()) + } + + fn verify_block_external(&self, _header: &Header, _engine: &dyn VerifyingEngine) -> Result<(), EthcoreError> { + Ok(()) + } +} diff --git a/ethcore/verification/src/queue/kind.rs b/ethcore/verification/src/queue/kind.rs new file mode 100644 index 00000000000..a17bdb36640 --- /dev/null +++ b/ethcore/verification/src/queue/kind.rs @@ -0,0 +1,246 @@ +// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// This file is part of Parity Ethereum. + +// Parity Ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Ethereum. If not, see . + +//! Definition of valid items for the verification queue. + +//use engines::Engine; +use client_traits::VerifyingEngine; + +use parity_util_mem::MallocSizeOf; +use ethereum_types::{H256, U256}; + +pub use self::{ + blocks::Blocks, + headers::Headers, +}; +use common_types::{ + errors::EthcoreError as Error, +}; + +/// Something which can produce a hash and a parent hash. +pub trait BlockLike { + /// Get the hash of this item. + fn hash(&self) -> H256; + + /// Get the hash of this item's parent. + fn parent_hash(&self) -> H256; + + /// Get the difficulty of this item. + fn difficulty(&self) -> U256; +} + +/// Defines transitions between stages of verification. +/// +/// It starts with a fallible transformation from an "input" into the unverified item. +/// This consists of quick, simply done checks as well as extracting particular data. +/// +/// Then, there is a `verify` function which performs more expensive checks and +/// produces the verified output. +/// +/// For correctness, the hashes produced by each stage of the pipeline should be +/// consistent. +pub trait Kind: 'static + Sized + Send + Sync { + /// The first stage: completely unverified. + type Input: Sized + Send + BlockLike + MallocSizeOf; + + /// The second stage: partially verified. + type Unverified: Sized + Send + BlockLike + MallocSizeOf; + + /// The third stage: completely verified. + type Verified: Sized + Send + BlockLike + MallocSizeOf; + + /// Attempt to create the `Unverified` item from the input. + fn create(input: Self::Input, engine: &dyn VerifyingEngine, check_seal: bool) -> Result; + + /// Attempt to verify the `Unverified` item using the given engine. + fn verify(unverified: Self::Unverified, engine: &dyn VerifyingEngine, check_seal: bool) -> Result; +} + +/// The blocks verification module. +pub mod blocks { + use super::{Kind, BlockLike}; + + use client_traits::VerifyingEngine; + use crate::{ + verification::{verify_block_basic, verify_block_unordered} + }; + use common_types::{ + block::PreverifiedBlock, + errors::{BlockError, EthcoreError as Error}, + header::Header, + transaction::UnverifiedTransaction + }; + + use parity_util_mem::MallocSizeOf; + use ethereum_types::{H256, U256}; + use parity_bytes::Bytes; + use log::{debug, warn}; + + /// A mode for verifying blocks. + pub struct Blocks; + + impl Kind for Blocks { + type Input = Unverified; + type Unverified = Unverified; + type Verified = PreverifiedBlock; + + fn create( + input: Self::Input, + engine: &dyn VerifyingEngine, + check_seal: bool + ) -> Result { + match verify_block_basic(&input, engine, check_seal) { + Ok(()) => Ok(input), + Err(Error::Block(BlockError::TemporarilyInvalid(oob))) => { + debug!(target: "client", "Block received too early {}: {:?}", input.hash(), oob); + Err((input, BlockError::TemporarilyInvalid(oob).into())) + }, + Err(e) => { + warn!(target: "client", "Stage 1 block verification failed for {}: {:?}", input.hash(), e); + Err((input, e)) + } + } + } + + fn verify( + un: Self::Unverified, + engine: &dyn VerifyingEngine, + check_seal: bool + ) -> Result { + let hash = un.hash(); + match verify_block_unordered(un, engine, check_seal) { + Ok(verified) => Ok(verified), + Err(e) => { + warn!(target: "client", "Stage 2 block verification failed for {}: {:?}", hash, e); + Err(e) + } + } + } + } + + /// An unverified block. + #[derive(PartialEq, Debug, MallocSizeOf)] + pub struct Unverified { + /// Unverified block header. + pub header: Header, + /// Unverified block transactions. + pub transactions: Vec, + /// Unverified block uncles. + pub uncles: Vec
, + /// Raw block bytes. + pub bytes: Bytes, + } + + impl Unverified { + /// Create an `Unverified` from raw bytes. + pub fn from_rlp(bytes: Bytes) -> Result { + use rlp::Rlp; + let (header, transactions, uncles) = { + let rlp = Rlp::new(&bytes); + let header = rlp.val_at(0)?; + let transactions = rlp.list_at(1)?; + let uncles = rlp.list_at(2)?; + (header, transactions, uncles) + }; + + Ok(Unverified { + header, + transactions, + uncles, + bytes, + }) + } + } + + impl BlockLike for Unverified { + fn hash(&self) -> H256 { + self.header.hash() + } + + fn parent_hash(&self) -> H256 { + self.header.parent_hash().clone() + } + + fn difficulty(&self) -> U256 { + self.header.difficulty().clone() + } + } + + impl BlockLike for PreverifiedBlock { + fn hash(&self) -> H256 { + self.header.hash() + } + + fn parent_hash(&self) -> H256 { + self.header.parent_hash().clone() + } + + fn difficulty(&self) -> U256 { + self.header.difficulty().clone() + } + } +} + +/// Verification for headers. +pub mod headers { + use super::{Kind, BlockLike}; + + use common_types::{ + errors::EthcoreError as Error, + header::Header + }; + use client_traits::VerifyingEngine; + use ethereum_types::{H256, U256}; + + use crate::verification::verify_header_params; + + impl BlockLike for Header { + fn hash(&self) -> H256 { self.hash() } + fn parent_hash(&self) -> H256 { self.parent_hash().clone() } + fn difficulty(&self) -> U256 { self.difficulty().clone() } + } + + /// A mode for verifying headers. + pub struct Headers; + + impl Kind for Headers { + type Input = Header; + type Unverified = Header; + type Verified = Header; + + fn create( + input: Self::Input, + engine: &dyn VerifyingEngine, + check_seal: bool + ) -> Result { + match verify_header_params(&input, engine, true, check_seal) { + Ok(_) => Ok(input), + Err(err) => Err((input, err)) + } + } + + fn verify( + unverified: Self::Unverified, + engine: &dyn VerifyingEngine, + check_seal: bool + ) -> Result { + match check_seal { + true => engine.verify_block_unordered(&unverified,).map(|_| unverified).map_err(Into::into), + false => Ok(unverified), + } + } + } +} diff --git a/ethcore/verification/src/queue/mod.rs b/ethcore/verification/src/queue/mod.rs new file mode 100644 index 00000000000..11f20839462 --- /dev/null +++ b/ethcore/verification/src/queue/mod.rs @@ -0,0 +1,949 @@ +// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// This file is part of Parity Ethereum. + +// Parity Ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Ethereum. If not, see . + +//! A queue of blocks. Sits between network or other I/O and the `BlockChain`. +//! Sorts them ready for blockchain insertion. + +use std::thread::{self, JoinHandle}; +use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering as AtomicOrdering}; +use std::sync::Arc; +use std::cmp; +use std::collections::{VecDeque, HashSet, HashMap}; +use parity_util_mem::{MallocSizeOf, MallocSizeOfExt}; +use ethereum_types::{H256, U256}; +use parking_lot::{Condvar, Mutex, RwLock}; +use ethcore_io::*; +use len_caching_lock::LenCachingMutex; +use num_cpus; +use common_types::{ + errors::{BlockError, EthcoreError as Error, ImportError}, + verification_queue_info::VerificationQueueInfo as QueueInfo, + block_status::BlockStatus, + client_io_message::ClientIoMessage, +}; + +use client_traits::VerifyingEngine; +use log::{debug, trace}; + +use crate::queue::kind::{BlockLike, Kind}; + +pub mod kind; + +const MIN_MEM_LIMIT: usize = 16384; +const MIN_QUEUE_LIMIT: usize = 512; + +/// Type alias for block queue convenience. +pub type BlockQueue = VerificationQueue; + +/// Type alias for header queue convenience. +pub type HeaderQueue = VerificationQueue; + +/// Verification queue configuration +#[derive(Debug, PartialEq, Clone)] +pub struct Config { + /// Maximum number of items to keep in unverified queue. + /// When the limit is reached, is_full returns true. + pub max_queue_size: usize, + /// Maximum heap memory to use. + /// When the limit is reached, is_full returns true. + pub max_mem_use: usize, + /// Settings for the number of verifiers and adaptation strategy. + pub verifier_settings: VerifierSettings, +} + +impl Default for Config { + fn default() -> Self { + Config { + max_queue_size: 30000, + max_mem_use: 50 * 1024 * 1024, + verifier_settings: VerifierSettings::default(), + } + } +} + +/// Verifier settings. +#[derive(Debug, PartialEq, Clone)] +pub struct VerifierSettings { + /// Whether to scale amount of verifiers according to load. + // Todo: replace w/ strategy enum? + pub scale_verifiers: bool, + /// Beginning amount of verifiers. + pub num_verifiers: usize, +} + +impl Default for VerifierSettings { + fn default() -> Self { + VerifierSettings { + scale_verifiers: false, + num_verifiers: num_cpus::get(), + } + } +} + +// pool states +enum State { + // all threads with id < inner value are to work. + Work(usize), + Exit, +} + +/// An item which is in the process of being verified. +#[derive(MallocSizeOf)] +pub struct Verifying { + hash: H256, + output: Option, +} + +/// Status of items in the queue. +pub enum Status { + /// Currently queued. + Queued, + /// Known to be bad. + Bad, + /// Unknown. + Unknown, +} + +impl Into for Status { + fn into(self) -> BlockStatus { + match self { + Status::Queued => BlockStatus::Queued, + Status::Bad => BlockStatus::Bad, + Status::Unknown => BlockStatus::Unknown, + } + } +} + +// the internal queue sizes. +struct Sizes { + unverified: AtomicUsize, + verifying: AtomicUsize, + verified: AtomicUsize, +} + +/// A queue of items to be verified. Sits between network or other I/O and the `BlockChain`. +/// Keeps them in the same order as inserted, minus invalid items. +pub struct VerificationQueue { + engine: Arc, + more_to_verify: Arc, + verification: Arc>, + deleting: Arc, + ready_signal: Arc, + empty: Arc, + processing: RwLock>, // hash to difficulty + ticks_since_adjustment: AtomicUsize, + max_queue_size: usize, + max_mem_use: usize, + scale_verifiers: bool, + verifier_handles: Vec>, + state: Arc<(Mutex, Condvar)>, + total_difficulty: RwLock, +} + +struct QueueSignal { + deleting: Arc, + signalled: AtomicBool, + message_channel: Mutex>, +} + +impl QueueSignal { + fn set_sync(&self) { + // Do not signal when we are about to close + if self.deleting.load(AtomicOrdering::Relaxed) { + return; + } + + if self.signalled.compare_and_swap(false, true, AtomicOrdering::Relaxed) == false { + let channel = self.message_channel.lock().clone(); + if let Err(e) = channel.send_sync(ClientIoMessage::BlockVerified) { + debug!("Error sending BlockVerified message: {:?}", e); + } + } + } + + fn set_async(&self) { + // Do not signal when we are about to close + if self.deleting.load(AtomicOrdering::Relaxed) { + return; + } + + if self.signalled.compare_and_swap(false, true, AtomicOrdering::Relaxed) == false { + let channel = self.message_channel.lock().clone(); + if let Err(e) = channel.send(ClientIoMessage::BlockVerified) { + debug!("Error sending BlockVerified message: {:?}", e); + } + } + } + + fn reset(&self) { + self.signalled.store(false, AtomicOrdering::Relaxed); + } +} + +struct Verification { + // All locks must be captured in the order declared here. + unverified: LenCachingMutex>, + verifying: LenCachingMutex>>, + verified: LenCachingMutex>, + bad: Mutex>, + sizes: Sizes, + check_seal: bool, +} + +impl VerificationQueue { + /// Creates a new queue instance. + pub fn new(config: Config, engine: Arc, message_channel: IoChannel, check_seal: bool) -> Self { + let verification = Arc::new(Verification { + unverified: LenCachingMutex::new(VecDeque::new()), + verifying: LenCachingMutex::new(VecDeque::new()), + verified: LenCachingMutex::new(VecDeque::new()), + bad: Mutex::new(HashSet::new()), + sizes: Sizes { + unverified: AtomicUsize::new(0), + verifying: AtomicUsize::new(0), + verified: AtomicUsize::new(0), + }, + check_seal, + }); + let more_to_verify = Arc::new(Condvar::new()); + let deleting = Arc::new(AtomicBool::new(false)); + let ready_signal = Arc::new(QueueSignal { + deleting: deleting.clone(), + signalled: AtomicBool::new(false), + message_channel: Mutex::new(message_channel), + }); + let empty = Arc::new(Condvar::new()); + let scale_verifiers = config.verifier_settings.scale_verifiers; + + let max_verifiers = ::num_cpus::get(); + let default_amount = cmp::max(1, cmp::min(max_verifiers, config.verifier_settings.num_verifiers)); + + // if `auto-scaling` is enabled spawn up extra threads as they might be needed + // otherwise just spawn the number of threads specified by the config + let number_of_threads = if scale_verifiers { + max_verifiers + } else { + cmp::min(default_amount, max_verifiers) + }; + + let state = Arc::new((Mutex::new(State::Work(default_amount)), Condvar::new())); + let mut verifier_handles = Vec::with_capacity(number_of_threads); + + debug!(target: "verification", "Allocating {} verifiers, {} initially active", number_of_threads, default_amount); + debug!(target: "verification", "Verifier auto-scaling {}", if scale_verifiers { "enabled" } else { "disabled" }); + + for i in 0..number_of_threads { + debug!(target: "verification", "Adding verification thread #{}", i); + + let verification = verification.clone(); + let engine = engine.clone(); + let wait = more_to_verify.clone(); + let ready = ready_signal.clone(); + let empty = empty.clone(); + let state = state.clone(); + + let handle = thread::Builder::new() + .name(format!("Verifier #{}", i)) + .spawn(move || { + VerificationQueue::verify( + verification, + engine, + wait, + ready, + empty, + state, + i, + ) + }) + .expect("Failed to create verifier thread."); + verifier_handles.push(handle); + } + + VerificationQueue { + engine, + ready_signal, + more_to_verify, + verification, + deleting, + processing: RwLock::new(HashMap::new()), + empty, + ticks_since_adjustment: AtomicUsize::new(0), + max_queue_size: cmp::max(config.max_queue_size, MIN_QUEUE_LIMIT), + max_mem_use: cmp::max(config.max_mem_use, MIN_MEM_LIMIT), + scale_verifiers, + verifier_handles, + state, + total_difficulty: RwLock::new(0.into()), + } + } + + fn verify( + verification: Arc>, + engine: Arc, + wait: Arc, + ready: Arc, + empty: Arc, + state: Arc<(Mutex, Condvar)>, + id: usize, + ) { + loop { + // check current state. + { + let mut cur_state = state.0.lock(); + while let State::Work(x) = *cur_state { + // sleep until this thread is required. + if id < x { break } + + debug!(target: "verification", "verifier {} sleeping", id); + state.1.wait(&mut cur_state); + debug!(target: "verification", "verifier {} waking up", id); + } + + if let State::Exit = *cur_state { + debug!(target: "verification", "verifier {} exiting", id); + break; + } + } + + // wait for work if empty. + { + let mut unverified = verification.unverified.lock(); + + if unverified.is_empty() && verification.verifying.lock().is_empty() { + empty.notify_all(); + } + + while unverified.is_empty() { + if let State::Exit = *state.0.lock() { + debug!(target: "verification", "verifier {} exiting", id); + return; + } + + wait.wait(unverified.inner_mut()); + } + + if let State::Exit = *state.0.lock() { + debug!(target: "verification", "verifier {} exiting", id); + return; + } + } + + // do work. + let item = { + // acquire these locks before getting the item to verify. + let mut unverified = verification.unverified.lock(); + let mut verifying = verification.verifying.lock(); + + let item = match unverified.pop_front() { + Some(item) => item, + None => continue, + }; + + verification.sizes.unverified.fetch_sub(item.malloc_size_of(), AtomicOrdering::SeqCst); + verifying.push_back(Verifying { hash: item.hash(), output: None }); + item + }; + + let hash = item.hash(); + let is_ready = match K::verify(item, &*engine, verification.check_seal) { + Ok(verified) => { + let mut verifying = verification.verifying.lock(); + let mut idx = None; + for (i, e) in verifying.iter_mut().enumerate() { + if e.hash == hash { + idx = Some(i); + + verification.sizes.verifying.fetch_add(verified.malloc_size_of(), AtomicOrdering::SeqCst); + e.output = Some(verified); + break; + } + } + + if idx == Some(0) { + // we're next! + let mut verified = verification.verified.lock(); + let mut bad = verification.bad.lock(); + VerificationQueue::drain_verifying(&mut verifying, &mut verified, &mut bad, &verification.sizes); + true + } else { + false + } + }, + Err(_) => { + let mut verifying = verification.verifying.lock(); + let mut verified = verification.verified.lock(); + let mut bad = verification.bad.lock(); + + bad.insert(hash.clone()); + verifying.retain(|e| e.hash != hash); + + if verifying.front().map_or(false, |x| x.output.is_some()) { + VerificationQueue::drain_verifying(&mut verifying, &mut verified, &mut bad, &verification.sizes); + true + } else { + false + } + } + }; + if is_ready { + // Import the block immediately + ready.set_sync(); + } + } + } + + fn drain_verifying( + verifying: &mut VecDeque>, + verified: &mut VecDeque, + bad: &mut HashSet, + sizes: &Sizes, + ) { + let mut removed_size = 0; + let mut inserted_size = 0; + + while let Some(output) = verifying.front_mut().and_then(|x| x.output.take()) { + assert!(verifying.pop_front().is_some()); + let size = output.malloc_size_of(); + removed_size += size; + + if bad.contains(&output.parent_hash()) { + bad.insert(output.hash()); + } else { + inserted_size += size; + verified.push_back(output); + } + } + + sizes.verifying.fetch_sub(removed_size, AtomicOrdering::SeqCst); + sizes.verified.fetch_add(inserted_size, AtomicOrdering::SeqCst); + } + + /// Clear the queue and stop verification activity. + pub fn clear(&self) { + let mut unverified = self.verification.unverified.lock(); + let mut verifying = self.verification.verifying.lock(); + let mut verified = self.verification.verified.lock(); + unverified.clear(); + verifying.clear(); + verified.clear(); + + let sizes = &self.verification.sizes; + sizes.unverified.store(0, AtomicOrdering::Release); + sizes.verifying.store(0, AtomicOrdering::Release); + sizes.verified.store(0, AtomicOrdering::Release); + *self.total_difficulty.write() = 0.into(); + + self.processing.write().clear(); + } + + /// Wait for unverified queue to be empty + pub fn flush(&self) { + let mut unverified = self.verification.unverified.lock(); + while !unverified.is_empty() || !self.verification.verifying.lock().is_empty() { + self.empty.wait(unverified.inner_mut()); + } + } + + /// Check if the item is currently in the queue + pub fn status(&self, hash: &H256) -> Status { + if self.processing.read().contains_key(hash) { + return Status::Queued; + } + if self.verification.bad.lock().contains(hash) { + return Status::Bad; + } + Status::Unknown + } + + /// Add a block to the queue. + pub fn import(&self, input: K::Input) -> Result { + let hash = input.hash(); + { + if self.processing.read().contains_key(&hash) { + return Err((input, Error::Import(ImportError::AlreadyQueued).into())); + } + + let mut bad = self.verification.bad.lock(); + if bad.contains(&hash) { + return Err((input, Error::Import(ImportError::KnownBad).into())); + } + + if bad.contains(&input.parent_hash()) { + bad.insert(hash); + return Err((input, Error::Import(ImportError::KnownBad).into())); + } + } + + match K::create(input, &*self.engine, self.verification.check_seal) { + Ok(item) => { + self.verification.sizes.unverified.fetch_add(item.malloc_size_of(), AtomicOrdering::SeqCst); + + self.processing.write().insert(hash, item.difficulty()); + { + let mut td = self.total_difficulty.write(); + *td = *td + item.difficulty(); + } + self.verification.unverified.lock().push_back(item); + self.more_to_verify.notify_all(); + Ok(hash) + }, + Err((input, err)) => { + match err { + // Don't mark future blocks as bad. + Error::Block(BlockError::TemporarilyInvalid(_)) => {}, + _ => { + self.verification.bad.lock().insert(hash); + } + } + Err((input, err)) + } + } + } + + /// Mark given item and all its children as bad. pauses verification + /// until complete. + pub fn mark_as_bad(&self, hashes: &[H256]) { + if hashes.is_empty() { + return; + } + let mut verified_lock = self.verification.verified.lock(); + let verified = &mut *verified_lock; + let mut bad = self.verification.bad.lock(); + let mut processing = self.processing.write(); + bad.reserve(hashes.len()); + for hash in hashes { + bad.insert(hash.clone()); + if let Some(difficulty) = processing.remove(hash) { + let mut td = self.total_difficulty.write(); + *td = *td - difficulty; + } + } + + let mut new_verified = VecDeque::new(); + let mut removed_size = 0; + for output in verified.drain(..) { + if bad.contains(&output.parent_hash()) { + removed_size += output.malloc_size_of(); + bad.insert(output.hash()); + if let Some(difficulty) = processing.remove(&output.hash()) { + let mut td = self.total_difficulty.write(); + *td = *td - difficulty; + } + } else { + new_verified.push_back(output); + } + } + + self.verification.sizes.verified.fetch_sub(removed_size, AtomicOrdering::SeqCst); + *verified = new_verified; + } + + /// Mark given item as processed. + /// Returns true if the queue becomes empty. + pub fn mark_as_good(&self, hashes: &[H256]) -> bool { + if hashes.is_empty() { + return self.processing.read().is_empty(); + } + let mut processing = self.processing.write(); + for hash in hashes { + if let Some(difficulty) = processing.remove(hash) { + let mut td = self.total_difficulty.write(); + *td = *td - difficulty; + } + } + processing.is_empty() + } + + /// Removes up to `max` verified items from the queue + pub fn drain(&self, max: usize) -> Vec { + let mut verified = self.verification.verified.lock(); + let count = cmp::min(max, verified.len()); + let result = verified.drain(..count).collect::>(); + + let drained_size = result.iter().map(MallocSizeOfExt::malloc_size_of).fold(0, |a, c| a + c); + self.verification.sizes.verified.fetch_sub(drained_size, AtomicOrdering::SeqCst); + + self.ready_signal.reset(); + if !verified.is_empty() { + self.ready_signal.set_async(); + } + result + } + + /// Returns true if there is nothing currently in the queue. + pub fn is_empty(&self) -> bool { + let v = &self.verification; + + v.unverified.load_len() == 0 + && v.verifying.load_len() == 0 + && v.verified.load_len() == 0 + } + + /// Get queue status. + pub fn queue_info(&self) -> QueueInfo { + use std::mem::size_of; + + let (unverified_len, unverified_bytes) = { + let len = self.verification.unverified.load_len(); + let size = self.verification.sizes.unverified.load(AtomicOrdering::Acquire); + + (len, size + len * size_of::()) + }; + let (verifying_len, verifying_bytes) = { + let len = self.verification.verifying.load_len(); + let size = self.verification.sizes.verifying.load(AtomicOrdering::Acquire); + (len, size + len * size_of::>()) + }; + let (verified_len, verified_bytes) = { + let len = self.verification.verified.load_len(); + let size = self.verification.sizes.verified.load(AtomicOrdering::Acquire); + (len, size + len * size_of::()) + }; + + QueueInfo { + unverified_queue_size: unverified_len, + verifying_queue_size: verifying_len, + verified_queue_size: verified_len, + max_queue_size: self.max_queue_size, + max_mem_use: self.max_mem_use, + mem_used: unverified_bytes + + verifying_bytes + + verified_bytes + } + } + + /// Get the total difficulty of all the blocks in the queue. + pub fn total_difficulty(&self) -> U256 { + self.total_difficulty.read().clone() + } + + /// Get the current number of working verifiers. + pub fn num_verifiers(&self) -> usize { + match *self.state.0.lock() { + State::Work(x) => x, + State::Exit => panic!("state only set to exit on drop; queue live now; qed"), + } + } + + /// Optimise memory footprint of the heap fields, and adjust the number of threads + /// to better suit the workload. + pub fn collect_garbage(&self) { + // number of ticks to average queue stats over + // when deciding whether to change the number of verifiers. + #[cfg(not(test))] + const READJUSTMENT_PERIOD: usize = 12; + + #[cfg(test)] + const READJUSTMENT_PERIOD: usize = 1; + + let (u_len, v_len) = { + let u_len = { + let mut q = self.verification.unverified.lock(); + q.shrink_to_fit(); + q.len() + }; + self.verification.verifying.lock().shrink_to_fit(); + + let v_len = { + let mut q = self.verification.verified.lock(); + q.shrink_to_fit(); + q.len() + }; + + (u_len as isize, v_len as isize) + }; + + self.processing.write().shrink_to_fit(); + + if !self.scale_verifiers { return } + + if self.ticks_since_adjustment.fetch_add(1, AtomicOrdering::SeqCst) + 1 >= READJUSTMENT_PERIOD { + self.ticks_since_adjustment.store(0, AtomicOrdering::SeqCst); + } else { + return; + } + + let current = self.num_verifiers(); + + let diff = (v_len - u_len).abs(); + let total = v_len + u_len; + + self.scale_verifiers( + if u_len < 20 { + 1 + } else if diff <= total / 10 { + current + } else if v_len > u_len { + current - 1 + } else { + current + 1 + } + ); + } + + // wake up or sleep verifiers to get as close to the target as + // possible, never going over the amount of initially allocated threads + // or below 1. + fn scale_verifiers(&self, target: usize) { + let current = self.num_verifiers(); + let target = cmp::min(self.verifier_handles.len(), target); + let target = cmp::max(1, target); + + debug!(target: "verification", "Scaling from {} to {} verifiers", current, target); + + *self.state.0.lock() = State::Work(target); + self.state.1.notify_all(); + } +} + +impl Drop for VerificationQueue { + fn drop(&mut self) { + trace!(target: "shutdown", "[VerificationQueue] Closing..."); + self.clear(); + self.deleting.store(true, AtomicOrdering::SeqCst); + + // set exit state; should be done before `more_to_verify` notification. + *self.state.0.lock() = State::Exit; + self.state.1.notify_all(); + + // acquire this lock to force threads to reach the waiting point + // if they're in-between the exit check and the more_to_verify wait. + { + let _unverified = self.verification.unverified.lock(); + self.more_to_verify.notify_all(); + } + + // wait for all verifier threads to join. + for thread in self.verifier_handles.drain(..) { + thread.join().expect("Propagating verifier thread panic on shutdown"); + } + + trace!(target: "shutdown", "[VerificationQueue] Closed."); + } +} + +#[cfg(test)] +mod tests { + use io::*; + use spec::Spec; + use super::{BlockQueue, Config, State}; + use super::kind::blocks::Unverified; + use test_helpers::{get_good_dummy_block_seq, get_good_dummy_block}; + use bytes::Bytes; + use types::{ + view::{self, BlockView}, + errors::{EthcoreError as Error, ImportError}, + }; + use types::views::BlockView; + + // create a test block queue. + // auto_scaling enables verifier adjustment. + fn get_test_queue(auto_scale: bool) -> BlockQueue { + let spec = Spec::new_test(); + let engine = spec.engine; + + let mut config = Config::default(); + config.verifier_settings.scale_verifiers = auto_scale; + BlockQueue::new(config, engine, IoChannel::disconnected(), true) + } + + fn get_test_config(num_verifiers: usize, is_auto_scale: bool) -> Config { + let mut config = Config::default(); + config.verifier_settings.num_verifiers = num_verifiers; + config.verifier_settings.scale_verifiers = is_auto_scale; + config + } + + fn new_unverified(bytes: Bytes) -> Unverified { + Unverified::from_rlp(bytes).expect("Should be valid rlp") + } + + #[test] + fn can_be_created() { + // TODO better test + let spec = Spec::new_test(); + let engine = spec.engine; + let _ = BlockQueue::new(Config::default(), engine, IoChannel::disconnected(), true); + } + + #[test] + fn can_import_blocks() { + let queue = get_test_queue(false); + if let Err(e) = queue.import(new_unverified(get_good_dummy_block())) { + panic!("error importing block that is valid by definition({:?})", e); + } + } + + #[test] + fn returns_error_for_duplicates() { + let queue = get_test_queue(false); + if let Err(e) = queue.import(new_unverified(get_good_dummy_block())) { + panic!("error importing block that is valid by definition({:?})", e); + } + + let duplicate_import = queue.import(new_unverified(get_good_dummy_block())); + match duplicate_import { + Err((_, e)) => { + match e { + Error::Import(ImportError::AlreadyQueued) => {}, + _ => { panic!("must return AlreadyQueued error"); } + } + } + Ok(_) => { panic!("must produce error"); } + } + } + + #[test] + fn returns_total_difficulty() { + let queue = get_test_queue(false); + let block = get_good_dummy_block(); + let hash = view!(BlockView, &block).header().hash().clone(); + if let Err(e) = queue.import(new_unverified(block)) { + panic!("error importing block that is valid by definition({:?})", e); + } + queue.flush(); + assert_eq!(queue.total_difficulty(), 131072.into()); + queue.drain(10); + assert_eq!(queue.total_difficulty(), 131072.into()); + queue.mark_as_good(&[ hash ]); + assert_eq!(queue.total_difficulty(), 0.into()); + } + + #[test] + fn returns_ok_for_drained_duplicates() { + let queue = get_test_queue(false); + let block = get_good_dummy_block(); + let hash = view!(BlockView, &block).header().hash().clone(); + if let Err(e) = queue.import(new_unverified(block)) { + panic!("error importing block that is valid by definition({:?})", e); + } + queue.flush(); + queue.drain(10); + queue.mark_as_good(&[ hash ]); + + if let Err(e) = queue.import(new_unverified(get_good_dummy_block())) { + panic!("error importing block that has already been drained ({:?})", e); + } + } + + #[test] + fn returns_empty_once_finished() { + let queue = get_test_queue(false); + queue.import(new_unverified(get_good_dummy_block())) + .expect("error importing block that is valid by definition"); + queue.flush(); + queue.drain(1); + + assert!(queue.queue_info().is_empty()); + } + + #[test] + fn test_mem_limit() { + let spec = Spec::new_test(); + let engine = spec.engine; + let mut config = Config::default(); + config.max_mem_use = super::MIN_MEM_LIMIT; // empty queue uses about 15000 + let queue = BlockQueue::new(config, engine, IoChannel::disconnected(), true); + assert!(!queue.queue_info().is_full()); + let mut blocks = get_good_dummy_block_seq(50); + for b in blocks.drain(..) { + queue.import(new_unverified(b)).unwrap(); + } + assert!(queue.queue_info().is_full()); + } + + #[test] + fn scaling_limits() { + let max_verifiers = ::num_cpus::get(); + let queue = get_test_queue(true); + queue.scale_verifiers(max_verifiers + 1); + + assert!(queue.num_verifiers() < max_verifiers + 1); + + queue.scale_verifiers(0); + + assert!(queue.num_verifiers() == 1); + } + + #[test] + fn readjust_verifiers() { + let queue = get_test_queue(true); + + // put all the verifiers to sleep to ensure + // the test isn't timing sensitive. + *queue.state.0.lock() = State::Work(0); + + for block in get_good_dummy_block_seq(5000) { + queue.import(new_unverified(block)).expect("Block good by definition; qed"); + } + + // almost all unverified == bump verifier count. + queue.collect_garbage(); + assert_eq!(queue.num_verifiers(), 1); + + queue.flush(); + + // nothing to verify == use minimum number of verifiers. + queue.collect_garbage(); + assert_eq!(queue.num_verifiers(), 1); + } + + #[test] + fn worker_threads_honor_specified_number_without_scaling() { + let spec = Spec::new_test(); + let engine = spec.engine; + let config = get_test_config(1, false); + let queue = BlockQueue::new(config, engine, IoChannel::disconnected(), true); + + assert_eq!(queue.num_verifiers(), 1); + } + + #[test] + fn worker_threads_specified_to_zero_should_set_to_one() { + let spec = Spec::new_test(); + let engine = spec.engine; + let config = get_test_config(0, false); + let queue = BlockQueue::new(config, engine, IoChannel::disconnected(), true); + + assert_eq!(queue.num_verifiers(), 1); + } + + #[test] + fn worker_threads_should_only_accept_max_number_cpus() { + let spec = Spec::new_test(); + let engine = spec.engine; + let config = get_test_config(10_000, false); + let queue = BlockQueue::new(config, engine, IoChannel::disconnected(), true); + let num_cpus = ::num_cpus::get(); + + assert_eq!(queue.num_verifiers(), num_cpus); + } + + #[test] + fn worker_threads_scaling_with_specifed_num_of_workers() { + let num_cpus = ::num_cpus::get(); + // only run the test with at least 2 CPUs + if num_cpus > 1 { + let spec = Spec::new_test(); + let engine = spec.engine; + let config = get_test_config(num_cpus - 1, true); + let queue = BlockQueue::new(config, engine, IoChannel::disconnected(), true); + queue.scale_verifiers(num_cpus); + + assert_eq!(queue.num_verifiers(), num_cpus); + } + } +} diff --git a/ethcore/verification/src/verification.rs b/ethcore/verification/src/verification.rs new file mode 100644 index 00000000000..13c18cf139c --- /dev/null +++ b/ethcore/verification/src/verification.rs @@ -0,0 +1,390 @@ +// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// This file is part of Parity Ethereum. + +// Parity Ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Ethereum. If not, see . + +//! Block and transaction verification functions +//! +//! Block verification is done in 3 steps +//! 1. Quick verification upon adding to the block queue +//! 2. Signatures verification done in the queue. +//! 3. Final verification against the blockchain done before enactment. + +use std::{ + collections::HashSet, + time::{Duration, SystemTime, UNIX_EPOCH}, +}; + +use keccak_hash::keccak; +use rlp::Rlp; +use triehash_ethereum::ordered_trie_root; +use unexpected::{Mismatch, OutOfBounds}; + +use ethcore_blockchain::BlockProvider; +use call_contract::CallContract; +use client_traits::{BlockInfo, VerifyingEngine, VerifyingClient}; + +use crate::{ + queue::kind::blocks::Unverified, +}; +use common_types::{ + BlockNumber, + header::Header, + block::PreverifiedBlock, + errors::{BlockError, EthcoreError}, + engines::MAX_UNCLE_AGE, +}; + +use time_utils::CheckedSystemTime; + +/// Parameters for full verification of block family +pub struct FullFamilyParams<'a, C: BlockInfo + CallContract + 'a> { + /// Pre-verified block + pub block: &'a PreverifiedBlock, + + /// Block provider to use during verification + pub block_provider: &'a dyn BlockProvider, + + /// Engine client to use during verification + pub client: &'a C, +} + +/// Phase 1 quick block verification. Only does checks that are cheap. Operates on a single block +pub fn verify_block_basic(block: &Unverified, engine: &dyn VerifyingEngine, check_seal: bool) -> Result<(), EthcoreError> { + verify_header_params(&block.header, engine, true, check_seal)?; + verify_block_integrity(block)?; + + if check_seal { + engine.verify_block_basic(&block.header)?; + } + + for uncle in &block.uncles { + verify_header_params(uncle, engine, false, check_seal)?; + if check_seal { + engine.verify_block_basic(uncle)?; + } + } + + for t in &block.transactions { + engine.verify_transaction_basic(t, &block.header)?; + } + + Ok(()) +} + +/// Phase 2 verification. Perform costly checks such as transaction signatures and block nonce for ethash. +/// Still operates on a individual block +/// Returns a `PreverifiedBlock` structure populated with transactions +pub fn verify_block_unordered( + block: Unverified, + engine: &dyn VerifyingEngine, + check_seal: bool +) -> Result { + let header = block.header; + if check_seal { + engine.verify_block_unordered(&header)?; + for uncle in &block.uncles { + engine.verify_block_unordered(uncle)?; + } + } + // Verify transactions. + let nonce_cap = if header.number() >= engine.params().dust_protection_transition { + Some((engine.params().nonce_cap_increment * header.number()).into()) + } else { + None + }; + + let transactions = block.transactions + .into_iter() + .map(|t| { + let t = engine.verify_transaction_unordered(t, &header)?; + if let Some(max_nonce) = nonce_cap { + if t.nonce >= max_nonce { + return Err(BlockError::TooManyTransactions(t.sender()).into()); + } + } + Ok(t) + }) + .collect::, EthcoreError>>()?; + + Ok(PreverifiedBlock { + header, + transactions, + uncles: block.uncles, + bytes: block.bytes, + }) +} + +/// Phase 3 verification. Check block information against parent and uncles. +//pub fn verify_block_family( +pub fn verify_block_family( + header: &Header, + parent: &Header, + engine: &dyn VerifyingEngine, + do_full: Option> +) -> Result<(), EthcoreError> { + // TODO: verify timestamp + verify_parent(&header, &parent, engine)?; + engine.verify_block_family(&header, &parent)?; + + let params = match do_full { + Some(x) => x, + None => return Ok(()), + }; + + verify_uncles(params.block, params.block_provider, engine)?; + + // transactions are verified against the parent header since the current + // state wasn't available when the tx was created + engine.verify_transactions(¶ms.block.transactions, parent, params.client)?; +// for tx in ¶ms.block.transactions { +// // transactions are verified against the parent header since the current +// // state wasn't available when the tx was created +// engine.machine().verify_transaction(tx, parent, params.client)?; +// } + + Ok(()) +} + + +/// Phase 4 verification. Check block information against transaction enactment results, +pub fn verify_block_final( + expected: &Header, + got: &Header +) -> Result<(), EthcoreError> { + if expected.state_root() != got.state_root() { + return Err(From::from(BlockError::InvalidStateRoot(Mismatch { expected: *expected.state_root(), found: *got.state_root() }))) + } + if expected.gas_used() != got.gas_used() { + return Err(From::from(BlockError::InvalidGasUsed(Mismatch { expected: *expected.gas_used(), found: *got.gas_used() }))) + } + if expected.log_bloom() != got.log_bloom() { + return Err(From::from(BlockError::InvalidLogBloom(Box::new(Mismatch { expected: *expected.log_bloom(), found: *got.log_bloom() })))) + } + if expected.receipts_root() != got.receipts_root() { + return Err(From::from(BlockError::InvalidReceiptsRoot(Mismatch { expected: *expected.receipts_root(), found: *got.receipts_root() }))) + } + Ok(()) +} + +/// Check basic header parameters. +pub fn verify_header_params(header: &Header, engine: &dyn VerifyingEngine, is_full: bool, check_seal: bool) -> Result<(), EthcoreError> { + if check_seal { + let expected_seal_fields = engine.seal_fields(header); + if header.seal().len() != expected_seal_fields { + return Err(From::from(BlockError::InvalidSealArity( + Mismatch { expected: expected_seal_fields, found: header.seal().len() } + ))); + } + } + + if header.number() >= From::from(BlockNumber::max_value()) { + return Err(From::from(BlockError::RidiculousNumber(OutOfBounds { max: Some(From::from(BlockNumber::max_value())), min: None, found: header.number() }))) + } + if header.gas_used() > header.gas_limit() { + return Err(From::from(BlockError::TooMuchGasUsed(OutOfBounds { max: Some(*header.gas_limit()), min: None, found: *header.gas_used() }))); + } + let min_gas_limit = engine.params().min_gas_limit; + if header.gas_limit() < &min_gas_limit { + return Err(From::from(BlockError::InvalidGasLimit(OutOfBounds { min: Some(min_gas_limit), max: None, found: *header.gas_limit() }))); + } + if let Some(limit) = engine.maximum_gas_limit() { + if header.gas_limit() > &limit { + return Err(From::from(BlockError::InvalidGasLimit(OutOfBounds { min: None, max: Some(limit), found: *header.gas_limit() }))); + } + } + let maximum_extra_data_size = engine.maximum_extra_data_size(); + if header.number() != 0 && header.extra_data().len() > maximum_extra_data_size { + return Err(From::from(BlockError::ExtraDataOutOfBounds(OutOfBounds { min: None, max: Some(maximum_extra_data_size), found: header.extra_data().len() }))); + } + + if let Some(ext) = engine.ethash_extensions() { + if header.number() >= ext.dao_hardfork_transition && + header.number() <= ext.dao_hardfork_transition + 9 && + header.extra_data()[..] != b"dao-hard-fork"[..] { + return Err(From::from(BlockError::ExtraDataOutOfBounds(OutOfBounds { min: None, max: None, found: 0 }))); + } + } + + 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 = CheckedSystemTime::checked_add(UNIX_EPOCH, 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 }.into()))) + } + + if timestamp > max_time { + return Err(From::from(BlockError::TemporarilyInvalid(OutOfBounds { max: Some(max_time), min: None, found: timestamp }.into()))) + } + } + + Ok(()) +} + +/// Check header parameters agains parent header. +fn verify_parent(header: &Header, parent: &Header, engine: &dyn VerifyingEngine) -> Result<(), EthcoreError> { + assert!(header.parent_hash().is_zero() || &parent.hash() == header.parent_hash(), + "Parent hash should already have been verified; qed"); + + let gas_limit_divisor = engine.params().gas_limit_bound_divisor; + + if !engine.is_timestamp_valid(header.timestamp(), parent.timestamp()) { + let now = SystemTime::now(); + let min = CheckedSystemTime::checked_add(now, Duration::from_secs(parent.timestamp().saturating_add(1))) + .ok_or(BlockError::TimestampOverflow)?; + let found = CheckedSystemTime::checked_add(now, Duration::from_secs(header.timestamp())) + .ok_or(BlockError::TimestampOverflow)?; + return Err(From::from(BlockError::InvalidTimestamp(OutOfBounds { max: None, min: Some(min), found }.into()))) + } + if header.number() != parent.number() + 1 { + return Err(From::from(BlockError::InvalidNumber(Mismatch { expected: parent.number() + 1, found: header.number() }))); + } + + if header.number() == 0 { + return Err(BlockError::RidiculousNumber(OutOfBounds { min: Some(1), max: None, found: header.number() }).into()); + } + + let parent_gas_limit = *parent.gas_limit(); + let min_gas = parent_gas_limit - parent_gas_limit / gas_limit_divisor; + let max_gas = parent_gas_limit + parent_gas_limit / gas_limit_divisor; + if header.gas_limit() <= &min_gas || header.gas_limit() >= &max_gas { + return Err(From::from(BlockError::InvalidGasLimit(OutOfBounds { min: Some(min_gas), max: Some(max_gas), found: *header.gas_limit() }))); + } + + Ok(()) +} + + +fn verify_uncles( + block: &PreverifiedBlock, + bc: &dyn BlockProvider, + engine: &dyn VerifyingEngine +) -> Result<(), EthcoreError> { + let header = &block.header; + let num_uncles = block.uncles.len(); + let max_uncles = engine.maximum_uncle_count(header.number()); + if num_uncles != 0 { + if num_uncles > max_uncles { + return Err(From::from(BlockError::TooManyUncles(OutOfBounds { + min: None, + max: Some(max_uncles), + found: num_uncles, + }))); + } + + let mut excluded = HashSet::new(); + excluded.insert(header.hash()); + let mut hash = header.parent_hash().clone(); + excluded.insert(hash.clone()); + for _ in 0..MAX_UNCLE_AGE { + match bc.block_details(&hash) { + Some(details) => { + excluded.insert(details.parent); + let b = bc.block(&hash) + .expect("parent already known to be stored; qed"); + excluded.extend(b.uncle_hashes()); + hash = details.parent; + } + None => break + } + } + + let mut verified = HashSet::new(); + for uncle in &block.uncles { + if excluded.contains(&uncle.hash()) { + return Err(From::from(BlockError::UncleInChain(uncle.hash()))) + } + + if verified.contains(&uncle.hash()) { + return Err(From::from(BlockError::DuplicateUncle(uncle.hash()))) + } + + // m_currentBlock.number() - uncle.number() m_cB.n - uP.n() + // 1 2 + // 2 + // 3 + // 4 + // 5 + // 6 7 + // (8 Invalid) + + let depth = if header.number() > uncle.number() { header.number() - uncle.number() } else { 0 }; + if depth > MAX_UNCLE_AGE as u64 { + return Err(From::from(BlockError::UncleTooOld(OutOfBounds { min: Some(header.number() - depth), max: Some(header.number() - 1), found: uncle.number() }))); + } + else if depth < 1 { + return Err(From::from(BlockError::UncleIsBrother(OutOfBounds { min: Some(header.number() - depth), max: Some(header.number() - 1), found: uncle.number() }))); + } + + // cB + // cB.p^1 1 depth, valid uncle + // cB.p^2 ---/ 2 + // cB.p^3 -----/ 3 + // cB.p^4 -------/ 4 + // cB.p^5 ---------/ 5 + // cB.p^6 -----------/ 6 + // cB.p^7 -------------/ + // cB.p^8 + let mut expected_uncle_parent = header.parent_hash().clone(); + let uncle_parent = bc.block_header_data(&uncle.parent_hash()) + .ok_or_else(|| EthcoreError::from(BlockError::UnknownUncleParent(uncle.parent_hash().clone())))?; + for _ in 0..depth { + match bc.block_details(&expected_uncle_parent) { + Some(details) => { + expected_uncle_parent = details.parent; + }, + None => break + } + } + if expected_uncle_parent != uncle_parent.hash() { + return Err(From::from(BlockError::UncleParentNotInChain(uncle_parent.hash()))); + } + + let uncle_parent = uncle_parent.decode()?; + verify_parent(&uncle, &uncle_parent, engine)?; + engine.verify_block_family(&uncle, &uncle_parent)?; + verified.insert(uncle.hash()); + } + } + + Ok(()) +} + +/// Verify block data against header: transactions root and uncles hash. +fn verify_block_integrity(block: &Unverified) -> Result<(), EthcoreError> { + let block_rlp = Rlp::new(&block.bytes); + let tx = block_rlp.at(1)?; + let expected_root = ordered_trie_root(tx.iter().map(|r| r.as_raw())); + if &expected_root != block.header.transactions_root() { + return Err(BlockError::InvalidTransactionsRoot(Mismatch { + expected: expected_root, + found: *block.header.transactions_root(), + }).into()); + } + let expected_uncles = keccak(block_rlp.at(2)?.as_raw()); + if &expected_uncles != block.header.uncles_hash(){ + return Err(BlockError::InvalidUnclesHash(Mismatch { + expected: expected_uncles, + found: *block.header.uncles_hash(), + }).into()); + } + Ok(()) +} + + diff --git a/ethcore/verification/src/verifier.rs b/ethcore/verification/src/verifier.rs new file mode 100644 index 00000000000..3ab7fb199e5 --- /dev/null +++ b/ethcore/verification/src/verifier.rs @@ -0,0 +1,45 @@ +// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// This file is part of Parity Ethereum. + +// Parity Ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Ethereum. If not, see . + +//! A generic verifier trait. + +use call_contract::CallContract; +use client_traits::{BlockInfo, VerifyingEngine}; +use common_types::{ + errors::EthcoreError, + header::Header, +}; + +use crate::verification::FullFamilyParams; + +/// Should be used to verify blocks. +pub trait Verifier: Send + Sync + where C: BlockInfo + CallContract +{ + /// Verify a block relative to its parent and uncles. + fn verify_block_family( + &self, + header: &Header, + parent: &Header, + engine: &dyn VerifyingEngine, + do_full: Option> + ) -> Result<(), EthcoreError>; + + /// Do a final verification check for an enacted header vs its expected counterpart. + fn verify_block_final(&self, expected: &Header, got: &Header) -> Result<(), EthcoreError>; + /// Verify a block, inspecting external state. + fn verify_block_external(&self, header: &Header, engine: &dyn VerifyingEngine) -> Result<(), EthcoreError>; +}