From f144f88ab6f1fbb6c52f670bacc0803b35e73378 Mon Sep 17 00:00:00 2001 From: David Palm Date: Wed, 3 Jul 2019 15:57:07 +0200 Subject: [PATCH 1/9] 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 2/9] 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 3/9] 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 4/9] 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 5/9] 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 6/9] 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 e0701e44fac18b150719a1677eba82b38e2792dc Mon Sep 17 00:00:00 2001 From: David Palm Date: Thu, 4 Jul 2019 16:15:18 +0200 Subject: [PATCH 7/9] 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 8/9] 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 9/9] 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]