From f144f88ab6f1fbb6c52f670bacc0803b35e73378 Mon Sep 17 00:00:00 2001 From: David Palm Date: Wed, 3 Jul 2019 15:57:07 +0200 Subject: [PATCH 01/68] 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/68] 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/68] 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/68] 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/68] 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/68] 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/68] =?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/68] 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/68] 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/68] 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/68] 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/68] 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/68] 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/68] 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/68] 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/68] 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/68] 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/68] 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/68] 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/68] 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/68] 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/68] 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/68] 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/68] 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/68] 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/68] 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/68] 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/68] 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/68] 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/68] 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/68] 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/68] 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/68] 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/68] 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/68] 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/68] 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/68] 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/68] 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/68] 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/68] 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/68] 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/68] 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/68] 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/68] 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/68] 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/68] 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/68] 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/68] 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/68] 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/68] 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/68] 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/68] 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/68] 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/68] 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/68] 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/68] 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/68] 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/68] 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/68] 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/68] 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/68] 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/68] 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/68] 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/68] 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/68] 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 994424e000105bd433f7d9a4a1ec37933f825fc6 Mon Sep 17 00:00:00 2001 From: David Palm Date: Mon, 8 Jul 2019 14:14:33 +0200 Subject: [PATCH 66/68] 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 38d4c09f94712a818ea55be1d52601425b54a835 Mon Sep 17 00:00:00 2001 From: David Palm Date: Mon, 8 Jul 2019 18:39:12 +0200 Subject: [PATCH 67/68] Fix unclean merge --- ethcore/factories/Cargo.toml | 15 --------------- 1 file changed, 15 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 77c078caa52..00000000000 --- a/ethcore/factories/Cargo.toml +++ /dev/null @@ -1,15 +0,0 @@ -[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" } From 1bf91f25d1bc6ab4cf8dceacf9735cffb1d9e914 Mon Sep 17 00:00:00 2001 From: David Palm Date: Mon, 8 Jul 2019 18:47:44 +0200 Subject: [PATCH 68/68] Add license --- ethcore/state-db/Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/ethcore/state-db/Cargo.toml b/ethcore/state-db/Cargo.toml index c3e444ec96f..2742522db20 100644 --- a/ethcore/state-db/Cargo.toml +++ b/ethcore/state-db/Cargo.toml @@ -3,6 +3,7 @@ description = "State database" name = "state-db" version = "0.1.0" authors = ["Parity Technologies "] +license = "GPL-3.0" edition = "2018" [dependencies]