From f144f88ab6f1fbb6c52f670bacc0803b35e73378 Mon Sep 17 00:00:00 2001 From: David Palm Date: Wed, 3 Jul 2019 15:57:07 +0200 Subject: [PATCH 01/87] WIP move errors, pod_account and state account to own crates --- Cargo.lock | 68 +++ Cargo.toml | 7 +- ethcore/ethcore-error/Cargo.toml | 20 + ethcore/ethcore-error/src/lib.rs | 303 +++++++++++++ ethcore/pod-account/Cargo.toml | 26 ++ ethcore/pod-account/src/lib.rs | 256 +++++++++++ ethcore/state-account/Cargo.toml | 23 + ethcore/state-account/src/lib.rs | 747 +++++++++++++++++++++++++++++++ 8 files changed, 1449 insertions(+), 1 deletion(-) create mode 100644 ethcore/ethcore-error/Cargo.toml create mode 100644 ethcore/ethcore-error/src/lib.rs create mode 100644 ethcore/pod-account/Cargo.toml create mode 100644 ethcore/pod-account/src/lib.rs create mode 100644 ethcore/state-account/Cargo.toml create mode 100644 ethcore/state-account/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 3bf2f113f45..7f18806d1ba 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1008,6 +1008,22 @@ dependencies = [ "rlp_derive 0.1.0", ] +[[package]] +name = "ethcore-error" +version = "0.1.0" +dependencies = [ + "common-types 0.1.0", + "derive_more 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ethcore 1.12.0", + "ethcore-io 1.12.0", + "ethereum-types 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ethkey 0.3.0", + "parity-snappy 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "patricia-trie-ethereum 0.1.0", + "rlp 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "unexpected 0.1.0", +] + [[package]] name = "ethcore-io" version = "1.12.0" @@ -1916,6 +1932,14 @@ dependencies = [ "either 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "itertools" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "either 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "itertools-num" version = "0.1.3" @@ -3219,6 +3243,29 @@ dependencies = [ "crunchy 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "pod-account" +version = "0.1.0" +dependencies = [ + "common-types 0.1.0", + "ethereum-types 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ethjson 0.1.0", + "hash-db 0.12.4 (registry+https://github.com/rust-lang/crates.io-index)", + "itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "keccak-hash 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "keccak-hasher 0.1.1", + "kvdb 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-bytes 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "patricia-trie-ethereum 0.1.0", + "rlp 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", + "state-account 0.1.0", + "trie-db 0.12.4 (registry+https://github.com/rust-lang/crates.io-index)", + "triehash-ethereum 0.2.0", +] + [[package]] name = "pretty_assertions" version = "0.1.2" @@ -3898,6 +3945,26 @@ name = "stable_deref_trait" version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "state-account" +version = "0.1.0" +dependencies = [ + "common-types 0.1.0", + "ethcore-error 0.1.0", + "ethereum-types 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "hash-db 0.12.4 (registry+https://github.com/rust-lang/crates.io-index)", + "keccak-hash 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "keccak-hasher 0.1.1", + "kvdb 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "lru-cache 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-bytes 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "patricia-trie-ethereum 0.1.0", + "rlp 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", + "trie-db 0.12.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "static_assertions" version = "0.2.5" @@ -4870,6 +4937,7 @@ dependencies = [ "checksum ipnetwork 0.12.8 (registry+https://github.com/rust-lang/crates.io-index)" = "70783119ac90828aaba91eae39db32c6c1b8838deea3637e5238efa0130801ab" "checksum itertools 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4833d6978da405305126af4ac88569b5d71ff758581ce5a987dbfa3755f694fc" "checksum itertools 0.7.8 (registry+https://github.com/rust-lang/crates.io-index)" = "f58856976b776fedd95533137617a02fb25719f40e7d9b01c7043cd65474f450" +"checksum itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5b8467d9c1cebe26feb08c640139247fac215782d35371ade9a2136ed6085358" "checksum itertools-num 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a872a22f9e6f7521ca557660adb96dd830e54f0f490fa115bb55dd69d38b27e7" "checksum itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1306f3464951f30e30d12373d31c79fbd52d236e5e896fd92f96ec7babbbe60b" "checksum jemalloc-sys 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "bfc62c8e50e381768ce8ee0428ee53741929f7ebd73e4d83f669bcf7693e00ae" diff --git a/Cargo.toml b/Cargo.toml index 764a2d30d29..f2a9ab20c20 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -139,5 +139,10 @@ members = [ "util/keccak-hasher", "util/patricia-trie-ethereum", "util/fastmap", - "util/time-utils" + "util/time-utils", + +# TODO: remove these once they're inserted into the dep tree + "ethcore/pod-account", + "ethcore/state-account", + "ethcore/ethcore-error", ] diff --git a/ethcore/ethcore-error/Cargo.toml b/ethcore/ethcore-error/Cargo.toml new file mode 100644 index 00000000000..6189f7415d6 --- /dev/null +++ b/ethcore/ethcore-error/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "ethcore-error" +version = "0.1.0" +authors = ["David Palm "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +derive_more = "0.14.0" +ethereum-types = "0.6.0" +ethkey = { path = "../../accounts/ethkey" } +ethtrie = { package = "patricia-trie-ethereum", path = "../../util/patricia-trie-ethereum" } +rlp = "0.4.0" +parity-snappy = "0.1" +# TODO: this ruins everything, get it sorted +ethcore = { path = ".." } +common-types = {path = "../types/" } +unexpected = { path = "../../util/unexpected" } +ethcore-io = { path = "../../util/io" } diff --git a/ethcore/ethcore-error/src/lib.rs b/ethcore/ethcore-error/src/lib.rs new file mode 100644 index 00000000000..4a925713fb9 --- /dev/null +++ b/ethcore/ethcore-error/src/lib.rs @@ -0,0 +1,303 @@ +// 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::{fmt, error}; +use std::time::SystemTime; + +use derive_more::{Display, From}; +use ethereum_types::{H256, U256, Address, Bloom}; +use ethkey::Error as EthkeyError; +use ethtrie::TrieError; +use rlp; +use parity_snappy::InvalidInput; +// TODO: move this error somewhere else (here?) +use ethcore::snapshot::Error as SnapshotError; +use common_types::BlockNumber; +use common_types::transaction::Error as TransactionError; +use unexpected::{Mismatch, OutOfBounds}; + +// TODO: move this error somewhere else (here?) +use ethcore::engines::EngineError; + +pub use ethcore::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" + } +} + +/// Queue error +#[derive(Debug, Display, From)] +pub enum QueueError { + /// Queue is full + #[display(fmt = "Queue is full ({})", _0)] + Full(usize), + /// Io channel error + #[display(fmt = "Io channel error: {}", _0)] + Channel(ethcore_io::IoError) +} + +impl error::Error for QueueError { + fn source(&self) -> Option<&(dyn error::Error + 'static)> { + match self { + QueueError::Channel(e) => Some(e), + _ => None, + } + } +} + +/// 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 {} + +/// Api-level error for transaction import +#[derive(Debug, Clone)] +pub enum TransactionImportError { + /// Transaction error + Transaction(TransactionError), + /// Other error + Other(String), +} + +impl From for TransactionImportError { + fn from(e: Error) -> Self { + match e { + Error::Transaction(transaction_error) => TransactionImportError::Transaction(transaction_error), + _ => TransactionImportError::Other(format!("other block import error: {:?}", e)), + } + } +} + +/// 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 error: {}", _0)] + Queue(QueueError), + /// Io create error + #[display(fmt = "Io error: {}", _0)] + Io(ethcore_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, + /// Unknown engine given + #[display(fmt = "Unknown engine name ({})", _0)] + UnknownEngineName(String), + /// A convenient variant for String. + #[display(fmt = "{}", _0)] + Msg(String), +} + +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), + _ => None, + } + } +} + +impl From for Error { + fn from(s: String) -> Self { + Error::Msg(s) + } +} + +impl From<&str> for Error { + fn from(s: &str) -> Self { + Error::Msg(s.into()) + } +} + +impl From> for Error where Error: From { + fn from(err: Box) -> Error { + Error::from(*err) + } +} diff --git a/ethcore/pod-account/Cargo.toml b/ethcore/pod-account/Cargo.toml new file mode 100644 index 00000000000..7849b6e56f2 --- /dev/null +++ b/ethcore/pod-account/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "pod-account" +version = "0.1.0" +authors = ["David Palm "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +common-types = { path = "../types" } +ethereum-types = "0.6" +ethjson = { path = "../../json" } +ethtrie = { package = "patricia-trie-ethereum", path = "../../util/patricia-trie-ethereum" } +hash-db = "0.12" +itertools = "0.8" +keccak-hash = "0.2.0" +keccak-hasher = { path = "../../util/keccak-hasher" } +kvdb = "0.1" +log = "0.4" +parity-bytes = "0.1.0" +rlp = "0.4" +rustc-hex = "1" +serde = { version = "1.0", features = ["derive"] } +state-account = { path = "../state-account" } +trie-db = "0.12.4" +triehash = { package = "triehash-ethereum", version = "0.2", path = "../../util/triehash-ethereum" } diff --git a/ethcore/pod-account/src/lib.rs b/ethcore/pod-account/src/lib.rs new file mode 100644 index 00000000000..b072758e4b7 --- /dev/null +++ b/ethcore/pod-account/src/lib.rs @@ -0,0 +1,256 @@ +// 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 . + +//! Account system expressed in Plain Old Data. +use log::warn; +use std::collections::BTreeMap; +use itertools::Itertools; +use keccak_hash::keccak; +use ethereum_types::{H256, U256, BigEndianHash}; +use hash_db::HashDB; +use kvdb::DBValue; +use keccak_hasher::KeccakHasher; +use triehash::sec_trie_root; +use parity_bytes::Bytes; +use trie_db::TrieFactory; +use ethtrie::RlpCodec; +use state_account::Account; +use ethjson; +use common_types::account_diff::*; +use rlp::{self, RlpStream}; +use serde::{Serializer, Serialize}; +use rustc_hex::ToHex; + +#[derive(Debug, Clone, PartialEq, Eq, Serialize)] +/// An account, expressed as Plain-Old-Data (hence the name). +/// Does not have a DB overlay cache, code hash or anything like that. +pub struct PodAccount { + /// The balance of the account. + pub balance: U256, + /// The nonce of the account. + pub nonce: U256, + #[serde(serialize_with="opt_bytes_to_hex")] + /// The code of the account or `None` in the special case that it is unknown. + pub code: Option, + /// The storage of the account. + pub storage: BTreeMap, +} + +fn opt_bytes_to_hex(opt_bytes: &Option, serializer: S) -> Result + where S: Serializer +{ + let readable = opt_bytes.as_ref().map(|b| b.to_hex()).unwrap_or_default(); + serializer.collect_str(&format_args!("0x{}", readable)) +} + +impl PodAccount { + /// Convert Account to a PodAccount. + /// NOTE: This will silently fail unless the account is fully cached. + pub fn from_account(acc: &Account) -> PodAccount { + PodAccount { + balance: *acc.balance(), + nonce: *acc.nonce(), + storage: acc.storage_changes().iter().fold(BTreeMap::new(), |mut m, (k, v)| {m.insert(k.clone(), v.clone()); m}), + code: acc.code().map(|x| x.to_vec()), + } + } + + /// Returns the RLP for this account. + pub fn rlp(&self) -> Bytes { + let mut stream = RlpStream::new_list(4); + stream.append(&self.nonce); + stream.append(&self.balance); + stream.append(&sec_trie_root(self.storage.iter().map(|(k, v)| (k, rlp::encode(&v.into_uint()))))); + stream.append(&keccak(&self.code.as_ref().unwrap_or(&vec![]))); + stream.out() + } + + /// Place additional data into given hash DB. + pub fn insert_additional(&self, db: &mut dyn HashDB, factory: &TrieFactory) { + match self.code { + Some(ref c) if !c.is_empty() => { db.insert(hash_db::EMPTY_PREFIX, c); } + _ => {} + } + let mut r = H256::zero(); + let mut t = factory.create(db, &mut r); + for (k, v) in &self.storage { + if let Err(e) = t.insert(k.as_bytes(), &rlp::encode(&v.into_uint())) { + warn!("Encountered potential DB corruption: {}", e); + } + } + } +} + +impl From for PodAccount { + fn from(a: ethjson::blockchain::Account) -> Self { + PodAccount { + balance: a.balance.into(), + nonce: a.nonce.into(), + code: Some(a.code.into()), + storage: a.storage.into_iter().map(|(key, value)| { + let key: U256 = key.into(); + let value: U256 = value.into(); + (BigEndianHash::from_uint(&key), BigEndianHash::from_uint(&value)) + }).collect(), + } + } +} + +impl From for PodAccount { + fn from(a: ethjson::spec::Account) -> Self { + PodAccount { + balance: a.balance.map_or_else(U256::zero, Into::into), + nonce: a.nonce.map_or_else(U256::zero, Into::into), + code: Some(a.code.map_or_else(Vec::new, Into::into)), + storage: a.storage.map_or_else(BTreeMap::new, |s| s.into_iter().map(|(key, value)| { + let key: U256 = key.into(); + let value: U256 = value.into(); + (BigEndianHash::from_uint(&key), BigEndianHash::from_uint(&value)) + }).collect()), + } + } +} + +/// Determine difference between two optionally existant `Account`s. Returns None +/// if they are the same. +pub fn diff_pod(pre: Option<&PodAccount>, post: Option<&PodAccount>) -> Option { + match (pre, post) { + (None, Some(x)) => Some(AccountDiff { + balance: Diff::Born(x.balance), + nonce: Diff::Born(x.nonce), + code: Diff::Born(x.code.as_ref().expect("account is newly created; newly created accounts must be given code; all caches should remain in place; qed").clone()), + storage: x.storage.iter().map(|(k, v)| (k.clone(), Diff::Born(v.clone()))).collect(), + }), + (Some(x), None) => Some(AccountDiff { + balance: Diff::Died(x.balance), + nonce: Diff::Died(x.nonce), + code: Diff::Died(x.code.as_ref().expect("account is deleted; only way to delete account is running SUICIDE; account must have had own code cached to make operation; all caches should remain in place; qed").clone()), + storage: x.storage.iter().map(|(k, v)| (k.clone(), Diff::Died(v.clone()))).collect(), + }), + (Some(pre), Some(post)) => { + let storage: Vec<_> = pre.storage.keys().merge(post.storage.keys()) + .filter(|k| pre.storage.get(k).unwrap_or(&H256::zero()) != post.storage.get(k).unwrap_or(&H256::zero())) + .collect(); + let r = AccountDiff { + balance: Diff::new(pre.balance, post.balance), + nonce: Diff::new(pre.nonce, post.nonce), + code: match (pre.code.clone(), post.code.clone()) { + (Some(pre_code), Some(post_code)) => Diff::new(pre_code, post_code), + _ => Diff::Same, + }, + storage: storage.into_iter().map(|k| + (k.clone(), Diff::new( + pre.storage.get(k).cloned().unwrap_or_else(H256::zero), + post.storage.get(k).cloned().unwrap_or_else(H256::zero) + ))).collect(), + }; + if r.balance.is_same() && r.nonce.is_same() && r.code.is_same() && r.storage.is_empty() { + None + } else { + Some(r) + } + }, + _ => None, + } +} + +#[cfg(test)] +mod test { + use std::collections::BTreeMap; + use types::account_diff::*; + use super::{PodAccount, diff_pod}; + use ethereum_types::H256; + + #[test] + fn existence() { + let a = PodAccount{balance: 69.into(), nonce: 0.into(), code: Some(vec![]), storage: map![]}; + assert_eq!(diff_pod(Some(&a), Some(&a)), None); + assert_eq!(diff_pod(None, Some(&a)), Some(AccountDiff{ + balance: Diff::Born(69.into()), + nonce: Diff::Born(0.into()), + code: Diff::Born(vec![]), + storage: map![], + })); + } + + #[test] + fn basic() { + let a = PodAccount{balance: 69.into(), nonce: 0.into(), code: Some(vec![]), storage: map![]}; + let b = PodAccount{balance: 42.into(), nonce: 1.into(), code: Some(vec![]), storage: map![]}; + assert_eq!(diff_pod(Some(&a), Some(&b)), Some(AccountDiff { + balance: Diff::Changed(69.into(), 42.into()), + nonce: Diff::Changed(0.into(), 1.into()), + code: Diff::Same, + storage: map![], + })); + } + + #[test] + fn code() { + let a = PodAccount{balance: 0.into(), nonce: 0.into(), code: Some(vec![]), storage: map![]}; + let b = PodAccount{balance: 0.into(), nonce: 1.into(), code: Some(vec![0]), storage: map![]}; + assert_eq!(diff_pod(Some(&a), Some(&b)), Some(AccountDiff { + balance: Diff::Same, + nonce: Diff::Changed(0.into(), 1.into()), + code: Diff::Changed(vec![], vec![0]), + storage: map![], + })); + } + + #[test] + fn storage() { + let a = PodAccount { + balance: 0.into(), + nonce: 0.into(), + code: Some(vec![]), + storage: map![ + H256::from_low_u64_be(1) => H256::from_low_u64_be(1), + H256::from_low_u64_be(2) => H256::from_low_u64_be(2), + H256::from_low_u64_be(3) => H256::from_low_u64_be(3), + H256::from_low_u64_be(4) => H256::from_low_u64_be(4), + H256::from_low_u64_be(5) => H256::from_low_u64_be(0), + H256::from_low_u64_be(6) => H256::from_low_u64_be(0), + H256::from_low_u64_be(7) => H256::from_low_u64_be(0) + ], + }; + let b = PodAccount { + balance: 0.into(), + nonce: 0.into(), + code: Some(vec![]), + storage: map![ + H256::from_low_u64_be(1) => H256::from_low_u64_be(1), + H256::from_low_u64_be(2) => H256::from_low_u64_be(3), + H256::from_low_u64_be(3) => H256::from_low_u64_be(0), + H256::from_low_u64_be(5) => H256::from_low_u64_be(0), + H256::from_low_u64_be(7) => H256::from_low_u64_be(7), + H256::from_low_u64_be(8) => H256::from_low_u64_be(0), + H256::from_low_u64_be(9) => H256::from_low_u64_be(9) + ] + }; + assert_eq!(diff_pod(Some(&a), Some(&b)), Some(AccountDiff { + balance: Diff::Same, + nonce: Diff::Same, + code: Diff::Same, + storage: map![ + H256::from_low_u64_be(2) => Diff::new(H256::from_low_u64_be(2), H256::from_low_u64_be(3)), + H256::from_low_u64_be(3) => Diff::new(H256::from_low_u64_be(3), H256::from_low_u64_be(0)), + H256::from_low_u64_be(4) => Diff::new(H256::from_low_u64_be(4), H256::from_low_u64_be(0)), + H256::from_low_u64_be(7) => Diff::new(H256::from_low_u64_be(0), H256::from_low_u64_be(7)), + H256::from_low_u64_be(9) => Diff::new(H256::from_low_u64_be(0), H256::from_low_u64_be(9)) + ], + })); + } +} diff --git a/ethcore/state-account/Cargo.toml b/ethcore/state-account/Cargo.toml new file mode 100644 index 00000000000..084f1bbe77b --- /dev/null +++ b/ethcore/state-account/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "state-account" +version = "0.1.0" +authors = ["David Palm "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +common-types = { path = "../types"} +ethcore-error = { path = "../ethcore-error" } +ethereum-types = "0.6.0" +ethtrie = { package = "patricia-trie-ethereum", path = "../../util/patricia-trie-ethereum" } +hash-db = "0.12.4" +keccak-hash = "0.2.0" +keccak-hasher = { path = "../../util/keccak-hasher" } +kvdb = "0.1.0" +log = "0.4" +lru-cache = "0.1.2" +parity-bytes = "0.1.0" +rlp = "0.4.0" +serde = { version = "1.0", features = ["derive"] } +trie-db = "0.12.4" diff --git a/ethcore/state-account/src/lib.rs b/ethcore/state-account/src/lib.rs new file mode 100644 index 00000000000..63b7bcb0f3e --- /dev/null +++ b/ethcore/state-account/src/lib.rs @@ -0,0 +1,747 @@ +// 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 . + +//! Single account in the system. +use log::{warn, trace}; +use std::fmt; +use std::sync::Arc; +use std::collections::{HashMap, BTreeMap}; +use keccak_hash::{KECCAK_EMPTY, KECCAK_NULL_RLP, keccak}; +use ethereum_types::{H256, U256, Address, BigEndianHash}; +use ethcore_error::Error; +use hash_db::HashDB; +use keccak_hasher::KeccakHasher; +use kvdb::DBValue; +use parity_bytes::{Bytes, ToPretty}; +use trie_db::{Trie, Recorder}; +use ethtrie::{TrieFactory, TrieDB, SecTrieDB, Result as TrieResult}; +// TODO: sort out the cyclic dependency here +//use pod_account::PodAccount; +use rlp::{RlpStream, encode}; +use lru_cache::LruCache; +use common_types::basic_account::BasicAccount; + +use std::cell::{RefCell, Cell}; + +const STORAGE_CACHE_ITEMS: usize = 8192; + +/// Boolean type for clean/dirty status. +#[derive(PartialEq, Eq, Clone, Copy, Debug)] +pub enum Filth { + /// Data has not been changed. + Clean, + /// Data has been changed. + Dirty, +} + +/// Single account in the system. +/// Keeps track of changes to the code and storage. +/// The changes are applied in `commit_storage` and `commit_code` +pub struct Account { + // Balance of the account. + balance: U256, + // Nonce of the account. + nonce: U256, + // Trie-backed storage. + storage_root: H256, + // LRU Cache of the trie-backed storage. + // This is limited to `STORAGE_CACHE_ITEMS` recent queries + storage_cache: RefCell>, + // LRU Cache of the trie-backed storage for original value. + // This is only used when the initial storage root is different compared to + // what is in the database. That is, it is only used for new contracts. + original_storage_cache: Option<(H256, RefCell>)>, + // Modified storage. Accumulates changes to storage made in `set_storage` + // Takes precedence over `storage_cache`. + storage_changes: HashMap, + // Code hash of the account. + code_hash: H256, + // Size of the account code. + code_size: Option, + // Code cache of the account. + code_cache: Arc, + // Account code new or has been modified. + code_filth: Filth, + // Cached address hash. + address_hash: Cell>, +} + +impl From for Account { + fn from(basic: BasicAccount) -> Self { + Account { + balance: basic.balance, + nonce: basic.nonce, + storage_root: basic.storage_root, + storage_cache: Self::empty_storage_cache(), + original_storage_cache: None, + storage_changes: HashMap::new(), + code_hash: basic.code_hash, + code_size: None, + code_cache: Arc::new(vec![]), + code_filth: Filth::Clean, + address_hash: Cell::new(None), + } + } +} + +impl Account { + #[cfg(test)] + /// General constructor. + pub fn new(balance: U256, nonce: U256, storage: HashMap, code: Bytes) -> Account { + Account { + balance: balance, + nonce: nonce, + storage_root: KECCAK_NULL_RLP, + storage_cache: Self::empty_storage_cache(), + original_storage_cache: None, + storage_changes: storage, + code_hash: keccak(&code), + code_size: Some(code.len()), + code_cache: Arc::new(code), + code_filth: Filth::Dirty, + address_hash: Cell::new(None), + } + } + + fn empty_storage_cache() -> RefCell> { + RefCell::new(LruCache::new(STORAGE_CACHE_ITEMS)) + } + + // TODO: sort out which should be depending on which +// /// General constructor. +// pub fn from_pod(pod: PodAccount) -> Account { +// Account { +// balance: pod.balance, +// nonce: pod.nonce, +// storage_root: KECCAK_NULL_RLP, +// storage_cache: Self::empty_storage_cache(), +// original_storage_cache: None, +// storage_changes: pod.storage.into_iter().collect(), +// code_hash: pod.code.as_ref().map_or(KECCAK_EMPTY, |c| keccak(c)), +// code_filth: Filth::Dirty, +// code_size: Some(pod.code.as_ref().map_or(0, |c| c.len())), +// code_cache: Arc::new(pod.code.map_or_else(|| { warn!("POD account with unknown code is being created! Assuming no code."); vec![] }, |c| c)), +// address_hash: Cell::new(None), +// } +// } + + /// Create a new account with the given balance. + pub fn new_basic(balance: U256, nonce: U256) -> Account { + Account { + balance: balance, + nonce: nonce, + storage_root: KECCAK_NULL_RLP, + storage_cache: Self::empty_storage_cache(), + original_storage_cache: None, + storage_changes: HashMap::new(), + code_hash: KECCAK_EMPTY, + code_cache: Arc::new(vec![]), + code_size: Some(0), + code_filth: Filth::Clean, + address_hash: Cell::new(None), + } + } + + /// Create a new account from RLP. + pub fn from_rlp(rlp: &[u8]) -> Result { + ::rlp::decode::(rlp) + .map(|ba| ba.into()) + .map_err(|e| e.into()) + } + + /// Create a new contract account. + /// NOTE: make sure you use `init_code` on this before `commit`ing. + pub fn new_contract(balance: U256, nonce: U256, original_storage_root: H256) -> Account { + Account { + balance: balance, + nonce: nonce, + storage_root: KECCAK_NULL_RLP, + storage_cache: Self::empty_storage_cache(), + original_storage_cache: if original_storage_root == KECCAK_NULL_RLP { + None + } else { + Some((original_storage_root, Self::empty_storage_cache())) + }, + storage_changes: HashMap::new(), + code_hash: KECCAK_EMPTY, + code_cache: Arc::new(vec![]), + code_size: None, + code_filth: Filth::Clean, + address_hash: Cell::new(None), + } + } + + /// Set this account's code to the given code. + /// NOTE: Account should have been created with `new_contract()` + pub fn init_code(&mut self, code: Bytes) { + self.code_hash = keccak(&code); + self.code_cache = Arc::new(code); + self.code_size = Some(self.code_cache.len()); + self.code_filth = Filth::Dirty; + } + + /// Reset this account's code to the given code. + pub fn reset_code(&mut self, code: Bytes) { + self.init_code(code); + } + + /// Reset this account's code and storage to given values. + pub fn reset_code_and_storage(&mut self, code: Arc, storage: HashMap) { + self.code_hash = keccak(&*code); + self.code_cache = code; + self.code_size = Some(self.code_cache.len()); + self.code_filth = Filth::Dirty; + self.storage_cache = Self::empty_storage_cache(); + self.storage_changes = storage; + if self.storage_root != KECCAK_NULL_RLP { + self.original_storage_cache = Some((self.storage_root, Self::empty_storage_cache())); + } + self.storage_root = KECCAK_NULL_RLP; + } + + /// Set (and cache) the contents of the trie's storage at `key` to `value`. + pub fn set_storage(&mut self, key: H256, value: H256) { + self.storage_changes.insert(key, value); + } + + /// Get (and cache) the contents of the trie's storage at `key`. + /// Takes modified storage into account. + pub fn storage_at(&self, db: &dyn HashDB, key: &H256) -> TrieResult { + if let Some(value) = self.cached_storage_at(key) { + return Ok(value); + } + Self::get_and_cache_storage( + &self.storage_root, + &mut self.storage_cache.borrow_mut(), + db, + key) + } + + /// Get (and cache) the contents of the trie's storage at `key`. + /// Does not take modified storage into account. + pub fn original_storage_at(&self, db: &dyn HashDB, key: &H256) -> TrieResult { + if let Some(value) = self.cached_original_storage_at(key) { + return Ok(value); + } + match &self.original_storage_cache { + Some((ref original_storage_root, ref original_storage_cache)) => + Self::get_and_cache_storage( + original_storage_root, + &mut original_storage_cache.borrow_mut(), + db, + key + ), + None => + Self::get_and_cache_storage( + &self.storage_root, + &mut self.storage_cache.borrow_mut(), + db, + key + ), + } + } + + fn get_and_cache_storage(storage_root: &H256, storage_cache: &mut LruCache, db: &dyn HashDB, key: &H256) -> TrieResult { + let db = SecTrieDB::new(&db, storage_root)?; + let panicky_decoder = |bytes:&[u8]| ::rlp::decode(&bytes).expect("decoding db value failed"); + let item: U256 = db.get_with(key.as_bytes(), panicky_decoder)?.unwrap_or_else(U256::zero); + let value: H256 = BigEndianHash::from_uint(&item); + storage_cache.insert(key.clone(), value.clone()); + Ok(value) + } + + /// Get cached storage value if any. Returns `None` if the + /// key is not in the cache. + pub fn cached_storage_at(&self, key: &H256) -> Option { + if let Some(value) = self.storage_changes.get(key) { + return Some(value.clone()) + } + self.cached_moved_original_storage_at(key) + } + + /// Get cached original storage value after last state commitment. Returns `None` if the key is not in the cache. + pub fn cached_original_storage_at(&self, key: &H256) -> Option { + match &self.original_storage_cache { + Some((_, ref original_storage_cache)) => { + if let Some(value) = original_storage_cache.borrow_mut().get_mut(key) { + Some(value.clone()) + } else { + None + } + }, + None => { + self.cached_moved_original_storage_at(key) + }, + } + } + + /// Get cached original storage value since last contract creation on this address. Returns `None` if the key is not in the cache. + fn cached_moved_original_storage_at(&self, key: &H256) -> Option { + // If storage root is empty RLP, then early return zero value. Practically, this makes it so that if + // `original_storage_cache` is used, then `storage_cache` will always remain empty. + if self.storage_root == KECCAK_NULL_RLP { + return Some(H256::zero()); + } + + if let Some(value) = self.storage_cache.borrow_mut().get_mut(key) { + Some(value.clone()) + } else { + None + } + } + + /// return the balance associated with this account. + pub fn balance(&self) -> &U256 { &self.balance } + + /// return the nonce associated with this account. + pub fn nonce(&self) -> &U256 { &self.nonce } + + /// return the code hash associated with this account. + pub fn code_hash(&self) -> H256 { + self.code_hash.clone() + } + + /// return and cache `keccak(address)`, `address` must be the address of this + /// account. + pub fn address_hash(&self, address: &Address) -> H256 { + let hash = self.address_hash.get(); + hash.unwrap_or_else(|| { + let hash = keccak(address); + self.address_hash.set(Some(hash.clone())); + hash + }) + } + + /// returns the account's code. If `None` then the code cache isn't available - + /// get someone who knows to call `note_code`. + pub fn code(&self) -> Option> { + if self.code_hash != KECCAK_EMPTY && self.code_cache.is_empty() { + return None; + } + Some(self.code_cache.clone()) + } + + /// returns the account's code size. If `None` then the code cache or code size cache isn't available - + /// get someone who knows to call `note_code`. + pub fn code_size(&self) -> Option { + self.code_size.clone() + } + + #[cfg(test)] + /// Provide a byte array which hashes to the `code_hash`. returns the hash as a result. + pub fn note_code(&mut self, code: Bytes) -> Result<(), H256> { + let h = keccak(&code); + if self.code_hash == h { + self.code_cache = Arc::new(code); + self.code_size = Some(self.code_cache.len()); + Ok(()) + } else { + Err(h) + } + } + + /// Is `code_cache` valid; such that code is going to return Some? + pub fn is_cached(&self) -> bool { + !self.code_cache.is_empty() || (self.code_cache.is_empty() && self.code_hash == KECCAK_EMPTY) + } + + /// Provide a database to get `code_hash`. Should not be called if it is a contract without code. Returns the cached code, if successful. + #[must_use] + pub fn cache_code(&mut self, db: &dyn HashDB) -> Option> { + // TODO: fill out self.code_cache; + trace!("Account::cache_code: ic={}; self.code_hash={:?}, self.code_cache={}", self.is_cached(), self.code_hash, self.code_cache.pretty()); + + if self.is_cached() { return Some(self.code_cache.clone()); } + + match db.get(&self.code_hash, hash_db::EMPTY_PREFIX) { + Some(x) => { + self.code_size = Some(x.len()); + self.code_cache = Arc::new(x.into_vec()); + Some(self.code_cache.clone()) + }, + _ => { + warn!("Failed reverse get of {}", self.code_hash); + None + }, + } + } + + /// Provide code to cache. For correctness, should be the correct code for the account. + pub fn cache_given_code(&mut self, code: Arc) { + trace!("Account::cache_given_code: ic={}; self.code_hash={:?}, self.code_cache={}", self.is_cached(), self.code_hash, self.code_cache.pretty()); + + self.code_size = Some(code.len()); + self.code_cache = code; + } + + /// Provide a database to get `code_size`. Should not be called if it is a contract without code. Returns whether + /// the cache succeeds. + #[must_use] + pub fn cache_code_size(&mut self, db: &dyn HashDB) -> bool { + // TODO: fill out self.code_cache; + trace!("Account::cache_code_size: ic={}; self.code_hash={:?}, self.code_cache={}", self.is_cached(), self.code_hash, self.code_cache.pretty()); + self.code_size.is_some() || + if self.code_hash != KECCAK_EMPTY { + match db.get(&self.code_hash, hash_db::EMPTY_PREFIX) { + Some(x) => { + self.code_size = Some(x.len()); + true + }, + _ => { + warn!("Failed reverse get of {}", self.code_hash); + false + }, + } + } else { + // If the code hash is empty hash, then the code size is zero. + self.code_size = Some(0); + true + } + } + + /// Determine whether there are any un-`commit()`-ed storage-setting operations. + pub fn storage_is_clean(&self) -> bool { self.storage_changes.is_empty() } + + /// Check if account has zero nonce, balance, no code and no storage. + /// + /// NOTE: Will panic if `!self.storage_is_clean()` + pub fn is_empty(&self) -> bool { + assert!(self.storage_is_clean(), "Account::is_empty() may only legally be called when storage is clean."); + self.is_null() && self.storage_root == KECCAK_NULL_RLP + } + + /// Check if account has zero nonce, balance, no code. + pub fn is_null(&self) -> bool { + self.balance.is_zero() && + self.nonce.is_zero() && + self.code_hash == KECCAK_EMPTY + } + + /// Check if account is basic (Has no code). + pub fn is_basic(&self) -> bool { + self.code_hash == KECCAK_EMPTY + } + + /// Return the storage root associated with this account or None if it has been altered via the overlay. + pub fn storage_root(&self) -> Option { + if self.storage_is_clean() { + Some(self.storage_root) + } else { + None + } + } + + /// Return the original storage root of this account. + pub fn original_storage_root(&self) -> H256 { + if let Some((original_storage_root, _)) = self.original_storage_cache { + original_storage_root + } else { + self.storage_root + } + } + + /// Whether the base storage root of this account is unchanged. + pub fn is_base_storage_root_unchanged(&self) -> bool { + self.original_storage_cache.is_none() + } + + /// Storage root where the account changes are based upon. + pub fn base_storage_root(&self) -> H256 { + self.storage_root + } + + /// Return the storage overlay. + pub fn storage_changes(&self) -> &HashMap { &self.storage_changes } + + /// Increment the nonce of the account by one. + pub fn inc_nonce(&mut self) { + self.nonce = self.nonce.saturating_add(U256::from(1u8)); + } + + /// Increase account balance. + pub fn add_balance(&mut self, x: &U256) { + self.balance = self.balance.saturating_add(*x); + } + + /// Decrease account balance. + /// Panics if balance is less than `x` + pub fn sub_balance(&mut self, x: &U256) { + assert!(self.balance >= *x); + self.balance = self.balance - *x; + } + + /// Commit the `storage_changes` to the backing DB and update `storage_root`. + pub fn commit_storage(&mut self, trie_factory: &TrieFactory, db: &mut dyn HashDB) -> TrieResult<()> { + let mut t = trie_factory.from_existing(db, &mut self.storage_root)?; + for (k, v) in self.storage_changes.drain() { + // cast key and value to trait type, + // so we can call overloaded `to_bytes` method + match v.is_zero() { + true => t.remove(k.as_bytes())?, + false => t.insert(k.as_bytes(), &encode(&v.into_uint()))?, + }; + + self.storage_cache.borrow_mut().insert(k, v); + } + self.original_storage_cache = None; + Ok(()) + } + + /// Commit any unsaved code. `code_hash` will always return the hash of the `code_cache` after this. + pub fn commit_code(&mut self, db: &mut dyn HashDB) { + trace!("Commiting code of {:?} - {:?}, {:?}", self, self.code_filth == Filth::Dirty, self.code_cache.is_empty()); + match (self.code_filth == Filth::Dirty, self.code_cache.is_empty()) { + (true, true) => { + self.code_size = Some(0); + self.code_filth = Filth::Clean; + }, + (true, false) => { + db.emplace(self.code_hash.clone(), hash_db::EMPTY_PREFIX, DBValue::from_slice(&*self.code_cache)); + self.code_size = Some(self.code_cache.len()); + self.code_filth = Filth::Clean; + }, + (false, _) => {}, + } + } + + /// Export to RLP. + pub fn rlp(&self) -> Bytes { + let mut stream = RlpStream::new_list(4); + stream.append(&self.nonce); + stream.append(&self.balance); + stream.append(&self.storage_root); + stream.append(&self.code_hash); + stream.out() + } + + /// Clone basic account data + pub fn clone_basic(&self) -> Account { + Account { + balance: self.balance.clone(), + nonce: self.nonce.clone(), + storage_root: self.storage_root.clone(), + storage_cache: Self::empty_storage_cache(), + original_storage_cache: self.original_storage_cache.as_ref().map(|(r, _)| (*r, Self::empty_storage_cache())), + storage_changes: HashMap::new(), + code_hash: self.code_hash.clone(), + code_size: self.code_size.clone(), + code_cache: self.code_cache.clone(), + code_filth: self.code_filth, + address_hash: self.address_hash.clone(), + } + } + + /// Clone account data and dirty storage keys + pub fn clone_dirty(&self) -> Account { + let mut account = self.clone_basic(); + account.storage_changes = self.storage_changes.clone(); + account + } + + /// Clone account data, dirty storage keys and cached storage keys. + pub fn clone_all(&self) -> Account { + let mut account = self.clone_dirty(); + account.storage_cache = self.storage_cache.clone(); + account.original_storage_cache = self.original_storage_cache.clone(); + account + } + + /// Replace self with the data from other account merging storage cache. + /// Basic account data and all modifications are overwritten + /// with new values. + pub fn overwrite_with(&mut self, other: Account) { + self.balance = other.balance; + self.nonce = other.nonce; + self.code_hash = other.code_hash; + self.code_filth = other.code_filth; + self.code_cache = other.code_cache; + self.code_size = other.code_size; + self.address_hash = other.address_hash; + if self.storage_root == other.storage_root { + let mut cache = self.storage_cache.borrow_mut(); + for (k, v) in other.storage_cache.into_inner() { + cache.insert(k, v); + } + } else { + self.storage_cache = other.storage_cache; + } + self.original_storage_cache = other.original_storage_cache; + self.storage_root = other.storage_root; + self.storage_changes = other.storage_changes; + } +} + +// light client storage proof. +impl Account { + /// Prove a storage key's existence or nonexistence in the account's storage + /// trie. + /// `storage_key` is the hash of the desired storage key, meaning + /// this will only work correctly under a secure trie. + pub fn prove_storage(&self, db: &dyn HashDB, storage_key: H256) -> TrieResult<(Vec, H256)> { + let mut recorder = Recorder::new(); + + let trie = TrieDB::new(&db, &self.storage_root)?; + let item: U256 = { + let panicky_decoder = |bytes:&[u8]| ::rlp::decode(bytes).expect("decoding db value failed"); + let query = (&mut recorder, panicky_decoder); + trie.get_with(storage_key.as_bytes(), query)?.unwrap_or_else(U256::zero) + }; + + Ok((recorder.drain().into_iter().map(|r| r.data).collect(), BigEndianHash::from_uint(&item))) + } +} + +impl fmt::Debug for Account { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("Account") + .field("balance", &self.balance) + .field("nonce", &self.nonce) + .field("code", &self.code()) + .field("storage", &self.storage_changes.iter().collect::>()) + .finish() + } +} + +#[cfg(test)] +mod tests { + use rlp_compress::{compress, decompress, snapshot_swapper}; + use ethereum_types::{H256, Address}; + use journaldb::new_memory_db; + use bytes::Bytes; + use super::*; + use account_db::*; + use std::str::FromStr; + + #[test] + fn account_compress() { + let raw = Account::new_basic(2.into(), 4.into()).rlp(); + let compact_vec = compress(&raw, snapshot_swapper()); + assert!(raw.len() > compact_vec.len()); + let again_raw = decompress(&compact_vec, snapshot_swapper()); + assert_eq!(raw, again_raw.into_vec()); + } + + #[test] + fn storage_at() { + let mut db = new_memory_db(); + let mut db = AccountDBMut::new(&mut db, &Address::zero()); + let rlp = { + let mut a = Account::new_contract(69.into(), 0.into(), KECCAK_NULL_RLP); + a.set_storage(H256::zero(), H256::from_low_u64_be(0x1234)); + a.commit_storage(&Default::default(), &mut db).unwrap(); + a.init_code(vec![]); + a.commit_code(&mut db); + a.rlp() + }; + + let a = Account::from_rlp(&rlp).expect("decoding db value failed"); + assert_eq!(a.storage_root().unwrap(), H256::from_str("c57e1afb758b07f8d2c8f13a3b6e44fa5ff94ab266facc5a4fd3f062426e50b2").unwrap()); + assert_eq!(a.storage_at(&db.immutable(), &H256::zero()).unwrap(), H256::from_low_u64_be(0x1234)); + assert_eq!(a.storage_at(&db.immutable(), &H256::from_low_u64_be(0x01)).unwrap(), H256::zero()); + } + + #[test] + fn note_code() { + let mut db = new_memory_db(); + let mut db = AccountDBMut::new(&mut db, &Address::zero()); + + let rlp = { + let mut a = Account::new_contract(69.into(), 0.into(), KECCAK_NULL_RLP); + a.init_code(vec![0x55, 0x44, 0xffu8]); + a.commit_code(&mut db); + a.rlp() + }; + + let mut a = Account::from_rlp(&rlp).expect("decoding db value failed"); + assert!(a.cache_code(&db.immutable()).is_some()); + + let mut a = Account::from_rlp(&rlp).expect("decoding db value failed"); + assert_eq!(a.note_code(vec![0x55, 0x44, 0xffu8]), Ok(())); + } + + #[test] + fn commit_storage() { + let mut a = Account::new_contract(69.into(), 0.into(), KECCAK_NULL_RLP); + let mut db = new_memory_db(); + let mut db = AccountDBMut::new(&mut db, &Address::zero()); + a.set_storage(H256::from_low_u64_be(0), H256::from_low_u64_be(0x1234)); + assert_eq!(a.storage_root(), None); + a.commit_storage(&Default::default(), &mut db).unwrap(); + assert_eq!(a.storage_root().unwrap(), H256::from_str("c57e1afb758b07f8d2c8f13a3b6e44fa5ff94ab266facc5a4fd3f062426e50b2").unwrap()); + } + + #[test] + fn commit_remove_commit_storage() { + let mut a = Account::new_contract(69.into(), 0.into(), KECCAK_NULL_RLP); + let mut db = new_memory_db(); + let mut db = AccountDBMut::new(&mut db, &Address::zero()); + a.set_storage(H256::from_low_u64_be(0), H256::from_low_u64_be(0x1234)); + a.commit_storage(&Default::default(), &mut db).unwrap(); + a.set_storage(H256::from_low_u64_be(1), H256::from_low_u64_be(0x1234)); + a.commit_storage(&Default::default(), &mut db).unwrap(); + a.set_storage(H256::from_low_u64_be(1), H256::from_low_u64_be(0)); + a.commit_storage(&Default::default(), &mut db).unwrap(); + assert_eq!(a.storage_root().unwrap(), H256::from_str("c57e1afb758b07f8d2c8f13a3b6e44fa5ff94ab266facc5a4fd3f062426e50b2").unwrap()); + } + + #[test] + fn commit_code() { + let mut a = Account::new_contract(69.into(), 0.into(), KECCAK_NULL_RLP); + let mut db = new_memory_db(); + let mut db = AccountDBMut::new(&mut db, &Address::zero()); + a.init_code(vec![0x55, 0x44, 0xffu8]); + assert_eq!(a.code_filth, Filth::Dirty); + assert_eq!(a.code_size(), Some(3)); + a.commit_code(&mut db); + assert_eq!(a.code_hash(), H256::from_str("af231e631776a517ca23125370d542873eca1fb4d613ed9b5d5335a46ae5b7eb").unwrap()); + } + + #[test] + fn reset_code() { + let mut a = Account::new_contract(69.into(), 0.into(), KECCAK_NULL_RLP); + let mut db = new_memory_db(); + let mut db = AccountDBMut::new(&mut db, &Address::zero()); + a.init_code(vec![0x55, 0x44, 0xffu8]); + assert_eq!(a.code_filth, Filth::Dirty); + a.commit_code(&mut db); + assert_eq!(a.code_filth, Filth::Clean); + assert_eq!(a.code_hash(), H256::from_str("af231e631776a517ca23125370d542873eca1fb4d613ed9b5d5335a46ae5b7eb").unwrap()); + a.reset_code(vec![0x55]); + assert_eq!(a.code_filth, Filth::Dirty); + a.commit_code(&mut db); + assert_eq!(a.code_hash(), H256::from_str("37bf2238b11b68cdc8382cece82651b59d3c3988873b6e0f33d79694aa45f1be").unwrap()); + } + + #[test] + fn rlpio() { + let a = Account::new(69u8.into(), 0u8.into(), HashMap::new(), Bytes::new()); + let b = Account::from_rlp(&a.rlp()).unwrap(); + assert_eq!(a.balance(), b.balance()); + assert_eq!(a.nonce(), b.nonce()); + assert_eq!(a.code_hash(), b.code_hash()); + assert_eq!(a.storage_root(), b.storage_root()); + } + + #[test] + fn new_account() { + let a = Account::new(69u8.into(), 0u8.into(), HashMap::new(), Bytes::new()); + assert_eq!(a.rlp().to_hex(), "f8448045a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a0c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"); + assert_eq!(*a.balance(), 69u8.into()); + assert_eq!(*a.nonce(), 0u8.into()); + assert_eq!(a.code_hash(), KECCAK_EMPTY); + assert_eq!(a.storage_root().unwrap(), KECCAK_NULL_RLP); + } +} From c8bf87ff7b88d2520d265a1148e256b6caa2a532 Mon Sep 17 00:00:00 2001 From: David Palm Date: Thu, 4 Jul 2019 13:06:58 +0200 Subject: [PATCH 02/87] Sort out dependencies, fix broken code and tests Remove botched ethcore-error crate --- Cargo.lock | 25 +- Cargo.toml | 5 - ethcore/Cargo.toml | 2 + ethcore/ethcore-error/Cargo.toml | 20 - ethcore/ethcore-error/src/lib.rs | 303 ------------- ethcore/pod-account/Cargo.toml | 9 +- ethcore/pod-account/src/lib.rs | 15 +- ethcore/src/account_db.rs | 9 +- ethcore/src/lib.rs | 7 +- ethcore/src/pod_account.rs | 256 ----------- ethcore/src/state/account.rs | 745 ------------------------------- ethcore/src/state/mod.rs | 7 +- ethcore/state-account/Cargo.toml | 11 +- ethcore/state-account/src/lib.rs | 72 +-- 14 files changed, 75 insertions(+), 1411 deletions(-) delete mode 100644 ethcore/ethcore-error/Cargo.toml delete mode 100644 ethcore/ethcore-error/src/lib.rs delete mode 100644 ethcore/src/pod_account.rs delete mode 100644 ethcore/src/state/account.rs diff --git a/Cargo.lock b/Cargo.lock index 7f18806d1ba..d5c2ba0d420 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -914,6 +914,7 @@ dependencies = [ "parity-util-mem 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "patricia-trie-ethereum 0.1.0", + "pod-account 0.1.0", "rand 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", "rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "rayon 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -924,6 +925,7 @@ dependencies = [ "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", + "state-account 0.1.0", "stats 0.1.0", "tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "time-utils 0.1.0", @@ -1008,22 +1010,6 @@ dependencies = [ "rlp_derive 0.1.0", ] -[[package]] -name = "ethcore-error" -version = "0.1.0" -dependencies = [ - "common-types 0.1.0", - "derive_more 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ethcore 1.12.0", - "ethcore-io 1.12.0", - "ethereum-types 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ethkey 0.3.0", - "parity-snappy 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "patricia-trie-ethereum 0.1.0", - "rlp 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "unexpected 0.1.0", -] - [[package]] name = "ethcore-io" version = "1.12.0" @@ -3256,12 +3242,12 @@ dependencies = [ "keccak-hasher 0.1.1", "kvdb 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "macros 0.1.0", "parity-bytes 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "patricia-trie-ethereum 0.1.0", "rlp 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", - "state-account 0.1.0", "trie-db 0.12.4 (registry+https://github.com/rust-lang/crates.io-index)", "triehash-ethereum 0.2.0", ] @@ -3950,9 +3936,10 @@ name = "state-account" version = "0.1.0" dependencies = [ "common-types 0.1.0", - "ethcore-error 0.1.0", + "ethcore 1.12.0", "ethereum-types 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "hash-db 0.12.4 (registry+https://github.com/rust-lang/crates.io-index)", + "journaldb 0.2.0", "keccak-hash 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "keccak-hasher 0.1.1", "kvdb 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3960,7 +3947,9 @@ dependencies = [ "lru-cache 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "parity-bytes 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "patricia-trie-ethereum 0.1.0", + "pod-account 0.1.0", "rlp 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rlp_compress 0.1.0", "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", "trie-db 0.12.4 (registry+https://github.com/rust-lang/crates.io-index)", ] diff --git a/Cargo.toml b/Cargo.toml index f2a9ab20c20..369fa5bd68a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -140,9 +140,4 @@ members = [ "util/patricia-trie-ethereum", "util/fastmap", "util/time-utils", - -# TODO: remove these once they're inserted into the dep tree - "ethcore/pod-account", - "ethcore/state-account", - "ethcore/ethcore-error", ] diff --git a/ethcore/Cargo.toml b/ethcore/Cargo.toml index 9c315fffbbc..3a1f17e9017 100644 --- a/ethcore/Cargo.toml +++ b/ethcore/Cargo.toml @@ -52,6 +52,7 @@ parity-bytes = "0.1" parity-crypto = "0.4.0" parity-snappy = "0.1" parking_lot = "0.7" +pod-account = { path = "pod-account" } trie-db = "0.12.4" patricia-trie-ethereum = { path = "../util/patricia-trie-ethereum" } rand = "0.6" @@ -61,6 +62,7 @@ rlp_derive = { path = "../util/rlp-derive" } rustc-hex = "1.0" serde = "1.0" serde_derive = "1.0" +state-account = { path = "state-account" } stats = { path = "../util/stats" } tempdir = { version = "0.3", optional = true } time-utils = { path = "../util/time-utils" } diff --git a/ethcore/ethcore-error/Cargo.toml b/ethcore/ethcore-error/Cargo.toml deleted file mode 100644 index 6189f7415d6..00000000000 --- a/ethcore/ethcore-error/Cargo.toml +++ /dev/null @@ -1,20 +0,0 @@ -[package] -name = "ethcore-error" -version = "0.1.0" -authors = ["David Palm "] -edition = "2018" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -derive_more = "0.14.0" -ethereum-types = "0.6.0" -ethkey = { path = "../../accounts/ethkey" } -ethtrie = { package = "patricia-trie-ethereum", path = "../../util/patricia-trie-ethereum" } -rlp = "0.4.0" -parity-snappy = "0.1" -# TODO: this ruins everything, get it sorted -ethcore = { path = ".." } -common-types = {path = "../types/" } -unexpected = { path = "../../util/unexpected" } -ethcore-io = { path = "../../util/io" } diff --git a/ethcore/ethcore-error/src/lib.rs b/ethcore/ethcore-error/src/lib.rs deleted file mode 100644 index 4a925713fb9..00000000000 --- a/ethcore/ethcore-error/src/lib.rs +++ /dev/null @@ -1,303 +0,0 @@ -// 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::{fmt, error}; -use std::time::SystemTime; - -use derive_more::{Display, From}; -use ethereum_types::{H256, U256, Address, Bloom}; -use ethkey::Error as EthkeyError; -use ethtrie::TrieError; -use rlp; -use parity_snappy::InvalidInput; -// TODO: move this error somewhere else (here?) -use ethcore::snapshot::Error as SnapshotError; -use common_types::BlockNumber; -use common_types::transaction::Error as TransactionError; -use unexpected::{Mismatch, OutOfBounds}; - -// TODO: move this error somewhere else (here?) -use ethcore::engines::EngineError; - -pub use ethcore::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" - } -} - -/// Queue error -#[derive(Debug, Display, From)] -pub enum QueueError { - /// Queue is full - #[display(fmt = "Queue is full ({})", _0)] - Full(usize), - /// Io channel error - #[display(fmt = "Io channel error: {}", _0)] - Channel(ethcore_io::IoError) -} - -impl error::Error for QueueError { - fn source(&self) -> Option<&(dyn error::Error + 'static)> { - match self { - QueueError::Channel(e) => Some(e), - _ => None, - } - } -} - -/// 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 {} - -/// Api-level error for transaction import -#[derive(Debug, Clone)] -pub enum TransactionImportError { - /// Transaction error - Transaction(TransactionError), - /// Other error - Other(String), -} - -impl From for TransactionImportError { - fn from(e: Error) -> Self { - match e { - Error::Transaction(transaction_error) => TransactionImportError::Transaction(transaction_error), - _ => TransactionImportError::Other(format!("other block import error: {:?}", e)), - } - } -} - -/// 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 error: {}", _0)] - Queue(QueueError), - /// Io create error - #[display(fmt = "Io error: {}", _0)] - Io(ethcore_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, - /// Unknown engine given - #[display(fmt = "Unknown engine name ({})", _0)] - UnknownEngineName(String), - /// A convenient variant for String. - #[display(fmt = "{}", _0)] - Msg(String), -} - -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), - _ => None, - } - } -} - -impl From for Error { - fn from(s: String) -> Self { - Error::Msg(s) - } -} - -impl From<&str> for Error { - fn from(s: &str) -> Self { - Error::Msg(s.into()) - } -} - -impl From> for Error where Error: From { - fn from(err: Box) -> Error { - Error::from(*err) - } -} diff --git a/ethcore/pod-account/Cargo.toml b/ethcore/pod-account/Cargo.toml index 7849b6e56f2..23d752489e1 100644 --- a/ethcore/pod-account/Cargo.toml +++ b/ethcore/pod-account/Cargo.toml @@ -1,11 +1,10 @@ [package] +description = "Account system expressed in Plain Old Data." name = "pod-account" version = "0.1.0" -authors = ["David Palm "] +authors = ["Parity Technologies "] edition = "2018" -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - [dependencies] common-types = { path = "../types" } ethereum-types = "0.6" @@ -21,6 +20,8 @@ parity-bytes = "0.1.0" rlp = "0.4" rustc-hex = "1" serde = { version = "1.0", features = ["derive"] } -state-account = { path = "../state-account" } trie-db = "0.12.4" triehash = { package = "triehash-ethereum", version = "0.2", path = "../../util/triehash-ethereum" } + +[dev-dependencies] +macros = { path = "../../util/macros" } diff --git a/ethcore/pod-account/src/lib.rs b/ethcore/pod-account/src/lib.rs index b072758e4b7..ea004f01715 100644 --- a/ethcore/pod-account/src/lib.rs +++ b/ethcore/pod-account/src/lib.rs @@ -27,7 +27,6 @@ use triehash::sec_trie_root; use parity_bytes::Bytes; use trie_db::TrieFactory; use ethtrie::RlpCodec; -use state_account::Account; use ethjson; use common_types::account_diff::*; use rlp::{self, RlpStream}; @@ -57,17 +56,6 @@ fn opt_bytes_to_hex(opt_bytes: &Option, serializer: S) -> Result PodAccount { - PodAccount { - balance: *acc.balance(), - nonce: *acc.nonce(), - storage: acc.storage_changes().iter().fold(BTreeMap::new(), |mut m, (k, v)| {m.insert(k.clone(), v.clone()); m}), - code: acc.code().map(|x| x.to_vec()), - } - } - /// Returns the RLP for this account. pub fn rlp(&self) -> Bytes { let mut stream = RlpStream::new_list(4); @@ -170,9 +158,10 @@ pub fn diff_pod(pre: Option<&PodAccount>, post: Option<&PodAccount>) -> Option AccountDBMut<'db> { Self::from_hash(db, keccak(address)) } - /// Create a new AcountDB from an address' hash. + /// Create a new `AccountDBMut` from an address' hash. pub fn from_hash(db: &'db mut dyn HashDB, address_hash: H256) -> Self { - AccountDBMut { - db: db, - address_hash: address_hash, - } + AccountDBMut { db, address_hash } } - #[cfg(test)] + /// Create an `AccountDB` from an `AccountDBMut`. pub fn immutable(&'db self) -> AccountDB<'db> { AccountDB { db: self.db, address_hash: self.address_hash.clone() } } diff --git a/ethcore/src/lib.rs b/ethcore/src/lib.rs index a0ee0c2d701..ea3347ffd51 100644 --- a/ethcore/src/lib.rs +++ b/ethcore/src/lib.rs @@ -88,6 +88,7 @@ extern crate parity_bytes as bytes; extern crate parity_crypto; extern crate parity_snappy as snappy; extern crate parking_lot; +extern crate pod_account; extern crate trie_db as trie; extern crate patricia_trie_ethereum as ethtrie; extern crate rand; @@ -98,6 +99,7 @@ extern crate parity_util_mem as malloc_size_of; extern crate rustc_hex; extern crate serde; extern crate stats; +extern crate state_account; extern crate time_utils; extern crate triehash_ethereum as triehash; extern crate unexpected; @@ -120,8 +122,6 @@ extern crate blooms_db; #[cfg(any(test, feature = "env_logger"))] extern crate env_logger; #[cfg(test)] -extern crate rlp_compress; -#[cfg(test)] extern crate serde_json; #[macro_use] @@ -162,7 +162,6 @@ pub mod executive; pub mod machine; pub mod miner; pub mod pod_state; -pub mod pod_account; pub mod snapshot; pub mod spec; pub mod state; @@ -170,8 +169,8 @@ pub mod state_db; pub mod trace; pub mod transaction_ext; pub mod verification; +pub mod account_db; -mod account_db; mod externalities; mod factory; mod tx_filter; diff --git a/ethcore/src/pod_account.rs b/ethcore/src/pod_account.rs deleted file mode 100644 index 196d627b124..00000000000 --- a/ethcore/src/pod_account.rs +++ /dev/null @@ -1,256 +0,0 @@ -// 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 . - -//! Account system expressed in Plain Old Data. - -use std::collections::BTreeMap; -use itertools::Itertools; -use hash::{keccak}; -use ethereum_types::{H256, U256, BigEndianHash}; -use hash_db::HashDB; -use kvdb::DBValue; -use keccak_hasher::KeccakHasher; -use triehash::sec_trie_root; -use bytes::Bytes; -use trie::TrieFactory; -use ethtrie::RlpCodec; -use state::Account; -use ethjson; -use types::account_diff::*; -use rlp::{self, RlpStream}; -use serde::Serializer; -use rustc_hex::ToHex; - -#[derive(Debug, Clone, PartialEq, Eq, Serialize)] -/// An account, expressed as Plain-Old-Data (hence the name). -/// Does not have a DB overlay cache, code hash or anything like that. -pub struct PodAccount { - /// The balance of the account. - pub balance: U256, - /// The nonce of the account. - pub nonce: U256, - #[serde(serialize_with="opt_bytes_to_hex")] - /// The code of the account or `None` in the special case that it is unknown. - pub code: Option, - /// The storage of the account. - pub storage: BTreeMap, -} - -fn opt_bytes_to_hex(opt_bytes: &Option, serializer: S) -> Result - where S: Serializer -{ - let readable = opt_bytes.as_ref().map(|b| b.to_hex()).unwrap_or_default(); - serializer.collect_str(&format_args!("0x{}", readable)) -} - -impl PodAccount { - /// Convert Account to a PodAccount. - /// NOTE: This will silently fail unless the account is fully cached. - pub fn from_account(acc: &Account) -> PodAccount { - PodAccount { - balance: *acc.balance(), - nonce: *acc.nonce(), - storage: acc.storage_changes().iter().fold(BTreeMap::new(), |mut m, (k, v)| {m.insert(k.clone(), v.clone()); m}), - code: acc.code().map(|x| x.to_vec()), - } - } - - /// Returns the RLP for this account. - pub fn rlp(&self) -> Bytes { - let mut stream = RlpStream::new_list(4); - stream.append(&self.nonce); - stream.append(&self.balance); - stream.append(&sec_trie_root(self.storage.iter().map(|(k, v)| (k, rlp::encode(&v.into_uint()))))); - stream.append(&keccak(&self.code.as_ref().unwrap_or(&vec![]))); - stream.out() - } - - /// Place additional data into given hash DB. - pub fn insert_additional(&self, db: &mut dyn HashDB, factory: &TrieFactory) { - match self.code { - Some(ref c) if !c.is_empty() => { db.insert(hash_db::EMPTY_PREFIX, c); } - _ => {} - } - let mut r = H256::zero(); - let mut t = factory.create(db, &mut r); - for (k, v) in &self.storage { - if let Err(e) = t.insert(k.as_bytes(), &rlp::encode(&v.into_uint())) { - warn!("Encountered potential DB corruption: {}", e); - } - } - } -} - -impl From for PodAccount { - fn from(a: ethjson::blockchain::Account) -> Self { - PodAccount { - balance: a.balance.into(), - nonce: a.nonce.into(), - code: Some(a.code.into()), - storage: a.storage.into_iter().map(|(key, value)| { - let key: U256 = key.into(); - let value: U256 = value.into(); - (BigEndianHash::from_uint(&key), BigEndianHash::from_uint(&value)) - }).collect(), - } - } -} - -impl From for PodAccount { - fn from(a: ethjson::spec::Account) -> Self { - PodAccount { - balance: a.balance.map_or_else(U256::zero, Into::into), - nonce: a.nonce.map_or_else(U256::zero, Into::into), - code: Some(a.code.map_or_else(Vec::new, Into::into)), - storage: a.storage.map_or_else(BTreeMap::new, |s| s.into_iter().map(|(key, value)| { - let key: U256 = key.into(); - let value: U256 = value.into(); - (BigEndianHash::from_uint(&key), BigEndianHash::from_uint(&value)) - }).collect()), - } - } -} - -/// Determine difference between two optionally existant `Account`s. Returns None -/// if they are the same. -pub fn diff_pod(pre: Option<&PodAccount>, post: Option<&PodAccount>) -> Option { - match (pre, post) { - (None, Some(x)) => Some(AccountDiff { - balance: Diff::Born(x.balance), - nonce: Diff::Born(x.nonce), - code: Diff::Born(x.code.as_ref().expect("account is newly created; newly created accounts must be given code; all caches should remain in place; qed").clone()), - storage: x.storage.iter().map(|(k, v)| (k.clone(), Diff::Born(v.clone()))).collect(), - }), - (Some(x), None) => Some(AccountDiff { - balance: Diff::Died(x.balance), - nonce: Diff::Died(x.nonce), - code: Diff::Died(x.code.as_ref().expect("account is deleted; only way to delete account is running SUICIDE; account must have had own code cached to make operation; all caches should remain in place; qed").clone()), - storage: x.storage.iter().map(|(k, v)| (k.clone(), Diff::Died(v.clone()))).collect(), - }), - (Some(pre), Some(post)) => { - let storage: Vec<_> = pre.storage.keys().merge(post.storage.keys()) - .filter(|k| pre.storage.get(k).unwrap_or(&H256::zero()) != post.storage.get(k).unwrap_or(&H256::zero())) - .collect(); - let r = AccountDiff { - balance: Diff::new(pre.balance, post.balance), - nonce: Diff::new(pre.nonce, post.nonce), - code: match (pre.code.clone(), post.code.clone()) { - (Some(pre_code), Some(post_code)) => Diff::new(pre_code, post_code), - _ => Diff::Same, - }, - storage: storage.into_iter().map(|k| - (k.clone(), Diff::new( - pre.storage.get(k).cloned().unwrap_or_else(H256::zero), - post.storage.get(k).cloned().unwrap_or_else(H256::zero) - ))).collect(), - }; - if r.balance.is_same() && r.nonce.is_same() && r.code.is_same() && r.storage.is_empty() { - None - } else { - Some(r) - } - }, - _ => None, - } -} - -#[cfg(test)] -mod test { - use std::collections::BTreeMap; - use types::account_diff::*; - use super::{PodAccount, diff_pod}; - use ethereum_types::H256; - - #[test] - fn existence() { - let a = PodAccount{balance: 69.into(), nonce: 0.into(), code: Some(vec![]), storage: map![]}; - assert_eq!(diff_pod(Some(&a), Some(&a)), None); - assert_eq!(diff_pod(None, Some(&a)), Some(AccountDiff{ - balance: Diff::Born(69.into()), - nonce: Diff::Born(0.into()), - code: Diff::Born(vec![]), - storage: map![], - })); - } - - #[test] - fn basic() { - let a = PodAccount{balance: 69.into(), nonce: 0.into(), code: Some(vec![]), storage: map![]}; - let b = PodAccount{balance: 42.into(), nonce: 1.into(), code: Some(vec![]), storage: map![]}; - assert_eq!(diff_pod(Some(&a), Some(&b)), Some(AccountDiff { - balance: Diff::Changed(69.into(), 42.into()), - nonce: Diff::Changed(0.into(), 1.into()), - code: Diff::Same, - storage: map![], - })); - } - - #[test] - fn code() { - let a = PodAccount{balance: 0.into(), nonce: 0.into(), code: Some(vec![]), storage: map![]}; - let b = PodAccount{balance: 0.into(), nonce: 1.into(), code: Some(vec![0]), storage: map![]}; - assert_eq!(diff_pod(Some(&a), Some(&b)), Some(AccountDiff { - balance: Diff::Same, - nonce: Diff::Changed(0.into(), 1.into()), - code: Diff::Changed(vec![], vec![0]), - storage: map![], - })); - } - - #[test] - fn storage() { - let a = PodAccount { - balance: 0.into(), - nonce: 0.into(), - code: Some(vec![]), - storage: map![ - H256::from_low_u64_be(1) => H256::from_low_u64_be(1), - H256::from_low_u64_be(2) => H256::from_low_u64_be(2), - H256::from_low_u64_be(3) => H256::from_low_u64_be(3), - H256::from_low_u64_be(4) => H256::from_low_u64_be(4), - H256::from_low_u64_be(5) => H256::from_low_u64_be(0), - H256::from_low_u64_be(6) => H256::from_low_u64_be(0), - H256::from_low_u64_be(7) => H256::from_low_u64_be(0) - ], - }; - let b = PodAccount { - balance: 0.into(), - nonce: 0.into(), - code: Some(vec![]), - storage: map![ - H256::from_low_u64_be(1) => H256::from_low_u64_be(1), - H256::from_low_u64_be(2) => H256::from_low_u64_be(3), - H256::from_low_u64_be(3) => H256::from_low_u64_be(0), - H256::from_low_u64_be(5) => H256::from_low_u64_be(0), - H256::from_low_u64_be(7) => H256::from_low_u64_be(7), - H256::from_low_u64_be(8) => H256::from_low_u64_be(0), - H256::from_low_u64_be(9) => H256::from_low_u64_be(9) - ] - }; - assert_eq!(diff_pod(Some(&a), Some(&b)), Some(AccountDiff { - balance: Diff::Same, - nonce: Diff::Same, - code: Diff::Same, - storage: map![ - H256::from_low_u64_be(2) => Diff::new(H256::from_low_u64_be(2), H256::from_low_u64_be(3)), - H256::from_low_u64_be(3) => Diff::new(H256::from_low_u64_be(3), H256::from_low_u64_be(0)), - H256::from_low_u64_be(4) => Diff::new(H256::from_low_u64_be(4), H256::from_low_u64_be(0)), - H256::from_low_u64_be(7) => Diff::new(H256::from_low_u64_be(0), H256::from_low_u64_be(7)), - H256::from_low_u64_be(9) => Diff::new(H256::from_low_u64_be(0), H256::from_low_u64_be(9)) - ], - })); - } -} diff --git a/ethcore/src/state/account.rs b/ethcore/src/state/account.rs deleted file mode 100644 index 2c9abc0df67..00000000000 --- a/ethcore/src/state/account.rs +++ /dev/null @@ -1,745 +0,0 @@ -// 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 . - -//! Single account in the system. - -use std::fmt; -use std::sync::Arc; -use std::collections::{HashMap, BTreeMap}; -use hash::{KECCAK_EMPTY, KECCAK_NULL_RLP, keccak}; -use ethereum_types::{H256, U256, Address, BigEndianHash}; -use error::Error; -use hash_db::HashDB; -use keccak_hasher::KeccakHasher; -use kvdb::DBValue; -use bytes::{Bytes, ToPretty}; -use trie::{Trie, Recorder}; -use ethtrie::{TrieFactory, TrieDB, SecTrieDB, Result as TrieResult}; -use pod_account::*; -use rlp::{RlpStream, encode}; -use lru_cache::LruCache; -use types::basic_account::BasicAccount; - -use std::cell::{RefCell, Cell}; - -const STORAGE_CACHE_ITEMS: usize = 8192; - -/// Boolean type for clean/dirty status. -#[derive(PartialEq, Eq, Clone, Copy, Debug)] -pub enum Filth { - /// Data has not been changed. - Clean, - /// Data has been changed. - Dirty, -} - -/// Single account in the system. -/// Keeps track of changes to the code and storage. -/// The changes are applied in `commit_storage` and `commit_code` -pub struct Account { - // Balance of the account. - balance: U256, - // Nonce of the account. - nonce: U256, - // Trie-backed storage. - storage_root: H256, - // LRU Cache of the trie-backed storage. - // This is limited to `STORAGE_CACHE_ITEMS` recent queries - storage_cache: RefCell>, - // LRU Cache of the trie-backed storage for original value. - // This is only used when the initial storage root is different compared to - // what is in the database. That is, it is only used for new contracts. - original_storage_cache: Option<(H256, RefCell>)>, - // Modified storage. Accumulates changes to storage made in `set_storage` - // Takes precedence over `storage_cache`. - storage_changes: HashMap, - // Code hash of the account. - code_hash: H256, - // Size of the account code. - code_size: Option, - // Code cache of the account. - code_cache: Arc, - // Account code new or has been modified. - code_filth: Filth, - // Cached address hash. - address_hash: Cell>, -} - -impl From for Account { - fn from(basic: BasicAccount) -> Self { - Account { - balance: basic.balance, - nonce: basic.nonce, - storage_root: basic.storage_root, - storage_cache: Self::empty_storage_cache(), - original_storage_cache: None, - storage_changes: HashMap::new(), - code_hash: basic.code_hash, - code_size: None, - code_cache: Arc::new(vec![]), - code_filth: Filth::Clean, - address_hash: Cell::new(None), - } - } -} - -impl Account { - #[cfg(test)] - /// General constructor. - pub fn new(balance: U256, nonce: U256, storage: HashMap, code: Bytes) -> Account { - Account { - balance: balance, - nonce: nonce, - storage_root: KECCAK_NULL_RLP, - storage_cache: Self::empty_storage_cache(), - original_storage_cache: None, - storage_changes: storage, - code_hash: keccak(&code), - code_size: Some(code.len()), - code_cache: Arc::new(code), - code_filth: Filth::Dirty, - address_hash: Cell::new(None), - } - } - - fn empty_storage_cache() -> RefCell> { - RefCell::new(LruCache::new(STORAGE_CACHE_ITEMS)) - } - - /// General constructor. - pub fn from_pod(pod: PodAccount) -> Account { - Account { - balance: pod.balance, - nonce: pod.nonce, - storage_root: KECCAK_NULL_RLP, - storage_cache: Self::empty_storage_cache(), - original_storage_cache: None, - storage_changes: pod.storage.into_iter().collect(), - code_hash: pod.code.as_ref().map_or(KECCAK_EMPTY, |c| keccak(c)), - code_filth: Filth::Dirty, - code_size: Some(pod.code.as_ref().map_or(0, |c| c.len())), - code_cache: Arc::new(pod.code.map_or_else(|| { warn!("POD account with unknown code is being created! Assuming no code."); vec![] }, |c| c)), - address_hash: Cell::new(None), - } - } - - /// Create a new account with the given balance. - pub fn new_basic(balance: U256, nonce: U256) -> Account { - Account { - balance: balance, - nonce: nonce, - storage_root: KECCAK_NULL_RLP, - storage_cache: Self::empty_storage_cache(), - original_storage_cache: None, - storage_changes: HashMap::new(), - code_hash: KECCAK_EMPTY, - code_cache: Arc::new(vec![]), - code_size: Some(0), - code_filth: Filth::Clean, - address_hash: Cell::new(None), - } - } - - /// Create a new account from RLP. - pub fn from_rlp(rlp: &[u8]) -> Result { - ::rlp::decode::(rlp) - .map(|ba| ba.into()) - .map_err(|e| e.into()) - } - - /// Create a new contract account. - /// NOTE: make sure you use `init_code` on this before `commit`ing. - pub fn new_contract(balance: U256, nonce: U256, original_storage_root: H256) -> Account { - Account { - balance: balance, - nonce: nonce, - storage_root: KECCAK_NULL_RLP, - storage_cache: Self::empty_storage_cache(), - original_storage_cache: if original_storage_root == KECCAK_NULL_RLP { - None - } else { - Some((original_storage_root, Self::empty_storage_cache())) - }, - storage_changes: HashMap::new(), - code_hash: KECCAK_EMPTY, - code_cache: Arc::new(vec![]), - code_size: None, - code_filth: Filth::Clean, - address_hash: Cell::new(None), - } - } - - /// Set this account's code to the given code. - /// NOTE: Account should have been created with `new_contract()` - pub fn init_code(&mut self, code: Bytes) { - self.code_hash = keccak(&code); - self.code_cache = Arc::new(code); - self.code_size = Some(self.code_cache.len()); - self.code_filth = Filth::Dirty; - } - - /// Reset this account's code to the given code. - pub fn reset_code(&mut self, code: Bytes) { - self.init_code(code); - } - - /// Reset this account's code and storage to given values. - pub fn reset_code_and_storage(&mut self, code: Arc, storage: HashMap) { - self.code_hash = keccak(&*code); - self.code_cache = code; - self.code_size = Some(self.code_cache.len()); - self.code_filth = Filth::Dirty; - self.storage_cache = Self::empty_storage_cache(); - self.storage_changes = storage; - if self.storage_root != KECCAK_NULL_RLP { - self.original_storage_cache = Some((self.storage_root, Self::empty_storage_cache())); - } - self.storage_root = KECCAK_NULL_RLP; - } - - /// Set (and cache) the contents of the trie's storage at `key` to `value`. - pub fn set_storage(&mut self, key: H256, value: H256) { - self.storage_changes.insert(key, value); - } - - /// Get (and cache) the contents of the trie's storage at `key`. - /// Takes modified storage into account. - pub fn storage_at(&self, db: &dyn HashDB, key: &H256) -> TrieResult { - if let Some(value) = self.cached_storage_at(key) { - return Ok(value); - } - Self::get_and_cache_storage( - &self.storage_root, - &mut self.storage_cache.borrow_mut(), - db, - key) - } - - /// Get (and cache) the contents of the trie's storage at `key`. - /// Does not take modified storage into account. - pub fn original_storage_at(&self, db: &dyn HashDB, key: &H256) -> TrieResult { - if let Some(value) = self.cached_original_storage_at(key) { - return Ok(value); - } - match &self.original_storage_cache { - Some((ref original_storage_root, ref original_storage_cache)) => - Self::get_and_cache_storage( - original_storage_root, - &mut original_storage_cache.borrow_mut(), - db, - key - ), - None => - Self::get_and_cache_storage( - &self.storage_root, - &mut self.storage_cache.borrow_mut(), - db, - key - ), - } - } - - fn get_and_cache_storage(storage_root: &H256, storage_cache: &mut LruCache, db: &dyn HashDB, key: &H256) -> TrieResult { - let db = SecTrieDB::new(&db, storage_root)?; - let panicky_decoder = |bytes:&[u8]| ::rlp::decode(&bytes).expect("decoding db value failed"); - let item: U256 = db.get_with(key.as_bytes(), panicky_decoder)?.unwrap_or_else(U256::zero); - let value: H256 = BigEndianHash::from_uint(&item); - storage_cache.insert(key.clone(), value.clone()); - Ok(value) - } - - /// Get cached storage value if any. Returns `None` if the - /// key is not in the cache. - pub fn cached_storage_at(&self, key: &H256) -> Option { - if let Some(value) = self.storage_changes.get(key) { - return Some(value.clone()) - } - self.cached_moved_original_storage_at(key) - } - - /// Get cached original storage value after last state commitment. Returns `None` if the key is not in the cache. - pub fn cached_original_storage_at(&self, key: &H256) -> Option { - match &self.original_storage_cache { - Some((_, ref original_storage_cache)) => { - if let Some(value) = original_storage_cache.borrow_mut().get_mut(key) { - Some(value.clone()) - } else { - None - } - }, - None => { - self.cached_moved_original_storage_at(key) - }, - } - } - - /// Get cached original storage value since last contract creation on this address. Returns `None` if the key is not in the cache. - fn cached_moved_original_storage_at(&self, key: &H256) -> Option { - // If storage root is empty RLP, then early return zero value. Practically, this makes it so that if - // `original_storage_cache` is used, then `storage_cache` will always remain empty. - if self.storage_root == KECCAK_NULL_RLP { - return Some(H256::zero()); - } - - if let Some(value) = self.storage_cache.borrow_mut().get_mut(key) { - Some(value.clone()) - } else { - None - } - } - - /// return the balance associated with this account. - pub fn balance(&self) -> &U256 { &self.balance } - - /// return the nonce associated with this account. - pub fn nonce(&self) -> &U256 { &self.nonce } - - /// return the code hash associated with this account. - pub fn code_hash(&self) -> H256 { - self.code_hash.clone() - } - - /// return and cache `keccak(address)`, `address` must be the address of this - /// account. - pub fn address_hash(&self, address: &Address) -> H256 { - let hash = self.address_hash.get(); - hash.unwrap_or_else(|| { - let hash = keccak(address); - self.address_hash.set(Some(hash.clone())); - hash - }) - } - - /// returns the account's code. If `None` then the code cache isn't available - - /// get someone who knows to call `note_code`. - pub fn code(&self) -> Option> { - if self.code_hash != KECCAK_EMPTY && self.code_cache.is_empty() { - return None; - } - Some(self.code_cache.clone()) - } - - /// returns the account's code size. If `None` then the code cache or code size cache isn't available - - /// get someone who knows to call `note_code`. - pub fn code_size(&self) -> Option { - self.code_size.clone() - } - - #[cfg(test)] - /// Provide a byte array which hashes to the `code_hash`. returns the hash as a result. - pub fn note_code(&mut self, code: Bytes) -> Result<(), H256> { - let h = keccak(&code); - if self.code_hash == h { - self.code_cache = Arc::new(code); - self.code_size = Some(self.code_cache.len()); - Ok(()) - } else { - Err(h) - } - } - - /// Is `code_cache` valid; such that code is going to return Some? - pub fn is_cached(&self) -> bool { - !self.code_cache.is_empty() || (self.code_cache.is_empty() && self.code_hash == KECCAK_EMPTY) - } - - /// Provide a database to get `code_hash`. Should not be called if it is a contract without code. Returns the cached code, if successful. - #[must_use] - pub fn cache_code(&mut self, db: &dyn HashDB) -> Option> { - // TODO: fill out self.code_cache; - trace!("Account::cache_code: ic={}; self.code_hash={:?}, self.code_cache={}", self.is_cached(), self.code_hash, self.code_cache.pretty()); - - if self.is_cached() { return Some(self.code_cache.clone()); } - - match db.get(&self.code_hash, hash_db::EMPTY_PREFIX) { - Some(x) => { - self.code_size = Some(x.len()); - self.code_cache = Arc::new(x.into_vec()); - Some(self.code_cache.clone()) - }, - _ => { - warn!("Failed reverse get of {}", self.code_hash); - None - }, - } - } - - /// Provide code to cache. For correctness, should be the correct code for the account. - pub fn cache_given_code(&mut self, code: Arc) { - trace!("Account::cache_given_code: ic={}; self.code_hash={:?}, self.code_cache={}", self.is_cached(), self.code_hash, self.code_cache.pretty()); - - self.code_size = Some(code.len()); - self.code_cache = code; - } - - /// Provide a database to get `code_size`. Should not be called if it is a contract without code. Returns whether - /// the cache succeeds. - #[must_use] - pub fn cache_code_size(&mut self, db: &dyn HashDB) -> bool { - // TODO: fill out self.code_cache; - trace!("Account::cache_code_size: ic={}; self.code_hash={:?}, self.code_cache={}", self.is_cached(), self.code_hash, self.code_cache.pretty()); - self.code_size.is_some() || - if self.code_hash != KECCAK_EMPTY { - match db.get(&self.code_hash, hash_db::EMPTY_PREFIX) { - Some(x) => { - self.code_size = Some(x.len()); - true - }, - _ => { - warn!("Failed reverse get of {}", self.code_hash); - false - }, - } - } else { - // If the code hash is empty hash, then the code size is zero. - self.code_size = Some(0); - true - } - } - - /// Determine whether there are any un-`commit()`-ed storage-setting operations. - pub fn storage_is_clean(&self) -> bool { self.storage_changes.is_empty() } - - /// Check if account has zero nonce, balance, no code and no storage. - /// - /// NOTE: Will panic if `!self.storage_is_clean()` - pub fn is_empty(&self) -> bool { - assert!(self.storage_is_clean(), "Account::is_empty() may only legally be called when storage is clean."); - self.is_null() && self.storage_root == KECCAK_NULL_RLP - } - - /// Check if account has zero nonce, balance, no code. - pub fn is_null(&self) -> bool { - self.balance.is_zero() && - self.nonce.is_zero() && - self.code_hash == KECCAK_EMPTY - } - - /// Check if account is basic (Has no code). - pub fn is_basic(&self) -> bool { - self.code_hash == KECCAK_EMPTY - } - - /// Return the storage root associated with this account or None if it has been altered via the overlay. - pub fn storage_root(&self) -> Option { - if self.storage_is_clean() { - Some(self.storage_root) - } else { - None - } - } - - /// Return the original storage root of this account. - pub fn original_storage_root(&self) -> H256 { - if let Some((original_storage_root, _)) = self.original_storage_cache { - original_storage_root - } else { - self.storage_root - } - } - - /// Whether the base storage root of this account is unchanged. - pub fn is_base_storage_root_unchanged(&self) -> bool { - self.original_storage_cache.is_none() - } - - /// Storage root where the account changes are based upon. - pub fn base_storage_root(&self) -> H256 { - self.storage_root - } - - /// Return the storage overlay. - pub fn storage_changes(&self) -> &HashMap { &self.storage_changes } - - /// Increment the nonce of the account by one. - pub fn inc_nonce(&mut self) { - self.nonce = self.nonce.saturating_add(U256::from(1u8)); - } - - /// Increase account balance. - pub fn add_balance(&mut self, x: &U256) { - self.balance = self.balance.saturating_add(*x); - } - - /// Decrease account balance. - /// Panics if balance is less than `x` - pub fn sub_balance(&mut self, x: &U256) { - assert!(self.balance >= *x); - self.balance = self.balance - *x; - } - - /// Commit the `storage_changes` to the backing DB and update `storage_root`. - pub fn commit_storage(&mut self, trie_factory: &TrieFactory, db: &mut dyn HashDB) -> TrieResult<()> { - let mut t = trie_factory.from_existing(db, &mut self.storage_root)?; - for (k, v) in self.storage_changes.drain() { - // cast key and value to trait type, - // so we can call overloaded `to_bytes` method - match v.is_zero() { - true => t.remove(k.as_bytes())?, - false => t.insert(k.as_bytes(), &encode(&v.into_uint()))?, - }; - - self.storage_cache.borrow_mut().insert(k, v); - } - self.original_storage_cache = None; - Ok(()) - } - - /// Commit any unsaved code. `code_hash` will always return the hash of the `code_cache` after this. - pub fn commit_code(&mut self, db: &mut dyn HashDB) { - trace!("Commiting code of {:?} - {:?}, {:?}", self, self.code_filth == Filth::Dirty, self.code_cache.is_empty()); - match (self.code_filth == Filth::Dirty, self.code_cache.is_empty()) { - (true, true) => { - self.code_size = Some(0); - self.code_filth = Filth::Clean; - }, - (true, false) => { - db.emplace(self.code_hash.clone(), hash_db::EMPTY_PREFIX, DBValue::from_slice(&*self.code_cache)); - self.code_size = Some(self.code_cache.len()); - self.code_filth = Filth::Clean; - }, - (false, _) => {}, - } - } - - /// Export to RLP. - pub fn rlp(&self) -> Bytes { - let mut stream = RlpStream::new_list(4); - stream.append(&self.nonce); - stream.append(&self.balance); - stream.append(&self.storage_root); - stream.append(&self.code_hash); - stream.out() - } - - /// Clone basic account data - pub fn clone_basic(&self) -> Account { - Account { - balance: self.balance.clone(), - nonce: self.nonce.clone(), - storage_root: self.storage_root.clone(), - storage_cache: Self::empty_storage_cache(), - original_storage_cache: self.original_storage_cache.as_ref().map(|(r, _)| (*r, Self::empty_storage_cache())), - storage_changes: HashMap::new(), - code_hash: self.code_hash.clone(), - code_size: self.code_size.clone(), - code_cache: self.code_cache.clone(), - code_filth: self.code_filth, - address_hash: self.address_hash.clone(), - } - } - - /// Clone account data and dirty storage keys - pub fn clone_dirty(&self) -> Account { - let mut account = self.clone_basic(); - account.storage_changes = self.storage_changes.clone(); - account - } - - /// Clone account data, dirty storage keys and cached storage keys. - pub fn clone_all(&self) -> Account { - let mut account = self.clone_dirty(); - account.storage_cache = self.storage_cache.clone(); - account.original_storage_cache = self.original_storage_cache.clone(); - account - } - - /// Replace self with the data from other account merging storage cache. - /// Basic account data and all modifications are overwritten - /// with new values. - pub fn overwrite_with(&mut self, other: Account) { - self.balance = other.balance; - self.nonce = other.nonce; - self.code_hash = other.code_hash; - self.code_filth = other.code_filth; - self.code_cache = other.code_cache; - self.code_size = other.code_size; - self.address_hash = other.address_hash; - if self.storage_root == other.storage_root { - let mut cache = self.storage_cache.borrow_mut(); - for (k, v) in other.storage_cache.into_inner() { - cache.insert(k, v); - } - } else { - self.storage_cache = other.storage_cache; - } - self.original_storage_cache = other.original_storage_cache; - self.storage_root = other.storage_root; - self.storage_changes = other.storage_changes; - } -} - -// light client storage proof. -impl Account { - /// Prove a storage key's existence or nonexistence in the account's storage - /// trie. - /// `storage_key` is the hash of the desired storage key, meaning - /// this will only work correctly under a secure trie. - pub fn prove_storage(&self, db: &dyn HashDB, storage_key: H256) -> TrieResult<(Vec, H256)> { - let mut recorder = Recorder::new(); - - let trie = TrieDB::new(&db, &self.storage_root)?; - let item: U256 = { - let panicky_decoder = |bytes:&[u8]| ::rlp::decode(bytes).expect("decoding db value failed"); - let query = (&mut recorder, panicky_decoder); - trie.get_with(storage_key.as_bytes(), query)?.unwrap_or_else(U256::zero) - }; - - Ok((recorder.drain().into_iter().map(|r| r.data).collect(), BigEndianHash::from_uint(&item))) - } -} - -impl fmt::Debug for Account { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("Account") - .field("balance", &self.balance) - .field("nonce", &self.nonce) - .field("code", &self.code()) - .field("storage", &self.storage_changes.iter().collect::>()) - .finish() - } -} - -#[cfg(test)] -mod tests { - use rlp_compress::{compress, decompress, snapshot_swapper}; - use ethereum_types::{H256, Address}; - use journaldb::new_memory_db; - use bytes::Bytes; - use super::*; - use account_db::*; - use std::str::FromStr; - - #[test] - fn account_compress() { - let raw = Account::new_basic(2.into(), 4.into()).rlp(); - let compact_vec = compress(&raw, snapshot_swapper()); - assert!(raw.len() > compact_vec.len()); - let again_raw = decompress(&compact_vec, snapshot_swapper()); - assert_eq!(raw, again_raw.into_vec()); - } - - #[test] - fn storage_at() { - let mut db = new_memory_db(); - let mut db = AccountDBMut::new(&mut db, &Address::zero()); - let rlp = { - let mut a = Account::new_contract(69.into(), 0.into(), KECCAK_NULL_RLP); - a.set_storage(H256::zero(), H256::from_low_u64_be(0x1234)); - a.commit_storage(&Default::default(), &mut db).unwrap(); - a.init_code(vec![]); - a.commit_code(&mut db); - a.rlp() - }; - - let a = Account::from_rlp(&rlp).expect("decoding db value failed"); - assert_eq!(a.storage_root().unwrap(), H256::from_str("c57e1afb758b07f8d2c8f13a3b6e44fa5ff94ab266facc5a4fd3f062426e50b2").unwrap()); - assert_eq!(a.storage_at(&db.immutable(), &H256::zero()).unwrap(), H256::from_low_u64_be(0x1234)); - assert_eq!(a.storage_at(&db.immutable(), &H256::from_low_u64_be(0x01)).unwrap(), H256::zero()); - } - - #[test] - fn note_code() { - let mut db = new_memory_db(); - let mut db = AccountDBMut::new(&mut db, &Address::zero()); - - let rlp = { - let mut a = Account::new_contract(69.into(), 0.into(), KECCAK_NULL_RLP); - a.init_code(vec![0x55, 0x44, 0xffu8]); - a.commit_code(&mut db); - a.rlp() - }; - - let mut a = Account::from_rlp(&rlp).expect("decoding db value failed"); - assert!(a.cache_code(&db.immutable()).is_some()); - - let mut a = Account::from_rlp(&rlp).expect("decoding db value failed"); - assert_eq!(a.note_code(vec![0x55, 0x44, 0xffu8]), Ok(())); - } - - #[test] - fn commit_storage() { - let mut a = Account::new_contract(69.into(), 0.into(), KECCAK_NULL_RLP); - let mut db = new_memory_db(); - let mut db = AccountDBMut::new(&mut db, &Address::zero()); - a.set_storage(H256::from_low_u64_be(0), H256::from_low_u64_be(0x1234)); - assert_eq!(a.storage_root(), None); - a.commit_storage(&Default::default(), &mut db).unwrap(); - assert_eq!(a.storage_root().unwrap(), H256::from_str("c57e1afb758b07f8d2c8f13a3b6e44fa5ff94ab266facc5a4fd3f062426e50b2").unwrap()); - } - - #[test] - fn commit_remove_commit_storage() { - let mut a = Account::new_contract(69.into(), 0.into(), KECCAK_NULL_RLP); - let mut db = new_memory_db(); - let mut db = AccountDBMut::new(&mut db, &Address::zero()); - a.set_storage(H256::from_low_u64_be(0), H256::from_low_u64_be(0x1234)); - a.commit_storage(&Default::default(), &mut db).unwrap(); - a.set_storage(H256::from_low_u64_be(1), H256::from_low_u64_be(0x1234)); - a.commit_storage(&Default::default(), &mut db).unwrap(); - a.set_storage(H256::from_low_u64_be(1), H256::from_low_u64_be(0)); - a.commit_storage(&Default::default(), &mut db).unwrap(); - assert_eq!(a.storage_root().unwrap(), H256::from_str("c57e1afb758b07f8d2c8f13a3b6e44fa5ff94ab266facc5a4fd3f062426e50b2").unwrap()); - } - - #[test] - fn commit_code() { - let mut a = Account::new_contract(69.into(), 0.into(), KECCAK_NULL_RLP); - let mut db = new_memory_db(); - let mut db = AccountDBMut::new(&mut db, &Address::zero()); - a.init_code(vec![0x55, 0x44, 0xffu8]); - assert_eq!(a.code_filth, Filth::Dirty); - assert_eq!(a.code_size(), Some(3)); - a.commit_code(&mut db); - assert_eq!(a.code_hash(), H256::from_str("af231e631776a517ca23125370d542873eca1fb4d613ed9b5d5335a46ae5b7eb").unwrap()); - } - - #[test] - fn reset_code() { - let mut a = Account::new_contract(69.into(), 0.into(), KECCAK_NULL_RLP); - let mut db = new_memory_db(); - let mut db = AccountDBMut::new(&mut db, &Address::zero()); - a.init_code(vec![0x55, 0x44, 0xffu8]); - assert_eq!(a.code_filth, Filth::Dirty); - a.commit_code(&mut db); - assert_eq!(a.code_filth, Filth::Clean); - assert_eq!(a.code_hash(), H256::from_str("af231e631776a517ca23125370d542873eca1fb4d613ed9b5d5335a46ae5b7eb").unwrap()); - a.reset_code(vec![0x55]); - assert_eq!(a.code_filth, Filth::Dirty); - a.commit_code(&mut db); - assert_eq!(a.code_hash(), H256::from_str("37bf2238b11b68cdc8382cece82651b59d3c3988873b6e0f33d79694aa45f1be").unwrap()); - } - - #[test] - fn rlpio() { - let a = Account::new(69u8.into(), 0u8.into(), HashMap::new(), Bytes::new()); - let b = Account::from_rlp(&a.rlp()).unwrap(); - assert_eq!(a.balance(), b.balance()); - assert_eq!(a.nonce(), b.nonce()); - assert_eq!(a.code_hash(), b.code_hash()); - assert_eq!(a.storage_root(), b.storage_root()); - } - - #[test] - fn new_account() { - let a = Account::new(69u8.into(), 0u8.into(), HashMap::new(), Bytes::new()); - assert_eq!(a.rlp().to_hex(), "f8448045a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a0c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"); - assert_eq!(*a.balance(), 69u8.into()); - assert_eq!(*a.nonce(), 0u8.into()); - assert_eq!(a.code_hash(), KECCAK_EMPTY); - assert_eq!(a.storage_root().unwrap(), KECCAK_NULL_RLP); - } -} diff --git a/ethcore/src/state/mod.rs b/ethcore/src/state/mod.rs index 4f26823aea6..cae58224e73 100644 --- a/ethcore/src/state/mod.rs +++ b/ethcore/src/state/mod.rs @@ -51,12 +51,11 @@ use bytes::Bytes; use trie::{Trie, TrieError, Recorder}; use ethtrie::{TrieDB, Result as TrieResult}; -mod account; mod substate; pub mod backend; -pub use self::account::Account; +pub use state_account::Account; pub use self::backend::Backend; pub use self::substate::Substate; @@ -964,7 +963,7 @@ impl State { assert!(self.checkpoints.borrow().is_empty()); PodState::from(self.cache.borrow().iter().fold(BTreeMap::new(), |mut m, (add, opt)| { if let Some(ref acc) = opt.account { - m.insert(*add, PodAccount::from_account(acc)); + m.insert(*add, acc.to_pod()); } m })) @@ -1031,7 +1030,7 @@ impl State { } } - let mut pod_account = PodAccount::from_account(&account); + let mut pod_account = account.to_pod(); // cached one first pod_storage.append(&mut pod_account.storage); pod_account.storage = pod_storage; diff --git a/ethcore/state-account/Cargo.toml b/ethcore/state-account/Cargo.toml index 084f1bbe77b..dc1554da087 100644 --- a/ethcore/state-account/Cargo.toml +++ b/ethcore/state-account/Cargo.toml @@ -1,14 +1,14 @@ [package] +description = "Ethereum accounts, keeps track of changes to the code and storage." name = "state-account" version = "0.1.0" -authors = ["David Palm "] +authors = ["Parity Technologies "] edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] common-types = { path = "../types"} -ethcore-error = { path = "../ethcore-error" } ethereum-types = "0.6.0" ethtrie = { package = "patricia-trie-ethereum", path = "../../util/patricia-trie-ethereum" } hash-db = "0.12.4" @@ -18,6 +18,13 @@ kvdb = "0.1.0" log = "0.4" lru-cache = "0.1.2" parity-bytes = "0.1.0" +pod-account = { path = "../pod-account" } rlp = "0.4.0" serde = { version = "1.0", features = ["derive"] } trie-db = "0.12.4" + +[dev-dependencies] +ethcore = { path = ".." } +rlp_compress = { path = "../../util/rlp-compress" } +journaldb = { path = "../../util/journaldb" } +parity-bytes = "0.1.0" diff --git a/ethcore/state-account/src/lib.rs b/ethcore/state-account/src/lib.rs index 63b7bcb0f3e..24e136d1e7b 100644 --- a/ethcore/state-account/src/lib.rs +++ b/ethcore/state-account/src/lib.rs @@ -21,16 +21,14 @@ use std::sync::Arc; use std::collections::{HashMap, BTreeMap}; use keccak_hash::{KECCAK_EMPTY, KECCAK_NULL_RLP, keccak}; use ethereum_types::{H256, U256, Address, BigEndianHash}; -use ethcore_error::Error; use hash_db::HashDB; use keccak_hasher::KeccakHasher; use kvdb::DBValue; use parity_bytes::{Bytes, ToPretty}; use trie_db::{Trie, Recorder}; use ethtrie::{TrieFactory, TrieDB, SecTrieDB, Result as TrieResult}; -// TODO: sort out the cyclic dependency here -//use pod_account::PodAccount; -use rlp::{RlpStream, encode}; +use pod_account::PodAccount; +use rlp::{RlpStream, DecoderError, encode}; use lru_cache::LruCache; use common_types::basic_account::BasicAccount; @@ -120,23 +118,36 @@ impl Account { RefCell::new(LruCache::new(STORAGE_CACHE_ITEMS)) } - // TODO: sort out which should be depending on which -// /// General constructor. -// pub fn from_pod(pod: PodAccount) -> Account { -// Account { -// balance: pod.balance, -// nonce: pod.nonce, -// storage_root: KECCAK_NULL_RLP, -// storage_cache: Self::empty_storage_cache(), -// original_storage_cache: None, -// storage_changes: pod.storage.into_iter().collect(), -// code_hash: pod.code.as_ref().map_or(KECCAK_EMPTY, |c| keccak(c)), -// code_filth: Filth::Dirty, -// code_size: Some(pod.code.as_ref().map_or(0, |c| c.len())), -// code_cache: Arc::new(pod.code.map_or_else(|| { warn!("POD account with unknown code is being created! Assuming no code."); vec![] }, |c| c)), -// address_hash: Cell::new(None), -// } -// } + /// General constructor. + pub fn from_pod(pod: PodAccount) -> Account { + Account { + balance: pod.balance, + nonce: pod.nonce, + storage_root: KECCAK_NULL_RLP, + storage_cache: Self::empty_storage_cache(), + original_storage_cache: None, + storage_changes: pod.storage.into_iter().collect(), + code_hash: pod.code.as_ref().map_or(KECCAK_EMPTY, |c| keccak(c)), + code_filth: Filth::Dirty, + code_size: Some(pod.code.as_ref().map_or(0, |c| c.len())), + code_cache: Arc::new(pod.code.map_or_else(|| { warn!("POD account with unknown code is being created! Assuming no code."); vec![] }, |c| c)), + address_hash: Cell::new(None), + } + } + + /// Convert Account to a PodAccount. + /// NOTE: This will silently fail unless the account is fully cached. + pub fn to_pod(&self) -> PodAccount { + PodAccount { + balance: self.balance, + nonce: self.nonce, + storage: self.storage_changes.iter().fold(BTreeMap::new(), |mut m, (k, v)| { + m.insert(k.clone(), v.clone()); + m + }), + code: self.code().map(|x| x.to_vec()), + } + } /// Create a new account with the given balance. pub fn new_basic(balance: U256, nonce: U256) -> Account { @@ -156,10 +167,9 @@ impl Account { } /// Create a new account from RLP. - pub fn from_rlp(rlp: &[u8]) -> Result { + pub fn from_rlp(rlp: &[u8]) -> Result { ::rlp::decode::(rlp) .map(|ba| ba.into()) - .map_err(|e| e.into()) } /// Create a new contract account. @@ -620,9 +630,9 @@ mod tests { use rlp_compress::{compress, decompress, snapshot_swapper}; use ethereum_types::{H256, Address}; use journaldb::new_memory_db; - use bytes::Bytes; + use parity_bytes::Bytes; use super::*; - use account_db::*; + use ethcore::account_db::*; use std::str::FromStr; #[test] @@ -637,7 +647,7 @@ mod tests { #[test] fn storage_at() { let mut db = new_memory_db(); - let mut db = AccountDBMut::new(&mut db, &Address::zero()); + let mut db = AccountDBMut::from_hash(&mut db, keccak(&Address::zero())); let rlp = { let mut a = Account::new_contract(69.into(), 0.into(), KECCAK_NULL_RLP); a.set_storage(H256::zero(), H256::from_low_u64_be(0x1234)); @@ -656,7 +666,7 @@ mod tests { #[test] fn note_code() { let mut db = new_memory_db(); - let mut db = AccountDBMut::new(&mut db, &Address::zero()); + let mut db = AccountDBMut::from_hash(&mut db, keccak(&Address::zero())); let rlp = { let mut a = Account::new_contract(69.into(), 0.into(), KECCAK_NULL_RLP); @@ -676,7 +686,7 @@ mod tests { fn commit_storage() { let mut a = Account::new_contract(69.into(), 0.into(), KECCAK_NULL_RLP); let mut db = new_memory_db(); - let mut db = AccountDBMut::new(&mut db, &Address::zero()); + let mut db = AccountDBMut::from_hash(&mut db, keccak(&Address::zero())); a.set_storage(H256::from_low_u64_be(0), H256::from_low_u64_be(0x1234)); assert_eq!(a.storage_root(), None); a.commit_storage(&Default::default(), &mut db).unwrap(); @@ -687,7 +697,7 @@ mod tests { fn commit_remove_commit_storage() { let mut a = Account::new_contract(69.into(), 0.into(), KECCAK_NULL_RLP); let mut db = new_memory_db(); - let mut db = AccountDBMut::new(&mut db, &Address::zero()); + let mut db = AccountDBMut::from_hash(&mut db, keccak(&Address::zero())); a.set_storage(H256::from_low_u64_be(0), H256::from_low_u64_be(0x1234)); a.commit_storage(&Default::default(), &mut db).unwrap(); a.set_storage(H256::from_low_u64_be(1), H256::from_low_u64_be(0x1234)); @@ -701,7 +711,7 @@ mod tests { fn commit_code() { let mut a = Account::new_contract(69.into(), 0.into(), KECCAK_NULL_RLP); let mut db = new_memory_db(); - let mut db = AccountDBMut::new(&mut db, &Address::zero()); + let mut db = AccountDBMut::from_hash(&mut db, keccak(&Address::zero())); a.init_code(vec![0x55, 0x44, 0xffu8]); assert_eq!(a.code_filth, Filth::Dirty); assert_eq!(a.code_size(), Some(3)); @@ -713,7 +723,7 @@ mod tests { fn reset_code() { let mut a = Account::new_contract(69.into(), 0.into(), KECCAK_NULL_RLP); let mut db = new_memory_db(); - let mut db = AccountDBMut::new(&mut db, &Address::zero()); + let mut db = AccountDBMut::from_hash(&mut db, keccak(&Address::zero())); a.init_code(vec![0x55, 0x44, 0xffu8]); assert_eq!(a.code_filth, Filth::Dirty); a.commit_code(&mut db); From 47bc6fbeeefcfa4480227660cb1995e5683bb3e8 Mon Sep 17 00:00:00 2001 From: David Palm Date: Thu, 4 Jul 2019 13:14:29 +0200 Subject: [PATCH 03/87] remove template line --- ethcore/state-account/Cargo.toml | 2 -- 1 file changed, 2 deletions(-) diff --git a/ethcore/state-account/Cargo.toml b/ethcore/state-account/Cargo.toml index dc1554da087..3e2d8bc6a42 100644 --- a/ethcore/state-account/Cargo.toml +++ b/ethcore/state-account/Cargo.toml @@ -5,8 +5,6 @@ version = "0.1.0" authors = ["Parity Technologies "] edition = "2018" -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - [dependencies] common-types = { path = "../types"} ethereum-types = "0.6.0" From f8756bb725a4283de6e91b9c6be6fada12cb4abb Mon Sep 17 00:00:00 2001 From: David Palm Date: Thu, 4 Jul 2019 13:33:27 +0200 Subject: [PATCH 04/87] fix review feedback --- ethcore/src/account_db.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ethcore/src/account_db.rs b/ethcore/src/account_db.rs index bd61d9cc36a..7d7b3c61485 100644 --- a/ethcore/src/account_db.rs +++ b/ethcore/src/account_db.rs @@ -148,7 +148,7 @@ impl<'db> AccountDBMut<'db> { AccountDBMut { db, address_hash } } - /// Create an `AccountDB` from an `AccountDBMut`. + /// Create an `AccountDB` from an `AccountDBMut` (used in tests). pub fn immutable(&'db self) -> AccountDB<'db> { AccountDB { db: self.db, address_hash: self.address_hash.clone() } } From d54ebabb0043a7196e75d8cd5f65f3b5ada7cb58 Mon Sep 17 00:00:00 2001 From: David Palm Date: Thu, 4 Jul 2019 13:41:04 +0200 Subject: [PATCH 05/87] Remove test-only AccountDBMut::new --- ethcore/src/account_db.rs | 6 ------ ethcore/src/snapshot/account.rs | 20 ++++++++++---------- 2 files changed, 10 insertions(+), 16 deletions(-) diff --git a/ethcore/src/account_db.rs b/ethcore/src/account_db.rs index 7d7b3c61485..57c24e5b007 100644 --- a/ethcore/src/account_db.rs +++ b/ethcore/src/account_db.rs @@ -137,12 +137,6 @@ pub struct AccountDBMut<'db> { } impl<'db> AccountDBMut<'db> { - /// Create a new AccountDB from an address. - #[cfg(test)] - pub fn new(db: &'db mut dyn HashDB, address: &Address) -> Self { - Self::from_hash(db, keccak(address)) - } - /// Create a new `AccountDBMut` from an address' hash. pub fn from_hash(db: &'db mut dyn HashDB, address_hash: H256) -> Self { AccountDBMut { db, address_hash } diff --git a/ethcore/src/snapshot/account.rs b/ethcore/src/snapshot/account.rs index 84baf543d7b..1b437388ce8 100644 --- a/ethcore/src/snapshot/account.rs +++ b/ethcore/src/snapshot/account.rs @@ -253,7 +253,7 @@ mod tests { let p = Progress::default(); let fat_rlps = to_fat_rlps(&keccak(&addr), &account, &AccountDB::new(db.as_hash_db(), &addr), &mut Default::default(), usize::max_value(), usize::max_value(), &p).unwrap(); let fat_rlp = Rlp::new(&fat_rlps[0]).at(1).unwrap(); - assert_eq!(from_fat_rlp(&mut AccountDBMut::new(db.as_hash_db_mut(), &addr), fat_rlp, H256::zero()).unwrap().0, account); + assert_eq!(from_fat_rlp(&mut AccountDBMut::from_hash(db.as_hash_db_mut(), keccak(addr)), fat_rlp, H256::zero()).unwrap().0, account); } #[test] @@ -262,7 +262,7 @@ mod tests { let addr = Address::random(); let account = { - let acct_db = AccountDBMut::new(db.as_hash_db_mut(), &addr); + let acct_db = AccountDBMut::from_hash(db.as_hash_db_mut(), keccak(addr)); let mut root = KECCAK_NULL_RLP; fill_storage(acct_db, &mut root, &mut H256::zero()); BasicAccount { @@ -280,7 +280,7 @@ mod tests { let fat_rlp = to_fat_rlps(&keccak(&addr), &account, &AccountDB::new(db.as_hash_db(), &addr), &mut Default::default(), usize::max_value(), usize::max_value(), &p).unwrap(); let fat_rlp = Rlp::new(&fat_rlp[0]).at(1).unwrap(); - assert_eq!(from_fat_rlp(&mut AccountDBMut::new(db.as_hash_db_mut(), &addr), fat_rlp, H256::zero()).unwrap().0, account); + assert_eq!(from_fat_rlp(&mut AccountDBMut::from_hash(db.as_hash_db_mut(), keccak(addr)), fat_rlp, H256::zero()).unwrap().0, account); } #[test] @@ -289,7 +289,7 @@ mod tests { let addr = Address::random(); let account = { - let acct_db = AccountDBMut::new(db.as_hash_db_mut(), &addr); + let acct_db = AccountDBMut::from_hash(db.as_hash_db_mut(), keccak(addr)); let mut root = KECCAK_NULL_RLP; fill_storage(acct_db, &mut root, &mut H256::zero()); BasicAccount { @@ -309,7 +309,7 @@ mod tests { let mut restored_account = None; for rlp in fat_rlps { let fat_rlp = Rlp::new(&rlp).at(1).unwrap(); - restored_account = Some(from_fat_rlp(&mut AccountDBMut::new(db.as_hash_db_mut(), &addr), fat_rlp, root).unwrap().0); + restored_account = Some(from_fat_rlp(&mut AccountDBMut::from_hash(db.as_hash_db_mut(), keccak(addr)), fat_rlp, root).unwrap().0); root = restored_account.as_ref().unwrap().storage_root.clone(); } assert_eq!(restored_account, Some(account)); @@ -323,12 +323,12 @@ mod tests { let addr2 = Address::random(); let code_hash = { - let mut acct_db = AccountDBMut::new(db.as_hash_db_mut(), &addr1); + let mut acct_db = AccountDBMut::from_hash(db.as_hash_db_mut(), keccak(addr1)); acct_db.insert(EMPTY_PREFIX, b"this is definitely code") }; { - let mut acct_db = AccountDBMut::new(db.as_hash_db_mut(), &addr2); + let mut acct_db = AccountDBMut::from_hash(db.as_hash_db_mut(), keccak(addr2)); acct_db.emplace(code_hash.clone(), EMPTY_PREFIX, DBValue::from_slice(b"this is definitely code")); } @@ -356,11 +356,11 @@ mod tests { let fat_rlp1 = Rlp::new(&fat_rlp1[0]).at(1).unwrap(); let fat_rlp2 = Rlp::new(&fat_rlp2[0]).at(1).unwrap(); - let (acc, maybe_code) = from_fat_rlp(&mut AccountDBMut::new(db.as_hash_db_mut(), &addr2), fat_rlp2, H256::zero()).unwrap(); + let (acc, maybe_code) = from_fat_rlp(&mut AccountDBMut::from_hash(db.as_hash_db_mut(), keccak(addr2)), fat_rlp2, H256::zero()).unwrap(); assert!(maybe_code.is_none()); assert_eq!(acc, account2); - let (acc, maybe_code) = from_fat_rlp(&mut AccountDBMut::new(db.as_hash_db_mut(), &addr1), fat_rlp1, H256::zero()).unwrap(); + let (acc, maybe_code) = from_fat_rlp(&mut AccountDBMut::from_hash(db.as_hash_db_mut(), keccak(addr1)), fat_rlp1, H256::zero()).unwrap(); assert_eq!(maybe_code, Some(b"this is definitely code".to_vec())); assert_eq!(acc, account1); } @@ -368,6 +368,6 @@ mod tests { #[test] fn encoding_empty_acc() { let mut db = get_temp_state_db(); - assert_eq!(from_fat_rlp(&mut AccountDBMut::new(db.as_hash_db_mut(), &Address::zero()), Rlp::new(&::rlp::NULL_RLP), H256::zero()).unwrap(), (ACC_EMPTY, None)); + assert_eq!(from_fat_rlp(&mut AccountDBMut::from_hash(db.as_hash_db_mut(), keccak(Address::zero())), Rlp::new(&::rlp::NULL_RLP), H256::zero()).unwrap(), (ACC_EMPTY, None)); } } From 7792810f07b5d856814593c95a5858220eb4c23f Mon Sep 17 00:00:00 2001 From: David Palm Date: Thu, 4 Jul 2019 15:13:14 +0200 Subject: [PATCH 06/87] Extract AccountDB to account-db --- Cargo.lock | 15 +++++++++++++- ethcore/Cargo.toml | 1 + ethcore/account-db/Cargo.toml | 14 +++++++++++++ .../account_db.rs => account-db/src/lib.rs} | 20 ++++--------------- ethcore/src/lib.rs | 2 +- ethcore/src/snapshot/account.rs | 10 +++++----- ethcore/state-account/Cargo.toml | 2 +- ethcore/state-account/src/lib.rs | 2 +- 8 files changed, 41 insertions(+), 25 deletions(-) create mode 100644 ethcore/account-db/Cargo.toml rename ethcore/{src/account_db.rs => account-db/src/lib.rs} (94%) diff --git a/Cargo.lock b/Cargo.lock index d5c2ba0d420..b58980f6bce 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,5 +1,17 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +[[package]] +name = "account-db" +version = "0.1.0" +dependencies = [ + "ethereum-types 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "hash-db 0.12.4 (registry+https://github.com/rust-lang/crates.io-index)", + "keccak-hash 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "keccak-hasher 0.1.1", + "kvdb 0.1.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 = "aes" version = "0.3.2" @@ -864,6 +876,7 @@ dependencies = [ name = "ethcore" version = "1.12.0" dependencies = [ + "account-db 0.1.0", "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "blooms-db 0.1.0", "bn 0.4.4 (git+https://github.com/paritytech/bn)", @@ -3935,8 +3948,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" name = "state-account" version = "0.1.0" dependencies = [ + "account-db 0.1.0", "common-types 0.1.0", - "ethcore 1.12.0", "ethereum-types 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "hash-db 0.12.4 (registry+https://github.com/rust-lang/crates.io-index)", "journaldb 0.2.0", diff --git a/ethcore/Cargo.toml b/ethcore/Cargo.toml index 3a1f17e9017..55e0c8b6083 100644 --- a/ethcore/Cargo.toml +++ b/ethcore/Cargo.toml @@ -7,6 +7,7 @@ version = "1.12.0" authors = ["Parity Technologies "] [dependencies] +account-db = { path = "account-db" } ansi_term = "0.11" blooms-db = { path = "../util/blooms-db", optional = true } bn = { git = "https://github.com/paritytech/bn", default-features = false } diff --git a/ethcore/account-db/Cargo.toml b/ethcore/account-db/Cargo.toml new file mode 100644 index 00000000000..e3d0dea2121 --- /dev/null +++ b/ethcore/account-db/Cargo.toml @@ -0,0 +1,14 @@ +[package] +description = "DB backend wrapper for Account trie" +name = "account-db" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2018" + +[dependencies] +ethereum-types = "0.6" +hash-db = "0.12.4" +keccak-hash = "0.2.0" +keccak-hasher = { path = "../../util/keccak-hasher" } +kvdb = "0.1" +rlp = "0.4" diff --git a/ethcore/src/account_db.rs b/ethcore/account-db/src/lib.rs similarity index 94% rename from ethcore/src/account_db.rs rename to ethcore/account-db/src/lib.rs index 57c24e5b007..e88464dab81 100644 --- a/ethcore/src/account_db.rs +++ b/ethcore/account-db/src/lib.rs @@ -16,16 +16,13 @@ //! DB backend wrapper for Account trie use ethereum_types::H256; -use hash::{KECCAK_NULL_RLP, keccak}; +use keccak_hash::{KECCAK_NULL_RLP, keccak}; use hash_db::{HashDB, AsHashDB, Prefix}; use keccak_hasher::KeccakHasher; use kvdb::DBValue; use rlp::NULL_RLP; -#[cfg(test)] -use ethereum_types::Address; - -// combines a key with an address hash to ensure uniqueness. +// Combines a key with an address hash to ensure uniqueness. // leaves the first 96 bits untouched in order to support partial key lookup. #[inline] fn combine_key<'a>(address_hash: &'a H256, key: &'a H256) -> H256 { @@ -82,18 +79,9 @@ pub struct AccountDB<'db> { } impl<'db> AccountDB<'db> { - /// Create a new AccountDB from an address. - #[cfg(test)] - pub fn new(db: &'db dyn HashDB, address: &Address) -> Self { - Self::from_hash(db, keccak(address)) - } - - /// Create a new AcountDB from an address' hash. + /// Create a new AccountDB from an address' hash. pub fn from_hash(db: &'db dyn HashDB, address_hash: H256) -> Self { - AccountDB { - db: db, - address_hash: address_hash, - } + AccountDB { db, address_hash } } } diff --git a/ethcore/src/lib.rs b/ethcore/src/lib.rs index ea3347ffd51..d6835c34874 100644 --- a/ethcore/src/lib.rs +++ b/ethcore/src/lib.rs @@ -53,6 +53,7 @@ //! cargo build --release //! ``` +extern crate account_db; extern crate ansi_term; extern crate bn; extern crate common_types as types; @@ -169,7 +170,6 @@ pub mod state_db; pub mod trace; pub mod transaction_ext; pub mod verification; -pub mod account_db; mod externalities; mod factory; diff --git a/ethcore/src/snapshot/account.rs b/ethcore/src/snapshot/account.rs index 1b437388ce8..72bf2b66a24 100644 --- a/ethcore/src/snapshot/account.rs +++ b/ethcore/src/snapshot/account.rs @@ -251,7 +251,7 @@ mod tests { let thin_rlp = ::rlp::encode(&account); assert_eq!(::rlp::decode::(&thin_rlp).unwrap(), account); let p = Progress::default(); - let fat_rlps = to_fat_rlps(&keccak(&addr), &account, &AccountDB::new(db.as_hash_db(), &addr), &mut Default::default(), usize::max_value(), usize::max_value(), &p).unwrap(); + let fat_rlps = to_fat_rlps(&keccak(&addr), &account, &AccountDB::from_hash(db.as_hash_db(), keccak(addr)), &mut Default::default(), usize::max_value(), usize::max_value(), &p).unwrap(); let fat_rlp = Rlp::new(&fat_rlps[0]).at(1).unwrap(); assert_eq!(from_fat_rlp(&mut AccountDBMut::from_hash(db.as_hash_db_mut(), keccak(addr)), fat_rlp, H256::zero()).unwrap().0, account); } @@ -278,7 +278,7 @@ mod tests { let p = Progress::default(); - let fat_rlp = to_fat_rlps(&keccak(&addr), &account, &AccountDB::new(db.as_hash_db(), &addr), &mut Default::default(), usize::max_value(), usize::max_value(), &p).unwrap(); + let fat_rlp = to_fat_rlps(&keccak(&addr), &account, &AccountDB::from_hash(db.as_hash_db(), keccak(addr)), &mut Default::default(), usize::max_value(), usize::max_value(), &p).unwrap(); let fat_rlp = Rlp::new(&fat_rlp[0]).at(1).unwrap(); assert_eq!(from_fat_rlp(&mut AccountDBMut::from_hash(db.as_hash_db_mut(), keccak(addr)), fat_rlp, H256::zero()).unwrap().0, account); } @@ -304,7 +304,7 @@ mod tests { assert_eq!(::rlp::decode::(&thin_rlp).unwrap(), account); let p = Progress::default(); - let fat_rlps = to_fat_rlps(&keccak(addr), &account, &AccountDB::new(db.as_hash_db(), &addr), &mut Default::default(), 500, 1000, &p).unwrap(); + let fat_rlps = to_fat_rlps(&keccak(addr), &account, &AccountDB::from_hash(db.as_hash_db(), keccak(addr)), &mut Default::default(), 500, 1000, &p).unwrap(); let mut root = KECCAK_NULL_RLP; let mut restored_account = None; for rlp in fat_rlps { @@ -349,8 +349,8 @@ mod tests { let mut used_code = HashSet::new(); let p1 = Progress::default(); let p2 = Progress::default(); - let fat_rlp1 = to_fat_rlps(&keccak(&addr1), &account1, &AccountDB::new(db.as_hash_db(), &addr1), &mut used_code, usize::max_value(), usize::max_value(), &p1).unwrap(); - let fat_rlp2 = to_fat_rlps(&keccak(&addr2), &account2, &AccountDB::new(db.as_hash_db(), &addr2), &mut used_code, usize::max_value(), usize::max_value(), &p2).unwrap(); + let fat_rlp1 = to_fat_rlps(&keccak(&addr1), &account1, &AccountDB::from_hash(db.as_hash_db(), keccak(addr1)), &mut used_code, usize::max_value(), usize::max_value(), &p1).unwrap(); + let fat_rlp2 = to_fat_rlps(&keccak(&addr2), &account2, &AccountDB::from_hash(db.as_hash_db(), keccak(addr2)), &mut used_code, usize::max_value(), usize::max_value(), &p2).unwrap(); assert_eq!(used_code.len(), 1); let fat_rlp1 = Rlp::new(&fat_rlp1[0]).at(1).unwrap(); diff --git a/ethcore/state-account/Cargo.toml b/ethcore/state-account/Cargo.toml index 3e2d8bc6a42..e50c23fc71e 100644 --- a/ethcore/state-account/Cargo.toml +++ b/ethcore/state-account/Cargo.toml @@ -22,7 +22,7 @@ serde = { version = "1.0", features = ["derive"] } trie-db = "0.12.4" [dev-dependencies] -ethcore = { path = ".." } +account-db = { path = "../account-db" } rlp_compress = { path = "../../util/rlp-compress" } journaldb = { path = "../../util/journaldb" } parity-bytes = "0.1.0" diff --git a/ethcore/state-account/src/lib.rs b/ethcore/state-account/src/lib.rs index 24e136d1e7b..d2f773c3cd8 100644 --- a/ethcore/state-account/src/lib.rs +++ b/ethcore/state-account/src/lib.rs @@ -632,7 +632,7 @@ mod tests { use journaldb::new_memory_db; use parity_bytes::Bytes; use super::*; - use ethcore::account_db::*; + use account_db::*; use std::str::FromStr; #[test] From a55ca1e11d2cc18fbdc1c2b06ecd4743384ab77a Mon Sep 17 00:00:00 2001 From: David Palm Date: Thu, 4 Jul 2019 15:54:31 +0200 Subject: [PATCH 07/87] =?UTF-8?q?Move=20Substate=20to=20state-account=20?= =?UTF-8?q?=E2=80=93=20wip?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cargo.lock | 46 ++++++++++++++++++- ethcore/Cargo.toml | 1 - ethcore/src/lib.rs | 1 - ethcore/src/state/mod.rs | 36 +++++++-------- ethcore/state-account/Cargo.toml | 5 ++ .../state-account/src/{lib.rs => account.rs} | 38 ++++++++------- .../state => state-account/src}/backend.rs | 14 +++--- .../state => state-account/src}/substate.rs | 23 ++++++++-- 8 files changed, 115 insertions(+), 49 deletions(-) rename ethcore/state-account/src/{lib.rs => account.rs} (98%) rename ethcore/{src/state => state-account/src}/backend.rs (98%) rename ethcore/{src/state => state-account/src}/substate.rs (85%) diff --git a/Cargo.lock b/Cargo.lock index b58980f6bce..25b678c4b71 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -917,7 +917,6 @@ dependencies = [ "lru-cache 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "macros 0.1.0", "memory-cache 0.1.0", - "memory-db 0.12.4 (registry+https://github.com/rust-lang/crates.io-index)", "num 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", "num_cpus 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", "parity-bytes 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2265,6 +2264,14 @@ dependencies = [ "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "lock_api" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "log" version = "0.3.9" @@ -3113,6 +3120,16 @@ dependencies = [ "parking_lot_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "parking_lot" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lock_api 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot_core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "parking_lot_core" version = "0.3.1" @@ -3140,6 +3157,21 @@ dependencies = [ "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "parking_lot_core" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "patricia-trie-ethereum" version = "0.1.0" @@ -3770,6 +3802,11 @@ name = "scopeguard" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "scopeguard" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "scrypt" version = "0.2.0" @@ -3951,6 +3988,7 @@ dependencies = [ "account-db 0.1.0", "common-types 0.1.0", "ethereum-types 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "evm 0.1.0", "hash-db 0.12.4 (registry+https://github.com/rust-lang/crates.io-index)", "journaldb 0.2.0", "keccak-hash 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3958,7 +3996,9 @@ dependencies = [ "kvdb 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "lru-cache 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "memory-db 0.12.4 (registry+https://github.com/rust-lang/crates.io-index)", "parity-bytes 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)", "patricia-trie-ethereum 0.1.0", "pod-account 0.1.0", "rlp 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -4969,6 +5009,7 @@ dependencies = [ "checksum linked-hash-map 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "70fb39025bc7cdd76305867c4eccf2f2dcf6e9a57f5b21a93e1c2d86cd03ec9e" "checksum local-encoding 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e1ceb20f39ff7ae42f3ff9795f3986b1daad821caaa1e1732a0944103a5a1a66" "checksum lock_api 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "775751a3e69bde4df9b38dd00a1b5d6ac13791e4223d4a0506577f0dd27cfb7a" +"checksum lock_api 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ed946d4529956a20f2d63ebe1b69996d5a2137c91913fe3ebbeff957f5bca7ff" "checksum log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b" "checksum log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c84ec4b527950aa83a329754b01dbe3f58361d1c5efacd1f6d68c494d08a17c6" "checksum lru-cache 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c" @@ -5024,8 +5065,10 @@ dependencies = [ "checksum parity-ws 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2fec5048fba72a2e01baeb0d08089db79aead4b57e2443df172fb1840075a233" "checksum parking_lot 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "f0802bff09003b291ba756dc7e79313e51cc31667e94afbe847def490424cde5" "checksum parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ab41b4aed082705d1056416ae4468b6ea99d52599ecf3169b00088d43113e337" +"checksum parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fa7767817701cce701d5585b9c4db3cdd02086398322c1d7e8bf5094a96a2ce7" "checksum parking_lot_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ad7f7e6ebdc79edff6fdcb87a55b620174f7a989e3eb31b65231f4af57f00b8c" "checksum parking_lot_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "94c8c7923936b28d546dfd14d4472eaf34c99b14e1c973a32b3e6d4eb04298c9" +"checksum parking_lot_core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cb88cb1cb3790baa6776844f968fea3be44956cf184fa1be5a03341f5491278c" "checksum pbkdf2 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "006c038a43a45995a9670da19e67600114740e8511d4333bf97a56e66a7542d9" "checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" "checksum pest 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0fce5d8b5cc33983fc74f78ad552b5522ab41442c4ca91606e4236eb4b5ceefc" @@ -5089,6 +5132,7 @@ dependencies = [ "checksum same-file 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "10f7794e2fda7f594866840e95f5c5962e886e228e68b6505885811a94dd728c" "checksum scoped_threadpool 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "1d51f5df5af43ab3f1360b429fa5e0152ac5ce8c0bd6485cae490332e96846a8" "checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27" +"checksum scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b42e15e59b18a828bbf5c58ea01debb36b9b096346de35d941dcb89009f24a0d" "checksum scrypt 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "656c79d0e90d0ab28ac86bf3c3d10bfbbac91450d3f190113b4e76d9fec3cfdd" "checksum sct 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2f5adf8fbd58e1b1b52699dc8bed2630faecb6d8c7bee77d009d6bbe4af569b9" "checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" diff --git a/ethcore/Cargo.toml b/ethcore/Cargo.toml index 55e0c8b6083..ff39b332efd 100644 --- a/ethcore/Cargo.toml +++ b/ethcore/Cargo.toml @@ -46,7 +46,6 @@ log = "0.4" lru-cache = "0.1" macros = { path = "../util/macros" } memory-cache = { path = "../util/memory-cache" } -memory-db = "0.12.4" num = { version = "0.1", default-features = false, features = ["bigint"] } num_cpus = "1.2" parity-bytes = "0.1" diff --git a/ethcore/src/lib.rs b/ethcore/src/lib.rs index d6835c34874..d75aa2afa43 100644 --- a/ethcore/src/lib.rs +++ b/ethcore/src/lib.rs @@ -82,7 +82,6 @@ extern crate kvdb_memorydb; extern crate len_caching_lock; extern crate lru_cache; extern crate memory_cache; -extern crate memory_db; extern crate num; extern crate num_cpus; extern crate parity_bytes as bytes; diff --git a/ethcore/src/state/mod.rs b/ethcore/src/state/mod.rs index cae58224e73..6a43b4ce5c2 100644 --- a/ethcore/src/state/mod.rs +++ b/ethcore/src/state/mod.rs @@ -51,13 +51,12 @@ use bytes::Bytes; use trie::{Trie, TrieError, Recorder}; use ethtrie::{TrieDB, Result as TrieResult}; -mod substate; - -pub mod backend; - -pub use state_account::Account; -pub use self::backend::Backend; -pub use self::substate::Substate; +// TODO: need to be pub? +pub use state_account::account::Account; +// TODO: need to be pub? +pub use state_account::backend::Backend; +pub use state_account::backend; +pub use state_account::substate::{CleanupMode, Substate}; /// Used to return information about an `State::apply` operation. pub struct ApplyOutcome { @@ -234,7 +233,7 @@ pub fn prove_transaction_virtual + Send + Syn env_info: &EnvInfo, factories: Factories, ) -> Option<(Bytes, Vec)> { - use self::backend::Proving; + use state_account::backend::Proving; let backend = Proving::new(db); let res = State::from_existing( @@ -322,16 +321,17 @@ enum RequireCache { Code, } -/// Mode of dealing with null accounts. -#[derive(PartialEq)] -pub enum CleanupMode<'a> { - /// Create accounts which would be null. - ForceCreate, - /// Don't delete null accounts upon touching, but also don't create them. - NoEmpty, - /// Mark all touched accounts. - TrackTouched(&'a mut HashSet
), -} +// TODO: put this back +///// Mode of dealing with null accounts. +//#[derive(PartialEq)] +//pub enum CleanupMode<'a> { +// /// Create accounts which would be null. +// ForceCreate, +// /// Don't delete null accounts upon touching, but also don't create them. +// NoEmpty, +// /// Mark all touched accounts. +// TrackTouched(&'a mut HashSet
), +//} /// Provides subset of `State` methods to query state information pub trait StateInfo { diff --git a/ethcore/state-account/Cargo.toml b/ethcore/state-account/Cargo.toml index e50c23fc71e..f4f53716bbc 100644 --- a/ethcore/state-account/Cargo.toml +++ b/ethcore/state-account/Cargo.toml @@ -21,6 +21,11 @@ rlp = "0.4.0" serde = { version = "1.0", features = ["derive"] } trie-db = "0.12.4" +parking_lot = "0.8.0" +memory-db = "0.12.4" +journaldb = { path = "../../util/journaldb" } +evm = { path = "../evm" } + [dev-dependencies] account-db = { path = "../account-db" } rlp_compress = { path = "../../util/rlp-compress" } diff --git a/ethcore/state-account/src/lib.rs b/ethcore/state-account/src/account.rs similarity index 98% rename from ethcore/state-account/src/lib.rs rename to ethcore/state-account/src/account.rs index d2f773c3cd8..b38d54bb976 100644 --- a/ethcore/state-account/src/lib.rs +++ b/ethcore/state-account/src/account.rs @@ -15,24 +15,25 @@ // along with Parity Ethereum. If not, see . //! Single account in the system. -use log::{warn, trace}; +use std::cell::{Cell, RefCell}; +use std::collections::{BTreeMap, HashMap}; use std::fmt; use std::sync::Arc; -use std::collections::{HashMap, BTreeMap}; -use keccak_hash::{KECCAK_EMPTY, KECCAK_NULL_RLP, keccak}; -use ethereum_types::{H256, U256, Address, BigEndianHash}; + +use ethereum_types::{Address, BigEndianHash, H256, U256}; use hash_db::HashDB; -use keccak_hasher::KeccakHasher; +use keccak_hash::{keccak, KECCAK_EMPTY, KECCAK_NULL_RLP}; use kvdb::DBValue; -use parity_bytes::{Bytes, ToPretty}; -use trie_db::{Trie, Recorder}; -use ethtrie::{TrieFactory, TrieDB, SecTrieDB, Result as TrieResult}; -use pod_account::PodAccount; -use rlp::{RlpStream, DecoderError, encode}; +use log::{trace, warn}; use lru_cache::LruCache; -use common_types::basic_account::BasicAccount; +use parity_bytes::{Bytes, ToPretty}; +use rlp::{DecoderError, encode, RlpStream}; +use trie_db::{Recorder, Trie}; -use std::cell::{RefCell, Cell}; +use common_types::basic_account::BasicAccount; +use ethtrie::{Result as TrieResult, SecTrieDB, TrieDB, TrieFactory}; +use keccak_hasher::KeccakHasher; +use pod_account::PodAccount; const STORAGE_CACHE_ITEMS: usize = 8192; @@ -627,13 +628,16 @@ impl fmt::Debug for Account { #[cfg(test)] mod tests { - use rlp_compress::{compress, decompress, snapshot_swapper}; - use ethereum_types::{H256, Address}; - use journaldb::new_memory_db; + use std::str::FromStr; + + use ethereum_types::{Address, H256}; use parity_bytes::Bytes; - use super::*; + use account_db::*; - use std::str::FromStr; + use journaldb::new_memory_db; + use rlp_compress::{compress, decompress, snapshot_swapper}; + + use super::*; #[test] fn account_compress() { diff --git a/ethcore/src/state/backend.rs b/ethcore/state-account/src/backend.rs similarity index 98% rename from ethcore/src/state/backend.rs rename to ethcore/state-account/src/backend.rs index c9175f26980..563aa1023dc 100644 --- a/ethcore/src/state/backend.rs +++ b/ethcore/state-account/src/backend.rs @@ -21,17 +21,19 @@ //! should become general over time to the point where not even a //! merkle trie is strictly necessary. -use std::collections::{HashSet, HashMap}; +use std::collections::{HashMap, HashSet}; use std::sync::Arc; -use state::Account; -use parking_lot::Mutex; use ethereum_types::{Address, H256}; -use memory_db::{MemoryDB, HashKey}; -use hash_db::{AsHashDB, HashDB, Prefix, EMPTY_PREFIX}; +use hash_db::{AsHashDB, EMPTY_PREFIX, HashDB, Prefix}; use kvdb::DBValue; -use keccak_hasher::KeccakHasher; +use memory_db::{HashKey, MemoryDB}; +use parking_lot::Mutex; + use journaldb::AsKeyedHashDB; +use keccak_hasher::KeccakHasher; + +use crate::account::Account; /// State backend. See module docs for more details. pub trait Backend: Send { diff --git a/ethcore/src/state/substate.rs b/ethcore/state-account/src/substate.rs similarity index 85% rename from ethcore/src/state/substate.rs rename to ethcore/state-account/src/substate.rs index cb6981437e7..eea85b79017 100644 --- a/ethcore/src/state/substate.rs +++ b/ethcore/state-account/src/substate.rs @@ -16,10 +16,22 @@ //! Execution environment substate. use std::collections::HashSet; + use ethereum_types::Address; -use types::log_entry::LogEntry; -use evm::{Schedule, CleanDustMode}; -use super::CleanupMode; +use common_types::log_entry::LogEntry; +use evm::{CleanDustMode, Schedule}; + +// TODO: Move back to state/mod.rs? +/// Mode of dealing with null accounts. +#[derive(PartialEq)] +pub enum CleanupMode<'a> { + /// Create accounts which would be null. + ForceCreate, + /// Don't delete null accounts upon touching, but also don't create them. + NoEmpty, + /// Mark all touched accounts. + TrackTouched(&'a mut HashSet
), +} /// State changes which should be applied in finalize, /// after transaction is fully executed. @@ -68,8 +80,9 @@ impl Substate { #[cfg(test)] mod tests { - use super::{Substate, Address}; - use types::log_entry::LogEntry; + use ethereum_types::Address; + use common_types::log_entry::LogEntry; + use super::Substate; #[test] fn created() { From 201dac730eefbcfea7de6c96687eaeab0f677401 Mon Sep 17 00:00:00 2001 From: David Palm Date: Thu, 4 Jul 2019 15:55:00 +0200 Subject: [PATCH 08/87] Add lib.rs --- ethcore/state-account/src/lib.rs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 ethcore/state-account/src/lib.rs diff --git a/ethcore/state-account/src/lib.rs b/ethcore/state-account/src/lib.rs new file mode 100644 index 00000000000..68a453cccc8 --- /dev/null +++ b/ethcore/state-account/src/lib.rs @@ -0,0 +1,19 @@ +// 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 . + +pub mod account; +pub mod backend; +pub mod substate; From e23acadb737ca485d47eb43ea9a7b954a061534e Mon Sep 17 00:00:00 2001 From: David Palm Date: Thu, 4 Jul 2019 16:02:12 +0200 Subject: [PATCH 09/87] cleanup --- ethcore/src/state/mod.rs | 2 -- ethcore/src/state_db.rs | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/ethcore/src/state/mod.rs b/ethcore/src/state/mod.rs index 6a43b4ce5c2..8ddc6447658 100644 --- a/ethcore/src/state/mod.rs +++ b/ethcore/src/state/mod.rs @@ -51,9 +51,7 @@ use bytes::Bytes; use trie::{Trie, TrieError, Recorder}; use ethtrie::{TrieDB, Result as TrieResult}; -// TODO: need to be pub? pub use state_account::account::Account; -// TODO: need to be pub? pub use state_account::backend::Backend; pub use state_account::backend; pub use state_account::substate::{CleanupMode, Substate}; diff --git a/ethcore/src/state_db.rs b/ethcore/src/state_db.rs index cb00d2132e8..bde87cb5282 100644 --- a/ethcore/src/state_db.rs +++ b/ethcore/src/state_db.rs @@ -284,7 +284,7 @@ impl StateDB { if is_best { let acc = account.account.0; if let Some(&mut Some(ref mut existing)) = cache.accounts.get_mut(&account.address) { - if let Some(new) = acc { + if let Some(new) = acc { if account.modified { existing.overwrite_with(new); } From e0701e44fac18b150719a1677eba82b38e2792dc Mon Sep 17 00:00:00 2001 From: David Palm Date: Thu, 4 Jul 2019 16:15:18 +0200 Subject: [PATCH 10/87] test failure --- ethcore/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ethcore/src/lib.rs b/ethcore/src/lib.rs index a6dec0c1c2d..3324575cf80 100644 --- a/ethcore/src/lib.rs +++ b/ethcore/src/lib.rs @@ -170,7 +170,6 @@ pub mod state_db; pub mod trace; pub mod transaction_ext; pub mod verification; -pub mod account_db; mod externalities; mod factory; @@ -186,3 +185,4 @@ pub mod test_helpers; pub use executive::contract_address; pub use evm::CreateContractAddress; pub use trie::TrieSpec; +pub use account_db; From b42d16add6f057b03ae062427eda243918342df8 Mon Sep 17 00:00:00 2001 From: David Palm Date: Thu, 4 Jul 2019 16:35:24 +0200 Subject: [PATCH 11/87] test failure 2 --- ethcore/src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/ethcore/src/lib.rs b/ethcore/src/lib.rs index 3324575cf80..d6835c34874 100644 --- a/ethcore/src/lib.rs +++ b/ethcore/src/lib.rs @@ -185,4 +185,3 @@ pub mod test_helpers; pub use executive::contract_address; pub use evm::CreateContractAddress; pub use trie::TrieSpec; -pub use account_db; From e45d13f481602c75a281093dd0c5fea80765f6a0 Mon Sep 17 00:00:00 2001 From: David Palm Date: Thu, 4 Jul 2019 16:51:49 +0200 Subject: [PATCH 12/87] third time's the charm --- ethcore/state-account/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ethcore/state-account/src/lib.rs b/ethcore/state-account/src/lib.rs index 24e136d1e7b..d2f773c3cd8 100644 --- a/ethcore/state-account/src/lib.rs +++ b/ethcore/state-account/src/lib.rs @@ -632,7 +632,7 @@ mod tests { use journaldb::new_memory_db; use parity_bytes::Bytes; use super::*; - use ethcore::account_db::*; + use account_db::*; use std::str::FromStr; #[test] From 1c0722b4f4dbda9c595f2c55ed20ccdeb53ef923 Mon Sep 17 00:00:00 2001 From: David Palm Date: Thu, 4 Jul 2019 22:29:08 +0200 Subject: [PATCH 13/87] Add factories crate --- ethcore/factories/Cargo.toml | 15 ++++++++ ethcore/factories/src/lib.rs | 70 ++++++++++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+) create mode 100644 ethcore/factories/Cargo.toml create mode 100644 ethcore/factories/src/lib.rs diff --git a/ethcore/factories/Cargo.toml b/ethcore/factories/Cargo.toml new file mode 100644 index 00000000000..77c078caa52 --- /dev/null +++ b/ethcore/factories/Cargo.toml @@ -0,0 +1,15 @@ +[package] +description = "Collection of factories" +name = "factories" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2018" + +[dependencies] +trie-db = "0.12.4" +ethtrie = { package = "patricia-trie-ethereum", path = "../../util/patricia-trie-ethereum" } +account-db = { path = "../account-db" } +evm = { path = "../evm" } +vm = { path = "../vm" } +wasm = { path = "../wasm" } +keccak-hasher = { path = "../../util/keccak-hasher" } diff --git a/ethcore/factories/src/lib.rs b/ethcore/factories/src/lib.rs new file mode 100644 index 00000000000..c2f4a71db0f --- /dev/null +++ b/ethcore/factories/src/lib.rs @@ -0,0 +1,70 @@ +// 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 . + +use trie_db::TrieFactory; +use ethtrie::RlpCodec; +use account_db::Factory as AccountFactory; +use evm::{Factory as EvmFactory, VMType}; +use vm::{Exec, ActionParams, Schedule}; +use wasm::WasmInterpreter; +use keccak_hasher::KeccakHasher; + +const WASM_MAGIC_NUMBER: &'static [u8; 4] = b"\0asm"; + +/// Virtual machine factory +#[derive(Default, Clone)] +pub struct VmFactory { + evm: EvmFactory, +} + +impl VmFactory { + pub fn create( + &self, + params: ActionParams, + schedule: &Schedule, + depth: usize + ) -> Box { + if schedule.wasm.is_some() + && params.code.as_ref().map_or(false, |code| { + code.len() > 4 && &code[0..4] == WASM_MAGIC_NUMBER + }) { + Box::new(WasmInterpreter::new(params)) + } else { + self.evm.create(params, schedule, depth) + } + } + + pub fn new(evm: VMType, cache_size: usize) -> Self { + VmFactory { evm: EvmFactory::new(evm, cache_size) } + } +} + +impl From for VmFactory { + fn from(evm: EvmFactory) -> Self { + VmFactory { evm: evm } + } +} + +/// Collection of factories. +#[derive(Default, Clone)] +pub struct Factories { + /// factory for evm. + pub vm: VmFactory, + /// factory for tries. + pub trie: TrieFactory, + /// factory for account databases. + pub accountdb: AccountFactory, +} From 2114c12621ec8eae590c7aafb269239d1d69449e Mon Sep 17 00:00:00 2001 From: David Palm Date: Thu, 4 Jul 2019 22:30:30 +0200 Subject: [PATCH 14/87] Use new factories crate --- ethcore/src/block.rs | 4 +- ethcore/src/client/client.rs | 2 +- ethcore/src/client/evm_test_client.rs | 2 +- ethcore/src/executive.rs | 2 +- ethcore/src/factory.rs | 62 --------------------------- ethcore/src/lib.rs | 3 +- ethcore/src/spec/spec.rs | 2 +- ethcore/src/state/mod.rs | 12 +++--- ethcore/src/test_helpers.rs | 2 +- 9 files changed, 14 insertions(+), 77 deletions(-) delete mode 100644 ethcore/src/factory.rs diff --git a/ethcore/src/block.rs b/ethcore/src/block.rs index 4b22633a977..3a584a196db 100644 --- a/ethcore/src/block.rs +++ b/ethcore/src/block.rs @@ -40,7 +40,7 @@ use ethereum_types::{H256, U256, Address, Bloom}; use engines::Engine; use error::{Error, BlockError}; -use factory::Factories; +use factories::Factories; use state_db::StateDB; use state::State; use trace::Tracing; @@ -550,7 +550,7 @@ mod tests { use engines::Engine; use vm::LastHashes; use error::Error; - use factory::Factories; + use factories::Factories; use state_db::StateDB; use ethereum_types::Address; use std::sync::Arc; diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index 1744f22aca7..ff0900b1a2a 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -66,7 +66,7 @@ use error::{ QueueError, Error as EthcoreError, EthcoreResult, }; use executive::{Executive, Executed, TransactOptions, contract_address}; -use factory::{Factories, VmFactory}; +use factories::{Factories, VmFactory}; use miner::{Miner, MinerService}; use snapshot::{self, io as snapshot_io, SnapshotClient}; use spec::Spec; diff --git a/ethcore/src/client/evm_test_client.rs b/ethcore/src/client/evm_test_client.rs index 1e56e8c2d99..30cc2576d9c 100644 --- a/ethcore/src/client/evm_test_client.rs +++ b/ethcore/src/client/evm_test_client.rs @@ -23,7 +23,7 @@ use {factory, journaldb, trie, kvdb_memorydb}; use kvdb::{self, KeyValueDB}; use {state, state_db, client, executive, trace, db, spec, pod_state}; use types::{log_entry, receipt, transaction}; -use factory::Factories; +use factories::Factories; use evm::{VMType, FinalizationResult}; use vm::{self, ActionParams}; use ethtrie; diff --git a/ethcore/src/executive.rs b/ethcore/src/executive.rs index 8c8ace25c65..8202bcc9b15 100644 --- a/ethcore/src/executive.rs +++ b/ethcore/src/executive.rs @@ -29,7 +29,7 @@ use vm::{ self, EnvInfo, CreateContractAddress, ReturnData, CleanDustMode, ActionParams, ActionValue, Schedule, TrapError, ResumeCall, ResumeCreate }; -use factory::VmFactory; +use factories::VmFactory; use externalities::*; use trace::{self, Tracer, VMTracer}; use types::transaction::{Action, SignedTransaction}; diff --git a/ethcore/src/factory.rs b/ethcore/src/factory.rs deleted file mode 100644 index 574cbeeb39d..00000000000 --- a/ethcore/src/factory.rs +++ /dev/null @@ -1,62 +0,0 @@ -// 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 . - -use trie::TrieFactory; -use ethtrie::RlpCodec; -use account_db::Factory as AccountFactory; -use evm::{Factory as EvmFactory, VMType}; -use vm::{Exec, ActionParams, Schedule}; -use wasm::WasmInterpreter; -use keccak_hasher::KeccakHasher; - -const WASM_MAGIC_NUMBER: &'static [u8; 4] = b"\0asm"; - -/// Virtual machine factory -#[derive(Default, Clone)] -pub struct VmFactory { - evm: EvmFactory, -} - -impl VmFactory { - pub fn create(&self, params: ActionParams, schedule: &Schedule, depth: usize) -> Box { - if schedule.wasm.is_some() && params.code.as_ref().map_or(false, |code| code.len() > 4 && &code[0..4] == WASM_MAGIC_NUMBER) { - Box::new(WasmInterpreter::new(params)) - } else { - self.evm.create(params, schedule, depth) - } - } - - pub fn new(evm: VMType, cache_size: usize) -> Self { - VmFactory { evm: EvmFactory::new(evm, cache_size) } - } -} - -impl From for VmFactory { - fn from(evm: EvmFactory) -> Self { - VmFactory { evm: evm } - } -} - -/// Collection of factories. -#[derive(Default, Clone)] -pub struct Factories { - /// factory for evm. - pub vm: VmFactory, - /// factory for tries. - pub trie: TrieFactory, - /// factory for account databases. - pub accountdb: AccountFactory, -} diff --git a/ethcore/src/lib.rs b/ethcore/src/lib.rs index d75aa2afa43..aac8b6718ac 100644 --- a/ethcore/src/lib.rs +++ b/ethcore/src/lib.rs @@ -69,6 +69,7 @@ extern crate ethcore_miner; extern crate ethereum_types; extern crate ethjson; extern crate ethkey; +extern crate factories; extern crate futures; extern crate hash_db; extern crate itertools; @@ -105,7 +106,6 @@ extern crate triehash_ethereum as triehash; extern crate unexpected; extern crate using_queue; extern crate vm; -extern crate wasm; #[cfg(test)] extern crate rand_xorshift; @@ -171,7 +171,6 @@ pub mod transaction_ext; pub mod verification; mod externalities; -mod factory; mod tx_filter; #[cfg(test)] diff --git a/ethcore/src/spec/spec.rs b/ethcore/src/spec/spec.rs index 7a409f52188..5e1d116c071 100644 --- a/ethcore/src/spec/spec.rs +++ b/ethcore/src/spec/spec.rs @@ -40,7 +40,7 @@ use engines::{ }; use error::Error; use executive::Executive; -use factory::Factories; +use factories::Factories; use machine::Machine; use pod_state::PodState; use spec::Genesis; diff --git a/ethcore/src/state/mod.rs b/ethcore/src/state/mod.rs index 0fed1d333c4..bd4a0069d0b 100644 --- a/ethcore/src/state/mod.rs +++ b/ethcore/src/state/mod.rs @@ -31,7 +31,7 @@ use machine::Machine; use vm::EnvInfo; use error::Error; use executive::{Executive, TransactOptions}; -use factory::Factories; +use factories::{Factories, VmFactory}; use trace::{self, FlatTrace, VMTrace}; use pod_account::*; use pod_state::{self, PodState}; @@ -40,7 +40,6 @@ use executed::{Executed, ExecutionError}; use types::state_diff::StateDiff; use types::transaction::SignedTransaction; use state_db::StateDB; -use factory::VmFactory; use ethereum_types::{H256, U256, Address}; use hash_db::{HashDB, AsHashDB}; @@ -51,10 +50,11 @@ use bytes::Bytes; use trie::{Trie, TrieError, Recorder}; use ethtrie::{TrieDB, Result as TrieResult}; -pub use state_account::account::Account; -pub use state_account::backend::Backend; -pub use state_account::backend; -pub use state_account::substate::{CleanupMode, Substate}; +pub use state_account::{ + account::Account, + backend::{self, Backend}, + substate::{CleanupMode, Substate}, +}; /// Used to return information about an `State::apply` operation. pub struct ApplyOutcome { diff --git a/ethcore/src/test_helpers.rs b/ethcore/src/test_helpers.rs index 5137aade025..f365f61cf53 100644 --- a/ethcore/src/test_helpers.rs +++ b/ethcore/src/test_helpers.rs @@ -41,7 +41,7 @@ use types::views::BlockView; use block::{OpenBlock, Drain}; use client::{Client, ClientConfig, ChainInfo, ImportBlock, ChainNotify, ChainMessageType, PrepareOpenBlock}; -use factory::Factories; +use factories::Factories; use miner::Miner; use spec::Spec; use state::*; From 80ededaf682cbdcb91df33d360f1a31a422f6aba Mon Sep 17 00:00:00 2001 From: David Palm Date: Thu, 4 Jul 2019 22:31:41 +0200 Subject: [PATCH 15/87] Use factories crate --- Cargo.lock | 17 ++++++++++++++++- ethcore/Cargo.toml | 2 +- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 25b678c4b71..2c696e94529 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -901,6 +901,7 @@ dependencies = [ "ethjson 0.1.0", "ethkey 0.3.0", "evm 0.1.0", + "factories 0.1.0", "fetch 0.1.0", "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", "hash-db 0.12.4 (registry+https://github.com/rust-lang/crates.io-index)", @@ -948,7 +949,6 @@ dependencies = [ "unexpected 0.1.0", "using_queue 0.1.0", "vm 0.1.0", - "wasm 0.1.0", ] [[package]] @@ -1481,6 +1481,19 @@ dependencies = [ "vm 0.1.0", ] +[[package]] +name = "factories" +version = "0.1.0" +dependencies = [ + "account-db 0.1.0", + "evm 0.1.0", + "keccak-hasher 0.1.1", + "patricia-trie-ethereum 0.1.0", + "trie-db 0.12.4 (registry+https://github.com/rust-lang/crates.io-index)", + "vm 0.1.0", + "wasm 0.1.0", +] + [[package]] name = "failsafe" version = "0.3.0" @@ -3989,6 +4002,7 @@ dependencies = [ "common-types 0.1.0", "ethereum-types 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "evm 0.1.0", + "factories 0.1.0", "hash-db 0.12.4 (registry+https://github.com/rust-lang/crates.io-index)", "journaldb 0.2.0", "keccak-hash 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -4005,6 +4019,7 @@ dependencies = [ "rlp_compress 0.1.0", "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", "trie-db 0.12.4 (registry+https://github.com/rust-lang/crates.io-index)", + "vm 0.1.0", ] [[package]] diff --git a/ethcore/Cargo.toml b/ethcore/Cargo.toml index ff39b332efd..c4f5bcb5c7d 100644 --- a/ethcore/Cargo.toml +++ b/ethcore/Cargo.toml @@ -30,6 +30,7 @@ ethereum-types = "0.6.0" ethjson = { path = "../json" } ethkey = { path = "../accounts/ethkey" } evm = { path = "evm" } +factories = { path = "factories" } futures = "0.1" hash-db = "0.12.4" parity-util-mem = "0.1" @@ -71,7 +72,6 @@ triehash-ethereum = { version = "0.2", path = "../util/triehash-ethereum" } unexpected = { path = "../util/unexpected" } using_queue = { path = "../miner/using-queue" } vm = { path = "vm" } -wasm = { path = "wasm" } rand_xorshift = "0.1.1" [dev-dependencies] From d9ceaa6a7ba801d83664056c6d554e0250bc694d Mon Sep 17 00:00:00 2001 From: David Palm Date: Fri, 5 Jul 2019 00:27:31 +0200 Subject: [PATCH 16/87] Extract trace --- Cargo.lock | 21 +++++ ethcore/Cargo.toml | 1 + ethcore/src/client/evm_test_client.rs | 4 +- ethcore/src/client/mod.rs | 1 - ethcore/src/client/trace.rs | 40 --------- ethcore/src/lib.rs | 4 +- ethcore/trace/Cargo.toml | 25 ++++++ ethcore/{src/trace => trace/src}/config.rs | 2 +- ethcore/{src/trace => trace/src}/db.rs | 63 ++++++++++---- .../trace => trace/src}/executive_tracer.rs | 7 +- ethcore/{src/trace => trace/src}/import.rs | 4 +- .../{src/trace/mod.rs => trace/src/lib.rs} | 12 +-- .../{src/trace => trace/src}/noop_tracer.rs | 6 +- .../{src/trace => trace/src}/types/error.rs | 2 +- .../{src/trace => trace/src}/types/filter.rs | 83 +++++++++---------- .../{src/trace => trace/src}/types/flat.rs | 12 +-- .../trace => trace/src}/types/localized.rs | 2 +- ethcore/{src/trace => trace/src}/types/mod.rs | 0 .../{src/trace => trace/src}/types/trace.rs | 6 +- 19 files changed, 170 insertions(+), 125 deletions(-) delete mode 100644 ethcore/src/client/trace.rs create mode 100644 ethcore/trace/Cargo.toml rename ethcore/{src/trace => trace/src}/config.rs (97%) rename ethcore/{src/trace => trace/src}/db.rs (93%) rename ethcore/{src/trace => trace/src}/executive_tracer.rs (97%) rename ethcore/{src/trace => trace/src}/import.rs (95%) rename ethcore/{src/trace/mod.rs => trace/src/lib.rs} (97%) rename ethcore/{src/trace => trace/src}/noop_tracer.rs (95%) rename ethcore/{src/trace => trace/src}/types/error.rs (99%) rename ethcore/{src/trace => trace/src}/types/filter.rs (90%) rename ethcore/{src/trace => trace/src}/types/flat.rs (95%) rename ethcore/{src/trace => trace/src}/types/localized.rs (97%) rename ethcore/{src/trace => trace/src}/types/mod.rs (100%) rename ethcore/{src/trace => trace/src}/types/trace.rs (99%) diff --git a/Cargo.lock b/Cargo.lock index 2c696e94529..7fbf3b02646 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -942,6 +942,7 @@ dependencies = [ "stats 0.1.0", "tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "time-utils 0.1.0", + "trace 0.1.0", "trace-time 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "trie-db 0.12.4 (registry+https://github.com/rust-lang/crates.io-index)", "trie-standardmap 0.12.4 (registry+https://github.com/rust-lang/crates.io-index)", @@ -4439,6 +4440,26 @@ dependencies = [ "fxhash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "trace" +version = "0.1.0" +dependencies = [ + "common-types 0.1.0", + "ethcore 1.12.0", + "ethcore-blockchain 0.1.0", + "ethcore-db 0.1.0", + "ethereum-types 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "evm 0.1.0", + "kvdb 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.6 (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.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rlp 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rlp_derive 0.1.0", + "vm 0.1.0", +] + [[package]] name = "trace-time" version = "0.1.1" diff --git a/ethcore/Cargo.toml b/ethcore/Cargo.toml index c4f5bcb5c7d..7ca1456364e 100644 --- a/ethcore/Cargo.toml +++ b/ethcore/Cargo.toml @@ -67,6 +67,7 @@ state-account = { path = "state-account" } stats = { path = "../util/stats" } tempdir = { version = "0.3", optional = true } time-utils = { path = "../util/time-utils" } +trace = { path = "trace" } trace-time = "0.1" triehash-ethereum = { version = "0.2", path = "../util/triehash-ethereum" } unexpected = { path = "../util/unexpected" } diff --git a/ethcore/src/client/evm_test_client.rs b/ethcore/src/client/evm_test_client.rs index 30cc2576d9c..b331c214501 100644 --- a/ethcore/src/client/evm_test_client.rs +++ b/ethcore/src/client/evm_test_client.rs @@ -19,7 +19,7 @@ use std::fmt; use std::sync::Arc; use ethereum_types::{H256, U256, H160}; -use {factory, journaldb, trie, kvdb_memorydb}; +use {factories, journaldb, trie, kvdb_memorydb}; use kvdb::{self, KeyValueDB}; use {state, state_db, client, executive, trace, db, spec, pod_state}; use types::{log_entry, receipt, transaction}; @@ -142,7 +142,7 @@ impl<'a> EvmTestClient<'a> { fn factories(trie_spec: trie::TrieSpec) -> Factories { Factories { - vm: factory::VmFactory::new(VMType::Interpreter, 5 * 1024), + vm: factories::VmFactory::new(VMType::Interpreter, 5 * 1024), trie: trie::TrieFactory::new(trie_spec), accountdb: Default::default(), } diff --git a/ethcore/src/client/mod.rs b/ethcore/src/client/mod.rs index 51b4f53db84..972daad1eb4 100644 --- a/ethcore/src/client/mod.rs +++ b/ethcore/src/client/mod.rs @@ -25,7 +25,6 @@ mod evm_test_client; mod io_message; #[cfg(any(test, feature = "test-helpers"))] mod test_client; -mod trace; pub use self::client::*; pub use self::config::{Mode, ClientConfig, DatabaseCompactionProfile, BlockChainConfig, VMType}; diff --git a/ethcore/src/client/trace.rs b/ethcore/src/client/trace.rs deleted file mode 100644 index 7be957b33ec..00000000000 --- a/ethcore/src/client/trace.rs +++ /dev/null @@ -1,40 +0,0 @@ -// 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 . - -//! Bridge between Tracedb and Blockchain. - -use blockchain::{BlockChain, BlockProvider, TransactionAddress}; -use ethereum_types::H256; -use trace::DatabaseExtras as TraceDatabaseExtras; -use types::BlockNumber; - -impl TraceDatabaseExtras for BlockChain { - fn block_hash(&self, block_number: BlockNumber) -> Option { - (self as &dyn BlockProvider).block_hash(block_number) - } - - fn transaction_hash(&self, block_number: BlockNumber, tx_position: usize) -> Option { - (self as &dyn BlockProvider).block_hash(block_number) - .and_then(|block_hash| { - let tx_address = TransactionAddress { - block_hash: block_hash, - index: tx_position - }; - self.transaction(&tx_address) - }) - .map(|tx| tx.hash()) - } -} diff --git a/ethcore/src/lib.rs b/ethcore/src/lib.rs index aac8b6718ac..f7f20d1b15b 100644 --- a/ethcore/src/lib.rs +++ b/ethcore/src/lib.rs @@ -102,6 +102,7 @@ extern crate serde; extern crate stats; extern crate state_account; extern crate time_utils; +extern crate trace; extern crate triehash_ethereum as triehash; extern crate unexpected; extern crate using_queue; @@ -166,7 +167,8 @@ pub mod snapshot; pub mod spec; pub mod state; pub mod state_db; -pub mod trace; +// TODO: need to reexport trace? +//pub mod trace; pub mod transaction_ext; pub mod verification; diff --git a/ethcore/trace/Cargo.toml b/ethcore/trace/Cargo.toml new file mode 100644 index 00000000000..9fbc5612a2c --- /dev/null +++ b/ethcore/trace/Cargo.toml @@ -0,0 +1,25 @@ +[package] +description = "Transaction tracing" +name = "trace" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2018" + +[dependencies] +common-types = { path = "../types"} +ethcore-blockchain = { path = "../blockchain" } +ethcore-db = { path = "../db" } +ethereum-types = "0.6" +evm = { path = "../evm" } +kvdb = "0.1.0" +log = "0.4" +parity-bytes = "0.1.0" +parity-util-mem = "0.1" +parking_lot = "0.7.0" +rlp = "0.4.0" +rlp_derive = { path = "../../util/rlp-derive" } +vm = { path = "../vm" } + +[dev-dependencies] +# Used for test helpers +ethcore = { path = "..", features = ["test-helpers"] } diff --git a/ethcore/src/trace/config.rs b/ethcore/trace/src/config.rs similarity index 97% rename from ethcore/src/trace/config.rs rename to ethcore/trace/src/config.rs index 72fc1765511..b0c7cd348fc 100644 --- a/ethcore/src/trace/config.rs +++ b/ethcore/trace/src/config.rs @@ -22,7 +22,7 @@ pub struct Config { /// Indicates if tracing should be enabled or not. /// If it's None, it will be automatically configured. pub enabled: bool, - /// Preferef cache-size. + /// Preferred cache-size. pub pref_cache_size: usize, /// Max cache-size. pub max_cache_size: usize, diff --git a/ethcore/src/trace/db.rs b/ethcore/trace/src/db.rs similarity index 93% rename from ethcore/src/trace/db.rs rename to ethcore/trace/src/db.rs index baeca316665..38b76b0b431 100644 --- a/ethcore/src/trace/db.rs +++ b/ethcore/trace/src/db.rs @@ -18,17 +18,22 @@ use std::collections::HashMap; use std::sync::Arc; use parity_util_mem::MallocSizeOfExt; - -use blockchain::BlockChainDB; -use db::cache_manager::CacheManager; -use db::{self, Key, Writable, Readable, CacheUpdatePolicy}; +use ethcore_db::{ + self as db, + cache_manager::CacheManager, + Key, Writable, Readable, CacheUpdatePolicy, +}; +use ethcore_blockchain::BlockChainDB; use ethereum_types::{H256, H264}; -use kvdb::{DBTransaction}; +use kvdb::DBTransaction; use parking_lot::RwLock; -use types::BlockNumber; +use common_types::BlockNumber; +use ethcore_blockchain::{BlockChain, BlockProvider, TransactionAddress}; -use trace::{LocalizedTrace, Config, Filter, Database as TraceDatabase, ImportRequest, DatabaseExtras}; -use trace::flat::{FlatTrace, FlatBlockTraces, FlatTransactionTraces}; +use crate::{ + LocalizedTrace, Config, Filter, Database as TraceDatabase, ImportRequest, DatabaseExtras, + flat::{FlatTrace, FlatBlockTraces, FlatTransactionTraces}, +}; const TRACE_DB_VER: &'static [u8] = b"1.0"; @@ -332,19 +337,43 @@ impl TraceDatabase for TraceDB where T: DatabaseExtras { } } +/// Bridge between TraceDb and Blockchain. +impl DatabaseExtras for BlockChain { + fn block_hash(&self, block_number: BlockNumber) -> Option { + (self as &dyn BlockProvider).block_hash(block_number) + } + + fn transaction_hash(&self, block_number: BlockNumber, tx_position: usize) -> Option { + (self as &dyn BlockProvider).block_hash(block_number) + .and_then(|block_hash| { + let tx_address = TransactionAddress { + block_hash, + index: tx_position + }; + self.transaction(&tx_address) + }) + .map(|tx| tx.hash()) + } +} + #[cfg(test)] mod tests { - use std::collections::HashMap; - use std::sync::Arc; + use std::{ + collections::HashMap, + sync::Arc, + }; + use common_types::BlockNumber; + use ethcore::test_helpers::new_db; use ethereum_types::{H256, U256, Address}; - use kvdb::{DBTransaction}; - use types::BlockNumber; - use trace::{Config, TraceDB, Database as TraceDatabase, DatabaseExtras, ImportRequest}; - use trace::{Filter, LocalizedTrace, AddressesFilter, TraceError}; - use trace::trace::{Call, Action, Res}; - use trace::flat::{FlatTrace, FlatBlockTraces, FlatTransactionTraces}; use evm::CallType; - use test_helpers::new_db; + use kvdb::DBTransaction; + + use crate::{ + Config, TraceDB, Database as TraceDatabase, DatabaseExtras, ImportRequest, + Filter, LocalizedTrace, AddressesFilter, TraceError, + trace::{Call, Action, Res}, + flat::{FlatTrace, FlatBlockTraces, FlatTransactionTraces} + }; struct NoopExtras; diff --git a/ethcore/src/trace/executive_tracer.rs b/ethcore/trace/src/executive_tracer.rs similarity index 97% rename from ethcore/src/trace/executive_tracer.rs rename to ethcore/trace/src/executive_tracer.rs index fdd9428b5d2..6ece943fd96 100644 --- a/ethcore/src/trace/executive_tracer.rs +++ b/ethcore/trace/src/executive_tracer.rs @@ -18,8 +18,11 @@ use ethereum_types::{U256, Address}; use vm::{Error as VmError, ActionParams}; -use trace::trace::{Call, Create, Action, Res, CreateResult, CallResult, VMTrace, VMOperation, VMExecutedOperation, MemoryDiff, StorageDiff, Suicide, Reward, RewardType}; -use trace::{Tracer, VMTracer, FlatTrace}; +use log::debug; +use crate::{ + Tracer, VMTracer, FlatTrace, + trace::{Call, Create, Action, Res, CreateResult, CallResult, VMTrace, VMOperation, VMExecutedOperation, MemoryDiff, StorageDiff, Suicide, Reward, RewardType}, +}; /// Simple executive tracer. Traces all calls and creates. Ignores delegatecalls. #[derive(Default)] diff --git a/ethcore/src/trace/import.rs b/ethcore/trace/src/import.rs similarity index 95% rename from ethcore/src/trace/import.rs rename to ethcore/trace/src/import.rs index e9ec9c77bad..a1a6bb1a548 100644 --- a/ethcore/src/trace/import.rs +++ b/ethcore/trace/src/import.rs @@ -16,9 +16,9 @@ //! Traces import request. use ethereum_types::H256; -use types::BlockNumber; +use common_types::BlockNumber; -use trace::FlatBlockTraces; +use crate::FlatBlockTraces; /// Traces import request. pub struct ImportRequest { diff --git a/ethcore/src/trace/mod.rs b/ethcore/trace/src/lib.rs similarity index 97% rename from ethcore/src/trace/mod.rs rename to ethcore/trace/src/lib.rs index d3586614754..8fc443d92e0 100644 --- a/ethcore/src/trace/mod.rs +++ b/ethcore/trace/src/lib.rs @@ -16,6 +16,13 @@ //! Tracing +use common_types::BlockNumber; +use ethereum_types::{H256, U256, Address}; +use kvdb::DBTransaction; +use vm::{Error as VmError, ActionParams}; +// The MallocSizeOf derive looks for this in the root +use parity_util_mem as malloc_size_of; + mod config; mod db; mod executive_tracer; @@ -36,11 +43,6 @@ pub use self::types::trace::{VMTrace, VMOperation, VMExecutedOperation, MemoryDi pub use self::types::flat::{FlatTrace, FlatTransactionTraces, FlatBlockTraces}; pub use self::types::filter::{Filter, AddressesFilter}; -use ethereum_types::{H256, U256, Address}; -use kvdb::DBTransaction; -use vm::{Error as VmError, ActionParams}; -use types::BlockNumber; - /// This trait is used by executive to build traces. pub trait Tracer: Send { /// Data returned when draining the Tracer. diff --git a/ethcore/src/trace/noop_tracer.rs b/ethcore/trace/src/noop_tracer.rs similarity index 95% rename from ethcore/src/trace/noop_tracer.rs rename to ethcore/trace/src/noop_tracer.rs index 62cce8e0111..32f7b61cfef 100644 --- a/ethcore/src/trace/noop_tracer.rs +++ b/ethcore/trace/src/noop_tracer.rs @@ -18,8 +18,10 @@ use ethereum_types::{U256, Address}; use vm::{Error as VmError, ActionParams}; -use trace::{Tracer, VMTracer, FlatTrace}; -use trace::trace::{VMTrace, RewardType}; +use crate::{ + Tracer, VMTracer, FlatTrace, + trace::{VMTrace, RewardType} +}; /// Nonoperative tracer. Does not trace anything. pub struct NoopTracer; diff --git a/ethcore/src/trace/types/error.rs b/ethcore/trace/src/types/error.rs similarity index 99% rename from ethcore/src/trace/types/error.rs rename to ethcore/trace/src/types/error.rs index 5c775dcb6ec..b57c1f972cd 100644 --- a/ethcore/src/trace/types/error.rs +++ b/ethcore/trace/src/types/error.rs @@ -137,7 +137,7 @@ impl Decodable for Error { #[cfg(test)] mod tests { - use rlp::*; + use rlp::RlpStream; use super::Error; #[test] diff --git a/ethcore/src/trace/types/filter.rs b/ethcore/trace/src/types/filter.rs similarity index 90% rename from ethcore/src/trace/types/filter.rs rename to ethcore/trace/src/types/filter.rs index f4c8598d98f..936de0c4000 100644 --- a/ethcore/src/trace/types/filter.rs +++ b/ethcore/trace/src/types/filter.rs @@ -18,8 +18,7 @@ use std::ops::Range; use ethereum_types::{Address, Bloom, BloomInput}; -use trace::flat::FlatTrace; -use super::trace::{Action, Res}; +use crate::{flat::FlatTrace, trace::{Action, Res}}; /// Addresses filter. /// @@ -126,10 +125,12 @@ impl Filter { #[cfg(test)] mod tests { use ethereum_types::{Address, Bloom, BloomInput}; - use trace::trace::{Action, Call, Res, Create, CreateResult, Suicide, Reward}; - use trace::flat::FlatTrace; - use trace::{Filter, AddressesFilter, TraceError, RewardType}; use evm::CallType; + use crate::{ + Filter, AddressesFilter, TraceError, RewardType, + trace::{Action, Call, Res, Create, CreateResult, Suicide, Reward}, + flat::FlatTrace, + }; #[test] fn empty_trace_filter_bloom_possibilities() { @@ -386,42 +387,40 @@ mod tests { assert!(f2.matches(&trace)); } - #[test] - fn filter_match_failed_contract_creation_fix_9822() { - - let f0 = Filter { - range: (0..0), - from_address: vec![Address::from_low_u64_be(1)].into(), - to_address: vec![].into(), - }; - - let f1 = Filter { - range: (0..0), - from_address: vec![].into(), - to_address: vec![].into(), - }; - - let f2 = Filter { - range: (0..0), - from_address: vec![].into(), - to_address: vec![Address::from_low_u64_be(2)].into(), - }; - - let trace = FlatTrace { - action: Action::Create(Create { - from: Address::from_low_u64_be(1), - gas: 4.into(), - init: vec![0x5], - value: 3.into(), - }), - result: Res::FailedCall(TraceError::BadInstruction), - trace_address: vec![].into_iter().collect(), - subtraces: 0 - }; - - assert!(f0.matches(&trace)); - assert!(f1.matches(&trace)); - assert!(!f2.matches(&trace)); - } + #[test] + fn filter_match_failed_contract_creation_fix_9822() { + let f0 = Filter { + range: (0..0), + from_address: vec![Address::from_low_u64_be(1)].into(), + to_address: vec![].into(), + }; + + let f1 = Filter { + range: (0..0), + from_address: vec![].into(), + to_address: vec![].into(), + }; + + let f2 = Filter { + range: (0..0), + from_address: vec![].into(), + to_address: vec![Address::from_low_u64_be(2)].into(), + }; + + let trace = FlatTrace { + action: Action::Create(Create { + from: Address::from_low_u64_be(1), + gas: 4.into(), + init: vec![0x5], + value: 3.into(), + }), + result: Res::FailedCall(TraceError::BadInstruction), + trace_address: vec![].into_iter().collect(), + subtraces: 0 + }; + assert!(f0.matches(&trace)); + assert!(f1.matches(&trace)); + assert!(!f2.matches(&trace)); + } } diff --git a/ethcore/src/trace/types/flat.rs b/ethcore/trace/src/types/flat.rs similarity index 95% rename from ethcore/src/trace/types/flat.rs rename to ethcore/trace/src/types/flat.rs index 17ea1d37887..203eb7f5db4 100644 --- a/ethcore/src/trace/types/flat.rs +++ b/ethcore/trace/src/types/flat.rs @@ -17,6 +17,7 @@ //! Flat trace module use rlp::{Rlp, RlpStream, Decodable, Encodable, DecoderError}; +use rlp_derive::{RlpEncodableWrapper, RlpDecodableWrapper}; use parity_util_mem::MallocSizeOf; use ethereum_types::Bloom; use super::trace::{Action, Res}; @@ -73,7 +74,7 @@ impl Decodable for FlatTrace { /// Represents all traces produced by a single transaction. #[derive(Debug, PartialEq, Clone, RlpEncodableWrapper, RlpDecodableWrapper, MallocSizeOf)] -pub struct FlatTransactionTraces(Vec); +pub struct FlatTransactionTraces(pub(crate) Vec); impl From> for FlatTransactionTraces { fn from(v: Vec) -> Self { @@ -96,7 +97,7 @@ impl Into> for FlatTransactionTraces { /// Represents all traces produced by transactions in a single block. #[derive(Debug, PartialEq, Clone, Default, RlpEncodableWrapper, RlpDecodableWrapper, MallocSizeOf)] -pub struct FlatBlockTraces(Vec); +pub struct FlatBlockTraces(pub(crate) Vec); impl From> for FlatBlockTraces { fn from(v: Vec) -> Self { @@ -120,10 +121,11 @@ impl Into> for FlatBlockTraces { #[cfg(test)] mod tests { use rlp::*; - use super::{FlatBlockTraces, FlatTransactionTraces, FlatTrace}; - use trace::trace::{Action, Res, CallResult, Call, Suicide, Reward}; + use crate::{ + FlatBlockTraces, FlatTransactionTraces, FlatTrace, + trace::{Action, Res, CallResult, Call, Suicide, Reward, RewardType} + }; use evm::CallType; - use trace::RewardType; #[test] fn encode_flat_transaction_traces() { diff --git a/ethcore/src/trace/types/localized.rs b/ethcore/trace/src/types/localized.rs similarity index 97% rename from ethcore/src/trace/types/localized.rs rename to ethcore/trace/src/types/localized.rs index 330d23a7289..28a8788e89f 100644 --- a/ethcore/src/trace/types/localized.rs +++ b/ethcore/trace/src/types/localized.rs @@ -18,7 +18,7 @@ use ethereum_types::H256; use super::trace::{Action, Res}; -use types::BlockNumber; +use common_types::BlockNumber; /// Localized trace. #[derive(Debug, PartialEq, Clone)] diff --git a/ethcore/src/trace/types/mod.rs b/ethcore/trace/src/types/mod.rs similarity index 100% rename from ethcore/src/trace/types/mod.rs rename to ethcore/trace/src/types/mod.rs diff --git a/ethcore/src/trace/types/trace.rs b/ethcore/trace/src/types/trace.rs similarity index 99% rename from ethcore/src/trace/types/trace.rs rename to ethcore/trace/src/types/trace.rs index ba10c9069e2..a0a92003157 100644 --- a/ethcore/src/trace/types/trace.rs +++ b/ethcore/trace/src/types/trace.rs @@ -14,12 +14,12 @@ // You should have received a copy of the GNU General Public License // along with Parity Ethereum. If not, see . -//! Tracing datatypes. +//! Tracing data types. use ethereum_types::{U256, Address, Bloom, BloomInput}; -use bytes::Bytes; +use parity_bytes::Bytes; use rlp::{Rlp, RlpStream, Encodable, DecoderError, Decodable}; - +use rlp_derive::{RlpEncodable, RlpDecodable}; use vm::ActionParams; use evm::CallType; use super::error::Error; From 9f3aa7396b60cb7f78cd519ce59458b81fa6cd4f Mon Sep 17 00:00:00 2001 From: David Palm Date: Fri, 5 Jul 2019 00:31:31 +0200 Subject: [PATCH 17/87] Fix tests --- ethcore/src/tests/client.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ethcore/src/tests/client.rs b/ethcore/src/tests/client.rs index 343751b0481..63f0ea60f66 100644 --- a/ethcore/src/tests/client.rs +++ b/ethcore/src/tests/client.rs @@ -349,7 +349,7 @@ fn transaction_proof() { let proof = client.prove_transaction(transaction.clone(), BlockId::Latest).unwrap().1; let backend = state::backend::ProofCheck::new(&proof); - let mut factories = ::factory::Factories::default(); + let mut factories = ::factories::Factories::default(); factories.accountdb = ::account_db::Factory::Plain; // raw state values, no mangled keys. let root = *client.best_block_header().state_root(); From a6a25ba2769cefae227f0c5e7190ef15c9487e8c Mon Sep 17 00:00:00 2001 From: David Palm Date: Fri, 5 Jul 2019 01:31:22 +0200 Subject: [PATCH 18/87] Sort out parity-util-mem and parking_lot --- Cargo.lock | 62 ++++++++++++++-------------------------------- ethcore/src/lib.rs | 4 +-- 2 files changed, 21 insertions(+), 45 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7fbf3b02646..484ddb4989c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -679,6 +679,19 @@ dependencies = [ "syn 0.15.26 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "derive_more" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.26 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "difference" version = "1.0.0" @@ -1376,6 +1389,7 @@ dependencies = [ "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "parity-crypto 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "parity-wordlist 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2278,14 +2292,6 @@ dependencies = [ "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "lock_api" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "log" version = "0.3.9" @@ -3134,16 +3140,6 @@ dependencies = [ "parking_lot_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "parking_lot" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "lock_api 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot_core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "parking_lot_core" version = "0.3.1" @@ -3171,21 +3167,6 @@ dependencies = [ "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "parking_lot_core" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "patricia-trie-ethereum" version = "0.1.0" @@ -3816,11 +3797,6 @@ name = "scopeguard" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "scopeguard" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "scrypt" version = "0.2.0" @@ -4001,6 +3977,7 @@ version = "0.1.0" dependencies = [ "account-db 0.1.0", "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)", "evm 0.1.0", "factories 0.1.0", @@ -4013,12 +3990,14 @@ dependencies = [ "lru-cache 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "memory-db 0.12.4 (registry+https://github.com/rust-lang/crates.io-index)", "parity-bytes 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)", + "parity-util-mem 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "patricia-trie-ethereum 0.1.0", "pod-account 0.1.0", "rlp 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "rlp_compress 0.1.0", "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", + "trace 0.1.0", "trie-db 0.12.4 (registry+https://github.com/rust-lang/crates.io-index)", "vm 0.1.0", ] @@ -4949,6 +4928,7 @@ dependencies = [ "checksum ctr 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "022cd691704491df67d25d006fe8eca083098253c4d43516c2206479c58c6736" "checksum ctrlc 1.1.1 (git+https://github.com/paritytech/rust-ctrlc.git)" = "" "checksum derive_more 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fbe9f11be34f800b3ecaaed0ec9ec2e015d1d0ba0c8644c1310f73d6e8994615" +"checksum derive_more 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a141330240c921ec6d074a3e188a7c7ef95668bb95e7d44fa0e5778ec2a7afe" "checksum difference 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b3304d19798a8e067e48d8e69b2c37f0b5e9b4e462504ad9e27e9f3fce02bba8" "checksum digest 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "03b072242a8cbaf9c145665af9d250c59af3b958f83ed6824e13533cf76d5b90" "checksum digest 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "05f47366984d3ad862010e22c7ce81a7dbcaebbdfb37241a620f8b6596ee135c" @@ -5045,7 +5025,6 @@ dependencies = [ "checksum linked-hash-map 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "70fb39025bc7cdd76305867c4eccf2f2dcf6e9a57f5b21a93e1c2d86cd03ec9e" "checksum local-encoding 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e1ceb20f39ff7ae42f3ff9795f3986b1daad821caaa1e1732a0944103a5a1a66" "checksum lock_api 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "775751a3e69bde4df9b38dd00a1b5d6ac13791e4223d4a0506577f0dd27cfb7a" -"checksum lock_api 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ed946d4529956a20f2d63ebe1b69996d5a2137c91913fe3ebbeff957f5bca7ff" "checksum log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b" "checksum log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c84ec4b527950aa83a329754b01dbe3f58361d1c5efacd1f6d68c494d08a17c6" "checksum lru-cache 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c" @@ -5101,10 +5080,8 @@ dependencies = [ "checksum parity-ws 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2fec5048fba72a2e01baeb0d08089db79aead4b57e2443df172fb1840075a233" "checksum parking_lot 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "f0802bff09003b291ba756dc7e79313e51cc31667e94afbe847def490424cde5" "checksum parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ab41b4aed082705d1056416ae4468b6ea99d52599ecf3169b00088d43113e337" -"checksum parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fa7767817701cce701d5585b9c4db3cdd02086398322c1d7e8bf5094a96a2ce7" "checksum parking_lot_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ad7f7e6ebdc79edff6fdcb87a55b620174f7a989e3eb31b65231f4af57f00b8c" "checksum parking_lot_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "94c8c7923936b28d546dfd14d4472eaf34c99b14e1c973a32b3e6d4eb04298c9" -"checksum parking_lot_core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cb88cb1cb3790baa6776844f968fea3be44956cf184fa1be5a03341f5491278c" "checksum pbkdf2 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "006c038a43a45995a9670da19e67600114740e8511d4333bf97a56e66a7542d9" "checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" "checksum pest 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0fce5d8b5cc33983fc74f78ad552b5522ab41442c4ca91606e4236eb4b5ceefc" @@ -5168,7 +5145,6 @@ dependencies = [ "checksum same-file 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "10f7794e2fda7f594866840e95f5c5962e886e228e68b6505885811a94dd728c" "checksum scoped_threadpool 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "1d51f5df5af43ab3f1360b429fa5e0152ac5ce8c0bd6485cae490332e96846a8" "checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27" -"checksum scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b42e15e59b18a828bbf5c58ea01debb36b9b096346de35d941dcb89009f24a0d" "checksum scrypt 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "656c79d0e90d0ab28ac86bf3c3d10bfbbac91450d3f190113b4e76d9fec3cfdd" "checksum sct 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2f5adf8fbd58e1b1b52699dc8bed2630faecb6d8c7bee77d009d6bbe4af569b9" "checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" diff --git a/ethcore/src/lib.rs b/ethcore/src/lib.rs index f7f20d1b15b..bb3c035615c 100644 --- a/ethcore/src/lib.rs +++ b/ethcore/src/lib.rs @@ -102,7 +102,7 @@ extern crate serde; extern crate stats; extern crate state_account; extern crate time_utils; -extern crate trace; +pub extern crate trace; extern crate triehash_ethereum as triehash; extern crate unexpected; extern crate using_queue; @@ -167,7 +167,7 @@ pub mod snapshot; pub mod spec; pub mod state; pub mod state_db; -// TODO: need to reexport trace? +// TODO: need to reexport trace? In general, why do we export so many of these modules? //pub mod trace; pub mod transaction_ext; pub mod verification; From 44a090445b38b032752bba7a7dbdc3ef82468f30 Mon Sep 17 00:00:00 2001 From: David Palm Date: Fri, 5 Jul 2019 01:33:22 +0200 Subject: [PATCH 19/87] cleanup --- ethcore/src/lib.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/ethcore/src/lib.rs b/ethcore/src/lib.rs index bb3c035615c..37151723d35 100644 --- a/ethcore/src/lib.rs +++ b/ethcore/src/lib.rs @@ -167,8 +167,6 @@ pub mod snapshot; pub mod spec; pub mod state; pub mod state_db; -// TODO: need to reexport trace? In general, why do we export so many of these modules? -//pub mod trace; pub mod transaction_ext; pub mod verification; From ce3a211eea6ab342716e7246b0675d0853488cfc Mon Sep 17 00:00:00 2001 From: David Palm Date: Fri, 5 Jul 2019 01:50:09 +0200 Subject: [PATCH 20/87] WIP port over the rest of state from ethcore --- Cargo.lock | 1 - ethcore/state-account/Cargo.toml | 9 +- ethcore/state-account/src/lib.rs | 3 + ethcore/state-account/src/state.rs | 1004 +++++++++++++++++++++++++ ethcore/state-account/src/substate.rs | 26 +- 5 files changed, 1028 insertions(+), 15 deletions(-) create mode 100644 ethcore/state-account/src/state.rs diff --git a/Cargo.lock b/Cargo.lock index 484ddb4989c..996d8742aaf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1389,7 +1389,6 @@ dependencies = [ "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "parity-crypto 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "parity-wordlist 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/ethcore/state-account/Cargo.toml b/ethcore/state-account/Cargo.toml index f4f53716bbc..5a15ff5a293 100644 --- a/ethcore/state-account/Cargo.toml +++ b/ethcore/state-account/Cargo.toml @@ -1,5 +1,5 @@ [package] -description = "Ethereum accounts, keeps track of changes to the code and storage." +description = "Ethereum accounts, keeps track of changes to the code and storage" name = "state-account" version = "0.1.0" authors = ["Parity Technologies "] @@ -7,8 +7,10 @@ edition = "2018" [dependencies] common-types = { path = "../types"} +derive_more = "0.15.0" ethereum-types = "0.6.0" ethtrie = { package = "patricia-trie-ethereum", path = "../../util/patricia-trie-ethereum" } +factories = { path = "../factories" } hash-db = "0.12.4" keccak-hash = "0.2.0" keccak-hasher = { path = "../../util/keccak-hasher" } @@ -16,12 +18,15 @@ kvdb = "0.1.0" log = "0.4" lru-cache = "0.1.2" parity-bytes = "0.1.0" +parity-util-mem = "0.1.0" pod-account = { path = "../pod-account" } rlp = "0.4.0" serde = { version = "1.0", features = ["derive"] } +trace = { path = "../trace" } trie-db = "0.12.4" +vm = { path = "../vm" } -parking_lot = "0.8.0" +parking_lot = "0.7.0" memory-db = "0.12.4" journaldb = { path = "../../util/journaldb" } evm = { path = "../evm" } diff --git a/ethcore/state-account/src/lib.rs b/ethcore/state-account/src/lib.rs index 68a453cccc8..7f5994bef01 100644 --- a/ethcore/state-account/src/lib.rs +++ b/ethcore/state-account/src/lib.rs @@ -17,3 +17,6 @@ pub mod account; pub mod backend; pub mod substate; +// TODO: WIP +//pub mod state; + diff --git a/ethcore/state-account/src/state.rs b/ethcore/state-account/src/state.rs new file mode 100644 index 00000000000..4857ef55f9e --- /dev/null +++ b/ethcore/state-account/src/state.rs @@ -0,0 +1,1004 @@ +// 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 mutable state representation suitable to execute transactions. +//! Generic over a `Backend`. Deals with `Account`s. +//! Unconfirmed sub-states are managed with `checkpoint`s which may be canonicalized +//! or rolled back. + +use std::{ + cell::{RefCell, RefMut}, + collections::{BTreeMap, BTreeSet, HashMap, HashSet}, + collections::hash_map::Entry, + fmt, + sync::Arc, + error, io::Error as IoError, +}; + +use ethereum_types::{Address, H256, U256}; +use ethtrie::TrieError; +use factories::{Factories, VmFactory}; +use hash_db::{AsHashDB, HashDB}; +use keccak_hash::{KECCAK_EMPTY, KECCAK_NULL_RLP}; +use kvdb::DBValue; +use log::{warn, trace}; +use parity_bytes::Bytes; +use trie_db::{Recorder, Trie}; +use common_types::{ + basic_account::BasicAccount, + receipt::{Receipt, TransactionOutcome}, + state_diff::StateDiff, + transaction::SignedTransaction +}; +use ethtrie::{Result as TrieResult, TrieDB}; +use keccak_hasher::KeccakHasher; +use pod_account::*; +use rlp::DecoderError; +use vm::EnvInfo; +use derive_more::{Display, From}; + +use crate::{ + account::Account, + backend::Backend, +}; + +/// Used to return information about an `State::apply` operation. +pub struct ApplyOutcome { + /// The receipt for the applied transaction. + pub receipt: Receipt, + /// The output of the applied transaction. + pub output: Bytes, + /// The trace for the applied transaction, empty if tracing was not produced. + pub trace: Vec, + /// The VM trace for the applied transaction, None if tracing was not produced. + pub vm_trace: Option +} + +// TODO: sort out error handling +#[derive(Debug, Display, From)] +pub enum Error { + /// Trie error. + Trie(TrieError), + /// Decoder error. + Decoder(DecoderError), + /// Io error. + Io(IoError), // TODO: maybe not needed? +} + +// TODO: needed? +impl error::Error for Error {} + +impl From> for Error where Error: From { + fn from(err: Box) -> Self { + Error::from(*err) + } +} + +/// Result type for the execution ("application") of a transaction. +pub type ApplyResult = Result, Error>; + +// TODO: needs ExecutionError and Executed +///// Return type of proof validity check. +//#[derive(Debug, Clone)] +//pub enum ProvedExecution { +// /// Proof wasn't enough to complete execution. +// BadProof, +// /// The transaction failed, but not due to a bad proof. +// Failed(ExecutionError), +// /// The transaction successfully completed with the given proof. +// Complete(Box), +//} + +#[derive(Eq, PartialEq, Clone, Copy, Debug)] +/// Account modification state. Used to check if the account was +/// Modified in between commits and overall. +enum AccountState { + /// Account was loaded from disk and never modified in this state object. + CleanFresh, + /// Account was loaded from the global cache and never modified. + CleanCached, + /// Account has been modified and is not committed to the trie yet. + /// This is set if any of the account data is changed, including + /// storage and code. + Dirty, + /// Account was modified and committed to the trie. + Committed, +} + +#[derive(Debug)] +/// In-memory copy of the account data. Holds the optional account +/// and the modification status. +/// Account entry can contain existing (`Some`) or non-existing +/// account (`None`) +struct AccountEntry { + /// Account entry. `None` if account known to be non-existent. + account: Option, + /// Unmodified account balance. + old_balance: Option, + /// Entry state. + state: AccountState, +} + +// Account cache item. Contains account data and +// modification state +impl AccountEntry { + fn is_dirty(&self) -> bool { + self.state == AccountState::Dirty + } + + fn exists_and_is_null(&self) -> bool { + self.account.as_ref().map_or(false, |a| a.is_null()) + } + + /// Clone dirty data into new `AccountEntry`. This includes + /// basic account data and modified storage keys. + /// Returns None if clean. + fn clone_if_dirty(&self) -> Option { + match self.is_dirty() { + true => Some(self.clone_dirty()), + false => None, + } + } + + /// Clone dirty data into new `AccountEntry`. This includes + /// basic account data and modified storage keys. + fn clone_dirty(&self) -> AccountEntry { + AccountEntry { + old_balance: self.old_balance, + account: self.account.as_ref().map(Account::clone_dirty), + state: self.state, + } + } + + // Create a new account entry and mark it as dirty. + fn new_dirty(account: Option) -> AccountEntry { + AccountEntry { + old_balance: account.as_ref().map(|a| a.balance().clone()), + account, + state: AccountState::Dirty, + } + } + + // Create a new account entry and mark it as clean. + fn new_clean(account: Option) -> AccountEntry { + AccountEntry { + old_balance: account.as_ref().map(|a| a.balance().clone()), + account, + state: AccountState::CleanFresh, + } + } + + // Create a new account entry and mark it as clean and cached. + fn new_clean_cached(account: Option) -> AccountEntry { + AccountEntry { + old_balance: account.as_ref().map(|a| a.balance().clone()), + account, + state: AccountState::CleanCached, + } + } + + // Replace data with another entry but preserve storage cache. + fn overwrite_with(&mut self, other: AccountEntry) { + self.state = other.state; + match other.account { + Some(acc) => { + if let Some(ref mut ours) = self.account { + ours.overwrite_with(acc); + } else { + self.account = Some(acc); + } + }, + None => self.account = None, + } + } +} + +// TODO: needs Machine +///// Check the given proof of execution. +///// `Err(ExecutionError::Internal)` indicates failure, everything else indicates +///// a successful proof (as the transaction itself may be poorly chosen). +//pub fn check_proof( +// proof: &[DBValue], +// root: H256, +// transaction: &SignedTransaction, +// machine: &Machine, +// env_info: &EnvInfo, +//) -> ProvedExecution + +// TODO: needs Machine +///// Prove a `virtual` transaction on the given state. +///// Returns `None` when the transacion could not be proved, +///// and a proof otherwise. +//pub fn prove_transaction_virtual + Send + Sync>( +// db: H, +// root: H256, +// transaction: &SignedTransaction, +// machine: &Machine, +// env_info: &EnvInfo, +// factories: Factories, +//) -> Option<(Bytes, Vec)> { + + +/// Representation of the entire state of all accounts in the system. +/// +/// `State` can work together with `StateDB` to share account cache. +/// +/// Local cache contains changes made locally and changes accumulated +/// locally from previous commits. Global cache reflects the database +/// state and never contains any changes. +/// +/// Cache items contains account data, or the flag that account does not exist +/// and modification state (see `AccountState`) +/// +/// Account data can be in the following cache states: +/// * In global but not local - something that was queried from the database, +/// but never modified +/// * In local but not global - something that was just added (e.g. new account) +/// * In both with the same value - something that was changed to a new value, +/// but changed back to a previous block in the same block (same State instance) +/// * In both with different values - something that was overwritten with a +/// new value. +/// +/// All read-only state queries check local cache/modifications first, +/// then global state cache. If data is not found in any of the caches +/// it is loaded from the DB to the local cache. +/// +/// **** IMPORTANT ************************************************************* +/// All the modifications to the account data must set the `Dirty` state in the +/// `AccountEntry`. This is done in `require` and `require_or_from`. So just +/// use that. +/// **************************************************************************** +/// +/// Upon destruction all the local cache data propagated into the global cache. +/// Propagated items might be rejected if current state is non-canonical. +/// +/// State checkpointing. +/// +/// A new checkpoint can be created with `checkpoint()`. checkpoints can be +/// created in a hierarchy. +/// When a checkpoint is active all changes are applied directly into +/// `cache` and the original value is copied into an active checkpoint. +/// Reverting a checkpoint with `revert_to_checkpoint` involves copying +/// original values from the latest checkpoint back into `cache`. The code +/// takes care not to overwrite cached storage while doing that. +/// A checkpoint can be discarded with `discard_checkpoint`. All of the original +/// backed-up values are moved into a parent checkpoint (if any). +/// +pub struct State { + db: B, + root: H256, + cache: RefCell>, + // The original account is preserved in + checkpoints: RefCell>>>, + account_start_nonce: U256, + factories: Factories, +} + +#[derive(Copy, Clone)] +enum RequireCache { + None, + CodeSize, + Code, +} + +/// Mode of dealing with null accounts. +#[derive(PartialEq)] +pub enum CleanupMode<'a> { + /// Create accounts which would be null. + ForceCreate, + /// Don't delete null accounts upon touching, but also don't create them. + NoEmpty, + /// Mark all touched accounts. + TrackTouched(&'a mut HashSet
), +} + +/// Provides subset of `State` methods to query state information +pub trait StateInfo { + /// Get the nonce of account `a`. + fn nonce(&self, a: &Address) -> TrieResult; + + /// Get the balance of account `a`. + fn balance(&self, a: &Address) -> TrieResult; + + /// Mutate storage of account `address` so that it is `value` for `key`. + fn storage_at(&self, address: &Address, key: &H256) -> TrieResult; + + /// Get accounts' code. + fn code(&self, a: &Address) -> TrieResult>>; +} + +impl StateInfo for State { + fn nonce(&self, a: &Address) -> TrieResult { State::nonce(self, a) } + fn balance(&self, a: &Address) -> TrieResult { State::balance(self, a) } + fn storage_at(&self, address: &Address, key: &H256) -> TrieResult { State::storage_at(self, address, key) } + fn code(&self, address: &Address) -> TrieResult>> { State::code(self, address) } +} + +const SEC_TRIE_DB_UNWRAP_STR: &'static str = "A state can only be created with valid root. Creating a SecTrieDB with a valid root will not fail. \ + Therefore creating a SecTrieDB with this state's root will not fail."; + +impl State { + /// Creates new state with empty state root + /// Used for tests. + pub fn new(mut db: B, account_start_nonce: U256, factories: Factories) -> State { + let mut root = H256::zero(); + { + // init trie and reset root to null + let _ = factories.trie.create(db.as_hash_db_mut(), &mut root); + } + + State { + db, + root, + cache: RefCell::new(HashMap::new()), + checkpoints: RefCell::new(Vec::new()), + account_start_nonce, + factories, + } + } + + /// Creates new state with existing state root + pub fn from_existing(db: B, root: H256, account_start_nonce: U256, factories: Factories) -> TrieResult> { + if !db.as_hash_db().contains(&root, hash_db::EMPTY_PREFIX) { + return Err(Box::new(TrieError::InvalidStateRoot(root))); + } + + let state = State { + db, + root, + cache: RefCell::new(HashMap::new()), + checkpoints: RefCell::new(Vec::new()), + account_start_nonce, + factories, + }; + + Ok(state) + } + + /// Get a VM factory that can execute on this state. + pub fn vm_factory(&self) -> VmFactory { + self.factories.vm.clone() + } + + /// Create a recoverable checkpoint of this state. Return the checkpoint index. + pub fn checkpoint(&mut self) -> usize { + let checkpoints = self.checkpoints.get_mut(); + let index = checkpoints.len(); + checkpoints.push(HashMap::new()); + index + } + + /// Merge last checkpoint with previous. + pub fn discard_checkpoint(&mut self) { + // merge with previous checkpoint + let last = self.checkpoints.get_mut().pop(); + if let Some(mut checkpoint) = last { + if let Some(ref mut prev) = self.checkpoints.get_mut().last_mut() { + if prev.is_empty() { + **prev = checkpoint; + } else { + for (k, v) in checkpoint.drain() { + prev.entry(k).or_insert(v); + } + } + } + } + } + + /// Revert to the last checkpoint and discard it. + pub fn revert_to_checkpoint(&mut self) { + if let Some(mut checkpoint) = self.checkpoints.get_mut().pop() { + for (k, v) in checkpoint.drain() { + match v { + Some(v) => { + match self.cache.get_mut().entry(k) { + Entry::Occupied(mut e) => { + // Merge checkpointed changes back into the main account + // storage preserving the cache. + e.get_mut().overwrite_with(v); + }, + Entry::Vacant(e) => { + e.insert(v); + } + } + }, + None => { + if let Entry::Occupied(e) = self.cache.get_mut().entry(k) { + if e.get().is_dirty() { + e.remove(); + } + } + } + } + } + } + } + + fn insert_cache(&self, address: &Address, account: AccountEntry) { + // Dirty account which is not in the cache means this is a new account. + // It goes directly into the checkpoint as there's nothing to rever to. + // + // In all other cases account is read as clean first, and after that made + // dirty in and added to the checkpoint with `note_cache`. + let is_dirty = account.is_dirty(); + let old_value = self.cache.borrow_mut().insert(*address, account); + if is_dirty { + if let Some(ref mut checkpoint) = self.checkpoints.borrow_mut().last_mut() { + checkpoint.entry(*address).or_insert(old_value); + } + } + } + + fn note_cache(&self, address: &Address) { + if let Some(ref mut checkpoint) = self.checkpoints.borrow_mut().last_mut() { + checkpoint.entry(*address) + .or_insert_with(|| self.cache.borrow().get(address).map(AccountEntry::clone_dirty)); + } + } + + /// Destroy the current object and return root and database. + pub fn drop(mut self) -> (H256, B) { + self.propagate_to_global_cache(); + (self.root, self.db) + } + + /// Destroy the current object and return single account data. + pub fn into_account(self, account: &Address) -> TrieResult<(Option>, HashMap)> { + // TODO: deconstruct without cloning. + let account = self.require(account, true)?; + Ok((account.code().clone(), account.storage_changes().clone())) + } + + /// Return reference to root + pub fn root(&self) -> &H256 { + &self.root + } + + /// Create a new contract at address `contract`. If there is already an account at the address + /// it will have its code reset, ready for `init_code()`. + pub fn new_contract(&mut self, contract: &Address, balance: U256, nonce_offset: U256) -> TrieResult<()> { + let original_storage_root = self.original_storage_root(contract)?; + let (nonce, overflow) = self.account_start_nonce.overflowing_add(nonce_offset); + if overflow { + return Err(Box::new(TrieError::DecoderError(H256::from(*contract), + rlp::DecoderError::Custom("Nonce overflow".into())))); + } + self.insert_cache(contract, AccountEntry::new_dirty(Some(Account::new_contract(balance, nonce, original_storage_root)))); + Ok(()) + } + + /// Remove an existing account. + pub fn kill_account(&mut self, account: &Address) { + self.insert_cache(account, AccountEntry::new_dirty(None)); + } + + /// Determine whether an account exists. + pub fn exists(&self, a: &Address) -> TrieResult { + // Bloom filter does not contain empty accounts, so it is important here to + // check if account exists in the database directly before EIP-161 is in effect. + self.ensure_cached(a, RequireCache::None, false, |a| a.is_some()) + } + + /// Determine whether an account exists and if not empty. + pub fn exists_and_not_null(&self, a: &Address) -> TrieResult { + self.ensure_cached(a, RequireCache::None, false, |a| a.map_or(false, |a| !a.is_null())) + } + + /// Determine whether an account exists and has code or non-zero nonce. + pub fn exists_and_has_code_or_nonce(&self, a: &Address) -> TrieResult { + self.ensure_cached(a, RequireCache::CodeSize, false, + |a| a.map_or(false, |a| a.code_hash() != KECCAK_EMPTY || *a.nonce() != self.account_start_nonce)) + } + + /// Get the balance of account `a`. + pub fn balance(&self, a: &Address) -> TrieResult { + self.ensure_cached(a, RequireCache::None, true, + |a| a.as_ref().map_or(U256::zero(), |account| *account.balance())) + } + + /// Get the nonce of account `a`. + pub fn nonce(&self, a: &Address) -> TrieResult { + self.ensure_cached(a, RequireCache::None, true, + |a| a.as_ref().map_or(self.account_start_nonce, |account| *account.nonce())) + } + + /// Whether the base storage root of an account remains unchanged. + pub fn is_base_storage_root_unchanged(&self, a: &Address) -> TrieResult { + Ok(self.ensure_cached(a, RequireCache::None, true, + |a| a.as_ref().map(|account| account.is_base_storage_root_unchanged()))? + .unwrap_or(true)) + } + + /// Get the storage root of account `a`. + pub fn storage_root(&self, a: &Address) -> TrieResult> { + self.ensure_cached(a, RequireCache::None, true, + |a| a.as_ref().and_then(|account| account.storage_root())) + } + + /// Get the original storage root since last commit of account `a`. + pub fn original_storage_root(&self, a: &Address) -> TrieResult { + Ok(self.ensure_cached(a, RequireCache::None, true, + |a| a.as_ref().map(|account| account.original_storage_root()))? + .unwrap_or(KECCAK_NULL_RLP)) + } + + /// Initialise the code of account `a` so that it is `code`. + /// NOTE: Account should have been created with `new_contract`. + pub fn init_code(&mut self, a: &Address, code: Bytes) -> TrieResult<()> { + self.require_or_from(a, true, || Account::new_contract(0.into(), self.account_start_nonce, KECCAK_NULL_RLP), |_| {})?.init_code(code); + Ok(()) + } + + /// Reset the code of account `a` so that it is `code`. + pub fn reset_code(&mut self, a: &Address, code: Bytes) -> TrieResult<()> { + self.require_or_from(a, true, || Account::new_contract(0.into(), self.account_start_nonce, KECCAK_NULL_RLP), |_| {})?.reset_code(code); + Ok(()) + } + + // TODO: Needs Machine and TransactionOptions + /// Execute a given transaction, producing a receipt and an optional trace. + /// This will change the state accordingly. +// pub fn apply(&mut self, env_info: &EnvInfo, machine: &Machine, t: &SignedTransaction, tracing: bool) -> ApplyResult { +// if tracing { +// let options = TransactOptions::with_tracing(); +// self.apply_with_tracing(env_info, machine, t, options.tracer, options.vm_tracer) +// } else { +// let options = TransactOptions::with_no_tracing(); +// self.apply_with_tracing(env_info, machine, t, options.tracer, options.vm_tracer) +// } +// } + + // TODO: Needs Machine + /// Execute a given transaction with given tracer and VM tracer producing a receipt and an optional trace. + /// This will change the state accordingly. +// pub fn apply_with_tracing( +// &mut self, +// env_info: &EnvInfo, +// machine: &Machine, +// t: &SignedTransaction, +// tracer: T, +// vm_tracer: V, +// ) -> ApplyResult where +// T: trace::Tracer, +// V: trace::VMTracer, +// { +// let options = TransactOptions::new(tracer, vm_tracer); +// let e = self.execute(env_info, machine, t, options, false)?; +// let params = machine.params(); +// +// let eip658 = env_info.number >= params.eip658_transition; +// let no_intermediate_commits = +// eip658 || +// (env_info.number >= params.eip98_transition && env_info.number >= params.validate_receipts_transition); +// +// let outcome = if no_intermediate_commits { +// if eip658 { +// TransactionOutcome::StatusCode(if e.exception.is_some() { 0 } else { 1 }) +// } else { +// TransactionOutcome::Unknown +// } +// } else { +// self.commit()?; +// TransactionOutcome::StateRoot(self.root().clone()) +// }; +// +// let output = e.output; +// let receipt = Receipt::new(outcome, e.cumulative_gas_used, e.logs); +// trace!(target: "state", "Transaction receipt: {:?}", receipt); +// +// Ok(ApplyOutcome { +// receipt, +// output, +// trace: e.trace, +// vm_trace: e.vm_trace, +// }) +// } + + // TODO: Needs Machine and Executed, ExecutionError, Executive + // Execute a given transaction without committing changes. + // + // `virt` signals that we are executing outside of a block set and restrictions like + // gas limits and gas costs should be lifted. +// fn execute(&mut self, env_info: &EnvInfo, machine: &Machine, t: &SignedTransaction, options: TransactOptions, virt: bool) +// -> Result, ExecutionError> where T: trace::Tracer, V: trace::VMTracer, +// { +// let schedule = machine.schedule(env_info.number); +// let mut e = Executive::new(self, env_info, machine, &schedule); +// +// match virt { +// true => e.transact_virtual(t, options), +// false => e.transact(t, options), +// } +// } + + fn touch(&mut self, a: &Address) -> TrieResult<()> { + self.require(a, false)?; + Ok(()) + } + + /// Commits our cached account changes into the trie. + pub fn commit(&mut self) -> Result<(), Error> { + assert!(self.checkpoints.borrow().is_empty()); + // first, commit the sub trees. + let mut accounts = self.cache.borrow_mut(); + for (address, ref mut a) in accounts.iter_mut().filter(|&(_, ref a)| a.is_dirty()) { + if let Some(ref mut account) = a.account { + let addr_hash = account.address_hash(address); + { + let mut account_db = self.factories.accountdb.create(self.db.as_hash_db_mut(), addr_hash); + account.commit_storage(&self.factories.trie, account_db.as_hash_db_mut())?; + account.commit_code(account_db.as_hash_db_mut()); + } + if !account.is_empty() { + self.db.note_non_null_account(address); + } + } + } + + { + let mut trie = self.factories.trie.from_existing(self.db.as_hash_db_mut(), &mut self.root)?; + for (address, ref mut a) in accounts.iter_mut().filter(|&(_, ref a)| a.is_dirty()) { + a.state = AccountState::Committed; + match a.account { + Some(ref mut account) => { + trie.insert(address.as_bytes(), &account.rlp())?; + }, + None => { + trie.remove(address.as_bytes())?; + }, + }; + } + } + + Ok(()) + } + + /// Propagate local cache into shared canonical state cache. + fn propagate_to_global_cache(&mut self) { + let mut addresses = self.cache.borrow_mut(); + trace!("Committing cache {:?} entries", addresses.len()); + for (address, a) in addresses.drain().filter(|&(_, ref a)| a.state == AccountState::Committed || a.state == AccountState::CleanFresh) { + self.db.add_to_account_cache(address, a.account, a.state == AccountState::Committed); + } + } + + /// Clear state cache + pub fn clear(&mut self) { + assert!(self.checkpoints.borrow().is_empty()); + self.cache.borrow_mut().clear(); + } + + /// Remove any touched empty or dust accounts. + pub fn kill_garbage(&mut self, touched: &HashSet
, remove_empty_touched: bool, min_balance: &Option, kill_contracts: bool) -> TrieResult<()> { + let to_kill: HashSet<_> = { + self.cache.borrow().iter().filter_map(|(address, ref entry)| + if touched.contains(address) && // Check all touched accounts + ((remove_empty_touched && entry.exists_and_is_null()) // Remove all empty touched accounts. + || min_balance.map_or(false, |ref balance| entry.account.as_ref().map_or(false, |account| + (account.is_basic() || kill_contracts) // Remove all basic and optionally contract accounts where balance has been decreased. + && account.balance() < balance && entry.old_balance.as_ref().map_or(false, |b| account.balance() < b)))) { + + Some(address.clone()) + } else { None }).collect() + }; + for address in to_kill { + self.kill_account(&address); + } + Ok(()) + } + + // TODO: Needs PodState + /// Populate the state from `accounts`. + /// Used for tests. +// pub fn populate_from(&mut self, accounts: PodState) { +// assert!(self.checkpoints.borrow().is_empty()); +// for (add, acc) in accounts.drain().into_iter() { +// self.cache.borrow_mut().insert(add, AccountEntry::new_dirty(Some(Account::from_pod(acc)))); +// } +// } +// +// /// Populate a PodAccount map from this state. +// fn to_pod_cache(&self) -> PodState { +// assert!(self.checkpoints.borrow().is_empty()); +// PodState::from(self.cache.borrow().iter().fold(BTreeMap::new(), |mut m, (add, opt)| { +// if let Some(ref acc) = opt.account { +// m.insert(*add, acc.to_pod()); +// } +// m +// })) +// } + + // TODO: sort out features + #[cfg(feature="to-pod-full")] + /// Populate a PodAccount map from this state. + /// Warning this is not for real time use. + /// Use of this method requires FatDB mode to be able + /// to iterate on accounts. + pub fn to_pod_full(&self) -> Result { + + assert!(self.checkpoints.borrow().is_empty()); + assert!(self.factories.trie.is_fat()); + + let mut result = BTreeMap::new(); + + let db = &self.db.as_hash_db(); + let trie = self.factories.trie.readonly(db, &self.root)?; + + // put trie in cache + for item in trie.iter()? { + if let Ok((addr, _dbval)) = item { + let address = Address::from_slice(&addr); + let _ = self.require(&address, true); + } + } + + // Resolve missing part + for (add, opt) in self.cache.borrow().iter() { + if let Some(ref acc) = opt.account { + let pod_account = self.account_to_pod_account(acc, add)?; + result.insert(add.clone(), pod_account); + } + } + + Ok(PodState::from(result)) + } + + // TODO: sort out features + /// Create a PodAccount from an account. + /// Differs from existing method by including all storage + /// values of the account to the PodAccount. + /// This function is only intended for use in small tests or with fresh accounts. + /// It requires FatDB. + #[cfg(feature="to-pod-full")] + fn account_to_pod_account(&self, account: &Account, address: &Address) -> Result { + use ethereum_types::BigEndianHash; + + let mut pod_storage = BTreeMap::new(); + let addr_hash = account.address_hash(address); + let accountdb = self.factories.accountdb.readonly(self.db.as_hash_db(), addr_hash); + let root = account.base_storage_root(); + + let accountdb = &accountdb.as_hash_db(); + let trie = self.factories.trie.readonly(accountdb, &root)?; + for o_kv in trie.iter()? { + if let Ok((key, val)) = o_kv { + pod_storage.insert( + H256::from_slice(&key[..]), + BigEndianHash::from_uint( + &rlp::decode::(&val[..]).expect("Decoded from trie which was encoded from the same type; qed") + ), + ); + } + } + + let mut pod_account = account.to_pod(); + // cached one first + pod_storage.append(&mut pod_account.storage); + pod_account.storage = pod_storage; + Ok(pod_account) + } + +// TODO: Needs PodState +// /// Populate a PodAccount map from this state, with another state as the account and storage query. +// fn to_pod_diff(&mut self, query: &State) -> TrieResult { +// assert!(self.checkpoints.borrow().is_empty()); +// +// // Merge PodAccount::to_pod for cache of self and `query`. +// let all_addresses = self.cache.borrow().keys().cloned() +// .chain(query.cache.borrow().keys().cloned()) +// .collect::>(); +// +// Ok(PodState::from(all_addresses.into_iter().fold(Ok(BTreeMap::new()), |m: TrieResult<_>, address| { +// let mut m = m?; +// +// let account = self.ensure_cached(&address, RequireCache::Code, true, |acc| { +// acc.map(|acc| { +// // Merge all modified storage keys. +// let all_keys = { +// let self_keys = acc.storage_changes().keys().cloned() +// .collect::>(); +// +// if let Some(ref query_storage) = query.cache.borrow().get(&address) +// .and_then(|opt| { +// Some(opt.account.as_ref()?.storage_changes().keys().cloned() +// .collect::>()) +// }) +// { +// self_keys.union(&query_storage).cloned().collect::>() +// } else { +// self_keys.into_iter().collect::>() +// } +// }; +// +// // Storage must be fetched after ensure_cached to avoid borrow problem. +// (*acc.balance(), *acc.nonce(), all_keys, acc.code().map(|x| x.to_vec())) +// }) +// })?; +// +// if let Some((balance, nonce, storage_keys, code)) = account { +// let storage = storage_keys.into_iter().fold(Ok(BTreeMap::new()), |s: TrieResult<_>, key| { +// let mut s = s?; +// +// s.insert(key, self.storage_at(&address, &key)?); +// Ok(s) +// })?; +// +// m.insert(address, PodAccount { +// balance, nonce, storage, code +// }); +// } +// +// Ok(m) +// })?)) +// } + + // TODO: Needs PodState +// /// Returns a `StateDiff` describing the difference from `orig` to `self`. +// /// Consumes self. +// pub fn diff_from(&self, mut orig: State) -> TrieResult { +// let pod_state_post = self.to_pod_cache(); +// let pod_state_pre = orig.to_pod_diff(self)?; +// Ok(pod_state::diff_pod(&pod_state_pre, &pod_state_post)) +// } + + /// Load required account data from the databases. Returns whether the cache succeeds. + #[must_use] + fn update_account_cache(require: RequireCache, account: &mut Account, state_db: &B, db: &dyn HashDB) -> bool { + if let RequireCache::None = require { + return true; + } + + if account.is_cached() { + return true; + } + + // if there's already code in the global cache, always cache it localy + let hash = account.code_hash(); + match state_db.get_cached_code(&hash) { + Some(code) => { + account.cache_given_code(code); + true + }, + None => match require { + RequireCache::None => true, + RequireCache::Code => { + if let Some(code) = account.cache_code(db) { + // propagate code loaded from the database to + // the global code cache. + state_db.cache_code(hash, code); + true + } else { + false + } + }, + RequireCache::CodeSize => { + account.cache_code_size(db) + } + } + } + } + + /// Check caches for required data + /// First searches for account in the local, then the shared cache. + /// Populates local cache if nothing found. + fn ensure_cached(&self, a: &Address, require: RequireCache, check_null: bool, f: F) -> TrieResult + where F: Fn(Option<&Account>) -> U { + // check local cache first + if let Some(ref mut maybe_acc) = self.cache.borrow_mut().get_mut(a) { + if let Some(ref mut account) = maybe_acc.account { + let accountdb = self.factories.accountdb.readonly(self.db.as_hash_db(), account.address_hash(a)); + if Self::update_account_cache(require, account, &self.db, accountdb.as_hash_db()) { + return Ok(f(Some(account))); + } else { + return Err(Box::new(TrieError::IncompleteDatabase(H256::from(*a)))); + } + } + return Ok(f(None)); + } + // check global cache + let result = self.db.get_cached(a, |mut acc| { + if let Some(ref mut account) = acc { + let accountdb = self.factories.accountdb.readonly(self.db.as_hash_db(), account.address_hash(a)); + if !Self::update_account_cache(require, account, &self.db, accountdb.as_hash_db()) { + return Err(Box::new(TrieError::IncompleteDatabase(H256::from(*a)))); + } + } + Ok(f(acc.map(|a| &*a))) + }); + match result { + Some(r) => Ok(r?), + None => { + // first check if it is not in database for sure + if check_null && self.db.is_known_null(a) { return Ok(f(None)); } + + // not found in the global cache, get from the DB and insert into local + let db = &self.db.as_hash_db(); + let db = self.factories.trie.readonly(db, &self.root)?; + let from_rlp = |b: &[u8]| Account::from_rlp(b).expect("decoding db value failed"); + let mut maybe_acc = db.get_with(a.as_bytes(), from_rlp)?; + if let Some(ref mut account) = maybe_acc.as_mut() { + let accountdb = self.factories.accountdb.readonly(self.db.as_hash_db(), account.address_hash(a)); + if !Self::update_account_cache(require, account, &self.db, accountdb.as_hash_db()) { + return Err(Box::new(TrieError::IncompleteDatabase(H256::from(*a)))); + } + } + let r = f(maybe_acc.as_ref()); + self.insert_cache(a, AccountEntry::new_clean(maybe_acc)); + Ok(r) + } + } + } + + /// Pull account `a` in our cache from the trie DB. `require_code` requires that the code be cached, too. + fn require<'a>(&'a self, a: &Address, require_code: bool) -> TrieResult> { + self.require_or_from(a, require_code, || Account::new_basic(0u8.into(), self.account_start_nonce), |_| {}) + } + + /// Pull account `a` in our cache from the trie DB. `require_code` requires that the code be cached, too. + /// If it doesn't exist, make account equal the evaluation of `default`. + fn require_or_from<'a, F, G>(&'a self, a: &Address, require_code: bool, default: F, not_default: G) -> TrieResult> + where F: FnOnce() -> Account, G: FnOnce(&mut Account), + { + let contains_key = self.cache.borrow().contains_key(a); + if !contains_key { + match self.db.get_cached_account(a) { + Some(acc) => self.insert_cache(a, AccountEntry::new_clean_cached(acc)), + None => { + let maybe_acc = if !self.db.is_known_null(a) { + let db = &self.db.as_hash_db(); + let db = self.factories.trie.readonly(db, &self.root)?; + let from_rlp = |b:&[u8]| { Account::from_rlp(b).expect("decoding db value failed") }; + AccountEntry::new_clean(db.get_with(a.as_bytes(), from_rlp)?) + } else { + AccountEntry::new_clean(None) + }; + self.insert_cache(a, maybe_acc); + } + } + } + self.note_cache(a); + + // at this point the entry is guaranteed to be in the cache. + let mut account = RefMut::map(self.cache.borrow_mut(), |c| { + let entry = c.get_mut(a).expect("entry known to exist in the cache; qed"); + + match &mut entry.account { + &mut Some(ref mut acc) => not_default(acc), + slot => *slot = Some(default()), + } + + // set the dirty flag after changing account data. + entry.state = AccountState::Dirty; + entry.account.as_mut().expect("Required account must always exist; qed") + }); + + if require_code { + let addr_hash = account.address_hash(a); + let accountdb = self.factories.accountdb.readonly(self.db.as_hash_db(), addr_hash); + + if !Self::update_account_cache(RequireCache::Code, &mut account, &self.db, accountdb.as_hash_db()) { + return Err(Box::new(TrieError::IncompleteDatabase(H256::from(*a)))) + } + } + + Ok(account) + } + + /// Replace account code and storage. Creates account if it does not exist. + pub fn patch_account(&self, a: &Address, code: Arc, storage: HashMap) -> TrieResult<()> { + Ok(self.require(a, false)?.reset_code_and_storage(code, storage)) + } +} diff --git a/ethcore/state-account/src/substate.rs b/ethcore/state-account/src/substate.rs index eea85b79017..59a2ed1345b 100644 --- a/ethcore/state-account/src/substate.rs +++ b/ethcore/state-account/src/substate.rs @@ -21,18 +21,6 @@ use ethereum_types::Address; use common_types::log_entry::LogEntry; use evm::{CleanDustMode, Schedule}; -// TODO: Move back to state/mod.rs? -/// Mode of dealing with null accounts. -#[derive(PartialEq)] -pub enum CleanupMode<'a> { - /// Create accounts which would be null. - ForceCreate, - /// Don't delete null accounts upon touching, but also don't create them. - NoEmpty, - /// Mark all touched accounts. - TrackTouched(&'a mut HashSet
), -} - /// State changes which should be applied in finalize, /// after transaction is fully executed. #[derive(Debug, Default)] @@ -78,6 +66,20 @@ impl Substate { } } +// TODO: belongs in `state.rs` +//use crate::state::CleanupMode; +/// Mode of dealing with null accounts. +#[derive(PartialEq)] +pub enum CleanupMode<'a> { + /// Create accounts which would be null. + ForceCreate, + /// Don't delete null accounts upon touching, but also don't create them. + NoEmpty, + /// Mark all touched accounts. + TrackTouched(&'a mut HashSet
), +} + + #[cfg(test)] mod tests { use ethereum_types::Address; From 9ed1d47d1a019c786f5902cfdfe0b4604506747a Mon Sep 17 00:00:00 2001 From: David Palm Date: Fri, 5 Jul 2019 08:44:50 +0200 Subject: [PATCH 21/87] Collect all impls for Machine --- ethcore/src/machine.rs | 29 ++++++++++++----------------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/ethcore/src/machine.rs b/ethcore/src/machine.rs index 51d4e3f8278..95ca11012e2 100644 --- a/ethcore/src/machine.rs +++ b/ethcore/src/machine.rs @@ -34,7 +34,7 @@ use call_contract::CallContract; use client::BlockInfo; use error::Error; use executive::Executive; -use spec::CommonParams; +use spec::CommonParams; // TODO: move to common_types use state::{CleanupMode, Substate}; use trace::{NoopTracer, NoopVMTracer}; use tx_filter::TransactionFilter; @@ -108,9 +108,7 @@ impl Machine { pub fn ethash_extensions(&self) -> Option<&EthashExtensions> { self.ethash_extensions.as_ref() } -} -impl Machine { /// Execute a call as the system address. Block environment information passed to the /// VM is modified to have its gas limit bounded at the upper limit of possible used /// gases including this system call, capped at the maximum value able to be @@ -394,8 +392,18 @@ impl Machine { } rlp.as_val().map_err(|e| transaction::Error::InvalidRlp(e.to_string())) } -} + /// Get the balance, in base units, associated with an account. + /// Extracts data from the live block. + pub fn balance(&self, live: &ExecutedBlock, address: &Address) -> Result { + live.state.balance(address).map_err(Into::into) + } + + /// Increment the balance of an account in the state of the live block. + pub fn add_balance(&self, live: &mut ExecutedBlock, address: &Address, amount: &U256) -> Result<(), Error> { + live.state_mut().add_balance(address, amount, CleanupMode::NoEmpty).map_err(Into::into) + } +} /// Auxiliary data fetcher for an Ethereum machine. In Ethereum-like machines /// there are two kinds of auxiliary data: bodies and receipts. #[derive(Default, Clone)] @@ -421,19 +429,6 @@ pub enum AuxiliaryRequest { Both, } -impl Machine { - /// Get the balance, in base units, associated with an account. - /// Extracts data from the live block. - pub fn balance(&self, live: &ExecutedBlock, address: &Address) -> Result { - live.state.balance(address).map_err(Into::into) - } - - /// Increment the balance of an account in the state of the live block. - pub fn add_balance(&self, live: &mut ExecutedBlock, address: &Address, amount: &U256) -> Result<(), Error> { - live.state_mut().add_balance(address, amount, CleanupMode::NoEmpty).map_err(Into::into) - } -} - // Try to round gas_limit a bit so that: // 1) it will still be in desired range // 2) it will be a nearest (with tendency to increase) multiple of PARITY_GAS_LIMIT_DETERMINANT From 67332d74c694b08b279c775c6073458acdfae7f9 Mon Sep 17 00:00:00 2001 From: David Palm Date: Fri, 5 Jul 2019 09:00:16 +0200 Subject: [PATCH 22/87] some notes --- ethcore/state-account/src/state.rs | 53 ++++++++++++++++++++++++++++-- 1 file changed, 50 insertions(+), 3 deletions(-) diff --git a/ethcore/state-account/src/state.rs b/ethcore/state-account/src/state.rs index 4857ef55f9e..0b63a43c28d 100644 --- a/ethcore/state-account/src/state.rs +++ b/ethcore/state-account/src/state.rs @@ -216,7 +216,30 @@ impl AccountEntry { // transaction: &SignedTransaction, // machine: &Machine, // env_info: &EnvInfo, -//) -> ProvedExecution +//) -> ProvedExecution { +// let backend = self::backend::ProofCheck::new(proof); +// let mut factories = Factories::default(); +// factories.accountdb = ::account_db::Factory::Plain; +// +// let res = State::from_existing( +// backend, +// root, +// machine.account_start_nonce(env_info.number), +// factories +// ); +// +// let mut state = match res { +// Ok(state) => state, +// Err(_) => return ProvedExecution::BadProof, +// }; +// +// let options = TransactOptions::with_no_tracing().save_output_from_contract(); +// match state.execute(env_info, machine, transaction, options, true) { +// Ok(executed) => ProvedExecution::Complete(Box::new(executed)), +// Err(ExecutionError::Internal(_)) => ProvedExecution::BadProof, +// Err(e) => ProvedExecution::Failed(e), +// } +//} // TODO: needs Machine ///// Prove a `virtual` transaction on the given state. @@ -230,7 +253,31 @@ impl AccountEntry { // env_info: &EnvInfo, // factories: Factories, //) -> Option<(Bytes, Vec)> { - +// use state_account::backend::Proving; +// +// let backend = Proving::new(db); +// let res = State::from_existing( +// backend, +// root, +// machine.account_start_nonce(env_info.number), +// factories, +// ); +// +// let mut state = match res { +// Ok(state) => state, +// Err(_) => return None, +// }; +// +// let options = TransactOptions::with_no_tracing().dont_check_nonce().save_output_from_contract(); +// match state.execute(env_info, machine, transaction, options, true) { +// Err(ExecutionError::Internal(_)) => None, +// Err(e) => { +// trace!(target: "state", "Proved call failed: {}", e); +// Some((Vec::new(), state.drop().1.extract_proof())) +// } +// Ok(res) => Some((res.output, state.drop().1.extract_proof())), +// } +//} /// Representation of the entire state of all accounts in the system. /// @@ -548,7 +595,7 @@ impl State { Ok(()) } - // TODO: Needs Machine and TransactionOptions + // TODO: Needs Machine and TransactOptions /// Execute a given transaction, producing a receipt and an optional trace. /// This will change the state accordingly. // pub fn apply(&mut self, env_info: &EnvInfo, machine: &Machine, t: &SignedTransaction, tracing: bool) -> ApplyResult { From 0a42ea8b91ba2a144455ad72918e4f3a1c00eeff Mon Sep 17 00:00:00 2001 From: David Palm Date: Fri, 5 Jul 2019 09:07:48 +0200 Subject: [PATCH 23/87] Rename pod-account to pod --- Cargo.lock | 6 +++--- ethcore/Cargo.toml | 2 +- ethcore/{pod-account => pod}/Cargo.toml | 4 ++-- .../src/lib.rs => pod/src/account.rs} | 0 ethcore/pod/src/lib.rs | 19 +++++++++++++++++++ ethcore/pod/src/state.rs | 0 ethcore/src/pod_state.rs | 4 ++-- ethcore/src/state/mod.rs | 2 +- ethcore/state-account/Cargo.toml | 2 +- ethcore/state-account/src/account.rs | 2 +- ethcore/state-account/src/state.rs | 2 +- 11 files changed, 31 insertions(+), 12 deletions(-) rename ethcore/{pod-account => pod}/Cargo.toml (89%) rename ethcore/{pod-account/src/lib.rs => pod/src/account.rs} (100%) create mode 100644 ethcore/pod/src/lib.rs create mode 100644 ethcore/pod/src/state.rs diff --git a/Cargo.lock b/Cargo.lock index 996d8742aaf..61711b9970c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -940,7 +940,7 @@ dependencies = [ "parity-util-mem 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "patricia-trie-ethereum 0.1.0", - "pod-account 0.1.0", + "pod 0.1.0", "rand 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", "rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "rayon 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3269,7 +3269,7 @@ dependencies = [ ] [[package]] -name = "pod-account" +name = "pod" version = "0.1.0" dependencies = [ "common-types 0.1.0", @@ -3992,7 +3992,7 @@ dependencies = [ "parity-util-mem 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "patricia-trie-ethereum 0.1.0", - "pod-account 0.1.0", + "pod 0.1.0", "rlp 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "rlp_compress 0.1.0", "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/ethcore/Cargo.toml b/ethcore/Cargo.toml index 7ca1456364e..be59a9a5dcf 100644 --- a/ethcore/Cargo.toml +++ b/ethcore/Cargo.toml @@ -53,7 +53,7 @@ parity-bytes = "0.1" parity-crypto = "0.4.0" parity-snappy = "0.1" parking_lot = "0.7" -pod-account = { path = "pod-account" } +pod = { path = "pod" } trie-db = "0.12.4" patricia-trie-ethereum = { path = "../util/patricia-trie-ethereum" } rand = "0.6" diff --git a/ethcore/pod-account/Cargo.toml b/ethcore/pod/Cargo.toml similarity index 89% rename from ethcore/pod-account/Cargo.toml rename to ethcore/pod/Cargo.toml index 23d752489e1..cef7d8542e0 100644 --- a/ethcore/pod-account/Cargo.toml +++ b/ethcore/pod/Cargo.toml @@ -1,6 +1,6 @@ [package] -description = "Account system expressed in Plain Old Data." -name = "pod-account" +description = "State/Account system expressed in Plain Old Data." +name = "pod" version = "0.1.0" authors = ["Parity Technologies "] edition = "2018" diff --git a/ethcore/pod-account/src/lib.rs b/ethcore/pod/src/account.rs similarity index 100% rename from ethcore/pod-account/src/lib.rs rename to ethcore/pod/src/account.rs diff --git a/ethcore/pod/src/lib.rs b/ethcore/pod/src/lib.rs new file mode 100644 index 00000000000..0662e2bee23 --- /dev/null +++ b/ethcore/pod/src/lib.rs @@ -0,0 +1,19 @@ +// 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 . + +pub mod account; +pub mod state; + diff --git a/ethcore/pod/src/state.rs b/ethcore/pod/src/state.rs new file mode 100644 index 00000000000..e69de29bb2d diff --git a/ethcore/src/pod_state.rs b/ethcore/src/pod_state.rs index 406bcc05dc4..b952b99554d 100644 --- a/ethcore/src/pod_state.rs +++ b/ethcore/src/pod_state.rs @@ -19,7 +19,7 @@ use std::collections::BTreeMap; use ethereum_types::{H256, Address}; use triehash::sec_trie_root; -use pod_account::{self, PodAccount}; +use pod::{self, PodAccount}; use types::state_diff::StateDiff; use ethjson; @@ -76,7 +76,7 @@ pub fn diff_pod(pre: &PodState, post: &PodState) -> StateDiff { #[cfg(test)] mod test { use std::collections::BTreeMap; - use pod_account::PodAccount; + use pod::PodAccount; use types::account_diff::{AccountDiff, Diff}; use types::state_diff::StateDiff; use super::{PodState, Address}; diff --git a/ethcore/src/state/mod.rs b/ethcore/src/state/mod.rs index bd4a0069d0b..3c5f5e564c6 100644 --- a/ethcore/src/state/mod.rs +++ b/ethcore/src/state/mod.rs @@ -33,7 +33,7 @@ use error::Error; use executive::{Executive, TransactOptions}; use factories::{Factories, VmFactory}; use trace::{self, FlatTrace, VMTrace}; -use pod_account::*; +use pod::*; use pod_state::{self, PodState}; use types::basic_account::BasicAccount; use executed::{Executed, ExecutionError}; diff --git a/ethcore/state-account/Cargo.toml b/ethcore/state-account/Cargo.toml index 5a15ff5a293..4ad59cfeb0c 100644 --- a/ethcore/state-account/Cargo.toml +++ b/ethcore/state-account/Cargo.toml @@ -19,7 +19,7 @@ log = "0.4" lru-cache = "0.1.2" parity-bytes = "0.1.0" parity-util-mem = "0.1.0" -pod-account = { path = "../pod-account" } +pod = { path = "../pod" } rlp = "0.4.0" serde = { version = "1.0", features = ["derive"] } trace = { path = "../trace" } diff --git a/ethcore/state-account/src/account.rs b/ethcore/state-account/src/account.rs index b38d54bb976..77c5eb14f62 100644 --- a/ethcore/state-account/src/account.rs +++ b/ethcore/state-account/src/account.rs @@ -33,7 +33,7 @@ use trie_db::{Recorder, Trie}; use common_types::basic_account::BasicAccount; use ethtrie::{Result as TrieResult, SecTrieDB, TrieDB, TrieFactory}; use keccak_hasher::KeccakHasher; -use pod_account::PodAccount; +use pod::PodAccount; const STORAGE_CACHE_ITEMS: usize = 8192; diff --git a/ethcore/state-account/src/state.rs b/ethcore/state-account/src/state.rs index 0b63a43c28d..82a19fb485a 100644 --- a/ethcore/state-account/src/state.rs +++ b/ethcore/state-account/src/state.rs @@ -45,7 +45,7 @@ use common_types::{ }; use ethtrie::{Result as TrieResult, TrieDB}; use keccak_hasher::KeccakHasher; -use pod_account::*; +use pod::*; use rlp::DecoderError; use vm::EnvInfo; use derive_more::{Display, From}; From d9ce43d4672670ecd5f67012f2e7b91626c45ecb Mon Sep 17 00:00:00 2001 From: David Palm Date: Fri, 5 Jul 2019 09:16:01 +0200 Subject: [PATCH 24/87] Move PodState to pod crate --- ethcore/pod/src/state.rs | 200 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 200 insertions(+) diff --git a/ethcore/pod/src/state.rs b/ethcore/pod/src/state.rs index e69de29bb2d..00e2d7755a1 100644 --- a/ethcore/pod/src/state.rs +++ b/ethcore/pod/src/state.rs @@ -0,0 +1,200 @@ +// 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 of all accounts in the system expressed in Plain Old Data. + +use std::collections::BTreeMap; +use ethereum_types::{H256, Address}; +use triehash::sec_trie_root; +use common_types::state_diff::StateDiff; +use ethjson; +use serde::Serialize; + +use crate::account::PodAccount; + +/// State of all accounts in the system expressed in Plain Old Data. +#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize)] +pub struct PodState(BTreeMap); + +impl PodState { + /// Get the underlying map. + pub fn get(&self) -> &BTreeMap { &self.0 } + + /// Get the root hash of the trie of the RLP of this. + pub fn root(&self) -> H256 { + sec_trie_root(self.0.iter().map(|(k, v)| (k, v.rlp()))) + } + + /// Drain object to get the underlying map. + pub fn drain(self) -> BTreeMap { self.0 } +} + +impl From for PodState { + fn from(s: ethjson::blockchain::State) -> PodState { + let state = s.into_iter().map(|(addr, acc)| (addr.into(), PodAccount::from(acc))).collect(); + PodState(state) + } +} + +impl From for PodState { + fn from(s: ethjson::spec::State) -> PodState { + let state: BTreeMap<_,_> = s.into_iter() + .filter(|pair| !pair.1.is_empty()) + .map(|(addr, acc)| (addr.into(), PodAccount::from(acc))) + .collect(); + PodState(state) + } +} + +impl From> for PodState { + fn from(s: BTreeMap) -> Self { + PodState(s) + } +} + +/// Calculate and return diff between `pre` state and `post` state. +pub fn diff_pod(pre: &PodState, post: &PodState) -> StateDiff { + StateDiff { + raw: pre.0.keys() + .chain(post.0.keys()) + .filter_map(|acc| crate::account::diff_pod(pre.0.get(acc), post.0.get(acc)).map(|d| (*acc, d))) + .collect() + } +} + +#[cfg(test)] +mod test { + use std::collections::BTreeMap; + use common_types::{ + account_diff::{AccountDiff, Diff}, + state_diff::StateDiff, + }; + use ethereum_types::Address; + use crate::account::PodAccount; + use super::PodState; + use macros::map; + + #[test] + fn create_delete() { + let a = PodState::from(map![ + Address::from_low_u64_be(1) => PodAccount { + balance: 69.into(), + nonce: 0.into(), + code: Some(Vec::new()), + storage: map![], + } + ]); + assert_eq!(super::diff_pod(&a, &PodState::default()), StateDiff { raw: map![ + Address::from_low_u64_be(1) => AccountDiff{ + balance: Diff::Died(69.into()), + nonce: Diff::Died(0.into()), + code: Diff::Died(vec![]), + storage: map![], + } + ]}); + assert_eq!(super::diff_pod(&PodState::default(), &a), StateDiff{ raw: map![ + Address::from_low_u64_be(1) => AccountDiff{ + balance: Diff::Born(69.into()), + nonce: Diff::Born(0.into()), + code: Diff::Born(vec![]), + storage: map![], + } + ]}); + } + + #[test] + fn create_delete_with_unchanged() { + let a = PodState::from(map![ + Address::from_low_u64_be(1) => PodAccount { + balance: 69.into(), + nonce: 0.into(), + code: Some(Vec::new()), + storage: map![], + } + ]); + let b = PodState::from(map![ + Address::from_low_u64_be(1) => PodAccount { + balance: 69.into(), + nonce: 0.into(), + code: Some(Vec::new()), + storage: map![], + }, + Address::from_low_u64_be(2) => PodAccount { + balance: 69.into(), + nonce: 0.into(), + code: Some(Vec::new()), + storage: map![], + } + ]); + assert_eq!(super::diff_pod(&a, &b), StateDiff { raw: map![ + Address::from_low_u64_be(2) => AccountDiff{ + balance: Diff::Born(69.into()), + nonce: Diff::Born(0.into()), + code: Diff::Born(vec![]), + storage: map![], + } + ]}); + assert_eq!(super::diff_pod(&b, &a), StateDiff { raw: map![ + Address::from_low_u64_be(2) => AccountDiff{ + balance: Diff::Died(69.into()), + nonce: Diff::Died(0.into()), + code: Diff::Died(vec![]), + storage: map![], + } + ]}); + } + + #[test] + fn change_with_unchanged() { + let a = PodState::from(map![ + Address::from_low_u64_be(1) => PodAccount { + balance: 69.into(), + nonce: 0.into(), + code: Some(Vec::new()), + storage: map![], + }, + Address::from_low_u64_be(2) => PodAccount { + balance: 69.into(), + nonce: 0.into(), + code: Some(Vec::new()), + storage: map![], + } + ]); + let b = PodState::from(map![ + Address::from_low_u64_be(1) => PodAccount { + balance: 69.into(), + nonce: 1.into(), + code: Some(Vec::new()), + storage: map![], + }, + Address::from_low_u64_be(2) => PodAccount { + balance: 69.into(), + nonce: 0.into(), + code: Some(Vec::new()), + storage: map![], + } + ]); + assert_eq!(super::diff_pod(&a, &b), StateDiff { raw: map![ + Address::from_low_u64_be(1) => AccountDiff{ + balance: Diff::Same, + nonce: Diff::Changed(0.into(), 1.into()), + code: Diff::Same, + storage: map![], + } + ]}); + } + +} From aebee1d651be1f757b5179ea95681443a2458221 Mon Sep 17 00:00:00 2001 From: David Palm Date: Fri, 5 Jul 2019 09:32:58 +0200 Subject: [PATCH 25/87] Use PodState from pod crate --- ethcore/pod/src/lib.rs | 4 ++++ ethcore/src/client/evm_test_client.rs | 19 ++++++++++--------- ethcore/src/lib.rs | 7 +++---- ethcore/src/spec/spec.rs | 2 +- ethcore/src/state/mod.rs | 14 +++++--------- 5 files changed, 23 insertions(+), 23 deletions(-) diff --git a/ethcore/pod/src/lib.rs b/ethcore/pod/src/lib.rs index 0662e2bee23..21a291508d4 100644 --- a/ethcore/pod/src/lib.rs +++ b/ethcore/pod/src/lib.rs @@ -17,3 +17,7 @@ pub mod account; pub mod state; +pub use { + account::PodAccount, + state::PodState, +}; diff --git a/ethcore/src/client/evm_test_client.rs b/ethcore/src/client/evm_test_client.rs index b331c214501..ce79a5c190b 100644 --- a/ethcore/src/client/evm_test_client.rs +++ b/ethcore/src/client/evm_test_client.rs @@ -21,7 +21,8 @@ use std::sync::Arc; use ethereum_types::{H256, U256, H160}; use {factories, journaldb, trie, kvdb_memorydb}; use kvdb::{self, KeyValueDB}; -use {state, state_db, client, executive, trace, db, spec, pod_state}; +use {state, state_db, client, executive, trace, db, spec}; +use pod::PodState; use types::{log_entry, receipt, transaction}; use factories::Factories; use evm::{VMType, FinalizationResult}; @@ -67,10 +68,10 @@ use ethjson::spec::ForkSpec; pub struct EvmTestClient<'a> { state: state::State, spec: &'a spec::Spec, - dump_state: fn(&state::State) -> Option, + dump_state: fn(&state::State) -> Option, } -fn no_dump_state(_: &state::State) -> Option { +fn no_dump_state(_: &state::State) -> Option { None } @@ -100,7 +101,7 @@ impl<'a> EvmTestClient<'a> { } /// Change default function for dump state (default does not dump) - pub fn set_dump_state_fn(&mut self, dump_state: fn(&state::State) -> Option) { + pub fn set_dump_state_fn(&mut self, dump_state: fn(&state::State) -> Option) { self.dump_state = dump_state; } @@ -124,7 +125,7 @@ impl<'a> EvmTestClient<'a> { /// Creates new EVM test client with an in-memory DB initialized with given PodState. /// Takes a `TrieSpec` to set the type of trie. - pub fn from_pod_state_with_trie(spec: &'a spec::Spec, pod_state: pod_state::PodState, trie_spec: trie::TrieSpec) -> Result { + pub fn from_pod_state_with_trie(spec: &'a spec::Spec, pod_state: PodState, trie_spec: trie::TrieSpec) -> Result { let factories = Self::factories(trie_spec); let state = Self::state_from_pod(spec, &factories, pod_state)?; @@ -136,7 +137,7 @@ impl<'a> EvmTestClient<'a> { } /// Creates new EVM test client with an in-memory DB initialized with given PodState. - pub fn from_pod_state(spec: &'a spec::Spec, pod_state: pod_state::PodState) -> Result { + pub fn from_pod_state(spec: &'a spec::Spec, pod_state: PodState) -> Result { Self::from_pod_state_with_trie(spec, pod_state, trie::TrieSpec::Secure) } @@ -170,7 +171,7 @@ impl<'a> EvmTestClient<'a> { ).map_err(EvmTestError::Trie) } - fn state_from_pod(spec: &'a spec::Spec, factories: &Factories, pod_state: pod_state::PodState) -> Result, EvmTestError> { + fn state_from_pod(spec: &'a spec::Spec, factories: &Factories, pod_state: PodState) -> Result, EvmTestError> { let db = Arc::new(kvdb_memorydb::create(db::NUM_COLUMNS.expect("We use column-based DB; qed"))); let journal_db = journaldb::new(db.clone(), journaldb::Algorithm::EarlyMerge, db::COL_STATE); let state_db = state_db::StateDB::new(journal_db, 5 * 1024 * 1024); @@ -330,7 +331,7 @@ pub struct TransactSuccess { /// outcome pub outcome: receipt::TransactionOutcome, /// end state if needed - pub end_state: Option, + pub end_state: Option, } /// To be returned inside a std::result::Result::Err after a failed @@ -342,5 +343,5 @@ pub struct TransactErr { /// Execution error pub error: ::error::Error, /// end state if needed - pub end_state: Option, + pub end_state: Option, } diff --git a/ethcore/src/lib.rs b/ethcore/src/lib.rs index 37151723d35..d8083536481 100644 --- a/ethcore/src/lib.rs +++ b/ethcore/src/lib.rs @@ -89,7 +89,7 @@ extern crate parity_bytes as bytes; extern crate parity_crypto; extern crate parity_snappy as snappy; extern crate parking_lot; -extern crate pod_account; +extern crate pod; extern crate trie_db as trie; extern crate patricia_trie_ethereum as ethtrie; extern crate rand; @@ -140,8 +140,6 @@ extern crate macros; extern crate rlp_derive; #[macro_use] extern crate trace_time; -#[macro_use] -extern crate serde_derive; #[cfg_attr(test, macro_use)] extern crate evm; @@ -162,7 +160,8 @@ pub mod executed; pub mod executive; pub mod machine; pub mod miner; -pub mod pod_state; +// TODO: +//pub mod pod_state; pub mod snapshot; pub mod spec; pub mod state; diff --git a/ethcore/src/spec/spec.rs b/ethcore/src/spec/spec.rs index 5e1d116c071..a441371862b 100644 --- a/ethcore/src/spec/spec.rs +++ b/ethcore/src/spec/spec.rs @@ -42,7 +42,7 @@ use error::Error; use executive::Executive; use factories::Factories; use machine::Machine; -use pod_state::PodState; +use pod::PodState; use spec::Genesis; use spec::seal::Generic as GenericSeal; use state::backend::Basic as BasicBackend; diff --git a/ethcore/src/state/mod.rs b/ethcore/src/state/mod.rs index 3c5f5e564c6..cb47022cf3f 100644 --- a/ethcore/src/state/mod.rs +++ b/ethcore/src/state/mod.rs @@ -33,8 +33,7 @@ use error::Error; use executive::{Executive, TransactOptions}; use factories::{Factories, VmFactory}; use trace::{self, FlatTrace, VMTrace}; -use pod::*; -use pod_state::{self, PodState}; +use pod::{self, PodAccount, PodState}; use types::basic_account::BasicAccount; use executed::{Executed, ExecutionError}; use types::state_diff::StateDiff; @@ -1093,7 +1092,7 @@ impl State { pub fn diff_from(&self, mut orig: State) -> TrieResult { let pod_state_post = self.to_pod_cache(); let pod_state_pre = orig.to_pod_diff(self)?; - Ok(pod_state::diff_pod(&pod_state_pre, &pod_state_post)) + Ok(pod::state::diff_pod(&pod_state_pre, &pod_state_post)) } /// Load required account data from the databases. Returns whether the cache succeeds. @@ -1348,6 +1347,7 @@ mod tests { use types::transaction::*; use trace::{FlatTrace, TraceError, trace}; use evm::CallType; + use pod; fn secret() -> Secret { keccak("").into() @@ -2659,8 +2659,6 @@ mod tests { #[test] fn should_trace_diff_suicided_accounts() { - use pod_account; - let a = Address::from_low_u64_be(10); let db = get_temp_state_db(); let (root, db) = { @@ -2679,7 +2677,7 @@ mod tests { assert_eq!(diff_map.len(), 1); assert!(diff_map.get(&a).is_some()); assert_eq!(diff_map.get(&a), - pod_account::diff_pod(Some(&PodAccount { + pod::account::diff_pod(Some(&PodAccount { balance: U256::from(100), nonce: U256::zero(), code: Some(Default::default()), @@ -2689,8 +2687,6 @@ mod tests { #[test] fn should_trace_diff_unmodified_storage() { - use pod_account; - let a = Address::from_low_u64_be(10); let db = get_temp_state_db(); @@ -2710,7 +2706,7 @@ mod tests { assert_eq!(diff_map.len(), 1); assert!(diff_map.get(&a).is_some()); assert_eq!(diff_map.get(&a), - pod_account::diff_pod(Some(&PodAccount { + pod::account::diff_pod(Some(&PodAccount { balance: U256::zero(), nonce: U256::zero(), code: Some(Default::default()), From 6344f9d73376a47574374bbbd0ea035382d61eb7 Mon Sep 17 00:00:00 2001 From: David Palm Date: Fri, 5 Jul 2019 09:50:16 +0200 Subject: [PATCH 26/87] Fix use clause for json tests --- ethcore/src/json_tests/state.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ethcore/src/json_tests/state.rs b/ethcore/src/json_tests/state.rs index c51a2c361c2..524010ffeea 100644 --- a/ethcore/src/json_tests/state.rs +++ b/ethcore/src/json_tests/state.rs @@ -16,7 +16,7 @@ use std::path::Path; use super::test_common::*; -use pod_state::PodState; +use pod::PodState; use trace; use client::{EvmTestClient, EvmTestError, TransactErr, TransactSuccess}; use ethjson; From 63564de1ed51372430d7bb7437aa7b3ae09224e9 Mon Sep 17 00:00:00 2001 From: David Palm Date: Fri, 5 Jul 2019 09:54:52 +0200 Subject: [PATCH 27/87] Sort out evmbin --- evmbin/Cargo.toml | 2 ++ evmbin/src/display/json.rs | 2 +- evmbin/src/display/std_json.rs | 8 ++++---- evmbin/src/info.rs | 16 +++++++++------- evmbin/src/main.rs | 3 ++- 5 files changed, 18 insertions(+), 13 deletions(-) diff --git a/evmbin/Cargo.toml b/evmbin/Cargo.toml index 57ed25289cb..cbd70661858 100644 --- a/evmbin/Cargo.toml +++ b/evmbin/Cargo.toml @@ -18,10 +18,12 @@ ethjson = { path = "../json" } evm = { path = "../ethcore/evm" } panic_hook = { path = "../util/panic-hook" } parity-bytes = "0.1" +pod = { path = "../ethcore/pod" } rustc-hex = "1.0" serde = "1.0" serde_derive = "1.0" serde_json = "1.0" +trace = { path = "../ethcore/trace" } vm = { path = "../ethcore/vm" } [dev-dependencies] diff --git a/evmbin/src/display/json.rs b/evmbin/src/display/json.rs index 195e00c7379..0989f9a0845 100644 --- a/evmbin/src/display/json.rs +++ b/evmbin/src/display/json.rs @@ -21,7 +21,7 @@ use std::mem; use ethereum_types::{U256, H256, BigEndianHash}; use bytes::ToPretty; -use ethcore::trace; +use trace; use display; use info as vm; diff --git a/evmbin/src/display/std_json.rs b/evmbin/src/display/std_json.rs index 1734c305e24..478cc24a848 100644 --- a/evmbin/src/display/std_json.rs +++ b/evmbin/src/display/std_json.rs @@ -21,8 +21,8 @@ use std::io; use ethereum_types::{H256, U256, BigEndianHash}; use bytes::ToPretty; -use ethcore::{trace, pod_state}; - +use trace; +use pod::PodState; use display; use info as vm; @@ -101,7 +101,7 @@ pub struct MessageFailure<'a> { #[derive(Serialize, Debug)] pub struct DumpData<'a> { root: &'a H256, - accounts: &'a pod_state::PodState, + accounts: &'a PodState, } #[derive(Serialize, Debug)] @@ -154,7 +154,7 @@ impl Informant { } } - fn dump_state_into(trace_sink: &mut Trace, root: H256, end_state: &Option) { + fn dump_state_into(trace_sink: &mut Trace, root: H256, end_state: &Option) { if let Some(ref end_state) = end_state { let dump_data = DumpData { diff --git a/evmbin/src/info.rs b/evmbin/src/info.rs index ca068c36b0d..a21d075aad6 100644 --- a/evmbin/src/info.rs +++ b/evmbin/src/info.rs @@ -19,8 +19,10 @@ use std::time::{Instant, Duration}; use ethereum_types::{H256, U256}; use ethcore::client::{self, EvmTestClient, EvmTestError, TransactErr, TransactSuccess}; -use ethcore::{state, state_db, trace, spec, pod_state, TrieSpec}; +use ethcore::{state, state_db, spec, TrieSpec}; +use trace; use ethjson; +use pod::PodState; use types::transaction; use vm::ActionParams; @@ -52,7 +54,7 @@ pub struct Success { /// Traces pub traces: Option, /// Optional end state dump - pub end_state: Option, + pub end_state: Option, } /// Execution failed @@ -69,7 +71,7 @@ pub struct Failure { /// Traces pub traces: Option, /// Optional end state dump - pub end_state: Option, + pub end_state: Option, } /// EVM Execution result @@ -105,7 +107,7 @@ pub fn run_transaction( name: &str, idx: usize, spec: ðjson::spec::ForkSpec, - pre_state: &pod_state::PodState, + pre_state: &PodState, post_root: H256, env_info: &client::EnvInfo, transaction: transaction::SignedTransaction, @@ -152,7 +154,7 @@ pub fn run_transaction( T::finish(result, &mut sink) } -fn dump_state(state: &state::State) -> Option { +fn dump_state(state: &state::State) -> Option { state.to_pod_full().ok() } @@ -161,10 +163,10 @@ pub fn run<'a, F, X>( spec: &'a spec::Spec, trie_spec: TrieSpec, initial_gas: U256, - pre_state: &'a pod_state::PodState, + pre_state: &'a PodState, run: F, ) -> RunResult where - F: FnOnce(EvmTestClient) -> (Result, EvmTestError>, H256, Option, Option, Option), + F: FnOnce(EvmTestClient) -> (Result, EvmTestError>, H256, Option, Option, Option), { let do_dump = trie_spec == TrieSpec::Fat; diff --git a/evmbin/src/main.rs b/evmbin/src/main.rs index ee75f9174d1..aef4cdfcbcf 100644 --- a/evmbin/src/main.rs +++ b/evmbin/src/main.rs @@ -41,7 +41,6 @@ extern crate rustc_hex; extern crate serde; #[macro_use] extern crate serde_derive; -#[macro_use] extern crate serde_json; extern crate docopt; extern crate parity_bytes as bytes; @@ -49,7 +48,9 @@ extern crate ethereum_types; extern crate vm; extern crate evm; extern crate panic_hook; +extern crate pod; extern crate env_logger; +extern crate trace; #[cfg(test)] #[macro_use] From 147bde5e69fad9eb6a23d6d058e5c444b05183f3 Mon Sep 17 00:00:00 2001 From: David Palm Date: Fri, 5 Jul 2019 10:11:30 +0200 Subject: [PATCH 28/87] Add missing code and use PodState --- ethcore/state-account/src/state.rs | 385 +++++++++++++++++++++++------ 1 file changed, 305 insertions(+), 80 deletions(-) diff --git a/ethcore/state-account/src/state.rs b/ethcore/state-account/src/state.rs index 82a19fb485a..a6a43c67bf6 100644 --- a/ethcore/state-account/src/state.rs +++ b/ethcore/state-account/src/state.rs @@ -36,6 +36,7 @@ use keccak_hash::{KECCAK_EMPTY, KECCAK_NULL_RLP}; use kvdb::DBValue; use log::{warn, trace}; use parity_bytes::Bytes; +use pod::{self, PodAccount, PodState}; use trie_db::{Recorder, Trie}; use common_types::{ basic_account::BasicAccount, @@ -45,7 +46,6 @@ use common_types::{ }; use ethtrie::{Result as TrieResult, TrieDB}; use keccak_hasher::KeccakHasher; -use pod::*; use rlp::DecoderError; use vm::EnvInfo; use derive_more::{Display, From}; @@ -582,6 +582,232 @@ impl State { .unwrap_or(KECCAK_NULL_RLP)) } + + /// Get the value of storage at a specific checkpoint. + pub fn checkpoint_storage_at(&self, start_checkpoint_index: usize, address: &Address, key: &H256) -> TrieResult> { + #[must_use] + enum ReturnKind { + /// Use original storage at value at this address. + OriginalAt, + /// The checkpoint storage value is the same as the checkpoint storage value at the next checkpoint. + SameAsNext, + } + + let kind = { + let checkpoints = self.checkpoints.borrow(); + + if start_checkpoint_index >= checkpoints.len() { + // The checkpoint was not found. Return None. + return Ok(None); + } + + let mut kind = None; + + for checkpoint in checkpoints.iter().skip(start_checkpoint_index) { + match checkpoint.get(address) { + // The account exists at this checkpoint. + Some(Some(AccountEntry { account: Some(ref account), .. })) => { + if let Some(value) = account.cached_storage_at(key) { + return Ok(Some(value)); + } else { + // This account has checkpoint entry, but the key is not in the entry's cache. We can use + // original_storage_at if current account's original storage root is the same as checkpoint + // account's original storage root. Otherwise, the account must be a newly created contract. + if account.base_storage_root() == self.original_storage_root(address)? { + kind = Some(ReturnKind::OriginalAt); + break + } else { + // If account base storage root is different from the original storage root since last + // commit, then it can only be created from a new contract, where the base storage root + // would always be empty. Note that this branch is actually never called, because + // `cached_storage_at` handled this case. + warn!(target: "state", "Trying to get an account's cached storage value, but base storage root does not equal to original storage root! Assuming the value is empty."); + return Ok(Some(H256::zero())); + } + } + }, + // The account didn't exist at that point. Return empty value. + Some(Some(AccountEntry { account: None, .. })) => return Ok(Some(H256::zero())), + // The value was not cached at that checkpoint, meaning it was not modified at all. + Some(None) => { + kind = Some(ReturnKind::OriginalAt); + break + }, + // This key does not have a checkpoint entry. + None => { + kind = Some(ReturnKind::SameAsNext); + }, + } + } + + kind.expect("start_checkpoint_index is checked to be below checkpoints_len; for loop above must have been executed at least once; it will either early return, or set the kind value to Some; qed") + }; + + match kind { + ReturnKind::SameAsNext => { + // If we reached here, all previous SameAsNext failed to early return. It means that the value we want + // to fetch is the same as current. + Ok(Some(self.storage_at(address, key)?)) + }, + ReturnKind::OriginalAt => Ok(Some(self.original_storage_at(address, key)?)), + } + } + + fn storage_at_inner( + &self, address: &Address, key: &H256, f_cached_at: FCachedStorageAt, f_at: FStorageAt, + ) -> TrieResult where + FCachedStorageAt: Fn(&Account, &H256) -> Option, + FStorageAt: Fn(&Account, &dyn HashDB, &H256) -> TrieResult + { + // Storage key search and update works like this: + // 1. If there's an entry for the account in the local cache check for the key and return it if found. + // 2. If there's an entry for the account in the global cache check for the key or load it into that account. + // 3. If account is missing in the global cache load it into the local cache and cache the key there. + + { + // check local cache first without updating + let local_cache = self.cache.borrow_mut(); + let mut local_account = None; + if let Some(maybe_acc) = local_cache.get(address) { + match maybe_acc.account { + Some(ref account) => { + if let Some(value) = f_cached_at(account, key) { + return Ok(value); + } else { + local_account = Some(maybe_acc); + } + }, + _ => return Ok(H256::zero()), + } + } + // check the global cache and and cache storage key there if found, + let trie_res = self.db.get_cached(address, |acc| match acc { + None => Ok(H256::zero()), + Some(a) => { + let account_db = self.factories.accountdb.readonly(self.db.as_hash_db(), a.address_hash(address)); + f_at(a, account_db.as_hash_db(), key) + } + }); + + if let Some(res) = trie_res { + return res; + } + + // otherwise cache the account localy and cache storage key there. + if let Some(ref mut acc) = local_account { + if let Some(ref account) = acc.account { + let account_db = self.factories.accountdb.readonly(self.db.as_hash_db(), account.address_hash(address)); + return f_at(account, account_db.as_hash_db(), key) + } else { + return Ok(H256::zero()) + } + } + } + + // check if the account could exist before any requests to trie + if self.db.is_known_null(address) { return Ok(H256::zero()) } + + // account is not found in the global cache, get from the DB and insert into local + let db = &self.db.as_hash_db(); + let db = self.factories.trie.readonly(db, &self.root).expect(SEC_TRIE_DB_UNWRAP_STR); + let from_rlp = |b: &[u8]| Account::from_rlp(b).expect("decoding db value failed"); + let maybe_acc = db.get_with(address.as_bytes(), from_rlp)?; + let r = maybe_acc.as_ref().map_or(Ok(H256::zero()), |a| { + let account_db = self.factories.accountdb.readonly(self.db.as_hash_db(), a.address_hash(address)); + f_at(a, account_db.as_hash_db(), key) + }); + self.insert_cache(address, AccountEntry::new_clean(maybe_acc)); + r + } + + /// Mutate storage of account `address` so that it is `value` for `key`. + pub fn storage_at(&self, address: &Address, key: &H256) -> TrieResult { + self.storage_at_inner( + address, + key, + |account, key| { account.cached_storage_at(key) }, + |account, db, key| { account.storage_at(db, key) }, + ) + } + + /// Get the value of storage after last state commitment. + pub fn original_storage_at(&self, address: &Address, key: &H256) -> TrieResult { + self.storage_at_inner( + address, + key, + |account, key| { account.cached_original_storage_at(key) }, + |account, db, key| { account.original_storage_at(db, key) }, + ) + } + + /// Get accounts' code. + pub fn code(&self, a: &Address) -> TrieResult>> { + self.ensure_cached(a, RequireCache::Code, true, + |a| a.as_ref().map_or(None, |a| a.code().clone())) + } + + /// Get an account's code hash. + pub fn code_hash(&self, a: &Address) -> TrieResult> { + self.ensure_cached(a, RequireCache::None, true, + |a| a.as_ref().map(|a| a.code_hash())) + } + + /// Get accounts' code size. + pub fn code_size(&self, a: &Address) -> TrieResult> { + self.ensure_cached(a, RequireCache::CodeSize, true, + |a| a.as_ref().and_then(|a| a.code_size())) + } + + /// Add `incr` to the balance of account `a`. + pub fn add_balance(&mut self, a: &Address, incr: &U256, cleanup_mode: CleanupMode) -> TrieResult<()> { + trace!(target: "state", "add_balance({}, {}): {}", a, incr, self.balance(a)?); + let is_value_transfer = !incr.is_zero(); + if is_value_transfer || (cleanup_mode == CleanupMode::ForceCreate && !self.exists(a)?) { + self.require(a, false)?.add_balance(incr); + } else if let CleanupMode::TrackTouched(set) = cleanup_mode { + if self.exists(a)? { + set.insert(*a); + self.touch(a)?; + } + } + Ok(()) + } + + /// Subtract `decr` from the balance of account `a`. + pub fn sub_balance(&mut self, a: &Address, decr: &U256, cleanup_mode: &mut CleanupMode) -> TrieResult<()> { + trace!(target: "state", "sub_balance({}, {}): {}", a, decr, self.balance(a)?); + if !decr.is_zero() || !self.exists(a)? { + self.require(a, false)?.sub_balance(decr); + } + if let CleanupMode::TrackTouched(ref mut set) = *cleanup_mode { + set.insert(*a); + } + Ok(()) + } + + /// Subtracts `by` from the balance of `from` and adds it to that of `to`. + pub fn transfer_balance(&mut self, from: &Address, to: &Address, by: &U256, mut cleanup_mode: CleanupMode) -> TrieResult<()> { + self.sub_balance(from, by, &mut cleanup_mode)?; + self.add_balance(to, by, cleanup_mode)?; + Ok(()) + } + + /// Increment the nonce of account `a` by 1. + pub fn inc_nonce(&mut self, a: &Address) -> TrieResult<()> { + self.require(a, false).map(|mut x| x.inc_nonce()) + } + + /// Mutate storage of account `a` so that it is `value` for `key`. + pub fn set_storage(&mut self, a: &Address, key: H256, value: H256) -> TrieResult<()> { + trace!(target: "state", "set_storage({}:{:x} to {:x})", a, key, value); + if self.storage_at(a, &key)? != value { + self.require(a, false)?.set_storage(key, value) + } + + Ok(()) + } + + /// Initialise the code of account `a` so that it is `code`. /// NOTE: Account should have been created with `new_contract`. pub fn init_code(&mut self, a: &Address, code: Bytes) -> TrieResult<()> { @@ -747,26 +973,25 @@ impl State { Ok(()) } - // TODO: Needs PodState /// Populate the state from `accounts`. /// Used for tests. -// pub fn populate_from(&mut self, accounts: PodState) { -// assert!(self.checkpoints.borrow().is_empty()); -// for (add, acc) in accounts.drain().into_iter() { -// self.cache.borrow_mut().insert(add, AccountEntry::new_dirty(Some(Account::from_pod(acc)))); -// } -// } -// -// /// Populate a PodAccount map from this state. -// fn to_pod_cache(&self) -> PodState { -// assert!(self.checkpoints.borrow().is_empty()); -// PodState::from(self.cache.borrow().iter().fold(BTreeMap::new(), |mut m, (add, opt)| { -// if let Some(ref acc) = opt.account { -// m.insert(*add, acc.to_pod()); -// } -// m -// })) -// } + pub fn populate_from(&mut self, accounts: PodState) { + assert!(self.checkpoints.borrow().is_empty()); + for (add, acc) in accounts.drain().into_iter() { + self.cache.borrow_mut().insert(add, AccountEntry::new_dirty(Some(Account::from_pod(acc)))); + } + } + + /// Populate a PodAccount map from this state. + fn to_pod_cache(&self) -> PodState { + assert!(self.checkpoints.borrow().is_empty()); + PodState::from(self.cache.borrow().iter().fold(BTreeMap::new(), |mut m, (add, opt)| { + if let Some(ref acc) = opt.account { + m.insert(*add, acc.to_pod()); + } + m + })) + } // TODO: sort out features #[cfg(feature="to-pod-full")] @@ -838,68 +1063,66 @@ impl State { Ok(pod_account) } -// TODO: Needs PodState -// /// Populate a PodAccount map from this state, with another state as the account and storage query. -// fn to_pod_diff(&mut self, query: &State) -> TrieResult { -// assert!(self.checkpoints.borrow().is_empty()); -// -// // Merge PodAccount::to_pod for cache of self and `query`. -// let all_addresses = self.cache.borrow().keys().cloned() -// .chain(query.cache.borrow().keys().cloned()) -// .collect::>(); -// -// Ok(PodState::from(all_addresses.into_iter().fold(Ok(BTreeMap::new()), |m: TrieResult<_>, address| { -// let mut m = m?; -// -// let account = self.ensure_cached(&address, RequireCache::Code, true, |acc| { -// acc.map(|acc| { -// // Merge all modified storage keys. -// let all_keys = { -// let self_keys = acc.storage_changes().keys().cloned() -// .collect::>(); -// -// if let Some(ref query_storage) = query.cache.borrow().get(&address) -// .and_then(|opt| { -// Some(opt.account.as_ref()?.storage_changes().keys().cloned() -// .collect::>()) -// }) -// { -// self_keys.union(&query_storage).cloned().collect::>() -// } else { -// self_keys.into_iter().collect::>() -// } -// }; -// -// // Storage must be fetched after ensure_cached to avoid borrow problem. -// (*acc.balance(), *acc.nonce(), all_keys, acc.code().map(|x| x.to_vec())) -// }) -// })?; -// -// if let Some((balance, nonce, storage_keys, code)) = account { -// let storage = storage_keys.into_iter().fold(Ok(BTreeMap::new()), |s: TrieResult<_>, key| { -// let mut s = s?; -// -// s.insert(key, self.storage_at(&address, &key)?); -// Ok(s) -// })?; -// -// m.insert(address, PodAccount { -// balance, nonce, storage, code -// }); -// } -// -// Ok(m) -// })?)) -// } + /// Populate a PodAccount map from this state, with another state as the account and storage query. + fn to_pod_diff(&mut self, query: &State) -> TrieResult { + assert!(self.checkpoints.borrow().is_empty()); - // TODO: Needs PodState -// /// Returns a `StateDiff` describing the difference from `orig` to `self`. -// /// Consumes self. -// pub fn diff_from(&self, mut orig: State) -> TrieResult { -// let pod_state_post = self.to_pod_cache(); -// let pod_state_pre = orig.to_pod_diff(self)?; -// Ok(pod_state::diff_pod(&pod_state_pre, &pod_state_post)) -// } + // Merge PodAccount::to_pod for cache of self and `query`. + let all_addresses = self.cache.borrow().keys().cloned() + .chain(query.cache.borrow().keys().cloned()) + .collect::>(); + + Ok(PodState::from(all_addresses.into_iter().fold(Ok(BTreeMap::new()), |m: TrieResult<_>, address| { + let mut m = m?; + + let account = self.ensure_cached(&address, RequireCache::Code, true, |acc| { + acc.map(|acc| { + // Merge all modified storage keys. + let all_keys = { + let self_keys = acc.storage_changes().keys().cloned() + .collect::>(); + + if let Some(ref query_storage) = query.cache.borrow().get(&address) + .and_then(|opt| { + Some(opt.account.as_ref()?.storage_changes().keys().cloned() + .collect::>()) + }) + { + self_keys.union(&query_storage).cloned().collect::>() + } else { + self_keys.into_iter().collect::>() + } + }; + + // Storage must be fetched after ensure_cached to avoid borrow problem. + (*acc.balance(), *acc.nonce(), all_keys, acc.code().map(|x| x.to_vec())) + }) + })?; + + if let Some((balance, nonce, storage_keys, code)) = account { + let storage = storage_keys.into_iter().fold(Ok(BTreeMap::new()), |s: TrieResult<_>, key| { + let mut s = s?; + + s.insert(key, self.storage_at(&address, &key)?); + Ok(s) + })?; + + m.insert(address, PodAccount { + balance, nonce, storage, code + }); + } + + Ok(m) + })?)) + } + + /// Returns a `StateDiff` describing the difference from `orig` to `self`. + /// Consumes self. + pub fn diff_from(&self, mut orig: State) -> TrieResult { + let pod_state_post = self.to_pod_cache(); + let pod_state_pre = orig.to_pod_diff(self)?; + Ok(pod::state::diff_pod(&pod_state_pre, &pod_state_post)) + } /// Load required account data from the databases. Returns whether the cache succeeds. #[must_use] @@ -1049,3 +1272,5 @@ impl State { Ok(self.require(a, false)?.reset_code_and_storage(code, storage)) } } + +// TODO tests and whatnot From ceda5f8fe1e06baaabf5a7d103c22ffd733d2492 Mon Sep 17 00:00:00 2001 From: David Palm Date: Fri, 5 Jul 2019 11:10:52 +0200 Subject: [PATCH 29/87] Move code that depends on Machine and Executive to own module --- Cargo.lock | 2 + ethcore/src/block.rs | 2 + ethcore/src/client/client.rs | 5 +- ethcore/src/client/evm_test_client.rs | 2 + .../engines/validator_set/safe_contract.rs | 2 +- ethcore/src/executive_state.rs | 222 ++++++++++++++++++ ethcore/src/lib.rs | 1 + ethcore/src/spec/spec.rs | 5 +- ethcore/src/state/mod.rs | 176 ++------------ ethcore/state-account/src/state.rs | 1 - 10 files changed, 249 insertions(+), 169 deletions(-) create mode 100644 ethcore/src/executive_state.rs diff --git a/Cargo.lock b/Cargo.lock index 61711b9970c..1c87387f887 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1486,12 +1486,14 @@ dependencies = [ "evm 0.1.0", "panic_hook 0.1.0", "parity-bytes 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "pod 0.1.0", "pretty_assertions 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", "tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", + "trace 0.1.0", "vm 0.1.0", ] diff --git a/ethcore/src/block.rs b/ethcore/src/block.rs index 3a584a196db..12afd98d195 100644 --- a/ethcore/src/block.rs +++ b/ethcore/src/block.rs @@ -55,6 +55,8 @@ use types::transaction::{SignedTransaction, Error as TransactionError}; use types::header::Header; use types::receipt::{Receipt, TransactionOutcome}; +use executive_state::ExecutiveStateWithMachineZomgBetterName; + /// Block that is ready for transactions to be added. /// /// It's a bit like a Vec, except that whenever a transaction is pushed, we execute it and diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index ff0900b1a2a..37ee894236d 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -70,7 +70,8 @@ use factories::{Factories, VmFactory}; use miner::{Miner, MinerService}; use snapshot::{self, io as snapshot_io, SnapshotClient}; use spec::Spec; -use state::{self, State}; +use state::State; +use executive_state; use state_db::StateDB; use trace::{self, TraceDB, ImportRequest as TraceImportRequest, LocalizedTrace, Database as TraceDatabase}; use transaction_ext::Transaction; @@ -2540,7 +2541,7 @@ impl ProvingBlockChainClient for Client { env_info.gas_limit = transaction.gas.clone(); let mut jdb = self.state_db.read().journal_db().boxed_clone(); - state::prove_transaction_virtual( + executive_state::prove_transaction_virtual( jdb.as_hash_db_mut(), header.state_root().clone(), &transaction, diff --git a/ethcore/src/client/evm_test_client.rs b/ethcore/src/client/evm_test_client.rs index ce79a5c190b..a92b2fcd492 100644 --- a/ethcore/src/client/evm_test_client.rs +++ b/ethcore/src/client/evm_test_client.rs @@ -29,6 +29,8 @@ use evm::{VMType, FinalizationResult}; use vm::{self, ActionParams}; use ethtrie; +use executive_state::ExecutiveStateWithMachineZomgBetterName; + /// EVM test Error. #[derive(Debug)] pub enum EvmTestError { diff --git a/ethcore/src/engines/validator_set/safe_contract.rs b/ethcore/src/engines/validator_set/safe_contract.rs index 0b3e804b1fc..b524f2ce70c 100644 --- a/ethcore/src/engines/validator_set/safe_contract.rs +++ b/ethcore/src/engines/validator_set/safe_contract.rs @@ -129,7 +129,7 @@ fn check_first_proof(machine: &Machine, contract_address: Address, old_header: H data, }.fake_sign(from); - let res = ::state::check_proof( + let res = ::executive_state::check_proof( state_items, *old_header.state_root(), &tx, diff --git a/ethcore/src/executive_state.rs b/ethcore/src/executive_state.rs new file mode 100644 index 00000000000..54629227fc6 --- /dev/null +++ b/ethcore/src/executive_state.rs @@ -0,0 +1,222 @@ +// 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 . + +use machine::Machine; +use vm::EnvInfo; +use executive::{Executive, TransactOptions}; +use executed::{Executed, ExecutionError}; +use types::{ + transaction::SignedTransaction, + receipt::{TransactionOutcome, Receipt}, +}; +use trace::{FlatTrace, VMTrace}; +use state::{ApplyResult, ApplyOutcome, State, ProvedExecution}; +use state_account::backend::{self, Backend}; +use ethereum_types::H256; +use factories::Factories; +use bytes::Bytes; +use keccak_hasher::KeccakHasher; +use kvdb::DBValue; +use hash_db::AsHashDB; + +/// Check the given proof of execution. +/// `Err(ExecutionError::Internal)` indicates failure, everything else indicates +/// a successful proof (as the transaction itself may be poorly chosen). +pub fn check_proof( + proof: &[DBValue], + root: H256, + transaction: &SignedTransaction, + machine: &Machine, + env_info: &EnvInfo, +) -> ProvedExecution { + let backend = self::backend::ProofCheck::new(proof); + let mut factories = Factories::default(); + factories.accountdb = ::account_db::Factory::Plain; + + let res = State::from_existing( + backend, + root, + machine.account_start_nonce(env_info.number), + factories + ); + + let mut state = match res { + Ok(state) => state, + Err(_) => return ProvedExecution::BadProof, + }; + + let options = TransactOptions::with_no_tracing().save_output_from_contract(); + match state.execute(env_info, machine, transaction, options, true) { + Ok(executed) => ProvedExecution::Complete(Box::new(executed)), + Err(ExecutionError::Internal(_)) => ProvedExecution::BadProof, + Err(e) => ProvedExecution::Failed(e), + } +} + +/// Prove a `virtual` transaction on the given state. +/// Returns `None` when the transacion could not be proved, +/// and a proof otherwise. +pub fn prove_transaction_virtual + Send + Sync>( + db: H, + root: H256, + transaction: &SignedTransaction, + machine: &Machine, + env_info: &EnvInfo, + factories: Factories, +) -> Option<(Bytes, Vec)> { + use state_account::backend::Proving; + + let backend = Proving::new(db); + let res = State::from_existing( + backend, + root, + machine.account_start_nonce(env_info.number), + factories, + ); + + let mut state = match res { + Ok(state) => state, + Err(_) => return None, + }; + + let options = TransactOptions::with_no_tracing().dont_check_nonce().save_output_from_contract(); + match state.execute(env_info, machine, transaction, options, true) { + Err(ExecutionError::Internal(_)) => None, + Err(e) => { + trace!(target: "state", "Proved call failed: {}", e); + Some((Vec::new(), state.drop().1.extract_proof())) + } + Ok(res) => Some((res.output, state.drop().1.extract_proof())), + } +} + +/// Collects code that needs a Machine and/or Executive +pub trait ExecutiveStateWithMachineZomgBetterName { + /// Execute a given transaction, producing a receipt and an optional trace. + /// This will change the state accordingly. + fn apply( + &mut self, + env_info: &EnvInfo, + machine: &Machine, + t: &SignedTransaction, + tracing: bool + ) -> ApplyResult; + + /// Execute a given transaction with given tracer and VM tracer producing a receipt and an optional trace. + /// This will change the state accordingly. + fn apply_with_tracing( + &mut self, + env_info: &EnvInfo, + machine: &Machine, + t: &SignedTransaction, + tracer: T, + vm_tracer: V, + ) -> ApplyResult + where + T: trace::Tracer, + V: trace::VMTracer; + // Execute a given transaction without committing changes. + // + // `virt` signals that we are executing outside of a block set and restrictions like + // gas limits and gas costs should be lifted. + fn execute( + &mut self, + env_info: &EnvInfo, + machine: &Machine, + t: &SignedTransaction, + options: TransactOptions, + virt: bool + ) -> Result, ExecutionError> + where + T: trace::Tracer, + V: trace::VMTracer; + +} + +impl ExecutiveStateWithMachineZomgBetterName for State { + /// Execute a given transaction, producing a receipt and an optional trace. + /// This will change the state accordingly. + fn apply(&mut self, env_info: &EnvInfo, machine: &Machine, t: &SignedTransaction, tracing: bool) -> ApplyResult { + if tracing { + let options = TransactOptions::with_tracing(); + self.apply_with_tracing(env_info, machine, t, options.tracer, options.vm_tracer) + } else { + let options = TransactOptions::with_no_tracing(); + self.apply_with_tracing(env_info, machine, t, options.tracer, options.vm_tracer) + } + } + + /// Execute a given transaction with given tracer and VM tracer producing a receipt and an optional trace. + /// This will change the state accordingly. + fn apply_with_tracing( + &mut self, + env_info: &EnvInfo, + machine: &Machine, + t: &SignedTransaction, + tracer: T, + vm_tracer: V, + ) -> ApplyResult where + T: trace::Tracer, + V: trace::VMTracer, + { + let options = TransactOptions::new(tracer, vm_tracer); + let e = self.execute(env_info, machine, t, options, false)?; + let params = machine.params(); + + let eip658 = env_info.number >= params.eip658_transition; + let no_intermediate_commits = + eip658 || + (env_info.number >= params.eip98_transition && env_info.number >= params.validate_receipts_transition); + + let outcome = if no_intermediate_commits { + if eip658 { + TransactionOutcome::StatusCode(if e.exception.is_some() { 0 } else { 1 }) + } else { + TransactionOutcome::Unknown + } + } else { + self.commit()?; + TransactionOutcome::StateRoot(self.root().clone()) + }; + + let output = e.output; + let receipt = Receipt::new(outcome, e.cumulative_gas_used, e.logs); + trace!(target: "state", "Transaction receipt: {:?}", receipt); + + Ok(ApplyOutcome { + receipt, + output, + trace: e.trace, + vm_trace: e.vm_trace, + }) + } + + // Execute a given transaction without committing changes. + // + // `virt` signals that we are executing outside of a block set and restrictions like + // gas limits and gas costs should be lifted. + fn execute(&mut self, env_info: &EnvInfo, machine: &Machine, t: &SignedTransaction, options: TransactOptions, virt: bool) + -> Result, ExecutionError> where T: trace::Tracer, V: trace::VMTracer, + { + let schedule = machine.schedule(env_info.number); + let mut e = Executive::new(self, env_info, machine, &schedule); + + match virt { + true => e.transact_virtual(t, options), + false => e.transact(t, options), + } + } +} diff --git a/ethcore/src/lib.rs b/ethcore/src/lib.rs index d8083536481..a1ae6cc336a 100644 --- a/ethcore/src/lib.rs +++ b/ethcore/src/lib.rs @@ -171,6 +171,7 @@ pub mod verification; mod externalities; mod tx_filter; +mod executive_state; #[cfg(test)] mod tests; diff --git a/ethcore/src/spec/spec.rs b/ethcore/src/spec/spec.rs index a441371862b..a5048ffb570 100644 --- a/ethcore/src/spec/spec.rs +++ b/ethcore/src/spec/spec.rs @@ -45,8 +45,7 @@ use machine::Machine; use pod::PodState; use spec::Genesis; use spec::seal::Generic as GenericSeal; -use state::backend::Basic as BasicBackend; -use state::{Backend, State, Substate}; +use state::{Backend, State, Substate, backend::Basic as BasicBackend}; use trace::{NoopTracer, NoopVMTracer}; pub use ethash::OptimizeFor; @@ -882,7 +881,7 @@ impl Spec { data: d, }.fake_sign(from); - let res = ::state::prove_transaction_virtual( + let res = ::executive_state::prove_transaction_virtual( db.as_hash_db_mut(), *genesis.state_root(), &tx, diff --git a/ethcore/src/state/mod.rs b/ethcore/src/state/mod.rs index cb47022cf3f..5705e89f3fd 100644 --- a/ethcore/src/state/mod.rs +++ b/ethcore/src/state/mod.rs @@ -26,30 +26,25 @@ use std::fmt; use std::sync::Arc; use hash::{KECCAK_NULL_RLP, KECCAK_EMPTY}; -use types::receipt::{Receipt, TransactionOutcome}; -use machine::Machine; -use vm::EnvInfo; +use bytes::Bytes; use error::Error; -use executive::{Executive, TransactOptions}; -use factories::{Factories, VmFactory}; -use trace::{self, FlatTrace, VMTrace}; -use pod::{self, PodAccount, PodState}; -use types::basic_account::BasicAccount; -use executed::{Executed, ExecutionError}; -use types::state_diff::StateDiff; -use types::transaction::SignedTransaction; -use state_db::StateDB; - use ethereum_types::{H256, U256, Address}; -use hash_db::{HashDB, AsHashDB}; +use ethtrie::{TrieDB, Result as TrieResult}; +use executed::{Executed, ExecutionError}; +use factories::{Factories, VmFactory}; +use hash_db::HashDB; use keccak_hasher::KeccakHasher; use kvdb::DBValue; -use bytes::Bytes; - +use pod::{self, PodAccount, PodState}; +use state_db::StateDB; use trie::{Trie, TrieError, Recorder}; -use ethtrie::{TrieDB, Result as TrieResult}; +use types::{ + basic_account::BasicAccount, + receipt::Receipt, + state_diff::StateDiff, +}; -pub use state_account::{ +use state_account::{ account::Account, backend::{self, Backend}, substate::{CleanupMode, Substate}, @@ -185,77 +180,6 @@ impl AccountEntry { } } -/// Check the given proof of execution. -/// `Err(ExecutionError::Internal)` indicates failure, everything else indicates -/// a successful proof (as the transaction itself may be poorly chosen). -pub fn check_proof( - proof: &[DBValue], - root: H256, - transaction: &SignedTransaction, - machine: &Machine, - env_info: &EnvInfo, -) -> ProvedExecution { - let backend = self::backend::ProofCheck::new(proof); - let mut factories = Factories::default(); - factories.accountdb = ::account_db::Factory::Plain; - - let res = State::from_existing( - backend, - root, - machine.account_start_nonce(env_info.number), - factories - ); - - let mut state = match res { - Ok(state) => state, - Err(_) => return ProvedExecution::BadProof, - }; - - let options = TransactOptions::with_no_tracing().save_output_from_contract(); - match state.execute(env_info, machine, transaction, options, true) { - Ok(executed) => ProvedExecution::Complete(Box::new(executed)), - Err(ExecutionError::Internal(_)) => ProvedExecution::BadProof, - Err(e) => ProvedExecution::Failed(e), - } -} - -/// Prove a `virtual` transaction on the given state. -/// Returns `None` when the transacion could not be proved, -/// and a proof otherwise. -pub fn prove_transaction_virtual + Send + Sync>( - db: H, - root: H256, - transaction: &SignedTransaction, - machine: &Machine, - env_info: &EnvInfo, - factories: Factories, -) -> Option<(Bytes, Vec)> { - use state_account::backend::Proving; - - let backend = Proving::new(db); - let res = State::from_existing( - backend, - root, - machine.account_start_nonce(env_info.number), - factories, - ); - - let mut state = match res { - Ok(state) => state, - Err(_) => return None, - }; - - let options = TransactOptions::with_no_tracing().dont_check_nonce().save_output_from_contract(); - match state.execute(env_info, machine, transaction, options, true) { - Err(ExecutionError::Internal(_)) => None, - Err(e) => { - trace!(target: "state", "Proved call failed: {}", e); - Some((Vec::new(), state.drop().1.extract_proof())) - } - Ok(res) => Some((res.output, state.drop().1.extract_proof())), - } -} - /// Representation of the entire state of all accounts in the system. /// /// `State` can work together with `StateDB` to share account cache. @@ -797,79 +721,6 @@ impl State { Ok(()) } - /// Execute a given transaction, producing a receipt and an optional trace. - /// This will change the state accordingly. - pub fn apply(&mut self, env_info: &EnvInfo, machine: &Machine, t: &SignedTransaction, tracing: bool) -> ApplyResult { - if tracing { - let options = TransactOptions::with_tracing(); - self.apply_with_tracing(env_info, machine, t, options.tracer, options.vm_tracer) - } else { - let options = TransactOptions::with_no_tracing(); - self.apply_with_tracing(env_info, machine, t, options.tracer, options.vm_tracer) - } - } - - /// Execute a given transaction with given tracer and VM tracer producing a receipt and an optional trace. - /// This will change the state accordingly. - pub fn apply_with_tracing( - &mut self, - env_info: &EnvInfo, - machine: &Machine, - t: &SignedTransaction, - tracer: T, - vm_tracer: V, - ) -> ApplyResult where - T: trace::Tracer, - V: trace::VMTracer, - { - let options = TransactOptions::new(tracer, vm_tracer); - let e = self.execute(env_info, machine, t, options, false)?; - let params = machine.params(); - - let eip658 = env_info.number >= params.eip658_transition; - let no_intermediate_commits = - eip658 || - (env_info.number >= params.eip98_transition && env_info.number >= params.validate_receipts_transition); - - let outcome = if no_intermediate_commits { - if eip658 { - TransactionOutcome::StatusCode(if e.exception.is_some() { 0 } else { 1 }) - } else { - TransactionOutcome::Unknown - } - } else { - self.commit()?; - TransactionOutcome::StateRoot(self.root().clone()) - }; - - let output = e.output; - let receipt = Receipt::new(outcome, e.cumulative_gas_used, e.logs); - trace!(target: "state", "Transaction receipt: {:?}", receipt); - - Ok(ApplyOutcome { - receipt, - output, - trace: e.trace, - vm_trace: e.vm_trace, - }) - } - - // Execute a given transaction without committing changes. - // - // `virt` signals that we are executing outside of a block set and restrictions like - // gas limits and gas costs should be lifted. - fn execute(&mut self, env_info: &EnvInfo, machine: &Machine, t: &SignedTransaction, options: TransactOptions, virt: bool) - -> Result, ExecutionError> where T: trace::Tracer, V: trace::VMTracer, - { - let schedule = machine.schedule(env_info.number); - let mut e = Executive::new(self, env_info, machine, &schedule); - - match virt { - true => e.transact_virtual(t, options), - false => e.transact(t, options), - } - } - fn touch(&mut self, a: &Address) -> TrieResult<()> { self.require(a, false)?; Ok(()) @@ -1348,6 +1199,7 @@ mod tests { use trace::{FlatTrace, TraceError, trace}; use evm::CallType; use pod; + use executive_state::ExecutiveStateWithMachineZomgBetterName; fn secret() -> Secret { keccak("").into() diff --git a/ethcore/state-account/src/state.rs b/ethcore/state-account/src/state.rs index a6a43c67bf6..b638703c247 100644 --- a/ethcore/state-account/src/state.rs +++ b/ethcore/state-account/src/state.rs @@ -807,7 +807,6 @@ impl State { Ok(()) } - /// Initialise the code of account `a` so that it is `code`. /// NOTE: Account should have been created with `new_contract`. pub fn init_code(&mut self, a: &Address, code: Bytes) -> TrieResult<()> { From fefd892da4e075d0b7dcb27920cdfd75a8154dae Mon Sep 17 00:00:00 2001 From: David Palm Date: Fri, 5 Jul 2019 15:17:23 +0200 Subject: [PATCH 30/87] Sort out cloning errors, fix ethcore to use new state crate --- ethcore/src/block.rs | 2 +- ethcore/src/client/client.rs | 4 +- ethcore/src/client/mod.rs | 2 +- ethcore/src/client/test_client.rs | 2 +- ethcore/src/client/traits.rs | 26 ++- .../engines/validator_set/safe_contract.rs | 6 +- ethcore/src/error.rs | 5 + ethcore/src/ethereum/mod.rs | 2 +- ethcore/src/executive.rs | 4 +- ethcore/src/executive_state.rs | 21 +- ethcore/src/externalities.rs | 4 +- ethcore/src/json_tests/executive.rs | 2 +- ethcore/src/lib.rs | 5 +- ethcore/src/machine.rs | 2 +- ethcore/src/miner/miner.rs | 2 +- ethcore/src/miner/mod.rs | 2 +- ethcore/src/snapshot/mod.rs | 2 +- ethcore/src/snapshot/tests/helpers.rs | 2 +- ethcore/src/spec/spec.rs | 4 +- ethcore/src/state_db.rs | 13 +- ethcore/src/test_helpers.rs | 2 +- ethcore/src/tests/client.rs | 2 +- ethcore/src/tests/evm.rs | 2 +- ethcore/state-account/src/lib.rs | 30 ++- ethcore/state-account/src/state.rs | 219 ++++++++---------- ethcore/state-account/src/substate.rs | 18 +- 26 files changed, 203 insertions(+), 182 deletions(-) diff --git a/ethcore/src/block.rs b/ethcore/src/block.rs index 12afd98d195..e0192e8dc67 100644 --- a/ethcore/src/block.rs +++ b/ethcore/src/block.rs @@ -42,7 +42,7 @@ use engines::Engine; use error::{Error, BlockError}; use factories::Factories; use state_db::StateDB; -use state::State; +use state_account::State; use trace::Tracing; use triehash::ordered_trie_root; use unexpected::{Mismatch, OutOfBounds}; diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index 37ee894236d..99e039fdda1 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -70,7 +70,7 @@ use factories::{Factories, VmFactory}; use miner::{Miner, MinerService}; use snapshot::{self, io as snapshot_io, SnapshotClient}; use spec::Spec; -use state::State; +use state_account::State; use executive_state; use state_db::StateDB; use trace::{self, TraceDB, ImportRequest as TraceImportRequest, LocalizedTrace, Database as TraceDatabase}; @@ -623,7 +623,7 @@ impl Importer { let call = move |addr, data| { let mut state_db = state_db.boxed_clone(); - let backend = ::state::backend::Proving::new(state_db.as_hash_db_mut()); + let backend = state_account::backend::Proving::new(state_db.as_hash_db_mut()); let transaction = client.contract_call_tx(BlockId::Hash(*header.parent_hash()), addr, data); diff --git a/ethcore/src/client/mod.rs b/ethcore/src/client/mod.rs index 972daad1eb4..7efd96c73be 100644 --- a/ethcore/src/client/mod.rs +++ b/ethcore/src/client/mod.rs @@ -39,7 +39,7 @@ pub use self::traits::{ StateOrBlock, StateClient, Call, EngineInfo, AccountData, BlockChain, BlockProducer, SealedBlockImporter, BadBlocks, BlockChainReset }; -pub use state::StateInfo; +pub use state_account::state::StateInfo; pub use self::traits::{BlockChainClient, EngineClient, ProvingBlockChainClient, IoClient}; pub use types::ids::*; diff --git a/ethcore/src/client/test_client.rs b/ethcore/src/client/test_client.rs index 7894a665de3..d5f5c736ea2 100644 --- a/ethcore/src/client/test_client.rs +++ b/ethcore/src/client/test_client.rs @@ -66,7 +66,7 @@ use executive::Executed; use journaldb; use miner::{self, Miner, MinerService}; use spec::Spec; -use state::StateInfo; +use state_account::state::StateInfo; use state_db::StateDB; use trace::LocalizedTrace; use verification::queue::QueueInfo as BlockQueueInfo; diff --git a/ethcore/src/client/traits.rs b/ethcore/src/client/traits.rs index ff4b778f4a8..7d4d6fcc6e6 100644 --- a/ethcore/src/client/traits.rs +++ b/ethcore/src/client/traits.rs @@ -47,7 +47,7 @@ use engines::Engine; use error::{Error, EthcoreResult}; use executed::CallError; use executive::Executed; -use state::StateInfo; +use state_account::state::StateInfo; use trace::LocalizedTrace; use verification::queue::QueueInfo as BlockQueueInfo; use verification::queue::kind::blocks::Unverified; @@ -67,17 +67,19 @@ impl From for StateOrBlock { } } -impl From> for StateOrBlock { - fn from(info: Box) -> StateOrBlock { - StateOrBlock::State(info) - } -} +// TODO: sort out why these imps conflict +//impl From> for StateOrBlock { +// fn from(info: Box) -> StateOrBlock { +// StateOrBlock::State(info) +// } +//} -impl From for StateOrBlock { - fn from(id: BlockId) -> StateOrBlock { - StateOrBlock::Block(id) - } -} +// TODO: sort out why these imps conflict +//impl From for StateOrBlock { +// fn from(id: BlockId) -> StateOrBlock { +// StateOrBlock::Block(id) +// } +//} /// Provides `nonce` and `latest_nonce` methods pub trait Nonce { @@ -195,7 +197,7 @@ pub trait IoClient: Sync + Send { /// Queue block import with transaction receipts. Does no sealing and transaction validation. fn queue_ancient_block(&self, block_bytes: Unverified, receipts_bytes: Bytes) -> EthcoreResult; - /// Queue conensus engine message. + /// Queue consensus engine message. fn queue_consensus_message(&self, message: Bytes); } diff --git a/ethcore/src/engines/validator_set/safe_contract.rs b/ethcore/src/engines/validator_set/safe_contract.rs index b524f2ce70c..50ee598dc73 100644 --- a/ethcore/src/engines/validator_set/safe_contract.rs +++ b/ethcore/src/engines/validator_set/safe_contract.rs @@ -138,9 +138,9 @@ fn check_first_proof(machine: &Machine, contract_address: Address, old_header: H ); match res { - ::state::ProvedExecution::BadProof => Err("Bad proof".into()), - ::state::ProvedExecution::Failed(e) => Err(format!("Failed call: {}", e)), - ::state::ProvedExecution::Complete(e) => decoder.decode(&e.output).map_err(|e| e.to_string()), + ::executive_state::ProvedExecution::BadProof => Err("Bad proof".into()), + ::executive_state::ProvedExecution::Failed(e) => Err(format!("Failed call: {}", e)), + ::executive_state::ProvedExecution::Complete(e) => decoder.decode(&e.output).map_err(|e| e.to_string()), } } diff --git a/ethcore/src/error.rs b/ethcore/src/error.rs index 477f67b26b1..f7308afcb64 100644 --- a/ethcore/src/error.rs +++ b/ethcore/src/error.rs @@ -261,6 +261,10 @@ pub enum Error { /// A convenient variant for String. #[display(fmt = "{}", _0)] Msg(String), + /// State errors + // TODO: fix error handling + #[display(fmt = "state error todo fixme ({})", _0)] + State(state_account::Error), } impl error::Error for Error { @@ -277,6 +281,7 @@ impl error::Error for Error { Error::Ethkey(e) => Some(e), Error::Decoder(e) => Some(e), Error::Snapshot(e) => Some(e), + Error::State(e) => Some(e), _ => None, } } diff --git a/ethcore/src/ethereum/mod.rs b/ethcore/src/ethereum/mod.rs index 01d1ccf80b2..f0c1296992e 100644 --- a/ethcore/src/ethereum/mod.rs +++ b/ethcore/src/ethereum/mod.rs @@ -189,7 +189,7 @@ pub fn new_kovan_wasm_test_machine() -> Machine { load_machine(include_bytes!(". mod tests { use std::str::FromStr; use ethereum_types::{U256, H256, Address}; - use state::*; + use state_account::*; use super::*; use test_helpers::get_temp_state_db; use types::view; diff --git a/ethcore/src/executive.rs b/ethcore/src/executive.rs index 8202bcc9b15..e127370fee8 100644 --- a/ethcore/src/executive.rs +++ b/ethcore/src/executive.rs @@ -21,7 +21,7 @@ use std::sync::Arc; use hash::keccak; use ethereum_types::{H256, U256, U512, Address}; use bytes::{Bytes, BytesRef}; -use state::{Backend as StateBackend, State, Substate, CleanupMode}; +use state_account::{Backend as StateBackend, State, Substate, CleanupMode}; use executed::ExecutionError; use machine::Machine; use evm::{CallType, Finalize, FinalizationResult}; @@ -1180,7 +1180,7 @@ mod tests { use evm::{Factory, VMType}; use error::ExecutionError; use machine::Machine; - use state::{Substate, CleanupMode}; + use state_account::{Substate, CleanupMode}; use test_helpers::{get_temp_state_with_factory, get_temp_state}; use trace::trace; use trace::{FlatTrace, Tracer, NoopTracer, ExecutiveTracer}; diff --git a/ethcore/src/executive_state.rs b/ethcore/src/executive_state.rs index 54629227fc6..40ea082506a 100644 --- a/ethcore/src/executive_state.rs +++ b/ethcore/src/executive_state.rs @@ -23,8 +23,10 @@ use types::{ receipt::{TransactionOutcome, Receipt}, }; use trace::{FlatTrace, VMTrace}; -use state::{ApplyResult, ApplyOutcome, State, ProvedExecution}; -use state_account::backend::{self, Backend}; +use state_account::{ + backend::{self, Backend}, + state::{ApplyResult, ApplyOutcome, State}, +}; use ethereum_types::H256; use factories::Factories; use bytes::Bytes; @@ -32,6 +34,18 @@ use keccak_hasher::KeccakHasher; use kvdb::DBValue; use hash_db::AsHashDB; +// TODO: is there a better place for this? +/// Return type of proof validity check. +#[derive(Debug, Clone)] +pub enum ProvedExecution { + /// Proof wasn't enough to complete execution. + BadProof, + /// The transaction failed, but not due to a bad proof. + Failed(ExecutionError), + /// The transaction successfully completed with the given proof. + Complete(Box), +} + /// Check the given proof of execution. /// `Err(ExecutionError::Internal)` indicates failure, everything else indicates /// a successful proof (as the transaction itself may be poorly chosen). @@ -173,7 +187,8 @@ impl ExecutiveStateWithMachineZomgBetterName for State { V: trace::VMTracer, { let options = TransactOptions::new(tracer, vm_tracer); - let e = self.execute(env_info, machine, t, options, false)?; + // TODO: fix error handling + let e = self.execute(env_info, machine, t, options, false).expect("TODO FIXME"); let params = machine.params(); let eip658 = env_info.number >= params.eip658_transition; diff --git a/ethcore/src/externalities.rs b/ethcore/src/externalities.rs index d5ffb5dbcc3..6c37bcb9208 100644 --- a/ethcore/src/externalities.rs +++ b/ethcore/src/externalities.rs @@ -19,7 +19,7 @@ use std::cmp; use std::sync::Arc; use ethereum_types::{H256, U256, Address, BigEndianHash}; use bytes::Bytes; -use state::{Backend as StateBackend, State, Substate, CleanupMode}; +use state_account::{Backend as StateBackend, State, Substate, CleanupMode}; use machine::Machine; use executive::*; use vm::{ @@ -429,7 +429,7 @@ impl<'a, T: 'a, V: 'a, B: 'a> Ext for Externalities<'a, T, V, B> mod tests { use ethereum_types::{U256, Address}; use evm::{EnvInfo, Ext, CallType}; - use state::{State, Substate}; + use state_account::{State, Substate}; use test_helpers::get_temp_state; use super::*; use trace::{NoopTracer, NoopVMTracer}; diff --git a/ethcore/src/json_tests/executive.rs b/ethcore/src/json_tests/executive.rs index 3c2792740b6..7c0eee591f7 100644 --- a/ethcore/src/json_tests/executive.rs +++ b/ethcore/src/json_tests/executive.rs @@ -17,7 +17,7 @@ use std::path::Path; use std::sync::Arc; use super::test_common::*; -use state::{Backend as StateBackend, State, Substate}; +use state_account::{Backend as StateBackend, State, Substate}; use executive::*; use evm::{VMType, Finalize}; use vm::{ diff --git a/ethcore/src/lib.rs b/ethcore/src/lib.rs index a1ae6cc336a..60e5f6d9e9f 100644 --- a/ethcore/src/lib.rs +++ b/ethcore/src/lib.rs @@ -160,11 +160,12 @@ pub mod executed; pub mod executive; pub mod machine; pub mod miner; -// TODO: +// TODO: need to be pub? //pub mod pod_state; pub mod snapshot; pub mod spec; -pub mod state; +// TODO: need to be pub? +//pub mod state; pub mod state_db; pub mod transaction_ext; pub mod verification; diff --git a/ethcore/src/machine.rs b/ethcore/src/machine.rs index 95ca11012e2..e1131a9badf 100644 --- a/ethcore/src/machine.rs +++ b/ethcore/src/machine.rs @@ -35,7 +35,7 @@ use client::BlockInfo; use error::Error; use executive::Executive; use spec::CommonParams; // TODO: move to common_types -use state::{CleanupMode, Substate}; +use state_account::{CleanupMode, Substate}; use trace::{NoopTracer, NoopVMTracer}; use tx_filter::TransactionFilter; diff --git a/ethcore/src/miner/miner.rs b/ethcore/src/miner/miner.rs index eed0b1595ad..3ad4afb7292 100644 --- a/ethcore/src/miner/miner.rs +++ b/ethcore/src/miner/miner.rs @@ -59,7 +59,7 @@ use error::Error; use executed::ExecutionError; use executive::contract_address; use spec::Spec; -use state::State; +use state_account::State; /// Different possible definitions for pending transaction set. #[derive(Debug, PartialEq)] diff --git a/ethcore/src/miner/mod.rs b/ethcore/src/miner/mod.rs index c6397fccc12..2085d4d2554 100644 --- a/ethcore/src/miner/mod.rs +++ b/ethcore/src/miner/mod.rs @@ -50,7 +50,7 @@ use client::{ AccountData, Nonce, }; use error::Error; -use state::StateInfo; +use state_account::state::StateInfo; /// Provides methods to verify incoming external transactions pub trait TransactionVerifierClient: Send + Sync diff --git a/ethcore/src/snapshot/mod.rs b/ethcore/src/snapshot/mod.rs index d5ce8c2b179..304d4b623c1 100644 --- a/ethcore/src/snapshot/mod.rs +++ b/ethcore/src/snapshot/mod.rs @@ -48,7 +48,7 @@ use num_cpus; use self::io::SnapshotWriter; use super::state_db::StateDB; -use super::state::Account as StateAccount; +use state_account::Account as StateAccount; use crossbeam::scope; use rand::{Rng, rngs::OsRng}; diff --git a/ethcore/src/snapshot/tests/helpers.rs b/ethcore/src/snapshot/tests/helpers.rs index a6e516b1b1a..4d04470487b 100644 --- a/ethcore/src/snapshot/tests/helpers.rs +++ b/ethcore/src/snapshot/tests/helpers.rs @@ -97,7 +97,7 @@ impl StateProducer { let address_hash = H256(rng.gen()); let balance: usize = rng.gen(); let nonce: usize = rng.gen(); - let acc = ::state::Account::new_basic(balance.into(), nonce.into()).rlp(); + let acc = state_account::Account::new_basic(balance.into(), nonce.into()).rlp(); trie.insert(&address_hash[..], &acc).unwrap(); } } diff --git a/ethcore/src/spec/spec.rs b/ethcore/src/spec/spec.rs index a5048ffb570..9ac31551293 100644 --- a/ethcore/src/spec/spec.rs +++ b/ethcore/src/spec/spec.rs @@ -45,7 +45,7 @@ use machine::Machine; use pod::PodState; use spec::Genesis; use spec::seal::Generic as GenericSeal; -use state::{Backend, State, Substate, backend::Basic as BasicBackend}; +use state_account::{Backend, State, Substate, backend::Basic as BasicBackend}; use trace::{NoopTracer, NoopVMTracer}; pub use ethash::OptimizeFor; @@ -990,7 +990,7 @@ impl Spec { #[cfg(test)] mod tests { use super::*; - use state::State; + use state_account::State; use test_helpers::get_temp_state_db; use tempdir::TempDir; use types::view; diff --git a/ethcore/src/state_db.rs b/ethcore/src/state_db.rs index bde87cb5282..825f83d1b89 100644 --- a/ethcore/src/state_db.rs +++ b/ethcore/src/state_db.rs @@ -33,7 +33,7 @@ use memory_cache::MemoryLruCache; use parking_lot::Mutex; use types::BlockNumber; -use state::{self, Account}; +use state_account::{self, Account, State}; /// Value used to initialize bloom bitmap size. /// @@ -125,8 +125,13 @@ pub struct StateDB { commit_number: Option, } -impl StateDB { +impl Clone for StateDB { + fn clone(&self) -> Self { + self.boxed_clone() + } +} +impl StateDB { /// Create a new instance wrapping `JournalDB` and the maximum allowed size /// of the LRU cache in bytes. Actual used memory may (read: will) be higher due to bookkeeping. // TODO: make the cache size actually accurate by moving the account storage cache @@ -407,7 +412,7 @@ impl StateDB { } } -impl state::Backend for StateDB { +impl state_account::Backend for StateDB { fn as_hash_db(&self) -> &dyn HashDB { self.db.as_hash_db() } fn as_hash_db_mut(&mut self) -> &mut dyn HashDB { @@ -482,7 +487,7 @@ mod tests { use ethereum_types::{H256, U256, Address}; use kvdb::DBTransaction; use test_helpers::get_temp_state_db; - use state::{Account, Backend}; + use state_account::{Account, Backend}; #[test] fn state_db_smoke() { diff --git a/ethcore/src/test_helpers.rs b/ethcore/src/test_helpers.rs index f365f61cf53..9a167788405 100644 --- a/ethcore/src/test_helpers.rs +++ b/ethcore/src/test_helpers.rs @@ -44,7 +44,7 @@ use client::{Client, ClientConfig, ChainInfo, ImportBlock, ChainNotify, ChainMes use factories::Factories; use miner::Miner; use spec::Spec; -use state::*; +use state_account::*; use state_db::StateDB; use verification::queue::kind::blocks::Unverified; diff --git a/ethcore/src/tests/client.rs b/ethcore/src/tests/client.rs index 63f0ea60f66..6845b540ab8 100644 --- a/ethcore/src/tests/client.rs +++ b/ethcore/src/tests/client.rs @@ -32,7 +32,7 @@ use ethereum; use executive::{Executive, TransactOptions}; use miner::{Miner, PendingOrdering, MinerService}; use spec::Spec; -use state::{self, State, CleanupMode}; +use state_account::{self, State, CleanupMode}; use test_helpers::{ self, generate_dummy_client, push_blocks_to_client, get_test_client_with_blocks, get_good_dummy_block_seq, diff --git a/ethcore/src/tests/evm.rs b/ethcore/src/tests/evm.rs index de1c14b2737..ba314c54144 100644 --- a/ethcore/src/tests/evm.rs +++ b/ethcore/src/tests/evm.rs @@ -21,7 +21,7 @@ use hash::keccak; use vm::{EnvInfo, ActionParams, ActionValue, CallType, ParamsType}; use evm::{Factory, VMType}; use executive::Executive; -use state::Substate; +use state_account::Substate; use test_helpers::get_temp_state_with_factory; use trace::{NoopVMTracer, NoopTracer}; use types::transaction::SYSTEM_ADDRESS; diff --git a/ethcore/state-account/src/lib.rs b/ethcore/state-account/src/lib.rs index 7f5994bef01..7c1027860d3 100644 --- a/ethcore/state-account/src/lib.rs +++ b/ethcore/state-account/src/lib.rs @@ -18,5 +18,33 @@ pub mod account; pub mod backend; pub mod substate; // TODO: WIP -//pub mod state; +pub mod state; +pub use { + account::Account, + backend::Backend, + substate::Substate, + state::{State, CleanupMode}, +}; + +// TODO: move to an errors.rs +use derive_more::{Display, From}; +// TODO: sort out error handling +#[derive(Debug, Display, From)] +pub enum Error { + /// Trie error. + Trie(ethtrie::TrieError), + /// Decoder error. + Decoder(rlp::DecoderError), + // Io error. +// Io(IoError), // TODO: maybe not needed? +} + +// TODO: needed? +impl std::error::Error for Error {} + +impl From> for Error where Error: From { + fn from(err: Box) -> Self { + Error::from(*err) + } +} diff --git a/ethcore/state-account/src/state.rs b/ethcore/state-account/src/state.rs index b638703c247..c5de958e8fd 100644 --- a/ethcore/state-account/src/state.rs +++ b/ethcore/state-account/src/state.rs @@ -23,34 +23,29 @@ use std::{ cell::{RefCell, RefMut}, collections::{BTreeMap, BTreeSet, HashMap, HashSet}, collections::hash_map::Entry, - fmt, sync::Arc, - error, io::Error as IoError, + fmt, }; +use common_types::{ + receipt::Receipt, + state_diff::StateDiff, + basic_account::BasicAccount, +}; use ethereum_types::{Address, H256, U256}; -use ethtrie::TrieError; +use ethtrie::{TrieDB, TrieError, Result as TrieResult}; use factories::{Factories, VmFactory}; -use hash_db::{AsHashDB, HashDB}; +use hash_db::HashDB; use keccak_hash::{KECCAK_EMPTY, KECCAK_NULL_RLP}; +use keccak_hasher::KeccakHasher; use kvdb::DBValue; use log::{warn, trace}; use parity_bytes::Bytes; use pod::{self, PodAccount, PodState}; -use trie_db::{Recorder, Trie}; -use common_types::{ - basic_account::BasicAccount, - receipt::{Receipt, TransactionOutcome}, - state_diff::StateDiff, - transaction::SignedTransaction -}; -use ethtrie::{Result as TrieResult, TrieDB}; -use keccak_hasher::KeccakHasher; -use rlp::DecoderError; -use vm::EnvInfo; -use derive_more::{Display, From}; +use trie_db::{Trie, Recorder}; use crate::{ + Error, account::Account, backend::Backend, }; @@ -67,41 +62,9 @@ pub struct ApplyOutcome { pub vm_trace: Option } -// TODO: sort out error handling -#[derive(Debug, Display, From)] -pub enum Error { - /// Trie error. - Trie(TrieError), - /// Decoder error. - Decoder(DecoderError), - /// Io error. - Io(IoError), // TODO: maybe not needed? -} - -// TODO: needed? -impl error::Error for Error {} - -impl From> for Error where Error: From { - fn from(err: Box) -> Self { - Error::from(*err) - } -} - /// Result type for the execution ("application") of a transaction. pub type ApplyResult = Result, Error>; -// TODO: needs ExecutionError and Executed -///// Return type of proof validity check. -//#[derive(Debug, Clone)] -//pub enum ProvedExecution { -// /// Proof wasn't enough to complete execution. -// BadProof, -// /// The transaction failed, but not due to a bad proof. -// Failed(ExecutionError), -// /// The transaction successfully completed with the given proof. -// Complete(Box), -//} - #[derive(Eq, PartialEq, Clone, Copy, Debug)] /// Account modification state. Used to check if the account was /// Modified in between commits and overall. @@ -206,79 +169,6 @@ impl AccountEntry { } } -// TODO: needs Machine -///// Check the given proof of execution. -///// `Err(ExecutionError::Internal)` indicates failure, everything else indicates -///// a successful proof (as the transaction itself may be poorly chosen). -//pub fn check_proof( -// proof: &[DBValue], -// root: H256, -// transaction: &SignedTransaction, -// machine: &Machine, -// env_info: &EnvInfo, -//) -> ProvedExecution { -// let backend = self::backend::ProofCheck::new(proof); -// let mut factories = Factories::default(); -// factories.accountdb = ::account_db::Factory::Plain; -// -// let res = State::from_existing( -// backend, -// root, -// machine.account_start_nonce(env_info.number), -// factories -// ); -// -// let mut state = match res { -// Ok(state) => state, -// Err(_) => return ProvedExecution::BadProof, -// }; -// -// let options = TransactOptions::with_no_tracing().save_output_from_contract(); -// match state.execute(env_info, machine, transaction, options, true) { -// Ok(executed) => ProvedExecution::Complete(Box::new(executed)), -// Err(ExecutionError::Internal(_)) => ProvedExecution::BadProof, -// Err(e) => ProvedExecution::Failed(e), -// } -//} - -// TODO: needs Machine -///// Prove a `virtual` transaction on the given state. -///// Returns `None` when the transacion could not be proved, -///// and a proof otherwise. -//pub fn prove_transaction_virtual + Send + Sync>( -// db: H, -// root: H256, -// transaction: &SignedTransaction, -// machine: &Machine, -// env_info: &EnvInfo, -// factories: Factories, -//) -> Option<(Bytes, Vec)> { -// use state_account::backend::Proving; -// -// let backend = Proving::new(db); -// let res = State::from_existing( -// backend, -// root, -// machine.account_start_nonce(env_info.number), -// factories, -// ); -// -// let mut state = match res { -// Ok(state) => state, -// Err(_) => return None, -// }; -// -// let options = TransactOptions::with_no_tracing().dont_check_nonce().save_output_from_contract(); -// match state.execute(env_info, machine, transaction, options, true) { -// Err(ExecutionError::Internal(_)) => None, -// Err(e) => { -// trace!(target: "state", "Proved call failed: {}", e); -// Some((Vec::new(), state.drop().1.extract_proof())) -// } -// Ok(res) => Some((res.output, state.drop().1.extract_proof())), -// } -//} - /// Representation of the entire state of all accounts in the system. /// /// `State` can work together with `StateDB` to share account cache. @@ -1272,4 +1162,91 @@ impl State { } } +// State proof implementations; useful for light client protocols. +impl State { + /// Prove an account's existence or nonexistence in the state trie. + /// Returns a merkle proof of the account's trie node omitted or an encountered trie error. + /// If the account doesn't exist in the trie, prove that and return defaults. + /// Requires a secure trie to be used for accurate results. + /// `account_key` == keccak(address) + pub fn prove_account(&self, account_key: H256) -> TrieResult<(Vec, BasicAccount)> { + let mut recorder = Recorder::new(); + let db = &self.db.as_hash_db(); + let trie = TrieDB::new(db, &self.root)?; + let maybe_account: Option = { + let panicky_decoder = |bytes: &[u8]| { + ::rlp::decode(bytes).unwrap_or_else(|_| panic!("prove_account, could not query trie for account key={}", &account_key)) + }; + let query = (&mut recorder, panicky_decoder); + trie.get_with(account_key.as_bytes(), query)? + }; + let account = maybe_account.unwrap_or_else(|| BasicAccount { + balance: 0.into(), + nonce: self.account_start_nonce, + code_hash: KECCAK_EMPTY, + storage_root: KECCAK_NULL_RLP, + }); + + Ok((recorder.drain().into_iter().map(|r| r.data).collect(), account)) + } + + /// Prove an account's storage key's existence or nonexistence in the state. + /// Returns a merkle proof of the account's storage trie. + /// Requires a secure trie to be used for correctness. + /// `account_key` == keccak(address) + /// `storage_key` == keccak(key) + pub fn prove_storage(&self, account_key: H256, storage_key: H256) -> TrieResult<(Vec, H256)> { + // TODO: probably could look into cache somehow but it's keyed by + // address, not keccak(address). + let db = &self.db.as_hash_db(); + let trie = TrieDB::new(db, &self.root)?; + let from_rlp = |b: &[u8]| Account::from_rlp(b).expect("decoding db value failed"); + let acc = match trie.get_with(account_key.as_bytes(), from_rlp)? { + Some(acc) => acc, + None => return Ok((Vec::new(), H256::zero())), + }; + + let account_db = self.factories.accountdb.readonly(self.db.as_hash_db(), account_key); + acc.prove_storage(account_db.as_hash_db(), storage_key) + } +} + +impl fmt::Debug for State { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{:?}", self.cache.borrow()) + } +} + +impl State { + /// Get a reference to the underlying state DB. + pub fn db(&self) -> &B { + &self.db + } +} + +//// TODO: cloning for `State` shouldn't be possible in general; Remove this and use +//// checkpoints where possible. +impl Clone for State { + fn clone(&self) -> State { + let cache = { + let mut cache: HashMap = HashMap::new(); + for (key, val) in self.cache.borrow().iter() { + if let Some(entry) = val.clone_if_dirty() { + cache.insert(key.clone(), entry); + } + } + cache + }; + + State { + db: self.db.clone(), + root: self.root.clone(), + cache: RefCell::new(cache), + checkpoints: RefCell::new(Vec::new()), + account_start_nonce: self.account_start_nonce.clone(), + factories: self.factories.clone(), + } + } +} + // TODO tests and whatnot diff --git a/ethcore/state-account/src/substate.rs b/ethcore/state-account/src/substate.rs index 59a2ed1345b..252b27761fa 100644 --- a/ethcore/state-account/src/substate.rs +++ b/ethcore/state-account/src/substate.rs @@ -17,10 +17,12 @@ //! Execution environment substate. use std::collections::HashSet; -use ethereum_types::Address; use common_types::log_entry::LogEntry; +use ethereum_types::Address; use evm::{CleanDustMode, Schedule}; +use crate::state::CleanupMode; + /// State changes which should be applied in finalize, /// after transaction is fully executed. #[derive(Debug, Default)] @@ -66,20 +68,6 @@ impl Substate { } } -// TODO: belongs in `state.rs` -//use crate::state::CleanupMode; -/// Mode of dealing with null accounts. -#[derive(PartialEq)] -pub enum CleanupMode<'a> { - /// Create accounts which would be null. - ForceCreate, - /// Don't delete null accounts upon touching, but also don't create them. - NoEmpty, - /// Mark all touched accounts. - TrackTouched(&'a mut HashSet
), -} - - #[cfg(test)] mod tests { use ethereum_types::Address; From d41f5d41105f1e6c3e0550b62d5ea435cee9a105 Mon Sep 17 00:00:00 2001 From: David Palm Date: Fri, 5 Jul 2019 15:24:30 +0200 Subject: [PATCH 31/87] Do without funky From impls --- ethcore/src/client/traits.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ethcore/src/client/traits.rs b/ethcore/src/client/traits.rs index 7d4d6fcc6e6..7123d165995 100644 --- a/ethcore/src/client/traits.rs +++ b/ethcore/src/client/traits.rs @@ -105,7 +105,7 @@ pub trait Balance { /// Get address balance at the latest block's state. fn latest_balance(&self, address: &Address) -> U256 { - self.balance(address, BlockId::Latest.into()) + self.balance(address, StateOrBlock::Block(BlockId::Latest)) .expect("balance will return Some if given BlockId::Latest. balance was given BlockId::Latest \ Therefore balance has returned Some; qed") } @@ -235,7 +235,7 @@ pub trait BlockChainClient : Sync + Send + AccountData + BlockChain + CallContra /// Get address code at the latest block's state. fn latest_code(&self, address: &Address) -> Option { - self.code(address, BlockId::Latest.into()) + self.code(address, StateOrBlock::Block(BlockId::Latest)) .expect("code will return Some if given BlockId::Latest; qed") } @@ -252,7 +252,7 @@ pub trait BlockChainClient : Sync + Send + AccountData + BlockChain + CallContra /// Get value of the storage at given position at the latest block's state. fn latest_storage_at(&self, address: &Address, position: &H256) -> H256 { - self.storage_at(address, position, BlockId::Latest.into()) + self.storage_at(address, position, StateOrBlock::Block(BlockId::Latest)) .expect("storage_at will return Some if given BlockId::Latest. storage_at was given BlockId::Latest. \ Therefore storage_at has returned Some; qed") } From ffec0b0d91427bdfa9947e1f7ae65afcdd28982c Mon Sep 17 00:00:00 2001 From: David Palm Date: Fri, 5 Jul 2019 15:43:05 +0200 Subject: [PATCH 32/87] Fix ethcore tests --- ethcore/src/client/evm_test_client.rs | 33 ++++++++++++--------------- ethcore/src/client/test_client.rs | 14 +++++++----- ethcore/src/state_db.rs | 2 +- ethcore/src/tests/client.rs | 4 ++-- 4 files changed, 26 insertions(+), 27 deletions(-) diff --git a/ethcore/src/client/evm_test_client.rs b/ethcore/src/client/evm_test_client.rs index a92b2fcd492..fc7f074398c 100644 --- a/ethcore/src/client/evm_test_client.rs +++ b/ethcore/src/client/evm_test_client.rs @@ -21,13 +21,14 @@ use std::sync::Arc; use ethereum_types::{H256, U256, H160}; use {factories, journaldb, trie, kvdb_memorydb}; use kvdb::{self, KeyValueDB}; -use {state, state_db, client, executive, trace, db, spec}; +use {state_db, client, executive, trace, db, spec}; use pod::PodState; use types::{log_entry, receipt, transaction}; use factories::Factories; use evm::{VMType, FinalizationResult}; use vm::{self, ActionParams}; use ethtrie; +use state_account::{CleanupMode, Substate, State}; use executive_state::ExecutiveStateWithMachineZomgBetterName; @@ -68,12 +69,12 @@ use ethjson::spec::ForkSpec; /// Simplified, single-block EVM test client. pub struct EvmTestClient<'a> { - state: state::State, + state: State, spec: &'a spec::Spec, - dump_state: fn(&state::State) -> Option, + dump_state: fn(&State) -> Option, } -fn no_dump_state(_: &state::State) -> Option { +fn no_dump_state(_: &State) -> Option { None } @@ -103,7 +104,7 @@ impl<'a> EvmTestClient<'a> { } /// Change default function for dump state (default does not dump) - pub fn set_dump_state_fn(&mut self, dump_state: fn(&state::State) -> Option) { + pub fn set_dump_state_fn(&mut self, dump_state: fn(&State) -> Option) { self.dump_state = dump_state; } @@ -151,7 +152,7 @@ impl<'a> EvmTestClient<'a> { } } - fn state_from_spec(spec: &'a spec::Spec, factories: &Factories) -> Result, EvmTestError> { + fn state_from_spec(spec: &'a spec::Spec, factories: &Factories) -> Result, EvmTestError> { let db = Arc::new(kvdb_memorydb::create(db::NUM_COLUMNS.expect("We use column-based DB; qed"))); let journal_db = journaldb::new(db.clone(), journaldb::Algorithm::EarlyMerge, db::COL_STATE); let mut state_db = state_db::StateDB::new(journal_db, 5 * 1024 * 1024); @@ -165,7 +166,7 @@ impl<'a> EvmTestClient<'a> { db.write(batch)?; } - state::State::from_existing( + State::from_existing( state_db, *genesis.state_root(), spec.engine.account_start_nonce(0), @@ -173,11 +174,11 @@ impl<'a> EvmTestClient<'a> { ).map_err(EvmTestError::Trie) } - fn state_from_pod(spec: &'a spec::Spec, factories: &Factories, pod_state: PodState) -> Result, EvmTestError> { + fn state_from_pod(spec: &'a spec::Spec, factories: &Factories, pod_state: PodState) -> Result, EvmTestError> { let db = Arc::new(kvdb_memorydb::create(db::NUM_COLUMNS.expect("We use column-based DB; qed"))); let journal_db = journaldb::new(db.clone(), journaldb::Algorithm::EarlyMerge, db::COL_STATE); let state_db = state_db::StateDB::new(journal_db, 5 * 1024 * 1024); - let mut state = state::State::new( + let mut state = State::new( state_db, spec.engine.account_start_nonce(0), factories.clone(), @@ -188,7 +189,7 @@ impl<'a> EvmTestClient<'a> { } /// Return current state. - pub fn state(&self) -> &state::State { + pub fn state(&self) -> &State { &self.state } @@ -224,7 +225,7 @@ impl<'a> EvmTestClient<'a> { info: client::EnvInfo, ) -> Result { - let mut substate = state::Substate::new(); + let mut substate = Substate::new(); let machine = self.spec.engine.machine(); let schedule = machine.schedule(info.number); let mut executive = executive::Executive::new(&mut self.state, &info, &machine, &schedule); @@ -266,9 +267,9 @@ impl<'a> EvmTestClient<'a> { // Details: https://github.com/paritytech/parity-ethereum/issues/9431 let schedule = self.spec.engine.machine().schedule(env_info.number); self.state.add_balance(&env_info.author, &0.into(), if schedule.no_empty { - state::CleanupMode::NoEmpty + CleanupMode::NoEmpty } else { - state::CleanupMode::ForceCreate + CleanupMode::ForceCreate }).ok(); // Touching also means that we should remove the account if it's within eip161 // conditions. @@ -303,11 +304,7 @@ impl<'a> EvmTestClient<'a> { end_state, } )}, - Err(error) => Err(TransactErr { - state_root, - error, - end_state, - }), + Err(e) => Err(TransactErr {state_root, error: e.into(), end_state}), } } } diff --git a/ethcore/src/client/test_client.rs b/ethcore/src/client/test_client.rs index d5f5c736ea2..40cb552d8c5 100644 --- a/ethcore/src/client/test_client.rs +++ b/ethcore/src/client/test_client.rs @@ -469,7 +469,7 @@ impl Balance for TestBlockChainClient { } fn latest_balance(&self, address: &Address) -> U256 { - self.balance(address, BlockId::Latest.into()).unwrap() + self.balance(address, StateOrBlock::Block(BlockId::Latest)).unwrap() } } @@ -586,7 +586,7 @@ impl ImportBlock for TestBlockChainClient { impl Call for TestBlockChainClient { // State will not be used by test client anyway, since all methods that accept state are mocked - type State = (); + type State = TestState; fn call(&self, _t: &SignedTransaction, _analytics: CallAnalytics, _state: &mut Self::State, _header: &Header) -> Result { self.execution_result.read().clone().unwrap() @@ -605,23 +605,25 @@ impl Call for TestBlockChainClient { } } -impl StateInfo for () { +pub struct TestState(()); +impl StateInfo for TestState { fn nonce(&self, _address: &Address) -> ethtrie::Result { unimplemented!() } fn balance(&self, _address: &Address) -> ethtrie::Result { unimplemented!() } fn storage_at(&self, _address: &Address, _key: &H256) -> ethtrie::Result { unimplemented!() } fn code(&self, _address: &Address) -> ethtrie::Result>> { unimplemented!() } } + impl StateClient for TestBlockChainClient { // State will not be used by test client anyway, since all methods that accept state are mocked - type State = (); + type State = TestState; fn latest_state(&self) -> Self::State { - () + TestState(()) } fn state_at(&self, _id: BlockId) -> Option { - Some(()) + Some(TestState(())) } } diff --git a/ethcore/src/state_db.rs b/ethcore/src/state_db.rs index 825f83d1b89..07abe1221f7 100644 --- a/ethcore/src/state_db.rs +++ b/ethcore/src/state_db.rs @@ -33,7 +33,7 @@ use memory_cache::MemoryLruCache; use parking_lot::Mutex; use types::BlockNumber; -use state_account::{self, Account, State}; +use state_account::{self, Account}; /// Value used to initialize bloom bitmap size. /// diff --git a/ethcore/src/tests/client.rs b/ethcore/src/tests/client.rs index 6845b540ab8..bf0c0b52b94 100644 --- a/ethcore/src/tests/client.rs +++ b/ethcore/src/tests/client.rs @@ -32,7 +32,7 @@ use ethereum; use executive::{Executive, TransactOptions}; use miner::{Miner, PendingOrdering, MinerService}; use spec::Spec; -use state_account::{self, State, CleanupMode}; +use state_account::{State, CleanupMode, backend}; use test_helpers::{ self, generate_dummy_client, push_blocks_to_client, get_test_client_with_blocks, get_good_dummy_block_seq, @@ -347,7 +347,7 @@ fn transaction_proof() { }.fake_sign(address); let proof = client.prove_transaction(transaction.clone(), BlockId::Latest).unwrap().1; - let backend = state::backend::ProofCheck::new(&proof); + let backend = backend::ProofCheck::new(&proof); let mut factories = ::factories::Factories::default(); factories.accountdb = ::account_db::Factory::Plain; // raw state values, no mangled keys. From 849c73f9160add113b69845817b8712231bd7cc2 Mon Sep 17 00:00:00 2001 From: David Palm Date: Fri, 5 Jul 2019 16:02:22 +0200 Subject: [PATCH 33/87] Fixes around the project to use new state crate --- Cargo.lock | 4 ++++ ethcore/light/Cargo.toml | 1 + ethcore/light/src/lib.rs | 1 + ethcore/light/src/on_demand/request.rs | 5 +++-- ethcore/private-tx/Cargo.toml | 2 ++ ethcore/private-tx/src/lib.rs | 9 ++++++--- ethcore/src/lib.rs | 3 ++- rpc/Cargo.toml | 1 + rpc/src/lib.rs | 1 + rpc/src/v1/impls/parity.rs | 2 +- 10 files changed, 22 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1c87387f887..bd0a9d4523b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1091,6 +1091,7 @@ dependencies = [ "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", + "state-account 0.1.0", "stats 0.1.0", "tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "trie-db 0.12.4 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1236,8 +1237,10 @@ dependencies = [ "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", + "state-account 0.1.0", "time-utils 0.1.0", "tiny-keccak 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "trace 0.1.0", "transaction-pool 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "trie-db 0.12.4 (registry+https://github.com/rust-lang/crates.io-index)", "url 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2935,6 +2938,7 @@ dependencies = [ "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", + "state-account 0.1.0", "stats 0.1.0", "tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "tiny-keccak 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/ethcore/light/Cargo.toml b/ethcore/light/Cargo.toml index 0078f5d952f..c91b8b8709e 100644 --- a/ethcore/light/Cargo.toml +++ b/ethcore/light/Cargo.toml @@ -35,6 +35,7 @@ itertools = "0.5" bincode = "1.1" serde = "1.0" serde_derive = "1.0" +state-account = { path = "../state-account" } parking_lot = "0.7" stats = { path = "../../util/stats" } keccak-hash = "0.2.0" diff --git a/ethcore/light/src/lib.rs b/ethcore/light/src/lib.rs index d169c6925e2..f7b1e25a805 100644 --- a/ethcore/light/src/lib.rs +++ b/ethcore/light/src/lib.rs @@ -89,6 +89,7 @@ extern crate triehash_ethereum as triehash; extern crate kvdb; extern crate memory_cache; extern crate derive_more; +extern crate state_account; #[cfg(test)] extern crate kvdb_memorydb; diff --git a/ethcore/light/src/on_demand/request.rs b/ethcore/light/src/on_demand/request.rs index b2bf39951f3..0c2b27f6bb1 100644 --- a/ethcore/light/src/on_demand/request.rs +++ b/ethcore/light/src/on_demand/request.rs @@ -25,7 +25,8 @@ use common_types::encoded; use common_types::receipt::Receipt; use common_types::transaction::SignedTransaction; use ethcore::engines::{Engine, StateDependentProof}; -use ethcore::state::{self, ProvedExecution}; +use ethcore::executive_state::{ProvedExecution, self}; +use state_account; use ethereum_types::{H256, U256, Address}; use ethtrie::{TrieError, TrieDB}; use hash::{KECCAK_NULL_RLP, KECCAK_EMPTY, KECCAK_EMPTY_LIST_RLP, keccak}; @@ -1048,7 +1049,7 @@ impl TransactionProof { let mut env_info = self.env_info.clone(); env_info.gas_limit = self.tx.gas; - let proved_execution = state::check_proof( + let proved_execution = executive_state::check_proof( state_items, root, &self.tx, diff --git a/ethcore/private-tx/Cargo.toml b/ethcore/private-tx/Cargo.toml index ec2f66382b3..254aa67276a 100644 --- a/ethcore/private-tx/Cargo.toml +++ b/ethcore/private-tx/Cargo.toml @@ -35,8 +35,10 @@ rustc-hex = "1.0" serde = "1.0" serde_derive = "1.0" serde_json = "1.0" +state-account = { path = "../state-account" } time-utils = { path = "../../util/time-utils" } tiny-keccak = "1.4" +trace = { path = "../trace" } transaction-pool = "2.0" url = "1" diff --git a/ethcore/private-tx/src/lib.rs b/ethcore/private-tx/src/lib.rs index 2f23de3fc86..7619e7f92c4 100644 --- a/ethcore/private-tx/src/lib.rs +++ b/ethcore/private-tx/src/lib.rs @@ -46,7 +46,9 @@ extern crate rlp; extern crate serde_derive; extern crate serde; extern crate serde_json; +extern crate state_account; extern crate rustc_hex; +extern crate trace; extern crate transaction_pool as txpool; extern crate url; #[macro_use] @@ -92,8 +94,9 @@ use ethcore::client::{ Call, BlockInfo }; use ethcore::miner::{self, Miner, MinerService, pool_client::NonceCache}; -use ethcore::{state, state_db}; -use ethcore::trace::{Tracer, VMTracer}; +use ethcore::state_db; +use state_account::State; +use trace::{Tracer, VMTracer}; use call_contract::CallContract; use rustc_hex::FromHex; use ethabi::FunctionOutputDecoder; @@ -539,7 +542,7 @@ impl Provider { raw } - fn patch_account_state(&self, contract_address: &Address, block: BlockId, state: &mut state::State) -> Result<(), Error> { + fn patch_account_state(&self, contract_address: &Address, block: BlockId, state: &mut State) -> Result<(), Error> { let contract_code = Arc::new(self.get_decrypted_code(contract_address, block)?); let contract_state = self.get_decrypted_state(contract_address, block)?; trace!(target: "privatetx", "Patching contract at {:?}, code: {:?}, state: {:?}", contract_address, contract_code, contract_state); diff --git a/ethcore/src/lib.rs b/ethcore/src/lib.rs index 60e5f6d9e9f..99c146d507d 100644 --- a/ethcore/src/lib.rs +++ b/ethcore/src/lib.rs @@ -158,6 +158,7 @@ pub mod error; pub mod ethereum; pub mod executed; pub mod executive; +pub mod executive_state; pub mod machine; pub mod miner; // TODO: need to be pub? @@ -172,7 +173,7 @@ pub mod verification; mod externalities; mod tx_filter; -mod executive_state; + #[cfg(test)] mod tests; diff --git a/rpc/Cargo.toml b/rpc/Cargo.toml index 7ba7b4fca73..248e9dfc6bc 100644 --- a/rpc/Cargo.toml +++ b/rpc/Cargo.toml @@ -60,6 +60,7 @@ parity-runtime = { path = "../util/runtime" } parity-updater = { path = "../updater" } parity-version = { path = "../util/version" } rlp = "0.4.0" +state-account = { path = "../ethcore/state-account" } stats = { path = "../util/stats" } vm = { path = "../ethcore/vm" } diff --git a/rpc/src/lib.rs b/rpc/src/lib.rs index 6b856eb5291..e54fcb12296 100644 --- a/rpc/src/lib.rs +++ b/rpc/src/lib.rs @@ -82,6 +82,7 @@ extern crate parity_updater as updater; extern crate parity_version as version; extern crate eip_712; extern crate rlp; +extern crate state_account; extern crate stats; extern crate tempdir; extern crate vm; diff --git a/rpc/src/v1/impls/parity.rs b/rpc/src/v1/impls/parity.rs index e040ed52b92..4ca3d3ae344 100644 --- a/rpc/src/v1/impls/parity.rs +++ b/rpc/src/v1/impls/parity.rs @@ -23,7 +23,7 @@ use ethereum_types::{H64, H160, H256, H512, U64, U256}; use ethcore::client::{BlockChainClient, StateClient, Call}; use ethcore::miner::{self, MinerService, FilterOptions}; use ethcore::snapshot::{SnapshotService, RestorationStatus}; -use ethcore::state::StateInfo; +use state_account::state::StateInfo; use ethcore_logger::RotatingLogger; use ethkey::{crypto::ecies, Brain, Generator}; use ethstore::random_phrase; From c772639a6303179995e1ffbbf12266afa874bbb4 Mon Sep 17 00:00:00 2001 From: David Palm Date: Fri, 5 Jul 2019 19:56:30 +0200 Subject: [PATCH 34/87] Add back the more specific impls of StateOrBlock From conversions --- ethcore/src/client/traits.rs | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/ethcore/src/client/traits.rs b/ethcore/src/client/traits.rs index 7123d165995..96967c1b940 100644 --- a/ethcore/src/client/traits.rs +++ b/ethcore/src/client/traits.rs @@ -61,25 +61,24 @@ pub enum StateOrBlock { Block(BlockId) } -impl From for StateOrBlock { - fn from(info: S) -> StateOrBlock { - StateOrBlock::State(Box::new(info) as Box<_>) - } -} - -// TODO: sort out why these imps conflict -//impl From> for StateOrBlock { -// fn from(info: Box) -> StateOrBlock { -// StateOrBlock::State(info) +// TODO: This doesn't work (StateInfo is now in a diff crate) but not sure where it was used, if at all. +//impl From for StateOrBlock { +// fn from(info: S) -> StateOrBlock { +// StateOrBlock::State(Box::new(info) as Box<_>) // } //} -// TODO: sort out why these imps conflict -//impl From for StateOrBlock { -// fn from(id: BlockId) -> StateOrBlock { -// StateOrBlock::Block(id) -// } -//} +impl From> for StateOrBlock { + fn from(info: Box) -> StateOrBlock { + StateOrBlock::State(info) + } +} + +impl From for StateOrBlock { + fn from(id: BlockId) -> StateOrBlock { + StateOrBlock::Block(id) + } +} /// Provides `nonce` and `latest_nonce` methods pub trait Nonce { From ffda775bb61ed5604370fe96a8182c1b887ccd9c Mon Sep 17 00:00:00 2001 From: David Palm Date: Fri, 5 Jul 2019 20:58:36 +0200 Subject: [PATCH 35/87] Move execute to freestanding function and remove it from trait Sort out the error handling in executive_state by moving the result types from state to ethcore Undo the verbose code added to work around the StateOrBlock From conversions --- ethcore/src/client/test_client.rs | 2 +- ethcore/src/client/traits.rs | 6 +- ethcore/src/executive_state.rs | 99 ++++++++++++++++++------------ ethcore/state-account/src/state.rs | 15 ----- 4 files changed, 64 insertions(+), 58 deletions(-) diff --git a/ethcore/src/client/test_client.rs b/ethcore/src/client/test_client.rs index 40cb552d8c5..add2d4031d4 100644 --- a/ethcore/src/client/test_client.rs +++ b/ethcore/src/client/test_client.rs @@ -469,7 +469,7 @@ impl Balance for TestBlockChainClient { } fn latest_balance(&self, address: &Address) -> U256 { - self.balance(address, StateOrBlock::Block(BlockId::Latest)).unwrap() + self.balance(address, BlockId::Latest.into()).unwrap() } } diff --git a/ethcore/src/client/traits.rs b/ethcore/src/client/traits.rs index 96967c1b940..fa3eaa8e0a5 100644 --- a/ethcore/src/client/traits.rs +++ b/ethcore/src/client/traits.rs @@ -104,7 +104,7 @@ pub trait Balance { /// Get address balance at the latest block's state. fn latest_balance(&self, address: &Address) -> U256 { - self.balance(address, StateOrBlock::Block(BlockId::Latest)) + self.balance(address, BlockId::Latest.into()) .expect("balance will return Some if given BlockId::Latest. balance was given BlockId::Latest \ Therefore balance has returned Some; qed") } @@ -234,7 +234,7 @@ pub trait BlockChainClient : Sync + Send + AccountData + BlockChain + CallContra /// Get address code at the latest block's state. fn latest_code(&self, address: &Address) -> Option { - self.code(address, StateOrBlock::Block(BlockId::Latest)) + self.code(address, BlockId::Latest.into()) .expect("code will return Some if given BlockId::Latest; qed") } @@ -251,7 +251,7 @@ pub trait BlockChainClient : Sync + Send + AccountData + BlockChain + CallContra /// Get value of the storage at given position at the latest block's state. fn latest_storage_at(&self, address: &Address, position: &H256) -> H256 { - self.storage_at(address, position, StateOrBlock::Block(BlockId::Latest)) + self.storage_at(address, position, BlockId::Latest.into()) .expect("storage_at will return Some if given BlockId::Latest. storage_at was given BlockId::Latest. \ Therefore storage_at has returned Some; qed") } diff --git a/ethcore/src/executive_state.rs b/ethcore/src/executive_state.rs index 40ea082506a..d96b47bfbc0 100644 --- a/ethcore/src/executive_state.rs +++ b/ethcore/src/executive_state.rs @@ -14,6 +14,10 @@ // You should have received a copy of the GNU General Public License // along with Parity Ethereum. If not, see . +//! Execute transactions and modify State. This is glue code between the `ethcore` and +//! `state-account` crates and contains everything that requires `Machine` or `Executive` (or types +//! thereof). + use machine::Machine; use vm::EnvInfo; use executive::{Executive, TransactOptions}; @@ -25,7 +29,7 @@ use types::{ use trace::{FlatTrace, VMTrace}; use state_account::{ backend::{self, Backend}, - state::{ApplyResult, ApplyOutcome, State}, + state::State, }; use ethereum_types::H256; use factories::Factories; @@ -34,6 +38,8 @@ use keccak_hasher::KeccakHasher; use kvdb::DBValue; use hash_db::AsHashDB; +use error::Error; + // TODO: is there a better place for this? /// Return type of proof validity check. #[derive(Debug, Clone)] @@ -46,6 +52,21 @@ pub enum ProvedExecution { Complete(Box), } +/// Used to return information about an `State::apply` operation. +pub struct ApplyOutcome { + /// The receipt for the applied transaction. + pub receipt: Receipt, + /// The output of the applied transaction. + pub output: Bytes, + /// The trace for the applied transaction, empty if tracing was not produced. + pub trace: Vec, + /// The VM trace for the applied transaction, None if tracing was not produced. + pub vm_trace: Option +} + +/// Result type for the execution ("application") of a transaction. +pub type ApplyResult = Result, Error>; + /// Check the given proof of execution. /// `Err(ExecutionError::Internal)` indicates failure, everything else indicates /// a successful proof (as the transaction itself may be poorly chosen). @@ -73,7 +94,7 @@ pub fn check_proof( }; let options = TransactOptions::with_no_tracing().save_output_from_contract(); - match state.execute(env_info, machine, transaction, options, true) { + match execute(&mut state, env_info, machine, transaction, options, true) { Ok(executed) => ProvedExecution::Complete(Box::new(executed)), Err(ExecutionError::Internal(_)) => ProvedExecution::BadProof, Err(e) => ProvedExecution::Failed(e), @@ -81,7 +102,7 @@ pub fn check_proof( } /// Prove a `virtual` transaction on the given state. -/// Returns `None` when the transacion could not be proved, +/// Returns `None` when the transaction could not be proved, /// and a proof otherwise. pub fn prove_transaction_virtual + Send + Sync>( db: H, @@ -107,7 +128,7 @@ pub fn prove_transaction_virtual + Send + Syn }; let options = TransactOptions::with_no_tracing().dont_check_nonce().save_output_from_contract(); - match state.execute(env_info, machine, transaction, options, true) { + match execute(&mut state, env_info, machine, transaction, options, true) { Err(ExecutionError::Internal(_)) => None, Err(e) => { trace!(target: "state", "Proved call failed: {}", e); @@ -142,28 +163,18 @@ pub trait ExecutiveStateWithMachineZomgBetterName { where T: trace::Tracer, V: trace::VMTracer; - // Execute a given transaction without committing changes. - // - // `virt` signals that we are executing outside of a block set and restrictions like - // gas limits and gas costs should be lifted. - fn execute( - &mut self, - env_info: &EnvInfo, - machine: &Machine, - t: &SignedTransaction, - options: TransactOptions, - virt: bool - ) -> Result, ExecutionError> - where - T: trace::Tracer, - V: trace::VMTracer; - } impl ExecutiveStateWithMachineZomgBetterName for State { /// Execute a given transaction, producing a receipt and an optional trace. /// This will change the state accordingly. - fn apply(&mut self, env_info: &EnvInfo, machine: &Machine, t: &SignedTransaction, tracing: bool) -> ApplyResult { + fn apply( + &mut self, + env_info: &EnvInfo, + machine: &Machine, + t: &SignedTransaction, + tracing: bool + ) -> ApplyResult { if tracing { let options = TransactOptions::with_tracing(); self.apply_with_tracing(env_info, machine, t, options.tracer, options.vm_tracer) @@ -182,13 +193,13 @@ impl ExecutiveStateWithMachineZomgBetterName for State { t: &SignedTransaction, tracer: T, vm_tracer: V, - ) -> ApplyResult where - T: trace::Tracer, - V: trace::VMTracer, + ) -> ApplyResult + where + T: trace::Tracer, + V: trace::VMTracer, { let options = TransactOptions::new(tracer, vm_tracer); - // TODO: fix error handling - let e = self.execute(env_info, machine, t, options, false).expect("TODO FIXME"); + let e = execute(self, env_info, machine, t, options, false)?; let params = machine.params(); let eip658 = env_info.number >= params.eip658_transition; @@ -218,20 +229,30 @@ impl ExecutiveStateWithMachineZomgBetterName for State { vm_trace: e.vm_trace, }) } +} - // Execute a given transaction without committing changes. - // - // `virt` signals that we are executing outside of a block set and restrictions like - // gas limits and gas costs should be lifted. - fn execute(&mut self, env_info: &EnvInfo, machine: &Machine, t: &SignedTransaction, options: TransactOptions, virt: bool) - -> Result, ExecutionError> where T: trace::Tracer, V: trace::VMTracer, - { - let schedule = machine.schedule(env_info.number); - let mut e = Executive::new(self, env_info, machine, &schedule); +// Execute a given transaction without committing changes. +// +// `virt` signals that we are executing outside of a block set and restrictions like +// gas limits and gas costs should be lifted. +fn execute( + state: &mut State, + env_info: &EnvInfo, + machine: &Machine, + t: &SignedTransaction, + options: TransactOptions, + virt: bool +) -> Result, ExecutionError> + where + B: Backend, + T: trace::Tracer, + V: trace::VMTracer, +{ + let schedule = machine.schedule(env_info.number); + let mut e = Executive::new(state, env_info, machine, &schedule); - match virt { - true => e.transact_virtual(t, options), - false => e.transact(t, options), - } + match virt { + true => e.transact_virtual(t, options), + false => e.transact(t, options), } } diff --git a/ethcore/state-account/src/state.rs b/ethcore/state-account/src/state.rs index c5de958e8fd..70dd6e55253 100644 --- a/ethcore/state-account/src/state.rs +++ b/ethcore/state-account/src/state.rs @@ -50,21 +50,6 @@ use crate::{ backend::Backend, }; -/// Used to return information about an `State::apply` operation. -pub struct ApplyOutcome { - /// The receipt for the applied transaction. - pub receipt: Receipt, - /// The output of the applied transaction. - pub output: Bytes, - /// The trace for the applied transaction, empty if tracing was not produced. - pub trace: Vec, - /// The VM trace for the applied transaction, None if tracing was not produced. - pub vm_trace: Option -} - -/// Result type for the execution ("application") of a transaction. -pub type ApplyResult = Result, Error>; - #[derive(Eq, PartialEq, Clone, Copy, Debug)] /// Account modification state. Used to check if the account was /// Modified in between commits and overall. From 2f9526be25615b06eae738e9c02981f49a2f3437 Mon Sep 17 00:00:00 2001 From: David Palm Date: Fri, 5 Jul 2019 21:29:29 +0200 Subject: [PATCH 36/87] cleanup --- ethcore/state-account/src/state.rs | 77 ------------------------------ 1 file changed, 77 deletions(-) diff --git a/ethcore/state-account/src/state.rs b/ethcore/state-account/src/state.rs index 70dd6e55253..2b303b78759 100644 --- a/ethcore/state-account/src/state.rs +++ b/ethcore/state-account/src/state.rs @@ -28,7 +28,6 @@ use std::{ }; use common_types::{ - receipt::Receipt, state_diff::StateDiff, basic_account::BasicAccount, }; @@ -695,82 +694,6 @@ impl State { Ok(()) } - // TODO: Needs Machine and TransactOptions - /// Execute a given transaction, producing a receipt and an optional trace. - /// This will change the state accordingly. -// pub fn apply(&mut self, env_info: &EnvInfo, machine: &Machine, t: &SignedTransaction, tracing: bool) -> ApplyResult { -// if tracing { -// let options = TransactOptions::with_tracing(); -// self.apply_with_tracing(env_info, machine, t, options.tracer, options.vm_tracer) -// } else { -// let options = TransactOptions::with_no_tracing(); -// self.apply_with_tracing(env_info, machine, t, options.tracer, options.vm_tracer) -// } -// } - - // TODO: Needs Machine - /// Execute a given transaction with given tracer and VM tracer producing a receipt and an optional trace. - /// This will change the state accordingly. -// pub fn apply_with_tracing( -// &mut self, -// env_info: &EnvInfo, -// machine: &Machine, -// t: &SignedTransaction, -// tracer: T, -// vm_tracer: V, -// ) -> ApplyResult where -// T: trace::Tracer, -// V: trace::VMTracer, -// { -// let options = TransactOptions::new(tracer, vm_tracer); -// let e = self.execute(env_info, machine, t, options, false)?; -// let params = machine.params(); -// -// let eip658 = env_info.number >= params.eip658_transition; -// let no_intermediate_commits = -// eip658 || -// (env_info.number >= params.eip98_transition && env_info.number >= params.validate_receipts_transition); -// -// let outcome = if no_intermediate_commits { -// if eip658 { -// TransactionOutcome::StatusCode(if e.exception.is_some() { 0 } else { 1 }) -// } else { -// TransactionOutcome::Unknown -// } -// } else { -// self.commit()?; -// TransactionOutcome::StateRoot(self.root().clone()) -// }; -// -// let output = e.output; -// let receipt = Receipt::new(outcome, e.cumulative_gas_used, e.logs); -// trace!(target: "state", "Transaction receipt: {:?}", receipt); -// -// Ok(ApplyOutcome { -// receipt, -// output, -// trace: e.trace, -// vm_trace: e.vm_trace, -// }) -// } - - // TODO: Needs Machine and Executed, ExecutionError, Executive - // Execute a given transaction without committing changes. - // - // `virt` signals that we are executing outside of a block set and restrictions like - // gas limits and gas costs should be lifted. -// fn execute(&mut self, env_info: &EnvInfo, machine: &Machine, t: &SignedTransaction, options: TransactOptions, virt: bool) -// -> Result, ExecutionError> where T: trace::Tracer, V: trace::VMTracer, -// { -// let schedule = machine.schedule(env_info.number); -// let mut e = Executive::new(self, env_info, machine, &schedule); -// -// match virt { -// true => e.transact_virtual(t, options), -// false => e.transact(t, options), -// } -// } - fn touch(&mut self, a: &Address) -> TrieResult<()> { self.require(a, false)?; Ok(()) From 6f8a1a70be8d376ebb2277fb83e3b40c13746fa7 Mon Sep 17 00:00:00 2001 From: David Palm Date: Fri, 5 Jul 2019 21:32:15 +0200 Subject: [PATCH 37/87] Fix "error: enum variants on type aliases are experimental" --- ethcore/state-account/src/state.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ethcore/state-account/src/state.rs b/ethcore/state-account/src/state.rs index 2b303b78759..2b46560570a 100644 --- a/ethcore/state-account/src/state.rs +++ b/ethcore/state-account/src/state.rs @@ -32,7 +32,7 @@ use common_types::{ basic_account::BasicAccount, }; use ethereum_types::{Address, H256, U256}; -use ethtrie::{TrieDB, TrieError, Result as TrieResult}; +use ethtrie::{TrieDB, Result as TrieResult}; use factories::{Factories, VmFactory}; use hash_db::HashDB; use keccak_hash::{KECCAK_EMPTY, KECCAK_NULL_RLP}; @@ -41,7 +41,7 @@ use kvdb::DBValue; use log::{warn, trace}; use parity_bytes::Bytes; use pod::{self, PodAccount, PodState}; -use trie_db::{Trie, Recorder}; +use trie_db::{Trie, TrieError, Recorder}; use crate::{ Error, From b11e078d0b476c4a37eccf6c52be1ae34892599c Mon Sep 17 00:00:00 2001 From: David Palm Date: Fri, 5 Jul 2019 22:45:57 +0200 Subject: [PATCH 38/87] Bring back the state tests Fix whitespace --- ethcore/src/executive_state.rs | 1441 ++++++++++++++++++++++++++++ ethcore/state-account/Cargo.toml | 6 + ethcore/state-account/src/state.rs | 31 +- 3 files changed, 1462 insertions(+), 16 deletions(-) diff --git a/ethcore/src/executive_state.rs b/ethcore/src/executive_state.rs index d96b47bfbc0..88a7ed32317 100644 --- a/ethcore/src/executive_state.rs +++ b/ethcore/src/executive_state.rs @@ -256,3 +256,1444 @@ fn execute( false => e.transact(t, options), } } + +#[cfg(test)] +mod tests { + use std::sync::Arc; + use std::str::FromStr; + use std::collections::HashSet; + use rustc_hex::FromHex; + use hash::{keccak, KECCAK_NULL_RLP}; + use super::*; + use ethkey::Secret; + use ethereum_types::{H256, U256, Address, BigEndianHash}; + use test_helpers::{get_temp_state, get_temp_state_db}; + use machine::Machine; + use vm::EnvInfo; + use spec::*; + use types::transaction::*; + use trace::{FlatTrace, TraceError, trace}; + use evm::CallType; + use pod::{self, PodAccount}; + #[cfg(feature="to-pod-full")] + use pod::PodState; + use executive_state::ExecutiveStateWithMachineZomgBetterName; + use state_account::{Account, CleanupMode}; + + fn secret() -> Secret { + keccak("").into() + } + + fn make_frontier_machine(max_depth: usize) -> Machine { + let mut machine = ::ethereum::new_frontier_test_machine(); + machine.set_schedule_creation_rules(Box::new(move |s, _| s.max_depth = max_depth)); + machine + } + + #[test] + fn should_apply_create_transaction() { + let _ = env_logger::try_init(); + + let mut state = get_temp_state(); + + let mut info = EnvInfo::default(); + info.gas_limit = 1_000_000.into(); + let machine = make_frontier_machine(5); + + let t = Transaction { + nonce: 0.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Create, + value: 100.into(), + data: FromHex::from_hex("601080600c6000396000f3006000355415600957005b60203560003555").unwrap(), + }.sign(&secret(), None); + + state.add_balance(&t.sender(), &(100.into()), CleanupMode::NoEmpty).unwrap(); + let result = state.apply(&info, &machine, &t, true).unwrap(); + let expected_trace = vec![FlatTrace { + trace_address: Default::default(), + subtraces: 0, + action: trace::Action::Create(trace::Create { + from: Address::from_str("9cce34f7ab185c7aba1b7c8140d620b4bda941d6").unwrap(), + value: 100.into(), + gas: 77412.into(), + init: vec![96, 16, 128, 96, 12, 96, 0, 57, 96, 0, 243, 0, 96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53, 85], + }), + result: trace::Res::Create(trace::CreateResult { + gas_used: U256::from(3224), + address: Address::from_str("8988167e088c87cd314df6d3c2b83da5acb93ace").unwrap(), + code: vec![96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53] + }), + }]; + + assert_eq!(result.trace, expected_trace); + } + + #[test] + fn should_work_when_cloned() { + let _ = env_logger::try_init(); + + let a = Address::zero(); + + let mut state = { + let mut state = get_temp_state(); + assert_eq!(state.exists(&a).unwrap(), false); + state.inc_nonce(&a).unwrap(); + state.commit().unwrap(); + state.clone() + }; + + state.inc_nonce(&a).unwrap(); + state.commit().unwrap(); + } + + #[test] + fn should_trace_failed_create_transaction() { + let _ = env_logger::try_init(); + + let mut state = get_temp_state(); + + let mut info = EnvInfo::default(); + info.gas_limit = 1_000_000.into(); + let machine = make_frontier_machine(5); + + let t = Transaction { + nonce: 0.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Create, + value: 100.into(), + data: FromHex::from_hex("5b600056").unwrap(), + }.sign(&secret(), None); + + state.add_balance(&t.sender(), &(100.into()), CleanupMode::NoEmpty).unwrap(); + let result = state.apply(&info, &machine, &t, true).unwrap(); + let expected_trace = vec![FlatTrace { + trace_address: Default::default(), + action: trace::Action::Create(trace::Create { + from: Address::from_str("9cce34f7ab185c7aba1b7c8140d620b4bda941d6").unwrap(), + value: 100.into(), + gas: 78792.into(), + init: vec![91, 96, 0, 86], + }), + result: trace::Res::FailedCreate(TraceError::OutOfGas), + subtraces: 0 + }]; + + assert_eq!(result.trace, expected_trace); + } + + #[test] + fn should_trace_call_transaction() { + let _ = env_logger::try_init(); + + let mut state = get_temp_state(); + + let mut info = EnvInfo::default(); + info.gas_limit = 1_000_000.into(); + let machine = make_frontier_machine(5); + + let t = Transaction { + nonce: 0.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Call(Address::from_low_u64_be(0xa)), + value: 100.into(), + data: vec![], + }.sign(&secret(), None); + + state.init_code(&Address::from_low_u64_be(0xa), FromHex::from_hex("6000").unwrap()).unwrap(); + state.add_balance(&t.sender(), &(100.into()), CleanupMode::NoEmpty).unwrap(); + let result = state.apply(&info, &machine, &t, true).unwrap(); + let expected_trace = vec![FlatTrace { + trace_address: Default::default(), + action: trace::Action::Call(trace::Call { + from: Address::from_str("9cce34f7ab185c7aba1b7c8140d620b4bda941d6").unwrap(), + to: Address::from_low_u64_be(0xa), + value: 100.into(), + gas: 79000.into(), + input: vec![], + call_type: CallType::Call, + }), + result: trace::Res::Call(trace::CallResult { + gas_used: U256::from(3), + output: vec![] + }), + subtraces: 0, + }]; + + assert_eq!(result.trace, expected_trace); + } + + #[test] + fn should_trace_basic_call_transaction() { + let _ = env_logger::try_init(); + + let mut state = get_temp_state(); + + let mut info = EnvInfo::default(); + info.gas_limit = 1_000_000.into(); + let machine = make_frontier_machine(5); + + let t = Transaction { + nonce: 0.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Call(Address::from_low_u64_be(0xa)), + value: 100.into(), + data: vec![], + }.sign(&secret(), None); + + state.add_balance(&t.sender(), &(100.into()), CleanupMode::NoEmpty).unwrap(); + let result = state.apply(&info, &machine, &t, true).unwrap(); + let expected_trace = vec![FlatTrace { + trace_address: Default::default(), + action: trace::Action::Call(trace::Call { + from: Address::from_str("9cce34f7ab185c7aba1b7c8140d620b4bda941d6").unwrap(), + to: Address::from_low_u64_be(0xa), + value: 100.into(), + gas: 79000.into(), + input: vec![], + call_type: CallType::Call, + }), + result: trace::Res::Call(trace::CallResult { + gas_used: U256::from(0), + output: vec![] + }), + subtraces: 0, + }]; + + assert_eq!(result.trace, expected_trace); + } + + #[test] + fn should_trace_call_transaction_to_builtin() { + let _ = env_logger::try_init(); + + let mut state = get_temp_state(); + + let mut info = EnvInfo::default(); + info.gas_limit = 1_000_000.into(); + let machine = Spec::new_test_machine(); + + let t = Transaction { + nonce: 0.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Call(Address::from_low_u64_be(0x1)), + value: 0.into(), + data: vec![], + }.sign(&secret(), None); + + let result = state.apply(&info, &machine, &t, true).unwrap(); + + let expected_trace = vec![FlatTrace { + trace_address: Default::default(), + action: trace::Action::Call(trace::Call { + from: Address::from_str("9cce34f7ab185c7aba1b7c8140d620b4bda941d6").unwrap(), + to: Address::from_str("0000000000000000000000000000000000000001").unwrap(), + value: 0.into(), + gas: 79_000.into(), + input: vec![], + call_type: CallType::Call, + }), + result: trace::Res::Call(trace::CallResult { + gas_used: U256::from(3000), + output: vec![] + }), + subtraces: 0, + }]; + + assert_eq!(result.trace, expected_trace); + } + + #[test] + fn should_not_trace_subcall_transaction_to_builtin() { + let _ = env_logger::try_init(); + + let mut state = get_temp_state(); + + let mut info = EnvInfo::default(); + info.gas_limit = 1_000_000.into(); + let machine = Spec::new_test_machine(); + + let t = Transaction { + nonce: 0.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Call(Address::from_low_u64_be(0xa)), + value: 0.into(), + data: vec![], + }.sign(&secret(), None); + + state.init_code(&Address::from_low_u64_be(0xa), FromHex::from_hex("600060006000600060006001610be0f1").unwrap()).unwrap(); + let result = state.apply(&info, &machine, &t, true).unwrap(); + + let expected_trace = vec![FlatTrace { + trace_address: Default::default(), + action: trace::Action::Call(trace::Call { + from: Address::from_str("9cce34f7ab185c7aba1b7c8140d620b4bda941d6").unwrap(), + to: Address::from_low_u64_be(0xa), + value: 0.into(), + gas: 79000.into(), + input: vec![], + call_type: CallType::Call, + }), + result: trace::Res::Call(trace::CallResult { + gas_used: U256::from(3_721), // in post-eip150 + output: vec![] + }), + subtraces: 0, + }]; + + assert_eq!(result.trace, expected_trace); + } + + #[test] + fn should_trace_callcode_properly() { + let _ = env_logger::try_init(); + + let mut state = get_temp_state(); + + let mut info = EnvInfo::default(); + info.gas_limit = 1_000_000.into(); + let machine = Spec::new_test_machine(); + + let t = Transaction { + nonce: 0.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Call(Address::from_low_u64_be(0xa)), + value: 0.into(), + data: vec![], + }.sign(&secret(), None); + + state.init_code(&Address::from_low_u64_be(0xa), FromHex::from_hex("60006000600060006000600b611000f2").unwrap()).unwrap(); + state.init_code(&Address::from_low_u64_be(0xb), FromHex::from_hex("6000").unwrap()).unwrap(); + let result = state.apply(&info, &machine, &t, true).unwrap(); + + let expected_trace = vec![FlatTrace { + trace_address: Default::default(), + subtraces: 1, + action: trace::Action::Call(trace::Call { + from: Address::from_str("9cce34f7ab185c7aba1b7c8140d620b4bda941d6").unwrap(), + to: Address::from_low_u64_be(0xa), + value: 0.into(), + gas: 79000.into(), + input: vec![], + call_type: CallType::Call, + }), + result: trace::Res::Call(trace::CallResult { + gas_used: 724.into(), // in post-eip150 + output: vec![] + }), + }, FlatTrace { + trace_address: vec![0].into_iter().collect(), + subtraces: 0, + action: trace::Action::Call(trace::Call { + from: Address::from_low_u64_be(0xa), + to: Address::from_low_u64_be(0xb), + value: 0.into(), + gas: 4096.into(), + input: vec![], + call_type: CallType::CallCode, + }), + result: trace::Res::Call(trace::CallResult { + gas_used: 3.into(), + output: vec![], + }), + }]; + + assert_eq!(result.trace, expected_trace); + } + + #[test] + fn should_trace_delegatecall_properly() { + let _ = env_logger::try_init(); + + let mut state = get_temp_state(); + + let mut info = EnvInfo::default(); + info.gas_limit = 1_000_000.into(); + info.number = 0x789b0; + let machine = Spec::new_test_machine(); + + let t = Transaction { + nonce: 0.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Call(Address::from_low_u64_be(0xa)), + value: 0.into(), + data: vec![], + }.sign(&secret(), None); + + state.init_code(&Address::from_low_u64_be(0xa), FromHex::from_hex("6000600060006000600b618000f4").unwrap()).unwrap(); + state.init_code(&Address::from_low_u64_be(0xb), FromHex::from_hex("60056000526001601ff3").unwrap()).unwrap(); + let result = state.apply(&info, &machine, &t, true).unwrap(); + + let expected_trace = vec![FlatTrace { + trace_address: Default::default(), + subtraces: 1, + action: trace::Action::Call(trace::Call { + from: Address::from_str("9cce34f7ab185c7aba1b7c8140d620b4bda941d6").unwrap(), + to: Address::from_low_u64_be(0xa), + value: 0.into(), + gas: 79000.into(), + input: vec![], + call_type: CallType::Call, + }), + result: trace::Res::Call(trace::CallResult { + gas_used: U256::from(736), // in post-eip150 + output: vec![] + }), + }, FlatTrace { + trace_address: vec![0].into_iter().collect(), + subtraces: 0, + action: trace::Action::Call(trace::Call { + from: Address::from_low_u64_be(0xa), + to: Address::from_low_u64_be(0xb), + value: 0.into(), + gas: 32768.into(), + input: vec![], + call_type: CallType::DelegateCall, + }), + result: trace::Res::Call(trace::CallResult { + gas_used: 18.into(), + output: vec![5], + }), + }]; + + assert_eq!(result.trace, expected_trace); + } + + #[test] + fn should_trace_failed_call_transaction() { + let _ = env_logger::try_init(); + + let mut state = get_temp_state(); + + let mut info = EnvInfo::default(); + info.gas_limit = 1_000_000.into(); + let machine = make_frontier_machine(5); + + let t = Transaction { + nonce: 0.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Call(Address::from_low_u64_be(0xa)), + value: 100.into(), + data: vec![], + }.sign(&secret(), None); + + state.init_code(&Address::from_low_u64_be(0xa), FromHex::from_hex("5b600056").unwrap()).unwrap(); + state.add_balance(&t.sender(), &(100.into()), CleanupMode::NoEmpty).unwrap(); + let result = state.apply(&info, &machine, &t, true).unwrap(); + let expected_trace = vec![FlatTrace { + trace_address: Default::default(), + action: trace::Action::Call(trace::Call { + from: Address::from_str("9cce34f7ab185c7aba1b7c8140d620b4bda941d6").unwrap(), + to: Address::from_low_u64_be(0xa), + value: 100.into(), + gas: 79000.into(), + input: vec![], + call_type: CallType::Call, + }), + result: trace::Res::FailedCall(TraceError::OutOfGas), + subtraces: 0, + }]; + + assert_eq!(result.trace, expected_trace); + } + + #[test] + fn should_trace_call_with_subcall_transaction() { + let _ = env_logger::try_init(); + + let mut state = get_temp_state(); + + let mut info = EnvInfo::default(); + info.gas_limit = 1_000_000.into(); + let machine = make_frontier_machine(5); + + let t = Transaction { + nonce: 0.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Call(Address::from_low_u64_be(0xa)), + value: 100.into(), + data: vec![], + }.sign(&secret(), None); + + state.init_code(&Address::from_low_u64_be(0xa), FromHex::from_hex("60006000600060006000600b602b5a03f1").unwrap()).unwrap(); + state.init_code(&Address::from_low_u64_be(0xb), FromHex::from_hex("6000").unwrap()).unwrap(); + state.add_balance(&t.sender(), &(100.into()), CleanupMode::NoEmpty).unwrap(); + let result = state.apply(&info, &machine, &t, true).unwrap(); + + let expected_trace = vec![FlatTrace { + trace_address: Default::default(), + subtraces: 1, + action: trace::Action::Call(trace::Call { + from: Address::from_str("9cce34f7ab185c7aba1b7c8140d620b4bda941d6").unwrap(), + to: Address::from_low_u64_be(0xa), + value: 100.into(), + gas: 79000.into(), + input: vec![], + call_type: CallType::Call, + }), + result: trace::Res::Call(trace::CallResult { + gas_used: U256::from(69), + output: vec![] + }), + }, FlatTrace { + trace_address: vec![0].into_iter().collect(), + subtraces: 0, + action: trace::Action::Call(trace::Call { + from: Address::from_low_u64_be(0xa), + to: Address::from_low_u64_be(0xb), + value: 0.into(), + gas: 78934.into(), + input: vec![], + call_type: CallType::Call, + }), + result: trace::Res::Call(trace::CallResult { + gas_used: U256::from(3), + output: vec![] + }), + }]; + + assert_eq!(result.trace, expected_trace); + } + + #[test] + fn should_trace_call_with_basic_subcall_transaction() { + let _ = env_logger::try_init(); + + let mut state = get_temp_state(); + + let mut info = EnvInfo::default(); + info.gas_limit = 1_000_000.into(); + let machine = make_frontier_machine(5); + + let t = Transaction { + nonce: 0.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Call(Address::from_low_u64_be(0xa)), + value: 100.into(), + data: vec![], + }.sign(&secret(), None); + + state.init_code(&Address::from_low_u64_be(0xa), FromHex::from_hex("60006000600060006045600b6000f1").unwrap()).unwrap(); + state.add_balance(&t.sender(), &(100.into()), CleanupMode::NoEmpty).unwrap(); + let result = state.apply(&info, &machine, &t, true).unwrap(); + let expected_trace = vec![FlatTrace { + trace_address: Default::default(), + subtraces: 1, + action: trace::Action::Call(trace::Call { + from: Address::from_str("9cce34f7ab185c7aba1b7c8140d620b4bda941d6").unwrap(), + to: Address::from_low_u64_be(0xa), + value: 100.into(), + gas: 79000.into(), + input: vec![], + call_type: CallType::Call, + }), + result: trace::Res::Call(trace::CallResult { + gas_used: U256::from(31761), + output: vec![] + }), + }, FlatTrace { + trace_address: vec![0].into_iter().collect(), + subtraces: 0, + action: trace::Action::Call(trace::Call { + from: Address::from_low_u64_be(0xa), + to: Address::from_low_u64_be(0xb), + value: 69.into(), + gas: 2300.into(), + input: vec![], + call_type: CallType::Call, + }), + result: trace::Res::Call(trace::CallResult::default()), + }]; + + assert_eq!(result.trace, expected_trace); + } + + #[test] + fn should_not_trace_call_with_invalid_basic_subcall_transaction() { + let _ = env_logger::try_init(); + + let mut state = get_temp_state(); + + let mut info = EnvInfo::default(); + info.gas_limit = 1_000_000.into(); + let machine = make_frontier_machine(5); + + let t = Transaction { + nonce: 0.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Call(Address::from_low_u64_be(0xa)), + value: 100.into(), + data: vec![], + }.sign(&secret(), None); + + state.init_code(&Address::from_low_u64_be(0xa), FromHex::from_hex("600060006000600060ff600b6000f1").unwrap()).unwrap(); // not enough funds. + state.add_balance(&t.sender(), &(100.into()), CleanupMode::NoEmpty).unwrap(); + let result = state.apply(&info, &machine, &t, true).unwrap(); + let expected_trace = vec![FlatTrace { + trace_address: Default::default(), + subtraces: 0, + action: trace::Action::Call(trace::Call { + from: Address::from_str("9cce34f7ab185c7aba1b7c8140d620b4bda941d6").unwrap(), + to: Address::from_low_u64_be(0xa), + value: 100.into(), + gas: 79000.into(), + input: vec![], + call_type: CallType::Call, + }), + result: trace::Res::Call(trace::CallResult { + gas_used: U256::from(31761), + output: vec![] + }), + }]; + + assert_eq!(result.trace, expected_trace); + } + + #[test] + fn should_trace_failed_subcall_transaction() { + let _ = env_logger::try_init(); + + let mut state = get_temp_state(); + + let mut info = EnvInfo::default(); + info.gas_limit = 1_000_000.into(); + let machine = make_frontier_machine(5); + + let t = Transaction { + nonce: 0.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Call(Address::from_low_u64_be(0xa)), + value: 100.into(), + data: vec![],//600480600b6000396000f35b600056 + }.sign(&secret(), None); + + state.init_code(&Address::from_low_u64_be(0xa), FromHex::from_hex("60006000600060006000600b602b5a03f1").unwrap()).unwrap(); + state.init_code(&Address::from_low_u64_be(0xb), FromHex::from_hex("5b600056").unwrap()).unwrap(); + state.add_balance(&t.sender(), &(100.into()), CleanupMode::NoEmpty).unwrap(); + let result = state.apply(&info, &machine, &t, true).unwrap(); + let expected_trace = vec![FlatTrace { + trace_address: Default::default(), + subtraces: 1, + action: trace::Action::Call(trace::Call { + from: Address::from_str("9cce34f7ab185c7aba1b7c8140d620b4bda941d6").unwrap(), + to: Address::from_low_u64_be(0xa), + value: 100.into(), + gas: 79000.into(), + input: vec![], + call_type: CallType::Call, + }), + result: trace::Res::Call(trace::CallResult { + gas_used: U256::from(79_000), + output: vec![] + }), + }, FlatTrace { + trace_address: vec![0].into_iter().collect(), + subtraces: 0, + action: trace::Action::Call(trace::Call { + from: Address::from_low_u64_be(0xa), + to: Address::from_low_u64_be(0xb), + value: 0.into(), + gas: 78934.into(), + input: vec![], + call_type: CallType::Call, + }), + result: trace::Res::FailedCall(TraceError::OutOfGas), + }]; + + assert_eq!(result.trace, expected_trace); + } + + #[test] + fn should_trace_call_with_subcall_with_subcall_transaction() { + let _ = env_logger::try_init(); + + let mut state = get_temp_state(); + + let mut info = EnvInfo::default(); + info.gas_limit = 1_000_000.into(); + let machine = make_frontier_machine(5); + + let t = Transaction { + nonce: 0.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Call(Address::from_low_u64_be(0xa)), + value: 100.into(), + data: vec![], + }.sign(&secret(), None); + + state.init_code(&Address::from_low_u64_be(0xa), FromHex::from_hex("60006000600060006000600b602b5a03f1").unwrap()).unwrap(); + state.init_code(&Address::from_low_u64_be(0xb), FromHex::from_hex("60006000600060006000600c602b5a03f1").unwrap()).unwrap(); + state.init_code(&Address::from_low_u64_be(0xc), FromHex::from_hex("6000").unwrap()).unwrap(); + state.add_balance(&t.sender(), &(100.into()), CleanupMode::NoEmpty).unwrap(); + let result = state.apply(&info, &machine, &t, true).unwrap(); + let expected_trace = vec![FlatTrace { + trace_address: Default::default(), + subtraces: 1, + action: trace::Action::Call(trace::Call { + from: Address::from_str("9cce34f7ab185c7aba1b7c8140d620b4bda941d6").unwrap(), + to: Address::from_low_u64_be(0xa), + value: 100.into(), + gas: 79000.into(), + input: vec![], + call_type: CallType::Call, + }), + result: trace::Res::Call(trace::CallResult { + gas_used: U256::from(135), + output: vec![] + }), + }, FlatTrace { + trace_address: vec![0].into_iter().collect(), + subtraces: 1, + action: trace::Action::Call(trace::Call { + from: Address::from_low_u64_be(0xa), + to: Address::from_low_u64_be(0xb), + value: 0.into(), + gas: 78934.into(), + input: vec![], + call_type: CallType::Call, + }), + result: trace::Res::Call(trace::CallResult { + gas_used: U256::from(69), + output: vec![] + }), + }, FlatTrace { + trace_address: vec![0, 0].into_iter().collect(), + subtraces: 0, + action: trace::Action::Call(trace::Call { + from: Address::from_low_u64_be(0xb), + to: Address::from_low_u64_be(0xc), + value: 0.into(), + gas: 78868.into(), + input: vec![], + call_type: CallType::Call, + }), + result: trace::Res::Call(trace::CallResult { + gas_used: U256::from(3), + output: vec![] + }), + }]; + + assert_eq!(result.trace, expected_trace); + } + + #[test] + fn should_trace_failed_subcall_with_subcall_transaction() { + let _ = env_logger::try_init(); + + let mut state = get_temp_state(); + + let mut info = EnvInfo::default(); + info.gas_limit = 1_000_000.into(); + let machine = make_frontier_machine(5); + + let t = Transaction { + nonce: 0.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Call(Address::from_low_u64_be(0xa)), + value: 100.into(), + data: vec![],//600480600b6000396000f35b600056 + }.sign(&secret(), None); + + state.init_code(&Address::from_low_u64_be(0xa), FromHex::from_hex("60006000600060006000600b602b5a03f1").unwrap()).unwrap(); + state.init_code(&Address::from_low_u64_be(0xb), FromHex::from_hex("60006000600060006000600c602b5a03f1505b601256").unwrap()).unwrap(); + state.init_code(&Address::from_low_u64_be(0xc), FromHex::from_hex("6000").unwrap()).unwrap(); + state.add_balance(&t.sender(), &(100.into()), CleanupMode::NoEmpty).unwrap(); + let result = state.apply(&info, &machine, &t, true).unwrap(); + + let expected_trace = vec![FlatTrace { + trace_address: Default::default(), + subtraces: 1, + action: trace::Action::Call(trace::Call { + from: Address::from_str("9cce34f7ab185c7aba1b7c8140d620b4bda941d6").unwrap(), + to: Address::from_low_u64_be(0xa), + value: 100.into(), + gas: 79000.into(), + input: vec![], + call_type: CallType::Call, + }), + result: trace::Res::Call(trace::CallResult { + gas_used: U256::from(79_000), + output: vec![] + }) + }, FlatTrace { + trace_address: vec![0].into_iter().collect(), + subtraces: 1, + action: trace::Action::Call(trace::Call { + from: Address::from_low_u64_be(0xa), + to: Address::from_low_u64_be(0xb), + value: 0.into(), + gas: 78934.into(), + input: vec![], + call_type: CallType::Call, + }), + result: trace::Res::FailedCall(TraceError::OutOfGas), + }, FlatTrace { + trace_address: vec![0, 0].into_iter().collect(), + subtraces: 0, + action: trace::Action::Call(trace::Call { + from: Address::from_low_u64_be(0xb), + to: Address::from_low_u64_be(0xc), + value: 0.into(), + gas: 78868.into(), + call_type: CallType::Call, + input: vec![], + }), + result: trace::Res::Call(trace::CallResult { + gas_used: U256::from(3), + output: vec![] + }), + }]; + + assert_eq!(result.trace, expected_trace); + } + + #[test] + fn should_trace_suicide() { + let _ = env_logger::try_init(); + + let mut state = get_temp_state(); + + let mut info = EnvInfo::default(); + info.gas_limit = 1_000_000.into(); + let machine = make_frontier_machine(5); + + let t = Transaction { + nonce: 0.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Call(Address::from_low_u64_be(0xa)), + value: 100.into(), + data: vec![], + }.sign(&secret(), None); + + state.init_code(&Address::from_low_u64_be(0xa), FromHex::from_hex("73000000000000000000000000000000000000000bff").unwrap()).unwrap(); + state.add_balance(&Address::from_low_u64_be(0xa), &50.into(), CleanupMode::NoEmpty).unwrap(); + state.add_balance(&t.sender(), &100.into(), CleanupMode::NoEmpty).unwrap(); + let result = state.apply(&info, &machine, &t, true).unwrap(); + let expected_trace = vec![FlatTrace { + trace_address: Default::default(), + subtraces: 1, + action: trace::Action::Call(trace::Call { + from: Address::from_str("9cce34f7ab185c7aba1b7c8140d620b4bda941d6").unwrap(), + to: Address::from_low_u64_be(0xa), + value: 100.into(), + gas: 79000.into(), + input: vec![], + call_type: CallType::Call, + }), + result: trace::Res::Call(trace::CallResult { + gas_used: 3.into(), + output: vec![] + }), + }, FlatTrace { + trace_address: vec![0].into_iter().collect(), + subtraces: 0, + action: trace::Action::Suicide(trace::Suicide { + address: Address::from_low_u64_be(0xa), + refund_address: Address::from_low_u64_be(0xb), + balance: 150.into(), + }), + result: trace::Res::None, + }]; + + assert_eq!(result.trace, expected_trace); + } + + #[test] + fn code_from_database() { + let a = Address::zero(); + let (root, db) = { + let mut state = get_temp_state(); + state.require_or_from(&a, false, || Account::new_contract(42.into(), 0.into(), KECCAK_NULL_RLP), |_|{}).unwrap(); + state.init_code(&a, vec![1, 2, 3]).unwrap(); + assert_eq!(state.code(&a).unwrap(), Some(Arc::new(vec![1u8, 2, 3]))); + state.commit().unwrap(); + assert_eq!(state.code(&a).unwrap(), Some(Arc::new(vec![1u8, 2, 3]))); + state.drop() + }; + + let state = State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap(); + assert_eq!(state.code(&a).unwrap(), Some(Arc::new(vec![1u8, 2, 3]))); + } + + #[test] + fn storage_at_from_database() { + let a = Address::zero(); + let (root, db) = { + let mut state = get_temp_state(); + state.set_storage(&a, BigEndianHash::from_uint(&U256::from(1u64)), BigEndianHash::from_uint(&U256::from(69u64))).unwrap(); + state.commit().unwrap(); + state.drop() + }; + + let s = State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap(); + let h1 = BigEndianHash::from_uint(&U256::from(1u64)); + let h2 = BigEndianHash::from_uint(&U256::from(69u64)); + assert_eq!(s.storage_at(&a, &h1).unwrap(), h2); + } + + #[test] + fn get_from_database() { + let a = Address::zero(); + let (root, db) = { + let mut state = get_temp_state(); + state.inc_nonce(&a).unwrap(); + state.add_balance(&a, &U256::from(69u64), CleanupMode::NoEmpty).unwrap(); + state.commit().unwrap(); + assert_eq!(state.balance(&a).unwrap(), U256::from(69u64)); + state.drop() + }; + + let state = State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap(); + assert_eq!(state.balance(&a).unwrap(), U256::from(69u64)); + assert_eq!(state.nonce(&a).unwrap(), U256::from(1u64)); + } + + #[test] + fn remove() { + let a = Address::zero(); + let mut state = get_temp_state(); + assert_eq!(state.exists(&a).unwrap(), false); + assert_eq!(state.exists_and_not_null(&a).unwrap(), false); + state.inc_nonce(&a).unwrap(); + assert_eq!(state.exists(&a).unwrap(), true); + assert_eq!(state.exists_and_not_null(&a).unwrap(), true); + assert_eq!(state.nonce(&a).unwrap(), U256::from(1u64)); + state.kill_account(&a); + assert_eq!(state.exists(&a).unwrap(), false); + assert_eq!(state.exists_and_not_null(&a).unwrap(), false); + assert_eq!(state.nonce(&a).unwrap(), U256::from(0u64)); + } + + #[test] + fn empty_account_is_not_created() { + let a = Address::zero(); + let db = get_temp_state_db(); + let (root, db) = { + let mut state = State::new(db, U256::from(0), Default::default()); + state.add_balance(&a, &U256::default(), CleanupMode::NoEmpty).unwrap(); // create an empty account + state.commit().unwrap(); + state.drop() + }; + let state = State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap(); + assert!(!state.exists(&a).unwrap()); + assert!(!state.exists_and_not_null(&a).unwrap()); + } + + #[test] + fn empty_account_exists_when_creation_forced() { + let a = Address::zero(); + let db = get_temp_state_db(); + let (root, db) = { + let mut state = State::new(db, U256::from(0), Default::default()); + state.add_balance(&a, &U256::default(), CleanupMode::ForceCreate).unwrap(); // create an empty account + state.commit().unwrap(); + state.drop() + }; + let state = State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap(); + assert!(state.exists(&a).unwrap()); + assert!(!state.exists_and_not_null(&a).unwrap()); + } + + #[test] + fn remove_from_database() { + let a = Address::zero(); + let (root, db) = { + let mut state = get_temp_state(); + state.inc_nonce(&a).unwrap(); + state.commit().unwrap(); + assert_eq!(state.exists(&a).unwrap(), true); + assert_eq!(state.nonce(&a).unwrap(), U256::from(1u64)); + state.drop() + }; + + let (root, db) = { + let mut state = State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap(); + assert_eq!(state.exists(&a).unwrap(), true); + assert_eq!(state.nonce(&a).unwrap(), U256::from(1u64)); + state.kill_account(&a); + state.commit().unwrap(); + assert_eq!(state.exists(&a).unwrap(), false); + assert_eq!(state.nonce(&a).unwrap(), U256::from(0u64)); + state.drop() + }; + + let state = State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap(); + assert_eq!(state.exists(&a).unwrap(), false); + assert_eq!(state.nonce(&a).unwrap(), U256::from(0u64)); + } + + #[test] + fn alter_balance() { + let mut state = get_temp_state(); + let a = Address::zero(); + let b = Address::from_low_u64_be(1u64); + state.add_balance(&a, &U256::from(69u64), CleanupMode::NoEmpty).unwrap(); + assert_eq!(state.balance(&a).unwrap(), U256::from(69u64)); + state.commit().unwrap(); + assert_eq!(state.balance(&a).unwrap(), U256::from(69u64)); + state.sub_balance(&a, &U256::from(42u64), &mut CleanupMode::NoEmpty).unwrap(); + assert_eq!(state.balance(&a).unwrap(), U256::from(27u64)); + state.commit().unwrap(); + assert_eq!(state.balance(&a).unwrap(), U256::from(27u64)); + state.transfer_balance(&a, &b, &U256::from(18u64), CleanupMode::NoEmpty).unwrap(); + assert_eq!(state.balance(&a).unwrap(), U256::from(9u64)); + assert_eq!(state.balance(&b).unwrap(), U256::from(18u64)); + state.commit().unwrap(); + assert_eq!(state.balance(&a).unwrap(), U256::from(9u64)); + assert_eq!(state.balance(&b).unwrap(), U256::from(18u64)); + } + + #[test] + fn alter_nonce() { + let mut state = get_temp_state(); + let a = Address::zero(); + state.inc_nonce(&a).unwrap(); + assert_eq!(state.nonce(&a).unwrap(), U256::from(1u64)); + state.inc_nonce(&a).unwrap(); + assert_eq!(state.nonce(&a).unwrap(), U256::from(2u64)); + state.commit().unwrap(); + assert_eq!(state.nonce(&a).unwrap(), U256::from(2u64)); + state.inc_nonce(&a).unwrap(); + assert_eq!(state.nonce(&a).unwrap(), U256::from(3u64)); + state.commit().unwrap(); + assert_eq!(state.nonce(&a).unwrap(), U256::from(3u64)); + } + + #[test] + fn balance_nonce() { + let mut state = get_temp_state(); + let a = Address::zero(); + assert_eq!(state.balance(&a).unwrap(), U256::from(0u64)); + assert_eq!(state.nonce(&a).unwrap(), U256::from(0u64)); + state.commit().unwrap(); + assert_eq!(state.balance(&a).unwrap(), U256::from(0u64)); + assert_eq!(state.nonce(&a).unwrap(), U256::from(0u64)); + } + + #[test] + fn ensure_cached() { + let mut state = get_temp_state(); + let a = Address::zero(); + state.require(&a, false).unwrap(); + state.commit().unwrap(); + assert_eq!(*state.root(), H256::from_str("0ce23f3c809de377b008a4a3ee94a0834aac8bec1f86e28ffe4fdb5a15b0c785").unwrap()); + } + + #[test] + fn checkpoint_basic() { + let mut state = get_temp_state(); + let a = Address::zero(); + state.checkpoint(); + state.add_balance(&a, &U256::from(69u64), CleanupMode::NoEmpty).unwrap(); + assert_eq!(state.balance(&a).unwrap(), U256::from(69u64)); + state.discard_checkpoint(); + assert_eq!(state.balance(&a).unwrap(), U256::from(69u64)); + state.checkpoint(); + state.add_balance(&a, &U256::from(1u64), CleanupMode::NoEmpty).unwrap(); + assert_eq!(state.balance(&a).unwrap(), U256::from(70u64)); + state.revert_to_checkpoint(); + assert_eq!(state.balance(&a).unwrap(), U256::from(69u64)); + } + + #[test] + fn checkpoint_nested() { + let mut state = get_temp_state(); + let a = Address::zero(); + state.checkpoint(); + state.checkpoint(); + state.add_balance(&a, &U256::from(69u64), CleanupMode::NoEmpty).unwrap(); + assert_eq!(state.balance(&a).unwrap(), U256::from(69u64)); + state.discard_checkpoint(); + assert_eq!(state.balance(&a).unwrap(), U256::from(69u64)); + state.revert_to_checkpoint(); + assert_eq!(state.balance(&a).unwrap(), U256::from(0)); + } + + #[test] + fn checkpoint_revert_to_get_storage_at() { + let mut state = get_temp_state(); + let a = Address::zero(); + let k = BigEndianHash::from_uint(&U256::from(0)); + + let c0 = state.checkpoint(); + let c1 = state.checkpoint(); + state.set_storage(&a, k, BigEndianHash::from_uint(&U256::from(1))).unwrap(); + + assert_eq!(state.checkpoint_storage_at(c0, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(0)))); + assert_eq!(state.checkpoint_storage_at(c1, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(0)))); + assert_eq!(state.storage_at(&a, &k).unwrap(), BigEndianHash::from_uint(&U256::from(1))); + + state.revert_to_checkpoint(); // Revert to c1. + assert_eq!(state.checkpoint_storage_at(c0, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(0)))); + assert_eq!(state.storage_at(&a, &k).unwrap(), BigEndianHash::from_uint(&U256::from(0))); + } + + #[test] + fn checkpoint_from_empty_get_storage_at() { + let mut state = get_temp_state(); + let a = Address::zero(); + let k = BigEndianHash::from_uint(&U256::from(0)); + let k2 = BigEndianHash::from_uint(&U256::from(1)); + + assert_eq!(state.storage_at(&a, &k).unwrap(), BigEndianHash::from_uint(&U256::from(0))); + state.clear(); + + let c0 = state.checkpoint(); + state.new_contract(&a, U256::zero(), U256::zero()).unwrap(); + let c1 = state.checkpoint(); + state.set_storage(&a, k, BigEndianHash::from_uint(&U256::from(1))).unwrap(); + let c2 = state.checkpoint(); + let c3 = state.checkpoint(); + state.set_storage(&a, k2, BigEndianHash::from_uint(&U256::from(3))).unwrap(); + state.set_storage(&a, k, BigEndianHash::from_uint(&U256::from(3))).unwrap(); + let c4 = state.checkpoint(); + state.set_storage(&a, k, BigEndianHash::from_uint(&U256::from(4))).unwrap(); + let c5 = state.checkpoint(); + + assert_eq!(state.checkpoint_storage_at(c0, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(0)))); + assert_eq!(state.checkpoint_storage_at(c1, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(0)))); + assert_eq!(state.checkpoint_storage_at(c2, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(1)))); + assert_eq!(state.checkpoint_storage_at(c3, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(1)))); + assert_eq!(state.checkpoint_storage_at(c4, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(3)))); + assert_eq!(state.checkpoint_storage_at(c5, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(4)))); + + state.discard_checkpoint(); // Commit/discard c5. + assert_eq!(state.checkpoint_storage_at(c0, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(0)))); + assert_eq!(state.checkpoint_storage_at(c1, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(0)))); + assert_eq!(state.checkpoint_storage_at(c2, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(1)))); + assert_eq!(state.checkpoint_storage_at(c3, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(1)))); + assert_eq!(state.checkpoint_storage_at(c4, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(3)))); + + state.revert_to_checkpoint(); // Revert to c4. + assert_eq!(state.checkpoint_storage_at(c0, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(0)))); + assert_eq!(state.checkpoint_storage_at(c1, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(0)))); + assert_eq!(state.checkpoint_storage_at(c2, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(1)))); + assert_eq!(state.checkpoint_storage_at(c3, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(1)))); + + state.discard_checkpoint(); // Commit/discard c3. + assert_eq!(state.checkpoint_storage_at(c0, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(0)))); + assert_eq!(state.checkpoint_storage_at(c1, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(0)))); + assert_eq!(state.checkpoint_storage_at(c2, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(1)))); + + state.revert_to_checkpoint(); // Revert to c2. + assert_eq!(state.checkpoint_storage_at(c0, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(0)))); + assert_eq!(state.checkpoint_storage_at(c1, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(0)))); + + state.discard_checkpoint(); // Commit/discard c1. + assert_eq!(state.checkpoint_storage_at(c0, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(0)))); + } + + #[test] + fn checkpoint_get_storage_at() { + let mut state = get_temp_state(); + let a = Address::zero(); + let k = BigEndianHash::from_uint(&U256::from(0)); + let k2 = BigEndianHash::from_uint(&U256::from(1)); + + state.set_storage(&a, k, BigEndianHash::from_uint(&U256::from(0xffff))).unwrap(); + state.commit().unwrap(); + state.clear(); + + assert_eq!(state.storage_at(&a, &k).unwrap(), BigEndianHash::from_uint(&U256::from(0xffff))); + state.clear(); + + let cm1 = state.checkpoint(); + let c0 = state.checkpoint(); + state.new_contract(&a, U256::zero(), U256::zero()).unwrap(); + let c1 = state.checkpoint(); + state.set_storage(&a, k, BigEndianHash::from_uint(&U256::from(1))).unwrap(); + let c2 = state.checkpoint(); + let c3 = state.checkpoint(); + state.set_storage(&a, k2, BigEndianHash::from_uint(&U256::from(3))).unwrap(); + state.set_storage(&a, k, BigEndianHash::from_uint(&U256::from(3))).unwrap(); + let c4 = state.checkpoint(); + state.set_storage(&a, k, BigEndianHash::from_uint(&U256::from(4))).unwrap(); + let c5 = state.checkpoint(); + + assert_eq!(state.checkpoint_storage_at(cm1, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(0xffff)))); + assert_eq!(state.checkpoint_storage_at(c0, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(0xffff)))); + assert_eq!(state.checkpoint_storage_at(c1, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(0)))); + assert_eq!(state.checkpoint_storage_at(c2, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(1)))); + assert_eq!(state.checkpoint_storage_at(c3, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(1)))); + assert_eq!(state.checkpoint_storage_at(c4, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(3)))); + assert_eq!(state.checkpoint_storage_at(c5, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(4)))); + + state.discard_checkpoint(); // Commit/discard c5. + assert_eq!(state.checkpoint_storage_at(cm1, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(0xffff)))); + assert_eq!(state.checkpoint_storage_at(c0, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(0xffff)))); + assert_eq!(state.checkpoint_storage_at(c1, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(0)))); + assert_eq!(state.checkpoint_storage_at(c2, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(1)))); + assert_eq!(state.checkpoint_storage_at(c3, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(1)))); + assert_eq!(state.checkpoint_storage_at(c4, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(3)))); + + state.revert_to_checkpoint(); // Revert to c4. + assert_eq!(state.checkpoint_storage_at(cm1, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(0xffff)))); + assert_eq!(state.checkpoint_storage_at(c0, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(0xffff)))); + assert_eq!(state.checkpoint_storage_at(c1, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(0)))); + assert_eq!(state.checkpoint_storage_at(c2, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(1)))); + assert_eq!(state.checkpoint_storage_at(c3, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(1)))); + + state.discard_checkpoint(); // Commit/discard c3. + assert_eq!(state.checkpoint_storage_at(cm1, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(0xffff)))); + assert_eq!(state.checkpoint_storage_at(c0, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(0xffff)))); + assert_eq!(state.checkpoint_storage_at(c1, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(0)))); + assert_eq!(state.checkpoint_storage_at(c2, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(1)))); + + state.revert_to_checkpoint(); // Revert to c2. + assert_eq!(state.checkpoint_storage_at(cm1, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(0xffff)))); + assert_eq!(state.checkpoint_storage_at(c0, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(0xffff)))); + assert_eq!(state.checkpoint_storage_at(c1, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(0)))); + + state.discard_checkpoint(); // Commit/discard c1. + assert_eq!(state.checkpoint_storage_at(cm1, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(0xffff)))); + assert_eq!(state.checkpoint_storage_at(c0, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(0xffff)))); + } + + #[test] + fn kill_account_with_checkpoints() { + let mut state = get_temp_state(); + let a = Address::zero(); + let k = BigEndianHash::from_uint(&U256::from(0)); + state.checkpoint(); + state.set_storage(&a, k, BigEndianHash::from_uint(&U256::from(1))).unwrap(); + state.checkpoint(); + state.kill_account(&a); + + assert_eq!(state.storage_at(&a, &k).unwrap(), BigEndianHash::from_uint(&U256::from(0))); + state.revert_to_checkpoint(); + assert_eq!(state.storage_at(&a, &k).unwrap(), BigEndianHash::from_uint(&U256::from(1))); + } + + #[test] + fn create_contract_fail() { + let mut state = get_temp_state(); + let orig_root = state.root().clone(); + let a = Address::from_low_u64_be(1000); + + state.checkpoint(); // c1 + state.new_contract(&a, U256::zero(), U256::zero()).unwrap(); + state.add_balance(&a, &U256::from(1), CleanupMode::ForceCreate).unwrap(); + state.checkpoint(); // c2 + state.add_balance(&a, &U256::from(1), CleanupMode::ForceCreate).unwrap(); + state.discard_checkpoint(); // discard c2 + state.revert_to_checkpoint(); // revert to c1 + assert_eq!(state.exists(&a).unwrap(), false); + + state.commit().unwrap(); + assert_eq!(orig_root, state.root().clone()); + } + + #[test] + fn create_contract_fail_previous_storage() { + let mut state = get_temp_state(); + let a = Address::from_low_u64_be(1000); + let k = BigEndianHash::from_uint(&U256::from(0)); + + state.set_storage(&a, k, BigEndianHash::from_uint(&U256::from(0xffff))).unwrap(); + state.commit().unwrap(); + state.clear(); + + let orig_root = state.root().clone(); + assert_eq!(state.storage_at(&a, &k).unwrap(), BigEndianHash::from_uint(&U256::from(0xffff))); + state.clear(); + + state.checkpoint(); // c1 + state.new_contract(&a, U256::zero(), U256::zero()).unwrap(); + state.checkpoint(); // c2 + state.set_storage(&a, k, BigEndianHash::from_uint(&U256::from(2))).unwrap(); + state.revert_to_checkpoint(); // revert to c2 + assert_eq!(state.storage_at(&a, &k).unwrap(), BigEndianHash::from_uint(&U256::from(0))); + state.revert_to_checkpoint(); // revert to c1 + assert_eq!(state.storage_at(&a, &k).unwrap(), BigEndianHash::from_uint(&U256::from(0xffff))); + + state.commit().unwrap(); + assert_eq!(orig_root, state.root().clone()); + } + + #[test] + fn create_empty() { + let mut state = get_temp_state(); + state.commit().unwrap(); + assert_eq!(*state.root(), H256::from_str("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421").unwrap()); + } + + #[test] + fn should_not_panic_on_state_diff_with_storage() { + let mut state = get_temp_state(); + let a = Address::from_low_u64_be(0xa); + state.init_code(&a, b"abcdefg".to_vec()).unwrap();; + state.add_balance(&a, &256.into(), CleanupMode::NoEmpty).unwrap(); + state.set_storage(&a, H256::from_low_u64_be(0xb), H256::from_low_u64_be(0xc).into()).unwrap(); + + let mut new_state = state.clone(); + new_state.set_storage(&a, H256::from_low_u64_be(0xb), H256::from_low_u64_be(0xd).into()).unwrap(); + + new_state.diff_from(state).unwrap(); + } + + #[test] + fn should_kill_garbage() { + let a = Address::from_low_u64_be(10); + let b = Address::from_low_u64_be(20); + let c = Address::from_low_u64_be(30); + let d = Address::from_low_u64_be(40); + let e = Address::from_low_u64_be(50); + let x = Address::from_low_u64_be(0); + let db = get_temp_state_db(); + let (root, db) = { + let mut state = State::new(db, U256::from(0), Default::default()); + state.add_balance(&a, &U256::default(), CleanupMode::ForceCreate).unwrap(); // create an empty account + state.add_balance(&b, &100.into(), CleanupMode::ForceCreate).unwrap(); // create a dust account + state.add_balance(&c, &101.into(), CleanupMode::ForceCreate).unwrap(); // create a normal account + state.add_balance(&d, &99.into(), CleanupMode::ForceCreate).unwrap(); // create another dust account + state.new_contract(&e, 100.into(), 1.into()).unwrap(); // create a contract account + state.init_code(&e, vec![0x00]).unwrap(); + state.commit().unwrap(); + state.drop() + }; + + let mut state = State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap(); + let mut touched = HashSet::new(); + state.add_balance(&a, &U256::default(), CleanupMode::TrackTouched(&mut touched)).unwrap(); // touch an account + state.transfer_balance(&b, &x, &1.into(), CleanupMode::TrackTouched(&mut touched)).unwrap(); // touch an account decreasing its balance + state.transfer_balance(&c, &x, &1.into(), CleanupMode::TrackTouched(&mut touched)).unwrap(); // touch an account decreasing its balance + state.transfer_balance(&e, &x, &1.into(), CleanupMode::TrackTouched(&mut touched)).unwrap(); // touch an account decreasing its balance + state.kill_garbage(&touched, true, &None, false).unwrap(); + assert!(!state.exists(&a).unwrap()); + assert!(state.exists(&b).unwrap()); + state.kill_garbage(&touched, true, &Some(100.into()), false).unwrap(); + assert!(!state.exists(&b).unwrap()); + assert!(state.exists(&c).unwrap()); + assert!(state.exists(&d).unwrap()); + assert!(state.exists(&e).unwrap()); + state.kill_garbage(&touched, true, &Some(100.into()), true).unwrap(); + assert!(state.exists(&c).unwrap()); + assert!(state.exists(&d).unwrap()); + assert!(!state.exists(&e).unwrap()); + } + + #[test] + fn should_trace_diff_suicided_accounts() { + let a = Address::from_low_u64_be(10); + let db = get_temp_state_db(); + let (root, db) = { + let mut state = State::new(db, U256::from(0), Default::default()); + state.add_balance(&a, &100.into(), CleanupMode::ForceCreate).unwrap(); + state.commit().unwrap(); + state.drop() + }; + + let mut state = State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap(); + let original = state.clone(); + state.kill_account(&a); + + let diff = state.diff_from(original).unwrap(); + let diff_map = diff.raw; + assert_eq!(diff_map.len(), 1); + assert!(diff_map.get(&a).is_some()); + assert_eq!(diff_map.get(&a), + pod::account::diff_pod( + Some(&PodAccount { + balance: U256::from(100), + nonce: U256::zero(), + code: Some(Default::default()), + storage: Default::default() + }), None + ).as_ref()); + } + + #[test] + fn should_trace_diff_unmodified_storage() { + let a = Address::from_low_u64_be(10); + let db = get_temp_state_db(); + + let (root, db) = { + let mut state = State::new(db, U256::from(0), Default::default()); + state.set_storage(&a, BigEndianHash::from_uint(&U256::from(1u64)), BigEndianHash::from_uint(&U256::from(20u64))).unwrap(); + state.commit().unwrap(); + state.drop() + }; + + let mut state = State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap(); + let original = state.clone(); + state.set_storage(&a, BigEndianHash::from_uint(&U256::from(1u64)), BigEndianHash::from_uint(&U256::from(100u64))).unwrap(); + + let diff = state.diff_from(original).unwrap(); + let diff_map = diff.raw; + assert_eq!(diff_map.len(), 1); + assert!(diff_map.get(&a).is_some()); + assert_eq!(diff_map.get(&a), + pod::account::diff_pod( + Some(&PodAccount { + balance: U256::zero(), + nonce: U256::zero(), + code: Some(Default::default()), + storage: vec![(BigEndianHash::from_uint(&U256::from(1u64)), BigEndianHash::from_uint(&U256::from(20u64)))].into_iter().collect(), + }), + Some(&PodAccount { + balance: U256::zero(), + nonce: U256::zero(), + code: Some(Default::default()), + storage: vec![(BigEndianHash::from_uint(&U256::from(1u64)), BigEndianHash::from_uint(&U256::from(100u64)))].into_iter().collect(), + })).as_ref()); + } + + #[cfg(feature="to-pod-full")] + #[test] + fn should_get_full_pod_storage_values() { + use trie::{TrieFactory, TrieSpec}; + + let a = Address::from_low_u64_be(10); + let db = get_temp_state_db(); + + let factories = Factories { + vm: Default::default(), + trie: TrieFactory::new(TrieSpec::Fat), + accountdb: Default::default(), + }; + + let get_pod_state_val = |pod_state : &PodState, ak, k| { + pod_state.get().get(ak).unwrap().storage.get(&k).unwrap().clone() + }; + + let storage_address: H256 = BigEndianHash::from_uint(&U256::from(1u64)); + + let (root, db) = { + let mut state = State::new(db, U256::from(0), factories.clone()); + state.set_storage(&a, storage_address.clone(), BigEndianHash::from_uint(&U256::from(20u64))).unwrap(); + let dump = state.to_pod_full().unwrap(); + assert_eq!(get_pod_state_val(&dump, &a, storage_address.clone()), BigEndianHash::from_uint(&U256::from(20u64))); + state.commit().unwrap(); + let dump = state.to_pod_full().unwrap(); + assert_eq!(get_pod_state_val(&dump, &a, storage_address.clone()), BigEndianHash::from_uint(&U256::from(20u64))); + state.drop() + }; + + let mut state = State::from_existing(db, root, U256::from(0u8), factories).unwrap(); + let dump = state.to_pod_full().unwrap(); + assert_eq!(get_pod_state_val(&dump, &a, storage_address.clone()), BigEndianHash::from_uint(&U256::from(20u64))); + state.set_storage(&a, storage_address.clone(), BigEndianHash::from_uint(&U256::from(21u64))).unwrap(); + let dump = state.to_pod_full().unwrap(); + assert_eq!(get_pod_state_val(&dump, &a, storage_address.clone()), BigEndianHash::from_uint(&U256::from(21u64))); + state.commit().unwrap(); + state.set_storage(&a, storage_address.clone(), BigEndianHash::from_uint(&U256::from(0u64))).unwrap(); + let dump = state.to_pod_full().unwrap(); + assert_eq!(get_pod_state_val(&dump, &a, storage_address.clone()), BigEndianHash::from_uint(&U256::from(0u64))); + } +} diff --git a/ethcore/state-account/Cargo.toml b/ethcore/state-account/Cargo.toml index 4ad59cfeb0c..0dc78660d38 100644 --- a/ethcore/state-account/Cargo.toml +++ b/ethcore/state-account/Cargo.toml @@ -36,3 +36,9 @@ account-db = { path = "../account-db" } rlp_compress = { path = "../../util/rlp-compress" } journaldb = { path = "../../util/journaldb" } parity-bytes = "0.1.0" +ethkey = { path = "../../accounts/ethkey" } +trace = { path = "../trace" } +pod = { path = "../pod" } +ethcore = { path = "..", features = ["test-helpers"]} +rustc-hex = "2.0.1" +env_logger = "0.5" diff --git a/ethcore/state-account/src/state.rs b/ethcore/state-account/src/state.rs index 2b46560570a..0be89c24d73 100644 --- a/ethcore/state-account/src/state.rs +++ b/ethcore/state-account/src/state.rs @@ -19,6 +19,8 @@ //! Unconfirmed sub-states are managed with `checkpoint`s which may be canonicalized //! or rolled back. +// NOTE: state tests are found in ethcore/src/executive_state.rs + use std::{ cell::{RefCell, RefMut}, collections::{BTreeMap, BTreeSet, HashMap, HashSet}, @@ -394,8 +396,7 @@ impl State { let original_storage_root = self.original_storage_root(contract)?; let (nonce, overflow) = self.account_start_nonce.overflowing_add(nonce_offset); if overflow { - return Err(Box::new(TrieError::DecoderError(H256::from(*contract), - rlp::DecoderError::Custom("Nonce overflow".into())))); + return Err(Box::new(TrieError::DecoderError(H256::from(*contract), rlp::DecoderError::Custom("Nonce overflow".into())))); } self.insert_cache(contract, AccountEntry::new_dirty(Some(Account::new_contract(balance, nonce, original_storage_root)))); Ok(()) @@ -421,38 +422,38 @@ impl State { /// Determine whether an account exists and has code or non-zero nonce. pub fn exists_and_has_code_or_nonce(&self, a: &Address) -> TrieResult { self.ensure_cached(a, RequireCache::CodeSize, false, - |a| a.map_or(false, |a| a.code_hash() != KECCAK_EMPTY || *a.nonce() != self.account_start_nonce)) + |a| a.map_or(false, |a| a.code_hash() != KECCAK_EMPTY || *a.nonce() != self.account_start_nonce)) } /// Get the balance of account `a`. pub fn balance(&self, a: &Address) -> TrieResult { self.ensure_cached(a, RequireCache::None, true, - |a| a.as_ref().map_or(U256::zero(), |account| *account.balance())) + |a| a.as_ref().map_or(U256::zero(), |account| *account.balance())) } /// Get the nonce of account `a`. pub fn nonce(&self, a: &Address) -> TrieResult { self.ensure_cached(a, RequireCache::None, true, - |a| a.as_ref().map_or(self.account_start_nonce, |account| *account.nonce())) + |a| a.as_ref().map_or(self.account_start_nonce, |account| *account.nonce())) } /// Whether the base storage root of an account remains unchanged. pub fn is_base_storage_root_unchanged(&self, a: &Address) -> TrieResult { Ok(self.ensure_cached(a, RequireCache::None, true, - |a| a.as_ref().map(|account| account.is_base_storage_root_unchanged()))? + |a| a.as_ref().map(|account| account.is_base_storage_root_unchanged()))? .unwrap_or(true)) } /// Get the storage root of account `a`. pub fn storage_root(&self, a: &Address) -> TrieResult> { self.ensure_cached(a, RequireCache::None, true, - |a| a.as_ref().and_then(|account| account.storage_root())) + |a| a.as_ref().and_then(|account| account.storage_root())) } /// Get the original storage root since last commit of account `a`. pub fn original_storage_root(&self, a: &Address) -> TrieResult { Ok(self.ensure_cached(a, RequireCache::None, true, - |a| a.as_ref().map(|account| account.original_storage_root()))? + |a| a.as_ref().map(|account| account.original_storage_root()))? .unwrap_or(KECCAK_NULL_RLP)) } @@ -567,7 +568,7 @@ impl State { return res; } - // otherwise cache the account localy and cache storage key there. + // otherwise cache the account locally and cache storage key there. if let Some(ref mut acc) = local_account { if let Some(ref account) = acc.account { let account_db = self.factories.accountdb.readonly(self.db.as_hash_db(), account.address_hash(address)); @@ -617,19 +618,19 @@ impl State { /// Get accounts' code. pub fn code(&self, a: &Address) -> TrieResult>> { self.ensure_cached(a, RequireCache::Code, true, - |a| a.as_ref().map_or(None, |a| a.code().clone())) + |a| a.as_ref().map_or(None, |a| a.code().clone())) } /// Get an account's code hash. pub fn code_hash(&self, a: &Address) -> TrieResult> { self.ensure_cached(a, RequireCache::None, true, - |a| a.as_ref().map(|a| a.code_hash())) + |a| a.as_ref().map(|a| a.code_hash())) } /// Get accounts' code size. pub fn code_size(&self, a: &Address) -> TrieResult> { self.ensure_cached(a, RequireCache::CodeSize, true, - |a| a.as_ref().and_then(|a| a.code_size())) + |a| a.as_ref().and_then(|a| a.code_size())) } /// Add `incr` to the balance of account `a`. @@ -1010,13 +1011,13 @@ impl State { } /// Pull account `a` in our cache from the trie DB. `require_code` requires that the code be cached, too. - fn require<'a>(&'a self, a: &Address, require_code: bool) -> TrieResult> { + pub fn require<'a>(&'a self, a: &Address, require_code: bool) -> TrieResult> { self.require_or_from(a, require_code, || Account::new_basic(0u8.into(), self.account_start_nonce), |_| {}) } /// Pull account `a` in our cache from the trie DB. `require_code` requires that the code be cached, too. /// If it doesn't exist, make account equal the evaluation of `default`. - fn require_or_from<'a, F, G>(&'a self, a: &Address, require_code: bool, default: F, not_default: G) -> TrieResult> + pub fn require_or_from<'a, F, G>(&'a self, a: &Address, require_code: bool, default: F, not_default: G) -> TrieResult> where F: FnOnce() -> Account, G: FnOnce(&mut Account), { let contains_key = self.cache.borrow().contains_key(a); @@ -1156,5 +1157,3 @@ impl Clone for State { } } } - -// TODO tests and whatnot From 06a37ba52283afb29d9b1792f2ce3d3717cb3911 Mon Sep 17 00:00:00 2001 From: David Palm Date: Fri, 5 Jul 2019 22:47:31 +0200 Subject: [PATCH 39/87] remove ethcore/state/mod.rs --- ethcore/src/state/mod.rs | 2620 -------------------------------------- 1 file changed, 2620 deletions(-) delete mode 100644 ethcore/src/state/mod.rs diff --git a/ethcore/src/state/mod.rs b/ethcore/src/state/mod.rs deleted file mode 100644 index 5705e89f3fd..00000000000 --- a/ethcore/src/state/mod.rs +++ /dev/null @@ -1,2620 +0,0 @@ -// 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 mutable state representation suitable to execute transactions. -//! Generic over a `Backend`. Deals with `Account`s. -//! Unconfirmed sub-states are managed with `checkpoint`s which may be canonicalized -//! or rolled back. - -use std::cell::{RefCell, RefMut}; -use std::collections::hash_map::Entry; -use std::collections::{HashMap, BTreeMap, BTreeSet, HashSet}; -use std::fmt; -use std::sync::Arc; -use hash::{KECCAK_NULL_RLP, KECCAK_EMPTY}; - -use bytes::Bytes; -use error::Error; -use ethereum_types::{H256, U256, Address}; -use ethtrie::{TrieDB, Result as TrieResult}; -use executed::{Executed, ExecutionError}; -use factories::{Factories, VmFactory}; -use hash_db::HashDB; -use keccak_hasher::KeccakHasher; -use kvdb::DBValue; -use pod::{self, PodAccount, PodState}; -use state_db::StateDB; -use trie::{Trie, TrieError, Recorder}; -use types::{ - basic_account::BasicAccount, - receipt::Receipt, - state_diff::StateDiff, -}; - -use state_account::{ - account::Account, - backend::{self, Backend}, - substate::{CleanupMode, Substate}, -}; - -/// Used to return information about an `State::apply` operation. -pub struct ApplyOutcome { - /// The receipt for the applied transaction. - pub receipt: Receipt, - /// The output of the applied transaction. - pub output: Bytes, - /// The trace for the applied transaction, empty if tracing was not produced. - pub trace: Vec, - /// The VM trace for the applied transaction, None if tracing was not produced. - pub vm_trace: Option -} - -/// Result type for the execution ("application") of a transaction. -pub type ApplyResult = Result, Error>; - -/// Return type of proof validity check. -#[derive(Debug, Clone)] -pub enum ProvedExecution { - /// Proof wasn't enough to complete execution. - BadProof, - /// The transaction failed, but not due to a bad proof. - Failed(ExecutionError), - /// The transaction successfully completed with the given proof. - Complete(Box), -} - -#[derive(Eq, PartialEq, Clone, Copy, Debug)] -/// Account modification state. Used to check if the account was -/// Modified in between commits and overall. -enum AccountState { - /// Account was loaded from disk and never modified in this state object. - CleanFresh, - /// Account was loaded from the global cache and never modified. - CleanCached, - /// Account has been modified and is not committed to the trie yet. - /// This is set if any of the account data is changed, including - /// storage and code. - Dirty, - /// Account was modified and committed to the trie. - Committed, -} - -#[derive(Debug)] -/// In-memory copy of the account data. Holds the optional account -/// and the modification status. -/// Account entry can contain existing (`Some`) or non-existing -/// account (`None`) -struct AccountEntry { - /// Account entry. `None` if account known to be non-existant. - account: Option, - /// Unmodified account balance. - old_balance: Option, - /// Entry state. - state: AccountState, -} - -// Account cache item. Contains account data and -// modification state -impl AccountEntry { - fn is_dirty(&self) -> bool { - self.state == AccountState::Dirty - } - - fn exists_and_is_null(&self) -> bool { - self.account.as_ref().map_or(false, |a| a.is_null()) - } - - /// Clone dirty data into new `AccountEntry`. This includes - /// basic account data and modified storage keys. - /// Returns None if clean. - fn clone_if_dirty(&self) -> Option { - match self.is_dirty() { - true => Some(self.clone_dirty()), - false => None, - } - } - - /// Clone dirty data into new `AccountEntry`. This includes - /// basic account data and modified storage keys. - fn clone_dirty(&self) -> AccountEntry { - AccountEntry { - old_balance: self.old_balance, - account: self.account.as_ref().map(Account::clone_dirty), - state: self.state, - } - } - - // Create a new account entry and mark it as dirty. - fn new_dirty(account: Option) -> AccountEntry { - AccountEntry { - old_balance: account.as_ref().map(|a| a.balance().clone()), - account: account, - state: AccountState::Dirty, - } - } - - // Create a new account entry and mark it as clean. - fn new_clean(account: Option) -> AccountEntry { - AccountEntry { - old_balance: account.as_ref().map(|a| a.balance().clone()), - account: account, - state: AccountState::CleanFresh, - } - } - - // Create a new account entry and mark it as clean and cached. - fn new_clean_cached(account: Option) -> AccountEntry { - AccountEntry { - old_balance: account.as_ref().map(|a| a.balance().clone()), - account: account, - state: AccountState::CleanCached, - } - } - - // Replace data with another entry but preserve storage cache. - fn overwrite_with(&mut self, other: AccountEntry) { - self.state = other.state; - match other.account { - Some(acc) => { - if let Some(ref mut ours) = self.account { - ours.overwrite_with(acc); - } else { - self.account = Some(acc); - } - }, - None => self.account = None, - } - } -} - -/// Representation of the entire state of all accounts in the system. -/// -/// `State` can work together with `StateDB` to share account cache. -/// -/// Local cache contains changes made locally and changes accumulated -/// locally from previous commits. Global cache reflects the database -/// state and never contains any changes. -/// -/// Cache items contains account data, or the flag that account does not exist -/// and modification state (see `AccountState`) -/// -/// Account data can be in the following cache states: -/// * In global but not local - something that was queried from the database, -/// but never modified -/// * In local but not global - something that was just added (e.g. new account) -/// * In both with the same value - something that was changed to a new value, -/// but changed back to a previous block in the same block (same State instance) -/// * In both with different values - something that was overwritten with a -/// new value. -/// -/// All read-only state queries check local cache/modifications first, -/// then global state cache. If data is not found in any of the caches -/// it is loaded from the DB to the local cache. -/// -/// **** IMPORTANT ************************************************************* -/// All the modifications to the account data must set the `Dirty` state in the -/// `AccountEntry`. This is done in `require` and `require_or_from`. So just -/// use that. -/// **************************************************************************** -/// -/// Upon destruction all the local cache data propagated into the global cache. -/// Propagated items might be rejected if current state is non-canonical. -/// -/// State checkpointing. -/// -/// A new checkpoint can be created with `checkpoint()`. checkpoints can be -/// created in a hierarchy. -/// When a checkpoint is active all changes are applied directly into -/// `cache` and the original value is copied into an active checkpoint. -/// Reverting a checkpoint with `revert_to_checkpoint` involves copying -/// original values from the latest checkpoint back into `cache`. The code -/// takes care not to overwrite cached storage while doing that. -/// A checkpoint can be discarded with `discard_checkpoint`. All of the original -/// backed-up values are moved into a parent checkpoint (if any). -/// -pub struct State { - db: B, - root: H256, - cache: RefCell>, - // The original account is preserved in - checkpoints: RefCell>>>, - account_start_nonce: U256, - factories: Factories, -} - -#[derive(Copy, Clone)] -enum RequireCache { - None, - CodeSize, - Code, -} - -// TODO: put this back -///// Mode of dealing with null accounts. -//#[derive(PartialEq)] -//pub enum CleanupMode<'a> { -// /// Create accounts which would be null. -// ForceCreate, -// /// Don't delete null accounts upon touching, but also don't create them. -// NoEmpty, -// /// Mark all touched accounts. -// TrackTouched(&'a mut HashSet
), -//} - -/// Provides subset of `State` methods to query state information -pub trait StateInfo { - /// Get the nonce of account `a`. - fn nonce(&self, a: &Address) -> TrieResult; - - /// Get the balance of account `a`. - fn balance(&self, a: &Address) -> TrieResult; - - /// Mutate storage of account `address` so that it is `value` for `key`. - fn storage_at(&self, address: &Address, key: &H256) -> TrieResult; - - /// Get accounts' code. - fn code(&self, a: &Address) -> TrieResult>>; -} - -impl StateInfo for State { - fn nonce(&self, a: &Address) -> TrieResult { State::nonce(self, a) } - fn balance(&self, a: &Address) -> TrieResult { State::balance(self, a) } - fn storage_at(&self, address: &Address, key: &H256) -> TrieResult { State::storage_at(self, address, key) } - fn code(&self, address: &Address) -> TrieResult>> { State::code(self, address) } -} - -const SEC_TRIE_DB_UNWRAP_STR: &'static str = "A state can only be created with valid root. Creating a SecTrieDB with a valid root will not fail. \ - Therefore creating a SecTrieDB with this state's root will not fail."; - -impl State { - /// Creates new state with empty state root - /// Used for tests. - pub fn new(mut db: B, account_start_nonce: U256, factories: Factories) -> State { - let mut root = H256::zero(); - { - // init trie and reset root to null - let _ = factories.trie.create(db.as_hash_db_mut(), &mut root); - } - - State { - db: db, - root: root, - cache: RefCell::new(HashMap::new()), - checkpoints: RefCell::new(Vec::new()), - account_start_nonce: account_start_nonce, - factories: factories, - } - } - - /// Creates new state with existing state root - pub fn from_existing(db: B, root: H256, account_start_nonce: U256, factories: Factories) -> TrieResult> { - if !db.as_hash_db().contains(&root, hash_db::EMPTY_PREFIX) { - return Err(Box::new(TrieError::InvalidStateRoot(root))); - } - - let state = State { - db: db, - root: root, - cache: RefCell::new(HashMap::new()), - checkpoints: RefCell::new(Vec::new()), - account_start_nonce: account_start_nonce, - factories: factories - }; - - Ok(state) - } - - /// Get a VM factory that can execute on this state. - pub fn vm_factory(&self) -> VmFactory { - self.factories.vm.clone() - } - - /// Create a recoverable checkpoint of this state. Return the checkpoint index. - pub fn checkpoint(&mut self) -> usize { - let checkpoints = self.checkpoints.get_mut(); - let index = checkpoints.len(); - checkpoints.push(HashMap::new()); - index - } - - /// Merge last checkpoint with previous. - pub fn discard_checkpoint(&mut self) { - // merge with previous checkpoint - let last = self.checkpoints.get_mut().pop(); - if let Some(mut checkpoint) = last { - if let Some(ref mut prev) = self.checkpoints.get_mut().last_mut() { - if prev.is_empty() { - **prev = checkpoint; - } else { - for (k, v) in checkpoint.drain() { - prev.entry(k).or_insert(v); - } - } - } - } - } - - /// Revert to the last checkpoint and discard it. - pub fn revert_to_checkpoint(&mut self) { - if let Some(mut checkpoint) = self.checkpoints.get_mut().pop() { - for (k, v) in checkpoint.drain() { - match v { - Some(v) => { - match self.cache.get_mut().entry(k) { - Entry::Occupied(mut e) => { - // Merge checkpointed changes back into the main account - // storage preserving the cache. - e.get_mut().overwrite_with(v); - }, - Entry::Vacant(e) => { - e.insert(v); - } - } - }, - None => { - if let Entry::Occupied(e) = self.cache.get_mut().entry(k) { - if e.get().is_dirty() { - e.remove(); - } - } - } - } - } - } - } - - fn insert_cache(&self, address: &Address, account: AccountEntry) { - // Dirty account which is not in the cache means this is a new account. - // It goes directly into the checkpoint as there's nothing to rever to. - // - // In all other cases account is read as clean first, and after that made - // dirty in and added to the checkpoint with `note_cache`. - let is_dirty = account.is_dirty(); - let old_value = self.cache.borrow_mut().insert(*address, account); - if is_dirty { - if let Some(ref mut checkpoint) = self.checkpoints.borrow_mut().last_mut() { - checkpoint.entry(*address).or_insert(old_value); - } - } - } - - fn note_cache(&self, address: &Address) { - if let Some(ref mut checkpoint) = self.checkpoints.borrow_mut().last_mut() { - checkpoint.entry(*address) - .or_insert_with(|| self.cache.borrow().get(address).map(AccountEntry::clone_dirty)); - } - } - - /// Destroy the current object and return root and database. - pub fn drop(mut self) -> (H256, B) { - self.propagate_to_global_cache(); - (self.root, self.db) - } - - /// Destroy the current object and return single account data. - pub fn into_account(self, account: &Address) -> TrieResult<(Option>, HashMap)> { - // TODO: deconstruct without cloning. - let account = self.require(account, true)?; - Ok((account.code().clone(), account.storage_changes().clone())) - } - - /// Return reference to root - pub fn root(&self) -> &H256 { - &self.root - } - - /// Create a new contract at address `contract`. If there is already an account at the address - /// it will have its code reset, ready for `init_code()`. - pub fn new_contract(&mut self, contract: &Address, balance: U256, nonce_offset: U256) -> TrieResult<()> { - let original_storage_root = self.original_storage_root(contract)?; - let (nonce, overflow) = self.account_start_nonce.overflowing_add(nonce_offset); - if overflow { - return Err(Box::new(TrieError::DecoderError(H256::from(*contract), - rlp::DecoderError::Custom("Nonce overflow".into())))); - } - self.insert_cache(contract, AccountEntry::new_dirty(Some(Account::new_contract(balance, nonce, original_storage_root)))); - Ok(()) - } - - /// Remove an existing account. - pub fn kill_account(&mut self, account: &Address) { - self.insert_cache(account, AccountEntry::new_dirty(None)); - } - - /// Determine whether an account exists. - pub fn exists(&self, a: &Address) -> TrieResult { - // Bloom filter does not contain empty accounts, so it is important here to - // check if account exists in the database directly before EIP-161 is in effect. - self.ensure_cached(a, RequireCache::None, false, |a| a.is_some()) - } - - /// Determine whether an account exists and if not empty. - pub fn exists_and_not_null(&self, a: &Address) -> TrieResult { - self.ensure_cached(a, RequireCache::None, false, |a| a.map_or(false, |a| !a.is_null())) - } - - /// Determine whether an account exists and has code or non-zero nonce. - pub fn exists_and_has_code_or_nonce(&self, a: &Address) -> TrieResult { - self.ensure_cached(a, RequireCache::CodeSize, false, - |a| a.map_or(false, |a| a.code_hash() != KECCAK_EMPTY || *a.nonce() != self.account_start_nonce)) - } - - /// Get the balance of account `a`. - pub fn balance(&self, a: &Address) -> TrieResult { - self.ensure_cached(a, RequireCache::None, true, - |a| a.as_ref().map_or(U256::zero(), |account| *account.balance())) - } - - /// Get the nonce of account `a`. - pub fn nonce(&self, a: &Address) -> TrieResult { - self.ensure_cached(a, RequireCache::None, true, - |a| a.as_ref().map_or(self.account_start_nonce, |account| *account.nonce())) - } - - /// Whether the base storage root of an account remains unchanged. - pub fn is_base_storage_root_unchanged(&self, a: &Address) -> TrieResult { - Ok(self.ensure_cached(a, RequireCache::None, true, - |a| a.as_ref().map(|account| account.is_base_storage_root_unchanged()))? - .unwrap_or(true)) - } - - /// Get the storage root of account `a`. - pub fn storage_root(&self, a: &Address) -> TrieResult> { - self.ensure_cached(a, RequireCache::None, true, - |a| a.as_ref().and_then(|account| account.storage_root())) - } - - /// Get the original storage root since last commit of account `a`. - pub fn original_storage_root(&self, a: &Address) -> TrieResult { - Ok(self.ensure_cached(a, RequireCache::None, true, - |a| a.as_ref().map(|account| account.original_storage_root()))? - .unwrap_or(KECCAK_NULL_RLP)) - } - - /// Get the value of storage at a specific checkpoint. - pub fn checkpoint_storage_at(&self, start_checkpoint_index: usize, address: &Address, key: &H256) -> TrieResult> { - #[must_use] - enum ReturnKind { - /// Use original storage at value at this address. - OriginalAt, - /// The checkpoint storage value is the same as the checkpoint storage value at the next checkpoint. - SameAsNext, - } - - let kind = { - let checkpoints = self.checkpoints.borrow(); - - if start_checkpoint_index >= checkpoints.len() { - // The checkpoint was not found. Return None. - return Ok(None); - } - - let mut kind = None; - - for checkpoint in checkpoints.iter().skip(start_checkpoint_index) { - match checkpoint.get(address) { - // The account exists at this checkpoint. - Some(Some(AccountEntry { account: Some(ref account), .. })) => { - if let Some(value) = account.cached_storage_at(key) { - return Ok(Some(value)); - } else { - // This account has checkpoint entry, but the key is not in the entry's cache. We can use - // original_storage_at if current account's original storage root is the same as checkpoint - // account's original storage root. Otherwise, the account must be a newly created contract. - if account.base_storage_root() == self.original_storage_root(address)? { - kind = Some(ReturnKind::OriginalAt); - break - } else { - // If account base storage root is different from the original storage root since last - // commit, then it can only be created from a new contract, where the base storage root - // would always be empty. Note that this branch is actually never called, because - // `cached_storage_at` handled this case. - warn!(target: "state", "Trying to get an account's cached storage value, but base storage root does not equal to original storage root! Assuming the value is empty."); - return Ok(Some(H256::zero())); - } - } - }, - // The account didn't exist at that point. Return empty value. - Some(Some(AccountEntry { account: None, .. })) => return Ok(Some(H256::zero())), - // The value was not cached at that checkpoint, meaning it was not modified at all. - Some(None) => { - kind = Some(ReturnKind::OriginalAt); - break - }, - // This key does not have a checkpoint entry. - None => { - kind = Some(ReturnKind::SameAsNext); - }, - } - } - - kind.expect("start_checkpoint_index is checked to be below checkpoints_len; for loop above must have been executed at least once; it will either early return, or set the kind value to Some; qed") - }; - - match kind { - ReturnKind::SameAsNext => { - // If we reached here, all previous SameAsNext failed to early return. It means that the value we want - // to fetch is the same as current. - Ok(Some(self.storage_at(address, key)?)) - }, - ReturnKind::OriginalAt => Ok(Some(self.original_storage_at(address, key)?)), - } - } - - fn storage_at_inner( - &self, address: &Address, key: &H256, f_cached_at: FCachedStorageAt, f_at: FStorageAt, - ) -> TrieResult where - FCachedStorageAt: Fn(&Account, &H256) -> Option, - FStorageAt: Fn(&Account, &dyn HashDB, &H256) -> TrieResult - { - // Storage key search and update works like this: - // 1. If there's an entry for the account in the local cache check for the key and return it if found. - // 2. If there's an entry for the account in the global cache check for the key or load it into that account. - // 3. If account is missing in the global cache load it into the local cache and cache the key there. - - { - // check local cache first without updating - let local_cache = self.cache.borrow_mut(); - let mut local_account = None; - if let Some(maybe_acc) = local_cache.get(address) { - match maybe_acc.account { - Some(ref account) => { - if let Some(value) = f_cached_at(account, key) { - return Ok(value); - } else { - local_account = Some(maybe_acc); - } - }, - _ => return Ok(H256::zero()), - } - } - // check the global cache and and cache storage key there if found, - let trie_res = self.db.get_cached(address, |acc| match acc { - None => Ok(H256::zero()), - Some(a) => { - let account_db = self.factories.accountdb.readonly(self.db.as_hash_db(), a.address_hash(address)); - f_at(a, account_db.as_hash_db(), key) - } - }); - - if let Some(res) = trie_res { - return res; - } - - // otherwise cache the account localy and cache storage key there. - if let Some(ref mut acc) = local_account { - if let Some(ref account) = acc.account { - let account_db = self.factories.accountdb.readonly(self.db.as_hash_db(), account.address_hash(address)); - return f_at(account, account_db.as_hash_db(), key) - } else { - return Ok(H256::zero()) - } - } - } - - // check if the account could exist before any requests to trie - if self.db.is_known_null(address) { return Ok(H256::zero()) } - - // account is not found in the global cache, get from the DB and insert into local - let db = &self.db.as_hash_db(); - let db = self.factories.trie.readonly(db, &self.root).expect(SEC_TRIE_DB_UNWRAP_STR); - let from_rlp = |b: &[u8]| Account::from_rlp(b).expect("decoding db value failed"); - let maybe_acc = db.get_with(address.as_bytes(), from_rlp)?; - let r = maybe_acc.as_ref().map_or(Ok(H256::zero()), |a| { - let account_db = self.factories.accountdb.readonly(self.db.as_hash_db(), a.address_hash(address)); - f_at(a, account_db.as_hash_db(), key) - }); - self.insert_cache(address, AccountEntry::new_clean(maybe_acc)); - r - } - - /// Mutate storage of account `address` so that it is `value` for `key`. - pub fn storage_at(&self, address: &Address, key: &H256) -> TrieResult { - self.storage_at_inner( - address, - key, - |account, key| { account.cached_storage_at(key) }, - |account, db, key| { account.storage_at(db, key) }, - ) - } - - /// Get the value of storage after last state commitment. - pub fn original_storage_at(&self, address: &Address, key: &H256) -> TrieResult { - self.storage_at_inner( - address, - key, - |account, key| { account.cached_original_storage_at(key) }, - |account, db, key| { account.original_storage_at(db, key) }, - ) - } - - /// Get accounts' code. - pub fn code(&self, a: &Address) -> TrieResult>> { - self.ensure_cached(a, RequireCache::Code, true, - |a| a.as_ref().map_or(None, |a| a.code().clone())) - } - - /// Get an account's code hash. - pub fn code_hash(&self, a: &Address) -> TrieResult> { - self.ensure_cached(a, RequireCache::None, true, - |a| a.as_ref().map(|a| a.code_hash())) - } - - /// Get accounts' code size. - pub fn code_size(&self, a: &Address) -> TrieResult> { - self.ensure_cached(a, RequireCache::CodeSize, true, - |a| a.as_ref().and_then(|a| a.code_size())) - } - - /// Add `incr` to the balance of account `a`. - pub fn add_balance(&mut self, a: &Address, incr: &U256, cleanup_mode: CleanupMode) -> TrieResult<()> { - trace!(target: "state", "add_balance({}, {}): {}", a, incr, self.balance(a)?); - let is_value_transfer = !incr.is_zero(); - if is_value_transfer || (cleanup_mode == CleanupMode::ForceCreate && !self.exists(a)?) { - self.require(a, false)?.add_balance(incr); - } else if let CleanupMode::TrackTouched(set) = cleanup_mode { - if self.exists(a)? { - set.insert(*a); - self.touch(a)?; - } - } - Ok(()) - } - - /// Subtract `decr` from the balance of account `a`. - pub fn sub_balance(&mut self, a: &Address, decr: &U256, cleanup_mode: &mut CleanupMode) -> TrieResult<()> { - trace!(target: "state", "sub_balance({}, {}): {}", a, decr, self.balance(a)?); - if !decr.is_zero() || !self.exists(a)? { - self.require(a, false)?.sub_balance(decr); - } - if let CleanupMode::TrackTouched(ref mut set) = *cleanup_mode { - set.insert(*a); - } - Ok(()) - } - - /// Subtracts `by` from the balance of `from` and adds it to that of `to`. - pub fn transfer_balance(&mut self, from: &Address, to: &Address, by: &U256, mut cleanup_mode: CleanupMode) -> TrieResult<()> { - self.sub_balance(from, by, &mut cleanup_mode)?; - self.add_balance(to, by, cleanup_mode)?; - Ok(()) - } - - /// Increment the nonce of account `a` by 1. - pub fn inc_nonce(&mut self, a: &Address) -> TrieResult<()> { - self.require(a, false).map(|mut x| x.inc_nonce()) - } - - /// Mutate storage of account `a` so that it is `value` for `key`. - pub fn set_storage(&mut self, a: &Address, key: H256, value: H256) -> TrieResult<()> { - trace!(target: "state", "set_storage({}:{:x} to {:x})", a, key, value); - if self.storage_at(a, &key)? != value { - self.require(a, false)?.set_storage(key, value) - } - - Ok(()) - } - - /// Initialise the code of account `a` so that it is `code`. - /// NOTE: Account should have been created with `new_contract`. - pub fn init_code(&mut self, a: &Address, code: Bytes) -> TrieResult<()> { - self.require_or_from(a, true, || Account::new_contract(0.into(), self.account_start_nonce, KECCAK_NULL_RLP), |_| {})?.init_code(code); - Ok(()) - } - - /// Reset the code of account `a` so that it is `code`. - pub fn reset_code(&mut self, a: &Address, code: Bytes) -> TrieResult<()> { - self.require_or_from(a, true, || Account::new_contract(0.into(), self.account_start_nonce, KECCAK_NULL_RLP), |_| {})?.reset_code(code); - Ok(()) - } - - fn touch(&mut self, a: &Address) -> TrieResult<()> { - self.require(a, false)?; - Ok(()) - } - - /// Commits our cached account changes into the trie. - pub fn commit(&mut self) -> Result<(), Error> { - assert!(self.checkpoints.borrow().is_empty()); - // first, commit the sub trees. - let mut accounts = self.cache.borrow_mut(); - for (address, ref mut a) in accounts.iter_mut().filter(|&(_, ref a)| a.is_dirty()) { - if let Some(ref mut account) = a.account { - let addr_hash = account.address_hash(address); - { - let mut account_db = self.factories.accountdb.create(self.db.as_hash_db_mut(), addr_hash); - account.commit_storage(&self.factories.trie, account_db.as_hash_db_mut())?; - account.commit_code(account_db.as_hash_db_mut()); - } - if !account.is_empty() { - self.db.note_non_null_account(address); - } - } - } - - { - let mut trie = self.factories.trie.from_existing(self.db.as_hash_db_mut(), &mut self.root)?; - for (address, ref mut a) in accounts.iter_mut().filter(|&(_, ref a)| a.is_dirty()) { - a.state = AccountState::Committed; - match a.account { - Some(ref mut account) => { - trie.insert(address.as_bytes(), &account.rlp())?; - }, - None => { - trie.remove(address.as_bytes())?; - }, - }; - } - } - - Ok(()) - } - - /// Propagate local cache into shared canonical state cache. - fn propagate_to_global_cache(&mut self) { - let mut addresses = self.cache.borrow_mut(); - trace!("Committing cache {:?} entries", addresses.len()); - for (address, a) in addresses.drain().filter(|&(_, ref a)| a.state == AccountState::Committed || a.state == AccountState::CleanFresh) { - self.db.add_to_account_cache(address, a.account, a.state == AccountState::Committed); - } - } - - /// Clear state cache - pub fn clear(&mut self) { - assert!(self.checkpoints.borrow().is_empty()); - self.cache.borrow_mut().clear(); - } - - /// Remove any touched empty or dust accounts. - pub fn kill_garbage(&mut self, touched: &HashSet
, remove_empty_touched: bool, min_balance: &Option, kill_contracts: bool) -> TrieResult<()> { - let to_kill: HashSet<_> = { - self.cache.borrow().iter().filter_map(|(address, ref entry)| - if touched.contains(address) && // Check all touched accounts - ((remove_empty_touched && entry.exists_and_is_null()) // Remove all empty touched accounts. - || min_balance.map_or(false, |ref balance| entry.account.as_ref().map_or(false, |account| - (account.is_basic() || kill_contracts) // Remove all basic and optionally contract accounts where balance has been decreased. - && account.balance() < balance && entry.old_balance.as_ref().map_or(false, |b| account.balance() < b)))) { - - Some(address.clone()) - } else { None }).collect() - }; - for address in to_kill { - self.kill_account(&address); - } - Ok(()) - } - - /// Populate the state from `accounts`. - /// Used for tests. - pub fn populate_from(&mut self, accounts: PodState) { - assert!(self.checkpoints.borrow().is_empty()); - for (add, acc) in accounts.drain().into_iter() { - self.cache.borrow_mut().insert(add, AccountEntry::new_dirty(Some(Account::from_pod(acc)))); - } - } - - /// Populate a PodAccount map from this state. - fn to_pod_cache(&self) -> PodState { - assert!(self.checkpoints.borrow().is_empty()); - PodState::from(self.cache.borrow().iter().fold(BTreeMap::new(), |mut m, (add, opt)| { - if let Some(ref acc) = opt.account { - m.insert(*add, acc.to_pod()); - } - m - })) - } - - #[cfg(feature="to-pod-full")] - /// Populate a PodAccount map from this state. - /// Warning this is not for real time use. - /// Use of this method requires FatDB mode to be able - /// to iterate on accounts. - pub fn to_pod_full(&self) -> Result { - - assert!(self.checkpoints.borrow().is_empty()); - assert!(self.factories.trie.is_fat()); - - let mut result = BTreeMap::new(); - - let db = &self.db.as_hash_db(); - let trie = self.factories.trie.readonly(db, &self.root)?; - - // put trie in cache - for item in trie.iter()? { - if let Ok((addr, _dbval)) = item { - let address = Address::from_slice(&addr); - let _ = self.require(&address, true); - } - } - - // Resolve missing part - for (add, opt) in self.cache.borrow().iter() { - if let Some(ref acc) = opt.account { - let pod_account = self.account_to_pod_account(acc, add)?; - result.insert(add.clone(), pod_account); - } - } - - Ok(PodState::from(result)) - } - - /// Create a PodAccount from an account. - /// Differs from existing method by including all storage - /// values of the account to the PodAccount. - /// This function is only intended for use in small tests or with fresh accounts. - /// It requires FatDB. - #[cfg(feature="to-pod-full")] - fn account_to_pod_account(&self, account: &Account, address: &Address) -> Result { - use ethereum_types::BigEndianHash; - - let mut pod_storage = BTreeMap::new(); - let addr_hash = account.address_hash(address); - let accountdb = self.factories.accountdb.readonly(self.db.as_hash_db(), addr_hash); - let root = account.base_storage_root(); - - let accountdb = &accountdb.as_hash_db(); - let trie = self.factories.trie.readonly(accountdb, &root)?; - for o_kv in trie.iter()? { - if let Ok((key, val)) = o_kv { - pod_storage.insert( - H256::from_slice(&key[..]), - BigEndianHash::from_uint( - &rlp::decode::(&val[..]).expect("Decoded from trie which was encoded from the same type; qed") - ), - ); - } - } - - let mut pod_account = account.to_pod(); - // cached one first - pod_storage.append(&mut pod_account.storage); - pod_account.storage = pod_storage; - Ok(pod_account) - } - - /// Populate a PodAccount map from this state, with another state as the account and storage query. - fn to_pod_diff(&mut self, query: &State) -> TrieResult { - assert!(self.checkpoints.borrow().is_empty()); - - // Merge PodAccount::to_pod for cache of self and `query`. - let all_addresses = self.cache.borrow().keys().cloned() - .chain(query.cache.borrow().keys().cloned()) - .collect::>(); - - Ok(PodState::from(all_addresses.into_iter().fold(Ok(BTreeMap::new()), |m: TrieResult<_>, address| { - let mut m = m?; - - let account = self.ensure_cached(&address, RequireCache::Code, true, |acc| { - acc.map(|acc| { - // Merge all modified storage keys. - let all_keys = { - let self_keys = acc.storage_changes().keys().cloned() - .collect::>(); - - if let Some(ref query_storage) = query.cache.borrow().get(&address) - .and_then(|opt| { - Some(opt.account.as_ref()?.storage_changes().keys().cloned() - .collect::>()) - }) - { - self_keys.union(&query_storage).cloned().collect::>() - } else { - self_keys.into_iter().collect::>() - } - }; - - // Storage must be fetched after ensure_cached to avoid borrow problem. - (*acc.balance(), *acc.nonce(), all_keys, acc.code().map(|x| x.to_vec())) - }) - })?; - - if let Some((balance, nonce, storage_keys, code)) = account { - let storage = storage_keys.into_iter().fold(Ok(BTreeMap::new()), |s: TrieResult<_>, key| { - let mut s = s?; - - s.insert(key, self.storage_at(&address, &key)?); - Ok(s) - })?; - - m.insert(address, PodAccount { - balance, nonce, storage, code - }); - } - - Ok(m) - })?)) - } - - /// Returns a `StateDiff` describing the difference from `orig` to `self`. - /// Consumes self. - pub fn diff_from(&self, mut orig: State) -> TrieResult { - let pod_state_post = self.to_pod_cache(); - let pod_state_pre = orig.to_pod_diff(self)?; - Ok(pod::state::diff_pod(&pod_state_pre, &pod_state_post)) - } - - /// Load required account data from the databases. Returns whether the cache succeeds. - #[must_use] - fn update_account_cache(require: RequireCache, account: &mut Account, state_db: &B, db: &dyn HashDB) -> bool { - if let RequireCache::None = require { - return true; - } - - if account.is_cached() { - return true; - } - - // if there's already code in the global cache, always cache it localy - let hash = account.code_hash(); - match state_db.get_cached_code(&hash) { - Some(code) => { - account.cache_given_code(code); - true - }, - None => match require { - RequireCache::None => true, - RequireCache::Code => { - if let Some(code) = account.cache_code(db) { - // propagate code loaded from the database to - // the global code cache. - state_db.cache_code(hash, code); - true - } else { - false - } - }, - RequireCache::CodeSize => { - account.cache_code_size(db) - } - } - } - } - - /// Check caches for required data - /// First searches for account in the local, then the shared cache. - /// Populates local cache if nothing found. - fn ensure_cached(&self, a: &Address, require: RequireCache, check_null: bool, f: F) -> TrieResult - where F: Fn(Option<&Account>) -> U { - // check local cache first - if let Some(ref mut maybe_acc) = self.cache.borrow_mut().get_mut(a) { - if let Some(ref mut account) = maybe_acc.account { - let accountdb = self.factories.accountdb.readonly(self.db.as_hash_db(), account.address_hash(a)); - if Self::update_account_cache(require, account, &self.db, accountdb.as_hash_db()) { - return Ok(f(Some(account))); - } else { - return Err(Box::new(TrieError::IncompleteDatabase(H256::from(*a)))); - } - } - return Ok(f(None)); - } - // check global cache - let result = self.db.get_cached(a, |mut acc| { - if let Some(ref mut account) = acc { - let accountdb = self.factories.accountdb.readonly(self.db.as_hash_db(), account.address_hash(a)); - if !Self::update_account_cache(require, account, &self.db, accountdb.as_hash_db()) { - return Err(Box::new(TrieError::IncompleteDatabase(H256::from(*a)))); - } - } - Ok(f(acc.map(|a| &*a))) - }); - match result { - Some(r) => Ok(r?), - None => { - // first check if it is not in database for sure - if check_null && self.db.is_known_null(a) { return Ok(f(None)); } - - // not found in the global cache, get from the DB and insert into local - let db = &self.db.as_hash_db(); - let db = self.factories.trie.readonly(db, &self.root)?; - let from_rlp = |b: &[u8]| Account::from_rlp(b).expect("decoding db value failed"); - let mut maybe_acc = db.get_with(a.as_bytes(), from_rlp)?; - if let Some(ref mut account) = maybe_acc.as_mut() { - let accountdb = self.factories.accountdb.readonly(self.db.as_hash_db(), account.address_hash(a)); - if !Self::update_account_cache(require, account, &self.db, accountdb.as_hash_db()) { - return Err(Box::new(TrieError::IncompleteDatabase(H256::from(*a)))); - } - } - let r = f(maybe_acc.as_ref()); - self.insert_cache(a, AccountEntry::new_clean(maybe_acc)); - Ok(r) - } - } - } - - /// Pull account `a` in our cache from the trie DB. `require_code` requires that the code be cached, too. - fn require<'a>(&'a self, a: &Address, require_code: bool) -> TrieResult> { - self.require_or_from(a, require_code, || Account::new_basic(0u8.into(), self.account_start_nonce), |_| {}) - } - - /// Pull account `a` in our cache from the trie DB. `require_code` requires that the code be cached, too. - /// If it doesn't exist, make account equal the evaluation of `default`. - fn require_or_from<'a, F, G>(&'a self, a: &Address, require_code: bool, default: F, not_default: G) -> TrieResult> - where F: FnOnce() -> Account, G: FnOnce(&mut Account), - { - let contains_key = self.cache.borrow().contains_key(a); - if !contains_key { - match self.db.get_cached_account(a) { - Some(acc) => self.insert_cache(a, AccountEntry::new_clean_cached(acc)), - None => { - let maybe_acc = if !self.db.is_known_null(a) { - let db = &self.db.as_hash_db(); - let db = self.factories.trie.readonly(db, &self.root)?; - let from_rlp = |b:&[u8]| { Account::from_rlp(b).expect("decoding db value failed") }; - AccountEntry::new_clean(db.get_with(a.as_bytes(), from_rlp)?) - } else { - AccountEntry::new_clean(None) - }; - self.insert_cache(a, maybe_acc); - } - } - } - self.note_cache(a); - - // at this point the entry is guaranteed to be in the cache. - let mut account = RefMut::map(self.cache.borrow_mut(), |c| { - let entry = c.get_mut(a).expect("entry known to exist in the cache; qed"); - - match &mut entry.account { - &mut Some(ref mut acc) => not_default(acc), - slot => *slot = Some(default()), - } - - // set the dirty flag after changing account data. - entry.state = AccountState::Dirty; - entry.account.as_mut().expect("Required account must always exist; qed") - }); - - if require_code { - let addr_hash = account.address_hash(a); - let accountdb = self.factories.accountdb.readonly(self.db.as_hash_db(), addr_hash); - - if !Self::update_account_cache(RequireCache::Code, &mut account, &self.db, accountdb.as_hash_db()) { - return Err(Box::new(TrieError::IncompleteDatabase(H256::from(*a)))) - } - } - - Ok(account) - } - - /// Replace account code and storage. Creates account if it does not exist. - pub fn patch_account(&self, a: &Address, code: Arc, storage: HashMap) -> TrieResult<()> { - Ok(self.require(a, false)?.reset_code_and_storage(code, storage)) - } -} - -// State proof implementations; useful for light client protocols. -impl State { - /// Prove an account's existence or nonexistence in the state trie. - /// Returns a merkle proof of the account's trie node omitted or an encountered trie error. - /// If the account doesn't exist in the trie, prove that and return defaults. - /// Requires a secure trie to be used for accurate results. - /// `account_key` == keccak(address) - pub fn prove_account(&self, account_key: H256) -> TrieResult<(Vec, BasicAccount)> { - let mut recorder = Recorder::new(); - let db = &self.db.as_hash_db(); - let trie = TrieDB::new(db, &self.root)?; - let maybe_account: Option = { - let panicky_decoder = |bytes: &[u8]| { - ::rlp::decode(bytes).unwrap_or_else(|_| panic!("prove_account, could not query trie for account key={}", &account_key)) - }; - let query = (&mut recorder, panicky_decoder); - trie.get_with(account_key.as_bytes(), query)? - }; - let account = maybe_account.unwrap_or_else(|| BasicAccount { - balance: 0.into(), - nonce: self.account_start_nonce, - code_hash: KECCAK_EMPTY, - storage_root: KECCAK_NULL_RLP, - }); - - Ok((recorder.drain().into_iter().map(|r| r.data).collect(), account)) - } - - /// Prove an account's storage key's existence or nonexistence in the state. - /// Returns a merkle proof of the account's storage trie. - /// Requires a secure trie to be used for correctness. - /// `account_key` == keccak(address) - /// `storage_key` == keccak(key) - pub fn prove_storage(&self, account_key: H256, storage_key: H256) -> TrieResult<(Vec, H256)> { - // TODO: probably could look into cache somehow but it's keyed by - // address, not keccak(address). - let db = &self.db.as_hash_db(); - let trie = TrieDB::new(db, &self.root)?; - let from_rlp = |b: &[u8]| Account::from_rlp(b).expect("decoding db value failed"); - let acc = match trie.get_with(account_key.as_bytes(), from_rlp)? { - Some(acc) => acc, - None => return Ok((Vec::new(), H256::zero())), - }; - - let account_db = self.factories.accountdb.readonly(self.db.as_hash_db(), account_key); - acc.prove_storage(account_db.as_hash_db(), storage_key) - } -} - -impl fmt::Debug for State { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{:?}", self.cache.borrow()) - } -} - -impl State { - /// Get a reference to the underlying state DB. - pub fn db(&self) -> &StateDB { - &self.db - } -} - -// TODO: cloning for `State` shouldn't be possible in general; Remove this and use -// checkpoints where possible. -impl Clone for State { - fn clone(&self) -> State { - let cache = { - let mut cache: HashMap = HashMap::new(); - for (key, val) in self.cache.borrow().iter() { - if let Some(entry) = val.clone_if_dirty() { - cache.insert(key.clone(), entry); - } - } - cache - }; - - State { - db: self.db.boxed_clone(), - root: self.root.clone(), - cache: RefCell::new(cache), - checkpoints: RefCell::new(Vec::new()), - account_start_nonce: self.account_start_nonce.clone(), - factories: self.factories.clone(), - } - } -} - -#[cfg(test)] -mod tests { - use std::sync::Arc; - use std::str::FromStr; - use rustc_hex::FromHex; - use hash::{keccak, KECCAK_NULL_RLP}; - use super::*; - use ethkey::Secret; - use ethereum_types::{H256, U256, Address, BigEndianHash}; - use test_helpers::{get_temp_state, get_temp_state_db}; - use machine::Machine; - use vm::EnvInfo; - use spec::*; - use types::transaction::*; - use trace::{FlatTrace, TraceError, trace}; - use evm::CallType; - use pod; - use executive_state::ExecutiveStateWithMachineZomgBetterName; - - fn secret() -> Secret { - keccak("").into() - } - - fn make_frontier_machine(max_depth: usize) -> Machine { - let mut machine = ::ethereum::new_frontier_test_machine(); - machine.set_schedule_creation_rules(Box::new(move |s, _| s.max_depth = max_depth)); - machine - } - - #[test] - fn should_apply_create_transaction() { - let _ = env_logger::try_init(); - - let mut state = get_temp_state(); - - let mut info = EnvInfo::default(); - info.gas_limit = 1_000_000.into(); - let machine = make_frontier_machine(5); - - let t = Transaction { - nonce: 0.into(), - gas_price: 0.into(), - gas: 100_000.into(), - action: Action::Create, - value: 100.into(), - data: FromHex::from_hex("601080600c6000396000f3006000355415600957005b60203560003555").unwrap(), - }.sign(&secret(), None); - - state.add_balance(&t.sender(), &(100.into()), CleanupMode::NoEmpty).unwrap(); - let result = state.apply(&info, &machine, &t, true).unwrap(); - let expected_trace = vec![FlatTrace { - trace_address: Default::default(), - subtraces: 0, - action: trace::Action::Create(trace::Create { - from: Address::from_str("9cce34f7ab185c7aba1b7c8140d620b4bda941d6").unwrap(), - value: 100.into(), - gas: 77412.into(), - init: vec![96, 16, 128, 96, 12, 96, 0, 57, 96, 0, 243, 0, 96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53, 85], - }), - result: trace::Res::Create(trace::CreateResult { - gas_used: U256::from(3224), - address: Address::from_str("8988167e088c87cd314df6d3c2b83da5acb93ace").unwrap(), - code: vec![96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53] - }), - }]; - - assert_eq!(result.trace, expected_trace); - } - - #[test] - fn should_work_when_cloned() { - let _ = env_logger::try_init(); - - let a = Address::zero(); - - let mut state = { - let mut state = get_temp_state(); - assert_eq!(state.exists(&a).unwrap(), false); - state.inc_nonce(&a).unwrap(); - state.commit().unwrap(); - state.clone() - }; - - state.inc_nonce(&a).unwrap(); - state.commit().unwrap(); - } - - #[test] - fn should_trace_failed_create_transaction() { - let _ = env_logger::try_init(); - - let mut state = get_temp_state(); - - let mut info = EnvInfo::default(); - info.gas_limit = 1_000_000.into(); - let machine = make_frontier_machine(5); - - let t = Transaction { - nonce: 0.into(), - gas_price: 0.into(), - gas: 100_000.into(), - action: Action::Create, - value: 100.into(), - data: FromHex::from_hex("5b600056").unwrap(), - }.sign(&secret(), None); - - state.add_balance(&t.sender(), &(100.into()), CleanupMode::NoEmpty).unwrap(); - let result = state.apply(&info, &machine, &t, true).unwrap(); - let expected_trace = vec![FlatTrace { - trace_address: Default::default(), - action: trace::Action::Create(trace::Create { - from: Address::from_str("9cce34f7ab185c7aba1b7c8140d620b4bda941d6").unwrap(), - value: 100.into(), - gas: 78792.into(), - init: vec![91, 96, 0, 86], - }), - result: trace::Res::FailedCreate(TraceError::OutOfGas), - subtraces: 0 - }]; - - assert_eq!(result.trace, expected_trace); - } - - #[test] - fn should_trace_call_transaction() { - let _ = env_logger::try_init(); - - let mut state = get_temp_state(); - - let mut info = EnvInfo::default(); - info.gas_limit = 1_000_000.into(); - let machine = make_frontier_machine(5); - - let t = Transaction { - nonce: 0.into(), - gas_price: 0.into(), - gas: 100_000.into(), - action: Action::Call(Address::from_low_u64_be(0xa)), - value: 100.into(), - data: vec![], - }.sign(&secret(), None); - - state.init_code(&Address::from_low_u64_be(0xa), FromHex::from_hex("6000").unwrap()).unwrap(); - state.add_balance(&t.sender(), &(100.into()), CleanupMode::NoEmpty).unwrap(); - let result = state.apply(&info, &machine, &t, true).unwrap(); - let expected_trace = vec![FlatTrace { - trace_address: Default::default(), - action: trace::Action::Call(trace::Call { - from: Address::from_str("9cce34f7ab185c7aba1b7c8140d620b4bda941d6").unwrap(), - to: Address::from_low_u64_be(0xa), - value: 100.into(), - gas: 79000.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::Call(trace::CallResult { - gas_used: U256::from(3), - output: vec![] - }), - subtraces: 0, - }]; - - assert_eq!(result.trace, expected_trace); - } - - #[test] - fn should_trace_basic_call_transaction() { - let _ = env_logger::try_init(); - - let mut state = get_temp_state(); - - let mut info = EnvInfo::default(); - info.gas_limit = 1_000_000.into(); - let machine = make_frontier_machine(5); - - let t = Transaction { - nonce: 0.into(), - gas_price: 0.into(), - gas: 100_000.into(), - action: Action::Call(Address::from_low_u64_be(0xa)), - value: 100.into(), - data: vec![], - }.sign(&secret(), None); - - state.add_balance(&t.sender(), &(100.into()), CleanupMode::NoEmpty).unwrap(); - let result = state.apply(&info, &machine, &t, true).unwrap(); - let expected_trace = vec![FlatTrace { - trace_address: Default::default(), - action: trace::Action::Call(trace::Call { - from: Address::from_str("9cce34f7ab185c7aba1b7c8140d620b4bda941d6").unwrap(), - to: Address::from_low_u64_be(0xa), - value: 100.into(), - gas: 79000.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::Call(trace::CallResult { - gas_used: U256::from(0), - output: vec![] - }), - subtraces: 0, - }]; - - assert_eq!(result.trace, expected_trace); - } - - #[test] - fn should_trace_call_transaction_to_builtin() { - let _ = env_logger::try_init(); - - let mut state = get_temp_state(); - - let mut info = EnvInfo::default(); - info.gas_limit = 1_000_000.into(); - let machine = Spec::new_test_machine(); - - let t = Transaction { - nonce: 0.into(), - gas_price: 0.into(), - gas: 100_000.into(), - action: Action::Call(Address::from_low_u64_be(0x1)), - value: 0.into(), - data: vec![], - }.sign(&secret(), None); - - let result = state.apply(&info, &machine, &t, true).unwrap(); - - let expected_trace = vec![FlatTrace { - trace_address: Default::default(), - action: trace::Action::Call(trace::Call { - from: Address::from_str("9cce34f7ab185c7aba1b7c8140d620b4bda941d6").unwrap(), - to: Address::from_str("0000000000000000000000000000000000000001").unwrap(), - value: 0.into(), - gas: 79_000.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::Call(trace::CallResult { - gas_used: U256::from(3000), - output: vec![] - }), - subtraces: 0, - }]; - - assert_eq!(result.trace, expected_trace); - } - - #[test] - fn should_not_trace_subcall_transaction_to_builtin() { - let _ = env_logger::try_init(); - - let mut state = get_temp_state(); - - let mut info = EnvInfo::default(); - info.gas_limit = 1_000_000.into(); - let machine = Spec::new_test_machine(); - - let t = Transaction { - nonce: 0.into(), - gas_price: 0.into(), - gas: 100_000.into(), - action: Action::Call(Address::from_low_u64_be(0xa)), - value: 0.into(), - data: vec![], - }.sign(&secret(), None); - - state.init_code(&Address::from_low_u64_be(0xa), FromHex::from_hex("600060006000600060006001610be0f1").unwrap()).unwrap(); - let result = state.apply(&info, &machine, &t, true).unwrap(); - - let expected_trace = vec![FlatTrace { - trace_address: Default::default(), - action: trace::Action::Call(trace::Call { - from: Address::from_str("9cce34f7ab185c7aba1b7c8140d620b4bda941d6").unwrap(), - to: Address::from_low_u64_be(0xa), - value: 0.into(), - gas: 79000.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::Call(trace::CallResult { - gas_used: U256::from(3_721), // in post-eip150 - output: vec![] - }), - subtraces: 0, - }]; - - assert_eq!(result.trace, expected_trace); - } - - #[test] - fn should_trace_callcode_properly() { - let _ = env_logger::try_init(); - - let mut state = get_temp_state(); - - let mut info = EnvInfo::default(); - info.gas_limit = 1_000_000.into(); - let machine = Spec::new_test_machine(); - - let t = Transaction { - nonce: 0.into(), - gas_price: 0.into(), - gas: 100_000.into(), - action: Action::Call(Address::from_low_u64_be(0xa)), - value: 0.into(), - data: vec![], - }.sign(&secret(), None); - - state.init_code(&Address::from_low_u64_be(0xa), FromHex::from_hex("60006000600060006000600b611000f2").unwrap()).unwrap(); - state.init_code(&Address::from_low_u64_be(0xb), FromHex::from_hex("6000").unwrap()).unwrap(); - let result = state.apply(&info, &machine, &t, true).unwrap(); - - let expected_trace = vec![FlatTrace { - trace_address: Default::default(), - subtraces: 1, - action: trace::Action::Call(trace::Call { - from: Address::from_str("9cce34f7ab185c7aba1b7c8140d620b4bda941d6").unwrap(), - to: Address::from_low_u64_be(0xa), - value: 0.into(), - gas: 79000.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::Call(trace::CallResult { - gas_used: 724.into(), // in post-eip150 - output: vec![] - }), - }, FlatTrace { - trace_address: vec![0].into_iter().collect(), - subtraces: 0, - action: trace::Action::Call(trace::Call { - from: Address::from_low_u64_be(0xa), - to: Address::from_low_u64_be(0xb), - value: 0.into(), - gas: 4096.into(), - input: vec![], - call_type: CallType::CallCode, - }), - result: trace::Res::Call(trace::CallResult { - gas_used: 3.into(), - output: vec![], - }), - }]; - - assert_eq!(result.trace, expected_trace); - } - - #[test] - fn should_trace_delegatecall_properly() { - let _ = env_logger::try_init(); - - let mut state = get_temp_state(); - - let mut info = EnvInfo::default(); - info.gas_limit = 1_000_000.into(); - info.number = 0x789b0; - let machine = Spec::new_test_machine(); - - let t = Transaction { - nonce: 0.into(), - gas_price: 0.into(), - gas: 100_000.into(), - action: Action::Call(Address::from_low_u64_be(0xa)), - value: 0.into(), - data: vec![], - }.sign(&secret(), None); - - state.init_code(&Address::from_low_u64_be(0xa), FromHex::from_hex("6000600060006000600b618000f4").unwrap()).unwrap(); - state.init_code(&Address::from_low_u64_be(0xb), FromHex::from_hex("60056000526001601ff3").unwrap()).unwrap(); - let result = state.apply(&info, &machine, &t, true).unwrap(); - - let expected_trace = vec![FlatTrace { - trace_address: Default::default(), - subtraces: 1, - action: trace::Action::Call(trace::Call { - from: Address::from_str("9cce34f7ab185c7aba1b7c8140d620b4bda941d6").unwrap(), - to: Address::from_low_u64_be(0xa), - value: 0.into(), - gas: 79000.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::Call(trace::CallResult { - gas_used: U256::from(736), // in post-eip150 - output: vec![] - }), - }, FlatTrace { - trace_address: vec![0].into_iter().collect(), - subtraces: 0, - action: trace::Action::Call(trace::Call { - from: Address::from_low_u64_be(0xa), - to: Address::from_low_u64_be(0xb), - value: 0.into(), - gas: 32768.into(), - input: vec![], - call_type: CallType::DelegateCall, - }), - result: trace::Res::Call(trace::CallResult { - gas_used: 18.into(), - output: vec![5], - }), - }]; - - assert_eq!(result.trace, expected_trace); - } - - #[test] - fn should_trace_failed_call_transaction() { - let _ = env_logger::try_init(); - - let mut state = get_temp_state(); - - let mut info = EnvInfo::default(); - info.gas_limit = 1_000_000.into(); - let machine = make_frontier_machine(5); - - let t = Transaction { - nonce: 0.into(), - gas_price: 0.into(), - gas: 100_000.into(), - action: Action::Call(Address::from_low_u64_be(0xa)), - value: 100.into(), - data: vec![], - }.sign(&secret(), None); - - state.init_code(&Address::from_low_u64_be(0xa), FromHex::from_hex("5b600056").unwrap()).unwrap(); - state.add_balance(&t.sender(), &(100.into()), CleanupMode::NoEmpty).unwrap(); - let result = state.apply(&info, &machine, &t, true).unwrap(); - let expected_trace = vec![FlatTrace { - trace_address: Default::default(), - action: trace::Action::Call(trace::Call { - from: Address::from_str("9cce34f7ab185c7aba1b7c8140d620b4bda941d6").unwrap(), - to: Address::from_low_u64_be(0xa), - value: 100.into(), - gas: 79000.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::FailedCall(TraceError::OutOfGas), - subtraces: 0, - }]; - - assert_eq!(result.trace, expected_trace); - } - - #[test] - fn should_trace_call_with_subcall_transaction() { - let _ = env_logger::try_init(); - - let mut state = get_temp_state(); - - let mut info = EnvInfo::default(); - info.gas_limit = 1_000_000.into(); - let machine = make_frontier_machine(5); - - let t = Transaction { - nonce: 0.into(), - gas_price: 0.into(), - gas: 100_000.into(), - action: Action::Call(Address::from_low_u64_be(0xa)), - value: 100.into(), - data: vec![], - }.sign(&secret(), None); - - state.init_code(&Address::from_low_u64_be(0xa), FromHex::from_hex("60006000600060006000600b602b5a03f1").unwrap()).unwrap(); - state.init_code(&Address::from_low_u64_be(0xb), FromHex::from_hex("6000").unwrap()).unwrap(); - state.add_balance(&t.sender(), &(100.into()), CleanupMode::NoEmpty).unwrap(); - let result = state.apply(&info, &machine, &t, true).unwrap(); - - let expected_trace = vec![FlatTrace { - trace_address: Default::default(), - subtraces: 1, - action: trace::Action::Call(trace::Call { - from: Address::from_str("9cce34f7ab185c7aba1b7c8140d620b4bda941d6").unwrap(), - to: Address::from_low_u64_be(0xa), - value: 100.into(), - gas: 79000.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::Call(trace::CallResult { - gas_used: U256::from(69), - output: vec![] - }), - }, FlatTrace { - trace_address: vec![0].into_iter().collect(), - subtraces: 0, - action: trace::Action::Call(trace::Call { - from: Address::from_low_u64_be(0xa), - to: Address::from_low_u64_be(0xb), - value: 0.into(), - gas: 78934.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::Call(trace::CallResult { - gas_used: U256::from(3), - output: vec![] - }), - }]; - - assert_eq!(result.trace, expected_trace); - } - - #[test] - fn should_trace_call_with_basic_subcall_transaction() { - let _ = env_logger::try_init(); - - let mut state = get_temp_state(); - - let mut info = EnvInfo::default(); - info.gas_limit = 1_000_000.into(); - let machine = make_frontier_machine(5); - - let t = Transaction { - nonce: 0.into(), - gas_price: 0.into(), - gas: 100_000.into(), - action: Action::Call(Address::from_low_u64_be(0xa)), - value: 100.into(), - data: vec![], - }.sign(&secret(), None); - - state.init_code(&Address::from_low_u64_be(0xa), FromHex::from_hex("60006000600060006045600b6000f1").unwrap()).unwrap(); - state.add_balance(&t.sender(), &(100.into()), CleanupMode::NoEmpty).unwrap(); - let result = state.apply(&info, &machine, &t, true).unwrap(); - let expected_trace = vec![FlatTrace { - trace_address: Default::default(), - subtraces: 1, - action: trace::Action::Call(trace::Call { - from: Address::from_str("9cce34f7ab185c7aba1b7c8140d620b4bda941d6").unwrap(), - to: Address::from_low_u64_be(0xa), - value: 100.into(), - gas: 79000.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::Call(trace::CallResult { - gas_used: U256::from(31761), - output: vec![] - }), - }, FlatTrace { - trace_address: vec![0].into_iter().collect(), - subtraces: 0, - action: trace::Action::Call(trace::Call { - from: Address::from_low_u64_be(0xa), - to: Address::from_low_u64_be(0xb), - value: 69.into(), - gas: 2300.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::Call(trace::CallResult::default()), - }]; - - assert_eq!(result.trace, expected_trace); - } - - #[test] - fn should_not_trace_call_with_invalid_basic_subcall_transaction() { - let _ = env_logger::try_init(); - - let mut state = get_temp_state(); - - let mut info = EnvInfo::default(); - info.gas_limit = 1_000_000.into(); - let machine = make_frontier_machine(5); - - let t = Transaction { - nonce: 0.into(), - gas_price: 0.into(), - gas: 100_000.into(), - action: Action::Call(Address::from_low_u64_be(0xa)), - value: 100.into(), - data: vec![], - }.sign(&secret(), None); - - state.init_code(&Address::from_low_u64_be(0xa), FromHex::from_hex("600060006000600060ff600b6000f1").unwrap()).unwrap(); // not enough funds. - state.add_balance(&t.sender(), &(100.into()), CleanupMode::NoEmpty).unwrap(); - let result = state.apply(&info, &machine, &t, true).unwrap(); - let expected_trace = vec![FlatTrace { - trace_address: Default::default(), - subtraces: 0, - action: trace::Action::Call(trace::Call { - from: Address::from_str("9cce34f7ab185c7aba1b7c8140d620b4bda941d6").unwrap(), - to: Address::from_low_u64_be(0xa), - value: 100.into(), - gas: 79000.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::Call(trace::CallResult { - gas_used: U256::from(31761), - output: vec![] - }), - }]; - - assert_eq!(result.trace, expected_trace); - } - - #[test] - fn should_trace_failed_subcall_transaction() { - let _ = env_logger::try_init(); - - let mut state = get_temp_state(); - - let mut info = EnvInfo::default(); - info.gas_limit = 1_000_000.into(); - let machine = make_frontier_machine(5); - - let t = Transaction { - nonce: 0.into(), - gas_price: 0.into(), - gas: 100_000.into(), - action: Action::Call(Address::from_low_u64_be(0xa)), - value: 100.into(), - data: vec![],//600480600b6000396000f35b600056 - }.sign(&secret(), None); - - state.init_code(&Address::from_low_u64_be(0xa), FromHex::from_hex("60006000600060006000600b602b5a03f1").unwrap()).unwrap(); - state.init_code(&Address::from_low_u64_be(0xb), FromHex::from_hex("5b600056").unwrap()).unwrap(); - state.add_balance(&t.sender(), &(100.into()), CleanupMode::NoEmpty).unwrap(); - let result = state.apply(&info, &machine, &t, true).unwrap(); - let expected_trace = vec![FlatTrace { - trace_address: Default::default(), - subtraces: 1, - action: trace::Action::Call(trace::Call { - from: Address::from_str("9cce34f7ab185c7aba1b7c8140d620b4bda941d6").unwrap(), - to: Address::from_low_u64_be(0xa), - value: 100.into(), - gas: 79000.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::Call(trace::CallResult { - gas_used: U256::from(79_000), - output: vec![] - }), - }, FlatTrace { - trace_address: vec![0].into_iter().collect(), - subtraces: 0, - action: trace::Action::Call(trace::Call { - from: Address::from_low_u64_be(0xa), - to: Address::from_low_u64_be(0xb), - value: 0.into(), - gas: 78934.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::FailedCall(TraceError::OutOfGas), - }]; - - assert_eq!(result.trace, expected_trace); - } - - #[test] - fn should_trace_call_with_subcall_with_subcall_transaction() { - let _ = env_logger::try_init(); - - let mut state = get_temp_state(); - - let mut info = EnvInfo::default(); - info.gas_limit = 1_000_000.into(); - let machine = make_frontier_machine(5); - - let t = Transaction { - nonce: 0.into(), - gas_price: 0.into(), - gas: 100_000.into(), - action: Action::Call(Address::from_low_u64_be(0xa)), - value: 100.into(), - data: vec![], - }.sign(&secret(), None); - - state.init_code(&Address::from_low_u64_be(0xa), FromHex::from_hex("60006000600060006000600b602b5a03f1").unwrap()).unwrap(); - state.init_code(&Address::from_low_u64_be(0xb), FromHex::from_hex("60006000600060006000600c602b5a03f1").unwrap()).unwrap(); - state.init_code(&Address::from_low_u64_be(0xc), FromHex::from_hex("6000").unwrap()).unwrap(); - state.add_balance(&t.sender(), &(100.into()), CleanupMode::NoEmpty).unwrap(); - let result = state.apply(&info, &machine, &t, true).unwrap(); - let expected_trace = vec![FlatTrace { - trace_address: Default::default(), - subtraces: 1, - action: trace::Action::Call(trace::Call { - from: Address::from_str("9cce34f7ab185c7aba1b7c8140d620b4bda941d6").unwrap(), - to: Address::from_low_u64_be(0xa), - value: 100.into(), - gas: 79000.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::Call(trace::CallResult { - gas_used: U256::from(135), - output: vec![] - }), - }, FlatTrace { - trace_address: vec![0].into_iter().collect(), - subtraces: 1, - action: trace::Action::Call(trace::Call { - from: Address::from_low_u64_be(0xa), - to: Address::from_low_u64_be(0xb), - value: 0.into(), - gas: 78934.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::Call(trace::CallResult { - gas_used: U256::from(69), - output: vec![] - }), - }, FlatTrace { - trace_address: vec![0, 0].into_iter().collect(), - subtraces: 0, - action: trace::Action::Call(trace::Call { - from: Address::from_low_u64_be(0xb), - to: Address::from_low_u64_be(0xc), - value: 0.into(), - gas: 78868.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::Call(trace::CallResult { - gas_used: U256::from(3), - output: vec![] - }), - }]; - - assert_eq!(result.trace, expected_trace); - } - - #[test] - fn should_trace_failed_subcall_with_subcall_transaction() { - let _ = env_logger::try_init(); - - let mut state = get_temp_state(); - - let mut info = EnvInfo::default(); - info.gas_limit = 1_000_000.into(); - let machine = make_frontier_machine(5); - - let t = Transaction { - nonce: 0.into(), - gas_price: 0.into(), - gas: 100_000.into(), - action: Action::Call(Address::from_low_u64_be(0xa)), - value: 100.into(), - data: vec![],//600480600b6000396000f35b600056 - }.sign(&secret(), None); - - state.init_code(&Address::from_low_u64_be(0xa), FromHex::from_hex("60006000600060006000600b602b5a03f1").unwrap()).unwrap(); - state.init_code(&Address::from_low_u64_be(0xb), FromHex::from_hex("60006000600060006000600c602b5a03f1505b601256").unwrap()).unwrap(); - state.init_code(&Address::from_low_u64_be(0xc), FromHex::from_hex("6000").unwrap()).unwrap(); - state.add_balance(&t.sender(), &(100.into()), CleanupMode::NoEmpty).unwrap(); - let result = state.apply(&info, &machine, &t, true).unwrap(); - - let expected_trace = vec![FlatTrace { - trace_address: Default::default(), - subtraces: 1, - action: trace::Action::Call(trace::Call { - from: Address::from_str("9cce34f7ab185c7aba1b7c8140d620b4bda941d6").unwrap(), - to: Address::from_low_u64_be(0xa), - value: 100.into(), - gas: 79000.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::Call(trace::CallResult { - gas_used: U256::from(79_000), - output: vec![] - }) - }, FlatTrace { - trace_address: vec![0].into_iter().collect(), - subtraces: 1, - action: trace::Action::Call(trace::Call { - from: Address::from_low_u64_be(0xa), - to: Address::from_low_u64_be(0xb), - value: 0.into(), - gas: 78934.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::FailedCall(TraceError::OutOfGas), - }, FlatTrace { - trace_address: vec![0, 0].into_iter().collect(), - subtraces: 0, - action: trace::Action::Call(trace::Call { - from: Address::from_low_u64_be(0xb), - to: Address::from_low_u64_be(0xc), - value: 0.into(), - gas: 78868.into(), - call_type: CallType::Call, - input: vec![], - }), - result: trace::Res::Call(trace::CallResult { - gas_used: U256::from(3), - output: vec![] - }), - }]; - - assert_eq!(result.trace, expected_trace); - } - - #[test] - fn should_trace_suicide() { - let _ = env_logger::try_init(); - - let mut state = get_temp_state(); - - let mut info = EnvInfo::default(); - info.gas_limit = 1_000_000.into(); - let machine = make_frontier_machine(5); - - let t = Transaction { - nonce: 0.into(), - gas_price: 0.into(), - gas: 100_000.into(), - action: Action::Call(Address::from_low_u64_be(0xa)), - value: 100.into(), - data: vec![], - }.sign(&secret(), None); - - state.init_code(&Address::from_low_u64_be(0xa), FromHex::from_hex("73000000000000000000000000000000000000000bff").unwrap()).unwrap(); - state.add_balance(&Address::from_low_u64_be(0xa), &50.into(), CleanupMode::NoEmpty).unwrap(); - state.add_balance(&t.sender(), &100.into(), CleanupMode::NoEmpty).unwrap(); - let result = state.apply(&info, &machine, &t, true).unwrap(); - let expected_trace = vec![FlatTrace { - trace_address: Default::default(), - subtraces: 1, - action: trace::Action::Call(trace::Call { - from: Address::from_str("9cce34f7ab185c7aba1b7c8140d620b4bda941d6").unwrap(), - to: Address::from_low_u64_be(0xa), - value: 100.into(), - gas: 79000.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::Call(trace::CallResult { - gas_used: 3.into(), - output: vec![] - }), - }, FlatTrace { - trace_address: vec![0].into_iter().collect(), - subtraces: 0, - action: trace::Action::Suicide(trace::Suicide { - address: Address::from_low_u64_be(0xa), - refund_address: Address::from_low_u64_be(0xb), - balance: 150.into(), - }), - result: trace::Res::None, - }]; - - assert_eq!(result.trace, expected_trace); - } - - #[test] - fn code_from_database() { - let a = Address::zero(); - let (root, db) = { - let mut state = get_temp_state(); - state.require_or_from(&a, false, || Account::new_contract(42.into(), 0.into(), KECCAK_NULL_RLP), |_|{}).unwrap(); - state.init_code(&a, vec![1, 2, 3]).unwrap(); - assert_eq!(state.code(&a).unwrap(), Some(Arc::new(vec![1u8, 2, 3]))); - state.commit().unwrap(); - assert_eq!(state.code(&a).unwrap(), Some(Arc::new(vec![1u8, 2, 3]))); - state.drop() - }; - - let state = State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap(); - assert_eq!(state.code(&a).unwrap(), Some(Arc::new(vec![1u8, 2, 3]))); - } - - #[test] - fn storage_at_from_database() { - let a = Address::zero(); - let (root, db) = { - let mut state = get_temp_state(); - state.set_storage(&a, BigEndianHash::from_uint(&U256::from(1u64)), BigEndianHash::from_uint(&U256::from(69u64))).unwrap(); - state.commit().unwrap(); - state.drop() - }; - - let s = State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap(); - let h1 = BigEndianHash::from_uint(&U256::from(1u64)); - let h2 = BigEndianHash::from_uint(&U256::from(69u64)); - assert_eq!(s.storage_at(&a, &h1).unwrap(), h2); - } - - #[test] - fn get_from_database() { - let a = Address::zero(); - let (root, db) = { - let mut state = get_temp_state(); - state.inc_nonce(&a).unwrap(); - state.add_balance(&a, &U256::from(69u64), CleanupMode::NoEmpty).unwrap(); - state.commit().unwrap(); - assert_eq!(state.balance(&a).unwrap(), U256::from(69u64)); - state.drop() - }; - - let state = State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap(); - assert_eq!(state.balance(&a).unwrap(), U256::from(69u64)); - assert_eq!(state.nonce(&a).unwrap(), U256::from(1u64)); - } - - #[test] - fn remove() { - let a = Address::zero(); - let mut state = get_temp_state(); - assert_eq!(state.exists(&a).unwrap(), false); - assert_eq!(state.exists_and_not_null(&a).unwrap(), false); - state.inc_nonce(&a).unwrap(); - assert_eq!(state.exists(&a).unwrap(), true); - assert_eq!(state.exists_and_not_null(&a).unwrap(), true); - assert_eq!(state.nonce(&a).unwrap(), U256::from(1u64)); - state.kill_account(&a); - assert_eq!(state.exists(&a).unwrap(), false); - assert_eq!(state.exists_and_not_null(&a).unwrap(), false); - assert_eq!(state.nonce(&a).unwrap(), U256::from(0u64)); - } - - #[test] - fn empty_account_is_not_created() { - let a = Address::zero(); - let db = get_temp_state_db(); - let (root, db) = { - let mut state = State::new(db, U256::from(0), Default::default()); - state.add_balance(&a, &U256::default(), CleanupMode::NoEmpty).unwrap(); // create an empty account - state.commit().unwrap(); - state.drop() - }; - let state = State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap(); - assert!(!state.exists(&a).unwrap()); - assert!(!state.exists_and_not_null(&a).unwrap()); - } - - #[test] - fn empty_account_exists_when_creation_forced() { - let a = Address::zero(); - let db = get_temp_state_db(); - let (root, db) = { - let mut state = State::new(db, U256::from(0), Default::default()); - state.add_balance(&a, &U256::default(), CleanupMode::ForceCreate).unwrap(); // create an empty account - state.commit().unwrap(); - state.drop() - }; - let state = State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap(); - assert!(state.exists(&a).unwrap()); - assert!(!state.exists_and_not_null(&a).unwrap()); - } - - #[test] - fn remove_from_database() { - let a = Address::zero(); - let (root, db) = { - let mut state = get_temp_state(); - state.inc_nonce(&a).unwrap(); - state.commit().unwrap(); - assert_eq!(state.exists(&a).unwrap(), true); - assert_eq!(state.nonce(&a).unwrap(), U256::from(1u64)); - state.drop() - }; - - let (root, db) = { - let mut state = State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap(); - assert_eq!(state.exists(&a).unwrap(), true); - assert_eq!(state.nonce(&a).unwrap(), U256::from(1u64)); - state.kill_account(&a); - state.commit().unwrap(); - assert_eq!(state.exists(&a).unwrap(), false); - assert_eq!(state.nonce(&a).unwrap(), U256::from(0u64)); - state.drop() - }; - - let state = State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap(); - assert_eq!(state.exists(&a).unwrap(), false); - assert_eq!(state.nonce(&a).unwrap(), U256::from(0u64)); - } - - #[test] - fn alter_balance() { - let mut state = get_temp_state(); - let a = Address::zero(); - let b = Address::from_low_u64_be(1u64); - state.add_balance(&a, &U256::from(69u64), CleanupMode::NoEmpty).unwrap(); - assert_eq!(state.balance(&a).unwrap(), U256::from(69u64)); - state.commit().unwrap(); - assert_eq!(state.balance(&a).unwrap(), U256::from(69u64)); - state.sub_balance(&a, &U256::from(42u64), &mut CleanupMode::NoEmpty).unwrap(); - assert_eq!(state.balance(&a).unwrap(), U256::from(27u64)); - state.commit().unwrap(); - assert_eq!(state.balance(&a).unwrap(), U256::from(27u64)); - state.transfer_balance(&a, &b, &U256::from(18u64), CleanupMode::NoEmpty).unwrap(); - assert_eq!(state.balance(&a).unwrap(), U256::from(9u64)); - assert_eq!(state.balance(&b).unwrap(), U256::from(18u64)); - state.commit().unwrap(); - assert_eq!(state.balance(&a).unwrap(), U256::from(9u64)); - assert_eq!(state.balance(&b).unwrap(), U256::from(18u64)); - } - - #[test] - fn alter_nonce() { - let mut state = get_temp_state(); - let a = Address::zero(); - state.inc_nonce(&a).unwrap(); - assert_eq!(state.nonce(&a).unwrap(), U256::from(1u64)); - state.inc_nonce(&a).unwrap(); - assert_eq!(state.nonce(&a).unwrap(), U256::from(2u64)); - state.commit().unwrap(); - assert_eq!(state.nonce(&a).unwrap(), U256::from(2u64)); - state.inc_nonce(&a).unwrap(); - assert_eq!(state.nonce(&a).unwrap(), U256::from(3u64)); - state.commit().unwrap(); - assert_eq!(state.nonce(&a).unwrap(), U256::from(3u64)); - } - - #[test] - fn balance_nonce() { - let mut state = get_temp_state(); - let a = Address::zero(); - assert_eq!(state.balance(&a).unwrap(), U256::from(0u64)); - assert_eq!(state.nonce(&a).unwrap(), U256::from(0u64)); - state.commit().unwrap(); - assert_eq!(state.balance(&a).unwrap(), U256::from(0u64)); - assert_eq!(state.nonce(&a).unwrap(), U256::from(0u64)); - } - - #[test] - fn ensure_cached() { - let mut state = get_temp_state(); - let a = Address::zero(); - state.require(&a, false).unwrap(); - state.commit().unwrap(); - assert_eq!(*state.root(), H256::from_str("0ce23f3c809de377b008a4a3ee94a0834aac8bec1f86e28ffe4fdb5a15b0c785").unwrap()); - } - - #[test] - fn checkpoint_basic() { - let mut state = get_temp_state(); - let a = Address::zero(); - state.checkpoint(); - state.add_balance(&a, &U256::from(69u64), CleanupMode::NoEmpty).unwrap(); - assert_eq!(state.balance(&a).unwrap(), U256::from(69u64)); - state.discard_checkpoint(); - assert_eq!(state.balance(&a).unwrap(), U256::from(69u64)); - state.checkpoint(); - state.add_balance(&a, &U256::from(1u64), CleanupMode::NoEmpty).unwrap(); - assert_eq!(state.balance(&a).unwrap(), U256::from(70u64)); - state.revert_to_checkpoint(); - assert_eq!(state.balance(&a).unwrap(), U256::from(69u64)); - } - - #[test] - fn checkpoint_nested() { - let mut state = get_temp_state(); - let a = Address::zero(); - state.checkpoint(); - state.checkpoint(); - state.add_balance(&a, &U256::from(69u64), CleanupMode::NoEmpty).unwrap(); - assert_eq!(state.balance(&a).unwrap(), U256::from(69u64)); - state.discard_checkpoint(); - assert_eq!(state.balance(&a).unwrap(), U256::from(69u64)); - state.revert_to_checkpoint(); - assert_eq!(state.balance(&a).unwrap(), U256::from(0)); - } - - #[test] - fn checkpoint_revert_to_get_storage_at() { - let mut state = get_temp_state(); - let a = Address::zero(); - let k = BigEndianHash::from_uint(&U256::from(0)); - - let c0 = state.checkpoint(); - let c1 = state.checkpoint(); - state.set_storage(&a, k, BigEndianHash::from_uint(&U256::from(1))).unwrap(); - - assert_eq!(state.checkpoint_storage_at(c0, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(0)))); - assert_eq!(state.checkpoint_storage_at(c1, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(0)))); - assert_eq!(state.storage_at(&a, &k).unwrap(), BigEndianHash::from_uint(&U256::from(1))); - - state.revert_to_checkpoint(); // Revert to c1. - assert_eq!(state.checkpoint_storage_at(c0, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(0)))); - assert_eq!(state.storage_at(&a, &k).unwrap(), BigEndianHash::from_uint(&U256::from(0))); - } - - #[test] - fn checkpoint_from_empty_get_storage_at() { - let mut state = get_temp_state(); - let a = Address::zero(); - let k = BigEndianHash::from_uint(&U256::from(0)); - let k2 = BigEndianHash::from_uint(&U256::from(1)); - - assert_eq!(state.storage_at(&a, &k).unwrap(), BigEndianHash::from_uint(&U256::from(0))); - state.clear(); - - let c0 = state.checkpoint(); - state.new_contract(&a, U256::zero(), U256::zero()).unwrap(); - let c1 = state.checkpoint(); - state.set_storage(&a, k, BigEndianHash::from_uint(&U256::from(1))).unwrap(); - let c2 = state.checkpoint(); - let c3 = state.checkpoint(); - state.set_storage(&a, k2, BigEndianHash::from_uint(&U256::from(3))).unwrap(); - state.set_storage(&a, k, BigEndianHash::from_uint(&U256::from(3))).unwrap(); - let c4 = state.checkpoint(); - state.set_storage(&a, k, BigEndianHash::from_uint(&U256::from(4))).unwrap(); - let c5 = state.checkpoint(); - - assert_eq!(state.checkpoint_storage_at(c0, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(0)))); - assert_eq!(state.checkpoint_storage_at(c1, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(0)))); - assert_eq!(state.checkpoint_storage_at(c2, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(1)))); - assert_eq!(state.checkpoint_storage_at(c3, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(1)))); - assert_eq!(state.checkpoint_storage_at(c4, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(3)))); - assert_eq!(state.checkpoint_storage_at(c5, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(4)))); - - state.discard_checkpoint(); // Commit/discard c5. - assert_eq!(state.checkpoint_storage_at(c0, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(0)))); - assert_eq!(state.checkpoint_storage_at(c1, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(0)))); - assert_eq!(state.checkpoint_storage_at(c2, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(1)))); - assert_eq!(state.checkpoint_storage_at(c3, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(1)))); - assert_eq!(state.checkpoint_storage_at(c4, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(3)))); - - state.revert_to_checkpoint(); // Revert to c4. - assert_eq!(state.checkpoint_storage_at(c0, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(0)))); - assert_eq!(state.checkpoint_storage_at(c1, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(0)))); - assert_eq!(state.checkpoint_storage_at(c2, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(1)))); - assert_eq!(state.checkpoint_storage_at(c3, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(1)))); - - state.discard_checkpoint(); // Commit/discard c3. - assert_eq!(state.checkpoint_storage_at(c0, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(0)))); - assert_eq!(state.checkpoint_storage_at(c1, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(0)))); - assert_eq!(state.checkpoint_storage_at(c2, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(1)))); - - state.revert_to_checkpoint(); // Revert to c2. - assert_eq!(state.checkpoint_storage_at(c0, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(0)))); - assert_eq!(state.checkpoint_storage_at(c1, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(0)))); - - state.discard_checkpoint(); // Commit/discard c1. - assert_eq!(state.checkpoint_storage_at(c0, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(0)))); - } - - #[test] - fn checkpoint_get_storage_at() { - let mut state = get_temp_state(); - let a = Address::zero(); - let k = BigEndianHash::from_uint(&U256::from(0)); - let k2 = BigEndianHash::from_uint(&U256::from(1)); - - state.set_storage(&a, k, BigEndianHash::from_uint(&U256::from(0xffff))).unwrap(); - state.commit().unwrap(); - state.clear(); - - assert_eq!(state.storage_at(&a, &k).unwrap(), BigEndianHash::from_uint(&U256::from(0xffff))); - state.clear(); - - let cm1 = state.checkpoint(); - let c0 = state.checkpoint(); - state.new_contract(&a, U256::zero(), U256::zero()).unwrap(); - let c1 = state.checkpoint(); - state.set_storage(&a, k, BigEndianHash::from_uint(&U256::from(1))).unwrap(); - let c2 = state.checkpoint(); - let c3 = state.checkpoint(); - state.set_storage(&a, k2, BigEndianHash::from_uint(&U256::from(3))).unwrap(); - state.set_storage(&a, k, BigEndianHash::from_uint(&U256::from(3))).unwrap(); - let c4 = state.checkpoint(); - state.set_storage(&a, k, BigEndianHash::from_uint(&U256::from(4))).unwrap(); - let c5 = state.checkpoint(); - - assert_eq!(state.checkpoint_storage_at(cm1, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(0xffff)))); - assert_eq!(state.checkpoint_storage_at(c0, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(0xffff)))); - assert_eq!(state.checkpoint_storage_at(c1, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(0)))); - assert_eq!(state.checkpoint_storage_at(c2, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(1)))); - assert_eq!(state.checkpoint_storage_at(c3, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(1)))); - assert_eq!(state.checkpoint_storage_at(c4, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(3)))); - assert_eq!(state.checkpoint_storage_at(c5, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(4)))); - - state.discard_checkpoint(); // Commit/discard c5. - assert_eq!(state.checkpoint_storage_at(cm1, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(0xffff)))); - assert_eq!(state.checkpoint_storage_at(c0, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(0xffff)))); - assert_eq!(state.checkpoint_storage_at(c1, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(0)))); - assert_eq!(state.checkpoint_storage_at(c2, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(1)))); - assert_eq!(state.checkpoint_storage_at(c3, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(1)))); - assert_eq!(state.checkpoint_storage_at(c4, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(3)))); - - state.revert_to_checkpoint(); // Revert to c4. - assert_eq!(state.checkpoint_storage_at(cm1, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(0xffff)))); - assert_eq!(state.checkpoint_storage_at(c0, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(0xffff)))); - assert_eq!(state.checkpoint_storage_at(c1, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(0)))); - assert_eq!(state.checkpoint_storage_at(c2, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(1)))); - assert_eq!(state.checkpoint_storage_at(c3, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(1)))); - - state.discard_checkpoint(); // Commit/discard c3. - assert_eq!(state.checkpoint_storage_at(cm1, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(0xffff)))); - assert_eq!(state.checkpoint_storage_at(c0, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(0xffff)))); - assert_eq!(state.checkpoint_storage_at(c1, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(0)))); - assert_eq!(state.checkpoint_storage_at(c2, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(1)))); - - state.revert_to_checkpoint(); // Revert to c2. - assert_eq!(state.checkpoint_storage_at(cm1, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(0xffff)))); - assert_eq!(state.checkpoint_storage_at(c0, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(0xffff)))); - assert_eq!(state.checkpoint_storage_at(c1, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(0)))); - - state.discard_checkpoint(); // Commit/discard c1. - assert_eq!(state.checkpoint_storage_at(cm1, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(0xffff)))); - assert_eq!(state.checkpoint_storage_at(c0, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(0xffff)))); - } - - #[test] - fn kill_account_with_checkpoints() { - let mut state = get_temp_state(); - let a = Address::zero(); - let k = BigEndianHash::from_uint(&U256::from(0)); - state.checkpoint(); - state.set_storage(&a, k, BigEndianHash::from_uint(&U256::from(1))).unwrap(); - state.checkpoint(); - state.kill_account(&a); - - assert_eq!(state.storage_at(&a, &k).unwrap(), BigEndianHash::from_uint(&U256::from(0))); - state.revert_to_checkpoint(); - assert_eq!(state.storage_at(&a, &k).unwrap(), BigEndianHash::from_uint(&U256::from(1))); - } - - #[test] - fn create_contract_fail() { - let mut state = get_temp_state(); - let orig_root = state.root().clone(); - let a = Address::from_low_u64_be(1000); - - state.checkpoint(); // c1 - state.new_contract(&a, U256::zero(), U256::zero()).unwrap(); - state.add_balance(&a, &U256::from(1), CleanupMode::ForceCreate).unwrap(); - state.checkpoint(); // c2 - state.add_balance(&a, &U256::from(1), CleanupMode::ForceCreate).unwrap(); - state.discard_checkpoint(); // discard c2 - state.revert_to_checkpoint(); // revert to c1 - assert_eq!(state.exists(&a).unwrap(), false); - - state.commit().unwrap(); - assert_eq!(orig_root, state.root().clone()); - } - - #[test] - fn create_contract_fail_previous_storage() { - let mut state = get_temp_state(); - let a = Address::from_low_u64_be(1000); - let k = BigEndianHash::from_uint(&U256::from(0)); - - state.set_storage(&a, k, BigEndianHash::from_uint(&U256::from(0xffff))).unwrap(); - state.commit().unwrap(); - state.clear(); - - let orig_root = state.root().clone(); - assert_eq!(state.storage_at(&a, &k).unwrap(), BigEndianHash::from_uint(&U256::from(0xffff))); - state.clear(); - - state.checkpoint(); // c1 - state.new_contract(&a, U256::zero(), U256::zero()).unwrap(); - state.checkpoint(); // c2 - state.set_storage(&a, k, BigEndianHash::from_uint(&U256::from(2))).unwrap(); - state.revert_to_checkpoint(); // revert to c2 - assert_eq!(state.storage_at(&a, &k).unwrap(), BigEndianHash::from_uint(&U256::from(0))); - state.revert_to_checkpoint(); // revert to c1 - assert_eq!(state.storage_at(&a, &k).unwrap(), BigEndianHash::from_uint(&U256::from(0xffff))); - - state.commit().unwrap(); - assert_eq!(orig_root, state.root().clone()); - } - - #[test] - fn create_empty() { - let mut state = get_temp_state(); - state.commit().unwrap(); - assert_eq!(*state.root(), H256::from_str("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421").unwrap()); - } - - #[test] - fn should_not_panic_on_state_diff_with_storage() { - let mut state = get_temp_state(); - let a = Address::from_low_u64_be(0xa); - state.init_code(&a, b"abcdefg".to_vec()).unwrap();; - state.add_balance(&a, &256.into(), CleanupMode::NoEmpty).unwrap(); - state.set_storage(&a, H256::from_low_u64_be(0xb), H256::from_low_u64_be(0xc).into()).unwrap(); - - let mut new_state = state.clone(); - new_state.set_storage(&a, H256::from_low_u64_be(0xb), H256::from_low_u64_be(0xd).into()).unwrap(); - - new_state.diff_from(state).unwrap(); - } - - #[test] - fn should_kill_garbage() { - let a = Address::from_low_u64_be(10); - let b = Address::from_low_u64_be(20); - let c = Address::from_low_u64_be(30); - let d = Address::from_low_u64_be(40); - let e = Address::from_low_u64_be(50); - let x = Address::from_low_u64_be(0); - let db = get_temp_state_db(); - let (root, db) = { - let mut state = State::new(db, U256::from(0), Default::default()); - state.add_balance(&a, &U256::default(), CleanupMode::ForceCreate).unwrap(); // create an empty account - state.add_balance(&b, &100.into(), CleanupMode::ForceCreate).unwrap(); // create a dust account - state.add_balance(&c, &101.into(), CleanupMode::ForceCreate).unwrap(); // create a normal account - state.add_balance(&d, &99.into(), CleanupMode::ForceCreate).unwrap(); // create another dust account - state.new_contract(&e, 100.into(), 1.into()).unwrap(); // create a contract account - state.init_code(&e, vec![0x00]).unwrap(); - state.commit().unwrap(); - state.drop() - }; - - let mut state = State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap(); - let mut touched = HashSet::new(); - state.add_balance(&a, &U256::default(), CleanupMode::TrackTouched(&mut touched)).unwrap(); // touch an account - state.transfer_balance(&b, &x, &1.into(), CleanupMode::TrackTouched(&mut touched)).unwrap(); // touch an account decreasing its balance - state.transfer_balance(&c, &x, &1.into(), CleanupMode::TrackTouched(&mut touched)).unwrap(); // touch an account decreasing its balance - state.transfer_balance(&e, &x, &1.into(), CleanupMode::TrackTouched(&mut touched)).unwrap(); // touch an account decreasing its balance - state.kill_garbage(&touched, true, &None, false).unwrap(); - assert!(!state.exists(&a).unwrap()); - assert!(state.exists(&b).unwrap()); - state.kill_garbage(&touched, true, &Some(100.into()), false).unwrap(); - assert!(!state.exists(&b).unwrap()); - assert!(state.exists(&c).unwrap()); - assert!(state.exists(&d).unwrap()); - assert!(state.exists(&e).unwrap()); - state.kill_garbage(&touched, true, &Some(100.into()), true).unwrap(); - assert!(state.exists(&c).unwrap()); - assert!(state.exists(&d).unwrap()); - assert!(!state.exists(&e).unwrap()); - } - - #[test] - fn should_trace_diff_suicided_accounts() { - let a = Address::from_low_u64_be(10); - let db = get_temp_state_db(); - let (root, db) = { - let mut state = State::new(db, U256::from(0), Default::default()); - state.add_balance(&a, &100.into(), CleanupMode::ForceCreate).unwrap(); - state.commit().unwrap(); - state.drop() - }; - - let mut state = State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap(); - let original = state.clone(); - state.kill_account(&a); - - let diff = state.diff_from(original).unwrap(); - let diff_map = diff.raw; - assert_eq!(diff_map.len(), 1); - assert!(diff_map.get(&a).is_some()); - assert_eq!(diff_map.get(&a), - pod::account::diff_pod(Some(&PodAccount { - balance: U256::from(100), - nonce: U256::zero(), - code: Some(Default::default()), - storage: Default::default() - }), None).as_ref()); - } - - #[test] - fn should_trace_diff_unmodified_storage() { - let a = Address::from_low_u64_be(10); - let db = get_temp_state_db(); - - let (root, db) = { - let mut state = State::new(db, U256::from(0), Default::default()); - state.set_storage(&a, BigEndianHash::from_uint(&U256::from(1u64)), BigEndianHash::from_uint(&U256::from(20u64))).unwrap(); - state.commit().unwrap(); - state.drop() - }; - - let mut state = State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap(); - let original = state.clone(); - state.set_storage(&a, BigEndianHash::from_uint(&U256::from(1u64)), BigEndianHash::from_uint(&U256::from(100u64))).unwrap(); - - let diff = state.diff_from(original).unwrap(); - let diff_map = diff.raw; - assert_eq!(diff_map.len(), 1); - assert!(diff_map.get(&a).is_some()); - assert_eq!(diff_map.get(&a), - pod::account::diff_pod(Some(&PodAccount { - balance: U256::zero(), - nonce: U256::zero(), - code: Some(Default::default()), - storage: vec![(BigEndianHash::from_uint(&U256::from(1u64)), BigEndianHash::from_uint(&U256::from(20u64)))] - .into_iter().collect(), - }), Some(&PodAccount { - balance: U256::zero(), - nonce: U256::zero(), - code: Some(Default::default()), - storage: vec![(BigEndianHash::from_uint(&U256::from(1u64)), BigEndianHash::from_uint(&U256::from(100u64)))] - .into_iter().collect(), - })).as_ref()); - } - - #[cfg(feature="to-pod-full")] - #[test] - fn should_get_full_pod_storage_values() { - use trie::{TrieFactory, TrieSpec}; - - let a = Address::from_low_u64_be(10); - let db = get_temp_state_db(); - - let factories = Factories { - vm: Default::default(), - trie: TrieFactory::new(TrieSpec::Fat), - accountdb: Default::default(), - }; - - let get_pod_state_val = |pod_state : &PodState, ak, k| { - pod_state.get().get(ak).unwrap().storage.get(&k).unwrap().clone() - }; - - let storage_address: H256 = BigEndianHash::from_uint(&U256::from(1u64)); - - let (root, db) = { - let mut state = State::new(db, U256::from(0), factories.clone()); - state.set_storage(&a, storage_address.clone(), BigEndianHash::from_uint(&U256::from(20u64))).unwrap(); - let dump = state.to_pod_full().unwrap(); - assert_eq!(get_pod_state_val(&dump, &a, storage_address.clone()), BigEndianHash::from_uint(&U256::from(20u64))); - state.commit().unwrap(); - let dump = state.to_pod_full().unwrap(); - assert_eq!(get_pod_state_val(&dump, &a, storage_address.clone()), BigEndianHash::from_uint(&U256::from(20u64))); - state.drop() - }; - - let mut state = State::from_existing(db, root, U256::from(0u8), factories).unwrap(); - let dump = state.to_pod_full().unwrap(); - assert_eq!(get_pod_state_val(&dump, &a, storage_address.clone()), BigEndianHash::from_uint(&U256::from(20u64))); - state.set_storage(&a, storage_address.clone(), BigEndianHash::from_uint(&U256::from(21u64))).unwrap(); - let dump = state.to_pod_full().unwrap(); - assert_eq!(get_pod_state_val(&dump, &a, storage_address.clone()), BigEndianHash::from_uint(&U256::from(21u64))); - state.commit().unwrap(); - state.set_storage(&a, storage_address.clone(), BigEndianHash::from_uint(&U256::from(0u64))).unwrap(); - let dump = state.to_pod_full().unwrap(); - assert_eq!(get_pod_state_val(&dump, &a, storage_address.clone()), BigEndianHash::from_uint(&U256::from(0u64))); - - } - -} From 10ab382a4cd41639b1667f59232d4920506216e2 Mon Sep 17 00:00:00 2001 From: David Palm Date: Fri, 5 Jul 2019 22:49:28 +0200 Subject: [PATCH 40/87] cleanup --- ethcore/state-account/Cargo.toml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/ethcore/state-account/Cargo.toml b/ethcore/state-account/Cargo.toml index 0dc78660d38..4ad59cfeb0c 100644 --- a/ethcore/state-account/Cargo.toml +++ b/ethcore/state-account/Cargo.toml @@ -36,9 +36,3 @@ account-db = { path = "../account-db" } rlp_compress = { path = "../../util/rlp-compress" } journaldb = { path = "../../util/journaldb" } parity-bytes = "0.1.0" -ethkey = { path = "../../accounts/ethkey" } -trace = { path = "../trace" } -pod = { path = "../pod" } -ethcore = { path = "..", features = ["test-helpers"]} -rustc-hex = "2.0.1" -env_logger = "0.5" From cff42804d0030a9e643e6c9d62e72fc6328dea7a Mon Sep 17 00:00:00 2001 From: David Palm Date: Fri, 5 Jul 2019 22:58:53 +0200 Subject: [PATCH 41/87] cleanup --- ethcore/state-account/Cargo.toml | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/ethcore/state-account/Cargo.toml b/ethcore/state-account/Cargo.toml index 4ad59cfeb0c..09c89dbb614 100644 --- a/ethcore/state-account/Cargo.toml +++ b/ethcore/state-account/Cargo.toml @@ -10,15 +10,19 @@ common-types = { path = "../types"} derive_more = "0.15.0" ethereum-types = "0.6.0" ethtrie = { package = "patricia-trie-ethereum", path = "../../util/patricia-trie-ethereum" } +evm = { path = "../evm" } factories = { path = "../factories" } hash-db = "0.12.4" +journaldb = { path = "../../util/journaldb" } keccak-hash = "0.2.0" keccak-hasher = { path = "../../util/keccak-hasher" } kvdb = "0.1.0" log = "0.4" lru-cache = "0.1.2" +memory-db = "0.12.4" parity-bytes = "0.1.0" parity-util-mem = "0.1.0" +parking_lot = "0.8.0" pod = { path = "../pod" } rlp = "0.4.0" serde = { version = "1.0", features = ["derive"] } @@ -26,13 +30,8 @@ trace = { path = "../trace" } trie-db = "0.12.4" vm = { path = "../vm" } -parking_lot = "0.7.0" -memory-db = "0.12.4" -journaldb = { path = "../../util/journaldb" } -evm = { path = "../evm" } - [dev-dependencies] account-db = { path = "../account-db" } -rlp_compress = { path = "../../util/rlp-compress" } journaldb = { path = "../../util/journaldb" } parity-bytes = "0.1.0" +rlp_compress = { path = "../../util/rlp-compress" } From cf4d8d57f0ff512e0e68fb95fbb393b34ebd8ee7 Mon Sep 17 00:00:00 2001 From: David Palm Date: Fri, 5 Jul 2019 23:05:49 +0200 Subject: [PATCH 42/87] Cleanup state-account errors --- ethcore/state-account/src/lib.rs | 25 ++----------------------- 1 file changed, 2 insertions(+), 23 deletions(-) diff --git a/ethcore/state-account/src/lib.rs b/ethcore/state-account/src/lib.rs index 7c1027860d3..d3b46ddf687 100644 --- a/ethcore/state-account/src/lib.rs +++ b/ethcore/state-account/src/lib.rs @@ -17,34 +17,13 @@ pub mod account; pub mod backend; pub mod substate; -// TODO: WIP pub mod state; +pub mod error; pub use { account::Account, backend::Backend, + error::Error, substate::Substate, state::{State, CleanupMode}, }; - -// TODO: move to an errors.rs -use derive_more::{Display, From}; -// TODO: sort out error handling -#[derive(Debug, Display, From)] -pub enum Error { - /// Trie error. - Trie(ethtrie::TrieError), - /// Decoder error. - Decoder(rlp::DecoderError), - // Io error. -// Io(IoError), // TODO: maybe not needed? -} - -// TODO: needed? -impl std::error::Error for Error {} - -impl From> for Error where Error: From { - fn from(err: Box) -> Self { - Error::from(*err) - } -} From e4a5176c8dee585d44d0addb6f1356483e9cf238 Mon Sep 17 00:00:00 2001 From: David Palm Date: Fri, 5 Jul 2019 23:16:31 +0200 Subject: [PATCH 43/87] Fix more todos Add module docs --- Cargo.lock | 44 +++++++++++++++++++++++++++++++- ethcore/src/block.rs | 3 +-- ethcore/src/error.rs | 3 +-- ethcore/src/machine.rs | 2 +- ethcore/state-account/src/lib.rs | 8 ++++++ 5 files changed, 54 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bd0a9d4523b..25f9dd34b9d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2296,6 +2296,14 @@ dependencies = [ "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "lock_api" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "log" version = "0.3.9" @@ -3145,6 +3153,16 @@ dependencies = [ "parking_lot_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "parking_lot" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lock_api 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot_core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "parking_lot_core" version = "0.3.1" @@ -3172,6 +3190,21 @@ dependencies = [ "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "parking_lot_core" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "patricia-trie-ethereum" version = "0.1.0" @@ -3802,6 +3835,11 @@ name = "scopeguard" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "scopeguard" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "scrypt" version = "0.2.0" @@ -3996,7 +4034,7 @@ dependencies = [ "memory-db 0.12.4 (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.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "patricia-trie-ethereum 0.1.0", "pod 0.1.0", "rlp 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -5030,6 +5068,7 @@ dependencies = [ "checksum linked-hash-map 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "70fb39025bc7cdd76305867c4eccf2f2dcf6e9a57f5b21a93e1c2d86cd03ec9e" "checksum local-encoding 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e1ceb20f39ff7ae42f3ff9795f3986b1daad821caaa1e1732a0944103a5a1a66" "checksum lock_api 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "775751a3e69bde4df9b38dd00a1b5d6ac13791e4223d4a0506577f0dd27cfb7a" +"checksum lock_api 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ed946d4529956a20f2d63ebe1b69996d5a2137c91913fe3ebbeff957f5bca7ff" "checksum log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b" "checksum log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c84ec4b527950aa83a329754b01dbe3f58361d1c5efacd1f6d68c494d08a17c6" "checksum lru-cache 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c" @@ -5085,8 +5124,10 @@ dependencies = [ "checksum parity-ws 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2fec5048fba72a2e01baeb0d08089db79aead4b57e2443df172fb1840075a233" "checksum parking_lot 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "f0802bff09003b291ba756dc7e79313e51cc31667e94afbe847def490424cde5" "checksum parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ab41b4aed082705d1056416ae4468b6ea99d52599ecf3169b00088d43113e337" +"checksum parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fa7767817701cce701d5585b9c4db3cdd02086398322c1d7e8bf5094a96a2ce7" "checksum parking_lot_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ad7f7e6ebdc79edff6fdcb87a55b620174f7a989e3eb31b65231f4af57f00b8c" "checksum parking_lot_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "94c8c7923936b28d546dfd14d4472eaf34c99b14e1c973a32b3e6d4eb04298c9" +"checksum parking_lot_core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cb88cb1cb3790baa6776844f968fea3be44956cf184fa1be5a03341f5491278c" "checksum pbkdf2 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "006c038a43a45995a9670da19e67600114740e8511d4333bf97a56e66a7542d9" "checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" "checksum pest 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0fce5d8b5cc33983fc74f78ad552b5522ab41442c4ca91606e4236eb4b5ceefc" @@ -5150,6 +5191,7 @@ dependencies = [ "checksum same-file 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "10f7794e2fda7f594866840e95f5c5962e886e228e68b6505885811a94dd728c" "checksum scoped_threadpool 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "1d51f5df5af43ab3f1360b429fa5e0152ac5ce8c0bd6485cae490332e96846a8" "checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27" +"checksum scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b42e15e59b18a828bbf5c58ea01debb36b9b096346de35d941dcb89009f24a0d" "checksum scrypt 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "656c79d0e90d0ab28ac86bf3c3d10bfbbac91450d3f190113b4e76d9fec3cfdd" "checksum sct 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2f5adf8fbd58e1b1b52699dc8bed2630faecb6d8c7bee77d009d6bbe4af569b9" "checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" diff --git a/ethcore/src/block.rs b/ethcore/src/block.rs index e0192e8dc67..1c422f1d7b2 100644 --- a/ethcore/src/block.rs +++ b/ethcore/src/block.rs @@ -22,7 +22,7 @@ //! and can be appended to with transactions and uncles. //! //! When ready, `OpenBlock` can be closed and turned into a `ClosedBlock`. A `ClosedBlock` can -//! be re-opend again by a miner under certain circumstances. On block close, state commit is +//! be re-opened 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 @@ -54,7 +54,6 @@ use rlp::{RlpStream, Encodable, encode_list}; use types::transaction::{SignedTransaction, Error as TransactionError}; use types::header::Header; use types::receipt::{Receipt, TransactionOutcome}; - use executive_state::ExecutiveStateWithMachineZomgBetterName; /// Block that is ready for transactions to be added. diff --git a/ethcore/src/error.rs b/ethcore/src/error.rs index f7308afcb64..01906373202 100644 --- a/ethcore/src/error.rs +++ b/ethcore/src/error.rs @@ -262,8 +262,7 @@ pub enum Error { #[display(fmt = "{}", _0)] Msg(String), /// State errors - // TODO: fix error handling - #[display(fmt = "state error todo fixme ({})", _0)] + #[display(fmt = "State error ({})", _0)] State(state_account::Error), } diff --git a/ethcore/src/machine.rs b/ethcore/src/machine.rs index e1131a9badf..9f69cf8c448 100644 --- a/ethcore/src/machine.rs +++ b/ethcore/src/machine.rs @@ -34,7 +34,7 @@ use call_contract::CallContract; use client::BlockInfo; use error::Error; use executive::Executive; -use spec::CommonParams; // TODO: move to common_types +use spec::CommonParams; use state_account::{CleanupMode, Substate}; use trace::{NoopTracer, NoopVMTracer}; use tx_filter::TransactionFilter; diff --git a/ethcore/state-account/src/lib.rs b/ethcore/state-account/src/lib.rs index d3b46ddf687..3ec3d291ec5 100644 --- a/ethcore/state-account/src/lib.rs +++ b/ethcore/state-account/src/lib.rs @@ -14,6 +14,14 @@ // You should have received a copy of the GNU General Public License // along with Parity Ethereum. If not, see . +//! Account state +//! This crate contains code used to create, convert, and update Accounts and the code and storage +//! associated with it. It also defines the trait used to construct a backend to build a complete +//! caching state database. +//! Note: the code that needs access to `ethcore` types such as `Machine` and `Executive` is found in +//! the `executive_state` module in `ethcore`. Most tests for the `State` module in this crate are +//! also found in `executive_state` (for the same reason). + pub mod account; pub mod backend; pub mod substate; From 618276d057901e2e45ac11dae2afe871b170e7cf Mon Sep 17 00:00:00 2001 From: David Palm Date: Fri, 5 Jul 2019 23:17:44 +0200 Subject: [PATCH 44/87] Add error.rs --- ethcore/state-account/src/error.rs | 35 ++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 ethcore/state-account/src/error.rs diff --git a/ethcore/state-account/src/error.rs b/ethcore/state-account/src/error.rs new file mode 100644 index 00000000000..0a2911f7913 --- /dev/null +++ b/ethcore/state-account/src/error.rs @@ -0,0 +1,35 @@ +// 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}; + +#[derive(Debug, Display, From)] +pub enum Error { + /// Trie error. + Trie(ethtrie::TrieError), + /// Decoder error. + Decoder(rlp::DecoderError), +} + +impl std::error::Error for Error {} + +impl From> for Error where Error: From { + fn from(err: Box) -> Self { + Error::from(*err) + } +} From 3a0db5ea8f880c651ceb8f127ca73bedac2ad7b8 Mon Sep 17 00:00:00 2001 From: David Palm Date: Fri, 5 Jul 2019 23:23:57 +0200 Subject: [PATCH 45/87] Fixup Cargo.lock --- Cargo.lock | 33 ++++----------------------------- ethcore/trace/Cargo.toml | 2 +- 2 files changed, 5 insertions(+), 30 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 42791a5810b..149c3b99743 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3110,16 +3110,6 @@ dependencies = [ "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "parking_lot" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "lock_api 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot_core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "parking_lot_core" version = "0.3.1" @@ -3150,21 +3140,6 @@ dependencies = [ "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "parking_lot_core" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "patricia-trie-ethereum" version = "0.1.0" @@ -3487,7 +3462,7 @@ name = "rand_chacha" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "rand_core 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -3514,7 +3489,7 @@ name = "rand_hc" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "rand_core 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -3552,7 +3527,7 @@ name = "rand_xorshift" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "rand_core 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -4458,7 +4433,7 @@ dependencies = [ "log 0.4.6 (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.7.1 (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)", "rlp_derive 0.1.0", "vm 0.1.0", diff --git a/ethcore/trace/Cargo.toml b/ethcore/trace/Cargo.toml index 9fbc5612a2c..dffc6c8bf70 100644 --- a/ethcore/trace/Cargo.toml +++ b/ethcore/trace/Cargo.toml @@ -15,7 +15,7 @@ kvdb = "0.1.0" log = "0.4" parity-bytes = "0.1.0" parity-util-mem = "0.1" -parking_lot = "0.7.0" +parking_lot = "0.8.0" rlp = "0.4.0" rlp_derive = { path = "../../util/rlp-derive" } vm = { path = "../vm" } From a61f135ceb39450026b9374e6068a1b0affa1ef1 Mon Sep 17 00:00:00 2001 From: David Palm Date: Fri, 5 Jul 2019 23:29:30 +0200 Subject: [PATCH 46/87] Smaller ethcore API is fine --- ethcore/src/lib.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/ethcore/src/lib.rs b/ethcore/src/lib.rs index 99c146d507d..55fb063021b 100644 --- a/ethcore/src/lib.rs +++ b/ethcore/src/lib.rs @@ -161,12 +161,8 @@ pub mod executive; pub mod executive_state; pub mod machine; pub mod miner; -// TODO: need to be pub? -//pub mod pod_state; pub mod snapshot; pub mod spec; -// TODO: need to be pub? -//pub mod state; pub mod state_db; pub mod transaction_ext; pub mod verification; From 14e475f726f97b3b9caa72f05b03c6395f81c7e6 Mon Sep 17 00:00:00 2001 From: David Palm Date: Sat, 6 Jul 2019 00:15:21 +0200 Subject: [PATCH 47/87] Add `to-pod-full` feature to state-account Fix evmbin --- Cargo.lock | 1 + ethcore/state-account/Cargo.toml | 4 ++++ evmbin/Cargo.toml | 1 + evmbin/src/info.rs | 5 +++-- evmbin/src/main.rs | 1 + 5 files changed, 10 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 149c3b99743..79c20c16ae4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1495,6 +1495,7 @@ dependencies = [ "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", + "state-account 0.1.0", "tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "trace 0.1.0", "vm 0.1.0", diff --git a/ethcore/state-account/Cargo.toml b/ethcore/state-account/Cargo.toml index 09c89dbb614..ab2fc2d55ba 100644 --- a/ethcore/state-account/Cargo.toml +++ b/ethcore/state-account/Cargo.toml @@ -35,3 +35,7 @@ account-db = { path = "../account-db" } journaldb = { path = "../../util/journaldb" } parity-bytes = "0.1.0" rlp_compress = { path = "../../util/rlp-compress" } + +[features] +# Enables slow 'to-pod-full' method for use in tests and evmbin. +to-pod-full = [] diff --git a/evmbin/Cargo.toml b/evmbin/Cargo.toml index cbd70661858..e1fce6b847f 100644 --- a/evmbin/Cargo.toml +++ b/evmbin/Cargo.toml @@ -23,6 +23,7 @@ rustc-hex = "1.0" serde = "1.0" serde_derive = "1.0" serde_json = "1.0" +state-account = { path = "../ethcore/state-account", features = ["to-pod-full"] } trace = { path = "../ethcore/trace" } vm = { path = "../ethcore/vm" } diff --git a/evmbin/src/info.rs b/evmbin/src/info.rs index a21d075aad6..a20957e6860 100644 --- a/evmbin/src/info.rs +++ b/evmbin/src/info.rs @@ -19,12 +19,13 @@ use std::time::{Instant, Duration}; use ethereum_types::{H256, U256}; use ethcore::client::{self, EvmTestClient, EvmTestError, TransactErr, TransactSuccess}; -use ethcore::{state, state_db, spec, TrieSpec}; +use ethcore::{state_db, spec, TrieSpec}; use trace; use ethjson; use pod::PodState; use types::transaction; use vm::ActionParams; +use state_account::State; /// VM execution informant pub trait Informant: trace::VMTracer { @@ -154,7 +155,7 @@ pub fn run_transaction( T::finish(result, &mut sink) } -fn dump_state(state: &state::State) -> Option { +fn dump_state(state: &State) -> Option { state.to_pod_full().ok() } diff --git a/evmbin/src/main.rs b/evmbin/src/main.rs index aef4cdfcbcf..cd6b6488a94 100644 --- a/evmbin/src/main.rs +++ b/evmbin/src/main.rs @@ -50,6 +50,7 @@ extern crate evm; extern crate panic_hook; extern crate pod; extern crate env_logger; +extern crate state_account; extern crate trace; #[cfg(test)] From 6d0df41d291d6f9cfc25023d4f6502d8ae461582 Mon Sep 17 00:00:00 2001 From: David Palm Date: Sat, 6 Jul 2019 00:35:50 +0200 Subject: [PATCH 48/87] Fix a few more test failures --- Cargo.lock | 1 + ethcore/src/json_tests/executive.rs | 2 +- rpc/Cargo.toml | 1 + rpc/src/lib.rs | 2 ++ rpc/src/v1/tests/helpers/miner_service.rs | 20 ++++++++++++++++---- 5 files changed, 21 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 79c20c16ae4..2037453d7c6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2911,6 +2911,7 @@ dependencies = [ "parity-updater 1.12.0", "parity-version 2.7.0", "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "patricia-trie-ethereum 0.1.0", "pretty_assertions 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", "rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/ethcore/src/json_tests/executive.rs b/ethcore/src/json_tests/executive.rs index 7c0eee591f7..45589b0d472 100644 --- a/ethcore/src/json_tests/executive.rs +++ b/ethcore/src/json_tests/executive.rs @@ -298,7 +298,7 @@ fn do_json_test_for(vm_type: &VMType, json_data: &[u8] &mut tracer, &mut vm_tracer, )); - let mut evm = vm_factory.create(params, &schedule, 0); + let evm = vm_factory.create(params, &schedule, 0); let res = evm.exec(&mut ex).ok().expect("TestExt never trap; resume error never happens; qed"); // a return in finalize will not alter callcreates let callcreates = ex.callcreates.clone(); diff --git a/rpc/Cargo.toml b/rpc/Cargo.toml index c6acb307dcb..624630e79c9 100644 --- a/rpc/Cargo.toml +++ b/rpc/Cargo.toml @@ -69,6 +69,7 @@ ethcore = { path = "../ethcore", features = ["test-helpers"] } ethcore-accounts = { path = "../accounts" } ethcore-io = { path = "../util/io" } ethcore-network = { path = "../util/network" } +patricia-trie-ethereum = { path = "../util/patricia-trie-ethereum" } fake-fetch = { path = "../util/fake-fetch" } macros = { path = "../util/macros" } pretty_assertions = "0.1" diff --git a/rpc/src/lib.rs b/rpc/src/lib.rs index e54fcb12296..310d064e0e2 100644 --- a/rpc/src/lib.rs +++ b/rpc/src/lib.rs @@ -104,6 +104,8 @@ extern crate rand_xorshift; #[cfg(test)] extern crate ethjson; #[cfg(test)] +extern crate patricia_trie_ethereum as ethtrie; +#[cfg(test)] extern crate transaction_pool as txpool; #[cfg(test)] diff --git a/rpc/src/v1/tests/helpers/miner_service.rs b/rpc/src/v1/tests/helpers/miner_service.rs index 694dd96626b..89b2fc08c8d 100644 --- a/rpc/src/v1/tests/helpers/miner_service.rs +++ b/rpc/src/v1/tests/helpers/miner_service.rs @@ -26,9 +26,11 @@ use ethcore::engines::{Engine, signer::EngineSigner}; use ethcore::error::Error; use ethcore::miner::{self, MinerService, AuthoringParams, FilterOptions}; use ethereum_types::{H256, U256, Address}; +use ethtrie; use miner::pool::local_transactions::Status as LocalTransactionStatus; use miner::pool::{verifier, VerifiedTransaction, QueueStatus}; use parking_lot::{RwLock, Mutex}; +use state_account::state::StateInfo; use types::transaction::{self, UnverifiedTransaction, SignedTransaction, PendingTransaction}; use txpool; use types::BlockNumber; @@ -85,16 +87,26 @@ impl TestMinerService { } } +pub struct TestState(()); + +impl StateInfo for TestState { + fn nonce(&self, _address: &Address) -> ethtrie::Result { unimplemented!() } + fn balance(&self, _address: &Address) -> ethtrie::Result { unimplemented!() } + fn storage_at(&self, _address: &Address, _key: &H256) -> ethtrie::Result { unimplemented!() } + fn code(&self, _address: &Address) -> ethtrie::Result>> { unimplemented!() } +} + + impl StateClient for TestMinerService { // State will not be used by test client anyway, since all methods that accept state are mocked - type State = (); + type State = TestState; fn latest_state(&self) -> Self::State { - () + TestState(()) } fn state_at(&self, _id: BlockId) -> Option { - Some(()) + Some(TestState(())) } } @@ -105,7 +117,7 @@ impl EngineInfo for TestMinerService { } impl MinerService for TestMinerService { - type State = (); + type State = TestState; fn pending_state(&self, _latest_block_number: BlockNumber) -> Option { None From 5363b454302b7ca8fac0db40d647e4e2957ef029 Mon Sep 17 00:00:00 2001 From: David Palm Date: Sat, 6 Jul 2019 13:55:26 +0200 Subject: [PATCH 49/87] Fix RPC test build --- ethcore/src/client/mod.rs | 2 +- ethcore/src/client/test_client.rs | 10 ++++++---- rpc/src/v1/tests/helpers/miner_service.rs | 16 +++------------- rpc/src/v1/tests/mocked/eth.rs | 12 ++++++------ 4 files changed, 16 insertions(+), 24 deletions(-) diff --git a/ethcore/src/client/mod.rs b/ethcore/src/client/mod.rs index 78d1cfbd77e..3afa832910b 100644 --- a/ethcore/src/client/mod.rs +++ b/ethcore/src/client/mod.rs @@ -32,7 +32,7 @@ pub use self::config::{Mode, ClientConfig, DatabaseCompactionProfile, BlockChain pub use self::evm_test_client::{EvmTestClient, EvmTestError, TransactErr, TransactSuccess}; pub use self::io_message::ClientIoMessage; #[cfg(any(test, feature = "test-helpers"))] -pub use self::test_client::{TestBlockChainClient, EachBlockWith}; +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, diff --git a/ethcore/src/client/test_client.rs b/ethcore/src/client/test_client.rs index add2d4031d4..4493be8c419 100644 --- a/ethcore/src/client/test_client.rs +++ b/ethcore/src/client/test_client.rs @@ -605,7 +605,10 @@ impl Call for TestBlockChainClient { } } -pub struct TestState(()); +/// NewType wrapper around `()` to impersonate `State` in trait impls. State will not be used by +/// test client, since all methods that accept state are mocked. +pub struct TestState; + impl StateInfo for TestState { fn nonce(&self, _address: &Address) -> ethtrie::Result { unimplemented!() } fn balance(&self, _address: &Address) -> ethtrie::Result { unimplemented!() } @@ -615,15 +618,14 @@ impl StateInfo for TestState { impl StateClient for TestBlockChainClient { - // State will not be used by test client anyway, since all methods that accept state are mocked type State = TestState; fn latest_state(&self) -> Self::State { - TestState(()) + TestState } fn state_at(&self, _id: BlockId) -> Option { - Some(TestState(())) + Some(TestState) } } diff --git a/rpc/src/v1/tests/helpers/miner_service.rs b/rpc/src/v1/tests/helpers/miner_service.rs index 89b2fc08c8d..b57f763eec3 100644 --- a/rpc/src/v1/tests/helpers/miner_service.rs +++ b/rpc/src/v1/tests/helpers/miner_service.rs @@ -21,7 +21,7 @@ use std::collections::{BTreeMap, BTreeSet, HashMap}; use bytes::Bytes; use ethcore::block::SealedBlock; -use ethcore::client::{Nonce, PrepareOpenBlock, StateClient, EngineInfo}; +use ethcore::client::{Nonce, PrepareOpenBlock, StateClient, EngineInfo, TestState}; use ethcore::engines::{Engine, signer::EngineSigner}; use ethcore::error::Error; use ethcore::miner::{self, MinerService, AuthoringParams, FilterOptions}; @@ -87,26 +87,16 @@ impl TestMinerService { } } -pub struct TestState(()); - -impl StateInfo for TestState { - fn nonce(&self, _address: &Address) -> ethtrie::Result { unimplemented!() } - fn balance(&self, _address: &Address) -> ethtrie::Result { unimplemented!() } - fn storage_at(&self, _address: &Address, _key: &H256) -> ethtrie::Result { unimplemented!() } - fn code(&self, _address: &Address) -> ethtrie::Result>> { unimplemented!() } -} - - impl StateClient for TestMinerService { // State will not be used by test client anyway, since all methods that accept state are mocked type State = TestState; fn latest_state(&self) -> Self::State { - TestState(()) + TestState } fn state_at(&self, _id: BlockId) -> Option { - Some(TestState(())) + Some(TestState) } } diff --git a/rpc/src/v1/tests/mocked/eth.rs b/rpc/src/v1/tests/mocked/eth.rs index e9af200f7ed..39da9aa2abc 100644 --- a/rpc/src/v1/tests/mocked/eth.rs +++ b/rpc/src/v1/tests/mocked/eth.rs @@ -100,13 +100,13 @@ impl EthTester { EthTester { runtime, - client: client, - sync: sync, + client, + sync, accounts_provider: ap, - miner: miner, - snapshot: snapshot, - io: io, - hashrates: hashrates, + miner, + snapshot, + io, + hashrates, } } From 234e9868000b55ca70530f7af6ce3d8c6ebb474a Mon Sep 17 00:00:00 2001 From: David Palm Date: Sat, 6 Jul 2019 21:45:44 +0200 Subject: [PATCH 50/87] Baptize the new trait --- ethcore/src/block.rs | 2 +- ethcore/src/client/evm_test_client.rs | 2 +- ethcore/src/executive_state.rs | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ethcore/src/block.rs b/ethcore/src/block.rs index 1c422f1d7b2..402d3dd5001 100644 --- a/ethcore/src/block.rs +++ b/ethcore/src/block.rs @@ -54,7 +54,7 @@ use rlp::{RlpStream, Encodable, encode_list}; use types::transaction::{SignedTransaction, Error as TransactionError}; use types::header::Header; use types::receipt::{Receipt, TransactionOutcome}; -use executive_state::ExecutiveStateWithMachineZomgBetterName; +use executive_state::ExecutiveState; /// Block that is ready for transactions to be added. /// diff --git a/ethcore/src/client/evm_test_client.rs b/ethcore/src/client/evm_test_client.rs index fc7f074398c..a15b14f1515 100644 --- a/ethcore/src/client/evm_test_client.rs +++ b/ethcore/src/client/evm_test_client.rs @@ -30,7 +30,7 @@ use vm::{self, ActionParams}; use ethtrie; use state_account::{CleanupMode, Substate, State}; -use executive_state::ExecutiveStateWithMachineZomgBetterName; +use executive_state::ExecutiveState; /// EVM test Error. #[derive(Debug)] diff --git a/ethcore/src/executive_state.rs b/ethcore/src/executive_state.rs index 88a7ed32317..9c359fff282 100644 --- a/ethcore/src/executive_state.rs +++ b/ethcore/src/executive_state.rs @@ -139,7 +139,7 @@ pub fn prove_transaction_virtual + Send + Syn } /// Collects code that needs a Machine and/or Executive -pub trait ExecutiveStateWithMachineZomgBetterName { +pub trait ExecutiveState { /// Execute a given transaction, producing a receipt and an optional trace. /// This will change the state accordingly. fn apply( @@ -165,7 +165,7 @@ pub trait ExecutiveStateWithMachineZomgBetterName { V: trace::VMTracer; } -impl ExecutiveStateWithMachineZomgBetterName for State { +impl ExecutiveState for State { /// Execute a given transaction, producing a receipt and an optional trace. /// This will change the state accordingly. fn apply( @@ -277,7 +277,7 @@ mod tests { use pod::{self, PodAccount}; #[cfg(feature="to-pod-full")] use pod::PodState; - use executive_state::ExecutiveStateWithMachineZomgBetterName; + use executive_state::ExecutiveState; use state_account::{Account, CleanupMode}; fn secret() -> Secret { From 7eb04e048245eee1acb9b7d5044d7c46fc144ea2 Mon Sep 17 00:00:00 2001 From: David Palm Date: Sat, 6 Jul 2019 21:55:02 +0200 Subject: [PATCH 51/87] Remove resolved TODOs --- ethcore/src/client/traits.rs | 7 ------- ethcore/src/executive_state.rs | 1 - ethcore/state-account/src/state.rs | 2 -- 3 files changed, 10 deletions(-) diff --git a/ethcore/src/client/traits.rs b/ethcore/src/client/traits.rs index fa3eaa8e0a5..fbd68c14062 100644 --- a/ethcore/src/client/traits.rs +++ b/ethcore/src/client/traits.rs @@ -61,13 +61,6 @@ pub enum StateOrBlock { Block(BlockId) } -// TODO: This doesn't work (StateInfo is now in a diff crate) but not sure where it was used, if at all. -//impl From for StateOrBlock { -// fn from(info: S) -> StateOrBlock { -// StateOrBlock::State(Box::new(info) as Box<_>) -// } -//} - impl From> for StateOrBlock { fn from(info: Box) -> StateOrBlock { StateOrBlock::State(info) diff --git a/ethcore/src/executive_state.rs b/ethcore/src/executive_state.rs index 9c359fff282..c6c037934dd 100644 --- a/ethcore/src/executive_state.rs +++ b/ethcore/src/executive_state.rs @@ -40,7 +40,6 @@ use hash_db::AsHashDB; use error::Error; -// TODO: is there a better place for this? /// Return type of proof validity check. #[derive(Debug, Clone)] pub enum ProvedExecution { diff --git a/ethcore/state-account/src/state.rs b/ethcore/state-account/src/state.rs index 0be89c24d73..01b9dd5dfd9 100644 --- a/ethcore/state-account/src/state.rs +++ b/ethcore/state-account/src/state.rs @@ -791,7 +791,6 @@ impl State { })) } - // TODO: sort out features #[cfg(feature="to-pod-full")] /// Populate a PodAccount map from this state. /// Warning this is not for real time use. @@ -826,7 +825,6 @@ impl State { Ok(PodState::from(result)) } - // TODO: sort out features /// Create a PodAccount from an account. /// Differs from existing method by including all storage /// values of the account to the PodAccount. From 0cab3144fc901425d28d4363d2178a607563dddc Mon Sep 17 00:00:00 2001 From: David Palm Date: Sat, 6 Jul 2019 22:04:39 +0200 Subject: [PATCH 52/87] Rename state-account to account-state --- Cargo.lock | 72 +++++++++---------- ethcore/Cargo.toml | 2 +- .../Cargo.toml | 2 +- .../src/account.rs | 0 .../src/backend.rs | 0 .../src/error.rs | 0 .../src/lib.rs | 0 .../src/state.rs | 0 .../src/substate.rs | 0 ethcore/light/Cargo.toml | 2 +- ethcore/light/src/lib.rs | 2 +- ethcore/light/src/on_demand/request.rs | 2 +- ethcore/private-tx/Cargo.toml | 2 +- ethcore/private-tx/src/lib.rs | 4 +- ethcore/src/block.rs | 2 +- ethcore/src/client/client.rs | 4 +- ethcore/src/client/evm_test_client.rs | 2 +- ethcore/src/client/mod.rs | 2 +- ethcore/src/client/test_client.rs | 2 +- ethcore/src/client/traits.rs | 2 +- ethcore/src/error.rs | 2 +- ethcore/src/ethereum/mod.rs | 2 +- ethcore/src/executive.rs | 4 +- ethcore/src/executive_state.rs | 8 +-- ethcore/src/externalities.rs | 4 +- ethcore/src/json_tests/executive.rs | 2 +- ethcore/src/lib.rs | 2 +- ethcore/src/machine.rs | 2 +- ethcore/src/miner/miner.rs | 2 +- ethcore/src/miner/mod.rs | 2 +- ethcore/src/snapshot/mod.rs | 2 +- ethcore/src/snapshot/tests/helpers.rs | 2 +- ethcore/src/spec/spec.rs | 4 +- ethcore/src/state_db.rs | 6 +- ethcore/src/test_helpers.rs | 2 +- ethcore/src/tests/client.rs | 2 +- ethcore/src/tests/evm.rs | 2 +- evmbin/Cargo.toml | 2 +- evmbin/src/info.rs | 2 +- evmbin/src/main.rs | 2 +- rpc/Cargo.toml | 2 +- rpc/src/lib.rs | 2 +- rpc/src/v1/impls/parity.rs | 2 +- rpc/src/v1/tests/helpers/miner_service.rs | 2 +- 44 files changed, 83 insertions(+), 83 deletions(-) rename ethcore/{state-account => account-state}/Cargo.toml (98%) rename ethcore/{state-account => account-state}/src/account.rs (100%) rename ethcore/{state-account => account-state}/src/backend.rs (100%) rename ethcore/{state-account => account-state}/src/error.rs (100%) rename ethcore/{state-account => account-state}/src/lib.rs (100%) rename ethcore/{state-account => account-state}/src/state.rs (100%) rename ethcore/{state-account => account-state}/src/substate.rs (100%) diff --git a/Cargo.lock b/Cargo.lock index 2037453d7c6..d43f91603d8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -12,6 +12,37 @@ dependencies = [ "rlp 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "account-state" +version = "0.1.0" +dependencies = [ + "account-db 0.1.0", + "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)", + "evm 0.1.0", + "factories 0.1.0", + "hash-db 0.12.4 (registry+https://github.com/rust-lang/crates.io-index)", + "journaldb 0.2.0", + "keccak-hash 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "keccak-hasher 0.1.1", + "kvdb 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "lru-cache 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "memory-db 0.12.4 (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)", + "patricia-trie-ethereum 0.1.0", + "pod 0.1.0", + "rlp 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rlp_compress 0.1.0", + "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", + "trace 0.1.0", + "trie-db 0.12.4 (registry+https://github.com/rust-lang/crates.io-index)", + "vm 0.1.0", +] + [[package]] name = "aes" version = "0.3.2" @@ -890,6 +921,7 @@ name = "ethcore" version = "1.12.0" dependencies = [ "account-db 0.1.0", + "account-state 0.1.0", "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "blooms-db 0.1.0", "bn 0.4.4 (git+https://github.com/paritytech/bn)", @@ -951,7 +983,6 @@ dependencies = [ "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", - "state-account 0.1.0", "stats 0.1.0", "tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "time-utils 0.1.0", @@ -1057,6 +1088,7 @@ dependencies = [ name = "ethcore-light" version = "1.12.0" dependencies = [ + "account-state 0.1.0", "bincode 1.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "common-types 0.1.0", "derive_more 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1091,7 +1123,6 @@ dependencies = [ "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", - "state-account 0.1.0", "stats 0.1.0", "tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "trie-db 0.12.4 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1208,6 +1239,7 @@ dependencies = [ name = "ethcore-private-tx" version = "1.0.0" dependencies = [ + "account-state 0.1.0", "common-types 0.1.0", "derive_more 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.5.13 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1237,7 +1269,6 @@ dependencies = [ "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", - "state-account 0.1.0", "time-utils 0.1.0", "tiny-keccak 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "trace 0.1.0", @@ -1480,6 +1511,7 @@ dependencies = [ name = "evmbin" version = "0.1.0" dependencies = [ + "account-state 0.1.0", "common-types 0.1.0", "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)", @@ -1495,7 +1527,6 @@ dependencies = [ "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", - "state-account 0.1.0", "tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "trace 0.1.0", "vm 0.1.0", @@ -2871,6 +2902,7 @@ dependencies = [ name = "parity-rpc" version = "1.12.0" dependencies = [ + "account-state 0.1.0", "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "cid 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "common-types 0.1.0", @@ -2921,7 +2953,6 @@ dependencies = [ "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", - "state-account 0.1.0", "stats 0.1.0", "tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "tiny-keccak 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3965,37 +3996,6 @@ name = "stable_deref_trait" version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "state-account" -version = "0.1.0" -dependencies = [ - "account-db 0.1.0", - "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)", - "evm 0.1.0", - "factories 0.1.0", - "hash-db 0.12.4 (registry+https://github.com/rust-lang/crates.io-index)", - "journaldb 0.2.0", - "keccak-hash 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "keccak-hasher 0.1.1", - "kvdb 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "lru-cache 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "memory-db 0.12.4 (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)", - "patricia-trie-ethereum 0.1.0", - "pod 0.1.0", - "rlp 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rlp_compress 0.1.0", - "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", - "trace 0.1.0", - "trie-db 0.12.4 (registry+https://github.com/rust-lang/crates.io-index)", - "vm 0.1.0", -] - [[package]] name = "static_assertions" version = "0.2.5" diff --git a/ethcore/Cargo.toml b/ethcore/Cargo.toml index 3c83e103f93..8f484df335c 100644 --- a/ethcore/Cargo.toml +++ b/ethcore/Cargo.toml @@ -63,7 +63,7 @@ rlp_derive = { path = "../util/rlp-derive" } rustc-hex = "1.0" serde = "1.0" serde_derive = "1.0" -state-account = { path = "state-account" } +account-state = { path = "account-state" } stats = { path = "../util/stats" } tempdir = { version = "0.3", optional = true } time-utils = { path = "../util/time-utils" } diff --git a/ethcore/state-account/Cargo.toml b/ethcore/account-state/Cargo.toml similarity index 98% rename from ethcore/state-account/Cargo.toml rename to ethcore/account-state/Cargo.toml index ab2fc2d55ba..9d32f3dea99 100644 --- a/ethcore/state-account/Cargo.toml +++ b/ethcore/account-state/Cargo.toml @@ -1,6 +1,6 @@ [package] description = "Ethereum accounts, keeps track of changes to the code and storage" -name = "state-account" +name = "account-state" version = "0.1.0" authors = ["Parity Technologies "] edition = "2018" diff --git a/ethcore/state-account/src/account.rs b/ethcore/account-state/src/account.rs similarity index 100% rename from ethcore/state-account/src/account.rs rename to ethcore/account-state/src/account.rs diff --git a/ethcore/state-account/src/backend.rs b/ethcore/account-state/src/backend.rs similarity index 100% rename from ethcore/state-account/src/backend.rs rename to ethcore/account-state/src/backend.rs diff --git a/ethcore/state-account/src/error.rs b/ethcore/account-state/src/error.rs similarity index 100% rename from ethcore/state-account/src/error.rs rename to ethcore/account-state/src/error.rs diff --git a/ethcore/state-account/src/lib.rs b/ethcore/account-state/src/lib.rs similarity index 100% rename from ethcore/state-account/src/lib.rs rename to ethcore/account-state/src/lib.rs diff --git a/ethcore/state-account/src/state.rs b/ethcore/account-state/src/state.rs similarity index 100% rename from ethcore/state-account/src/state.rs rename to ethcore/account-state/src/state.rs diff --git a/ethcore/state-account/src/substate.rs b/ethcore/account-state/src/substate.rs similarity index 100% rename from ethcore/state-account/src/substate.rs rename to ethcore/account-state/src/substate.rs diff --git a/ethcore/light/Cargo.toml b/ethcore/light/Cargo.toml index 0cb0b9a5ea3..90507a5158d 100644 --- a/ethcore/light/Cargo.toml +++ b/ethcore/light/Cargo.toml @@ -35,7 +35,7 @@ itertools = "0.5" bincode = "1.1" serde = "1.0" serde_derive = "1.0" -state-account = { path = "../state-account" } +account-state = { path = "../account-state" } parking_lot = "0.8" stats = { path = "../../util/stats" } keccak-hash = "0.2.0" diff --git a/ethcore/light/src/lib.rs b/ethcore/light/src/lib.rs index f7b1e25a805..dadb90c66b0 100644 --- a/ethcore/light/src/lib.rs +++ b/ethcore/light/src/lib.rs @@ -89,7 +89,7 @@ extern crate triehash_ethereum as triehash; extern crate kvdb; extern crate memory_cache; extern crate derive_more; -extern crate state_account; +extern crate account_state; #[cfg(test)] extern crate kvdb_memorydb; diff --git a/ethcore/light/src/on_demand/request.rs b/ethcore/light/src/on_demand/request.rs index 0c2b27f6bb1..7a89f40121e 100644 --- a/ethcore/light/src/on_demand/request.rs +++ b/ethcore/light/src/on_demand/request.rs @@ -26,7 +26,7 @@ use common_types::receipt::Receipt; use common_types::transaction::SignedTransaction; use ethcore::engines::{Engine, StateDependentProof}; use ethcore::executive_state::{ProvedExecution, self}; -use state_account; +use account_state; use ethereum_types::{H256, U256, Address}; use ethtrie::{TrieError, TrieDB}; use hash::{KECCAK_NULL_RLP, KECCAK_EMPTY, KECCAK_EMPTY_LIST_RLP, keccak}; diff --git a/ethcore/private-tx/Cargo.toml b/ethcore/private-tx/Cargo.toml index 500ec28c4e8..3f577a9fd1a 100644 --- a/ethcore/private-tx/Cargo.toml +++ b/ethcore/private-tx/Cargo.toml @@ -35,7 +35,7 @@ rustc-hex = "1.0" serde = "1.0" serde_derive = "1.0" serde_json = "1.0" -state-account = { path = "../state-account" } +account-state = { path = "../account-state" } time-utils = { path = "../../util/time-utils" } tiny-keccak = "1.4" trace = { path = "../trace" } diff --git a/ethcore/private-tx/src/lib.rs b/ethcore/private-tx/src/lib.rs index 7619e7f92c4..db081a5eae2 100644 --- a/ethcore/private-tx/src/lib.rs +++ b/ethcore/private-tx/src/lib.rs @@ -46,7 +46,7 @@ extern crate rlp; extern crate serde_derive; extern crate serde; extern crate serde_json; -extern crate state_account; +extern crate account_state; extern crate rustc_hex; extern crate trace; extern crate transaction_pool as txpool; @@ -95,7 +95,7 @@ use ethcore::client::{ }; use ethcore::miner::{self, Miner, MinerService, pool_client::NonceCache}; use ethcore::state_db; -use state_account::State; +use account_state::State; use trace::{Tracer, VMTracer}; use call_contract::CallContract; use rustc_hex::FromHex; diff --git a/ethcore/src/block.rs b/ethcore/src/block.rs index 402d3dd5001..72edc9fad51 100644 --- a/ethcore/src/block.rs +++ b/ethcore/src/block.rs @@ -42,7 +42,7 @@ use engines::Engine; use error::{Error, BlockError}; use factories::Factories; use state_db::StateDB; -use state_account::State; +use account_state::State; use trace::Tracing; use triehash::ordered_trie_root; use unexpected::{Mismatch, OutOfBounds}; diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index 99e039fdda1..d4f9356e832 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -70,7 +70,7 @@ use factories::{Factories, VmFactory}; use miner::{Miner, MinerService}; use snapshot::{self, io as snapshot_io, SnapshotClient}; use spec::Spec; -use state_account::State; +use account_state::State; use executive_state; use state_db::StateDB; use trace::{self, TraceDB, ImportRequest as TraceImportRequest, LocalizedTrace, Database as TraceDatabase}; @@ -623,7 +623,7 @@ impl Importer { let call = move |addr, data| { let mut state_db = state_db.boxed_clone(); - let backend = state_account::backend::Proving::new(state_db.as_hash_db_mut()); + let backend = account_state::backend::Proving::new(state_db.as_hash_db_mut()); let transaction = client.contract_call_tx(BlockId::Hash(*header.parent_hash()), addr, data); diff --git a/ethcore/src/client/evm_test_client.rs b/ethcore/src/client/evm_test_client.rs index a15b14f1515..091a70f903d 100644 --- a/ethcore/src/client/evm_test_client.rs +++ b/ethcore/src/client/evm_test_client.rs @@ -28,7 +28,7 @@ use factories::Factories; use evm::{VMType, FinalizationResult}; use vm::{self, ActionParams}; use ethtrie; -use state_account::{CleanupMode, Substate, State}; +use account_state::{CleanupMode, Substate, State}; use executive_state::ExecutiveState; diff --git a/ethcore/src/client/mod.rs b/ethcore/src/client/mod.rs index 3afa832910b..a7260b82509 100644 --- a/ethcore/src/client/mod.rs +++ b/ethcore/src/client/mod.rs @@ -39,7 +39,7 @@ pub use self::traits::{ StateOrBlock, StateClient, Call, EngineInfo, AccountData, BlockChain, BlockProducer, SealedBlockImporter, BadBlocks, BlockChainReset }; -pub use state_account::state::StateInfo; +pub use account_state::state::StateInfo; pub use self::traits::{BlockChainClient, EngineClient, ProvingBlockChainClient, IoClient}; pub use types::ids::*; diff --git a/ethcore/src/client/test_client.rs b/ethcore/src/client/test_client.rs index 4493be8c419..693fdad01bd 100644 --- a/ethcore/src/client/test_client.rs +++ b/ethcore/src/client/test_client.rs @@ -66,7 +66,7 @@ use executive::Executed; use journaldb; use miner::{self, Miner, MinerService}; use spec::Spec; -use state_account::state::StateInfo; +use account_state::state::StateInfo; use state_db::StateDB; use trace::LocalizedTrace; use verification::queue::QueueInfo as BlockQueueInfo; diff --git a/ethcore/src/client/traits.rs b/ethcore/src/client/traits.rs index fbd68c14062..50fe75a699b 100644 --- a/ethcore/src/client/traits.rs +++ b/ethcore/src/client/traits.rs @@ -47,7 +47,7 @@ use engines::Engine; use error::{Error, EthcoreResult}; use executed::CallError; use executive::Executed; -use state_account::state::StateInfo; +use account_state::state::StateInfo; use trace::LocalizedTrace; use verification::queue::QueueInfo as BlockQueueInfo; use verification::queue::kind::blocks::Unverified; diff --git a/ethcore/src/error.rs b/ethcore/src/error.rs index 78e6a2c3709..387579acc56 100644 --- a/ethcore/src/error.rs +++ b/ethcore/src/error.rs @@ -242,7 +242,7 @@ pub enum Error { Msg(String), /// State errors #[display(fmt = "State error ({})", _0)] - State(state_account::Error), + State(account_state::Error), } impl error::Error for Error { diff --git a/ethcore/src/ethereum/mod.rs b/ethcore/src/ethereum/mod.rs index f0c1296992e..af2114a1bd4 100644 --- a/ethcore/src/ethereum/mod.rs +++ b/ethcore/src/ethereum/mod.rs @@ -189,7 +189,7 @@ pub fn new_kovan_wasm_test_machine() -> Machine { load_machine(include_bytes!(". mod tests { use std::str::FromStr; use ethereum_types::{U256, H256, Address}; - use state_account::*; + use account_state::*; use super::*; use test_helpers::get_temp_state_db; use types::view; diff --git a/ethcore/src/executive.rs b/ethcore/src/executive.rs index e127370fee8..df17a2f559c 100644 --- a/ethcore/src/executive.rs +++ b/ethcore/src/executive.rs @@ -21,7 +21,7 @@ use std::sync::Arc; use hash::keccak; use ethereum_types::{H256, U256, U512, Address}; use bytes::{Bytes, BytesRef}; -use state_account::{Backend as StateBackend, State, Substate, CleanupMode}; +use account_state::{Backend as StateBackend, State, Substate, CleanupMode}; use executed::ExecutionError; use machine::Machine; use evm::{CallType, Finalize, FinalizationResult}; @@ -1180,7 +1180,7 @@ mod tests { use evm::{Factory, VMType}; use error::ExecutionError; use machine::Machine; - use state_account::{Substate, CleanupMode}; + 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}; diff --git a/ethcore/src/executive_state.rs b/ethcore/src/executive_state.rs index c6c037934dd..a99a4a15313 100644 --- a/ethcore/src/executive_state.rs +++ b/ethcore/src/executive_state.rs @@ -15,7 +15,7 @@ // along with Parity Ethereum. If not, see . //! Execute transactions and modify State. This is glue code between the `ethcore` and -//! `state-account` crates and contains everything that requires `Machine` or `Executive` (or types +//! `account-state` crates and contains everything that requires `Machine` or `Executive` (or types //! thereof). use machine::Machine; @@ -27,7 +27,7 @@ use types::{ receipt::{TransactionOutcome, Receipt}, }; use trace::{FlatTrace, VMTrace}; -use state_account::{ +use account_state::{ backend::{self, Backend}, state::State, }; @@ -111,7 +111,7 @@ pub fn prove_transaction_virtual + Send + Syn env_info: &EnvInfo, factories: Factories, ) -> Option<(Bytes, Vec)> { - use state_account::backend::Proving; + use account_state::backend::Proving; let backend = Proving::new(db); let res = State::from_existing( @@ -277,7 +277,7 @@ mod tests { #[cfg(feature="to-pod-full")] use pod::PodState; use executive_state::ExecutiveState; - use state_account::{Account, CleanupMode}; + use account_state::{Account, CleanupMode}; fn secret() -> Secret { keccak("").into() diff --git a/ethcore/src/externalities.rs b/ethcore/src/externalities.rs index 6c37bcb9208..f41cebfcb27 100644 --- a/ethcore/src/externalities.rs +++ b/ethcore/src/externalities.rs @@ -19,7 +19,7 @@ use std::cmp; use std::sync::Arc; use ethereum_types::{H256, U256, Address, BigEndianHash}; use bytes::Bytes; -use state_account::{Backend as StateBackend, State, Substate, CleanupMode}; +use account_state::{Backend as StateBackend, State, Substate, CleanupMode}; use machine::Machine; use executive::*; use vm::{ @@ -429,7 +429,7 @@ impl<'a, T: 'a, V: 'a, B: 'a> Ext for Externalities<'a, T, V, B> mod tests { use ethereum_types::{U256, Address}; use evm::{EnvInfo, Ext, CallType}; - use state_account::{State, Substate}; + use account_state::{State, Substate}; use test_helpers::get_temp_state; use super::*; use trace::{NoopTracer, NoopVMTracer}; diff --git a/ethcore/src/json_tests/executive.rs b/ethcore/src/json_tests/executive.rs index 45589b0d472..5a69dfe6032 100644 --- a/ethcore/src/json_tests/executive.rs +++ b/ethcore/src/json_tests/executive.rs @@ -17,7 +17,7 @@ use std::path::Path; use std::sync::Arc; use super::test_common::*; -use state_account::{Backend as StateBackend, State, Substate}; +use account_state::{Backend as StateBackend, State, Substate}; use executive::*; use evm::{VMType, Finalize}; use vm::{ diff --git a/ethcore/src/lib.rs b/ethcore/src/lib.rs index 55fb063021b..6c85fcbb983 100644 --- a/ethcore/src/lib.rs +++ b/ethcore/src/lib.rs @@ -100,7 +100,7 @@ extern crate parity_util_mem as malloc_size_of; extern crate rustc_hex; extern crate serde; extern crate stats; -extern crate state_account; +extern crate account_state; extern crate time_utils; pub extern crate trace; extern crate triehash_ethereum as triehash; diff --git a/ethcore/src/machine.rs b/ethcore/src/machine.rs index 9f69cf8c448..06f511209b7 100644 --- a/ethcore/src/machine.rs +++ b/ethcore/src/machine.rs @@ -35,7 +35,7 @@ use client::BlockInfo; use error::Error; use executive::Executive; use spec::CommonParams; -use state_account::{CleanupMode, Substate}; +use account_state::{CleanupMode, Substate}; use trace::{NoopTracer, NoopVMTracer}; use tx_filter::TransactionFilter; diff --git a/ethcore/src/miner/miner.rs b/ethcore/src/miner/miner.rs index 803773f301d..33c33883672 100644 --- a/ethcore/src/miner/miner.rs +++ b/ethcore/src/miner/miner.rs @@ -59,7 +59,7 @@ use error::Error; use executed::ExecutionError; use executive::contract_address; use spec::Spec; -use state_account::State; +use account_state::State; /// Different possible definitions for pending transaction set. #[derive(Debug, PartialEq)] diff --git a/ethcore/src/miner/mod.rs b/ethcore/src/miner/mod.rs index 2085d4d2554..10a101ee5d4 100644 --- a/ethcore/src/miner/mod.rs +++ b/ethcore/src/miner/mod.rs @@ -50,7 +50,7 @@ use client::{ AccountData, Nonce, }; use error::Error; -use state_account::state::StateInfo; +use account_state::state::StateInfo; /// Provides methods to verify incoming external transactions pub trait TransactionVerifierClient: Send + Sync diff --git a/ethcore/src/snapshot/mod.rs b/ethcore/src/snapshot/mod.rs index 304d4b623c1..569d0a0bada 100644 --- a/ethcore/src/snapshot/mod.rs +++ b/ethcore/src/snapshot/mod.rs @@ -48,7 +48,7 @@ use num_cpus; use self::io::SnapshotWriter; use super::state_db::StateDB; -use state_account::Account as StateAccount; +use account_state::Account as StateAccount; use crossbeam::scope; use rand::{Rng, rngs::OsRng}; diff --git a/ethcore/src/snapshot/tests/helpers.rs b/ethcore/src/snapshot/tests/helpers.rs index 4d04470487b..89e6f8730bb 100644 --- a/ethcore/src/snapshot/tests/helpers.rs +++ b/ethcore/src/snapshot/tests/helpers.rs @@ -97,7 +97,7 @@ impl StateProducer { let address_hash = H256(rng.gen()); let balance: usize = rng.gen(); let nonce: usize = rng.gen(); - let acc = state_account::Account::new_basic(balance.into(), nonce.into()).rlp(); + let acc = account_state::Account::new_basic(balance.into(), nonce.into()).rlp(); trie.insert(&address_hash[..], &acc).unwrap(); } } diff --git a/ethcore/src/spec/spec.rs b/ethcore/src/spec/spec.rs index 9ac31551293..a21b1e6de2d 100644 --- a/ethcore/src/spec/spec.rs +++ b/ethcore/src/spec/spec.rs @@ -45,7 +45,7 @@ use machine::Machine; use pod::PodState; use spec::Genesis; use spec::seal::Generic as GenericSeal; -use state_account::{Backend, State, Substate, backend::Basic as BasicBackend}; +use account_state::{Backend, State, Substate, backend::Basic as BasicBackend}; use trace::{NoopTracer, NoopVMTracer}; pub use ethash::OptimizeFor; @@ -990,7 +990,7 @@ impl Spec { #[cfg(test)] mod tests { use super::*; - use state_account::State; + use account_state::State; use test_helpers::get_temp_state_db; use tempdir::TempDir; use types::view; diff --git a/ethcore/src/state_db.rs b/ethcore/src/state_db.rs index 07abe1221f7..2419502976c 100644 --- a/ethcore/src/state_db.rs +++ b/ethcore/src/state_db.rs @@ -33,7 +33,7 @@ use memory_cache::MemoryLruCache; use parking_lot::Mutex; use types::BlockNumber; -use state_account::{self, Account}; +use account_state::{self, Account}; /// Value used to initialize bloom bitmap size. /// @@ -412,7 +412,7 @@ impl StateDB { } } -impl state_account::Backend for StateDB { +impl account_state::Backend for StateDB { fn as_hash_db(&self) -> &dyn HashDB { self.db.as_hash_db() } fn as_hash_db_mut(&mut self) -> &mut dyn HashDB { @@ -487,7 +487,7 @@ mod tests { use ethereum_types::{H256, U256, Address}; use kvdb::DBTransaction; use test_helpers::get_temp_state_db; - use state_account::{Account, Backend}; + use account_state::{Account, Backend}; #[test] fn state_db_smoke() { diff --git a/ethcore/src/test_helpers.rs b/ethcore/src/test_helpers.rs index 9a167788405..4c44a016d30 100644 --- a/ethcore/src/test_helpers.rs +++ b/ethcore/src/test_helpers.rs @@ -44,7 +44,7 @@ use client::{Client, ClientConfig, ChainInfo, ImportBlock, ChainNotify, ChainMes use factories::Factories; use miner::Miner; use spec::Spec; -use state_account::*; +use account_state::*; use state_db::StateDB; use verification::queue::kind::blocks::Unverified; diff --git a/ethcore/src/tests/client.rs b/ethcore/src/tests/client.rs index bf0c0b52b94..15498fa4bd9 100644 --- a/ethcore/src/tests/client.rs +++ b/ethcore/src/tests/client.rs @@ -32,7 +32,7 @@ use ethereum; use executive::{Executive, TransactOptions}; use miner::{Miner, PendingOrdering, MinerService}; use spec::Spec; -use state_account::{State, CleanupMode, backend}; +use account_state::{State, CleanupMode, backend}; use test_helpers::{ self, generate_dummy_client, push_blocks_to_client, get_test_client_with_blocks, get_good_dummy_block_seq, diff --git a/ethcore/src/tests/evm.rs b/ethcore/src/tests/evm.rs index ba314c54144..77867c4e891 100644 --- a/ethcore/src/tests/evm.rs +++ b/ethcore/src/tests/evm.rs @@ -21,7 +21,7 @@ use hash::keccak; use vm::{EnvInfo, ActionParams, ActionValue, CallType, ParamsType}; use evm::{Factory, VMType}; use executive::Executive; -use state_account::Substate; +use account_state::Substate; use test_helpers::get_temp_state_with_factory; use trace::{NoopVMTracer, NoopTracer}; use types::transaction::SYSTEM_ADDRESS; diff --git a/evmbin/Cargo.toml b/evmbin/Cargo.toml index e1fce6b847f..240abbf0aec 100644 --- a/evmbin/Cargo.toml +++ b/evmbin/Cargo.toml @@ -23,7 +23,7 @@ rustc-hex = "1.0" serde = "1.0" serde_derive = "1.0" serde_json = "1.0" -state-account = { path = "../ethcore/state-account", features = ["to-pod-full"] } +account-state = { path = "../ethcore/account-state", features = ["to-pod-full"] } trace = { path = "../ethcore/trace" } vm = { path = "../ethcore/vm" } diff --git a/evmbin/src/info.rs b/evmbin/src/info.rs index a20957e6860..697fc19b32e 100644 --- a/evmbin/src/info.rs +++ b/evmbin/src/info.rs @@ -25,7 +25,7 @@ use ethjson; use pod::PodState; use types::transaction; use vm::ActionParams; -use state_account::State; +use account_state::State; /// VM execution informant pub trait Informant: trace::VMTracer { diff --git a/evmbin/src/main.rs b/evmbin/src/main.rs index cd6b6488a94..845642c6027 100644 --- a/evmbin/src/main.rs +++ b/evmbin/src/main.rs @@ -50,7 +50,7 @@ extern crate evm; extern crate panic_hook; extern crate pod; extern crate env_logger; -extern crate state_account; +extern crate account_state; extern crate trace; #[cfg(test)] diff --git a/rpc/Cargo.toml b/rpc/Cargo.toml index 624630e79c9..17037173d43 100644 --- a/rpc/Cargo.toml +++ b/rpc/Cargo.toml @@ -60,7 +60,7 @@ parity-runtime = { path = "../util/runtime" } parity-updater = { path = "../updater" } parity-version = { path = "../util/version" } rlp = "0.4.0" -state-account = { path = "../ethcore/state-account" } +account-state = { path = "../ethcore/account-state" } stats = { path = "../util/stats" } vm = { path = "../ethcore/vm" } diff --git a/rpc/src/lib.rs b/rpc/src/lib.rs index 310d064e0e2..858e3eae687 100644 --- a/rpc/src/lib.rs +++ b/rpc/src/lib.rs @@ -82,7 +82,7 @@ extern crate parity_updater as updater; extern crate parity_version as version; extern crate eip_712; extern crate rlp; -extern crate state_account; +extern crate account_state; extern crate stats; extern crate tempdir; extern crate vm; diff --git a/rpc/src/v1/impls/parity.rs b/rpc/src/v1/impls/parity.rs index 4ca3d3ae344..d10e4674b38 100644 --- a/rpc/src/v1/impls/parity.rs +++ b/rpc/src/v1/impls/parity.rs @@ -23,7 +23,7 @@ use ethereum_types::{H64, H160, H256, H512, U64, U256}; use ethcore::client::{BlockChainClient, StateClient, Call}; use ethcore::miner::{self, MinerService, FilterOptions}; use ethcore::snapshot::{SnapshotService, RestorationStatus}; -use state_account::state::StateInfo; +use account_state::state::StateInfo; use ethcore_logger::RotatingLogger; use ethkey::{crypto::ecies, Brain, Generator}; use ethstore::random_phrase; diff --git a/rpc/src/v1/tests/helpers/miner_service.rs b/rpc/src/v1/tests/helpers/miner_service.rs index b57f763eec3..63dc2fa898b 100644 --- a/rpc/src/v1/tests/helpers/miner_service.rs +++ b/rpc/src/v1/tests/helpers/miner_service.rs @@ -30,7 +30,7 @@ use ethtrie; use miner::pool::local_transactions::Status as LocalTransactionStatus; use miner::pool::{verifier, VerifiedTransaction, QueueStatus}; use parking_lot::{RwLock, Mutex}; -use state_account::state::StateInfo; +use account_state::state::StateInfo; use types::transaction::{self, UnverifiedTransaction, SignedTransaction, PendingTransaction}; use txpool; use types::BlockNumber; From a42720b48de757ceedb3d6bdaf32a7b849aef8f5 Mon Sep 17 00:00:00 2001 From: David Palm Date: Sat, 6 Jul 2019 22:40:23 +0200 Subject: [PATCH 53/87] Do not re-export the trace crate --- Cargo.lock | 1 + ethcore/src/lib.rs | 2 +- evmbin/src/display/simple.rs | 2 +- rpc/Cargo.toml | 1 + rpc/src/lib.rs | 1 + rpc/src/v1/tests/mocked/traces.rs | 4 ++-- rpc/src/v1/types/trace.rs | 6 +++--- 7 files changed, 10 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d43f91603d8..72f50abc882 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2957,6 +2957,7 @@ dependencies = [ "tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "tiny-keccak 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-timer 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "trace 0.1.0", "transaction-pool 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "transient-hashmap 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "vm 0.1.0", diff --git a/ethcore/src/lib.rs b/ethcore/src/lib.rs index 6c85fcbb983..e18768a5a74 100644 --- a/ethcore/src/lib.rs +++ b/ethcore/src/lib.rs @@ -102,7 +102,7 @@ extern crate serde; extern crate stats; extern crate account_state; extern crate time_utils; -pub extern crate trace; +extern crate trace; extern crate triehash_ethereum as triehash; extern crate unexpected; extern crate using_queue; diff --git a/evmbin/src/display/simple.rs b/evmbin/src/display/simple.rs index 58d4a704585..68457183538 100644 --- a/evmbin/src/display/simple.rs +++ b/evmbin/src/display/simple.rs @@ -16,7 +16,7 @@ //! Simple VM output. -use ethcore::trace; +use trace; use bytes::ToPretty; use display; diff --git a/rpc/Cargo.toml b/rpc/Cargo.toml index 17037173d43..585fc471dc5 100644 --- a/rpc/Cargo.toml +++ b/rpc/Cargo.toml @@ -62,6 +62,7 @@ parity-version = { path = "../util/version" } rlp = "0.4.0" account-state = { path = "../ethcore/account-state" } stats = { path = "../util/stats" } +trace = { path = "../ethcore/trace" } vm = { path = "../ethcore/vm" } [dev-dependencies] diff --git a/rpc/src/lib.rs b/rpc/src/lib.rs index 858e3eae687..08142b3636e 100644 --- a/rpc/src/lib.rs +++ b/rpc/src/lib.rs @@ -85,6 +85,7 @@ extern crate rlp; extern crate account_state; extern crate stats; extern crate tempdir; +extern crate trace; extern crate vm; #[cfg(any(test, feature = "ethcore-accounts"))] diff --git a/rpc/src/v1/tests/mocked/traces.rs b/rpc/src/v1/tests/mocked/traces.rs index 8f0f0eaa0c6..d6cb0399b2f 100644 --- a/rpc/src/v1/tests/mocked/traces.rs +++ b/rpc/src/v1/tests/mocked/traces.rs @@ -17,8 +17,8 @@ use std::sync::Arc; use ethcore::executed::{Executed, CallError}; -use ethcore::trace::trace::{Action, Res, Call}; -use ethcore::trace::LocalizedTrace; +use trace::trace::{Action, Res, Call}; +use trace::LocalizedTrace; use ethcore::client::TestBlockChainClient; use ethereum_types::{Address, H256}; diff --git a/rpc/src/v1/types/trace.rs b/rpc/src/v1/types/trace.rs index b8fc2c001b3..ca6c89e9ef6 100644 --- a/rpc/src/v1/types/trace.rs +++ b/rpc/src/v1/types/trace.rs @@ -17,8 +17,8 @@ use std::collections::BTreeMap; use ethcore::client::Executed; -use ethcore::trace as et; -use ethcore::trace::{FlatTrace, LocalizedTrace as EthLocalizedTrace, trace, TraceError}; +use trace as et; +use trace::{FlatTrace, LocalizedTrace as EthLocalizedTrace, trace, TraceError}; use ethereum_types::{H160, H256, U256}; use serde::ser::SerializeStruct; use serde::{Serialize, Serializer}; @@ -656,7 +656,7 @@ mod tests { use serde_json; use std::collections::BTreeMap; use v1::types::Bytes; - use ethcore::trace::TraceError; + use trace::TraceError; use ethereum_types::Address; use super::*; From 515abdfca4d8cff4db32d98cf6b7043e1bcba297 Mon Sep 17 00:00:00 2001 From: David Palm Date: Sat, 6 Jul 2019 23:09:17 +0200 Subject: [PATCH 54/87] Don't export state_db from ethcore --- ethcore/private-tx/tests/private_contract.rs | 2 +- ethcore/src/client/evm_test_client.rs | 11 ++++++++++- ethcore/src/lib.rs | 4 ++-- ethcore/sync/src/chain/mod.rs | 2 +- ethcore/sync/src/tests/private.rs | 2 +- evmbin/src/info.rs | 8 ++------ 6 files changed, 17 insertions(+), 12 deletions(-) diff --git a/ethcore/private-tx/tests/private_contract.rs b/ethcore/private-tx/tests/private_contract.rs index 3bb854285b3..75d73dda4b4 100644 --- a/ethcore/private-tx/tests/private_contract.rs +++ b/ethcore/private-tx/tests/private_contract.rs @@ -35,7 +35,7 @@ use types::ids::BlockId; use types::transaction::{Transaction, Action}; use ethcore::CreateContractAddress; use ethcore::client::BlockChainClient; -use ethcore::executive::{contract_address}; +use ethcore::executive::contract_address; use ethcore::miner::Miner; use ethcore::test_helpers::{generate_dummy_client, push_block_with_transactions}; use ethkey::{Secret, KeyPair, Signature}; diff --git a/ethcore/src/client/evm_test_client.rs b/ethcore/src/client/evm_test_client.rs index 091a70f903d..396546a37c6 100644 --- a/ethcore/src/client/evm_test_client.rs +++ b/ethcore/src/client/evm_test_client.rs @@ -78,6 +78,15 @@ fn no_dump_state(_: &State) -> Option { None } +#[cfg(feature = "to-pod-full")] +fn dump_state(state: &State) -> Option { + state.to_pod_full().ok() +} +#[cfg(not(feature = "to-pod-full"))] +fn dump_state(_: &State) -> Option { + None +} + impl<'a> fmt::Debug for EvmTestClient<'a> { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { fmt.debug_struct("EvmTestClient") @@ -104,7 +113,7 @@ impl<'a> EvmTestClient<'a> { } /// Change default function for dump state (default does not dump) - pub fn set_dump_state_fn(&mut self, dump_state: fn(&State) -> Option) { + pub fn set_dump_state(&mut self) { self.dump_state = dump_state; } diff --git a/ethcore/src/lib.rs b/ethcore/src/lib.rs index e18768a5a74..302de9b3be8 100644 --- a/ethcore/src/lib.rs +++ b/ethcore/src/lib.rs @@ -163,11 +163,11 @@ pub mod machine; pub mod miner; pub mod snapshot; pub mod spec; -pub mod state_db; -pub mod transaction_ext; pub mod verification; mod externalities; +mod state_db; +mod transaction_ext; mod tx_filter; diff --git a/ethcore/sync/src/chain/mod.rs b/ethcore/sync/src/chain/mod.rs index fb655308d98..0c8d9f8e48c 100644 --- a/ethcore/sync/src/chain/mod.rs +++ b/ethcore/sync/src/chain/mod.rs @@ -109,7 +109,7 @@ use rlp::{RlpStream, DecoderError}; use network::{self, PeerId, PacketId}; use network::client_version::ClientVersion; use ethcore::client::{BlockChainClient, BlockStatus, BlockId, BlockChainInfo, BlockQueueInfo}; -use ethcore::snapshot::{RestorationStatus}; +use ethcore::snapshot::RestorationStatus; use sync_io::SyncIo; use super::{WarpSync, SyncConfig}; use block_sync::{BlockDownloader, DownloadAction}; diff --git a/ethcore/sync/src/tests/private.rs b/ethcore/sync/src/tests/private.rs index dd421cef8aa..4a74447d9c2 100644 --- a/ethcore/sync/src/tests/private.rs +++ b/ethcore/sync/src/tests/private.rs @@ -21,7 +21,7 @@ use types::transaction::{Transaction, Action}; use types::ids::BlockId; use ethcore::CreateContractAddress; use ethcore::client::{ClientIoMessage, BlockChainClient}; -use ethcore::executive::{contract_address}; +use ethcore::executive::contract_address; use ethcore::engines; use ethcore::miner::{self, MinerService}; use ethcore::spec::Spec; diff --git a/evmbin/src/info.rs b/evmbin/src/info.rs index 697fc19b32e..091adf74ca0 100644 --- a/evmbin/src/info.rs +++ b/evmbin/src/info.rs @@ -19,7 +19,7 @@ use std::time::{Instant, Duration}; use ethereum_types::{H256, U256}; use ethcore::client::{self, EvmTestClient, EvmTestError, TransactErr, TransactSuccess}; -use ethcore::{state_db, spec, TrieSpec}; +use ethcore::{spec, TrieSpec}; use trace; use ethjson; use pod::PodState; @@ -155,10 +155,6 @@ pub fn run_transaction( T::finish(result, &mut sink) } -fn dump_state(state: &State) -> Option { - state.to_pod_full().ok() -} - /// Execute VM with given `ActionParams` pub fn run<'a, F, X>( spec: &'a spec::Spec, @@ -182,7 +178,7 @@ pub fn run<'a, F, X>( })?; if do_dump { - test_client.set_dump_state_fn(dump_state); + test_client.set_dump_state(); } let start = Instant::now(); From 081e678f29ae5665e867e546534cb1f160ed3b14 Mon Sep 17 00:00:00 2001 From: David Palm Date: Sat, 6 Jul 2019 23:18:01 +0200 Subject: [PATCH 55/87] Let private-tx use StateDB. :( --- ethcore/private-tx/src/lib.rs | 4 ++-- ethcore/src/lib.rs | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/ethcore/private-tx/src/lib.rs b/ethcore/private-tx/src/lib.rs index db081a5eae2..5da4b86c0e2 100644 --- a/ethcore/private-tx/src/lib.rs +++ b/ethcore/private-tx/src/lib.rs @@ -94,7 +94,7 @@ use ethcore::client::{ Call, BlockInfo }; use ethcore::miner::{self, Miner, MinerService, pool_client::NonceCache}; -use ethcore::state_db; +use ethcore::StateDB; use account_state::State; use trace::{Tracer, VMTracer}; use call_contract::CallContract; @@ -542,7 +542,7 @@ impl Provider { raw } - fn patch_account_state(&self, contract_address: &Address, block: BlockId, state: &mut State) -> Result<(), Error> { + fn patch_account_state(&self, contract_address: &Address, block: BlockId, state: &mut State) -> Result<(), Error> { let contract_code = Arc::new(self.get_decrypted_code(contract_address, block)?); let contract_state = self.get_decrypted_state(contract_address, block)?; trace!(target: "privatetx", "Patching contract at {:?}, code: {:?}, state: {:?}", contract_address, contract_code, contract_state); diff --git a/ethcore/src/lib.rs b/ethcore/src/lib.rs index d70b50369c2..223ee42f287 100644 --- a/ethcore/src/lib.rs +++ b/ethcore/src/lib.rs @@ -181,3 +181,4 @@ pub mod test_helpers; pub use executive::contract_address; pub use evm::CreateContractAddress; pub use trie::TrieSpec; +pub use state_db::StateDB; From 659440a2295a55f699c70e4c30804dc7269188a1 Mon Sep 17 00:00:00 2001 From: David Palm Date: Sat, 6 Jul 2019 23:44:59 +0200 Subject: [PATCH 56/87] Remove ethcore/src/pod_state.rs --- ethcore/src/pod_state.rs | 194 --------------------------------------- 1 file changed, 194 deletions(-) delete mode 100644 ethcore/src/pod_state.rs diff --git a/ethcore/src/pod_state.rs b/ethcore/src/pod_state.rs deleted file mode 100644 index b952b99554d..00000000000 --- a/ethcore/src/pod_state.rs +++ /dev/null @@ -1,194 +0,0 @@ -// 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 of all accounts in the system expressed in Plain Old Data. - -use std::collections::BTreeMap; -use ethereum_types::{H256, Address}; -use triehash::sec_trie_root; -use pod::{self, PodAccount}; -use types::state_diff::StateDiff; -use ethjson; - -/// State of all accounts in the system expressed in Plain Old Data. -#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize)] -pub struct PodState(BTreeMap); - -impl PodState { - /// Get the underlying map. - pub fn get(&self) -> &BTreeMap { &self.0 } - - /// Get the root hash of the trie of the RLP of this. - pub fn root(&self) -> H256 { - sec_trie_root(self.0.iter().map(|(k, v)| (k, v.rlp()))) - } - - /// Drain object to get the underlying map. - pub fn drain(self) -> BTreeMap { self.0 } -} - -impl From for PodState { - fn from(s: ethjson::blockchain::State) -> PodState { - let state = s.into_iter().map(|(addr, acc)| (addr.into(), PodAccount::from(acc))).collect(); - PodState(state) - } -} - -impl From for PodState { - fn from(s: ethjson::spec::State) -> PodState { - let state: BTreeMap<_,_> = s.into_iter() - .filter(|pair| !pair.1.is_empty()) - .map(|(addr, acc)| (addr.into(), PodAccount::from(acc))) - .collect(); - PodState(state) - } -} - -impl From> for PodState { - fn from(s: BTreeMap) -> Self { - PodState(s) - } -} - -/// Calculate and return diff between `pre` state and `post` state. -pub fn diff_pod(pre: &PodState, post: &PodState) -> StateDiff { - StateDiff { - raw: pre.0.keys() - .chain(post.0.keys()) - .filter_map(|acc| pod_account::diff_pod(pre.0.get(acc), post.0.get(acc)).map(|d| (*acc, d))) - .collect() - } -} - -#[cfg(test)] -mod test { - use std::collections::BTreeMap; - use pod::PodAccount; - use types::account_diff::{AccountDiff, Diff}; - use types::state_diff::StateDiff; - use super::{PodState, Address}; - - #[test] - fn create_delete() { - let a = PodState::from(map![ - Address::from_low_u64_be(1) => PodAccount { - balance: 69.into(), - nonce: 0.into(), - code: Some(Vec::new()), - storage: map![], - } - ]); - assert_eq!(super::diff_pod(&a, &PodState::default()), StateDiff { raw: map![ - Address::from_low_u64_be(1) => AccountDiff{ - balance: Diff::Died(69.into()), - nonce: Diff::Died(0.into()), - code: Diff::Died(vec![]), - storage: map![], - } - ]}); - assert_eq!(super::diff_pod(&PodState::default(), &a), StateDiff{ raw: map![ - Address::from_low_u64_be(1) => AccountDiff{ - balance: Diff::Born(69.into()), - nonce: Diff::Born(0.into()), - code: Diff::Born(vec![]), - storage: map![], - } - ]}); - } - - #[test] - fn create_delete_with_unchanged() { - let a = PodState::from(map![ - Address::from_low_u64_be(1) => PodAccount { - balance: 69.into(), - nonce: 0.into(), - code: Some(Vec::new()), - storage: map![], - } - ]); - let b = PodState::from(map![ - Address::from_low_u64_be(1) => PodAccount { - balance: 69.into(), - nonce: 0.into(), - code: Some(Vec::new()), - storage: map![], - }, - Address::from_low_u64_be(2) => PodAccount { - balance: 69.into(), - nonce: 0.into(), - code: Some(Vec::new()), - storage: map![], - } - ]); - assert_eq!(super::diff_pod(&a, &b), StateDiff { raw: map![ - Address::from_low_u64_be(2) => AccountDiff{ - balance: Diff::Born(69.into()), - nonce: Diff::Born(0.into()), - code: Diff::Born(vec![]), - storage: map![], - } - ]}); - assert_eq!(super::diff_pod(&b, &a), StateDiff { raw: map![ - Address::from_low_u64_be(2) => AccountDiff{ - balance: Diff::Died(69.into()), - nonce: Diff::Died(0.into()), - code: Diff::Died(vec![]), - storage: map![], - } - ]}); - } - - #[test] - fn change_with_unchanged() { - let a = PodState::from(map![ - Address::from_low_u64_be(1) => PodAccount { - balance: 69.into(), - nonce: 0.into(), - code: Some(Vec::new()), - storage: map![], - }, - Address::from_low_u64_be(2) => PodAccount { - balance: 69.into(), - nonce: 0.into(), - code: Some(Vec::new()), - storage: map![], - } - ]); - let b = PodState::from(map![ - Address::from_low_u64_be(1) => PodAccount { - balance: 69.into(), - nonce: 1.into(), - code: Some(Vec::new()), - storage: map![], - }, - Address::from_low_u64_be(2) => PodAccount { - balance: 69.into(), - nonce: 0.into(), - code: Some(Vec::new()), - storage: map![], - } - ]); - assert_eq!(super::diff_pod(&a, &b), StateDiff { raw: map![ - Address::from_low_u64_be(1) => AccountDiff{ - balance: Diff::Same, - nonce: Diff::Changed(0.into(), 1.into()), - code: Diff::Same, - storage: map![], - } - ]}); - } - -} From 2f839f8a0f72f71334da64620f57e6dd6039f06b Mon Sep 17 00:00:00 2001 From: David Palm Date: Sun, 7 Jul 2019 16:18:11 +0200 Subject: [PATCH 57/87] Inner type does not need to be pub/pub(crate) --- ethcore/trace/src/types/flat.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ethcore/trace/src/types/flat.rs b/ethcore/trace/src/types/flat.rs index 203eb7f5db4..2c7b56ecd04 100644 --- a/ethcore/trace/src/types/flat.rs +++ b/ethcore/trace/src/types/flat.rs @@ -74,7 +74,7 @@ impl Decodable for FlatTrace { /// Represents all traces produced by a single transaction. #[derive(Debug, PartialEq, Clone, RlpEncodableWrapper, RlpDecodableWrapper, MallocSizeOf)] -pub struct FlatTransactionTraces(pub(crate) Vec); +pub struct FlatTransactionTraces(Vec); impl From> for FlatTransactionTraces { fn from(v: Vec) -> Self { @@ -97,7 +97,7 @@ impl Into> for FlatTransactionTraces { /// Represents all traces produced by transactions in a single block. #[derive(Debug, PartialEq, Clone, Default, RlpEncodableWrapper, RlpDecodableWrapper, MallocSizeOf)] -pub struct FlatBlockTraces(pub(crate) Vec); +pub struct FlatBlockTraces(Vec); impl From> for FlatBlockTraces { fn from(v: Vec) -> Self { From df23dccdfd00cf8322581f0136ed414e4d5201bc Mon Sep 17 00:00:00 2001 From: David Palm Date: Sun, 7 Jul 2019 16:25:54 +0200 Subject: [PATCH 58/87] optimise imports --- ethcore/trace/src/db.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ethcore/trace/src/db.rs b/ethcore/trace/src/db.rs index 38b76b0b431..c1b60ec13d6 100644 --- a/ethcore/trace/src/db.rs +++ b/ethcore/trace/src/db.rs @@ -23,12 +23,11 @@ use ethcore_db::{ cache_manager::CacheManager, Key, Writable, Readable, CacheUpdatePolicy, }; -use ethcore_blockchain::BlockChainDB; use ethereum_types::{H256, H264}; use kvdb::DBTransaction; use parking_lot::RwLock; use common_types::BlockNumber; -use ethcore_blockchain::{BlockChain, BlockProvider, TransactionAddress}; +use ethcore_blockchain::{BlockChain, BlockChainDB, BlockProvider, TransactionAddress}; use crate::{ LocalizedTrace, Config, Filter, Database as TraceDatabase, ImportRequest, DatabaseExtras, From e23cb6aa117c65a9061946848e0ee482b8e875f8 Mon Sep 17 00:00:00 2001 From: David Palm Date: Sun, 7 Jul 2019 17:10:24 +0200 Subject: [PATCH 59/87] Revert "Inner type does not need to be pub/pub(crate)" This reverts commit 2f839f8a0f72f71334da64620f57e6dd6039f06b. --- ethcore/trace/src/types/flat.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ethcore/trace/src/types/flat.rs b/ethcore/trace/src/types/flat.rs index 2c7b56ecd04..203eb7f5db4 100644 --- a/ethcore/trace/src/types/flat.rs +++ b/ethcore/trace/src/types/flat.rs @@ -74,7 +74,7 @@ impl Decodable for FlatTrace { /// Represents all traces produced by a single transaction. #[derive(Debug, PartialEq, Clone, RlpEncodableWrapper, RlpDecodableWrapper, MallocSizeOf)] -pub struct FlatTransactionTraces(Vec); +pub struct FlatTransactionTraces(pub(crate) Vec); impl From> for FlatTransactionTraces { fn from(v: Vec) -> Self { @@ -97,7 +97,7 @@ impl Into> for FlatTransactionTraces { /// Represents all traces produced by transactions in a single block. #[derive(Debug, PartialEq, Clone, Default, RlpEncodableWrapper, RlpDecodableWrapper, MallocSizeOf)] -pub struct FlatBlockTraces(Vec); +pub struct FlatBlockTraces(pub(crate) Vec); impl From> for FlatBlockTraces { fn from(v: Vec) -> Self { From de2aa804786e96b0cd29231ee46d3271222c7dd4 Mon Sep 17 00:00:00 2001 From: David Palm Date: Sun, 7 Jul 2019 17:25:02 +0200 Subject: [PATCH 60/87] Move DatabaseExtras to ethcore-blockchain --- ethcore/blockchain/src/lib.rs | 15 ++++++++----- ethcore/trace/src/db.rs | 26 ++++------------------- ethcore/trace/src/lib.rs | 40 +++++++++++++++-------------------- 3 files changed, 31 insertions(+), 50 deletions(-) diff --git a/ethcore/blockchain/src/lib.rs b/ethcore/blockchain/src/lib.rs index 004817138de..29b3a1808ae 100644 --- a/ethcore/blockchain/src/lib.rs +++ b/ethcore/blockchain/src/lib.rs @@ -28,13 +28,18 @@ mod cache; mod config; mod import_route; mod update; +mod database_extras; pub mod generator; -pub use self::blockchain::{BlockProvider, BlockChain, BlockChainDB, BlockChainDBHandler}; -pub use self::cache::CacheSize; -pub use self::config::Config; -pub use self::import_route::ImportRoute; -pub use self::update::ExtrasInsert; +pub use crate::{ + blockchain::{BlockProvider, BlockChain, BlockChainDB, BlockChainDBHandler}, + cache::CacheSize, + config::Config, + database_extras::DatabaseExtras, + import_route::ImportRoute, + update::ExtrasInsert, +}; pub use ethcore_db::keys::{BlockReceipts, BlockDetails, TransactionAddress, BlockNumberKey}; pub use common_types::tree_route::TreeRoute; + diff --git a/ethcore/trace/src/db.rs b/ethcore/trace/src/db.rs index c1b60ec13d6..53d7aeed4a5 100644 --- a/ethcore/trace/src/db.rs +++ b/ethcore/trace/src/db.rs @@ -27,10 +27,10 @@ use ethereum_types::{H256, H264}; use kvdb::DBTransaction; use parking_lot::RwLock; use common_types::BlockNumber; -use ethcore_blockchain::{BlockChain, BlockChainDB, BlockProvider, TransactionAddress}; +use ethcore_blockchain::{BlockChainDB, DatabaseExtras}; use crate::{ - LocalizedTrace, Config, Filter, Database as TraceDatabase, ImportRequest, DatabaseExtras, + LocalizedTrace, Config, Filter, Database as TraceDatabase, ImportRequest, flat::{FlatTrace, FlatBlockTraces, FlatTransactionTraces}, }; @@ -336,25 +336,6 @@ impl TraceDatabase for TraceDB where T: DatabaseExtras { } } -/// Bridge between TraceDb and Blockchain. -impl DatabaseExtras for BlockChain { - fn block_hash(&self, block_number: BlockNumber) -> Option { - (self as &dyn BlockProvider).block_hash(block_number) - } - - fn transaction_hash(&self, block_number: BlockNumber, tx_position: usize) -> Option { - (self as &dyn BlockProvider).block_hash(block_number) - .and_then(|block_hash| { - let tx_address = TransactionAddress { - block_hash, - index: tx_position - }; - self.transaction(&tx_address) - }) - .map(|tx| tx.hash()) - } -} - #[cfg(test)] mod tests { use std::{ @@ -362,13 +343,14 @@ mod tests { sync::Arc, }; use common_types::BlockNumber; + use ethcore_blockchain::DatabaseExtras; use ethcore::test_helpers::new_db; use ethereum_types::{H256, U256, Address}; use evm::CallType; use kvdb::DBTransaction; use crate::{ - Config, TraceDB, Database as TraceDatabase, DatabaseExtras, ImportRequest, + Config, TraceDB, Database as TraceDatabase, ImportRequest, Filter, LocalizedTrace, AddressesFilter, TraceError, trace::{Call, Action, Res}, flat::{FlatTrace, FlatBlockTraces, FlatTransactionTraces} diff --git a/ethcore/trace/src/lib.rs b/ethcore/trace/src/lib.rs index 8fc443d92e0..1bcdc478225 100644 --- a/ethcore/trace/src/lib.rs +++ b/ethcore/trace/src/lib.rs @@ -17,7 +17,7 @@ //! Tracing use common_types::BlockNumber; -use ethereum_types::{H256, U256, Address}; +use ethereum_types::{U256, Address}; use kvdb::DBTransaction; use vm::{Error as VmError, ActionParams}; // The MallocSizeOf derive looks for this in the root @@ -30,18 +30,22 @@ mod import; mod noop_tracer; mod types; -pub use self::config::Config; -pub use self::db::TraceDB; -pub use self::noop_tracer::{NoopTracer, NoopVMTracer}; -pub use self::executive_tracer::{ExecutiveTracer, ExecutiveVMTracer}; -pub use self::import::ImportRequest; -pub use self::localized::LocalizedTrace; - -pub use self::types::{filter, flat, localized, trace, Tracing}; -pub use self::types::error::Error as TraceError; -pub use self::types::trace::{VMTrace, VMOperation, VMExecutedOperation, MemoryDiff, StorageDiff, RewardType}; -pub use self::types::flat::{FlatTrace, FlatTransactionTraces, FlatBlockTraces}; -pub use self::types::filter::{Filter, AddressesFilter}; +pub use crate::{ + config::Config, + db::TraceDB, + localized::LocalizedTrace, + executive_tracer::{ExecutiveTracer, ExecutiveVMTracer}, + import::ImportRequest, + noop_tracer::{NoopTracer, NoopVMTracer}, + types::{ + Tracing, + error::Error as TraceError, + localized, + trace::{self, VMTrace, VMOperation, VMExecutedOperation, MemoryDiff, StorageDiff, RewardType}, + flat::{self, FlatTrace, FlatTransactionTraces, FlatBlockTraces}, + filter::{self, Filter, AddressesFilter}, + } +}; /// This trait is used by executive to build traces. pub trait Tracer: Send { @@ -101,16 +105,6 @@ pub trait VMTracer: Send { } -/// `DbExtras` provides an interface to query extra data which is not stored in tracesdb, -/// but necessary to work correctly. -pub trait DatabaseExtras { - /// Returns hash of given block number. - fn block_hash(&self, block_number: BlockNumber) -> Option; - - /// Returns hash of transaction at given position. - fn transaction_hash(&self, block_number: BlockNumber, tx_position: usize) -> Option; -} - /// Db provides an interface to query tracesdb. pub trait Database { /// Returns true if tracing is enabled. Otherwise false. From b4ea86a8071b9229a9866d2ff2b4575c274771c5 Mon Sep 17 00:00:00 2001 From: David Palm Date: Sun, 7 Jul 2019 17:26:00 +0200 Subject: [PATCH 61/87] Add database_extra module to ethcore-blockchain --- ethcore/blockchain/src/database_extras.rs | 53 +++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 ethcore/blockchain/src/database_extras.rs diff --git a/ethcore/blockchain/src/database_extras.rs b/ethcore/blockchain/src/database_extras.rs new file mode 100644 index 00000000000..b1470f35374 --- /dev/null +++ b/ethcore/blockchain/src/database_extras.rs @@ -0,0 +1,53 @@ +// 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 . + +//! Provides a `DatabaseExtras` trait that defines an interface to query for block data not +//! contained in a TraceDB. + +use common_types::BlockNumber; +use ethereum_types::H256; +use ethcore_db::keys::TransactionAddress; + +use crate::blockchain::{BlockProvider, BlockChain}; + +/// `DatabaseExtras` provides an interface to query extra data which is not stored in TraceDB, +/// but necessary to work correctly. +pub trait DatabaseExtras { + /// Returns hash of given block number. + fn block_hash(&self, block_number: BlockNumber) -> Option; + + /// Returns hash of transaction at given position. + fn transaction_hash(&self, block_number: BlockNumber, tx_position: usize) -> Option; +} + +/// Bridge between TraceDB and Blockchain. +impl DatabaseExtras for BlockChain { + fn block_hash(&self, block_number: BlockNumber) -> Option { + (self as &dyn BlockProvider).block_hash(block_number) + } + + fn transaction_hash(&self, block_number: BlockNumber, tx_position: usize) -> Option { + (self as &dyn BlockProvider).block_hash(block_number) + .and_then(|block_hash| { + let tx_address = TransactionAddress { + block_hash, + index: tx_position + }; + self.transaction(&tx_address) + }) + .map(|tx| tx.hash()) + } +} From a57517c3bc8ce2f81295d141c746eb7813f872e2 Mon Sep 17 00:00:00 2001 From: David Palm Date: Sun, 7 Jul 2019 19:57:30 +0200 Subject: [PATCH 62/87] Remove to-pod-full feature --- ethcore/Cargo.toml | 4 +--- ethcore/account-state/Cargo.toml | 4 ---- ethcore/account-state/src/state.rs | 3 +-- ethcore/src/client/evm_test_client.rs | 5 ----- ethcore/src/executive_state.rs | 2 -- evmbin/Cargo.toml | 4 ++-- 6 files changed, 4 insertions(+), 18 deletions(-) diff --git a/ethcore/Cargo.toml b/ethcore/Cargo.toml index 9a1ad42c809..4b559294c3f 100644 --- a/ethcore/Cargo.toml +++ b/ethcore/Cargo.toml @@ -108,15 +108,13 @@ evm-debug-tests = ["evm-debug", "evm/evm-debug-tests"] # EVM debug traces are printed. slow-blocks = [] # Run JSON consensus tests. -json-tests = ["env_logger", "test-helpers", "to-pod-full"] +json-tests = ["env_logger", "test-helpers"] # Skip JSON consensus tests with pending issues. ci-skip-tests = [] # Run memory/cpu heavy tests. test-heavy = [] # Compile test helpers test-helpers = ["tempdir", "kvdb-rocksdb", "blooms-db"] -# Enables slow 'to-pod-full' method for use in tests and evmbin. -to-pod-full = [] [[bench]] name = "builtin" diff --git a/ethcore/account-state/Cargo.toml b/ethcore/account-state/Cargo.toml index 9d32f3dea99..d2ce8ebfa9e 100644 --- a/ethcore/account-state/Cargo.toml +++ b/ethcore/account-state/Cargo.toml @@ -35,7 +35,3 @@ account-db = { path = "../account-db" } journaldb = { path = "../../util/journaldb" } parity-bytes = "0.1.0" rlp_compress = { path = "../../util/rlp-compress" } - -[features] -# Enables slow 'to-pod-full' method for use in tests and evmbin. -to-pod-full = [] diff --git a/ethcore/account-state/src/state.rs b/ethcore/account-state/src/state.rs index 01b9dd5dfd9..0ec5a8168aa 100644 --- a/ethcore/account-state/src/state.rs +++ b/ethcore/account-state/src/state.rs @@ -791,7 +791,6 @@ impl State { })) } - #[cfg(feature="to-pod-full")] /// Populate a PodAccount map from this state. /// Warning this is not for real time use. /// Use of this method requires FatDB mode to be able @@ -830,9 +829,9 @@ impl State { /// values of the account to the PodAccount. /// This function is only intended for use in small tests or with fresh accounts. /// It requires FatDB. - #[cfg(feature="to-pod-full")] fn account_to_pod_account(&self, account: &Account, address: &Address) -> Result { use ethereum_types::BigEndianHash; + assert!(self.factories.trie.is_fat()); let mut pod_storage = BTreeMap::new(); let addr_hash = account.address_hash(address); diff --git a/ethcore/src/client/evm_test_client.rs b/ethcore/src/client/evm_test_client.rs index 396546a37c6..5c003ddb70a 100644 --- a/ethcore/src/client/evm_test_client.rs +++ b/ethcore/src/client/evm_test_client.rs @@ -78,14 +78,9 @@ fn no_dump_state(_: &State) -> Option { None } -#[cfg(feature = "to-pod-full")] fn dump_state(state: &State) -> Option { state.to_pod_full().ok() } -#[cfg(not(feature = "to-pod-full"))] -fn dump_state(_: &State) -> Option { - None -} impl<'a> fmt::Debug for EvmTestClient<'a> { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { diff --git a/ethcore/src/executive_state.rs b/ethcore/src/executive_state.rs index a99a4a15313..67dfb38e286 100644 --- a/ethcore/src/executive_state.rs +++ b/ethcore/src/executive_state.rs @@ -274,7 +274,6 @@ mod tests { use trace::{FlatTrace, TraceError, trace}; use evm::CallType; use pod::{self, PodAccount}; - #[cfg(feature="to-pod-full")] use pod::PodState; use executive_state::ExecutiveState; use account_state::{Account, CleanupMode}; @@ -1653,7 +1652,6 @@ mod tests { })).as_ref()); } - #[cfg(feature="to-pod-full")] #[test] fn should_get_full_pod_storage_values() { use trie::{TrieFactory, TrieSpec}; diff --git a/evmbin/Cargo.toml b/evmbin/Cargo.toml index 240abbf0aec..a835674a460 100644 --- a/evmbin/Cargo.toml +++ b/evmbin/Cargo.toml @@ -12,7 +12,7 @@ path = "./src/main.rs" common-types = { path = "../ethcore/types" } docopt = "1.0" env_logger = "0.5" -ethcore = { path = "../ethcore", features = ["test-helpers", "json-tests", "to-pod-full"] } +ethcore = { path = "../ethcore", features = ["test-helpers", "json-tests"] } ethereum-types = "0.6.0" ethjson = { path = "../json" } evm = { path = "../ethcore/evm" } @@ -23,7 +23,7 @@ rustc-hex = "1.0" serde = "1.0" serde_derive = "1.0" serde_json = "1.0" -account-state = { path = "../ethcore/account-state", features = ["to-pod-full"] } +account-state = { path = "../ethcore/account-state" } trace = { path = "../ethcore/trace" } vm = { path = "../ethcore/vm" } From 5a28340a0a6dd9b90e70b08088909beedadb9ad6 Mon Sep 17 00:00:00 2001 From: David Palm Date: Mon, 8 Jul 2019 10:48:29 +0200 Subject: [PATCH 63/87] cosmetics --- ethcore/service/src/service.rs | 4 ++-- ethcore/src/machine.rs | 4 ++-- ethcore/src/spec/spec.rs | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/ethcore/service/src/service.rs b/ethcore/service/src/service.rs index da949cbe328..a227de06b5f 100644 --- a/ethcore/service/src/service.rs +++ b/ethcore/service/src/service.rs @@ -119,8 +119,8 @@ impl ClientService { let snapshot_params = SnapServiceParams { engine: spec.engine.clone(), genesis_block: spec.genesis_block(), - restoration_db_handler: restoration_db_handler, - pruning: pruning, + restoration_db_handler, + pruning, channel: io_service.channel(), snapshot_root: snapshot_path.into(), client: client.clone(), diff --git a/ethcore/src/machine.rs b/ethcore/src/machine.rs index 06f511209b7..4f411f7f0e4 100644 --- a/ethcore/src/machine.rs +++ b/ethcore/src/machine.rs @@ -83,9 +83,9 @@ impl Machine { pub fn regular(params: CommonParams, builtins: BTreeMap) -> Machine { let tx_filter = TransactionFilter::from_params(¶ms).map(Arc::new); Machine { - params: params, + params, builtins: Arc::new(builtins), - tx_filter: tx_filter, + tx_filter, ethash_extensions: None, schedule_rules: None, } diff --git a/ethcore/src/spec/spec.rs b/ethcore/src/spec/spec.rs index a21b1e6de2d..7df91a2feb7 100644 --- a/ethcore/src/spec/spec.rs +++ b/ethcore/src/spec/spec.rs @@ -536,8 +536,8 @@ fn load_from(spec_params: SpecParams, s: ethjson::spec::Spec) -> Result Date: Mon, 8 Jul 2019 11:50:34 +0200 Subject: [PATCH 64/87] New crate: state-db --- Cargo.lock | 24 ++++++++++ ethcore/Cargo.toml | 1 + ethcore/private-tx/Cargo.toml | 3 +- ethcore/private-tx/src/lib.rs | 2 +- ethcore/src/lib.rs | 5 +-- ethcore/state-db/Cargo.toml | 27 ++++++++++++ .../{src/state_db.rs => state-db/src/lib.rs} | 44 ++++++++++--------- 7 files changed, 80 insertions(+), 26 deletions(-) create mode 100644 ethcore/state-db/Cargo.toml rename ethcore/{src/state_db.rs => state-db/src/lib.rs} (96%) diff --git a/Cargo.lock b/Cargo.lock index d12114a9b60..4341ca6ca72 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -914,6 +914,7 @@ dependencies = [ "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", + "state-db 0.1.0", "stats 0.1.0", "tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "time-utils 0.1.0", @@ -1216,6 +1217,7 @@ dependencies = [ "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", + "state-db 0.1.0", "time-utils 0.1.0", "tiny-keccak 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "trace 0.1.0", @@ -3945,6 +3947,28 @@ name = "stable_deref_trait" version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "state-db" +version = "0.1.0" +dependencies = [ + "account-state 0.1.0", + "common-types 0.1.0", + "env_logger 0.5.13 (registry+https://github.com/rust-lang/crates.io-index)", + "ethcore 1.12.0", + "ethcore-bloom-journal 0.1.0", + "ethcore-db 0.1.0", + "ethereum-types 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "hash-db 0.12.4 (registry+https://github.com/rust-lang/crates.io-index)", + "journaldb 0.2.0", + "keccak-hash 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "keccak-hasher 0.1.1", + "kvdb 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "lru-cache 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "memory-cache 0.1.0", + "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "static_assertions" version = "0.2.5" diff --git a/ethcore/Cargo.toml b/ethcore/Cargo.toml index 4b559294c3f..7f0fbadc7a1 100644 --- a/ethcore/Cargo.toml +++ b/ethcore/Cargo.toml @@ -63,6 +63,7 @@ serde = "1.0" serde_derive = "1.0" account-state = { path = "account-state" } stats = { path = "../util/stats" } +state-db = { path = "state-db" } tempdir = { version = "0.3", optional = true } time-utils = { path = "../util/time-utils" } trace = { path = "trace" } diff --git a/ethcore/private-tx/Cargo.toml b/ethcore/private-tx/Cargo.toml index 3f577a9fd1a..afa06edf313 100644 --- a/ethcore/private-tx/Cargo.toml +++ b/ethcore/private-tx/Cargo.toml @@ -6,6 +6,7 @@ license = "GPL-3.0" authors = ["Parity Technologies "] [dependencies] +account-state = { path = "../account-state" } common-types = { path = "../types" } derive_more = "0.14.0" ethabi = "8.0" @@ -35,7 +36,7 @@ rustc-hex = "1.0" serde = "1.0" serde_derive = "1.0" serde_json = "1.0" -account-state = { path = "../account-state" } +state-db = { path = "../state-db" } time-utils = { path = "../../util/time-utils" } tiny-keccak = "1.4" trace = { path = "../trace" } diff --git a/ethcore/private-tx/src/lib.rs b/ethcore/private-tx/src/lib.rs index 5da4b86c0e2..6cf02cd09e0 100644 --- a/ethcore/private-tx/src/lib.rs +++ b/ethcore/private-tx/src/lib.rs @@ -94,7 +94,7 @@ use ethcore::client::{ Call, BlockInfo }; use ethcore::miner::{self, Miner, MinerService, pool_client::NonceCache}; -use ethcore::StateDB; +use state_db::StateDB; use account_state::State; use trace::{Tracer, VMTracer}; use call_contract::CallContract; diff --git a/ethcore/src/lib.rs b/ethcore/src/lib.rs index 15f48402675..9d9aa020f96 100644 --- a/ethcore/src/lib.rs +++ b/ethcore/src/lib.rs @@ -54,6 +54,7 @@ //! ``` extern crate account_db; +extern crate account_state; extern crate ansi_term; extern crate common_types as types; extern crate crossbeam_utils; @@ -97,8 +98,8 @@ extern crate parity_util_mem; extern crate parity_util_mem as malloc_size_of; extern crate rustc_hex; extern crate serde; +extern crate state_db; extern crate stats; -extern crate account_state; extern crate time_utils; extern crate trace; extern crate triehash_ethereum as triehash; @@ -163,7 +164,6 @@ pub mod spec; pub mod verification; mod externalities; -mod state_db; mod transaction_ext; mod tx_filter; @@ -177,4 +177,3 @@ pub mod test_helpers; pub use executive::contract_address; pub use evm::CreateContractAddress; pub use trie::TrieSpec; -pub use state_db::StateDB; diff --git a/ethcore/state-db/Cargo.toml b/ethcore/state-db/Cargo.toml new file mode 100644 index 00000000000..c3e444ec96f --- /dev/null +++ b/ethcore/state-db/Cargo.toml @@ -0,0 +1,27 @@ +[package] +description = "State database" +name = "state-db" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2018" + +[dependencies] +account-state = { path = "../account-state" } +bloom_journal = { package = "ethcore-bloom-journal", path = "../../util/bloom" } +common-types = { path = "../types"} +ethcore-db = { path = "../db" } +ethereum-types = "0.6.0" +hash-db = "0.12.4" +keccak-hash = "0.2.0" +keccak-hasher = { path = "../../util/keccak-hasher" } +journaldb = { path = "../../util/journaldb" } +kvdb = "0.1.0" +log = "0.4.6" +lru-cache = "0.1.2" +memory-cache = { path = "../../util/memory-cache" } +parking_lot = "0.8.0" + +[dev-dependencies] +env_logger = "0.5" +# Used for test helpers +ethcore = { path = "..", features = ["test-helpers"] } diff --git a/ethcore/src/state_db.rs b/ethcore/state-db/src/lib.rs similarity index 96% rename from ethcore/src/state_db.rs rename to ethcore/state-db/src/lib.rs index 2419502976c..de65ac85ca6 100644 --- a/ethcore/src/state_db.rs +++ b/ethcore/state-db/src/lib.rs @@ -16,24 +16,25 @@ //! State database abstraction. For more info, see the doc for `StateDB` -use std::collections::{VecDeque, HashSet}; +use std::collections::{HashSet, VecDeque}; use std::io; use std::sync::Arc; -use bloom_journal::{Bloom, BloomJournal}; -use db::COL_ACCOUNT_BLOOM; -use ethereum_types::{H256, Address}; -use hash::keccak; +use ethereum_types::{Address, H256}; use hash_db::HashDB; -use journaldb::JournalDB; -use keccak_hasher::KeccakHasher; -use kvdb::{KeyValueDB, DBTransaction, DBValue}; +use keccak_hash::keccak; +use kvdb::{DBTransaction, DBValue, KeyValueDB}; +use log::trace; use lru_cache::LruCache; -use memory_cache::MemoryLruCache; use parking_lot::Mutex; -use types::BlockNumber; use account_state::{self, Account}; +use bloom_journal::{Bloom, BloomJournal}; +use common_types::BlockNumber; +use ethcore_db::COL_ACCOUNT_BLOOM; +use journaldb::JournalDB; +use keccak_hasher::KeccakHasher; +use memory_cache::MemoryLruCache; /// Value used to initialize bloom bitmap size. /// @@ -68,7 +69,7 @@ struct AccountCache { struct CacheQueueItem { /// Account address. address: Address, - /// Acccount data or `None` if account does not exist. + /// Account data or `None` if account does not exist. account: SyncAccount, /// Indicates that the account was modified before being /// added to the cache. @@ -143,7 +144,7 @@ impl StateDB { let cache_items = acc_cache_size / ::std::mem::size_of::>(); StateDB { - db: db, + db, account_cache: Arc::new(Mutex::new(AccountCache { accounts: LruCache::new(cache_items), modifications: VecDeque::new(), @@ -151,7 +152,7 @@ impl StateDB { code_cache: Arc::new(Mutex::new(MemoryLruCache::new(code_cache_size))), local_cache: Vec::new(), account_bloom: Arc::new(Mutex::new(bloom)), - cache_size: cache_size, + cache_size, parent_hash: None, commit_hash: None, commit_number: None, @@ -159,7 +160,7 @@ impl StateDB { } /// Loads accounts bloom from the database - /// This bloom is used to handle request for the non-existant account fast + /// This bloom is used to handle request for the non-existent account fast pub fn load_bloom(db: &dyn KeyValueDB) -> Bloom { let hash_count_entry = db.get(COL_ACCOUNT_BLOOM, ACCOUNT_BLOOM_HASHCOUNT_KEY) .expect("Low-level database error"); @@ -177,7 +178,7 @@ impl StateDB { let key: [u8; 8] = (i as u64).to_le_bytes(); bloom_parts[i] = db.get(COL_ACCOUNT_BLOOM, &key).expect("low-level database error") .map(|val| { - assert!(val.len() == 8, "low-level database error"); + assert_eq!(val.len(), 8, "low-level database error"); let mut buff = [0u8; 8]; buff.copy_from_slice(&*val); u64::from_le_bytes(buff) @@ -233,7 +234,7 @@ impl StateDB { let cache = &mut *cache; // Purge changes from re-enacted and retracted blocks. - // Filter out commiting block if any. + // Filter out committing block if any. let mut clear = false; for block in enacted.iter().filter(|h| self.commit_hash.as_ref().map_or(true, |p| *h != p)) { clear = clear || { @@ -419,11 +420,11 @@ impl account_state::Backend for StateDB { self.db.as_hash_db_mut() } - fn add_to_account_cache(&mut self, addr: Address, data: Option, modified: bool) { + fn add_to_account_cache(&mut self, address: Address, data: Option, modified: bool) { self.local_cache.push(CacheQueueItem { - address: addr, + address, account: SyncAccount(data), - modified: modified, + modified, }) } @@ -484,10 +485,11 @@ unsafe impl Sync for SyncAccount {} #[cfg(test)] mod tests { - use ethereum_types::{H256, U256, Address}; + use ethereum_types::{Address, H256, U256}; use kvdb::DBTransaction; - use test_helpers::get_temp_state_db; + use account_state::{Account, Backend}; + use ethcore::test_helpers::get_temp_state_db; #[test] fn state_db_smoke() { From 0b1a692a0b5276ca6f3d72d72ef659bd35474c7d Mon Sep 17 00:00:00 2001 From: David Palm Date: Mon, 8 Jul 2019 11:53:49 +0200 Subject: [PATCH 65/87] Add new crate --- ethcore/private-tx/src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ethcore/private-tx/src/lib.rs b/ethcore/private-tx/src/lib.rs index 6cf02cd09e0..eeb400ec433 100644 --- a/ethcore/private-tx/src/lib.rs +++ b/ethcore/private-tx/src/lib.rs @@ -23,6 +23,7 @@ mod messages; mod error; mod log; +extern crate account_state; extern crate common_types as types; extern crate ethabi; extern crate ethcore; @@ -46,8 +47,8 @@ extern crate rlp; extern crate serde_derive; extern crate serde; extern crate serde_json; -extern crate account_state; extern crate rustc_hex; +extern crate state_db; extern crate trace; extern crate transaction_pool as txpool; extern crate url; From 90de4d22d70f6eb4b82eee056e587e88ec05546b Mon Sep 17 00:00:00 2001 From: David Palm Date: Mon, 8 Jul 2019 13:19:58 +0200 Subject: [PATCH 66/87] Move PreverifiedBlock and BlockError to types --- Cargo.lock | 2 + ethcore/src/block.rs | 10 +- ethcore/src/client/client.rs | 21 ++-- ethcore/src/error.rs | 123 +------------------ ethcore/src/verification/queue/kind.rs | 11 +- ethcore/src/verification/verification.rs | 24 +--- ethcore/types/Cargo.toml | 2 + ethcore/types/src/block.rs | 146 ++++++++++++++++++++++- ethcore/types/src/lib.rs | 3 + 9 files changed, 184 insertions(+), 158 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4341ca6ca72..b3b4e84de7a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -461,6 +461,8 @@ dependencies = [ name = "common-types" version = "0.1.0" dependencies = [ + "derive_more 0.14.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", diff --git a/ethcore/src/block.rs b/ethcore/src/block.rs index 72edc9fad51..1d4d4da9f0b 100644 --- a/ethcore/src/block.rs +++ b/ethcore/src/block.rs @@ -46,14 +46,16 @@ 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, + transaction::{SignedTransaction, Error as TransactionError}, + header::Header, + receipt::{Receipt, TransactionOutcome}, +}; use executive_state::ExecutiveState; /// Block that is ready for transactions to be added. diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index 507a9756f8f..ee8d5dc635f 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -33,14 +33,7 @@ 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, ExtendedHeader}}; use vm::{EnvInfo, LastHashes}; use hash_db::EMPTY_PREFIX; use block::{LockedBlock, Drain, ClosedBlock, OpenBlock, enact_verified, SealedBlock}; @@ -75,9 +68,21 @@ use executive_state; use state_db::StateDB; use trace::{self, TraceDB, ImportRequest as TraceImportRequest, LocalizedTrace, Database as TraceDatabase}; use transaction_ext::Transaction; +use types::{ + BlockNumber, + block::PreverifiedBlock, + transaction::{self, LocalizedTransaction, UnverifiedTransaction, SignedTransaction, Action}, + ancestry_action::AncestryAction, + encoded, + filter::Filter, + log_entry::LocalizedLogEntry, + receipt::{Receipt, LocalizedReceipt}, + header::{Header, ExtendedHeader} +}; + 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; diff --git a/ethcore/src/error.rs b/ethcore/src/error.rs index 8affe6a9757..01737f10bbc 100644 --- a/ethcore/src/error.rs +++ b/ethcore/src/error.rs @@ -16,139 +16,20 @@ //! General error types for use in ethcore. -use std::{fmt, error}; -use std::time::SystemTime; +use std::error; 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" - } -} +pub use types::block::BlockError; // TODO prolly dont want to re-export /// Block import Error #[derive(Debug, Display)] 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/verification.rs b/ethcore/src/verification/verification.rs index 664926374f4..f90d9693ff4 100644 --- a/ethcore/src/verification/verification.rs +++ b/ethcore/src/verification/verification.rs @@ -24,9 +24,7 @@ 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}; @@ -35,26 +33,16 @@ use blockchain::*; use call_contract::CallContract; use client::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)?; diff --git a/ethcore/types/Cargo.toml b/ethcore/types/Cargo.toml index 083205d717a..f12b55b6c5a 100644 --- a/ethcore/types/Cargo.toml +++ b/ethcore/types/Cargo.toml @@ -14,6 +14,8 @@ parity-bytes = "0.1" rlp = "0.4.0" rlp_derive = { path = "../../util/rlp-derive" } unexpected = { path = "../../util/unexpected" } +ethbloom = "0.6" +derive_more = "0.14.0" [dev-dependencies] rustc-hex= "1.0" diff --git a/ethcore/types/src/block.rs b/ethcore/types/src/block.rs index a423fb1ed1b..c075853d39a 100644 --- a/ethcore/types/src/block.rs +++ b/ethcore/types/src/block.rs @@ -22,20 +22,31 @@ //! 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. +use std::{ + fmt, + error, + time::SystemTime +}; use bytes::Bytes; +use derive_more::{Display, From}; +use parity_util_mem::MallocSizeOf; use header::Header; use rlp::{Rlp, RlpStream, Decodable, DecoderError}; -use transaction::UnverifiedTransaction; +use transaction::{UnverifiedTransaction, SignedTransaction}; +use unexpected::{Mismatch, OutOfBounds}; +use BlockNumber; +use ethbloom::Bloom; +use ethereum_types::{H256, U256, Address}; /// A block, encoded as it is on the block chain. #[derive(Default, Debug, Clone, PartialEq)] @@ -74,3 +85,132 @@ 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, +} + +/// 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" + } +} diff --git a/ethcore/types/src/lib.rs b/ethcore/types/src/lib.rs index 93145b2e9c5..49e246ea5d6 100644 --- a/ethcore/types/src/lib.rs +++ b/ethcore/types/src/lib.rs @@ -33,9 +33,12 @@ #![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 rlp; From 994424e000105bd433f7d9a4a1ec37933f825fc6 Mon Sep 17 00:00:00 2001 From: David Palm Date: Mon, 8 Jul 2019 14:14:33 +0200 Subject: [PATCH 67/87] Sort out the merge --- ethcore/account-state/src/account.rs | 6 +- ethcore/account-state/src/state.rs | 21 +- ethcore/factories/src/lib.rs | 1 - ethcore/src/executive_state.rs | 949 +-------------------------- 4 files changed, 41 insertions(+), 936 deletions(-) diff --git a/ethcore/account-state/src/account.rs b/ethcore/account-state/src/account.rs index 161de2c3921..c0bab08be89 100644 --- a/ethcore/account-state/src/account.rs +++ b/ethcore/account-state/src/account.rs @@ -27,7 +27,7 @@ use kvdb::DBValue; use log::{trace, warn}; use lru_cache::LruCache; use parity_bytes::{Bytes, ToPretty}; -use rlp::{DecoderError, encode, RlpStream}; +use rlp::{DecoderError, encode}; use trie_db::{Recorder, Trie}; use common_types::basic_account::BasicAccount; @@ -184,8 +184,8 @@ impl Account { /// NOTE: make sure you use `init_code` on this before `commit`ing. pub fn new_contract(balance: U256, nonce: U256, version: U256, original_storage_root: H256) -> Account { Account { - balance: balance, - nonce: nonce, + balance, + nonce, storage_root: KECCAK_NULL_RLP, storage_cache: Self::empty_storage_cache(), original_storage_cache: if original_storage_root == KECCAK_NULL_RLP { diff --git a/ethcore/account-state/src/state.rs b/ethcore/account-state/src/state.rs index 0ec5a8168aa..298e7f6ffd1 100644 --- a/ethcore/account-state/src/state.rs +++ b/ethcore/account-state/src/state.rs @@ -392,13 +392,13 @@ impl State { /// Create a new contract at address `contract`. If there is already an account at the address /// it will have its code reset, ready for `init_code()`. - pub fn new_contract(&mut self, contract: &Address, balance: U256, nonce_offset: U256) -> TrieResult<()> { + pub fn new_contract(&mut self, contract: &Address, balance: U256, nonce_offset: U256, version: U256) -> TrieResult<()> { let original_storage_root = self.original_storage_root(contract)?; let (nonce, overflow) = self.account_start_nonce.overflowing_add(nonce_offset); if overflow { return Err(Box::new(TrieError::DecoderError(H256::from(*contract), rlp::DecoderError::Custom("Nonce overflow".into())))); } - self.insert_cache(contract, AccountEntry::new_dirty(Some(Account::new_contract(balance, nonce, original_storage_root)))); + self.insert_cache(contract, AccountEntry::new_dirty(Some(Account::new_contract(balance, nonce, version, original_storage_root)))); Ok(()) } @@ -627,6 +627,12 @@ impl State { |a| a.as_ref().map(|a| a.code_hash())) } + /// Get an account's code version. + pub fn code_version(&self, a: &Address) -> TrieResult { + self.ensure_cached(a, RequireCache::None, true, + |a| a.as_ref().map(|a| *a.code_version()).unwrap_or(U256::zero())) + } + /// Get accounts' code size. pub fn code_size(&self, a: &Address) -> TrieResult> { self.ensure_cached(a, RequireCache::CodeSize, true, @@ -685,13 +691,13 @@ impl State { /// Initialise the code of account `a` so that it is `code`. /// NOTE: Account should have been created with `new_contract`. pub fn init_code(&mut self, a: &Address, code: Bytes) -> TrieResult<()> { - self.require_or_from(a, true, || Account::new_contract(0.into(), self.account_start_nonce, KECCAK_NULL_RLP), |_| {})?.init_code(code); + self.require_or_from(a, true, || Account::new_contract(0.into(), self.account_start_nonce, 0.into(),KECCAK_NULL_RLP), |_| {})?.init_code(code); Ok(()) } /// Reset the code of account `a` so that it is `code`. pub fn reset_code(&mut self, a: &Address, code: Bytes) -> TrieResult<()> { - self.require_or_from(a, true, || Account::new_contract(0.into(), self.account_start_nonce, KECCAK_NULL_RLP), |_| {})?.reset_code(code); + self.require_or_from(a, true, || Account::new_contract(0.into(), self.account_start_nonce, 0.into(), KECCAK_NULL_RLP), |_| {})?.reset_code(code); Ok(()) } @@ -890,11 +896,11 @@ impl State { }; // Storage must be fetched after ensure_cached to avoid borrow problem. - (*acc.balance(), *acc.nonce(), all_keys, acc.code().map(|x| x.to_vec())) + (*acc.balance(), *acc.nonce(), all_keys, acc.code().map(|x| x.to_vec()), *acc.code_version()) }) })?; - if let Some((balance, nonce, storage_keys, code)) = account { + if let Some((balance, nonce, storage_keys, code, version)) = account { let storage = storage_keys.into_iter().fold(Ok(BTreeMap::new()), |s: TrieResult<_>, key| { let mut s = s?; @@ -903,7 +909,7 @@ impl State { })?; m.insert(address, PodAccount { - balance, nonce, storage, code + balance, nonce, storage, code, version }); } @@ -1091,6 +1097,7 @@ impl State { nonce: self.account_start_nonce, code_hash: KECCAK_EMPTY, storage_root: KECCAK_NULL_RLP, + code_version: 0.into(), }); Ok((recorder.drain().into_iter().map(|r| r.data).collect(), account)) diff --git a/ethcore/factories/src/lib.rs b/ethcore/factories/src/lib.rs index 1162098713e..e1c9ed152af 100644 --- a/ethcore/factories/src/lib.rs +++ b/ethcore/factories/src/lib.rs @@ -17,7 +17,6 @@ use trie_db::TrieFactory; use ethtrie::RlpCodec; use account_db::Factory as AccountFactory; -use ethereum_types::U256; use evm::{Factory as EvmFactory, VMType}; use vm::{Exec, ActionParams, VersionedSchedule, Schedule}; use wasm::WasmInterpreter; diff --git a/ethcore/src/executive_state.rs b/ethcore/src/executive_state.rs index c21655a0867..162500d1795 100644 --- a/ethcore/src/executive_state.rs +++ b/ethcore/src/executive_state.rs @@ -164,457 +164,7 @@ pub trait ExecutiveState { V: trace::VMTracer; } -const SEC_TRIE_DB_UNWRAP_STR: &'static str = "A state can only be created with valid root. Creating a SecTrieDB with a valid root will not fail. \ - Therefore creating a SecTrieDB with this state's root will not fail."; - -impl State { - /// Creates new state with empty state root - /// Used for tests. - pub fn new(mut db: B, account_start_nonce: U256, factories: Factories) -> State { - let mut root = H256::zero(); - { - // init trie and reset root to null - let _ = factories.trie.create(db.as_hash_db_mut(), &mut root); - } - - State { - db: db, - root: root, - cache: RefCell::new(HashMap::new()), - checkpoints: RefCell::new(Vec::new()), - account_start_nonce: account_start_nonce, - factories: factories, - } - } - - /// Creates new state with existing state root - pub fn from_existing(db: B, root: H256, account_start_nonce: U256, factories: Factories) -> TrieResult> { - if !db.as_hash_db().contains(&root, hash_db::EMPTY_PREFIX) { - return Err(Box::new(TrieError::InvalidStateRoot(root))); - } - - let state = State { - db: db, - root: root, - cache: RefCell::new(HashMap::new()), - checkpoints: RefCell::new(Vec::new()), - account_start_nonce: account_start_nonce, - factories: factories - }; - - Ok(state) - } - - /// Get a VM factory that can execute on this state. - pub fn vm_factory(&self) -> VmFactory { - self.factories.vm.clone() - } - - /// Create a recoverable checkpoint of this state. Return the checkpoint index. - pub fn checkpoint(&mut self) -> usize { - let checkpoints = self.checkpoints.get_mut(); - let index = checkpoints.len(); - checkpoints.push(HashMap::new()); - index - } - - /// Merge last checkpoint with previous. - pub fn discard_checkpoint(&mut self) { - // merge with previous checkpoint - let last = self.checkpoints.get_mut().pop(); - if let Some(mut checkpoint) = last { - if let Some(ref mut prev) = self.checkpoints.get_mut().last_mut() { - if prev.is_empty() { - **prev = checkpoint; - } else { - for (k, v) in checkpoint.drain() { - prev.entry(k).or_insert(v); - } - } - } - } - } - - /// Revert to the last checkpoint and discard it. - pub fn revert_to_checkpoint(&mut self) { - if let Some(mut checkpoint) = self.checkpoints.get_mut().pop() { - for (k, v) in checkpoint.drain() { - match v { - Some(v) => { - match self.cache.get_mut().entry(k) { - Entry::Occupied(mut e) => { - // Merge checkpointed changes back into the main account - // storage preserving the cache. - e.get_mut().overwrite_with(v); - }, - Entry::Vacant(e) => { - e.insert(v); - } - } - }, - None => { - if let Entry::Occupied(e) = self.cache.get_mut().entry(k) { - if e.get().is_dirty() { - e.remove(); - } - } - } - } - } - } - } - - fn insert_cache(&self, address: &Address, account: AccountEntry) { - // Dirty account which is not in the cache means this is a new account. - // It goes directly into the checkpoint as there's nothing to rever to. - // - // In all other cases account is read as clean first, and after that made - // dirty in and added to the checkpoint with `note_cache`. - let is_dirty = account.is_dirty(); - let old_value = self.cache.borrow_mut().insert(*address, account); - if is_dirty { - if let Some(ref mut checkpoint) = self.checkpoints.borrow_mut().last_mut() { - checkpoint.entry(*address).or_insert(old_value); - } - } - } - - fn note_cache(&self, address: &Address) { - if let Some(ref mut checkpoint) = self.checkpoints.borrow_mut().last_mut() { - checkpoint.entry(*address) - .or_insert_with(|| self.cache.borrow().get(address).map(AccountEntry::clone_dirty)); - } - } - - /// Destroy the current object and return root and database. - pub fn drop(mut self) -> (H256, B) { - self.propagate_to_global_cache(); - (self.root, self.db) - } - - /// Destroy the current object and return single account data. - pub fn into_account(self, account: &Address) -> TrieResult<(Option>, HashMap)> { - // TODO: deconstruct without cloning. - let account = self.require(account, true)?; - Ok((account.code().clone(), account.storage_changes().clone())) - } - - /// Return reference to root - pub fn root(&self) -> &H256 { - &self.root - } - - /// Create a new contract at address `contract`. If there is already an account at the address - /// it will have its code reset, ready for `init_code()`. - pub fn new_contract(&mut self, contract: &Address, balance: U256, nonce_offset: U256, version: U256) -> TrieResult<()> { - let original_storage_root = self.original_storage_root(contract)?; - let (nonce, overflow) = self.account_start_nonce.overflowing_add(nonce_offset); - if overflow { - return Err(Box::new(TrieError::DecoderError(H256::from(*contract), - rlp::DecoderError::Custom("Nonce overflow".into())))); - } - self.insert_cache(contract, AccountEntry::new_dirty(Some(Account::new_contract(balance, nonce, version, original_storage_root)))); - Ok(()) - } - - /// Remove an existing account. - pub fn kill_account(&mut self, account: &Address) { - self.insert_cache(account, AccountEntry::new_dirty(None)); - } - - /// Determine whether an account exists. - pub fn exists(&self, a: &Address) -> TrieResult { - // Bloom filter does not contain empty accounts, so it is important here to - // check if account exists in the database directly before EIP-161 is in effect. - self.ensure_cached(a, RequireCache::None, false, |a| a.is_some()) - } - - /// Determine whether an account exists and if not empty. - pub fn exists_and_not_null(&self, a: &Address) -> TrieResult { - self.ensure_cached(a, RequireCache::None, false, |a| a.map_or(false, |a| !a.is_null())) - } - - /// Determine whether an account exists and has code or non-zero nonce. - pub fn exists_and_has_code_or_nonce(&self, a: &Address) -> TrieResult { - self.ensure_cached(a, RequireCache::CodeSize, false, - |a| a.map_or(false, |a| a.code_hash() != KECCAK_EMPTY || *a.nonce() != self.account_start_nonce)) - } - - /// Get the balance of account `a`. - pub fn balance(&self, a: &Address) -> TrieResult { - self.ensure_cached(a, RequireCache::None, true, - |a| a.as_ref().map_or(U256::zero(), |account| *account.balance())) - } - - /// Get the nonce of account `a`. - pub fn nonce(&self, a: &Address) -> TrieResult { - self.ensure_cached(a, RequireCache::None, true, - |a| a.as_ref().map_or(self.account_start_nonce, |account| *account.nonce())) - } - - /// Whether the base storage root of an account remains unchanged. - pub fn is_base_storage_root_unchanged(&self, a: &Address) -> TrieResult { - Ok(self.ensure_cached(a, RequireCache::None, true, - |a| a.as_ref().map(|account| account.is_base_storage_root_unchanged()))? - .unwrap_or(true)) - } - - /// Get the storage root of account `a`. - pub fn storage_root(&self, a: &Address) -> TrieResult> { - self.ensure_cached(a, RequireCache::None, true, - |a| a.as_ref().and_then(|account| account.storage_root())) - } - - /// Get the original storage root since last commit of account `a`. - pub fn original_storage_root(&self, a: &Address) -> TrieResult { - Ok(self.ensure_cached(a, RequireCache::None, true, - |a| a.as_ref().map(|account| account.original_storage_root()))? - .unwrap_or(KECCAK_NULL_RLP)) - } - - /// Get the value of storage at a specific checkpoint. - pub fn checkpoint_storage_at(&self, start_checkpoint_index: usize, address: &Address, key: &H256) -> TrieResult> { - #[must_use] - enum ReturnKind { - /// Use original storage at value at this address. - OriginalAt, - /// The checkpoint storage value is the same as the checkpoint storage value at the next checkpoint. - SameAsNext, - } - - let kind = { - let checkpoints = self.checkpoints.borrow(); - - if start_checkpoint_index >= checkpoints.len() { - // The checkpoint was not found. Return None. - return Ok(None); - } - - let mut kind = None; - - for checkpoint in checkpoints.iter().skip(start_checkpoint_index) { - match checkpoint.get(address) { - // The account exists at this checkpoint. - Some(Some(AccountEntry { account: Some(ref account), .. })) => { - if let Some(value) = account.cached_storage_at(key) { - return Ok(Some(value)); - } else { - // This account has checkpoint entry, but the key is not in the entry's cache. We can use - // original_storage_at if current account's original storage root is the same as checkpoint - // account's original storage root. Otherwise, the account must be a newly created contract. - if account.base_storage_root() == self.original_storage_root(address)? { - kind = Some(ReturnKind::OriginalAt); - break - } else { - // If account base storage root is different from the original storage root since last - // commit, then it can only be created from a new contract, where the base storage root - // would always be empty. Note that this branch is actually never called, because - // `cached_storage_at` handled this case. - warn!(target: "state", "Trying to get an account's cached storage value, but base storage root does not equal to original storage root! Assuming the value is empty."); - return Ok(Some(H256::zero())); - } - } - }, - // The account didn't exist at that point. Return empty value. - Some(Some(AccountEntry { account: None, .. })) => return Ok(Some(H256::zero())), - // The value was not cached at that checkpoint, meaning it was not modified at all. - Some(None) => { - kind = Some(ReturnKind::OriginalAt); - break - }, - // This key does not have a checkpoint entry. - None => { - kind = Some(ReturnKind::SameAsNext); - }, - } - } - - kind.expect("start_checkpoint_index is checked to be below checkpoints_len; for loop above must have been executed at least once; it will either early return, or set the kind value to Some; qed") - }; - - match kind { - ReturnKind::SameAsNext => { - // If we reached here, all previous SameAsNext failed to early return. It means that the value we want - // to fetch is the same as current. - Ok(Some(self.storage_at(address, key)?)) - }, - ReturnKind::OriginalAt => Ok(Some(self.original_storage_at(address, key)?)), - } - } - - fn storage_at_inner( - &self, address: &Address, key: &H256, f_cached_at: FCachedStorageAt, f_at: FStorageAt, - ) -> TrieResult where - FCachedStorageAt: Fn(&Account, &H256) -> Option, - FStorageAt: Fn(&Account, &dyn HashDB, &H256) -> TrieResult - { - // Storage key search and update works like this: - // 1. If there's an entry for the account in the local cache check for the key and return it if found. - // 2. If there's an entry for the account in the global cache check for the key or load it into that account. - // 3. If account is missing in the global cache load it into the local cache and cache the key there. - - { - // check local cache first without updating - let local_cache = self.cache.borrow_mut(); - let mut local_account = None; - if let Some(maybe_acc) = local_cache.get(address) { - match maybe_acc.account { - Some(ref account) => { - if let Some(value) = f_cached_at(account, key) { - return Ok(value); - } else { - local_account = Some(maybe_acc); - } - }, - _ => return Ok(H256::zero()), - } - } - // check the global cache and and cache storage key there if found, - let trie_res = self.db.get_cached(address, |acc| match acc { - None => Ok(H256::zero()), - Some(a) => { - let account_db = self.factories.accountdb.readonly(self.db.as_hash_db(), a.address_hash(address)); - f_at(a, account_db.as_hash_db(), key) - } - }); - - if let Some(res) = trie_res { - return res; - } - - // otherwise cache the account localy and cache storage key there. - if let Some(ref mut acc) = local_account { - if let Some(ref account) = acc.account { - let account_db = self.factories.accountdb.readonly(self.db.as_hash_db(), account.address_hash(address)); - return f_at(account, account_db.as_hash_db(), key) - } else { - return Ok(H256::zero()) - } - } - } - - // check if the account could exist before any requests to trie - if self.db.is_known_null(address) { return Ok(H256::zero()) } - - // account is not found in the global cache, get from the DB and insert into local - let db = &self.db.as_hash_db(); - let db = self.factories.trie.readonly(db, &self.root).expect(SEC_TRIE_DB_UNWRAP_STR); - let from_rlp = |b: &[u8]| Account::from_rlp(b).expect("decoding db value failed"); - let maybe_acc = db.get_with(address.as_bytes(), from_rlp)?; - let r = maybe_acc.as_ref().map_or(Ok(H256::zero()), |a| { - let account_db = self.factories.accountdb.readonly(self.db.as_hash_db(), a.address_hash(address)); - f_at(a, account_db.as_hash_db(), key) - }); - self.insert_cache(address, AccountEntry::new_clean(maybe_acc)); - r - } - - /// Mutate storage of account `address` so that it is `value` for `key`. - pub fn storage_at(&self, address: &Address, key: &H256) -> TrieResult { - self.storage_at_inner( - address, - key, - |account, key| { account.cached_storage_at(key) }, - |account, db, key| { account.storage_at(db, key) }, - ) - } - - /// Get the value of storage after last state commitment. - pub fn original_storage_at(&self, address: &Address, key: &H256) -> TrieResult { - self.storage_at_inner( - address, - key, - |account, key| { account.cached_original_storage_at(key) }, - |account, db, key| { account.original_storage_at(db, key) }, - ) - } - - /// Get accounts' code. - pub fn code(&self, a: &Address) -> TrieResult>> { - self.ensure_cached(a, RequireCache::Code, true, - |a| a.as_ref().map_or(None, |a| a.code().clone())) - } - - /// Get an account's code hash. - pub fn code_hash(&self, a: &Address) -> TrieResult> { - self.ensure_cached(a, RequireCache::None, true, - |a| a.as_ref().map(|a| a.code_hash())) - } - - /// Get an account's code version. - pub fn code_version(&self, a: &Address) -> TrieResult { - self.ensure_cached(a, RequireCache::None, true, - |a| a.as_ref().map(|a| *a.code_version()).unwrap_or(U256::zero())) - } - - /// Get accounts' code size. - pub fn code_size(&self, a: &Address) -> TrieResult> { - self.ensure_cached(a, RequireCache::CodeSize, true, - |a| a.as_ref().and_then(|a| a.code_size())) - } - - /// Add `incr` to the balance of account `a`. - pub fn add_balance(&mut self, a: &Address, incr: &U256, cleanup_mode: CleanupMode) -> TrieResult<()> { - trace!(target: "state", "add_balance({}, {}): {}", a, incr, self.balance(a)?); - let is_value_transfer = !incr.is_zero(); - if is_value_transfer || (cleanup_mode == CleanupMode::ForceCreate && !self.exists(a)?) { - self.require(a, false)?.add_balance(incr); - } else if let CleanupMode::TrackTouched(set) = cleanup_mode { - if self.exists(a)? { - set.insert(*a); - self.touch(a)?; - } - } - Ok(()) - } - - /// Subtract `decr` from the balance of account `a`. - pub fn sub_balance(&mut self, a: &Address, decr: &U256, cleanup_mode: &mut CleanupMode) -> TrieResult<()> { - trace!(target: "state", "sub_balance({}, {}): {}", a, decr, self.balance(a)?); - if !decr.is_zero() || !self.exists(a)? { - self.require(a, false)?.sub_balance(decr); - } - if let CleanupMode::TrackTouched(ref mut set) = *cleanup_mode { - set.insert(*a); - } - Ok(()) - } - - /// Subtracts `by` from the balance of `from` and adds it to that of `to`. - pub fn transfer_balance(&mut self, from: &Address, to: &Address, by: &U256, mut cleanup_mode: CleanupMode) -> TrieResult<()> { - self.sub_balance(from, by, &mut cleanup_mode)?; - self.add_balance(to, by, cleanup_mode)?; - Ok(()) - } - - /// Increment the nonce of account `a` by 1. - pub fn inc_nonce(&mut self, a: &Address) -> TrieResult<()> { - self.require(a, false).map(|mut x| x.inc_nonce()) - } - - /// Mutate storage of account `a` so that it is `value` for `key`. - pub fn set_storage(&mut self, a: &Address, key: H256, value: H256) -> TrieResult<()> { - trace!(target: "state", "set_storage({}:{:x} to {:x})", a, key, value); - if self.storage_at(a, &key)? != value { - self.require(a, false)?.set_storage(key, value) - } - - Ok(()) - } - - /// Initialise the code of account `a` so that it is `code`. - /// NOTE: Account should have been created with `new_contract`. - pub fn init_code(&mut self, a: &Address, code: Bytes) -> TrieResult<()> { - self.require_or_from(a, true, || Account::new_contract(0.into(), self.account_start_nonce, 0.into(), KECCAK_NULL_RLP), |_| {})?.init_code(code); - Ok(()) - } - - /// Reset the code of account `a` so that it is `code`. - pub fn reset_code(&mut self, a: &Address, code: Bytes) -> TrieResult<()> { - self.require_or_from(a, true, || Account::new_contract(0.into(), self.account_start_nonce, 0.into(), KECCAK_NULL_RLP), |_| {})?.reset_code(code); - Ok(()) - } - +impl ExecutiveState for State { /// Execute a given transaction, producing a receipt and an optional trace. /// This will change the state accordingly. fn apply( @@ -678,482 +228,31 @@ impl State { vm_trace: e.vm_trace, }) } - - // Execute a given transaction without committing changes. - // - // `virt` signals that we are executing outside of a block set and restrictions like - // gas limits and gas costs should be lifted. - fn execute(&mut self, env_info: &EnvInfo, machine: &Machine, t: &SignedTransaction, options: TransactOptions, virt: bool) - -> Result, ExecutionError> where T: trace::Tracer, V: trace::VMTracer, - { - let schedule = machine.schedule(env_info.number); - let mut e = Executive::new(self, env_info, machine, &schedule); - - match virt { - true => e.transact_virtual(t, options), - false => e.transact(t, options), - } - } - - fn touch(&mut self, a: &Address) -> TrieResult<()> { - self.require(a, false)?; - Ok(()) - } - - /// Commits our cached account changes into the trie. - pub fn commit(&mut self) -> Result<(), Error> { - assert!(self.checkpoints.borrow().is_empty()); - // first, commit the sub trees. - let mut accounts = self.cache.borrow_mut(); - for (address, ref mut a) in accounts.iter_mut().filter(|&(_, ref a)| a.is_dirty()) { - if let Some(ref mut account) = a.account { - let addr_hash = account.address_hash(address); - { - let mut account_db = self.factories.accountdb.create(self.db.as_hash_db_mut(), addr_hash); - account.commit_storage(&self.factories.trie, account_db.as_hash_db_mut())?; - account.commit_code(account_db.as_hash_db_mut()); - } - if !account.is_empty() { - self.db.note_non_null_account(address); - } - } - } - - { - let mut trie = self.factories.trie.from_existing(self.db.as_hash_db_mut(), &mut self.root)?; - for (address, ref mut a) in accounts.iter_mut().filter(|&(_, ref a)| a.is_dirty()) { - a.state = AccountState::Committed; - match a.account { - Some(ref mut account) => { - trie.insert(address.as_bytes(), &account.rlp())?; - }, - None => { - trie.remove(address.as_bytes())?; - }, - }; - } - } - - Ok(()) - } - - /// Propagate local cache into shared canonical state cache. - fn propagate_to_global_cache(&mut self) { - let mut addresses = self.cache.borrow_mut(); - trace!("Committing cache {:?} entries", addresses.len()); - for (address, a) in addresses.drain().filter(|&(_, ref a)| a.state == AccountState::Committed || a.state == AccountState::CleanFresh) { - self.db.add_to_account_cache(address, a.account, a.state == AccountState::Committed); - } - } - - /// Clear state cache - pub fn clear(&mut self) { - assert!(self.checkpoints.borrow().is_empty()); - self.cache.borrow_mut().clear(); - } - - /// Remove any touched empty or dust accounts. - pub fn kill_garbage(&mut self, touched: &HashSet
, remove_empty_touched: bool, min_balance: &Option, kill_contracts: bool) -> TrieResult<()> { - let to_kill: HashSet<_> = { - self.cache.borrow().iter().filter_map(|(address, ref entry)| - if touched.contains(address) && // Check all touched accounts - ((remove_empty_touched && entry.exists_and_is_null()) // Remove all empty touched accounts. - || min_balance.map_or(false, |ref balance| entry.account.as_ref().map_or(false, |account| - (account.is_basic() || kill_contracts) // Remove all basic and optionally contract accounts where balance has been decreased. - && account.balance() < balance && entry.old_balance.as_ref().map_or(false, |b| account.balance() < b)))) { - - Some(address.clone()) - } else { None }).collect() - }; - for address in to_kill { - self.kill_account(&address); - } - Ok(()) - } - - /// Populate the state from `accounts`. - /// Used for tests. - pub fn populate_from(&mut self, accounts: PodState) { - assert!(self.checkpoints.borrow().is_empty()); - for (add, acc) in accounts.drain().into_iter() { - self.cache.borrow_mut().insert(add, AccountEntry::new_dirty(Some(Account::from_pod(acc)))); - } - } - - /// Populate a PodAccount map from this state. - fn to_pod_cache(&self) -> PodState { - assert!(self.checkpoints.borrow().is_empty()); - PodState::from(self.cache.borrow().iter().fold(BTreeMap::new(), |mut m, (add, opt)| { - if let Some(ref acc) = opt.account { - m.insert(*add, acc.to_pod()); - } - m - })) - } - - #[cfg(feature="to-pod-full")] - /// Populate a PodAccount map from this state. - /// Warning this is not for real time use. - /// Use of this method requires FatDB mode to be able - /// to iterate on accounts. - pub fn to_pod_full(&self) -> Result { - - assert!(self.checkpoints.borrow().is_empty()); - assert!(self.factories.trie.is_fat()); - - let mut result = BTreeMap::new(); - - let db = &self.db.as_hash_db(); - let trie = self.factories.trie.readonly(db, &self.root)?; - - // put trie in cache - for item in trie.iter()? { - if let Ok((addr, _dbval)) = item { - let address = Address::from_slice(&addr); - let _ = self.require(&address, true); - } - } - - // Resolve missing part - for (add, opt) in self.cache.borrow().iter() { - if let Some(ref acc) = opt.account { - let pod_account = self.account_to_pod_account(acc, add)?; - result.insert(add.clone(), pod_account); - } - } - - Ok(PodState::from(result)) - } - - /// Create a PodAccount from an account. - /// Differs from existing method by including all storage - /// values of the account to the PodAccount. - /// This function is only intended for use in small tests or with fresh accounts. - /// It requires FatDB. - #[cfg(feature="to-pod-full")] - fn account_to_pod_account(&self, account: &Account, address: &Address) -> Result { - use ethereum_types::BigEndianHash; - - let mut pod_storage = BTreeMap::new(); - let addr_hash = account.address_hash(address); - let accountdb = self.factories.accountdb.readonly(self.db.as_hash_db(), addr_hash); - let root = account.base_storage_root(); - - let accountdb = &accountdb.as_hash_db(); - let trie = self.factories.trie.readonly(accountdb, &root)?; - for o_kv in trie.iter()? { - if let Ok((key, val)) = o_kv { - pod_storage.insert( - H256::from_slice(&key[..]), - BigEndianHash::from_uint( - &rlp::decode::(&val[..]).expect("Decoded from trie which was encoded from the same type; qed") - ), - ); - } - } - - let mut pod_account = account.to_pod(); - // cached one first - pod_storage.append(&mut pod_account.storage); - pod_account.storage = pod_storage; - Ok(pod_account) - } - - /// Populate a PodAccount map from this state, with another state as the account and storage query. - fn to_pod_diff(&mut self, query: &State) -> TrieResult { - assert!(self.checkpoints.borrow().is_empty()); - - // Merge PodAccount::to_pod for cache of self and `query`. - let all_addresses = self.cache.borrow().keys().cloned() - .chain(query.cache.borrow().keys().cloned()) - .collect::>(); - - Ok(PodState::from(all_addresses.into_iter().fold(Ok(BTreeMap::new()), |m: TrieResult<_>, address| { - let mut m = m?; - - let account = self.ensure_cached(&address, RequireCache::Code, true, |acc| { - acc.map(|acc| { - // Merge all modified storage keys. - let all_keys = { - let self_keys = acc.storage_changes().keys().cloned() - .collect::>(); - - if let Some(ref query_storage) = query.cache.borrow().get(&address) - .and_then(|opt| { - Some(opt.account.as_ref()?.storage_changes().keys().cloned() - .collect::>()) - }) - { - self_keys.union(&query_storage).cloned().collect::>() - } else { - self_keys.into_iter().collect::>() - } - }; - - // Storage must be fetched after ensure_cached to avoid borrow problem. - (*acc.balance(), *acc.nonce(), all_keys, acc.code().map(|x| x.to_vec()), *acc.code_version()) - }) - })?; - - if let Some((balance, nonce, storage_keys, code, version)) = account { - let storage = storage_keys.into_iter().fold(Ok(BTreeMap::new()), |s: TrieResult<_>, key| { - let mut s = s?; - - s.insert(key, self.storage_at(&address, &key)?); - Ok(s) - })?; - - m.insert(address, PodAccount { - balance, nonce, storage, code, version - }); - } - - Ok(m) - })?)) - } - - /// Returns a `StateDiff` describing the difference from `orig` to `self`. - /// Consumes self. - pub fn diff_from(&self, mut orig: State) -> TrieResult { - let pod_state_post = self.to_pod_cache(); - let pod_state_pre = orig.to_pod_diff(self)?; - Ok(pod_state::diff_pod(&pod_state_pre, &pod_state_post)) - } - - /// Load required account data from the databases. Returns whether the cache succeeds. - #[must_use] - fn update_account_cache(require: RequireCache, account: &mut Account, state_db: &B, db: &dyn HashDB) -> bool { - if let RequireCache::None = require { - return true; - } - - if account.is_cached() { - return true; - } - - // if there's already code in the global cache, always cache it localy - let hash = account.code_hash(); - match state_db.get_cached_code(&hash) { - Some(code) => { - account.cache_given_code(code); - true - }, - None => match require { - RequireCache::None => true, - RequireCache::Code => { - if let Some(code) = account.cache_code(db) { - // propagate code loaded from the database to - // the global code cache. - state_db.cache_code(hash, code); - true - } else { - false - } - }, - RequireCache::CodeSize => { - account.cache_code_size(db) - } - } - } - } - - /// Check caches for required data - /// First searches for account in the local, then the shared cache. - /// Populates local cache if nothing found. - fn ensure_cached(&self, a: &Address, require: RequireCache, check_null: bool, f: F) -> TrieResult - where F: Fn(Option<&Account>) -> U { - // check local cache first - if let Some(ref mut maybe_acc) = self.cache.borrow_mut().get_mut(a) { - if let Some(ref mut account) = maybe_acc.account { - let accountdb = self.factories.accountdb.readonly(self.db.as_hash_db(), account.address_hash(a)); - if Self::update_account_cache(require, account, &self.db, accountdb.as_hash_db()) { - return Ok(f(Some(account))); - } else { - return Err(Box::new(TrieError::IncompleteDatabase(H256::from(*a)))); - } - } - return Ok(f(None)); - } - // check global cache - let result = self.db.get_cached(a, |mut acc| { - if let Some(ref mut account) = acc { - let accountdb = self.factories.accountdb.readonly(self.db.as_hash_db(), account.address_hash(a)); - if !Self::update_account_cache(require, account, &self.db, accountdb.as_hash_db()) { - return Err(Box::new(TrieError::IncompleteDatabase(H256::from(*a)))); - } - } - Ok(f(acc.map(|a| &*a))) - }); - match result { - Some(r) => Ok(r?), - None => { - // first check if it is not in database for sure - if check_null && self.db.is_known_null(a) { return Ok(f(None)); } - - // not found in the global cache, get from the DB and insert into local - let db = &self.db.as_hash_db(); - let db = self.factories.trie.readonly(db, &self.root)?; - let from_rlp = |b: &[u8]| Account::from_rlp(b).expect("decoding db value failed"); - let mut maybe_acc = db.get_with(a.as_bytes(), from_rlp)?; - if let Some(ref mut account) = maybe_acc.as_mut() { - let accountdb = self.factories.accountdb.readonly(self.db.as_hash_db(), account.address_hash(a)); - if !Self::update_account_cache(require, account, &self.db, accountdb.as_hash_db()) { - return Err(Box::new(TrieError::IncompleteDatabase(H256::from(*a)))); - } - } - let r = f(maybe_acc.as_ref()); - self.insert_cache(a, AccountEntry::new_clean(maybe_acc)); - Ok(r) - } - } - } - - /// Pull account `a` in our cache from the trie DB. `require_code` requires that the code be cached, too. - fn require<'a>(&'a self, a: &Address, require_code: bool) -> TrieResult> { - self.require_or_from(a, require_code, || Account::new_basic(0u8.into(), self.account_start_nonce), |_| {}) - } - - /// Pull account `a` in our cache from the trie DB. `require_code` requires that the code be cached, too. - /// If it doesn't exist, make account equal the evaluation of `default`. - fn require_or_from<'a, F, G>(&'a self, a: &Address, require_code: bool, default: F, not_default: G) -> TrieResult> - where F: FnOnce() -> Account, G: FnOnce(&mut Account), - { - let contains_key = self.cache.borrow().contains_key(a); - if !contains_key { - match self.db.get_cached_account(a) { - Some(acc) => self.insert_cache(a, AccountEntry::new_clean_cached(acc)), - None => { - let maybe_acc = if !self.db.is_known_null(a) { - let db = &self.db.as_hash_db(); - let db = self.factories.trie.readonly(db, &self.root)?; - let from_rlp = |b:&[u8]| { Account::from_rlp(b).expect("decoding db value failed") }; - AccountEntry::new_clean(db.get_with(a.as_bytes(), from_rlp)?) - } else { - AccountEntry::new_clean(None) - }; - self.insert_cache(a, maybe_acc); - } - } - } - self.note_cache(a); - - // at this point the entry is guaranteed to be in the cache. - let mut account = RefMut::map(self.cache.borrow_mut(), |c| { - let entry = c.get_mut(a).expect("entry known to exist in the cache; qed"); - - match &mut entry.account { - &mut Some(ref mut acc) => not_default(acc), - slot => *slot = Some(default()), - } - - // set the dirty flag after changing account data. - entry.state = AccountState::Dirty; - entry.account.as_mut().expect("Required account must always exist; qed") - }); - - if require_code { - let addr_hash = account.address_hash(a); - let accountdb = self.factories.accountdb.readonly(self.db.as_hash_db(), addr_hash); - - if !Self::update_account_cache(RequireCache::Code, &mut account, &self.db, accountdb.as_hash_db()) { - return Err(Box::new(TrieError::IncompleteDatabase(H256::from(*a)))) - } - } - - Ok(account) - } - - /// Replace account code and storage. Creates account if it does not exist. - pub fn patch_account(&self, a: &Address, code: Arc, storage: HashMap) -> TrieResult<()> { - Ok(self.require(a, false)?.reset_code_and_storage(code, storage)) - } } -// State proof implementations; useful for light client protocols. -impl State { - /// Prove an account's existence or nonexistence in the state trie. - /// Returns a merkle proof of the account's trie node omitted or an encountered trie error. - /// If the account doesn't exist in the trie, prove that and return defaults. - /// Requires a secure trie to be used for accurate results. - /// `account_key` == keccak(address) - pub fn prove_account(&self, account_key: H256) -> TrieResult<(Vec, BasicAccount)> { - let mut recorder = Recorder::new(); - let db = &self.db.as_hash_db(); - let trie = TrieDB::new(db, &self.root)?; - let maybe_account: Option = { - let panicky_decoder = |bytes: &[u8]| { - ::rlp::decode(bytes).unwrap_or_else(|_| panic!("prove_account, could not query trie for account key={}", &account_key)) - }; - let query = (&mut recorder, panicky_decoder); - trie.get_with(account_key.as_bytes(), query)? - }; - let account = maybe_account.unwrap_or_else(|| BasicAccount { - balance: 0.into(), - nonce: self.account_start_nonce, - code_hash: KECCAK_EMPTY, - storage_root: KECCAK_NULL_RLP, - code_version: 0.into(), - }); - - Ok((recorder.drain().into_iter().map(|r| r.data).collect(), account)) - } - - /// Prove an account's storage key's existence or nonexistence in the state. - /// Returns a merkle proof of the account's storage trie. - /// Requires a secure trie to be used for correctness. - /// `account_key` == keccak(address) - /// `storage_key` == keccak(key) - pub fn prove_storage(&self, account_key: H256, storage_key: H256) -> TrieResult<(Vec, H256)> { - // TODO: probably could look into cache somehow but it's keyed by - // address, not keccak(address). - let db = &self.db.as_hash_db(); - let trie = TrieDB::new(db, &self.root)?; - let from_rlp = |b: &[u8]| Account::from_rlp(b).expect("decoding db value failed"); - let acc = match trie.get_with(account_key.as_bytes(), from_rlp)? { - Some(acc) => acc, - None => return Ok((Vec::new(), H256::zero())), - }; - - let account_db = self.factories.accountdb.readonly(self.db.as_hash_db(), account_key); - acc.prove_storage(account_db.as_hash_db(), storage_key) - } -} - -impl fmt::Debug for State { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{:?}", self.cache.borrow()) - } -} - -impl State { - /// Get a reference to the underlying state DB. - pub fn db(&self) -> &StateDB { - &self.db - } -} - -// TODO: cloning for `State` shouldn't be possible in general; Remove this and use -// checkpoints where possible. -impl Clone for State { - fn clone(&self) -> State { - let cache = { - let mut cache: HashMap = HashMap::new(); - for (key, val) in self.cache.borrow().iter() { - if let Some(entry) = val.clone_if_dirty() { - cache.insert(key.clone(), entry); - } - } - cache - }; - - State { - db: self.db.boxed_clone(), - root: self.root.clone(), - cache: RefCell::new(cache), - checkpoints: RefCell::new(Vec::new()), - account_start_nonce: self.account_start_nonce.clone(), - factories: self.factories.clone(), - } +// Execute a given transaction without committing changes. +// +// `virt` signals that we are executing outside of a block set and restrictions like +// gas limits and gas costs should be lifted. +fn execute( + state: &mut State, + env_info: &EnvInfo, + machine: &Machine, + t: &SignedTransaction, + options: TransactOptions, + virt: bool +) -> Result, ExecutionError> + where + B: Backend, + T: trace::Tracer, + V: trace::VMTracer, +{ + let schedule = machine.schedule(env_info.number); + let mut e = Executive::new(state, env_info, machine, &schedule); + + match virt { + true => e.transact_virtual(t, options), + false => e.transact(t, options), } } From 4fe6f5563d99cd0eb3223a9bc6a976517a6ab568 Mon Sep 17 00:00:00 2001 From: David Palm Date: Mon, 8 Jul 2019 14:27:38 +0200 Subject: [PATCH 68/87] Add missing `license` meta data keys --- ethcore/account-db/Cargo.toml | 5 +++-- ethcore/account-state/Cargo.toml | 5 +++-- ethcore/factories/Cargo.toml | 5 +++-- ethcore/pod/Cargo.toml | 5 +++-- ethcore/state-db/Cargo.toml | 5 +++-- ethcore/trace/Cargo.toml | 3 ++- 6 files changed, 17 insertions(+), 11 deletions(-) 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 d2ce8ebfa9e..7e383b5ac0f 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/factories/Cargo.toml b/ethcore/factories/Cargo.toml index 77c078caa52..a9ca0fd1f85 100644 --- a/ethcore/factories/Cargo.toml +++ b/ethcore/factories/Cargo.toml @@ -1,8 +1,9 @@ [package] -description = "Collection of factories" name = "factories" -version = "0.1.0" +description = "Collection of factories" authors = ["Parity Technologies "] +license = "GPL-3.0" +version = "0.1.0" edition = "2018" [dependencies] 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/state-db/Cargo.toml b/ethcore/state-db/Cargo.toml index c3e444ec96f..547c398dbc2 100644 --- a/ethcore/state-db/Cargo.toml +++ b/ethcore/state-db/Cargo.toml @@ -1,8 +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] From a3f08ca55bddb888db1b6e9af54b14c921f253bf Mon Sep 17 00:00:00 2001 From: David Palm Date: Mon, 8 Jul 2019 21:33:55 +0200 Subject: [PATCH 69/87] wip --- ethcore/verification/Cargo.toml | 19 ++++ ethcore/verification/src/error.rs | 32 ++++++ ethcore/verification/src/lib.rs | 63 ++++++++++++ ethcore/verification/src/verification.rs | 119 +++++++++++++++++++++++ ethcore/verification/src/verifier.rs | 45 +++++++++ 5 files changed, 278 insertions(+) create mode 100644 ethcore/verification/Cargo.toml create mode 100644 ethcore/verification/src/error.rs create mode 100644 ethcore/verification/src/lib.rs create mode 100644 ethcore/verification/src/verification.rs create mode 100644 ethcore/verification/src/verifier.rs diff --git a/ethcore/verification/Cargo.toml b/ethcore/verification/Cargo.toml new file mode 100644 index 00000000000..ae0da9eaf68 --- /dev/null +++ b/ethcore/verification/Cargo.toml @@ -0,0 +1,19 @@ +[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 = "0.7.0" +unexpected = { path = "../../util/unexpected" } +ethcore-blockchain = { path = "../blockchain" } +time-utils = { path = "../../util/time-utils" } diff --git a/ethcore/verification/src/error.rs b/ethcore/verification/src/error.rs new file mode 100644 index 00000000000..125595b6b95 --- /dev/null +++ b/ethcore/verification/src/error.rs @@ -0,0 +1,32 @@ +// 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}; + +#[derive(Debug, Display, From)] +pub enum Error { + Todo +} + +impl std::error::Error for Error {} + +impl From> for Error where Error: From { + fn from(err: Box) -> Self { + Error::from(*err) + } +} diff --git a/ethcore/verification/src/lib.rs b/ethcore/verification/src/lib.rs new file mode 100644 index 00000000000..062938ce5a7 --- /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. + +mod verification; +mod verifier; +//pub mod queue; +//mod canon_verifier; +//mod noop_verifier; +mod error; + +//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, QueueInfo}; + +use call_contract::CallContract; +use client_traits::BlockInfo; + +/// 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/verification.rs b/ethcore/verification/src/verification.rs new file mode 100644 index 00000000000..916077edc62 --- /dev/null +++ b/ethcore/verification/src/verification.rs @@ -0,0 +1,119 @@ +// 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; +use std::time::{Duration, SystemTime, UNIX_EPOCH}; + +use keccak_hash::keccak; +use rlp::Rlp; +use triehash::ordered_trie_root; +use unexpected::{Mismatch, OutOfBounds}; + +use ethcore_blockchain::BlockProvider; +use call_contract::CallContract; +use client_traits::BlockInfo; + +//use engines::{Engine, MAX_UNCLE_AGE}; +use crate::error::Error; +use common_types::{ + BlockNumber, + header::Header, + block::{BlockError, PreverifiedBlock}, +}; +//use verification::queue::kind::blocks::Unverified; + +use time_utils::CheckedSystemTime; + +/// 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)?; + 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 Engine, 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::, Error>>()?; + + Ok(PreverifiedBlock { + header, + transactions, + uncles: block.uncles, + bytes: block.bytes, + }) +} + +/// 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, +} diff --git a/ethcore/verification/src/verifier.rs b/ethcore/verification/src/verifier.rs new file mode 100644 index 00000000000..c7cd5ca9bf5 --- /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; +//use engines::Engine; +use crate::{ + error::Error +}; +use common_types::header::Header; +//use super::verification; + +/// 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 Engine, + do_full: Option> + ) -> Result<(), Error>; + + /// Do a final verification check for an enacted header vs its expected counterpart. + fn verify_block_final(&self, expected: &Header, got: &Header) -> Result<(), Error>; + /// Verify a block, inspecting external state. + fn verify_block_external(&self, header: &Header, engine: &dyn Engine) -> Result<(), Error>; +} From eb57f24247bdb934fcf3f13171951635101281ef Mon Sep 17 00:00:00 2001 From: David Palm Date: Mon, 8 Jul 2019 21:34:58 +0200 Subject: [PATCH 70/87] wip client-traits --- ethcore/client-traits/Cargo.toml | 9 ++++++ ethcore/client-traits/src/lib.rs | 53 ++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+) create mode 100644 ethcore/client-traits/Cargo.toml create mode 100644 ethcore/client-traits/src/lib.rs diff --git a/ethcore/client-traits/Cargo.toml b/ethcore/client-traits/Cargo.toml new file mode 100644 index 00000000000..2a836e8ad20 --- /dev/null +++ b/ethcore/client-traits/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "client-traits" +version = "0.1.0" +edition = "2018" + + +[dependencies] +ethereum-types = "0.6" +common-types = { path = "../types" } diff --git a/ethcore/client-traits/src/lib.rs b/ethcore/client-traits/src/lib.rs new file mode 100644 index 00000000000..a060e2fac76 --- /dev/null +++ b/ethcore/client-traits/src/lib.rs @@ -0,0 +1,53 @@ +use ethereum_types::{H256, Address}; +use common_types::{ + ids::BlockId, + header::Header, + encoded, + transaction::{self, UnverifiedTransaction}, +}; +/// 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; +} + +/// 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 { + + /// 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(()) } + + /// 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(()) } + + /// 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(()) } + + /// 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(()) } + + /// 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> {} +} From cdbab8c84f52720c50125e7899726d2bd4d089d4 Mon Sep 17 00:00:00 2001 From: David Palm Date: Tue, 9 Jul 2019 10:12:43 +0200 Subject: [PATCH 71/87] merge conflict --- ethcore/factories/Cargo.toml | 16 ---------------- 1 file changed, 16 deletions(-) delete mode 100644 ethcore/factories/Cargo.toml diff --git a/ethcore/factories/Cargo.toml b/ethcore/factories/Cargo.toml deleted file mode 100644 index a9ca0fd1f85..00000000000 --- a/ethcore/factories/Cargo.toml +++ /dev/null @@ -1,16 +0,0 @@ -[package] -name = "factories" -description = "Collection of factories" -authors = ["Parity Technologies "] -license = "GPL-3.0" -version = "0.1.0" -edition = "2018" - -[dependencies] -trie-db = "0.12.4" -ethtrie = { package = "patricia-trie-ethereum", path = "../../util/patricia-trie-ethereum" } -account-db = { path = "../account-db" } -evm = { path = "../evm" } -vm = { path = "../vm" } -wasm = { path = "../wasm" } -keccak-hasher = { path = "../../util/keccak-hasher" } From 62dcb7777347f67c3ac24b7613c7b964785af9e9 Mon Sep 17 00:00:00 2001 From: David Palm Date: Tue, 9 Jul 2019 13:49:26 +0200 Subject: [PATCH 72/87] verification crate type checks --- Cargo.lock | 61 +- Cargo.toml | 3 + ethcore/client-traits/Cargo.toml | 3 +- ethcore/client-traits/src/error.rs | 34 + ethcore/client-traits/src/lib.rs | 39 +- ethcore/src/machine.rs | 54 +- ethcore/src/spec/spec.rs | 203 ++--- ethcore/types/src/block.rs | 15 + ethcore/types/src/blockchain_info.rs | 2 +- ethcore/types/src/client_io_message.rs | 25 + ethcore/types/src/engines/mod.rs | 30 + ethcore/types/src/engines/params.rs | 102 +++ ethcore/types/src/lib.rs | 3 +- ethcore/verification/Cargo.toml | 10 +- ethcore/verification/src/canon_verifier.rs | 52 ++ ethcore/verification/src/error.rs | 13 +- ethcore/verification/src/lib.rs | 16 +- ethcore/verification/src/noop_verifier.rs | 49 ++ ethcore/verification/src/queue/kind.rs | 228 +++++ ethcore/verification/src/queue/mod.rs | 952 +++++++++++++++++++++ ethcore/verification/src/verification.rs | 112 ++- ethcore/verification/src/verifier.rs | 11 +- 22 files changed, 1842 insertions(+), 175 deletions(-) create mode 100644 ethcore/client-traits/src/error.rs create mode 100644 ethcore/types/src/client_io_message.rs create mode 100644 ethcore/types/src/engines/params.rs create mode 100644 ethcore/verification/src/canon_verifier.rs create mode 100644 ethcore/verification/src/noop_verifier.rs create mode 100644 ethcore/verification/src/queue/kind.rs create mode 100644 ethcore/verification/src/queue/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 17d571b83aa..524f378056f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -429,6 +429,15 @@ 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)", +] + [[package]] name = "cloudabi" version = "0.0.3" @@ -522,7 +531,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)", ] @@ -898,7 +907,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)", @@ -1029,7 +1038,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)", @@ -1434,7 +1443,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)", @@ -1629,7 +1638,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]] @@ -2084,7 +2093,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)", @@ -2181,7 +2190,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)", @@ -2564,7 +2573,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)", @@ -2725,7 +2734,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)", @@ -3524,7 +3533,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]] @@ -4141,7 +4150,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]] @@ -4264,7 +4273,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)", @@ -4312,7 +4321,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)", ] @@ -4639,6 +4648,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" @@ -5051,7 +5084,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/client-traits/Cargo.toml b/ethcore/client-traits/Cargo.toml index 2a836e8ad20..60eb027c887 100644 --- a/ethcore/client-traits/Cargo.toml +++ b/ethcore/client-traits/Cargo.toml @@ -5,5 +5,6 @@ edition = "2018" [dependencies] -ethereum-types = "0.6" common-types = { path = "../types" } +derive_more = "0.15.0" +ethereum-types = "0.6" diff --git a/ethcore/client-traits/src/error.rs b/ethcore/client-traits/src/error.rs new file mode 100644 index 00000000000..5dc05f62175 --- /dev/null +++ b/ethcore/client-traits/src/error.rs @@ -0,0 +1,34 @@ +// 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}; + +#[derive(Debug, Display, From)] +pub enum Error { + /// todo: what errors do I need? + Todo +} + +impl std::error::Error for Error {} + +// todo needed? +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 index a060e2fac76..5bf578a88ba 100644 --- a/ethcore/client-traits/src/lib.rs +++ b/ethcore/client-traits/src/lib.rs @@ -1,10 +1,19 @@ -use ethereum_types::{H256, Address}; +use ethereum_types::{H256, U256, Address}; use common_types::{ ids::BlockId, header::Header, encoded, - transaction::{self, UnverifiedTransaction}, + transaction::{self, UnverifiedTransaction, SignedTransaction}, + engines::{ + EthashExtensions, + params::CommonParams + }, }; + +pub mod error; + +use error::Error; + /// Provides various information on a block by it's ID pub trait BlockInfo { /// Get raw block header data by block id. @@ -23,6 +32,20 @@ pub trait BlockInfo { /// 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 } + + /// 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; + + /// 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. @@ -49,5 +72,15 @@ pub trait VerifyingEngine: 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> {} + 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; } diff --git a/ethcore/src/machine.rs b/ethcore/src/machine.rs index eb0ffd384cf..2fa6ff328e9 100644 --- a/ethcore/src/machine.rs +++ b/ethcore/src/machine.rs @@ -22,9 +22,12 @@ 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, + transaction::{self, SYSTEM_ADDRESS, UNSIGNED_SENDER, UnverifiedTransaction, SignedTransaction} + header::Header, + engines::EthashExtensions, +}; use vm::{CallType, ActionParams, ActionValue, ParamsType}; use vm::{EnvInfo, Schedule, CreateContractAddress}; @@ -42,29 +45,30 @@ 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]); +// todo remove /// 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(), - } - } -} +//#[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; diff --git a/ethcore/src/spec/spec.rs b/ethcore/src/spec/spec.rs index e31e1e25d16..1405e5272f0 100644 --- a/ethcore/src/spec/spec.rs +++ b/ethcore/src/spec/spec.rs @@ -28,9 +28,12 @@ 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 types::{ + BlockNumber, + encoded, + header::Header, + engines::params::CommonParams, +}; use vm::{EnvInfo, CallType, ActionValue, ActionParams, ParamsType, VersionedSchedule}; use builtin::Builtin; @@ -57,103 +60,103 @@ 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, -} +///// 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. diff --git a/ethcore/types/src/block.rs b/ethcore/types/src/block.rs index c075853d39a..513dc48d2d5 100644 --- a/ethcore/types/src/block.rs +++ b/ethcore/types/src/block.rs @@ -214,3 +214,18 @@ impl error::Error for BlockError { "Block error" } } + +// todo: hook up in ethcore +/// 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, +} 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..b33348f7fa5 --- /dev/null +++ b/ethcore/types/src/client_io_message.rs @@ -0,0 +1,25 @@ + +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..2e0f1aa0536 100644 --- a/ethcore/types/src/engines/mod.rs +++ b/ethcore/types/src/engines/mod.rs @@ -16,7 +16,13 @@ //! Engine-specific types. +use ethereum_types::Address; +use ethjson; + +use crate::BlockNumber; + pub mod epoch; +pub mod params; /// Fork choice. #[derive(Debug, PartialEq, Eq)] @@ -26,3 +32,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..7e6fada61a5 --- /dev/null +++ b/ethcore/types/src/engines/params.rs @@ -0,0 +1,102 @@ +use ethereum_types::{Address, U256, H256}; +use bytes::Bytes; + +use BlockNumber; + +/// 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))] // 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, +} diff --git a/ethcore/types/src/lib.rs b/ethcore/types/src/lib.rs index ebf2a61de00..3283ce7aacf 100644 --- a/ethcore/types/src/lib.rs +++ b/ethcore/types/src/lib.rs @@ -37,7 +37,6 @@ 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; @@ -47,7 +46,6 @@ 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)] @@ -63,6 +61,7 @@ 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 filter; diff --git a/ethcore/verification/Cargo.toml b/ethcore/verification/Cargo.toml index ae0da9eaf68..be15d87ccd5 100644 --- a/ethcore/verification/Cargo.toml +++ b/ethcore/verification/Cargo.toml @@ -13,7 +13,15 @@ client-traits = { path = "../client-traits" } derive_more = "0.15.0" keccak-hash = "0.2.0" rlp = "0.4" -triehash = "0.7.0" +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..4c4318a8c4e --- /dev/null +++ b/ethcore/verification/src/canon_verifier.rs @@ -0,0 +1,52 @@ +// 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 call_contract::CallContract; +use client_traits::{BlockInfo, VerifyingEngine}; +use crate::{ + error::Error, + Verifier, + verification::{FullFamilyParams}, +}; +use common_types::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) + Ok(()) + } + + fn verify_block_final(&self, expected: &Header, got: &Header) -> Result<(), Error> { +// verification::verify_block_final(expected, got) + Ok(()) + } + + fn verify_block_external(&self, header: &Header, engine: &dyn VerifyingEngine) -> Result<(), Error> { +// engine.verify_block_external(header) + Ok(()) + } +} diff --git a/ethcore/verification/src/error.rs b/ethcore/verification/src/error.rs index 125595b6b95..aca56a11441 100644 --- a/ethcore/verification/src/error.rs +++ b/ethcore/verification/src/error.rs @@ -18,9 +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 { - Todo + 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 index 062938ce5a7..df3dcf9d1c0 100644 --- a/ethcore/verification/src/lib.rs +++ b/ethcore/verification/src/lib.rs @@ -16,18 +16,20 @@ //! 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 mod queue; +mod canon_verifier; +mod noop_verifier; mod error; -//pub use self::verification::*; +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, QueueInfo}; +pub use self::canon_verifier::CanonVerifier; +pub use self::noop_verifier::NoopVerifier; +pub use self::queue::{BlockQueue, Config as QueueConfig, VerificationQueue, QueueInfo}; use call_contract::CallContract; use client_traits::BlockInfo; diff --git a/ethcore/verification/src/noop_verifier.rs b/ethcore/verification/src/noop_verifier.rs new file mode 100644 index 00000000000..e47151a5330 --- /dev/null +++ b/ethcore/verification/src/noop_verifier.rs @@ -0,0 +1,49 @@ +// 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::{ + error::Error, + Verifier, verification::FullFamilyParams +}; +use common_types::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<(), Error> { + Ok(()) + } + + fn verify_block_final(&self, _expected: &Header, _got: &Header) -> Result<(), Error> { + Ok(()) + } + + fn verify_block_external(&self, _header: &Header, _engine: &dyn VerifyingEngine) -> Result<(), Error> { + Ok(()) + } +} diff --git a/ethcore/verification/src/queue/kind.rs b/ethcore/verification/src/queue/kind.rs new file mode 100644 index 00000000000..7cbf53f0e28 --- /dev/null +++ b/ethcore/verification/src/queue/kind.rs @@ -0,0 +1,228 @@ +// 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}; + +use crate::error::Error; +pub use self::{ + blocks::Blocks, + headers::Headers, +}; + +/// 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::{ + error::Error, + verification::{verify_block_basic, verify_block_unordered} + }; + use common_types::{ + block::{PreverifiedBlock, BlockError}, + 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::header::Header; + use client_traits::VerifyingEngine; + use ethereum_types::{H256, U256}; + + use crate::{ + error::Error, + 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..493feaa30cf --- /dev/null +++ b/ethcore/verification/src/queue/mod.rs @@ -0,0 +1,952 @@ +// 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 error::{BlockError, ImportError, Error}; +//use engines::Engine; +//use client::ClientIoMessage; +use len_caching_lock::LenCachingMutex; +use num_cpus; +use crate::{ + error::Error, + queue::kind::{BlockLike, Kind}, +}; + +pub use common_types::{ + verification_queue_info::VerificationQueueInfo as QueueInfo, + block_status::BlockStatus, + block::{BlockError, ImportError}, + client_io_message::ClientIoMessage, +}; +use client_traits::VerifyingEngine; +use log::{debug, trace}; + +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: 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 error::*; + use bytes::Bytes; + use types::view; + 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 index 916077edc62..7691f9f3d8b 100644 --- a/ethcore/verification/src/verification.rs +++ b/ethcore/verification/src/verification.rs @@ -21,31 +21,45 @@ //! 2. Signatures verification done in the queue. //! 3. Final verification against the blockchain done before enactment. -use std::collections::HashSet; use std::time::{Duration, SystemTime, UNIX_EPOCH}; use keccak_hash::keccak; use rlp::Rlp; -use triehash::ordered_trie_root; +use triehash_ethereum::ordered_trie_root; use unexpected::{Mismatch, OutOfBounds}; use ethcore_blockchain::BlockProvider; use call_contract::CallContract; -use client_traits::BlockInfo; +use client_traits::{BlockInfo, VerifyingEngine}; //use engines::{Engine, MAX_UNCLE_AGE}; -use crate::error::Error; +use crate::{ + error::Error, + queue::kind::blocks::Unverified, +}; use common_types::{ BlockNumber, header::Header, block::{BlockError, PreverifiedBlock}, }; -//use verification::queue::kind::blocks::Unverified; 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 Engine, check_seal: bool) -> Result<(), Error> { +pub fn verify_block_basic(block: &Unverified, engine: &dyn VerifyingEngine, check_seal: bool) -> Result<(), Error> { verify_header_params(&block.header, engine, true, check_seal)?; verify_block_integrity(block)?; @@ -70,7 +84,7 @@ pub fn verify_block_basic(block: &Unverified, engine: &dyn Engine, check_seal: b /// 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 Engine, check_seal: bool) -> Result { +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)?; @@ -106,14 +120,82 @@ pub fn verify_block_unordered(block: Unverified, engine: &dyn Engine, check_seal }) } -/// Parameters for full verification of block family -pub struct FullFamilyParams<'a, C: BlockInfo + CallContract + 'a> { - /// Pre-verified block - pub block: &'a PreverifiedBlock, +/// Check basic header parameters. +pub fn verify_header_params(header: &Header, engine: &dyn VerifyingEngine, is_full: bool, check_seal: bool) -> Result<(), Error> { + 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() } + ))); + } + } - /// Block provider to use during verification - pub block_provider: &'a dyn BlockProvider, + 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() }))); + } - /// Engine client to use during verification - pub client: &'a C, + 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(()) +} + +/// Verify block data against header: transactions root and uncles hash. +fn verify_block_integrity(block: &Unverified) -> Result<(), Error> { + 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 index c7cd5ca9bf5..3ef9252f0d2 100644 --- a/ethcore/verification/src/verifier.rs +++ b/ethcore/verification/src/verifier.rs @@ -17,10 +17,11 @@ //! A generic verifier trait. use call_contract::CallContract; -use client_traits::BlockInfo; +use client_traits::{BlockInfo, VerifyingEngine}; //use engines::Engine; use crate::{ - error::Error + error::Error, + verification::FullFamilyParams, }; use common_types::header::Header; //use super::verification; @@ -34,12 +35,12 @@ pub trait Verifier: Send + Sync &self, header: &Header, parent: &Header, - engine: &dyn Engine, - do_full: Option> + engine: &dyn VerifyingEngine, + do_full: Option> ) -> Result<(), Error>; /// Do a final verification check for an enacted header vs its expected counterpart. fn verify_block_final(&self, expected: &Header, got: &Header) -> Result<(), Error>; /// Verify a block, inspecting external state. - fn verify_block_external(&self, header: &Header, engine: &dyn Engine) -> Result<(), Error>; + fn verify_block_external(&self, header: &Header, engine: &dyn VerifyingEngine) -> Result<(), Error>; } From 8d86b8548af6f2b890549f61cff1cb59fdf185e5 Mon Sep 17 00:00:00 2001 From: David Palm Date: Tue, 9 Jul 2019 14:40:20 +0200 Subject: [PATCH 73/87] Move impls for CommonParams to common_types Fix misc stuff in ethcore --- Cargo.lock | 4 +- ethcore/src/client/client.rs | 2 +- ethcore/src/engines/mod.rs | 16 +- ethcore/src/machine.rs | 8 +- ethcore/src/spec/mod.rs | 2 +- ethcore/src/spec/spec.rs | 294 +------------------------ ethcore/src/tx_filter.rs | 8 +- ethcore/types/Cargo.toml | 4 +- ethcore/types/src/client_io_message.rs | 17 ++ ethcore/types/src/engines/mod.rs | 4 + ethcore/types/src/engines/params.rs | 214 ++++++++++++++++++ ethcore/types/src/lib.rs | 1 + 12 files changed, 267 insertions(+), 307 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 524f378056f..61bf4c567ad 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -470,7 +470,7 @@ dependencies = [ name = "common-types" version = "0.1.0" dependencies = [ - "derive_more 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)", + "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", @@ -481,7 +481,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]] diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index f007798634b..1e8f044a466 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -77,7 +77,7 @@ use types::{ filter::Filter, log_entry::LocalizedLogEntry, receipt::{Receipt, LocalizedReceipt}, - header::{Header, ExtendedHeader} + header::Header, }; use verification::queue::kind::BlockLike; diff --git a/ethcore/src/engines/mod.rs b/ethcore/src/engines/mod.rs index 9cad9bdce02..1226c7d91ea 100644 --- a/ethcore/src/engines/mod.rs +++ b/ethcore/src/engines/mod.rs @@ -44,11 +44,18 @@ 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}, + engines::{ +// ForkChoice, // TODO [ToDr] Remove re-export (#10130) +// epoch::{self, Transition as EpochTransition}, + params::CommonParams + }, + transaction::{self, UnverifiedTransaction, SignedTransaction}, +}; use snapshot::SnapshotComponents; -use spec::CommonParams; -use types::transaction::{self, UnverifiedTransaction, SignedTransaction}; use client::EngineClient; use ethkey::{Signature}; @@ -56,7 +63,6 @@ use machine::{self, Machine, AuxiliaryRequest, AuxiliaryData}; use ethereum_types::{H64, H256, U256, Address}; use unexpected::{Mismatch, OutOfBounds}; use bytes::Bytes; -use types::ancestry_action::AncestryAction; use block::ExecutedBlock; /// Default EIP-210 contract code. diff --git a/ethcore/src/machine.rs b/ethcore/src/machine.rs index 2fa6ff328e9..d1f7b909c43 100644 --- a/ethcore/src/machine.rs +++ b/ethcore/src/machine.rs @@ -24,9 +24,12 @@ use ethereum_types::{U256, H256, Address}; use rlp::Rlp; use types::{ BlockNumber, - transaction::{self, SYSTEM_ADDRESS, UNSIGNED_SENDER, UnverifiedTransaction, SignedTransaction} + transaction::{self, SYSTEM_ADDRESS, UNSIGNED_SENDER, UnverifiedTransaction, SignedTransaction}, header::Header, - engines::EthashExtensions, + engines::{ + EthashExtensions, + params::CommonParams, + }, }; use vm::{CallType, ActionParams, ActionValue, ParamsType}; use vm::{EnvInfo, Schedule, CreateContractAddress}; @@ -37,7 +40,6 @@ use call_contract::CallContract; use client::BlockInfo; use error::Error; use executive::Executive; -use spec::CommonParams; use account_state::{CleanupMode, Substate}; use trace::{NoopTracer, NoopVMTracer}; use tx_filter::TransactionFilter; 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 1405e5272f0..08601d3d355 100644 --- a/ethcore/src/spec/spec.rs +++ b/ethcore/src/spec/spec.rs @@ -34,12 +34,12 @@ use types::{ header::Header, engines::params::CommonParams, }; -use vm::{EnvInfo, CallType, ActionValue, ActionParams, ParamsType, VersionedSchedule}; +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; @@ -53,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/tx_filter.rs b/ethcore/src/tx_filter.rs index 2b9a7c94198..9149eee4265 100644 --- a/ethcore/src/tx_filter.rs +++ b/ethcore/src/tx_filter.rs @@ -23,9 +23,11 @@ use ethabi::FunctionOutputDecoder; use call_contract::CallContract; use client::{BlockInfo, BlockId}; 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/types/Cargo.toml b/ethcore/types/Cargo.toml index f12b55b6c5a..4f0f607c211 100644 --- a/ethcore/types/Cargo.toml +++ b/ethcore/types/Cargo.toml @@ -15,7 +15,9 @@ rlp = "0.4.0" rlp_derive = { path = "../../util/rlp-derive" } unexpected = { path = "../../util/unexpected" } ethbloom = "0.6" -derive_more = "0.14.0" +derive_more = "0.15.0" +rustc-hex = "2.0.1" # todo: only needed for CommonParams. Remove. +vm = { path = "../vm"} # todo: this is irritating [dev-dependencies] rustc-hex= "1.0" diff --git a/ethcore/types/src/client_io_message.rs b/ethcore/types/src/client_io_message.rs index b33348f7fa5..f403621b36f 100644 --- a/ethcore/types/src/client_io_message.rs +++ b/ethcore/types/src/client_io_message.rs @@ -1,3 +1,20 @@ +// 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; diff --git a/ethcore/types/src/engines/mod.rs b/ethcore/types/src/engines/mod.rs index 2e0f1aa0536..73fc3916d7d 100644 --- a/ethcore/types/src/engines/mod.rs +++ b/ethcore/types/src/engines/mod.rs @@ -24,6 +24,10 @@ use crate::BlockNumber; pub mod epoch; pub mod params; +/// Default EIP-210 contract code. +/// As defined in https://github.com/ethereum/EIPs/pull/210 +pub const DEFAULT_BLOCKHASH_CONTRACT: &'static str = "73fffffffffffffffffffffffffffffffffffffffe33141561006a5760014303600035610100820755610100810715156100455760003561010061010083050761010001555b6201000081071515610064576000356101006201000083050761020001555b5061013e565b4360003512151561008457600060405260206040f361013d565b61010060003543031315156100a857610100600035075460605260206060f361013c565b6101006000350715156100c55762010000600035430313156100c8565b60005b156100ea576101006101006000350507610100015460805260206080f361013b565b620100006000350715156101095763010000006000354303131561010c565b60005b1561012f57610100620100006000350507610200015460a052602060a0f361013a565b600060c052602060c0f35b5b5b5b5b"; + /// Fork choice. #[derive(Debug, PartialEq, Eq)] pub enum ForkChoice { diff --git a/ethcore/types/src/engines/params.rs b/ethcore/types/src/engines/params.rs index 7e6fada61a5..d06b38c4aae 100644 --- a/ethcore/types/src/engines/params.rs +++ b/ethcore/types/src/engines/params.rs @@ -1,7 +1,31 @@ +// 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, @@ -100,3 +124,193 @@ pub struct CommonParams { /// 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/lib.rs b/ethcore/types/src/lib.rs index 3283ce7aacf..6fe1d2d8473 100644 --- a/ethcore/types/src/lib.rs +++ b/ethcore/types/src/lib.rs @@ -41,6 +41,7 @@ extern crate derive_more; extern crate keccak_hash as hash; extern crate parity_bytes as bytes; extern crate rlp; +extern crate rustc_hex; // todo: used only to `.from_hex()` a const which is dumb. Remove. extern crate unexpected; #[macro_use] From e7c32fcf9d778d5e72df928c270c05df3c1f265e Mon Sep 17 00:00:00 2001 From: David Palm Date: Tue, 9 Jul 2019 14:58:16 +0200 Subject: [PATCH 74/87] Fix tests --- ethcore/src/engines/authority_round/mod.rs | 9 ++++++--- ethcore/src/verification/verification.rs | 12 ++++++++---- ethcore/types/src/engines/params.rs | 4 ++-- 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/ethcore/src/engines/authority_round/mod.rs b/ethcore/src/engines/authority_round/mod.rs index 360ec0443ec..fd097379480 100644 --- a/ethcore/src/engines/authority_round/mod.rs +++ b/ethcore/src/engines/authority_round/mod.rs @@ -1615,7 +1615,11 @@ 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, + transaction::{Action, Transaction}, + }; use rlp::encode; use block::*; use test_helpers::{ @@ -1623,7 +1627,6 @@ mod tests { TestNotify }; use spec::Spec; - use types::transaction::{Action, Transaction}; use engines::{Seal, Engine, EngineError}; use engines::validator_set::{TestSet, SimpleList}; use error::Error; @@ -1653,7 +1656,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/verification/verification.rs b/ethcore/src/verification/verification.rs index f90d9693ff4..ddeae25c2c9 100644 --- a/ethcore/src/verification/verification.rs +++ b/ethcore/src/verification/verification.rs @@ -367,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/types/src/engines/params.rs b/ethcore/types/src/engines/params.rs index d06b38c4aae..919393824c0 100644 --- a/ethcore/types/src/engines/params.rs +++ b/ethcore/types/src/engines/params.rs @@ -33,8 +33,8 @@ const MAX_TRANSACTION_SIZE: usize = 300 * 1024; /// /// 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))] // todo: this will not work across crate boundaries I think +#[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, From e02251698d91525f95bfdc47d598788891012204 Mon Sep 17 00:00:00 2001 From: David Palm Date: Tue, 9 Jul 2019 21:00:56 +0200 Subject: [PATCH 75/87] Implement VerifyingEngine for all engines except Ethash Temporarily sort out error handling Move more types to common_types --- Cargo.lock | 2 + ethcore/Cargo.toml | 2 + ethcore/client-traits/Cargo.toml | 1 + ethcore/client-traits/src/error.rs | 27 ++- ethcore/client-traits/src/lib.rs | 2 +- ethcore/src/client/client.rs | 14 +- ethcore/src/client/mod.rs | 2 +- ethcore/src/client/test_client.rs | 3 +- ethcore/src/client/traits.rs | 16 +- ethcore/src/engines/authority_round/mod.rs | 12 +- ethcore/src/engines/basic_authority.rs | 32 ++- ethcore/src/engines/block_reward.rs | 11 +- ethcore/src/engines/clique/block_state.rs | 8 +- ethcore/src/engines/clique/mod.rs | 205 +++++++++++++++++- ethcore/src/engines/clique/tests.rs | 1 + ethcore/src/engines/clique/util.rs | 6 +- ethcore/src/engines/instant_seal.rs | 23 +- ethcore/src/engines/mod.rs | 90 +------- ethcore/src/engines/null_engine.rs | 27 ++- ethcore/src/engines/validator_set/contract.rs | 3 +- ethcore/src/engines/validator_set/multi.rs | 3 +- .../engines/validator_set/safe_contract.rs | 20 +- ethcore/src/error.rs | 24 +- ethcore/src/lib.rs | 1 + ethcore/src/machine.rs | 5 +- ethcore/src/miner/pool_client.rs | 3 +- ethcore/src/snapshot/service.rs | 3 +- ethcore/src/snapshot/tests/service.rs | 3 +- ethcore/src/snapshot/watcher.rs | 3 +- ethcore/src/tests/client.rs | 3 +- ethcore/src/tx_filter.rs | 3 +- ethcore/src/verification/canon_verifier.rs | 2 +- ethcore/src/verification/mod.rs | 2 +- ethcore/src/verification/noop_verifier.rs | 2 +- ethcore/src/verification/queue/mod.rs | 7 +- ethcore/src/verification/verification.rs | 2 +- ethcore/src/verification/verifier.rs | 2 +- ethcore/types/src/block.rs | 5 +- ethcore/types/src/engines/mod.rs | 95 +++++++- 39 files changed, 483 insertions(+), 192 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 61bf4c567ad..447394ab0ec 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -436,6 +436,7 @@ 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]] @@ -871,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)", diff --git a/ethcore/Cargo.toml b/ethcore/Cargo.toml index bfe330bcacf..ccb51e61897 100644 --- a/ethcore/Cargo.toml +++ b/ethcore/Cargo.toml @@ -74,6 +74,8 @@ using_queue = { path = "../miner/using-queue" } vm = { path = "vm" } rand_xorshift = "0.1.1" +client-traits = { path = "client-traits" } + [dev-dependencies] blooms-db = { path = "../util/blooms-db" } criterion = "0.2" diff --git a/ethcore/client-traits/Cargo.toml b/ethcore/client-traits/Cargo.toml index 60eb027c887..28d79547669 100644 --- a/ethcore/client-traits/Cargo.toml +++ b/ethcore/client-traits/Cargo.toml @@ -8,3 +8,4 @@ edition = "2018" 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 index 5dc05f62175..d99d1c28dde 100644 --- a/ethcore/client-traits/src/error.rs +++ b/ethcore/client-traits/src/error.rs @@ -17,16 +17,39 @@ //! 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? - Todo + 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 {} -// todo needed? +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 index 5bf578a88ba..71a4046fff0 100644 --- a/ethcore/client-traits/src/lib.rs +++ b/ethcore/client-traits/src/lib.rs @@ -39,7 +39,7 @@ pub trait VerifyingEngine: Sync + Send { 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; + fn maximum_extra_data_size(&self) -> usize { self.params().maximum_extra_data_size } /// Get the general parameters of the chain. fn params(&self) -> &CommonParams; diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index 1e8f044a466..52c90a2c4fa 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -39,7 +39,7 @@ 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, @@ -52,10 +52,11 @@ use client::{ IoClient, BadBlocks, }; use client::bad_blocks; -use engines::{MAX_UNCLE_AGE, Engine, EpochTransition, ForkChoice, EngineError, SealingState}; +use client_traits::BlockInfo; +use engines::{MAX_UNCLE_AGE, Engine, EpochTransition, ForkChoice, SealingState}; use engines::epoch::PendingTransition; use error::{ - ImportError, ExecutionError, CallError, BlockError, + ExecutionError, CallError, BlockError, Error as EthcoreError, EthcoreResult, }; use executive::{Executive, Executed, TransactOptions, contract_address}; @@ -69,11 +70,12 @@ use state_db::StateDB; use trace::{self, TraceDB, ImportRequest as TraceImportRequest, LocalizedTrace, Database as TraceDatabase}; use transaction_ext::Transaction; use types::{ - BlockNumber, - block::PreverifiedBlock, - transaction::{self, LocalizedTransaction, UnverifiedTransaction, SignedTransaction, Action}, ancestry_action::AncestryAction, + BlockNumber, + block::{ImportError, PreverifiedBlock}, encoded, + engines::EngineError, + transaction::{self, LocalizedTransaction, UnverifiedTransaction, SignedTransaction, Action}, filter::Filter, log_entry::LocalizedLogEntry, receipt::{Receipt, LocalizedReceipt}, 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..4bf47c92a52 100644 --- a/ethcore/src/client/test_client.rs +++ b/ethcore/src/client/test_client.rs @@ -52,13 +52,14 @@ 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 executed::CallError; diff --git a/ethcore/src/client/traits.rs b/ethcore/src/client/traits.rs index 50fe75a699b..422bc3d5cdd 100644 --- a/ethcore/src/client/traits.rs +++ b/ethcore/src/client/traits.rs @@ -43,6 +43,7 @@ use vm::LastHashes; use block::{OpenBlock, SealedBlock, ClosedBlock}; use client::Mode; +use client_traits::BlockInfo; use engines::Engine; use error::{Error, EthcoreResult}; use executed::CallError; @@ -112,21 +113,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 fd097379480..d3962f50d35 100644 --- a/ethcore/src/engines/authority_round/mod.rs +++ b/ethcore/src/engines/authority_round/mod.rs @@ -26,7 +26,7 @@ use std::time::{UNIX_EPOCH, Duration}; use block::*; use client::EngineClient; -use engines::{Engine, Seal, SealingState, EngineError, ConstructedVerifier}; +use engines::{Engine, Seal, SealingState, ConstructedVerifier}; use engines::block_reward; use engines::block_reward::{BlockRewardContract, RewardKind}; use error::{Error, BlockError}; @@ -43,9 +43,13 @@ 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::EngineError, + +}; use unexpected::{Mismatch, OutOfBounds}; mod finality; diff --git a/ethcore/src/engines/basic_authority.rs b/ethcore/src/engines/basic_authority.rs index 8232c0ff260..60440fc0f58 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::{EngineError, params::CommonParams}, + transaction::{self, UnverifiedTransaction, SignedTransaction}, +}; + use super::validator_set::{ValidatorSet, SimpleList, new_validator_set}; /// `BasicAuthority` params. @@ -90,6 +96,20 @@ impl BasicAuthority { } } +impl VerifyingEngine for BasicAuthority { + 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 BasicAuthority { fn name(&self) -> &str { "BasicAuthority" } @@ -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..660598aa81c 100644 --- a/ethcore/src/engines/block_reward.rs +++ b/ethcore/src/engines/block_reward.rs @@ -26,7 +26,10 @@ use hash::keccak; use error::Error; use machine::Machine; use trace; -use types::BlockNumber; +use types::{ + BlockNumber, + engines::EngineError, +}; use super::{SystemOrCodeCall, SystemOrCodeCallKind}; use trace::{Tracer, ExecutiveTracer, Tracing}; use block::ExecutedBlock; @@ -120,7 +123,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 +134,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 +142,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..4c30627ba90 100644 --- a/ethcore/src/engines/clique/block_state.rs +++ b/ethcore/src/engines/clique/block_state.rs @@ -18,15 +18,17 @@ 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, + engines::EngineError, +}; use unexpected::Mismatch; /// Type that keeps track of the state for a given vote diff --git a/ethcore/src/engines/clique/mod.rs b/ethcore/src/engines/clique/mod.rs index adfcf88f01d..5a6584a0184 100644 --- a/ethcore/src/engines/clique/mod.rs +++ b/ethcore/src/engines/clique/mod.rs @@ -68,8 +68,9 @@ use std::time::{Instant, Duration, SystemTime, UNIX_EPOCH}; use block::ExecutedBlock; use client::{BlockId, EngineClient}; +use client_traits::{VerifyingEngine, error::Error as ClientError}; use engines::clique::util::{extract_signers, recover_creator}; -use engines::{Engine, EngineError, Seal, SealingState}; +use engines::{Engine, Seal, SealingState}; use error::{BlockError, Error}; use ethereum_types::{Address, H64, H160, H256, U256}; use ethkey::Signature; @@ -82,9 +83,12 @@ 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::{EngineError, params::CommonParams}, + 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,6 +357,194 @@ impl Clique { } } +impl VerifyingEngine for Clique { + fn params(&self) -> &CommonParams { + self.machine.params() + } + + fn verify_block_basic(&self, header: &Header) -> Result<(), ClientError> { + // 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<(), ClientError> { + // 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(ClientError::Block(err)), + Error::Engine(err) => return Err(ClientError::Engine(err)), + _ => return Err(ClientError::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) + } +} + +// todo move to error.rs +impl Into for Error { + fn into(self) -> ClientError { + match self { + Error::Block(e) => ClientError::Block(e), + Error::Engine(e) => ClientError::Engine(e), + Error::State(e) => ClientError::State(e.to_string()), + Error::Import(e) => ClientError::Import(e), + Error::Decoder(e) => ClientError::Decoder(e), + Error::Transaction(e)=> ClientError::Transaction(e), + _ => ClientError::Other(self.to_string()) + } + } +} + impl Engine for Clique { fn name(&self) -> &str { "Clique" } @@ -710,7 +901,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..f73e55d0131 100644 --- a/ethcore/src/engines/clique/tests.rs +++ b/ethcore/src/engines/clique/tests.rs @@ -24,6 +24,7 @@ use ethkey::{Secret, KeyPair}; use state_db::StateDB; use super::*; use test_helpers::get_temp_state_db; +use types::engines::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..e60e9c0fe5e 100644 --- a/ethcore/src/engines/clique/util.rs +++ b/ethcore/src/engines/clique/util.rs @@ -16,7 +16,6 @@ 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}; @@ -24,7 +23,10 @@ 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, + engines::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..86989145d2f 100644 --- a/ethcore/src/engines/instant_seal.rs +++ b/ethcore/src/engines/instant_seal.rs @@ -15,8 +15,14 @@ // 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, + transaction::{self, UnverifiedTransaction, SignedTransaction}, +}; + use block::ExecutedBlock; use error::Error; @@ -52,6 +58,21 @@ 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 1226c7d91ea..858c9e14279 100644 --- a/ethcore/src/engines/mod.rs +++ b/ethcore/src/engines/mod.rs @@ -39,7 +39,6 @@ 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}; @@ -51,6 +50,7 @@ use types::{ engines::{ // ForkChoice, // TODO [ToDr] Remove re-export (#10130) // epoch::{self, Transition as EpochTransition}, + EngineError, params::CommonParams }, transaction::{self, UnverifiedTransaction, SignedTransaction}, @@ -71,94 +71,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 { diff --git a/ethcore/src/engines/null_engine.rs b/ethcore/src/engines/null_engine.rs index 2d81e4400fc..4b536d4fe91 100644 --- a/ethcore/src/engines/null_engine.rs +++ b/ethcore/src/engines/null_engine.rs @@ -15,11 +15,16 @@ // 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, + transaction::{self, UnverifiedTransaction, SignedTransaction}, +}; use block::ExecutedBlock; use error::Error; @@ -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..9ea80b64da7 100644 --- a/ethcore/src/engines/validator_set/contract.rs +++ b/ethcore/src/engines/validator_set/contract.rs @@ -147,7 +147,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/multi.rs b/ethcore/src/engines/validator_set/multi.rs index 0f6dd41afec..94a0060618c 100644 --- a/ethcore/src/engines/validator_set/multi.rs +++ b/ethcore/src/engines/validator_set/multi.rs @@ -151,7 +151,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..0534207eb2f 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, + engines::EngineError, + ids::BlockId, + log_entry::LogEntry, + receipt::Receipt, +}; use unexpected::Mismatch; use client::EngineClient; @@ -297,7 +300,7 @@ impl ValidatorSet for ValidatorSafeContract { 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) } @@ -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()); @@ -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/error.rs b/ethcore/src/error.rs index 01737f10bbc..db00b87bcbe 100644 --- a/ethcore/src/error.rs +++ b/ethcore/src/error.rs @@ -24,29 +24,15 @@ use ethtrie::TrieError; use rlp; use snappy::InvalidInput; use snapshot::Error as SnapshotError; -use types::transaction::Error as TransactionError; - -use engines::EngineError; +use types::{ + engines::EngineError, + transaction::Error as TransactionError, + block::ImportError, +}; pub use executed::{ExecutionError, CallError}; pub use types::block::BlockError; // TODO prolly dont want to re-export -/// 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 {} - /// Ethcore Result pub type EthcoreResult = Result; diff --git a/ethcore/src/lib.rs b/ethcore/src/lib.rs index 10caf9b0539..533e1198766 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; diff --git a/ethcore/src/machine.rs b/ethcore/src/machine.rs index d1f7b909c43..e2b7ee90c25 100644 --- a/ethcore/src/machine.rs +++ b/ethcore/src/machine.rs @@ -27,6 +27,7 @@ use types::{ transaction::{self, SYSTEM_ADDRESS, UNSIGNED_SENDER, UnverifiedTransaction, SignedTransaction}, header::Header, engines::{ + EngineError, EthashExtensions, params::CommonParams, }, @@ -37,7 +38,7 @@ use vm::{EnvInfo, Schedule, CreateContractAddress}; use block::ExecutedBlock; use builtin::Builtin; use call_contract::CallContract; -use client::BlockInfo; +use client_traits::BlockInfo; use error::Error; use executive::Executive; use account_state::{CleanupMode, Substate}; @@ -189,7 +190,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) 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/service.rs b/ethcore/src/snapshot/service.rs index 5e1efe13824..4eca150ea61 100644 --- a/ethcore/src/snapshot/service.rs +++ b/ethcore/src/snapshot/service.rs @@ -28,7 +28,8 @@ 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, ClientIoMessage}; use engines::Engine; use error::Error; use snapshot::{Error as SnapshotError}; 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/watcher.rs b/ethcore/src/snapshot/watcher.rs index 06df0c9cc42..4fc9d881f63 100644 --- a/ethcore/src/snapshot/watcher.rs +++ b/ethcore/src/snapshot/watcher.rs @@ -17,7 +17,8 @@ //! Watcher for snapshot-related chain events. use parking_lot::Mutex; -use client::{BlockInfo, Client, ChainNotify, NewBlocks, ClientIoMessage}; +use client::{Client, ChainNotify, NewBlocks, ClientIoMessage}; +use client_traits::BlockInfo; use types::ids::BlockId; use io::IoChannel; 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 9149eee4265..65a25fe9fb1 100644 --- a/ethcore/src/tx_filter.rs +++ b/ethcore/src/tx_filter.rs @@ -21,7 +21,8 @@ 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 types::{ BlockNumber, 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/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 ddeae25c2c9..d1930246169 100644 --- a/ethcore/src/verification/verification.rs +++ b/ethcore/src/verification/verification.rs @@ -31,7 +31,7 @@ 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::Error; use types::{ 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/types/src/block.rs b/ethcore/types/src/block.rs index 513dc48d2d5..1632bcdf190 100644 --- a/ethcore/types/src/block.rs +++ b/ethcore/types/src/block.rs @@ -99,6 +99,8 @@ pub struct PreverifiedBlock { pub bytes: Bytes, } +// TODO: consolidate errors + /// Errors concerning block processing. #[derive(Debug, Display, PartialEq, Clone, Eq)] pub enum BlockError { @@ -215,7 +217,6 @@ impl error::Error for BlockError { } } -// todo: hook up in ethcore /// Block import Error #[derive(Debug, Display)] pub enum ImportError { @@ -229,3 +230,5 @@ pub enum ImportError { #[display(fmt = "block known to be bad")] KnownBad, } + +impl error::Error for ImportError {} diff --git a/ethcore/types/src/engines/mod.rs b/ethcore/types/src/engines/mod.rs index 73fc3916d7d..57f8fbdabab 100644 --- a/ethcore/types/src/engines/mod.rs +++ b/ethcore/types/src/engines/mod.rs @@ -15,8 +15,9 @@ // along with Parity Ethereum. If not, see . //! Engine-specific types. +use std::fmt; -use ethereum_types::Address; +use ethereum_types::{Address, H256, H64}; use ethjson; use crate::BlockNumber; @@ -24,6 +25,7 @@ use crate::BlockNumber; pub mod epoch; pub mod params; +use unexpected::{Mismatch, OutOfBounds}; /// Default EIP-210 contract code. /// As defined in https://github.com/ethereum/EIPs/pull/210 pub const DEFAULT_BLOCKHASH_CONTRACT: &'static str = "73fffffffffffffffffffffffffffffffffffffffe33141561006a5760014303600035610100820755610100810715156100455760003561010061010083050761010001555b6201000081071515610064576000356101006201000083050761020001555b5061013e565b4360003512151561008457600060405260206040f361013d565b61010060003543031315156100a857610100600035075460605260206060f361013c565b6101006000350715156100c55762010000600035430313156100c8565b60005b156100ea576101006101006000350507610100015460805260206080f361013b565b620100006000350715156101095763010000006000354303131561010c565b60005b1561012f57610100620100006000350507610200015460a052602060a0f361013a565b600060c052602060c0f35b5b5b5b5b"; @@ -60,3 +62,94 @@ impl From for EthashExtensions { } } } + +// TODO: consolidate errors + +/// 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)) + } +} + +// TODO: wth is this? Use derive_more. +impl std::error::Error for EngineError { + fn description(&self) -> &str { + "Engine error" + } +} From 3037cbce9b09005d0fa555e6161f1249573a5649 Mon Sep 17 00:00:00 2001 From: David Palm Date: Tue, 9 Jul 2019 22:44:51 +0200 Subject: [PATCH 76/87] Split Engine in two and move code around --- Cargo.lock | 1 + ethcore/Cargo.toml | 1 + ethcore/client-traits/src/lib.rs | 2 +- ethcore/src/client/client.rs | 14 +- ethcore/src/engines/authority_round/mod.rs | 609 +++++++++--------- ethcore/src/engines/basic_authority.rs | 17 +- ethcore/src/engines/clique/mod.rs | 312 ++++----- ethcore/src/engines/instant_seal.rs | 1 - ethcore/src/engines/mod.rs | 30 +- .../src/ethereum/{ethash.rs => ethash_eng.rs} | 183 +++--- ethcore/src/ethereum/mod.rs | 4 +- ethcore/src/lib.rs | 3 +- ethcore/src/machine.rs | 25 - ethcore/src/miner/miner.rs | 13 +- ethcore/src/snapshot/service.rs | 7 +- ethcore/src/snapshot/watcher.rs | 7 +- ethcore/verification/src/queue/mod.rs | 3 - 17 files changed, 631 insertions(+), 601 deletions(-) rename ethcore/src/ethereum/{ethash.rs => ethash_eng.rs} (96%) diff --git a/Cargo.lock b/Cargo.lock index 447394ab0ec..bde4784c881 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -941,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", ] diff --git a/ethcore/Cargo.toml b/ethcore/Cargo.toml index ccb51e61897..abfd5bcc84e 100644 --- a/ethcore/Cargo.toml +++ b/ethcore/Cargo.toml @@ -75,6 +75,7 @@ vm = { path = "vm" } rand_xorshift = "0.1.1" client-traits = { path = "client-traits" } +verification = { path = "verification" } [dev-dependencies] blooms-db = { path = "../util/blooms-db" } diff --git a/ethcore/client-traits/src/lib.rs b/ethcore/client-traits/src/lib.rs index 71a4046fff0..3fcd68248ad 100644 --- a/ethcore/client-traits/src/lib.rs +++ b/ethcore/client-traits/src/lib.rs @@ -29,7 +29,7 @@ pub trait BlockInfo { fn code_hash(&self, address: &Address, id: BlockId) -> Option; } -/// A consensus mechanism for the chain. Generally either proof-of-work or proof-of-stake-based. +/// 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. diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index 52c90a2c4fa..efc242898a9 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -43,7 +43,7 @@ use client::{ 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, @@ -52,7 +52,7 @@ use client::{ IoClient, BadBlocks, }; use client::bad_blocks; -use client_traits::BlockInfo; +use client_traits::{BlockInfo, VerifyingEngine}; use engines::{MAX_UNCLE_AGE, Engine, EpochTransition, ForkChoice, SealingState}; use engines::epoch::PendingTransition; use error::{ @@ -80,6 +80,7 @@ use types::{ log_entry::LocalizedLogEntry, receipt::{Receipt, LocalizedReceipt}, header::Header, + client_io_message::ClientIoMessage, }; use verification::queue::kind::BlockLike; @@ -256,7 +257,14 @@ impl Importer { message_channel: IoChannel, miner: Arc, ) -> Result { - let block_queue = BlockQueue::new(config.queue.clone(), engine.clone(), message_channel.clone(), config.verifier_type.verifying_seal()); +// let block_queue = BlockQueue::new(config.queue.clone(), engine.clone(), message_channel.clone(), config.verifier_type.verifying_seal()); +// let vengine = engine as Arc; + let block_queue = BlockQueue::new( + config.queue.clone(), + engine.clone(), + message_channel.clone(), + config.verifier_type.verifying_seal() + ); Ok(Importer { import_lock: Mutex::new(()), diff --git a/ethcore/src/engines/authority_round/mod.rs b/ethcore/src/engines/authority_round/mod.rs index d3962f50d35..d1c32909955 100644 --- a/ethcore/src/engines/authority_round/mod.rs +++ b/ethcore/src/engines/authority_round/mod.rs @@ -26,6 +26,7 @@ use std::time::{UNIX_EPOCH, Duration}; use block::*; use client::EngineClient; +use client_traits::VerifyingEngine; use engines::{Engine, Seal, SealingState, ConstructedVerifier}; use engines::block_reward; use engines::block_reward::{BlockRewardContract, RewardKind}; @@ -47,8 +48,8 @@ use types::{ ancestry_action::AncestryAction, BlockNumber, header::{Header, ExtendedHeader}, - engines::EngineError, - + engines::{EngineError, params::CommonParams}, + transaction::{self, UnverifiedTransaction, SignedTransaction}, }; use unexpected::{Mismatch, OutOfBounds}; @@ -945,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)?; + + // 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 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 { @@ -1011,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 { @@ -1072,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 @@ -1212,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, &[])) @@ -1433,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, @@ -1547,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, @@ -1571,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) { @@ -1588,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 diff --git a/ethcore/src/engines/basic_authority.rs b/ethcore/src/engines/basic_authority.rs index 60440fc0f58..a9ac9b619e9 100644 --- a/ethcore/src/engines/basic_authority.rs +++ b/ethcore/src/engines/basic_authority.rs @@ -97,10 +97,17 @@ 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) + } + fn verify_transaction_basic(&self, t: &UnverifiedTransaction, header: &Header) -> Result<(), transaction::Error> { self.machine().verify_transaction_basic(t, header) } @@ -115,8 +122,8 @@ impl Engine for BasicAuthority { fn machine(&self) -> &Machine { &self.machine } - // One field - the signature - fn seal_fields(&self, _header: &Header) -> usize { 1 } +// // One field - the signature +// fn seal_fields(&self, _header: &Header) -> usize { 1 } fn sealing_state(&self) -> SealingState { if self.signer.read().is_some() { @@ -145,9 +152,9 @@ impl Engine for BasicAuthority { Ok(()) } - fn verify_block_external(&self, header: &Header) -> Result<(), Error> { - verify_external(header, &*self.validators) - } +// 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) diff --git a/ethcore/src/engines/clique/mod.rs b/ethcore/src/engines/clique/mod.rs index 5a6584a0184..00d4544e215 100644 --- a/ethcore/src/engines/clique/mod.rs +++ b/ethcore/src/engines/clique/mod.rs @@ -358,6 +358,9 @@ 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() } @@ -550,9 +553,6 @@ impl Engine for 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( @@ -727,159 +727,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))?; diff --git a/ethcore/src/engines/instant_seal.rs b/ethcore/src/engines/instant_seal.rs index 86989145d2f..eda0ef91958 100644 --- a/ethcore/src/engines/instant_seal.rs +++ b/ethcore/src/engines/instant_seal.rs @@ -72,7 +72,6 @@ impl VerifyingEngine for InstantSeal { } } - impl Engine for InstantSeal { fn name(&self) -> &str { "InstantSeal" diff --git a/ethcore/src/engines/mod.rs b/ethcore/src/engines/mod.rs index 858c9e14279..d4ba207b131 100644 --- a/ethcore/src/engines/mod.rs +++ b/ethcore/src/engines/mod.rs @@ -53,15 +53,15 @@ use types::{ EngineError, params::CommonParams }, - transaction::{self, UnverifiedTransaction, SignedTransaction}, + transaction::{self, UnverifiedTransaction}, }; use snapshot::SnapshotComponents; 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 block::ExecutedBlock; @@ -194,7 +194,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; @@ -203,7 +203,7 @@ 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() } @@ -264,18 +264,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()) } @@ -441,9 +441,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. /// @@ -455,9 +455,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/ethereum/ethash.rs b/ethcore/src/ethereum/ethash_eng.rs similarity index 96% rename from ethcore/src/ethereum/ethash.rs rename to ethcore/src/ethereum/ethash_eng.rs index 91964a11e95..b2ded4d2830 100644 --- a/ethcore/src/ethereum/ethash.rs +++ b/ethcore/src/ethereum/ethash_eng.rs @@ -19,12 +19,18 @@ 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}, +}; + use unexpected::{OutOfBounds, Mismatch}; use block::ExecutedBlock; @@ -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), - }) + } } } @@ -210,13 +216,93 @@ impl engines::EpochVerifier for Arc { } } -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> { @@ -306,77 +385,15 @@ impl Engine for Arc { .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(()) - } - 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))) } 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/lib.rs b/ethcore/src/lib.rs index 533e1198766..edb7fba1cb5 100644 --- a/ethcore/src/lib.rs +++ b/ethcore/src/lib.rs @@ -140,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; @@ -162,7 +163,7 @@ pub mod machine; pub mod miner; pub mod snapshot; pub mod spec; -pub mod verification; +//pub mod verification; mod externalities; mod transaction_ext; diff --git a/ethcore/src/machine.rs b/ethcore/src/machine.rs index e2b7ee90c25..c886fb44fdc 100644 --- a/ethcore/src/machine.rs +++ b/ethcore/src/machine.rs @@ -48,31 +48,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]); -// todo remove -/// 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; diff --git a/ethcore/src/miner/miner.rs b/ethcore/src/miner/miner.rs index 33c33883672..0870c48d482 100644 --- a/ethcore/src/miner/miner.rs +++ b/ethcore/src/miner/miner.rs @@ -43,17 +43,20 @@ 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, + 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; diff --git a/ethcore/src/snapshot/service.rs b/ethcore/src/snapshot/service.rs index 4eca150ea61..9b502e7ef35 100644 --- a/ethcore/src/snapshot/service.rs +++ b/ethcore/src/snapshot/service.rs @@ -29,12 +29,15 @@ use super::io::{SnapshotReader, LooseReader, SnapshotWriter, LooseWriter}; use blockchain::{BlockChain, BlockChainDB, BlockChainDBHandler}; use client_traits::BlockInfo; -use client::{BlockChainClient, Client, ChainInfo, ClientIoMessage}; +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, + ids::BlockId, +}; use io::IoChannel; diff --git a/ethcore/src/snapshot/watcher.rs b/ethcore/src/snapshot/watcher.rs index 4fc9d881f63..9ba7f601031 100644 --- a/ethcore/src/snapshot/watcher.rs +++ b/ethcore/src/snapshot/watcher.rs @@ -17,9 +17,12 @@ //! Watcher for snapshot-related chain events. use parking_lot::Mutex; -use client::{Client, ChainNotify, NewBlocks, ClientIoMessage}; +use client::{Client, ChainNotify, NewBlocks}; use client_traits::BlockInfo; -use types::ids::BlockId; +use types::{ + client_io_message::ClientIoMessage, + ids::BlockId +}; use io::IoChannel; use ethereum_types::H256; diff --git a/ethcore/verification/src/queue/mod.rs b/ethcore/verification/src/queue/mod.rs index 493feaa30cf..b65e0f8032e 100644 --- a/ethcore/verification/src/queue/mod.rs +++ b/ethcore/verification/src/queue/mod.rs @@ -26,9 +26,6 @@ use parity_util_mem::{MallocSizeOf, MallocSizeOfExt}; use ethereum_types::{H256, U256}; use parking_lot::{Condvar, Mutex, RwLock}; use ethcore_io::*; -//use error::{BlockError, ImportError, Error}; -//use engines::Engine; -//use client::ClientIoMessage; use len_caching_lock::LenCachingMutex; use num_cpus; use crate::{ From 183e6e1a285c5349e85497e61e227552000772fc Mon Sep 17 00:00:00 2001 From: David Palm Date: Tue, 9 Jul 2019 22:47:00 +0200 Subject: [PATCH 77/87] cleanup --- ethcore/src/client/client.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index efc242898a9..4c6d1d5751f 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -257,8 +257,6 @@ impl Importer { message_channel: IoChannel, miner: Arc, ) -> Result { -// let block_queue = BlockQueue::new(config.queue.clone(), engine.clone(), message_channel.clone(), config.verifier_type.verifying_seal()); -// let vengine = engine as Arc; let block_queue = BlockQueue::new( config.queue.clone(), engine.clone(), From 196605f58ea3978163a6ec1ac38e2dece415b51b Mon Sep 17 00:00:00 2001 From: David Palm Date: Wed, 10 Jul 2019 11:10:50 +0200 Subject: [PATCH 78/87] verification: don't rexport common_types --- ethcore/verification/src/lib.rs | 2 +- ethcore/verification/src/queue/mod.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ethcore/verification/src/lib.rs b/ethcore/verification/src/lib.rs index df3dcf9d1c0..22f2fada76f 100644 --- a/ethcore/verification/src/lib.rs +++ b/ethcore/verification/src/lib.rs @@ -29,7 +29,7 @@ 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, QueueInfo}; +pub use self::queue::{BlockQueue, Config as QueueConfig, VerificationQueue}; use call_contract::CallContract; use client_traits::BlockInfo; diff --git a/ethcore/verification/src/queue/mod.rs b/ethcore/verification/src/queue/mod.rs index b65e0f8032e..82d5c7c429b 100644 --- a/ethcore/verification/src/queue/mod.rs +++ b/ethcore/verification/src/queue/mod.rs @@ -33,7 +33,7 @@ use crate::{ queue::kind::{BlockLike, Kind}, }; -pub use common_types::{ +use common_types::{ verification_queue_info::VerificationQueueInfo as QueueInfo, block_status::BlockStatus, block::{BlockError, ImportError}, @@ -218,7 +218,7 @@ impl VerificationQueue { verifying: AtomicUsize::new(0), verified: AtomicUsize::new(0), }, - check_seal: check_seal, + check_seal, }); let more_to_verify = Arc::new(Condvar::new()); let deleting = Arc::new(AtomicBool::new(false)); From 28f282a5642e0a30d16a097f523deb34f8971ce2 Mon Sep 17 00:00:00 2001 From: David Palm Date: Wed, 10 Jul 2019 13:05:16 +0200 Subject: [PATCH 79/87] Use error from common_types --- ethcore/account-state/src/account.rs | 1 - ethcore/account-state/src/backend.rs | 3 +-- ethcore/account-state/src/error.rs | 35 ---------------------------- ethcore/account-state/src/lib.rs | 2 -- ethcore/account-state/src/state.rs | 2 +- 5 files changed, 2 insertions(+), 41 deletions(-) delete mode 100644 ethcore/account-state/src/error.rs 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/error.rs b/ethcore/account-state/src/error.rs deleted file mode 100644 index 0a2911f7913..00000000000 --- a/ethcore/account-state/src/error.rs +++ /dev/null @@ -1,35 +0,0 @@ -// 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}; - -#[derive(Debug, Display, From)] -pub enum Error { - /// Trie error. - Trie(ethtrie::TrieError), - /// Decoder error. - Decoder(rlp::DecoderError), -} - -impl std::error::Error for Error {} - -impl From> for Error where Error: From { - fn from(err: Box) -> Self { - Error::from(*err) - } -} 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, }; From 7cadd5485deadbde8053bbe0e108d91df4278a75 Mon Sep 17 00:00:00 2001 From: David Palm Date: Wed, 10 Jul 2019 13:05:54 +0200 Subject: [PATCH 80/87] Consolidate error types --- ethcore/types/Cargo.toml | 12 +- ethcore/types/src/block.rs | 146 +-------------- ethcore/types/src/engines/mod.rs | 95 +--------- ethcore/types/src/errors/block_error.rs | 148 +++++++++++++++ ethcore/types/src/errors/engine_error.rs | 94 ++++++++++ ethcore/types/src/errors/ethcore_error.rs | 207 +++++++++++++++++++++ ethcore/types/src/errors/mod.rs | 29 +++ ethcore/types/src/errors/snapshot_error.rs | 140 ++++++++++++++ ethcore/types/src/lib.rs | 7 +- 9 files changed, 631 insertions(+), 247 deletions(-) create mode 100644 ethcore/types/src/errors/block_error.rs create mode 100644 ethcore/types/src/errors/engine_error.rs create mode 100644 ethcore/types/src/errors/ethcore_error.rs create mode 100644 ethcore/types/src/errors/mod.rs create mode 100644 ethcore/types/src/errors/snapshot_error.rs diff --git a/ethcore/types/Cargo.toml b/ethcore/types/Cargo.toml index 4f0f607c211..a7897033f15 100644 --- a/ethcore/types/Cargo.toml +++ b/ethcore/types/Cargo.toml @@ -5,19 +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" } -ethbloom = "0.6" -derive_more = "0.15.0" -rustc-hex = "2.0.1" # todo: only needed for CommonParams. Remove. 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 1632bcdf190..7be08da984d 100644 --- a/ethcore/types/src/block.rs +++ b/ethcore/types/src/block.rs @@ -30,23 +30,15 @@ //! //! `ExecutedBlock` is an underlying data structure used by all structs above to store block //! related info. -use std::{ - fmt, - error, - time::SystemTime -}; +// todo: fix module docs + use bytes::Bytes; -use derive_more::{Display, From}; use parity_util_mem::MallocSizeOf; use header::Header; use rlp::{Rlp, RlpStream, Decodable, DecoderError}; use transaction::{UnverifiedTransaction, SignedTransaction}; -use unexpected::{Mismatch, OutOfBounds}; -use BlockNumber; -use ethbloom::Bloom; -use ethereum_types::{H256, U256, Address}; /// A block, encoded as it is on the block chain. #[derive(Default, Debug, Clone, PartialEq)] @@ -98,137 +90,3 @@ pub struct PreverifiedBlock { /// Block bytes pub bytes: Bytes, } - -// TODO: consolidate errors - -/// 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/engines/mod.rs b/ethcore/types/src/engines/mod.rs index 57f8fbdabab..73fc3916d7d 100644 --- a/ethcore/types/src/engines/mod.rs +++ b/ethcore/types/src/engines/mod.rs @@ -15,9 +15,8 @@ // along with Parity Ethereum. If not, see . //! Engine-specific types. -use std::fmt; -use ethereum_types::{Address, H256, H64}; +use ethereum_types::Address; use ethjson; use crate::BlockNumber; @@ -25,7 +24,6 @@ use crate::BlockNumber; pub mod epoch; pub mod params; -use unexpected::{Mismatch, OutOfBounds}; /// Default EIP-210 contract code. /// As defined in https://github.com/ethereum/EIPs/pull/210 pub const DEFAULT_BLOCKHASH_CONTRACT: &'static str = "73fffffffffffffffffffffffffffffffffffffffe33141561006a5760014303600035610100820755610100810715156100455760003561010061010083050761010001555b6201000081071515610064576000356101006201000083050761020001555b5061013e565b4360003512151561008457600060405260206040f361013d565b61010060003543031315156100a857610100600035075460605260206060f361013c565b6101006000350715156100c55762010000600035430313156100c8565b60005b156100ea576101006101006000350507610100015460805260206080f361013b565b620100006000350715156101095763010000006000354303131561010c565b60005b1561012f57610100620100006000350507610200015460a052602060a0f361013a565b600060c052602060c0f35b5b5b5b5b"; @@ -62,94 +60,3 @@ impl From for EthashExtensions { } } } - -// TODO: consolidate errors - -/// 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)) - } -} - -// 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/block_error.rs b/ethcore/types/src/errors/block_error.rs new file mode 100644 index 00000000000..f9aa0a32fae --- /dev/null +++ b/ethcore/types/src/errors/block_error.rs @@ -0,0 +1,148 @@ +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)] + // TODO: moved to ImportError – can remove here? + 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, + // todo now we have two of these, one in BlockError too. Fix! + /// Timestamp header field is too far in future. + #[display(fmt = "Future timestamp in header: {}", _0)] + TemporarilyInvalid(OutOfBoundsTime), +} + +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/types/src/errors/snapshot_error.rs b/ethcore/types/src/errors/snapshot_error.rs new file mode 100644 index 00000000000..ae4048fd2a3 --- /dev/null +++ b/ethcore/types/src/errors/snapshot_error.rs @@ -0,0 +1,140 @@ +// 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 . + +//! Snapshot-related errors. + +use std::error; +use std::fmt; + +use ethereum_types::H256; +use ethtrie::TrieError; +use rlp::DecoderError; + +use ids::BlockId; + +/// Snapshot-related errors. +#[derive(Debug)] +pub enum SnapshotError { + /// Invalid starting block for snapshot. + InvalidStartingBlock(BlockId), + /// Block not found. + BlockNotFound(H256), + /// Incomplete chain. + IncompleteChain, + /// Best block has wrong state root. + WrongStateRoot(H256, H256), + /// Wrong block hash. + WrongBlockHash(u64, H256, H256), + /// Too many blocks contained within the snapshot. + TooManyBlocks(u64, u64), + /// Old starting block in a pruned database. + OldBlockPrunedDB, + /// Missing code. + MissingCode(Vec), + /// Unrecognized code encoding. + UnrecognizedCodeState(u8), + /// Restoration aborted. + RestorationAborted, + /// Trie error. + Trie(TrieError), + /// Decoder error. + Decoder(DecoderError), + /// Io error. + Io(::std::io::Error), + /// Snapshot version is not supported. + VersionNotSupported(u64), + /// Max chunk size is to small to fit basic account data. + ChunkTooSmall, + /// Oversized chunk + ChunkTooLarge, + /// Snapshots not supported by the consensus engine. + SnapshotsUnsupported, + /// Aborted snapshot + SnapshotAborted, + /// Bad epoch transition. + BadEpochProof(u64), + /// Wrong chunk format. + WrongChunkFormat(String), + /// Unlinked ancient block chain; includes the parent hash where linkage failed + UnlinkedAncientBlockChain(H256), +} + +impl error::Error for SnapshotError { + fn source(&self) -> Option<&(dyn error::Error + 'static)> { + use self::SnapshotError::*; + match self { + Trie(e) => Some(e), + Decoder(e) => Some(e), + Io(e) => Some(e), + _ => None, + } + } +} + +impl fmt::Display for SnapshotError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use self::SnapshotError::*; + match *self { + 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), + 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."), + 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 SnapshotError { + fn from(err: ::std::io::Error) -> Self { + SnapshotError::Io(err) + } +} + +impl From for SnapshotError { + fn from(err: TrieError) -> Self { + SnapshotError::Trie(err) + } +} + +impl From for SnapshotError { + fn from(err: DecoderError) -> Self { + SnapshotError::Decoder(err) + } +} + +impl From> for SnapshotError where SnapshotError: From { + fn from(err: Box) -> Self { + SnapshotError::from(*err) + } +} diff --git a/ethcore/types/src/lib.rs b/ethcore/types/src/lib.rs index 6fe1d2d8473..d89a5055b3f 100644 --- a/ethcore/types/src/lib.rs +++ b/ethcore/types/src/lib.rs @@ -37,11 +37,14 @@ 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] @@ -49,9 +52,6 @@ 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; @@ -65,6 +65,7 @@ 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; From 0c09315563c8e41b7c643fd576d8667fbead059c Mon Sep 17 00:00:00 2001 From: David Palm Date: Wed, 10 Jul 2019 13:06:58 +0200 Subject: [PATCH 81/87] VerifyingEngine use Errors from common_types --- ethcore/client-traits/src/lib.rs | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/ethcore/client-traits/src/lib.rs b/ethcore/client-traits/src/lib.rs index 3fcd68248ad..d1e9525e431 100644 --- a/ethcore/client-traits/src/lib.rs +++ b/ethcore/client-traits/src/lib.rs @@ -8,12 +8,9 @@ use common_types::{ EthashExtensions, params::CommonParams }, + errors::EthcoreError, }; -pub mod error; - -use error::Error; - /// Provides various information on a block by it's ID pub trait BlockInfo { /// Get raw block header data by block id. @@ -49,18 +46,18 @@ pub trait VerifyingEngine: 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<(), 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<(), Error> { Ok(()) } + 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<(), Error> { Ok(()) } + 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<(), Error> { Ok(()) } + fn verify_block_external(&self, _header: &Header) -> Result<(), EthcoreError> { Ok(()) } /// Perform basic/cheap transaction verification. /// From 35037f8b23812f734bad5a2e28c34955b181fcda Mon Sep 17 00:00:00 2001 From: David Palm Date: Wed, 10 Jul 2019 13:30:21 +0200 Subject: [PATCH 82/87] verification: Use error type from common_types --- ethcore/verification/src/canon_verifier.rs | 13 ++++--- ethcore/verification/src/lib.rs | 1 - ethcore/verification/src/noop_verifier.rs | 12 ++++--- ethcore/verification/src/queue/kind.rs | 42 +++++++++++++++------- ethcore/verification/src/queue/mod.rs | 16 ++++----- ethcore/verification/src/verification.rs | 15 ++++---- ethcore/verification/src/verifier.rs | 13 ++++--- 7 files changed, 68 insertions(+), 44 deletions(-) diff --git a/ethcore/verification/src/canon_verifier.rs b/ethcore/verification/src/canon_verifier.rs index 4c4318a8c4e..03bfc7af60c 100644 --- a/ethcore/verification/src/canon_verifier.rs +++ b/ethcore/verification/src/canon_verifier.rs @@ -19,11 +19,13 @@ use call_contract::CallContract; use client_traits::{BlockInfo, VerifyingEngine}; use crate::{ - error::Error, Verifier, - verification::{FullFamilyParams}, + verification::{self, FullFamilyParams}, +}; +use common_types::{ + errors::EthcoreError as Error, + header::Header, }; -use common_types::header::Header; /// A canonical verifier -- this does full verification. pub struct CanonVerifier; @@ -36,17 +38,18 @@ impl Verifier for CanonVerifier { engine: &dyn VerifyingEngine, do_full: Option>, ) -> Result<(), Error> { + // todo: port over // verification::verify_block_family(header, parent, engine, do_full) Ok(()) } fn verify_block_final(&self, expected: &Header, got: &Header) -> Result<(), Error> { + // todo: port over // verification::verify_block_final(expected, got) Ok(()) } fn verify_block_external(&self, header: &Header, engine: &dyn VerifyingEngine) -> Result<(), Error> { -// engine.verify_block_external(header) - Ok(()) + engine.verify_block_external(header).map_err(Into::into) } } diff --git a/ethcore/verification/src/lib.rs b/ethcore/verification/src/lib.rs index 22f2fada76f..c8ab33fc3c4 100644 --- a/ethcore/verification/src/lib.rs +++ b/ethcore/verification/src/lib.rs @@ -23,7 +23,6 @@ mod verifier; pub mod queue; mod canon_verifier; mod noop_verifier; -mod error; pub use self::verification::*; pub use self::verifier::Verifier; diff --git a/ethcore/verification/src/noop_verifier.rs b/ethcore/verification/src/noop_verifier.rs index e47151a5330..29f0f7dd03d 100644 --- a/ethcore/verification/src/noop_verifier.rs +++ b/ethcore/verification/src/noop_verifier.rs @@ -19,10 +19,12 @@ use call_contract::CallContract; use client_traits::{BlockInfo, VerifyingEngine}; use crate::{ - error::Error, Verifier, verification::FullFamilyParams }; -use common_types::header::Header; +use common_types::{ + errors::EthcoreError, + header::Header, +}; /// A no-op verifier -- this will verify everything it's given immediately. #[allow(dead_code)] @@ -35,15 +37,15 @@ impl Verifier for NoopVerifier { _t: &Header, _: &dyn VerifyingEngine, _: Option> - ) -> Result<(), Error> { + ) -> Result<(), EthcoreError> { Ok(()) } - fn verify_block_final(&self, _expected: &Header, _got: &Header) -> Result<(), Error> { + fn verify_block_final(&self, _expected: &Header, _got: &Header) -> Result<(), EthcoreError> { Ok(()) } - fn verify_block_external(&self, _header: &Header, _engine: &dyn VerifyingEngine) -> Result<(), Error> { + 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 index 7cbf53f0e28..a17bdb36640 100644 --- a/ethcore/verification/src/queue/kind.rs +++ b/ethcore/verification/src/queue/kind.rs @@ -22,11 +22,13 @@ use client_traits::VerifyingEngine; use parity_util_mem::MallocSizeOf; use ethereum_types::{H256, U256}; -use crate::error::Error; 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 { @@ -73,11 +75,11 @@ pub mod blocks { use client_traits::VerifyingEngine; use crate::{ - error::Error, verification::{verify_block_basic, verify_block_unordered} }; use common_types::{ - block::{PreverifiedBlock, BlockError}, + block::PreverifiedBlock, + errors::{BlockError, EthcoreError as Error}, header::Header, transaction::UnverifiedTransaction }; @@ -95,7 +97,11 @@ pub mod blocks { type Unverified = Unverified; type Verified = PreverifiedBlock; - fn create(input: Self::Input, engine: &dyn VerifyingEngine, check_seal: bool) -> Result { + 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))) => { @@ -109,7 +115,11 @@ pub mod blocks { } } - fn verify(un: Self::Unverified, engine: &dyn VerifyingEngine, check_seal: bool) -> Result { + 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), @@ -188,14 +198,14 @@ pub mod blocks { pub mod headers { use super::{Kind, BlockLike}; - use common_types::header::Header; + use common_types::{ + errors::EthcoreError as Error, + header::Header + }; use client_traits::VerifyingEngine; use ethereum_types::{H256, U256}; - use crate::{ - error::Error, - verification::verify_header_params, - }; + use crate::verification::verify_header_params; impl BlockLike for Header { fn hash(&self) -> H256 { self.hash() } @@ -211,14 +221,22 @@ pub mod headers { type Unverified = Header; type Verified = Header; - fn create(input: Self::Input, engine: &dyn VerifyingEngine, check_seal: bool) -> Result { + 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 { + 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 index 82d5c7c429b..11f20839462 100644 --- a/ethcore/verification/src/queue/mod.rs +++ b/ethcore/verification/src/queue/mod.rs @@ -28,20 +28,18 @@ use parking_lot::{Condvar, Mutex, RwLock}; use ethcore_io::*; use len_caching_lock::LenCachingMutex; use num_cpus; -use crate::{ - error::Error, - queue::kind::{BlockLike, Kind}, -}; - use common_types::{ + errors::{BlockError, EthcoreError as Error, ImportError}, verification_queue_info::VerificationQueueInfo as QueueInfo, block_status::BlockStatus, - block::{BlockError, ImportError}, 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; @@ -744,9 +742,11 @@ mod tests { use super::{BlockQueue, Config, State}; use super::kind::blocks::Unverified; use test_helpers::{get_good_dummy_block_seq, get_good_dummy_block}; - use error::*; use bytes::Bytes; - use types::view; + use types::{ + view::{self, BlockView}, + errors::{EthcoreError as Error, ImportError}, + }; use types::views::BlockView; // create a test block queue. diff --git a/ethcore/verification/src/verification.rs b/ethcore/verification/src/verification.rs index 7691f9f3d8b..fb786b7cced 100644 --- a/ethcore/verification/src/verification.rs +++ b/ethcore/verification/src/verification.rs @@ -32,15 +32,14 @@ use ethcore_blockchain::BlockProvider; use call_contract::CallContract; use client_traits::{BlockInfo, VerifyingEngine}; -//use engines::{Engine, MAX_UNCLE_AGE}; use crate::{ - error::Error, queue::kind::blocks::Unverified, }; use common_types::{ BlockNumber, header::Header, - block::{BlockError, PreverifiedBlock}, + block::PreverifiedBlock, + errors::{BlockError, EthcoreError}, }; use time_utils::CheckedSystemTime; @@ -59,7 +58,7 @@ pub struct FullFamilyParams<'a, C: BlockInfo + CallContract + 'a> { } /// 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<(), Error> { +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)?; @@ -84,7 +83,7 @@ pub fn verify_block_basic(block: &Unverified, engine: &dyn VerifyingEngine, chec /// 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 { +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)?; @@ -110,7 +109,7 @@ pub fn verify_block_unordered(block: Unverified, engine: &dyn VerifyingEngine, c } Ok(t) }) - .collect::, Error>>()?; + .collect::, EthcoreError>>()?; Ok(PreverifiedBlock { header, @@ -121,7 +120,7 @@ pub fn verify_block_unordered(block: Unverified, engine: &dyn VerifyingEngine, c } /// Check basic header parameters. -pub fn verify_header_params(header: &Header, engine: &dyn VerifyingEngine, is_full: bool, check_seal: bool) -> Result<(), Error> { +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 { @@ -180,7 +179,7 @@ pub fn verify_header_params(header: &Header, engine: &dyn VerifyingEngine, is_fu } /// Verify block data against header: transactions root and uncles hash. -fn verify_block_integrity(block: &Unverified) -> Result<(), Error> { +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())); diff --git a/ethcore/verification/src/verifier.rs b/ethcore/verification/src/verifier.rs index 3ef9252f0d2..a559da8780c 100644 --- a/ethcore/verification/src/verifier.rs +++ b/ethcore/verification/src/verifier.rs @@ -20,10 +20,13 @@ use call_contract::CallContract; use client_traits::{BlockInfo, VerifyingEngine}; //use engines::Engine; use crate::{ - error::Error, +// error::Error, verification::FullFamilyParams, }; -use common_types::header::Header; +use common_types::{ + errors::EthcoreError, + header::Header, +}; //use super::verification; /// Should be used to verify blocks. @@ -37,10 +40,10 @@ pub trait Verifier: Send + Sync parent: &Header, engine: &dyn VerifyingEngine, do_full: Option> - ) -> Result<(), Error>; + ) -> 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<(), Error>; + 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<(), Error>; + fn verify_block_external(&self, header: &Header, engine: &dyn VerifyingEngine) -> Result<(), EthcoreError>; } From 0305525891ae83c7304e8efeababa3ce5229b6d9 Mon Sep 17 00:00:00 2001 From: David Palm Date: Wed, 10 Jul 2019 13:31:44 +0200 Subject: [PATCH 83/87] SnapshotError moved to common_types --- ethcore/src/snapshot/error.rs | 138 ------------------------ ethcore/types/src/errors/block_error.rs | 5 - 2 files changed, 143 deletions(-) delete mode 100644 ethcore/src/snapshot/error.rs diff --git a/ethcore/src/snapshot/error.rs b/ethcore/src/snapshot/error.rs deleted file mode 100644 index 68742e2e178..00000000000 --- a/ethcore/src/snapshot/error.rs +++ /dev/null @@ -1,138 +0,0 @@ -// 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 . - -//! Snapshot-related errors. - -use std::error; -use std::fmt; - -use types::ids::BlockId; - -use ethereum_types::H256; -use ethtrie::TrieError; -use rlp::DecoderError; - -/// Snapshot-related errors. -#[derive(Debug)] -pub enum Error { - /// Invalid starting block for snapshot. - InvalidStartingBlock(BlockId), - /// Block not found. - BlockNotFound(H256), - /// Incomplete chain. - IncompleteChain, - /// Best block has wrong state root. - WrongStateRoot(H256, H256), - /// Wrong block hash. - WrongBlockHash(u64, H256, H256), - /// Too many blocks contained within the snapshot. - TooManyBlocks(u64, u64), - /// Old starting block in a pruned database. - OldBlockPrunedDB, - /// Missing code. - MissingCode(Vec), - /// Unrecognized code encoding. - UnrecognizedCodeState(u8), - /// Restoration aborted. - RestorationAborted, - /// Trie error. - Trie(TrieError), - /// Decoder error. - Decoder(DecoderError), - /// Io error. - Io(::std::io::Error), - /// Snapshot version is not supported. - VersionNotSupported(u64), - /// Max chunk size is to small to fit basic account data. - ChunkTooSmall, - /// Oversized chunk - ChunkTooLarge, - /// Snapshots not supported by the consensus engine. - SnapshotsUnsupported, - /// Aborted snapshot - SnapshotAborted, - /// Bad epoch transition. - BadEpochProof(u64), - /// Wrong chunk format. - WrongChunkFormat(String), - /// Unlinked ancient block chain; includes the parent hash where linkage failed - UnlinkedAncientBlockChain(H256), -} - -impl error::Error for Error { - fn source(&self) -> Option<&(dyn error::Error + 'static)> { - match self { - Error::Trie(e) => Some(e), - Error::Decoder(e) => Some(e), - Error::Io(e) => Some(e), - _ => None, - } - } -} - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - 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) => - 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 \ - 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), - } - } -} - -impl From<::std::io::Error> for Error { - fn from(err: ::std::io::Error) -> Self { - Error::Io(err) - } -} - -impl From for Error { - fn from(err: TrieError) -> Self { - Error::Trie(err) - } -} - -impl From for Error { - fn from(err: DecoderError) -> Self { - Error::Decoder(err) - } -} - -impl From> for Error where Error: From { - fn from(err: Box) -> Self { - Error::from(*err) - } -} diff --git a/ethcore/types/src/errors/block_error.rs b/ethcore/types/src/errors/block_error.rs index f9aa0a32fae..261542f34a5 100644 --- a/ethcore/types/src/errors/block_error.rs +++ b/ethcore/types/src/errors/block_error.rs @@ -81,7 +81,6 @@ pub enum BlockError { InvalidTimestamp(OutOfBoundsTime), /// Timestamp header field is too far in future. #[display(fmt = "Future timestamp in header: {}", _0)] - // TODO: moved to ImportError – can remove here? TemporarilyInvalid(OutOfBoundsTime), /// Log bloom header field is invalid. #[display(fmt = "Invalid log bloom in header: {}", _0)] @@ -139,10 +138,6 @@ pub enum ImportError { /// Already marked as bad from a previous import (could mean parent is bad) #[display(fmt = "block known to be bad")] KnownBad, - // todo now we have two of these, one in BlockError too. Fix! - /// Timestamp header field is too far in future. - #[display(fmt = "Future timestamp in header: {}", _0)] - TemporarilyInvalid(OutOfBoundsTime), } impl error::Error for ImportError {} From 55f0b7394bb9b3aa83fe5a277d04971257427eb2 Mon Sep 17 00:00:00 2001 From: David Palm Date: Wed, 10 Jul 2019 14:40:33 +0200 Subject: [PATCH 84/87] Move more code from Engne to VerifyingEngine Add a VerifyingClient trait: BlockInfo + CallContract Whitespace --- ethcore/client-traits/Cargo.toml | 1 + ethcore/client-traits/src/lib.rs | 49 +++++++++++++++++++++++--------- 2 files changed, 37 insertions(+), 13 deletions(-) diff --git a/ethcore/client-traits/Cargo.toml b/ethcore/client-traits/Cargo.toml index 28d79547669..a19d447bf6c 100644 --- a/ethcore/client-traits/Cargo.toml +++ b/ethcore/client-traits/Cargo.toml @@ -5,6 +5,7 @@ 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" diff --git a/ethcore/client-traits/src/lib.rs b/ethcore/client-traits/src/lib.rs index d1e9525e431..3ed2e3f3c0d 100644 --- a/ethcore/client-traits/src/lib.rs +++ b/ethcore/client-traits/src/lib.rs @@ -1,37 +1,46 @@ -use ethereum_types::{H256, U256, Address}; +// todo: module docs + +use call_contract::CallContract; use common_types::{ - ids::BlockId, - header::Header, - encoded, - transaction::{self, UnverifiedTransaction, SignedTransaction}, + 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 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 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 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; + /// 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 } @@ -80,4 +89,18 @@ pub trait VerifyingEngine: 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; + + // 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 + } } From 2157d812bb9bbf5e5e5562487f5f29e96600124a Mon Sep 17 00:00:00 2001 From: David Palm Date: Wed, 10 Jul 2019 14:41:45 +0200 Subject: [PATCH 85/87] Add MAX_UNCLE_AGE const --- ethcore/types/src/engines/mod.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ethcore/types/src/engines/mod.rs b/ethcore/types/src/engines/mod.rs index 73fc3916d7d..33a6e78145b 100644 --- a/ethcore/types/src/engines/mod.rs +++ b/ethcore/types/src/engines/mod.rs @@ -24,8 +24,13 @@ 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. From 74b48fd02130326ddceeb10be5238239b073e948 Mon Sep 17 00:00:00 2001 From: David Palm Date: Wed, 10 Jul 2019 14:42:25 +0200 Subject: [PATCH 86/87] Port over remaining code from ethcore/verification --- ethcore/verification/src/canon_verifier.rs | 13 +- ethcore/verification/src/lib.rs | 5 +- ethcore/verification/src/verification.rs | 198 ++++++++++++++++++++- ethcore/verification/src/verifier.rs | 8 +- 4 files changed, 202 insertions(+), 22 deletions(-) diff --git a/ethcore/verification/src/canon_verifier.rs b/ethcore/verification/src/canon_verifier.rs index 03bfc7af60c..31f7f57aefc 100644 --- a/ethcore/verification/src/canon_verifier.rs +++ b/ethcore/verification/src/canon_verifier.rs @@ -16,8 +16,7 @@ //! Canonical verifier. -use call_contract::CallContract; -use client_traits::{BlockInfo, VerifyingEngine}; +use client_traits::{VerifyingEngine, VerifyingClient}; use crate::{ Verifier, verification::{self, FullFamilyParams}, @@ -30,7 +29,7 @@ use common_types::{ /// A canonical verifier -- this does full verification. pub struct CanonVerifier; -impl Verifier for CanonVerifier { +impl Verifier for CanonVerifier { fn verify_block_family( &self, header: &Header, @@ -38,15 +37,11 @@ impl Verifier for CanonVerifier { engine: &dyn VerifyingEngine, do_full: Option>, ) -> Result<(), Error> { - // todo: port over -// verification::verify_block_family(header, parent, engine, do_full) - Ok(()) + verification::verify_block_family(header, parent, engine, do_full) } fn verify_block_final(&self, expected: &Header, got: &Header) -> Result<(), Error> { - // todo: port over -// verification::verify_block_final(expected, got) - Ok(()) + verification::verify_block_final(expected, got) } fn verify_block_external(&self, header: &Header, engine: &dyn VerifyingEngine) -> Result<(), Error> { diff --git a/ethcore/verification/src/lib.rs b/ethcore/verification/src/lib.rs index c8ab33fc3c4..8eb0fcf0c30 100644 --- a/ethcore/verification/src/lib.rs +++ b/ethcore/verification/src/lib.rs @@ -30,8 +30,7 @@ pub use self::canon_verifier::CanonVerifier; pub use self::noop_verifier::NoopVerifier; pub use self::queue::{BlockQueue, Config as QueueConfig, VerificationQueue}; -use call_contract::CallContract; -use client_traits::BlockInfo; +use client_traits::VerifyingClient; /// Verifier type. #[derive(Debug, PartialEq, Clone)] @@ -46,7 +45,7 @@ pub enum VerifierType { } /// Create a new verifier based on type. -pub fn new(v: VerifierType) -> Box> { +pub fn new(v: VerifierType) -> Box> { match v { VerifierType::Canon | VerifierType::CanonNoSeal => Box::new(CanonVerifier), VerifierType::Noop => Box::new(NoopVerifier), diff --git a/ethcore/verification/src/verification.rs b/ethcore/verification/src/verification.rs index fb786b7cced..13c18cf139c 100644 --- a/ethcore/verification/src/verification.rs +++ b/ethcore/verification/src/verification.rs @@ -21,7 +21,10 @@ //! 2. Signatures verification done in the queue. //! 3. Final verification against the blockchain done before enactment. -use std::time::{Duration, SystemTime, UNIX_EPOCH}; +use std::{ + collections::HashSet, + time::{Duration, SystemTime, UNIX_EPOCH}, +}; use keccak_hash::keccak; use rlp::Rlp; @@ -30,7 +33,7 @@ use unexpected::{Mismatch, OutOfBounds}; use ethcore_blockchain::BlockProvider; use call_contract::CallContract; -use client_traits::{BlockInfo, VerifyingEngine}; +use client_traits::{BlockInfo, VerifyingEngine, VerifyingClient}; use crate::{ queue::kind::blocks::Unverified, @@ -40,11 +43,11 @@ use common_types::{ 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 @@ -83,7 +86,11 @@ pub fn verify_block_basic(block: &Unverified, engine: &dyn VerifyingEngine, chec /// 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 { +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)?; @@ -119,6 +126,58 @@ pub fn verify_block_unordered(block: Unverified, engine: &dyn VerifyingEngine, c }) } +/// 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 { @@ -178,6 +237,135 @@ pub fn verify_header_params(header: &Header, engine: &dyn VerifyingEngine, is_fu 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); @@ -198,3 +386,5 @@ fn verify_block_integrity(block: &Unverified) -> Result<(), EthcoreError> { } Ok(()) } + + diff --git a/ethcore/verification/src/verifier.rs b/ethcore/verification/src/verifier.rs index a559da8780c..3ab7fb199e5 100644 --- a/ethcore/verification/src/verifier.rs +++ b/ethcore/verification/src/verifier.rs @@ -18,16 +18,12 @@ use call_contract::CallContract; use client_traits::{BlockInfo, VerifyingEngine}; -//use engines::Engine; -use crate::{ -// error::Error, - verification::FullFamilyParams, -}; use common_types::{ errors::EthcoreError, header::Header, }; -//use super::verification; + +use crate::verification::FullFamilyParams; /// Should be used to verify blocks. pub trait Verifier: Send + Sync From fcd75fcea753f3686beebf207216318bfc811e54 Mon Sep 17 00:00:00 2001 From: David Palm Date: Wed, 10 Jul 2019 16:21:57 +0200 Subject: [PATCH 87/87] Use errors from common_types --- ethcore/src/block.rs | 10 +- ethcore/src/client/ancient_import.rs | 9 +- ethcore/src/client/client.rs | 33 +++--- ethcore/src/client/test_client.rs | 3 +- ethcore/src/client/traits.rs | 37 +++--- ethcore/src/engines/authority_round/mod.rs | 14 +-- ethcore/src/engines/basic_authority.rs | 13 +-- ethcore/src/engines/block_reward.rs | 3 +- ethcore/src/engines/clique/block_state.rs | 9 +- ethcore/src/engines/clique/mod.rs | 31 ++--- ethcore/src/engines/clique/tests.rs | 5 +- ethcore/src/engines/clique/util.rs | 3 +- ethcore/src/engines/instant_seal.rs | 2 +- ethcore/src/engines/mod.rs | 26 +---- ethcore/src/engines/null_engine.rs | 2 +- ethcore/src/engines/validator_set/contract.rs | 11 +- ethcore/src/engines/validator_set/mod.rs | 13 ++- ethcore/src/engines/validator_set/multi.rs | 13 ++- .../engines/validator_set/safe_contract.rs | 14 +-- .../src/engines/validator_set/simple_list.rs | 9 +- ethcore/src/error.rs | 106 +----------------- ethcore/src/ethereum/ethash_eng.rs | 15 ++- ethcore/src/executed.rs | 105 ++--------------- ethcore/src/executive.rs | 12 +- ethcore/src/executive_state.rs | 5 +- ethcore/src/externalities.rs | 11 +- ethcore/src/lib.rs | 3 +- ethcore/src/machine.rs | 14 ++- ethcore/src/miner/miner.rs | 3 +- ethcore/src/miner/mod.rs | 12 +- ethcore/src/snapshot/consensus/authority.rs | 57 +++++----- ethcore/src/snapshot/consensus/mod.rs | 11 +- ethcore/src/snapshot/consensus/work.rs | 44 ++++---- ethcore/src/snapshot/io.rs | 7 +- ethcore/src/snapshot/mod.rs | 25 +++-- ethcore/src/snapshot/service.rs | 4 +- ethcore/src/snapshot/tests/proof_of_work.rs | 2 +- ethcore/src/snapshot/tests/state.rs | 8 +- ethcore/src/spec/spec.rs | 4 +- 39 files changed, 251 insertions(+), 457 deletions(-) diff --git a/ethcore/src/block.rs b/ethcore/src/block.rs index 107b1de90fd..19a4a70b6a6 100644 --- a/ethcore/src/block.rs +++ b/ethcore/src/block.rs @@ -39,7 +39,6 @@ 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; @@ -52,6 +51,7 @@ use hash::keccak; use rlp::{RlpStream, Encodable, encode_list}; use types::{ block::PreverifiedBlock, + errors::{EthcoreError as Error, BlockError}, transaction::{SignedTransaction, Error as TransactionError}, header::Header, receipt::{Receipt, TransactionOutcome}, @@ -552,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 4c6d1d5751f..14942ffc43d 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -55,10 +55,7 @@ use client::bad_blocks; use client_traits::{BlockInfo, VerifyingEngine}; use engines::{MAX_UNCLE_AGE, Engine, EpochTransition, ForkChoice, SealingState}; use engines::epoch::PendingTransition; -use error::{ - 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}; @@ -72,9 +69,9 @@ use transaction_ext::Transaction; use types::{ ancestry_action::AncestryAction, BlockNumber, - block::{ImportError, PreverifiedBlock}, + block::PreverifiedBlock, encoded, - engines::EngineError, + errors::{EngineError, ExecutionError, BlockError, EthcoreError, SnapshotError, ImportError}, transaction::{self, LocalizedTransaction, UnverifiedTransaction, SignedTransaction, Action}, filter::Filter, log_entry::LocalizedLogEntry, @@ -90,11 +87,11 @@ 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"); @@ -256,7 +253,7 @@ impl Importer { engine: Arc, message_channel: IoChannel, miner: Arc, - ) -> Result { + ) -> Result { let block_queue = BlockQueue::new( config.queue.clone(), engine.clone(), @@ -397,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); @@ -717,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, @@ -963,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(()), @@ -1158,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); @@ -1175,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/test_client.rs b/ethcore/src/client/test_client.rs index 4bf47c92a52..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; @@ -61,7 +62,7 @@ use client::{ }; 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 422bc3d5cdd..2f99f7eedbd 100644 --- a/ethcore/src/client/traits.rs +++ b/ethcore/src/client/traits.rs @@ -25,33 +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 { diff --git a/ethcore/src/engines/authority_round/mod.rs b/ethcore/src/engines/authority_round/mod.rs index d1c32909955..440efca57fb 100644 --- a/ethcore/src/engines/authority_round/mod.rs +++ b/ethcore/src/engines/authority_round/mod.rs @@ -30,7 +30,6 @@ 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; @@ -48,7 +47,8 @@ use types::{ ancestry_action::AncestryAction, BlockNumber, header::{Header, ExtendedHeader}, - engines::{EngineError, params::CommonParams}, + engines::{params::CommonParams}, + errors::{BlockError, EthcoreError as Error, EngineError}, transaction::{self, UnverifiedTransaction, SignedTransaction}, }; use unexpected::{Mismatch, OutOfBounds}; @@ -995,7 +995,7 @@ impl VerifyingEngine for AuthorityRound { 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)?; + let (validators, set_number) = self.epoch_set(header).map_err(Into::into)?; // Ensure header is from the step after parent. if step == parent_step @@ -1074,7 +1074,7 @@ impl VerifyingEngine for AuthorityRound { // Check the validators. fn verify_block_external(&self, header: &Header) -> Result<(), Error> { - let (validators, set_number) = self.epoch_set(header)?; + 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. @@ -1092,7 +1092,7 @@ impl VerifyingEngine for AuthorityRound { }, _ => {}, } - res + res.map_err(Into::into) } fn verify_transaction_basic(&self, t: &UnverifiedTransaction, header: &Header) -> Result<(), transaction::Error> { @@ -1637,6 +1637,7 @@ mod tests { use types::{ header::Header, engines::params::CommonParams, + errors::{EthcoreError as Error, EngineError}, transaction::{Action, Transaction}, }; use rlp::encode; @@ -1646,9 +1647,8 @@ mod tests { TestNotify }; use spec::Spec; - 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; diff --git a/ethcore/src/engines/basic_authority.rs b/ethcore/src/engines/basic_authority.rs index a9ac9b619e9..b6d9ac0191b 100644 --- a/ethcore/src/engines/basic_authority.rs +++ b/ethcore/src/engines/basic_authority.rs @@ -23,15 +23,15 @@ use ethkey::{self, Signature}; use block::*; 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, - engines::{EngineError, params::CommonParams}, + engines::params::CommonParams, transaction::{self, UnverifiedTransaction, SignedTransaction}, + errors::{EngineError, BlockError, EthcoreError as Error} }; use super::validator_set::{ValidatorSet, SimpleList, new_validator_set}; @@ -105,7 +105,7 @@ impl VerifyingEngine for BasicAuthority { } fn verify_block_external(&self, header: &Header) -> Result<(), Error> { - verify_external(header, &*self.validators) + verify_external(header, &*self.validators).map_err(Into::into) } fn verify_transaction_basic(&self, t: &UnverifiedTransaction, header: &Header) -> Result<(), transaction::Error> { @@ -122,9 +122,6 @@ impl Engine for 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 @@ -152,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) } diff --git a/ethcore/src/engines/block_reward.rs b/ethcore/src/engines/block_reward.rs index 660598aa81c..908fc9ceb87 100644 --- a/ethcore/src/engines/block_reward.rs +++ b/ethcore/src/engines/block_reward.rs @@ -23,12 +23,11 @@ use ethereum_types::{H160, Address, U256}; use std::sync::Arc; use hash::keccak; -use error::Error; use machine::Machine; use trace; use types::{ BlockNumber, - engines::EngineError, + errors::{EngineError, EthcoreError as Error}, }; use super::{SystemOrCodeCall, SystemOrCodeCallKind}; use trace::{Tracer, ExecutiveTracer, Tracing}; diff --git a/ethcore/src/engines/clique/block_state.rs b/ethcore/src/engines/clique/block_state.rs index 4c30627ba90..787dd53dc12 100644 --- a/ethcore/src/engines/clique/block_state.rs +++ b/ethcore/src/engines/clique/block_state.rs @@ -20,14 +20,13 @@ use std::time::{Duration, SystemTime, UNIX_EPOCH}; 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, header::Header, - engines::EngineError, + errors::{BlockError, EthcoreError as Error, EngineError}, }; use unexpected::Mismatch; @@ -92,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!("{} {:?}", @@ -109,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 00d4544e215..b107216aece 100644 --- a/ethcore/src/engines/clique/mod.rs +++ b/ethcore/src/engines/clique/mod.rs @@ -68,10 +68,9 @@ use std::time::{Instant, Duration, SystemTime, UNIX_EPOCH}; use block::ExecutedBlock; use client::{BlockId, EngineClient}; -use client_traits::{VerifyingEngine, error::Error as ClientError}; +use client_traits::VerifyingEngine; use engines::clique::util::{extract_signers, recover_creator}; use engines::{Engine, Seal, SealingState}; -use error::{BlockError, Error}; use ethereum_types::{Address, H64, H160, H256, U256}; use ethkey::Signature; use hash::KECCAK_EMPTY_LIST_RLP; @@ -86,7 +85,8 @@ use time_utils::CheckedSystemTime; use types::{ BlockNumber, header::Header, - engines::{EngineError, params::CommonParams}, + engines::params::CommonParams, + errors::{BlockError, EthcoreError as Error, EngineError}, transaction::{self, SignedTransaction, UnverifiedTransaction}, }; use self::block_state::CliqueBlockState; @@ -365,7 +365,7 @@ impl VerifyingEngine for Clique { self.machine.params() } - fn verify_block_basic(&self, header: &Header) -> Result<(), ClientError> { + 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. @@ -478,7 +478,7 @@ impl VerifyingEngine for Clique { /// 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<(), ClientError> { + fn verify_block_family(&self, header: &Header, parent: &Header) -> Result<(), Error> { // Ignore genesis block. if header.number() == 0 { return Ok(()); @@ -509,9 +509,9 @@ impl VerifyingEngine for Clique { Err(e) => { // todo: these can be BlockError, EngineError and probably some others too match e { - Error::Block(err) => return Err(ClientError::Block(err)), - Error::Engine(err) => return Err(ClientError::Engine(err)), - _ => return Err(ClientError::Other(e.to_string())), + Error::Block(err) => return Err(Error::Block(err)), + Error::Engine(err) => return Err(Error::Engine(err)), + _ => return Err(Error::Other(e.to_string())), } } }; @@ -533,21 +533,6 @@ impl VerifyingEngine for Clique { } } -// todo move to error.rs -impl Into for Error { - fn into(self) -> ClientError { - match self { - Error::Block(e) => ClientError::Block(e), - Error::Engine(e) => ClientError::Engine(e), - Error::State(e) => ClientError::State(e.to_string()), - Error::Import(e) => ClientError::Import(e), - Error::Decoder(e) => ClientError::Decoder(e), - Error::Transaction(e)=> ClientError::Transaction(e), - _ => ClientError::Other(self.to_string()) - } - } -} - impl Engine for Clique { fn name(&self) -> &str { "Clique" } diff --git a/ethcore/src/engines/clique/tests.rs b/ethcore/src/engines/clique/tests.rs index f73e55d0131..ae965cabd1f 100644 --- a/ethcore/src/engines/clique/tests.rs +++ b/ethcore/src/engines/clique/tests.rs @@ -18,13 +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::engines::EngineError; +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 e60e9c0fe5e..afa0a73bf4f 100644 --- a/ethcore/src/engines/clique/util.rs +++ b/ethcore/src/engines/clique/util.rs @@ -17,7 +17,6 @@ use std::collections::BTreeSet; 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; @@ -25,7 +24,7 @@ use parking_lot::RwLock; use rlp::encode; use types::{ header::Header, - engines::EngineError, + errors::{EthcoreError as Error, EngineError}, }; /// How many recovered signature to cache in the memory. diff --git a/ethcore/src/engines/instant_seal.rs b/ethcore/src/engines/instant_seal.rs index eda0ef91958..6a70d0175ed 100644 --- a/ethcore/src/engines/instant_seal.rs +++ b/ethcore/src/engines/instant_seal.rs @@ -20,11 +20,11 @@ use machine::Machine; 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)] diff --git a/ethcore/src/engines/mod.rs b/ethcore/src/engines/mod.rs index d4ba207b131..1cefbad433f 100644 --- a/ethcore/src/engines/mod.rs +++ b/ethcore/src/engines/mod.rs @@ -42,17 +42,11 @@ use std::collections::BTreeMap; use builtin::Builtin; use vm::{EnvInfo, Schedule, CreateContractAddress, CallType, ActionValue}; -use error::Error; use types::{ BlockNumber, ancestry_action::AncestryAction, header::{Header, ExtendedHeader}, - engines::{ -// ForkChoice, // TODO [ToDr] Remove re-export (#10130) -// epoch::{self, Transition as EpochTransition}, - EngineError, - params::CommonParams - }, + errors::{EthcoreError as Error, EngineError}, transaction::{self, UnverifiedTransaction}, }; use snapshot::SnapshotComponents; @@ -208,9 +202,6 @@ pub trait Engine: VerifyingEngine + Sync + Send { /// 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 } @@ -376,11 +367,6 @@ pub trait Engine: VerifyingEngine + 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 { @@ -392,11 +378,6 @@ pub trait Engine: VerifyingEngine + 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) @@ -413,11 +394,6 @@ pub trait Engine: VerifyingEngine + 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) diff --git a/ethcore/src/engines/null_engine.rs b/ethcore/src/engines/null_engine.rs index 4b536d4fe91..aad44f057e8 100644 --- a/ethcore/src/engines/null_engine.rs +++ b/ethcore/src/engines/null_engine.rs @@ -23,10 +23,10 @@ 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)] diff --git a/ethcore/src/engines/validator_set/contract.rs b/ethcore/src/engines/validator_set/contract.rs index 9ea80b64da7..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) } 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 94a0060618c..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; diff --git a/ethcore/src/engines/validator_set/safe_contract.rs b/ethcore/src/engines/validator_set/safe_contract.rs index 0534207eb2f..7ca55aafc97 100644 --- a/ethcore/src/engines/validator_set/safe_contract.rs +++ b/ethcore/src/engines/validator_set/safe_contract.rs @@ -28,7 +28,7 @@ use parking_lot::RwLock; use rlp::{Rlp, RlpStream}; use types::{ header::Header, - engines::EngineError, + errors::{EngineError, EthcoreError, BlockError}, ids::BlockId, log_entry::LogEntry, receipt::Receipt, @@ -147,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)) } @@ -167,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)?)) } @@ -296,7 +296,7 @@ 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(|_| ()) @@ -351,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); @@ -377,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()); } 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 db00b87bcbe..8003612389f 100644 --- a/ethcore/src/error.rs +++ b/ethcore/src/error.rs @@ -16,110 +16,10 @@ //! General error types for use in ethcore. -use std::error; +use types::errors::EthcoreError; -use derive_more::{Display, From}; -use ethkey::Error as EthkeyError; -use ethtrie::TrieError; -use rlp; -use snappy::InvalidInput; -use snapshot::Error as SnapshotError; -use types::{ - engines::EngineError, - transaction::Error as TransactionError, - block::ImportError, -}; - -pub use executed::{ExecutionError, CallError}; -pub use types::block::BlockError; // TODO prolly dont want to re-export +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_eng.rs b/ethcore/src/ethereum/ethash_eng.rs index b2ded4d2830..6dfe0609135 100644 --- a/ethcore/src/ethereum/ethash_eng.rs +++ b/ethcore/src/ethereum/ethash_eng.rs @@ -29,6 +29,7 @@ use types::{ header::Header, engines::{EthashExtensions, params::CommonParams}, transaction::{self, UnverifiedTransaction, SignedTransaction}, + errors::{BlockError, EthcoreError as Error}, }; use unexpected::{OutOfBounds, Mismatch}; @@ -36,7 +37,6 @@ 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; @@ -209,10 +209,10 @@ 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() } } @@ -382,7 +382,8 @@ impl Engine for Ethash { #[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)) + .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> { @@ -503,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/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 edb7fba1cb5..61db6e92997 100644 --- a/ethcore/src/lib.rs +++ b/ethcore/src/lib.rs @@ -154,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; @@ -168,6 +168,7 @@ pub mod spec; 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 c886fb44fdc..bdf799034c6 100644 --- a/ethcore/src/machine.rs +++ b/ethcore/src/machine.rs @@ -24,13 +24,13 @@ use ethereum_types::{U256, H256, Address}; use rlp::Rlp; use types::{ BlockNumber, - transaction::{self, SYSTEM_ADDRESS, UNSIGNED_SENDER, UnverifiedTransaction, SignedTransaction}, header::Header, engines::{ - EngineError, 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}; @@ -39,7 +39,6 @@ use block::ExecutedBlock; use builtin::Builtin; use call_contract::CallContract; use client_traits::BlockInfo; -use error::Error; use executive::Executive; use account_state::{CleanupMode, Substate}; use trace::{NoopTracer, NoopVMTracer}; @@ -354,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 0870c48d482..3b42364384e 100644 --- a/ethcore/src/miner/miner.rs +++ b/ethcore/src/miner/miner.rs @@ -47,6 +47,7 @@ use types::{ BlockNumber, block::Block, header::Header, + errors::{EthcoreError as Error, ExecutionError}, receipt::RichReceipt, client_io_message::ClientIoMessage, }; @@ -58,8 +59,6 @@ use client::{ }; 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/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 9b502e7ef35..3e9823e3ff0 100644 --- a/ethcore/src/snapshot/service.rs +++ b/ethcore/src/snapshot/service.rs @@ -31,11 +31,10 @@ use blockchain::{BlockChain, BlockChainDB, BlockChainDBHandler}; 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::{ client_io_message::ClientIoMessage, + errors::{EthcoreError as Error, SnapshotError, SnapshotError::UnlinkedAncientBlockChain}, ids::BlockId, }; @@ -47,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/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/spec/spec.rs b/ethcore/src/spec/spec.rs index 08601d3d355..8b8dc2f12d4 100644 --- a/ethcore/src/spec/spec.rs +++ b/ethcore/src/spec/spec.rs @@ -30,9 +30,10 @@ use rlp::{Rlp, RlpStream}; use rustc_hex::{FromHex, ToHex}; use types::{ BlockNumber, - encoded, header::Header, + encoded, engines::params::CommonParams, + errors::EthcoreError as Error, }; use vm::{EnvInfo, CallType, ActionValue, ActionParams, ParamsType}; @@ -41,7 +42,6 @@ use engines::{ Engine, NullEngine, InstantSeal, InstantSealParams, BasicAuthority, Clique, AuthorityRound }; -use error::Error; use executive::Executive; use trie_vm_factories::Factories; use machine::Machine;