diff --git a/Cargo.lock b/Cargo.lock index 88ed2ba1515..705e8ca55d4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -12,6 +12,37 @@ dependencies = [ "rlp 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "account-state" +version = "0.1.0" +dependencies = [ + "account-db 0.1.0", + "common-types 0.1.0", + "derive_more 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ethereum-types 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "evm 0.1.0", + "hash-db 0.12.4 (registry+https://github.com/rust-lang/crates.io-index)", + "journaldb 0.2.0", + "keccak-hash 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "keccak-hasher 0.1.1", + "kvdb 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "lru-cache 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "memory-db 0.12.4 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-bytes 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-util-mem 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "patricia-trie-ethereum 0.1.0", + "pod 0.1.0", + "rlp 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rlp_compress 0.1.0", + "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", + "trace 0.1.0", + "trie-db 0.12.4 (registry+https://github.com/rust-lang/crates.io-index)", + "trie-vm-factories 0.1.0", + "vm 0.1.0", +] + [[package]] name = "aes" version = "0.3.2" @@ -612,6 +643,19 @@ dependencies = [ "syn 0.15.26 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "derive_more" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.26 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "difference" version = "1.0.0" @@ -810,6 +854,7 @@ name = "ethcore" version = "1.12.0" dependencies = [ "account-db 0.1.0", + "account-state 0.1.0", "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "blooms-db 0.1.0", "common-types 0.1.0", @@ -850,7 +895,6 @@ dependencies = [ "lru-cache 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "macros 0.1.0", "memory-cache 0.1.0", - "memory-db 0.12.4 (registry+https://github.com/rust-lang/crates.io-index)", "num_cpus 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", "parity-bytes 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "parity-runtime 0.1.0", @@ -858,7 +902,7 @@ dependencies = [ "parity-util-mem 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "patricia-trie-ethereum 0.1.0", - "pod-account 0.1.0", + "pod 0.1.0", "rand 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", "rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "rayon 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -869,18 +913,18 @@ 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", + "trace 0.1.0", "trace-time 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "trie-db 0.12.4 (registry+https://github.com/rust-lang/crates.io-index)", "trie-standardmap 0.12.4 (registry+https://github.com/rust-lang/crates.io-index)", + "trie-vm-factories 0.1.0", "triehash-ethereum 0.2.0", "unexpected 0.1.0", "using_queue 0.1.0", "vm 0.1.0", - "wasm 0.1.0", ] [[package]] @@ -991,6 +1035,7 @@ dependencies = [ name = "ethcore-light" version = "1.12.0" dependencies = [ + "account-state 0.1.0", "bincode 1.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "common-types 0.1.0", "derive_more 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1141,6 +1186,7 @@ dependencies = [ name = "ethcore-private-tx" version = "1.0.0" dependencies = [ + "account-state 0.1.0", "common-types 0.1.0", "derive_more 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.5.13 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1172,6 +1218,7 @@ dependencies = [ "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", "time-utils 0.1.0", "tiny-keccak 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "trace 0.1.0", "transaction-pool 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "trie-db 0.12.4 (registry+https://github.com/rust-lang/crates.io-index)", "url 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1411,6 +1458,7 @@ dependencies = [ name = "evmbin" version = "0.1.0" dependencies = [ + "account-state 0.1.0", "common-types 0.1.0", "docopt 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.5.13 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1420,12 +1468,14 @@ dependencies = [ "evm 0.1.0", "panic_hook 0.1.0", "parity-bytes 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "pod 0.1.0", "pretty_assertions 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", "tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", + "trace 0.1.0", "vm 0.1.0", ] @@ -2786,6 +2836,7 @@ dependencies = [ name = "parity-rpc" version = "1.12.0" dependencies = [ + "account-state 0.1.0", "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "cid 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "common-types 0.1.0", @@ -2826,6 +2877,7 @@ dependencies = [ "parity-updater 1.12.0", "parity-version 2.7.0", "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "patricia-trie-ethereum 0.1.0", "pretty_assertions 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", "rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2839,6 +2891,7 @@ dependencies = [ "tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "tiny-keccak 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-timer 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "trace 0.1.0", "transaction-pool 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "transient-hashmap 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "vm 0.1.0", @@ -3158,7 +3211,7 @@ dependencies = [ ] [[package]] -name = "pod-account" +name = "pod" version = "0.1.0" dependencies = [ "common-types 0.1.0", @@ -3879,29 +3932,6 @@ name = "stable_deref_trait" version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "state-account" -version = "0.1.0" -dependencies = [ - "account-db 0.1.0", - "common-types 0.1.0", - "ethereum-types 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "hash-db 0.12.4 (registry+https://github.com/rust-lang/crates.io-index)", - "journaldb 0.2.0", - "keccak-hash 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "keccak-hasher 0.1.1", - "kvdb 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "lru-cache 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "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)", -] - [[package]] name = "static_assertions" version = "0.2.5" @@ -4327,6 +4357,26 @@ dependencies = [ "fxhash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "trace" +version = "0.1.0" +dependencies = [ + "common-types 0.1.0", + "ethcore 1.12.0", + "ethcore-blockchain 0.1.0", + "ethcore-db 0.1.0", + "ethereum-types 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "evm 0.1.0", + "kvdb 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-bytes 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-util-mem 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rlp 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rlp_derive 0.1.0", + "vm 0.1.0", +] + [[package]] name = "trace-time" version = "0.1.1" @@ -4371,6 +4421,19 @@ dependencies = [ "keccak-hasher 0.12.4 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "trie-vm-factories" +version = "0.1.0" +dependencies = [ + "account-db 0.1.0", + "evm 0.1.0", + "keccak-hasher 0.1.1", + "patricia-trie-ethereum 0.1.0", + "trie-db 0.12.4 (registry+https://github.com/rust-lang/crates.io-index)", + "vm 0.1.0", + "wasm 0.1.0", +] + [[package]] name = "triehash" version = "0.6.0" @@ -4827,6 +4890,7 @@ dependencies = [ "checksum ctr 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "022cd691704491df67d25d006fe8eca083098253c4d43516c2206479c58c6736" "checksum ctrlc 1.1.1 (git+https://github.com/paritytech/rust-ctrlc.git)" = "" "checksum derive_more 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fbe9f11be34f800b3ecaaed0ec9ec2e015d1d0ba0c8644c1310f73d6e8994615" +"checksum derive_more 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a141330240c921ec6d074a3e188a7c7ef95668bb95e7d44fa0e5778ec2a7afe" "checksum difference 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b3304d19798a8e067e48d8e69b2c37f0b5e9b4e462504ad9e27e9f3fce02bba8" "checksum digest 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "03b072242a8cbaf9c145665af9d250c59af3b958f83ed6824e13533cf76d5b90" "checksum digest 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "05f47366984d3ad862010e22c7ce81a7dbcaebbdfb37241a620f8b6596ee135c" diff --git a/ethcore/Cargo.toml b/ethcore/Cargo.toml index a37cc12d920..426fd049d14 100644 --- a/ethcore/Cargo.toml +++ b/ethcore/Cargo.toml @@ -30,6 +30,7 @@ ethereum-types = "0.6.0" ethjson = { path = "../json" } ethkey = { path = "../accounts/ethkey" } evm = { path = "evm" } +trie-vm-factories = { path = "trie-vm-factories" } futures = "0.1" hash-db = "0.12.4" parity-util-mem = "0.1" @@ -46,12 +47,11 @@ log = "0.4" lru-cache = "0.1" macros = { path = "../util/macros" } memory-cache = { path = "../util/memory-cache" } -memory-db = "0.12.4" num_cpus = "1.2" parity-bytes = "0.1" parity-snappy = "0.1" parking_lot = "0.8" -pod-account = { path = "pod-account" } +pod = { path = "pod" } trie-db = "0.12.4" patricia-trie-ethereum = { path = "../util/patricia-trie-ethereum" } rand = "0.6" @@ -61,16 +61,16 @@ rlp_derive = { path = "../util/rlp-derive" } rustc-hex = "1.0" serde = "1.0" serde_derive = "1.0" -state-account = { path = "state-account" } +account-state = { path = "account-state" } stats = { path = "../util/stats" } tempdir = { version = "0.3", optional = true } time-utils = { path = "../util/time-utils" } +trace = { path = "trace" } trace-time = "0.1" triehash-ethereum = { version = "0.2", path = "../util/triehash-ethereum" } unexpected = { path = "../util/unexpected" } using_queue = { path = "../miner/using-queue" } vm = { path = "vm" } -wasm = { path = "wasm" } rand_xorshift = "0.1.1" [dev-dependencies] @@ -108,15 +108,13 @@ evm-debug-tests = ["evm-debug", "evm/evm-debug-tests"] # EVM debug traces are printed. slow-blocks = [] # Run JSON consensus tests. -json-tests = ["env_logger", "test-helpers", "to-pod-full"] +json-tests = ["env_logger", "test-helpers"] # Skip JSON consensus tests with pending issues. ci-skip-tests = [] # Run memory/cpu heavy tests. test-heavy = [] # Compile test helpers test-helpers = ["tempdir", "kvdb-rocksdb", "blooms-db"] -# Enables slow 'to-pod-full' method for use in tests and evmbin. -to-pod-full = [] [[bench]] name = "builtin" diff --git a/ethcore/state-account/Cargo.toml b/ethcore/account-state/Cargo.toml similarity index 69% rename from ethcore/state-account/Cargo.toml rename to ethcore/account-state/Cargo.toml index e50c23fc71e..2b07cc76987 100644 --- a/ethcore/state-account/Cargo.toml +++ b/ethcore/account-state/Cargo.toml @@ -1,28 +1,37 @@ [package] -description = "Ethereum accounts, keeps track of changes to the code and storage." -name = "state-account" +description = "Ethereum accounts, keeps track of changes to the code and storage" +name = "account-state" version = "0.1.0" authors = ["Parity Technologies "] edition = "2018" [dependencies] common-types = { path = "../types"} +derive_more = "0.15.0" ethereum-types = "0.6.0" ethtrie = { package = "patricia-trie-ethereum", path = "../../util/patricia-trie-ethereum" } +evm = { path = "../evm" } +trie-vm-factories = { path = "../trie-vm-factories" } hash-db = "0.12.4" +journaldb = { path = "../../util/journaldb" } keccak-hash = "0.2.0" keccak-hasher = { path = "../../util/keccak-hasher" } kvdb = "0.1.0" log = "0.4" lru-cache = "0.1.2" +memory-db = "0.12.4" parity-bytes = "0.1.0" -pod-account = { path = "../pod-account" } +parity-util-mem = "0.1.0" +parking_lot = "0.8.0" +pod = { path = "../pod" } rlp = "0.4.0" serde = { version = "1.0", features = ["derive"] } +trace = { path = "../trace" } trie-db = "0.12.4" +vm = { path = "../vm" } [dev-dependencies] account-db = { path = "../account-db" } -rlp_compress = { path = "../../util/rlp-compress" } journaldb = { path = "../../util/journaldb" } parity-bytes = "0.1.0" +rlp_compress = { path = "../../util/rlp-compress" } diff --git a/ethcore/state-account/src/lib.rs b/ethcore/account-state/src/account.rs similarity index 98% rename from ethcore/state-account/src/lib.rs rename to ethcore/account-state/src/account.rs index db3ad41473f..c0bab08be89 100644 --- a/ethcore/state-account/src/lib.rs +++ b/ethcore/account-state/src/account.rs @@ -15,24 +15,25 @@ // along with Parity Ethereum. If not, see . //! Single account in the system. -use log::{warn, trace}; +use std::cell::{Cell, RefCell}; +use std::collections::{BTreeMap, HashMap}; use std::fmt; use std::sync::Arc; -use std::collections::{HashMap, BTreeMap}; -use keccak_hash::{KECCAK_EMPTY, KECCAK_NULL_RLP, keccak}; -use ethereum_types::{H256, U256, Address, BigEndianHash}; + +use ethereum_types::{Address, BigEndianHash, H256, U256}; use hash_db::HashDB; -use keccak_hasher::KeccakHasher; +use keccak_hash::{keccak, KECCAK_EMPTY, KECCAK_NULL_RLP}; use kvdb::DBValue; -use parity_bytes::{Bytes, ToPretty}; -use trie_db::{Trie, Recorder}; -use ethtrie::{TrieFactory, TrieDB, SecTrieDB, Result as TrieResult}; -use pod_account::PodAccount; -use rlp::{RlpStream, DecoderError, encode}; +use log::{trace, warn}; use lru_cache::LruCache; -use common_types::basic_account::BasicAccount; +use parity_bytes::{Bytes, ToPretty}; +use rlp::{DecoderError, encode}; +use trie_db::{Recorder, Trie}; -use std::cell::{RefCell, Cell}; +use common_types::basic_account::BasicAccount; +use ethtrie::{Result as TrieResult, SecTrieDB, TrieDB, TrieFactory}; +use keccak_hasher::KeccakHasher; +use pod::PodAccount; const STORAGE_CACHE_ITEMS: usize = 8192; @@ -183,8 +184,8 @@ impl Account { /// NOTE: make sure you use `init_code` on this before `commit`ing. pub fn new_contract(balance: U256, nonce: U256, version: U256, original_storage_root: H256) -> Account { Account { - balance: balance, - nonce: nonce, + balance, + nonce, storage_root: KECCAK_NULL_RLP, storage_cache: Self::empty_storage_cache(), original_storage_cache: if original_storage_root == KECCAK_NULL_RLP { @@ -643,13 +644,16 @@ impl fmt::Debug for Account { #[cfg(test)] mod tests { - use rlp_compress::{compress, decompress, snapshot_swapper}; - use ethereum_types::{H256, Address}; - use journaldb::new_memory_db; + use std::str::FromStr; + + use ethereum_types::{Address, H256}; use parity_bytes::Bytes; - use super::*; + use account_db::*; - use std::str::FromStr; + use journaldb::new_memory_db; + use rlp_compress::{compress, decompress, snapshot_swapper}; + + use super::*; #[test] fn account_compress() { diff --git a/ethcore/src/state/backend.rs b/ethcore/account-state/src/backend.rs similarity index 98% rename from ethcore/src/state/backend.rs rename to ethcore/account-state/src/backend.rs index c9175f26980..563aa1023dc 100644 --- a/ethcore/src/state/backend.rs +++ b/ethcore/account-state/src/backend.rs @@ -21,17 +21,19 @@ //! should become general over time to the point where not even a //! merkle trie is strictly necessary. -use std::collections::{HashSet, HashMap}; +use std::collections::{HashMap, HashSet}; use std::sync::Arc; -use state::Account; -use parking_lot::Mutex; use ethereum_types::{Address, H256}; -use memory_db::{MemoryDB, HashKey}; -use hash_db::{AsHashDB, HashDB, Prefix, EMPTY_PREFIX}; +use hash_db::{AsHashDB, EMPTY_PREFIX, HashDB, Prefix}; use kvdb::DBValue; -use keccak_hasher::KeccakHasher; +use memory_db::{HashKey, MemoryDB}; +use parking_lot::Mutex; + use journaldb::AsKeyedHashDB; +use keccak_hasher::KeccakHasher; + +use crate::account::Account; /// State backend. See module docs for more details. pub trait Backend: Send { diff --git a/ethcore/account-state/src/error.rs b/ethcore/account-state/src/error.rs new file mode 100644 index 00000000000..0a2911f7913 --- /dev/null +++ b/ethcore/account-state/src/error.rs @@ -0,0 +1,35 @@ +// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// This file is part of Parity Ethereum. + +// Parity Ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Ethereum. If not, see . + +//! State related errors + +use derive_more::{Display, From}; + +#[derive(Debug, Display, From)] +pub enum Error { + /// Trie error. + Trie(ethtrie::TrieError), + /// Decoder error. + Decoder(rlp::DecoderError), +} + +impl std::error::Error for Error {} + +impl From> for Error where Error: From { + fn from(err: Box) -> Self { + Error::from(*err) + } +} diff --git a/ethcore/account-state/src/lib.rs b/ethcore/account-state/src/lib.rs new file mode 100644 index 00000000000..3ec3d291ec5 --- /dev/null +++ b/ethcore/account-state/src/lib.rs @@ -0,0 +1,37 @@ +// 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 state +//! This crate contains code used to create, convert, and update Accounts and the code and storage +//! associated with it. It also defines the trait used to construct a backend to build a complete +//! caching state database. +//! Note: the code that needs access to `ethcore` types such as `Machine` and `Executive` is found in +//! the `executive_state` module in `ethcore`. Most tests for the `State` module in this crate are +//! also found in `executive_state` (for the same reason). + +pub mod account; +pub mod backend; +pub mod substate; +pub mod state; +pub mod error; + +pub use { + account::Account, + backend::Backend, + error::Error, + substate::Substate, + state::{State, CleanupMode}, +}; diff --git a/ethcore/account-state/src/state.rs b/ethcore/account-state/src/state.rs new file mode 100644 index 00000000000..0b2775ff6c1 --- /dev/null +++ b/ethcore/account-state/src/state.rs @@ -0,0 +1,1163 @@ +// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// This file is part of Parity Ethereum. + +// Parity Ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Ethereum. If not, see . + +//! A mutable state representation suitable to execute transactions. +//! Generic over a `Backend`. Deals with `Account`s. +//! Unconfirmed sub-states are managed with `checkpoint`s which may be canonicalized +//! or rolled back. + +// NOTE: state tests are found in ethcore/src/executive_state.rs + +use std::{ + cell::{RefCell, RefMut}, + collections::{BTreeMap, BTreeSet, HashMap, HashSet}, + collections::hash_map::Entry, + sync::Arc, + fmt, +}; + +use common_types::{ + state_diff::StateDiff, + basic_account::BasicAccount, +}; +use ethereum_types::{Address, H256, U256}; +use ethtrie::{TrieDB, Result as TrieResult}; +use trie_vm_factories::{Factories, VmFactory}; +use hash_db::HashDB; +use keccak_hash::{KECCAK_EMPTY, KECCAK_NULL_RLP}; +use keccak_hasher::KeccakHasher; +use kvdb::DBValue; +use log::{warn, trace}; +use parity_bytes::Bytes; +use pod::{self, PodAccount, PodState}; +use trie_db::{Trie, TrieError, Recorder}; + +use crate::{ + Error, + account::Account, + backend::Backend, +}; + +#[derive(Eq, PartialEq, Clone, Copy, Debug)] +/// Account modification state. Used to check if the account was +/// Modified in between commits and overall. +enum AccountState { + /// Account was loaded from disk and never modified in this state object. + CleanFresh, + /// Account was loaded from the global cache and never modified. + CleanCached, + /// Account has been modified and is not committed to the trie yet. + /// This is set if any of the account data is changed, including + /// storage and code. + Dirty, + /// Account was modified and committed to the trie. + Committed, +} + +#[derive(Debug)] +/// In-memory copy of the account data. Holds the optional account +/// and the modification status. +/// Account entry can contain existing (`Some`) or non-existing +/// account (`None`) +struct AccountEntry { + /// Account entry. `None` if account known to be non-existent. + account: Option, + /// Unmodified account balance. + old_balance: Option, + /// Entry state. + state: AccountState, +} + +// Account cache item. Contains account data and +// modification state +impl AccountEntry { + fn is_dirty(&self) -> bool { + self.state == AccountState::Dirty + } + + fn exists_and_is_null(&self) -> bool { + self.account.as_ref().map_or(false, |a| a.is_null()) + } + + /// Clone dirty data into new `AccountEntry`. This includes + /// basic account data and modified storage keys. + /// Returns None if clean. + fn clone_if_dirty(&self) -> Option { + match self.is_dirty() { + true => Some(self.clone_dirty()), + false => None, + } + } + + /// Clone dirty data into new `AccountEntry`. This includes + /// basic account data and modified storage keys. + fn clone_dirty(&self) -> AccountEntry { + AccountEntry { + old_balance: self.old_balance, + account: self.account.as_ref().map(Account::clone_dirty), + state: self.state, + } + } + + // Create a new account entry and mark it as dirty. + fn new_dirty(account: Option) -> AccountEntry { + AccountEntry { + old_balance: account.as_ref().map(|a| a.balance().clone()), + account, + state: AccountState::Dirty, + } + } + + // Create a new account entry and mark it as clean. + fn new_clean(account: Option) -> AccountEntry { + AccountEntry { + old_balance: account.as_ref().map(|a| a.balance().clone()), + account, + state: AccountState::CleanFresh, + } + } + + // Create a new account entry and mark it as clean and cached. + fn new_clean_cached(account: Option) -> AccountEntry { + AccountEntry { + old_balance: account.as_ref().map(|a| a.balance().clone()), + account, + state: AccountState::CleanCached, + } + } + + // Replace data with another entry but preserve storage cache. + fn overwrite_with(&mut self, other: AccountEntry) { + self.state = other.state; + match other.account { + Some(acc) => { + if let Some(ref mut ours) = self.account { + ours.overwrite_with(acc); + } else { + self.account = Some(acc); + } + }, + None => self.account = None, + } + } +} + +/// Representation of the entire state of all accounts in the system. +/// +/// `State` can work together with `StateDB` to share account cache. +/// +/// Local cache contains changes made locally and changes accumulated +/// locally from previous commits. Global cache reflects the database +/// state and never contains any changes. +/// +/// Cache items contains account data, or the flag that account does not exist +/// and modification state (see `AccountState`) +/// +/// Account data can be in the following cache states: +/// * In global but not local - something that was queried from the database, +/// but never modified +/// * In local but not global - something that was just added (e.g. new account) +/// * In both with the same value - something that was changed to a new value, +/// but changed back to a previous block in the same block (same State instance) +/// * In both with different values - something that was overwritten with a +/// new value. +/// +/// All read-only state queries check local cache/modifications first, +/// then global state cache. If data is not found in any of the caches +/// it is loaded from the DB to the local cache. +/// +/// **** IMPORTANT ************************************************************* +/// All the modifications to the account data must set the `Dirty` state in the +/// `AccountEntry`. This is done in `require` and `require_or_from`. So just +/// use that. +/// **************************************************************************** +/// +/// Upon destruction all the local cache data propagated into the global cache. +/// Propagated items might be rejected if current state is non-canonical. +/// +/// State checkpointing. +/// +/// A new checkpoint can be created with `checkpoint()`. checkpoints can be +/// created in a hierarchy. +/// When a checkpoint is active all changes are applied directly into +/// `cache` and the original value is copied into an active checkpoint. +/// Reverting a checkpoint with `revert_to_checkpoint` involves copying +/// original values from the latest checkpoint back into `cache`. The code +/// takes care not to overwrite cached storage while doing that. +/// A checkpoint can be discarded with `discard_checkpoint`. All of the original +/// backed-up values are moved into a parent checkpoint (if any). +/// +pub struct State { + db: B, + root: H256, + cache: RefCell>, + // The original account is preserved in + checkpoints: RefCell>>>, + account_start_nonce: U256, + factories: Factories, +} + +#[derive(Copy, Clone)] +enum RequireCache { + None, + CodeSize, + Code, +} + +/// Mode of dealing with null accounts. +#[derive(PartialEq)] +pub enum CleanupMode<'a> { + /// Create accounts which would be null. + ForceCreate, + /// Don't delete null accounts upon touching, but also don't create them. + NoEmpty, + /// Mark all touched accounts. + TrackTouched(&'a mut HashSet
), +} + +/// Provides subset of `State` methods to query state information +pub trait StateInfo { + /// Get the nonce of account `a`. + fn nonce(&self, a: &Address) -> TrieResult; + + /// Get the balance of account `a`. + fn balance(&self, a: &Address) -> TrieResult; + + /// Mutate storage of account `address` so that it is `value` for `key`. + fn storage_at(&self, address: &Address, key: &H256) -> TrieResult; + + /// Get accounts' code. + fn code(&self, a: &Address) -> TrieResult>>; +} + +impl StateInfo for State { + fn nonce(&self, a: &Address) -> TrieResult { State::nonce(self, a) } + fn balance(&self, a: &Address) -> TrieResult { State::balance(self, a) } + fn storage_at(&self, address: &Address, key: &H256) -> TrieResult { State::storage_at(self, address, key) } + fn code(&self, address: &Address) -> TrieResult>> { State::code(self, address) } +} + +const SEC_TRIE_DB_UNWRAP_STR: &'static str = "A state can only be created with valid root. Creating a SecTrieDB with a valid root will not fail. \ + Therefore creating a SecTrieDB with this state's root will not fail."; + +impl State { + /// Creates new state with empty state root + /// Used for tests. + pub fn new(mut db: B, account_start_nonce: U256, factories: Factories) -> State { + let mut root = H256::zero(); + { + // init trie and reset root to null + let _ = factories.trie.create(db.as_hash_db_mut(), &mut root); + } + + State { + db, + root, + cache: RefCell::new(HashMap::new()), + checkpoints: RefCell::new(Vec::new()), + account_start_nonce, + factories, + } + } + + /// Creates new state with existing state root + pub fn from_existing(db: B, root: H256, account_start_nonce: U256, factories: Factories) -> TrieResult> { + if !db.as_hash_db().contains(&root, hash_db::EMPTY_PREFIX) { + return Err(Box::new(TrieError::InvalidStateRoot(root))); + } + + let state = State { + db, + root, + cache: RefCell::new(HashMap::new()), + checkpoints: RefCell::new(Vec::new()), + account_start_nonce, + factories, + }; + + Ok(state) + } + + /// Get a VM factory that can execute on this state. + pub fn vm_factory(&self) -> VmFactory { + self.factories.vm.clone() + } + + /// Create a recoverable checkpoint of this state. Return the checkpoint index. + pub fn checkpoint(&mut self) -> usize { + let checkpoints = self.checkpoints.get_mut(); + let index = checkpoints.len(); + checkpoints.push(HashMap::new()); + index + } + + /// Merge last checkpoint with previous. + pub fn discard_checkpoint(&mut self) { + // merge with previous checkpoint + let last = self.checkpoints.get_mut().pop(); + if let Some(mut checkpoint) = last { + if let Some(ref mut prev) = self.checkpoints.get_mut().last_mut() { + if prev.is_empty() { + **prev = checkpoint; + } else { + for (k, v) in checkpoint.drain() { + prev.entry(k).or_insert(v); + } + } + } + } + } + + /// Revert to the last checkpoint and discard it. + pub fn revert_to_checkpoint(&mut self) { + if let Some(mut checkpoint) = self.checkpoints.get_mut().pop() { + for (k, v) in checkpoint.drain() { + match v { + Some(v) => { + match self.cache.get_mut().entry(k) { + Entry::Occupied(mut e) => { + // Merge checkpointed changes back into the main account + // storage preserving the cache. + e.get_mut().overwrite_with(v); + }, + Entry::Vacant(e) => { + e.insert(v); + } + } + }, + None => { + if let Entry::Occupied(e) = self.cache.get_mut().entry(k) { + if e.get().is_dirty() { + e.remove(); + } + } + } + } + } + } + } + + fn insert_cache(&self, address: &Address, account: AccountEntry) { + // Dirty account which is not in the cache means this is a new account. + // It goes directly into the checkpoint as there's nothing to rever to. + // + // In all other cases account is read as clean first, and after that made + // dirty in and added to the checkpoint with `note_cache`. + let is_dirty = account.is_dirty(); + let old_value = self.cache.borrow_mut().insert(*address, account); + if is_dirty { + if let Some(ref mut checkpoint) = self.checkpoints.borrow_mut().last_mut() { + checkpoint.entry(*address).or_insert(old_value); + } + } + } + + fn note_cache(&self, address: &Address) { + if let Some(ref mut checkpoint) = self.checkpoints.borrow_mut().last_mut() { + checkpoint.entry(*address) + .or_insert_with(|| self.cache.borrow().get(address).map(AccountEntry::clone_dirty)); + } + } + + /// Destroy the current object and return root and database. + pub fn drop(mut self) -> (H256, B) { + self.propagate_to_global_cache(); + (self.root, self.db) + } + + /// Destroy the current object and return single account data. + pub fn into_account(self, account: &Address) -> TrieResult<(Option>, HashMap)> { + // TODO: deconstruct without cloning. + let account = self.require(account, true)?; + Ok((account.code().clone(), account.storage_changes().clone())) + } + + /// Return reference to root + pub fn root(&self) -> &H256 { + &self.root + } + + /// Create a new contract at address `contract`. If there is already an account at the address + /// it will have its code reset, ready for `init_code()`. + pub fn new_contract(&mut self, contract: &Address, balance: U256, nonce_offset: U256, version: U256) -> TrieResult<()> { + let original_storage_root = self.original_storage_root(contract)?; + let (nonce, overflow) = self.account_start_nonce.overflowing_add(nonce_offset); + if overflow { + return Err(Box::new(TrieError::DecoderError(H256::from(*contract), rlp::DecoderError::Custom("Nonce overflow".into())))); + } + self.insert_cache(contract, AccountEntry::new_dirty(Some(Account::new_contract(balance, nonce, version, original_storage_root)))); + Ok(()) + } + + /// Remove an existing account. + pub fn kill_account(&mut self, account: &Address) { + self.insert_cache(account, AccountEntry::new_dirty(None)); + } + + /// Determine whether an account exists. + pub fn exists(&self, a: &Address) -> TrieResult { + // Bloom filter does not contain empty accounts, so it is important here to + // check if account exists in the database directly before EIP-161 is in effect. + self.ensure_cached(a, RequireCache::None, false, |a| a.is_some()) + } + + /// Determine whether an account exists and if not empty. + pub fn exists_and_not_null(&self, a: &Address) -> TrieResult { + self.ensure_cached(a, RequireCache::None, false, |a| a.map_or(false, |a| !a.is_null())) + } + + /// Determine whether an account exists and has code or non-zero nonce. + pub fn exists_and_has_code_or_nonce(&self, a: &Address) -> TrieResult { + self.ensure_cached(a, RequireCache::CodeSize, false, + |a| a.map_or(false, |a| a.code_hash() != KECCAK_EMPTY || *a.nonce() != self.account_start_nonce)) + } + + /// Get the balance of account `a`. + pub fn balance(&self, a: &Address) -> TrieResult { + self.ensure_cached(a, RequireCache::None, true, + |a| a.as_ref().map_or(U256::zero(), |account| *account.balance())) + } + + /// Get the nonce of account `a`. + pub fn nonce(&self, a: &Address) -> TrieResult { + self.ensure_cached(a, RequireCache::None, true, + |a| a.as_ref().map_or(self.account_start_nonce, |account| *account.nonce())) + } + + /// Whether the base storage root of an account remains unchanged. + pub fn is_base_storage_root_unchanged(&self, a: &Address) -> TrieResult { + Ok(self.ensure_cached(a, RequireCache::None, true, + |a| a.as_ref().map(|account| account.is_base_storage_root_unchanged()))? + .unwrap_or(true)) + } + + /// Get the storage root of account `a`. + pub fn storage_root(&self, a: &Address) -> TrieResult> { + self.ensure_cached(a, RequireCache::None, true, + |a| a.as_ref().and_then(|account| account.storage_root())) + } + + /// Get the original storage root since last commit of account `a`. + pub fn original_storage_root(&self, a: &Address) -> TrieResult { + Ok(self.ensure_cached(a, RequireCache::None, true, + |a| a.as_ref().map(|account| account.original_storage_root()))? + .unwrap_or(KECCAK_NULL_RLP)) + } + + + /// Get the value of storage at a specific checkpoint. + pub fn checkpoint_storage_at(&self, start_checkpoint_index: usize, address: &Address, key: &H256) -> TrieResult> { + #[must_use] + enum ReturnKind { + /// Use original storage at value at this address. + OriginalAt, + /// The checkpoint storage value is the same as the checkpoint storage value at the next checkpoint. + SameAsNext, + } + + let kind = { + let checkpoints = self.checkpoints.borrow(); + + if start_checkpoint_index >= checkpoints.len() { + // The checkpoint was not found. Return None. + return Ok(None); + } + + let mut kind = None; + + for checkpoint in checkpoints.iter().skip(start_checkpoint_index) { + match checkpoint.get(address) { + // The account exists at this checkpoint. + Some(Some(AccountEntry { account: Some(ref account), .. })) => { + if let Some(value) = account.cached_storage_at(key) { + return Ok(Some(value)); + } else { + // This account has checkpoint entry, but the key is not in the entry's cache. We can use + // original_storage_at if current account's original storage root is the same as checkpoint + // account's original storage root. Otherwise, the account must be a newly created contract. + if account.base_storage_root() == self.original_storage_root(address)? { + kind = Some(ReturnKind::OriginalAt); + break + } else { + // If account base storage root is different from the original storage root since last + // commit, then it can only be created from a new contract, where the base storage root + // would always be empty. Note that this branch is actually never called, because + // `cached_storage_at` handled this case. + warn!(target: "state", "Trying to get an account's cached storage value, but base storage root does not equal to original storage root! Assuming the value is empty."); + return Ok(Some(H256::zero())); + } + } + }, + // The account didn't exist at that point. Return empty value. + Some(Some(AccountEntry { account: None, .. })) => return Ok(Some(H256::zero())), + // The value was not cached at that checkpoint, meaning it was not modified at all. + Some(None) => { + kind = Some(ReturnKind::OriginalAt); + break + }, + // This key does not have a checkpoint entry. + None => { + kind = Some(ReturnKind::SameAsNext); + }, + } + } + + kind.expect("start_checkpoint_index is checked to be below checkpoints_len; for loop above must have been executed at least once; it will either early return, or set the kind value to Some; qed") + }; + + match kind { + ReturnKind::SameAsNext => { + // If we reached here, all previous SameAsNext failed to early return. It means that the value we want + // to fetch is the same as current. + Ok(Some(self.storage_at(address, key)?)) + }, + ReturnKind::OriginalAt => Ok(Some(self.original_storage_at(address, key)?)), + } + } + + fn storage_at_inner( + &self, address: &Address, key: &H256, f_cached_at: FCachedStorageAt, f_at: FStorageAt, + ) -> TrieResult where + FCachedStorageAt: Fn(&Account, &H256) -> Option, + FStorageAt: Fn(&Account, &dyn HashDB, &H256) -> TrieResult + { + // Storage key search and update works like this: + // 1. If there's an entry for the account in the local cache check for the key and return it if found. + // 2. If there's an entry for the account in the global cache check for the key or load it into that account. + // 3. If account is missing in the global cache load it into the local cache and cache the key there. + + { + // check local cache first without updating + let local_cache = self.cache.borrow_mut(); + let mut local_account = None; + if let Some(maybe_acc) = local_cache.get(address) { + match maybe_acc.account { + Some(ref account) => { + if let Some(value) = f_cached_at(account, key) { + return Ok(value); + } else { + local_account = Some(maybe_acc); + } + }, + _ => return Ok(H256::zero()), + } + } + // check the global cache and and cache storage key there if found, + let trie_res = self.db.get_cached(address, |acc| match acc { + None => Ok(H256::zero()), + Some(a) => { + let account_db = self.factories.accountdb.readonly(self.db.as_hash_db(), a.address_hash(address)); + f_at(a, account_db.as_hash_db(), key) + } + }); + + if let Some(res) = trie_res { + return res; + } + + // otherwise cache the account locally and cache storage key there. + if let Some(ref mut acc) = local_account { + if let Some(ref account) = acc.account { + let account_db = self.factories.accountdb.readonly(self.db.as_hash_db(), account.address_hash(address)); + return f_at(account, account_db.as_hash_db(), key) + } else { + return Ok(H256::zero()) + } + } + } + + // check if the account could exist before any requests to trie + if self.db.is_known_null(address) { return Ok(H256::zero()) } + + // account is not found in the global cache, get from the DB and insert into local + let db = &self.db.as_hash_db(); + let db = self.factories.trie.readonly(db, &self.root).expect(SEC_TRIE_DB_UNWRAP_STR); + let from_rlp = |b: &[u8]| Account::from_rlp(b).expect("decoding db value failed"); + let maybe_acc = db.get_with(address.as_bytes(), from_rlp)?; + let r = maybe_acc.as_ref().map_or(Ok(H256::zero()), |a| { + let account_db = self.factories.accountdb.readonly(self.db.as_hash_db(), a.address_hash(address)); + f_at(a, account_db.as_hash_db(), key) + }); + self.insert_cache(address, AccountEntry::new_clean(maybe_acc)); + r + } + + /// Mutate storage of account `address` so that it is `value` for `key`. + pub fn storage_at(&self, address: &Address, key: &H256) -> TrieResult { + self.storage_at_inner( + address, + key, + |account, key| { account.cached_storage_at(key) }, + |account, db, key| { account.storage_at(db, key) }, + ) + } + + /// Get the value of storage after last state commitment. + pub fn original_storage_at(&self, address: &Address, key: &H256) -> TrieResult { + self.storage_at_inner( + address, + key, + |account, key| { account.cached_original_storage_at(key) }, + |account, db, key| { account.original_storage_at(db, key) }, + ) + } + + /// Get accounts' code. + pub fn code(&self, a: &Address) -> TrieResult>> { + self.ensure_cached(a, RequireCache::Code, true, + |a| a.as_ref().map_or(None, |a| a.code().clone())) + } + + /// Get an account's code hash. + pub fn code_hash(&self, a: &Address) -> TrieResult> { + self.ensure_cached(a, RequireCache::None, true, + |a| a.as_ref().map(|a| a.code_hash())) + } + + /// Get an account's code version. + pub fn code_version(&self, a: &Address) -> TrieResult { + self.ensure_cached(a, RequireCache::None, true, + |a| a.as_ref().map(|a| *a.code_version()).unwrap_or(U256::zero())) + } + + /// Get accounts' code size. + pub fn code_size(&self, a: &Address) -> TrieResult> { + self.ensure_cached(a, RequireCache::CodeSize, true, + |a| a.as_ref().and_then(|a| a.code_size())) + } + + /// Add `incr` to the balance of account `a`. + pub fn add_balance(&mut self, a: &Address, incr: &U256, cleanup_mode: CleanupMode) -> TrieResult<()> { + trace!(target: "state", "add_balance({}, {}): {}", a, incr, self.balance(a)?); + let is_value_transfer = !incr.is_zero(); + if is_value_transfer || (cleanup_mode == CleanupMode::ForceCreate && !self.exists(a)?) { + self.require(a, false)?.add_balance(incr); + } else if let CleanupMode::TrackTouched(set) = cleanup_mode { + if self.exists(a)? { + set.insert(*a); + self.touch(a)?; + } + } + Ok(()) + } + + /// Subtract `decr` from the balance of account `a`. + pub fn sub_balance(&mut self, a: &Address, decr: &U256, cleanup_mode: &mut CleanupMode) -> TrieResult<()> { + trace!(target: "state", "sub_balance({}, {}): {}", a, decr, self.balance(a)?); + if !decr.is_zero() || !self.exists(a)? { + self.require(a, false)?.sub_balance(decr); + } + if let CleanupMode::TrackTouched(ref mut set) = *cleanup_mode { + set.insert(*a); + } + Ok(()) + } + + /// Subtracts `by` from the balance of `from` and adds it to that of `to`. + pub fn transfer_balance(&mut self, from: &Address, to: &Address, by: &U256, mut cleanup_mode: CleanupMode) -> TrieResult<()> { + self.sub_balance(from, by, &mut cleanup_mode)?; + self.add_balance(to, by, cleanup_mode)?; + Ok(()) + } + + /// Increment the nonce of account `a` by 1. + pub fn inc_nonce(&mut self, a: &Address) -> TrieResult<()> { + self.require(a, false).map(|mut x| x.inc_nonce()) + } + + /// Mutate storage of account `a` so that it is `value` for `key`. + pub fn set_storage(&mut self, a: &Address, key: H256, value: H256) -> TrieResult<()> { + trace!(target: "state", "set_storage({}:{:x} to {:x})", a, key, value); + if self.storage_at(a, &key)? != value { + self.require(a, false)?.set_storage(key, value) + } + + Ok(()) + } + + /// Initialise the code of account `a` so that it is `code`. + /// NOTE: Account should have been created with `new_contract`. + pub fn init_code(&mut self, a: &Address, code: Bytes) -> TrieResult<()> { + self.require_or_from(a, true, || Account::new_contract(0.into(), self.account_start_nonce, 0.into(),KECCAK_NULL_RLP), |_| {})?.init_code(code); + Ok(()) + } + + /// Reset the code of account `a` so that it is `code`. + pub fn reset_code(&mut self, a: &Address, code: Bytes) -> TrieResult<()> { + self.require_or_from(a, true, || Account::new_contract(0.into(), self.account_start_nonce, 0.into(), KECCAK_NULL_RLP), |_| {})?.reset_code(code); + Ok(()) + } + + fn touch(&mut self, a: &Address) -> TrieResult<()> { + self.require(a, false)?; + Ok(()) + } + + /// Commits our cached account changes into the trie. + pub fn commit(&mut self) -> Result<(), Error> { + assert!(self.checkpoints.borrow().is_empty()); + // first, commit the sub trees. + let mut accounts = self.cache.borrow_mut(); + for (address, ref mut a) in accounts.iter_mut().filter(|&(_, ref a)| a.is_dirty()) { + if let Some(ref mut account) = a.account { + let addr_hash = account.address_hash(address); + { + let mut account_db = self.factories.accountdb.create(self.db.as_hash_db_mut(), addr_hash); + account.commit_storage(&self.factories.trie, account_db.as_hash_db_mut())?; + account.commit_code(account_db.as_hash_db_mut()); + } + if !account.is_empty() { + self.db.note_non_null_account(address); + } + } + } + + { + let mut trie = self.factories.trie.from_existing(self.db.as_hash_db_mut(), &mut self.root)?; + for (address, ref mut a) in accounts.iter_mut().filter(|&(_, ref a)| a.is_dirty()) { + a.state = AccountState::Committed; + match a.account { + Some(ref mut account) => { + trie.insert(address.as_bytes(), &account.rlp())?; + }, + None => { + trie.remove(address.as_bytes())?; + }, + }; + } + } + + Ok(()) + } + + /// Propagate local cache into shared canonical state cache. + fn propagate_to_global_cache(&mut self) { + let mut addresses = self.cache.borrow_mut(); + trace!("Committing cache {:?} entries", addresses.len()); + for (address, a) in addresses.drain().filter(|&(_, ref a)| a.state == AccountState::Committed || a.state == AccountState::CleanFresh) { + self.db.add_to_account_cache(address, a.account, a.state == AccountState::Committed); + } + } + + /// Clear state cache + pub fn clear(&mut self) { + assert!(self.checkpoints.borrow().is_empty()); + self.cache.borrow_mut().clear(); + } + + /// Remove any touched empty or dust accounts. + pub fn kill_garbage(&mut self, touched: &HashSet
, remove_empty_touched: bool, min_balance: &Option, kill_contracts: bool) -> TrieResult<()> { + let to_kill: HashSet<_> = { + self.cache.borrow().iter().filter_map(|(address, ref entry)| + if touched.contains(address) && // Check all touched accounts + ((remove_empty_touched && entry.exists_and_is_null()) // Remove all empty touched accounts. + || min_balance.map_or(false, |ref balance| entry.account.as_ref().map_or(false, |account| + (account.is_basic() || kill_contracts) // Remove all basic and optionally contract accounts where balance has been decreased. + && account.balance() < balance && entry.old_balance.as_ref().map_or(false, |b| account.balance() < b)))) { + + Some(address.clone()) + } else { None }).collect() + }; + for address in to_kill { + self.kill_account(&address); + } + Ok(()) + } + + /// Populate the state from `accounts`. + /// Used for tests. + pub fn populate_from(&mut self, accounts: PodState) { + assert!(self.checkpoints.borrow().is_empty()); + for (add, acc) in accounts.drain().into_iter() { + self.cache.borrow_mut().insert(add, AccountEntry::new_dirty(Some(Account::from_pod(acc)))); + } + } + + /// Populate a PodAccount map from this state. + fn to_pod_cache(&self) -> PodState { + assert!(self.checkpoints.borrow().is_empty()); + PodState::from(self.cache.borrow().iter().fold(BTreeMap::new(), |mut m, (add, opt)| { + if let Some(ref acc) = opt.account { + m.insert(*add, acc.to_pod()); + } + m + })) + } + + /// Populate a PodAccount map from this state. + /// Warning this is not for real time use. + /// Use of this method requires FatDB mode to be able + /// to iterate on accounts. + pub fn to_pod_full(&self) -> Result { + + assert!(self.checkpoints.borrow().is_empty()); + assert!(self.factories.trie.is_fat()); + + let mut result = BTreeMap::new(); + + let db = &self.db.as_hash_db(); + let trie = self.factories.trie.readonly(db, &self.root)?; + + // put trie in cache + for item in trie.iter()? { + if let Ok((addr, _dbval)) = item { + let address = Address::from_slice(&addr); + let _ = self.require(&address, true); + } + } + + // Resolve missing part + for (add, opt) in self.cache.borrow().iter() { + if let Some(ref acc) = opt.account { + let pod_account = self.account_to_pod_account(acc, add)?; + result.insert(add.clone(), pod_account); + } + } + + Ok(PodState::from(result)) + } + + /// Create a PodAccount from an account. + /// Differs from existing method by including all storage + /// values of the account to the PodAccount. + /// This function is only intended for use in small tests or with fresh accounts. + /// It requires FatDB. + fn account_to_pod_account(&self, account: &Account, address: &Address) -> Result { + use ethereum_types::BigEndianHash; + assert!(self.factories.trie.is_fat()); + + let mut pod_storage = BTreeMap::new(); + let addr_hash = account.address_hash(address); + let accountdb = self.factories.accountdb.readonly(self.db.as_hash_db(), addr_hash); + let root = account.base_storage_root(); + + let accountdb = &accountdb.as_hash_db(); + let trie = self.factories.trie.readonly(accountdb, &root)?; + for o_kv in trie.iter()? { + if let Ok((key, val)) = o_kv { + pod_storage.insert( + H256::from_slice(&key[..]), + BigEndianHash::from_uint( + &rlp::decode::(&val[..]).expect("Decoded from trie which was encoded from the same type; qed") + ), + ); + } + } + + let mut pod_account = account.to_pod(); + // cached one first + pod_storage.append(&mut pod_account.storage); + pod_account.storage = pod_storage; + Ok(pod_account) + } + + /// Populate a PodAccount map from this state, with another state as the account and storage query. + fn to_pod_diff(&mut self, query: &State) -> TrieResult { + assert!(self.checkpoints.borrow().is_empty()); + + // Merge PodAccount::to_pod for cache of self and `query`. + let all_addresses = self.cache.borrow().keys().cloned() + .chain(query.cache.borrow().keys().cloned()) + .collect::>(); + + Ok(PodState::from(all_addresses.into_iter().fold(Ok(BTreeMap::new()), |m: TrieResult<_>, address| { + let mut m = m?; + + let account = self.ensure_cached(&address, RequireCache::Code, true, |acc| { + acc.map(|acc| { + // Merge all modified storage keys. + let all_keys = { + let self_keys = acc.storage_changes().keys().cloned() + .collect::>(); + + if let Some(ref query_storage) = query.cache.borrow().get(&address) + .and_then(|opt| { + Some(opt.account.as_ref()?.storage_changes().keys().cloned() + .collect::>()) + }) + { + self_keys.union(&query_storage).cloned().collect::>() + } else { + self_keys.into_iter().collect::>() + } + }; + + // Storage must be fetched after ensure_cached to avoid borrow problem. + (*acc.balance(), *acc.nonce(), all_keys, acc.code().map(|x| x.to_vec()), *acc.code_version()) + }) + })?; + + if let Some((balance, nonce, storage_keys, code, version)) = account { + let storage = storage_keys.into_iter().fold(Ok(BTreeMap::new()), |s: TrieResult<_>, key| { + let mut s = s?; + + s.insert(key, self.storage_at(&address, &key)?); + Ok(s) + })?; + + m.insert(address, PodAccount { + balance, nonce, storage, code, version + }); + } + + Ok(m) + })?)) + } + + /// Returns a `StateDiff` describing the difference from `orig` to `self`. + /// Consumes self. + pub fn diff_from(&self, mut orig: State) -> TrieResult { + let pod_state_post = self.to_pod_cache(); + let pod_state_pre = orig.to_pod_diff(self)?; + Ok(pod::state::diff_pod(&pod_state_pre, &pod_state_post)) + } + + /// Load required account data from the databases. Returns whether the cache succeeds. + #[must_use] + fn update_account_cache(require: RequireCache, account: &mut Account, state_db: &B, db: &dyn HashDB) -> bool { + if let RequireCache::None = require { + return true; + } + + if account.is_cached() { + return true; + } + + // if there's already code in the global cache, always cache it localy + let hash = account.code_hash(); + match state_db.get_cached_code(&hash) { + Some(code) => { + account.cache_given_code(code); + true + }, + None => match require { + RequireCache::None => true, + RequireCache::Code => { + if let Some(code) = account.cache_code(db) { + // propagate code loaded from the database to + // the global code cache. + state_db.cache_code(hash, code); + true + } else { + false + } + }, + RequireCache::CodeSize => { + account.cache_code_size(db) + } + } + } + } + + /// Check caches for required data + /// First searches for account in the local, then the shared cache. + /// Populates local cache if nothing found. + fn ensure_cached(&self, a: &Address, require: RequireCache, check_null: bool, f: F) -> TrieResult + where F: Fn(Option<&Account>) -> U { + // check local cache first + if let Some(ref mut maybe_acc) = self.cache.borrow_mut().get_mut(a) { + if let Some(ref mut account) = maybe_acc.account { + let accountdb = self.factories.accountdb.readonly(self.db.as_hash_db(), account.address_hash(a)); + if Self::update_account_cache(require, account, &self.db, accountdb.as_hash_db()) { + return Ok(f(Some(account))); + } else { + return Err(Box::new(TrieError::IncompleteDatabase(H256::from(*a)))); + } + } + return Ok(f(None)); + } + // check global cache + let result = self.db.get_cached(a, |mut acc| { + if let Some(ref mut account) = acc { + let accountdb = self.factories.accountdb.readonly(self.db.as_hash_db(), account.address_hash(a)); + if !Self::update_account_cache(require, account, &self.db, accountdb.as_hash_db()) { + return Err(Box::new(TrieError::IncompleteDatabase(H256::from(*a)))); + } + } + Ok(f(acc.map(|a| &*a))) + }); + match result { + Some(r) => Ok(r?), + None => { + // first check if it is not in database for sure + if check_null && self.db.is_known_null(a) { return Ok(f(None)); } + + // not found in the global cache, get from the DB and insert into local + let db = &self.db.as_hash_db(); + let db = self.factories.trie.readonly(db, &self.root)?; + let from_rlp = |b: &[u8]| Account::from_rlp(b).expect("decoding db value failed"); + let mut maybe_acc = db.get_with(a.as_bytes(), from_rlp)?; + if let Some(ref mut account) = maybe_acc.as_mut() { + let accountdb = self.factories.accountdb.readonly(self.db.as_hash_db(), account.address_hash(a)); + if !Self::update_account_cache(require, account, &self.db, accountdb.as_hash_db()) { + return Err(Box::new(TrieError::IncompleteDatabase(H256::from(*a)))); + } + } + let r = f(maybe_acc.as_ref()); + self.insert_cache(a, AccountEntry::new_clean(maybe_acc)); + Ok(r) + } + } + } + + /// Pull account `a` in our cache from the trie DB. `require_code` requires that the code be cached, too. + pub fn require<'a>(&'a self, a: &Address, require_code: bool) -> TrieResult> { + self.require_or_from(a, require_code, || Account::new_basic(0u8.into(), self.account_start_nonce), |_| {}) + } + + /// Pull account `a` in our cache from the trie DB. `require_code` requires that the code be cached, too. + /// If it doesn't exist, make account equal the evaluation of `default`. + pub fn require_or_from<'a, F, G>(&'a self, a: &Address, require_code: bool, default: F, not_default: G) -> TrieResult> + where F: FnOnce() -> Account, G: FnOnce(&mut Account), + { + let contains_key = self.cache.borrow().contains_key(a); + if !contains_key { + match self.db.get_cached_account(a) { + Some(acc) => self.insert_cache(a, AccountEntry::new_clean_cached(acc)), + None => { + let maybe_acc = if !self.db.is_known_null(a) { + let db = &self.db.as_hash_db(); + let db = self.factories.trie.readonly(db, &self.root)?; + let from_rlp = |b:&[u8]| { Account::from_rlp(b).expect("decoding db value failed") }; + AccountEntry::new_clean(db.get_with(a.as_bytes(), from_rlp)?) + } else { + AccountEntry::new_clean(None) + }; + self.insert_cache(a, maybe_acc); + } + } + } + self.note_cache(a); + + // at this point the entry is guaranteed to be in the cache. + let mut account = RefMut::map(self.cache.borrow_mut(), |c| { + let entry = c.get_mut(a).expect("entry known to exist in the cache; qed"); + + match &mut entry.account { + &mut Some(ref mut acc) => not_default(acc), + slot => *slot = Some(default()), + } + + // set the dirty flag after changing account data. + entry.state = AccountState::Dirty; + entry.account.as_mut().expect("Required account must always exist; qed") + }); + + if require_code { + let addr_hash = account.address_hash(a); + let accountdb = self.factories.accountdb.readonly(self.db.as_hash_db(), addr_hash); + + if !Self::update_account_cache(RequireCache::Code, &mut account, &self.db, accountdb.as_hash_db()) { + return Err(Box::new(TrieError::IncompleteDatabase(H256::from(*a)))) + } + } + + Ok(account) + } + + /// Replace account code and storage. Creates account if it does not exist. + pub fn patch_account(&self, a: &Address, code: Arc, storage: HashMap) -> TrieResult<()> { + Ok(self.require(a, false)?.reset_code_and_storage(code, storage)) + } +} + +// State proof implementations; useful for light client protocols. +impl State { + /// Prove an account's existence or nonexistence in the state trie. + /// Returns a merkle proof of the account's trie node omitted or an encountered trie error. + /// If the account doesn't exist in the trie, prove that and return defaults. + /// Requires a secure trie to be used for accurate results. + /// `account_key` == keccak(address) + pub fn prove_account(&self, account_key: H256) -> TrieResult<(Vec, BasicAccount)> { + let mut recorder = Recorder::new(); + let db = &self.db.as_hash_db(); + let trie = TrieDB::new(db, &self.root)?; + let maybe_account: Option = { + let panicky_decoder = |bytes: &[u8]| { + ::rlp::decode(bytes).unwrap_or_else(|_| panic!("prove_account, could not query trie for account key={}", &account_key)) + }; + let query = (&mut recorder, panicky_decoder); + trie.get_with(account_key.as_bytes(), query)? + }; + let account = maybe_account.unwrap_or_else(|| BasicAccount { + balance: 0.into(), + nonce: self.account_start_nonce, + code_hash: KECCAK_EMPTY, + storage_root: KECCAK_NULL_RLP, + code_version: 0.into(), + }); + + Ok((recorder.drain().into_iter().map(|r| r.data).collect(), account)) + } + + /// Prove an account's storage key's existence or nonexistence in the state. + /// Returns a merkle proof of the account's storage trie. + /// Requires a secure trie to be used for correctness. + /// `account_key` == keccak(address) + /// `storage_key` == keccak(key) + pub fn prove_storage(&self, account_key: H256, storage_key: H256) -> TrieResult<(Vec, H256)> { + // TODO: probably could look into cache somehow but it's keyed by + // address, not keccak(address). + let db = &self.db.as_hash_db(); + let trie = TrieDB::new(db, &self.root)?; + let from_rlp = |b: &[u8]| Account::from_rlp(b).expect("decoding db value failed"); + let acc = match trie.get_with(account_key.as_bytes(), from_rlp)? { + Some(acc) => acc, + None => return Ok((Vec::new(), H256::zero())), + }; + + let account_db = self.factories.accountdb.readonly(self.db.as_hash_db(), account_key); + acc.prove_storage(account_db.as_hash_db(), storage_key) + } +} + +impl fmt::Debug for State { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{:?}", self.cache.borrow()) + } +} + +impl State { + /// Get a reference to the underlying state DB. + pub fn db(&self) -> &B { + &self.db + } +} + +//// TODO: cloning for `State` shouldn't be possible in general; Remove this and use +//// checkpoints where possible. +impl Clone for State { + fn clone(&self) -> State { + let cache = { + let mut cache: HashMap = HashMap::new(); + for (key, val) in self.cache.borrow().iter() { + if let Some(entry) = val.clone_if_dirty() { + cache.insert(key.clone(), entry); + } + } + cache + }; + + State { + db: self.db.clone(), + root: self.root.clone(), + cache: RefCell::new(cache), + checkpoints: RefCell::new(Vec::new()), + account_start_nonce: self.account_start_nonce.clone(), + factories: self.factories.clone(), + } + } +} diff --git a/ethcore/src/state/substate.rs b/ethcore/account-state/src/substate.rs similarity index 93% rename from ethcore/src/state/substate.rs rename to ethcore/account-state/src/substate.rs index cb6981437e7..54d982ffc04 100644 --- a/ethcore/src/state/substate.rs +++ b/ethcore/account-state/src/substate.rs @@ -16,10 +16,12 @@ //! Execution environment substate. use std::collections::HashSet; + +use common_types::log_entry::LogEntry; use ethereum_types::Address; -use types::log_entry::LogEntry; -use evm::{Schedule, CleanDustMode}; -use super::CleanupMode; +use evm::{CleanDustMode, Schedule}; + +use crate::state::CleanupMode; /// State changes which should be applied in finalize, /// after transaction is fully executed. @@ -68,8 +70,9 @@ impl Substate { #[cfg(test)] mod tests { - use super::{Substate, Address}; - use types::log_entry::LogEntry; + use common_types::log_entry::LogEntry; + use ethereum_types::Address; + use super::Substate; #[test] fn created() { diff --git a/ethcore/src/client/trace.rs b/ethcore/blockchain/src/database_extras.rs similarity index 61% rename from ethcore/src/client/trace.rs rename to ethcore/blockchain/src/database_extras.rs index 7be957b33ec..b1470f35374 100644 --- a/ethcore/src/client/trace.rs +++ b/ethcore/blockchain/src/database_extras.rs @@ -14,14 +14,27 @@ // You should have received a copy of the GNU General Public License // along with Parity Ethereum. If not, see . -//! Bridge between Tracedb and Blockchain. +//! Provides a `DatabaseExtras` trait that defines an interface to query for block data not +//! contained in a TraceDB. -use blockchain::{BlockChain, BlockProvider, TransactionAddress}; +use common_types::BlockNumber; use ethereum_types::H256; -use trace::DatabaseExtras as TraceDatabaseExtras; -use types::BlockNumber; +use ethcore_db::keys::TransactionAddress; -impl TraceDatabaseExtras for BlockChain { +use crate::blockchain::{BlockProvider, BlockChain}; + +/// `DatabaseExtras` provides an interface to query extra data which is not stored in TraceDB, +/// but necessary to work correctly. +pub trait DatabaseExtras { + /// Returns hash of given block number. + fn block_hash(&self, block_number: BlockNumber) -> Option; + + /// Returns hash of transaction at given position. + fn transaction_hash(&self, block_number: BlockNumber, tx_position: usize) -> Option; +} + +/// Bridge between TraceDB and Blockchain. +impl DatabaseExtras for BlockChain { fn block_hash(&self, block_number: BlockNumber) -> Option { (self as &dyn BlockProvider).block_hash(block_number) } @@ -30,7 +43,7 @@ impl TraceDatabaseExtras for BlockChain { (self as &dyn BlockProvider).block_hash(block_number) .and_then(|block_hash| { let tx_address = TransactionAddress { - block_hash: block_hash, + block_hash, index: tx_position }; self.transaction(&tx_address) diff --git a/ethcore/blockchain/src/lib.rs b/ethcore/blockchain/src/lib.rs index 004817138de..29b3a1808ae 100644 --- a/ethcore/blockchain/src/lib.rs +++ b/ethcore/blockchain/src/lib.rs @@ -28,13 +28,18 @@ mod cache; mod config; mod import_route; mod update; +mod database_extras; pub mod generator; -pub use self::blockchain::{BlockProvider, BlockChain, BlockChainDB, BlockChainDBHandler}; -pub use self::cache::CacheSize; -pub use self::config::Config; -pub use self::import_route::ImportRoute; -pub use self::update::ExtrasInsert; +pub use crate::{ + blockchain::{BlockProvider, BlockChain, BlockChainDB, BlockChainDBHandler}, + cache::CacheSize, + config::Config, + database_extras::DatabaseExtras, + import_route::ImportRoute, + update::ExtrasInsert, +}; pub use ethcore_db::keys::{BlockReceipts, BlockDetails, TransactionAddress, BlockNumberKey}; pub use common_types::tree_route::TreeRoute; + diff --git a/ethcore/light/Cargo.toml b/ethcore/light/Cargo.toml index be6f1b1640f..90507a5158d 100644 --- a/ethcore/light/Cargo.toml +++ b/ethcore/light/Cargo.toml @@ -35,6 +35,7 @@ itertools = "0.5" bincode = "1.1" serde = "1.0" serde_derive = "1.0" +account-state = { path = "../account-state" } parking_lot = "0.8" stats = { path = "../../util/stats" } keccak-hash = "0.2.0" diff --git a/ethcore/light/src/lib.rs b/ethcore/light/src/lib.rs index d169c6925e2..dadb90c66b0 100644 --- a/ethcore/light/src/lib.rs +++ b/ethcore/light/src/lib.rs @@ -89,6 +89,7 @@ extern crate triehash_ethereum as triehash; extern crate kvdb; extern crate memory_cache; extern crate derive_more; +extern crate account_state; #[cfg(test)] extern crate kvdb_memorydb; diff --git a/ethcore/light/src/on_demand/request.rs b/ethcore/light/src/on_demand/request.rs index 1efed6e33c3..a7e2eb809ce 100644 --- a/ethcore/light/src/on_demand/request.rs +++ b/ethcore/light/src/on_demand/request.rs @@ -25,7 +25,8 @@ use common_types::encoded; use common_types::receipt::Receipt; use common_types::transaction::SignedTransaction; use ethcore::engines::{Engine, StateDependentProof}; -use ethcore::state::{self, ProvedExecution}; +use ethcore::executive_state::{ProvedExecution, self}; +use account_state; use ethereum_types::{H256, U256, Address}; use ethtrie::{TrieError, TrieDB}; use hash::{KECCAK_NULL_RLP, KECCAK_EMPTY, KECCAK_EMPTY_LIST_RLP, keccak}; @@ -1042,7 +1043,7 @@ impl TransactionProof { let mut env_info = self.env_info.clone(); env_info.gas_limit = self.tx.gas; - let proved_execution = state::check_proof( + let proved_execution = executive_state::check_proof( state_items, root, &self.tx, diff --git a/ethcore/pod-account/Cargo.toml b/ethcore/pod/Cargo.toml similarity index 89% rename from ethcore/pod-account/Cargo.toml rename to ethcore/pod/Cargo.toml index 23d752489e1..cef7d8542e0 100644 --- a/ethcore/pod-account/Cargo.toml +++ b/ethcore/pod/Cargo.toml @@ -1,6 +1,6 @@ [package] -description = "Account system expressed in Plain Old Data." -name = "pod-account" +description = "State/Account system expressed in Plain Old Data." +name = "pod" version = "0.1.0" authors = ["Parity Technologies "] edition = "2018" diff --git a/ethcore/pod-account/src/lib.rs b/ethcore/pod/src/account.rs similarity index 100% rename from ethcore/pod-account/src/lib.rs rename to ethcore/pod/src/account.rs diff --git a/ethcore/pod/src/lib.rs b/ethcore/pod/src/lib.rs new file mode 100644 index 00000000000..21a291508d4 --- /dev/null +++ b/ethcore/pod/src/lib.rs @@ -0,0 +1,23 @@ +// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// This file is part of Parity Ethereum. + +// Parity Ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Ethereum. If not, see . + +pub mod account; +pub mod state; + +pub use { + account::PodAccount, + state::PodState, +}; diff --git a/ethcore/src/pod_state.rs b/ethcore/pod/src/state.rs similarity index 93% rename from ethcore/src/pod_state.rs rename to ethcore/pod/src/state.rs index 3cc3921ac84..bfee964b8a4 100644 --- a/ethcore/src/pod_state.rs +++ b/ethcore/pod/src/state.rs @@ -19,9 +19,11 @@ use std::collections::BTreeMap; use ethereum_types::{H256, Address}; use triehash::sec_trie_root; -use pod_account::{self, PodAccount}; -use types::state_diff::StateDiff; +use common_types::state_diff::StateDiff; use ethjson; +use serde::Serialize; + +use crate::account::PodAccount; /// State of all accounts in the system expressed in Plain Old Data. #[derive(Debug, Clone, PartialEq, Eq, Default, Serialize)] @@ -68,7 +70,7 @@ pub fn diff_pod(pre: &PodState, post: &PodState) -> StateDiff { StateDiff { raw: pre.0.keys() .chain(post.0.keys()) - .filter_map(|acc| pod_account::diff_pod(pre.0.get(acc), post.0.get(acc)).map(|d| (*acc, d))) + .filter_map(|acc| crate::account::diff_pod(pre.0.get(acc), post.0.get(acc)).map(|d| (*acc, d))) .collect() } } @@ -76,10 +78,14 @@ pub fn diff_pod(pre: &PodState, post: &PodState) -> StateDiff { #[cfg(test)] mod test { use std::collections::BTreeMap; - use pod_account::PodAccount; - use types::account_diff::{AccountDiff, Diff}; - use types::state_diff::StateDiff; - use super::{PodState, Address}; + use common_types::{ + account_diff::{AccountDiff, Diff}, + state_diff::StateDiff, + }; + use ethereum_types::Address; + use crate::account::PodAccount; + use super::PodState; + use macros::map; #[test] fn create_delete() { diff --git a/ethcore/private-tx/Cargo.toml b/ethcore/private-tx/Cargo.toml index 31d0fe9331a..3f577a9fd1a 100644 --- a/ethcore/private-tx/Cargo.toml +++ b/ethcore/private-tx/Cargo.toml @@ -35,8 +35,10 @@ rustc-hex = "1.0" serde = "1.0" serde_derive = "1.0" serde_json = "1.0" +account-state = { path = "../account-state" } time-utils = { path = "../../util/time-utils" } tiny-keccak = "1.4" +trace = { path = "../trace" } transaction-pool = "2.0" url = "1" diff --git a/ethcore/private-tx/src/lib.rs b/ethcore/private-tx/src/lib.rs index 2f23de3fc86..5da4b86c0e2 100644 --- a/ethcore/private-tx/src/lib.rs +++ b/ethcore/private-tx/src/lib.rs @@ -46,7 +46,9 @@ extern crate rlp; extern crate serde_derive; extern crate serde; extern crate serde_json; +extern crate account_state; extern crate rustc_hex; +extern crate trace; extern crate transaction_pool as txpool; extern crate url; #[macro_use] @@ -92,8 +94,9 @@ use ethcore::client::{ Call, BlockInfo }; use ethcore::miner::{self, Miner, MinerService, pool_client::NonceCache}; -use ethcore::{state, state_db}; -use ethcore::trace::{Tracer, VMTracer}; +use ethcore::StateDB; +use account_state::State; +use trace::{Tracer, VMTracer}; use call_contract::CallContract; use rustc_hex::FromHex; use ethabi::FunctionOutputDecoder; @@ -539,7 +542,7 @@ impl Provider { raw } - fn patch_account_state(&self, contract_address: &Address, block: BlockId, state: &mut state::State) -> Result<(), Error> { + fn patch_account_state(&self, contract_address: &Address, block: BlockId, state: &mut State) -> Result<(), Error> { let contract_code = Arc::new(self.get_decrypted_code(contract_address, block)?); let contract_state = self.get_decrypted_state(contract_address, block)?; trace!(target: "privatetx", "Patching contract at {:?}, code: {:?}, state: {:?}", contract_address, contract_code, contract_state); diff --git a/ethcore/private-tx/tests/private_contract.rs b/ethcore/private-tx/tests/private_contract.rs index 3bb854285b3..75d73dda4b4 100644 --- a/ethcore/private-tx/tests/private_contract.rs +++ b/ethcore/private-tx/tests/private_contract.rs @@ -35,7 +35,7 @@ use types::ids::BlockId; use types::transaction::{Transaction, Action}; use ethcore::CreateContractAddress; use ethcore::client::BlockChainClient; -use ethcore::executive::{contract_address}; +use ethcore::executive::contract_address; use ethcore::miner::Miner; use ethcore::test_helpers::{generate_dummy_client, push_block_with_transactions}; use ethkey::{Secret, KeyPair, Signature}; diff --git a/ethcore/src/block.rs b/ethcore/src/block.rs index 4b22633a977..ef8e27db15f 100644 --- a/ethcore/src/block.rs +++ b/ethcore/src/block.rs @@ -22,7 +22,7 @@ //! and can be appended to with transactions and uncles. //! //! When ready, `OpenBlock` can be closed and turned into a `ClosedBlock`. A `ClosedBlock` can -//! be re-opend again by a miner under certain circumstances. On block close, state commit is +//! be re-opened again by a miner under certain circumstances. On block close, state commit is //! performed. //! //! `LockedBlock` is a version of a `ClosedBlock` that cannot be reopened. It can be sealed @@ -40,9 +40,9 @@ use ethereum_types::{H256, U256, Address, Bloom}; use engines::Engine; use error::{Error, BlockError}; -use factory::Factories; +use trie_vm_factories::Factories; use state_db::StateDB; -use state::State; +use account_state::State; use trace::Tracing; use triehash::ordered_trie_root; use unexpected::{Mismatch, OutOfBounds}; @@ -54,6 +54,7 @@ use rlp::{RlpStream, Encodable, encode_list}; use types::transaction::{SignedTransaction, Error as TransactionError}; use types::header::Header; use types::receipt::{Receipt, TransactionOutcome}; +use executive_state::ExecutiveState; /// Block that is ready for transactions to be added. /// @@ -550,7 +551,7 @@ mod tests { use engines::Engine; use vm::LastHashes; use error::Error; - use factory::Factories; + use trie_vm_factories::Factories; use state_db::StateDB; use ethereum_types::Address; use std::sync::Arc; diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index c924fb784a4..92a63b5d74b 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -66,11 +66,12 @@ use error::{ Error as EthcoreError, EthcoreResult, }; use executive::{Executive, Executed, TransactOptions, contract_address}; -use factory::{Factories, VmFactory}; +use trie_vm_factories::{Factories, VmFactory}; use miner::{Miner, MinerService}; use snapshot::{self, io as snapshot_io, SnapshotClient}; use spec::Spec; -use state::{self, State}; +use account_state::State; +use executive_state; use state_db::StateDB; use trace::{self, TraceDB, ImportRequest as TraceImportRequest, LocalizedTrace, Database as TraceDatabase}; use transaction_ext::Transaction; @@ -622,7 +623,7 @@ impl Importer { let call = move |addr, data| { let mut state_db = state_db.boxed_clone(); - let backend = ::state::backend::Proving::new(state_db.as_hash_db_mut()); + let backend = account_state::backend::Proving::new(state_db.as_hash_db_mut()); let transaction = client.contract_call_tx(BlockId::Hash(*header.parent_hash()), addr, data); @@ -2540,7 +2541,7 @@ impl ProvingBlockChainClient for Client { env_info.gas_limit = transaction.gas.clone(); let mut jdb = self.state_db.read().journal_db().boxed_clone(); - state::prove_transaction_virtual( + executive_state::prove_transaction_virtual( jdb.as_hash_db_mut(), header.state_root().clone(), &transaction, diff --git a/ethcore/src/client/evm_test_client.rs b/ethcore/src/client/evm_test_client.rs index 1e56e8c2d99..f1cb591c02d 100644 --- a/ethcore/src/client/evm_test_client.rs +++ b/ethcore/src/client/evm_test_client.rs @@ -19,14 +19,18 @@ use std::fmt; use std::sync::Arc; use ethereum_types::{H256, U256, H160}; -use {factory, journaldb, trie, kvdb_memorydb}; +use {trie_vm_factories, journaldb, trie, kvdb_memorydb}; use kvdb::{self, KeyValueDB}; -use {state, state_db, client, executive, trace, db, spec, pod_state}; +use {state_db, client, executive, trace, db, spec}; +use pod::PodState; use types::{log_entry, receipt, transaction}; -use factory::Factories; +use trie_vm_factories::Factories; use evm::{VMType, FinalizationResult}; use vm::{self, ActionParams}; use ethtrie; +use account_state::{CleanupMode, Substate, State}; + +use executive_state::ExecutiveState; /// EVM test Error. #[derive(Debug)] @@ -65,15 +69,19 @@ use ethjson::spec::ForkSpec; /// Simplified, single-block EVM test client. pub struct EvmTestClient<'a> { - state: state::State, + state: State, spec: &'a spec::Spec, - dump_state: fn(&state::State) -> Option, + dump_state: fn(&State) -> Option, } -fn no_dump_state(_: &state::State) -> Option { +fn no_dump_state(_: &State) -> Option { None } +fn dump_state(state: &State) -> Option { + state.to_pod_full().ok() +} + impl<'a> fmt::Debug for EvmTestClient<'a> { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { fmt.debug_struct("EvmTestClient") @@ -100,7 +108,7 @@ impl<'a> EvmTestClient<'a> { } /// Change default function for dump state (default does not dump) - pub fn set_dump_state_fn(&mut self, dump_state: fn(&state::State) -> Option) { + pub fn set_dump_state(&mut self) { self.dump_state = dump_state; } @@ -124,7 +132,7 @@ impl<'a> EvmTestClient<'a> { /// Creates new EVM test client with an in-memory DB initialized with given PodState. /// Takes a `TrieSpec` to set the type of trie. - pub fn from_pod_state_with_trie(spec: &'a spec::Spec, pod_state: pod_state::PodState, trie_spec: trie::TrieSpec) -> Result { + pub fn from_pod_state_with_trie(spec: &'a spec::Spec, pod_state: PodState, trie_spec: trie::TrieSpec) -> Result { let factories = Self::factories(trie_spec); let state = Self::state_from_pod(spec, &factories, pod_state)?; @@ -136,19 +144,19 @@ impl<'a> EvmTestClient<'a> { } /// Creates new EVM test client with an in-memory DB initialized with given PodState. - pub fn from_pod_state(spec: &'a spec::Spec, pod_state: pod_state::PodState) -> Result { + pub fn from_pod_state(spec: &'a spec::Spec, pod_state: PodState) -> Result { Self::from_pod_state_with_trie(spec, pod_state, trie::TrieSpec::Secure) } fn factories(trie_spec: trie::TrieSpec) -> Factories { Factories { - vm: factory::VmFactory::new(VMType::Interpreter, 5 * 1024), + vm: trie_vm_factories::VmFactory::new(VMType::Interpreter, 5 * 1024), trie: trie::TrieFactory::new(trie_spec), accountdb: Default::default(), } } - fn state_from_spec(spec: &'a spec::Spec, factories: &Factories) -> Result, EvmTestError> { + fn state_from_spec(spec: &'a spec::Spec, factories: &Factories) -> Result, EvmTestError> { let db = Arc::new(kvdb_memorydb::create(db::NUM_COLUMNS.expect("We use column-based DB; qed"))); let journal_db = journaldb::new(db.clone(), journaldb::Algorithm::EarlyMerge, db::COL_STATE); let mut state_db = state_db::StateDB::new(journal_db, 5 * 1024 * 1024); @@ -162,7 +170,7 @@ impl<'a> EvmTestClient<'a> { db.write(batch)?; } - state::State::from_existing( + State::from_existing( state_db, *genesis.state_root(), spec.engine.account_start_nonce(0), @@ -170,11 +178,11 @@ impl<'a> EvmTestClient<'a> { ).map_err(EvmTestError::Trie) } - fn state_from_pod(spec: &'a spec::Spec, factories: &Factories, pod_state: pod_state::PodState) -> Result, EvmTestError> { + fn state_from_pod(spec: &'a spec::Spec, factories: &Factories, pod_state: PodState) -> Result, EvmTestError> { let db = Arc::new(kvdb_memorydb::create(db::NUM_COLUMNS.expect("We use column-based DB; qed"))); let journal_db = journaldb::new(db.clone(), journaldb::Algorithm::EarlyMerge, db::COL_STATE); let state_db = state_db::StateDB::new(journal_db, 5 * 1024 * 1024); - let mut state = state::State::new( + let mut state = State::new( state_db, spec.engine.account_start_nonce(0), factories.clone(), @@ -185,7 +193,7 @@ impl<'a> EvmTestClient<'a> { } /// Return current state. - pub fn state(&self) -> &state::State { + pub fn state(&self) -> &State { &self.state } @@ -221,7 +229,7 @@ impl<'a> EvmTestClient<'a> { info: client::EnvInfo, ) -> Result { - let mut substate = state::Substate::new(); + let mut substate = Substate::new(); let machine = self.spec.engine.machine(); let schedule = machine.schedule(info.number); let mut executive = executive::Executive::new(&mut self.state, &info, &machine, &schedule); @@ -263,9 +271,9 @@ impl<'a> EvmTestClient<'a> { // Details: https://github.com/paritytech/parity-ethereum/issues/9431 let schedule = self.spec.engine.machine().schedule(env_info.number); self.state.add_balance(&env_info.author, &0.into(), if schedule.no_empty { - state::CleanupMode::NoEmpty + CleanupMode::NoEmpty } else { - state::CleanupMode::ForceCreate + CleanupMode::ForceCreate }).ok(); // Touching also means that we should remove the account if it's within eip161 // conditions. @@ -300,11 +308,7 @@ impl<'a> EvmTestClient<'a> { end_state, } )}, - Err(error) => Err(TransactErr { - state_root, - error, - end_state, - }), + Err(e) => Err(TransactErr {state_root, error: e.into(), end_state}), } } } @@ -330,7 +334,7 @@ pub struct TransactSuccess { /// outcome pub outcome: receipt::TransactionOutcome, /// end state if needed - pub end_state: Option, + pub end_state: Option, } /// To be returned inside a std::result::Result::Err after a failed @@ -342,5 +346,5 @@ pub struct TransactErr { /// Execution error pub error: ::error::Error, /// end state if needed - pub end_state: Option, + pub end_state: Option, } diff --git a/ethcore/src/client/mod.rs b/ethcore/src/client/mod.rs index 973321fdb9f..a7260b82509 100644 --- a/ethcore/src/client/mod.rs +++ b/ethcore/src/client/mod.rs @@ -25,7 +25,6 @@ mod evm_test_client; mod io_message; #[cfg(any(test, feature = "test-helpers"))] mod test_client; -mod trace; pub use self::client::*; pub use self::config::{Mode, ClientConfig, DatabaseCompactionProfile, BlockChainConfig, VMType}; @@ -33,14 +32,14 @@ pub use self::config::{Mode, ClientConfig, DatabaseCompactionProfile, BlockChain pub use self::evm_test_client::{EvmTestClient, EvmTestError, TransactErr, TransactSuccess}; pub use self::io_message::ClientIoMessage; #[cfg(any(test, feature = "test-helpers"))] -pub use self::test_client::{TestBlockChainClient, EachBlockWith}; +pub use self::test_client::{TestBlockChainClient, EachBlockWith, TestState}; pub use self::chain_notify::{ChainNotify, NewBlocks, ChainRoute, ChainRouteType, ChainMessageType}; pub use self::traits::{ Nonce, Balance, ChainInfo, BlockInfo, ReopenBlock, PrepareOpenBlock, TransactionInfo, ScheduleInfo, ImportSealedBlock, BroadcastProposalBlock, ImportBlock, StateOrBlock, StateClient, Call, EngineInfo, AccountData, BlockChain, BlockProducer, SealedBlockImporter, BadBlocks, BlockChainReset }; -pub use state::StateInfo; +pub use account_state::state::StateInfo; pub use self::traits::{BlockChainClient, EngineClient, ProvingBlockChainClient, IoClient}; pub use types::ids::*; diff --git a/ethcore/src/client/test_client.rs b/ethcore/src/client/test_client.rs index 7894a665de3..693fdad01bd 100644 --- a/ethcore/src/client/test_client.rs +++ b/ethcore/src/client/test_client.rs @@ -66,7 +66,7 @@ use executive::Executed; use journaldb; use miner::{self, Miner, MinerService}; use spec::Spec; -use state::StateInfo; +use account_state::state::StateInfo; use state_db::StateDB; use trace::LocalizedTrace; use verification::queue::QueueInfo as BlockQueueInfo; @@ -586,7 +586,7 @@ impl ImportBlock for TestBlockChainClient { impl Call for TestBlockChainClient { // State will not be used by test client anyway, since all methods that accept state are mocked - type State = (); + type State = TestState; fn call(&self, _t: &SignedTransaction, _analytics: CallAnalytics, _state: &mut Self::State, _header: &Header) -> Result { self.execution_result.read().clone().unwrap() @@ -605,23 +605,27 @@ impl Call for TestBlockChainClient { } } -impl StateInfo for () { +/// NewType wrapper around `()` to impersonate `State` in trait impls. State will not be used by +/// test client, since all methods that accept state are mocked. +pub struct TestState; + +impl StateInfo for TestState { fn nonce(&self, _address: &Address) -> ethtrie::Result { unimplemented!() } fn balance(&self, _address: &Address) -> ethtrie::Result { unimplemented!() } fn storage_at(&self, _address: &Address, _key: &H256) -> ethtrie::Result { unimplemented!() } fn code(&self, _address: &Address) -> ethtrie::Result>> { unimplemented!() } } + impl StateClient for TestBlockChainClient { - // State will not be used by test client anyway, since all methods that accept state are mocked - type State = (); + type State = TestState; fn latest_state(&self) -> Self::State { - () + TestState } fn state_at(&self, _id: BlockId) -> Option { - Some(()) + Some(TestState) } } diff --git a/ethcore/src/client/traits.rs b/ethcore/src/client/traits.rs index ff4b778f4a8..50fe75a699b 100644 --- a/ethcore/src/client/traits.rs +++ b/ethcore/src/client/traits.rs @@ -47,7 +47,7 @@ use engines::Engine; use error::{Error, EthcoreResult}; use executed::CallError; use executive::Executed; -use state::StateInfo; +use account_state::state::StateInfo; use trace::LocalizedTrace; use verification::queue::QueueInfo as BlockQueueInfo; use verification::queue::kind::blocks::Unverified; @@ -61,12 +61,6 @@ pub enum StateOrBlock { Block(BlockId) } -impl From for StateOrBlock { - fn from(info: S) -> StateOrBlock { - StateOrBlock::State(Box::new(info) as Box<_>) - } -} - impl From> for StateOrBlock { fn from(info: Box) -> StateOrBlock { StateOrBlock::State(info) @@ -195,7 +189,7 @@ pub trait IoClient: Sync + Send { /// Queue block import with transaction receipts. Does no sealing and transaction validation. fn queue_ancient_block(&self, block_bytes: Unverified, receipts_bytes: Bytes) -> EthcoreResult; - /// Queue conensus engine message. + /// Queue consensus engine message. fn queue_consensus_message(&self, message: Bytes); } diff --git a/ethcore/src/engines/validator_set/safe_contract.rs b/ethcore/src/engines/validator_set/safe_contract.rs index 0b3e804b1fc..50ee598dc73 100644 --- a/ethcore/src/engines/validator_set/safe_contract.rs +++ b/ethcore/src/engines/validator_set/safe_contract.rs @@ -129,7 +129,7 @@ fn check_first_proof(machine: &Machine, contract_address: Address, old_header: H data, }.fake_sign(from); - let res = ::state::check_proof( + let res = ::executive_state::check_proof( state_items, *old_header.state_root(), &tx, @@ -138,9 +138,9 @@ fn check_first_proof(machine: &Machine, contract_address: Address, old_header: H ); match res { - ::state::ProvedExecution::BadProof => Err("Bad proof".into()), - ::state::ProvedExecution::Failed(e) => Err(format!("Failed call: {}", e)), - ::state::ProvedExecution::Complete(e) => decoder.decode(&e.output).map_err(|e| e.to_string()), + ::executive_state::ProvedExecution::BadProof => Err("Bad proof".into()), + ::executive_state::ProvedExecution::Failed(e) => Err(format!("Failed call: {}", e)), + ::executive_state::ProvedExecution::Complete(e) => decoder.decode(&e.output).map_err(|e| e.to_string()), } } diff --git a/ethcore/src/error.rs b/ethcore/src/error.rs index f9d2881655d..8affe6a9757 100644 --- a/ethcore/src/error.rs +++ b/ethcore/src/error.rs @@ -220,6 +220,9 @@ pub enum Error { /// A convenient variant for String. #[display(fmt = "{}", _0)] Msg(String), + /// State errors + #[display(fmt = "State error ({})", _0)] + State(account_state::Error), } impl error::Error for Error { @@ -236,6 +239,7 @@ impl error::Error for Error { Error::Ethkey(e) => Some(e), Error::Decoder(e) => Some(e), Error::Snapshot(e) => Some(e), + Error::State(e) => Some(e), _ => None, } } diff --git a/ethcore/src/ethereum/mod.rs b/ethcore/src/ethereum/mod.rs index 01d1ccf80b2..af2114a1bd4 100644 --- a/ethcore/src/ethereum/mod.rs +++ b/ethcore/src/ethereum/mod.rs @@ -189,7 +189,7 @@ pub fn new_kovan_wasm_test_machine() -> Machine { load_machine(include_bytes!(". mod tests { use std::str::FromStr; use ethereum_types::{U256, H256, Address}; - use state::*; + use account_state::*; use super::*; use test_helpers::get_temp_state_db; use types::view; diff --git a/ethcore/src/executive.rs b/ethcore/src/executive.rs index 29e0dc217c3..4ff2ed26d9d 100644 --- a/ethcore/src/executive.rs +++ b/ethcore/src/executive.rs @@ -21,7 +21,7 @@ use std::sync::Arc; use hash::keccak; use ethereum_types::{H256, U256, U512, Address}; use bytes::{Bytes, BytesRef}; -use state::{Backend as StateBackend, State, Substate, CleanupMode}; +use account_state::{Backend as StateBackend, State, Substate, CleanupMode}; use executed::ExecutionError; use machine::Machine; use evm::{CallType, Finalize, FinalizationResult}; @@ -29,7 +29,7 @@ use vm::{ self, EnvInfo, CreateContractAddress, ReturnData, CleanDustMode, ActionParams, ActionValue, Schedule, TrapError, ResumeCall, ResumeCreate }; -use factory::VmFactory; +use trie_vm_factories::VmFactory; use externalities::*; use trace::{self, Tracer, VMTracer}; use types::transaction::{Action, SignedTransaction}; @@ -1202,7 +1202,7 @@ mod tests { use evm::{Factory, VMType}; use error::ExecutionError; use machine::Machine; - use state::{Substate, CleanupMode}; + use account_state::{Substate, CleanupMode}; use test_helpers::{get_temp_state_with_factory, get_temp_state}; use trace::trace; use trace::{FlatTrace, Tracer, NoopTracer, ExecutiveTracer}; diff --git a/ethcore/src/state/mod.rs b/ethcore/src/executive_state.rs similarity index 56% rename from ethcore/src/state/mod.rs rename to ethcore/src/executive_state.rs index 402f9394bb5..f0f89f8bc6d 100644 --- a/ethcore/src/state/mod.rs +++ b/ethcore/src/executive_state.rs @@ -14,50 +14,42 @@ // You should have received a copy of the GNU General Public License // along with Parity Ethereum. If not, see . -//! A mutable state representation suitable to execute transactions. -//! Generic over a `Backend`. Deals with `Account`s. -//! Unconfirmed sub-states are managed with `checkpoint`s which may be canonicalized -//! or rolled back. - -use std::cell::{RefCell, RefMut}; -use std::collections::hash_map::Entry; -use std::collections::{HashMap, BTreeMap, BTreeSet, HashSet}; -use std::fmt; -use std::sync::Arc; -use hash::{KECCAK_NULL_RLP, KECCAK_EMPTY}; - -use types::receipt::{Receipt, TransactionOutcome}; +//! Execute transactions and modify State. This is glue code between the `ethcore` and +//! `account-state` crates and contains everything that requires `Machine` or `Executive` (or types +//! thereof). + use machine::Machine; use vm::EnvInfo; -use error::Error; use executive::{Executive, TransactOptions}; -use factory::Factories; -use trace::{self, FlatTrace, VMTrace}; -use pod_account::*; -use pod_state::{self, PodState}; -use types::basic_account::BasicAccount; use executed::{Executed, ExecutionError}; -use types::state_diff::StateDiff; -use types::transaction::SignedTransaction; -use state_db::StateDB; -use factory::VmFactory; - -use ethereum_types::{H256, U256, Address}; -use hash_db::{HashDB, AsHashDB}; +use types::{ + transaction::SignedTransaction, + receipt::{TransactionOutcome, Receipt}, +}; +use trace::{FlatTrace, VMTrace}; +use account_state::{ + backend::{self, Backend}, + state::State, +}; +use ethereum_types::H256; +use trie_vm_factories::Factories; +use bytes::Bytes; use keccak_hasher::KeccakHasher; use kvdb::DBValue; -use bytes::Bytes; +use hash_db::AsHashDB; -use trie::{Trie, TrieError, Recorder}; -use ethtrie::{TrieDB, Result as TrieResult}; - -mod substate; - -pub mod backend; +use error::Error; -pub use state_account::Account; -pub use self::backend::Backend; -pub use self::substate::Substate; +/// Return type of proof validity check. +#[derive(Debug, Clone)] +pub enum ProvedExecution { + /// Proof wasn't enough to complete execution. + BadProof, + /// The transaction failed, but not due to a bad proof. + Failed(ExecutionError), + /// The transaction successfully completed with the given proof. + Complete(Box), +} /// Used to return information about an `State::apply` operation. pub struct ApplyOutcome { @@ -74,121 +66,6 @@ pub struct ApplyOutcome { /// Result type for the execution ("application") of a transaction. pub type ApplyResult = Result, Error>; -/// Return type of proof validity check. -#[derive(Debug, Clone)] -pub enum ProvedExecution { - /// Proof wasn't enough to complete execution. - BadProof, - /// The transaction failed, but not due to a bad proof. - Failed(ExecutionError), - /// The transaction successfully completed with the given proof. - Complete(Box), -} - -#[derive(Eq, PartialEq, Clone, Copy, Debug)] -/// Account modification state. Used to check if the account was -/// Modified in between commits and overall. -enum AccountState { - /// Account was loaded from disk and never modified in this state object. - CleanFresh, - /// Account was loaded from the global cache and never modified. - CleanCached, - /// Account has been modified and is not committed to the trie yet. - /// This is set if any of the account data is changed, including - /// storage and code. - Dirty, - /// Account was modified and committed to the trie. - Committed, -} - -#[derive(Debug)] -/// In-memory copy of the account data. Holds the optional account -/// and the modification status. -/// Account entry can contain existing (`Some`) or non-existing -/// account (`None`) -struct AccountEntry { - /// Account entry. `None` if account known to be non-existant. - account: Option, - /// Unmodified account balance. - old_balance: Option, - /// Entry state. - state: AccountState, -} - -// Account cache item. Contains account data and -// modification state -impl AccountEntry { - fn is_dirty(&self) -> bool { - self.state == AccountState::Dirty - } - - fn exists_and_is_null(&self) -> bool { - self.account.as_ref().map_or(false, |a| a.is_null()) - } - - /// Clone dirty data into new `AccountEntry`. This includes - /// basic account data and modified storage keys. - /// Returns None if clean. - fn clone_if_dirty(&self) -> Option { - match self.is_dirty() { - true => Some(self.clone_dirty()), - false => None, - } - } - - /// Clone dirty data into new `AccountEntry`. This includes - /// basic account data and modified storage keys. - fn clone_dirty(&self) -> AccountEntry { - AccountEntry { - old_balance: self.old_balance, - account: self.account.as_ref().map(Account::clone_dirty), - state: self.state, - } - } - - // Create a new account entry and mark it as dirty. - fn new_dirty(account: Option) -> AccountEntry { - AccountEntry { - old_balance: account.as_ref().map(|a| a.balance().clone()), - account: account, - state: AccountState::Dirty, - } - } - - // Create a new account entry and mark it as clean. - fn new_clean(account: Option) -> AccountEntry { - AccountEntry { - old_balance: account.as_ref().map(|a| a.balance().clone()), - account: account, - state: AccountState::CleanFresh, - } - } - - // Create a new account entry and mark it as clean and cached. - fn new_clean_cached(account: Option) -> AccountEntry { - AccountEntry { - old_balance: account.as_ref().map(|a| a.balance().clone()), - account: account, - state: AccountState::CleanCached, - } - } - - // Replace data with another entry but preserve storage cache. - fn overwrite_with(&mut self, other: AccountEntry) { - self.state = other.state; - match other.account { - Some(acc) => { - if let Some(ref mut ours) = self.account { - ours.overwrite_with(acc); - } else { - self.account = Some(acc); - } - }, - None => self.account = None, - } - } -} - /// Check the given proof of execution. /// `Err(ExecutionError::Internal)` indicates failure, everything else indicates /// a successful proof (as the transaction itself may be poorly chosen). @@ -216,7 +93,7 @@ pub fn check_proof( }; let options = TransactOptions::with_no_tracing().save_output_from_contract(); - match state.execute(env_info, machine, transaction, options, true) { + match execute(&mut state, env_info, machine, transaction, options, true) { Ok(executed) => ProvedExecution::Complete(Box::new(executed)), Err(ExecutionError::Internal(_)) => ProvedExecution::BadProof, Err(e) => ProvedExecution::Failed(e), @@ -224,7 +101,7 @@ pub fn check_proof( } /// Prove a `virtual` transaction on the given state. -/// Returns `None` when the transacion could not be proved, +/// Returns `None` when the transaction could not be proved, /// and a proof otherwise. pub fn prove_transaction_virtual + Send + Sync>( db: H, @@ -234,7 +111,7 @@ pub fn prove_transaction_virtual + Send + Syn env_info: &EnvInfo, factories: Factories, ) -> Option<(Bytes, Vec)> { - use self::backend::Proving; + use account_state::backend::Proving; let backend = Proving::new(db); let res = State::from_existing( @@ -250,7 +127,7 @@ pub fn prove_transaction_virtual + Send + Syn }; let options = TransactOptions::with_no_tracing().dont_check_nonce().save_output_from_contract(); - match state.execute(env_info, machine, transaction, options, true) { + match execute(&mut state, env_info, machine, transaction, options, true) { Err(ExecutionError::Internal(_)) => None, Err(e) => { trace!(target: "state", "Proved call failed: {}", e); @@ -260,555 +137,43 @@ pub fn prove_transaction_virtual + Send + Syn } } -/// Representation of the entire state of all accounts in the system. -/// -/// `State` can work together with `StateDB` to share account cache. -/// -/// Local cache contains changes made locally and changes accumulated -/// locally from previous commits. Global cache reflects the database -/// state and never contains any changes. -/// -/// Cache items contains account data, or the flag that account does not exist -/// and modification state (see `AccountState`) -/// -/// Account data can be in the following cache states: -/// * In global but not local - something that was queried from the database, -/// but never modified -/// * In local but not global - something that was just added (e.g. new account) -/// * In both with the same value - something that was changed to a new value, -/// but changed back to a previous block in the same block (same State instance) -/// * In both with different values - something that was overwritten with a -/// new value. -/// -/// All read-only state queries check local cache/modifications first, -/// then global state cache. If data is not found in any of the caches -/// it is loaded from the DB to the local cache. -/// -/// **** IMPORTANT ************************************************************* -/// All the modifications to the account data must set the `Dirty` state in the -/// `AccountEntry`. This is done in `require` and `require_or_from`. So just -/// use that. -/// **************************************************************************** -/// -/// Upon destruction all the local cache data propagated into the global cache. -/// Propagated items might be rejected if current state is non-canonical. -/// -/// State checkpointing. -/// -/// A new checkpoint can be created with `checkpoint()`. checkpoints can be -/// created in a hierarchy. -/// When a checkpoint is active all changes are applied directly into -/// `cache` and the original value is copied into an active checkpoint. -/// Reverting a checkpoint with `revert_to_checkpoint` involves copying -/// original values from the latest checkpoint back into `cache`. The code -/// takes care not to overwrite cached storage while doing that. -/// A checkpoint can be discarded with `discard_checkpoint`. All of the original -/// backed-up values are moved into a parent checkpoint (if any). -/// -pub struct State { - db: B, - root: H256, - cache: RefCell>, - // The original account is preserved in - checkpoints: RefCell>>>, - account_start_nonce: U256, - factories: Factories, -} - -#[derive(Copy, Clone)] -enum RequireCache { - None, - CodeSize, - Code, -} - -/// Mode of dealing with null accounts. -#[derive(PartialEq)] -pub enum CleanupMode<'a> { - /// Create accounts which would be null. - ForceCreate, - /// Don't delete null accounts upon touching, but also don't create them. - NoEmpty, - /// Mark all touched accounts. - TrackTouched(&'a mut HashSet
), -} - -/// Provides subset of `State` methods to query state information -pub trait StateInfo { - /// Get the nonce of account `a`. - fn nonce(&self, a: &Address) -> TrieResult; - - /// Get the balance of account `a`. - fn balance(&self, a: &Address) -> TrieResult; - - /// Mutate storage of account `address` so that it is `value` for `key`. - fn storage_at(&self, address: &Address, key: &H256) -> TrieResult; - - /// Get accounts' code. - fn code(&self, a: &Address) -> TrieResult>>; -} +/// Collects code that needs a Machine and/or Executive +pub trait ExecutiveState { + /// Execute a given transaction, producing a receipt and an optional trace. + /// This will change the state accordingly. + fn apply( + &mut self, + env_info: &EnvInfo, + machine: &Machine, + t: &SignedTransaction, + tracing: bool + ) -> ApplyResult; -impl StateInfo for State { - fn nonce(&self, a: &Address) -> TrieResult { State::nonce(self, a) } - fn balance(&self, a: &Address) -> TrieResult { State::balance(self, a) } - fn storage_at(&self, address: &Address, key: &H256) -> TrieResult { State::storage_at(self, address, key) } - fn code(&self, address: &Address) -> TrieResult>> { State::code(self, address) } + /// Execute a given transaction with given tracer and VM tracer producing a receipt and an optional trace. + /// This will change the state accordingly. + fn apply_with_tracing( + &mut self, + env_info: &EnvInfo, + machine: &Machine, + t: &SignedTransaction, + tracer: T, + vm_tracer: V, + ) -> ApplyResult + where + T: trace::Tracer, + V: trace::VMTracer; } -const SEC_TRIE_DB_UNWRAP_STR: &'static str = "A state can only be created with valid root. Creating a SecTrieDB with a valid root will not fail. \ - Therefore creating a SecTrieDB with this state's root will not fail."; - -impl State { - /// Creates new state with empty state root - /// Used for tests. - pub fn new(mut db: B, account_start_nonce: U256, factories: Factories) -> State { - let mut root = H256::zero(); - { - // init trie and reset root to null - let _ = factories.trie.create(db.as_hash_db_mut(), &mut root); - } - - State { - db: db, - root: root, - cache: RefCell::new(HashMap::new()), - checkpoints: RefCell::new(Vec::new()), - account_start_nonce: account_start_nonce, - factories: factories, - } - } - - /// Creates new state with existing state root - pub fn from_existing(db: B, root: H256, account_start_nonce: U256, factories: Factories) -> TrieResult> { - if !db.as_hash_db().contains(&root, hash_db::EMPTY_PREFIX) { - return Err(Box::new(TrieError::InvalidStateRoot(root))); - } - - let state = State { - db: db, - root: root, - cache: RefCell::new(HashMap::new()), - checkpoints: RefCell::new(Vec::new()), - account_start_nonce: account_start_nonce, - factories: factories - }; - - Ok(state) - } - - /// Get a VM factory that can execute on this state. - pub fn vm_factory(&self) -> VmFactory { - self.factories.vm.clone() - } - - /// Create a recoverable checkpoint of this state. Return the checkpoint index. - pub fn checkpoint(&mut self) -> usize { - let checkpoints = self.checkpoints.get_mut(); - let index = checkpoints.len(); - checkpoints.push(HashMap::new()); - index - } - - /// Merge last checkpoint with previous. - pub fn discard_checkpoint(&mut self) { - // merge with previous checkpoint - let last = self.checkpoints.get_mut().pop(); - if let Some(mut checkpoint) = last { - if let Some(ref mut prev) = self.checkpoints.get_mut().last_mut() { - if prev.is_empty() { - **prev = checkpoint; - } else { - for (k, v) in checkpoint.drain() { - prev.entry(k).or_insert(v); - } - } - } - } - } - - /// Revert to the last checkpoint and discard it. - pub fn revert_to_checkpoint(&mut self) { - if let Some(mut checkpoint) = self.checkpoints.get_mut().pop() { - for (k, v) in checkpoint.drain() { - match v { - Some(v) => { - match self.cache.get_mut().entry(k) { - Entry::Occupied(mut e) => { - // Merge checkpointed changes back into the main account - // storage preserving the cache. - e.get_mut().overwrite_with(v); - }, - Entry::Vacant(e) => { - e.insert(v); - } - } - }, - None => { - if let Entry::Occupied(e) = self.cache.get_mut().entry(k) { - if e.get().is_dirty() { - e.remove(); - } - } - } - } - } - } - } - - fn insert_cache(&self, address: &Address, account: AccountEntry) { - // Dirty account which is not in the cache means this is a new account. - // It goes directly into the checkpoint as there's nothing to rever to. - // - // In all other cases account is read as clean first, and after that made - // dirty in and added to the checkpoint with `note_cache`. - let is_dirty = account.is_dirty(); - let old_value = self.cache.borrow_mut().insert(*address, account); - if is_dirty { - if let Some(ref mut checkpoint) = self.checkpoints.borrow_mut().last_mut() { - checkpoint.entry(*address).or_insert(old_value); - } - } - } - - fn note_cache(&self, address: &Address) { - if let Some(ref mut checkpoint) = self.checkpoints.borrow_mut().last_mut() { - checkpoint.entry(*address) - .or_insert_with(|| self.cache.borrow().get(address).map(AccountEntry::clone_dirty)); - } - } - - /// Destroy the current object and return root and database. - pub fn drop(mut self) -> (H256, B) { - self.propagate_to_global_cache(); - (self.root, self.db) - } - - /// Destroy the current object and return single account data. - pub fn into_account(self, account: &Address) -> TrieResult<(Option>, HashMap)> { - // TODO: deconstruct without cloning. - let account = self.require(account, true)?; - Ok((account.code().clone(), account.storage_changes().clone())) - } - - /// Return reference to root - pub fn root(&self) -> &H256 { - &self.root - } - - /// Create a new contract at address `contract`. If there is already an account at the address - /// it will have its code reset, ready for `init_code()`. - pub fn new_contract(&mut self, contract: &Address, balance: U256, nonce_offset: U256, version: U256) -> TrieResult<()> { - let original_storage_root = self.original_storage_root(contract)?; - let (nonce, overflow) = self.account_start_nonce.overflowing_add(nonce_offset); - if overflow { - return Err(Box::new(TrieError::DecoderError(H256::from(*contract), - rlp::DecoderError::Custom("Nonce overflow".into())))); - } - self.insert_cache(contract, AccountEntry::new_dirty(Some(Account::new_contract(balance, nonce, version, original_storage_root)))); - Ok(()) - } - - /// Remove an existing account. - pub fn kill_account(&mut self, account: &Address) { - self.insert_cache(account, AccountEntry::new_dirty(None)); - } - - /// Determine whether an account exists. - pub fn exists(&self, a: &Address) -> TrieResult { - // Bloom filter does not contain empty accounts, so it is important here to - // check if account exists in the database directly before EIP-161 is in effect. - self.ensure_cached(a, RequireCache::None, false, |a| a.is_some()) - } - - /// Determine whether an account exists and if not empty. - pub fn exists_and_not_null(&self, a: &Address) -> TrieResult { - self.ensure_cached(a, RequireCache::None, false, |a| a.map_or(false, |a| !a.is_null())) - } - - /// Determine whether an account exists and has code or non-zero nonce. - pub fn exists_and_has_code_or_nonce(&self, a: &Address) -> TrieResult { - self.ensure_cached(a, RequireCache::CodeSize, false, - |a| a.map_or(false, |a| a.code_hash() != KECCAK_EMPTY || *a.nonce() != self.account_start_nonce)) - } - - /// Get the balance of account `a`. - pub fn balance(&self, a: &Address) -> TrieResult { - self.ensure_cached(a, RequireCache::None, true, - |a| a.as_ref().map_or(U256::zero(), |account| *account.balance())) - } - - /// Get the nonce of account `a`. - pub fn nonce(&self, a: &Address) -> TrieResult { - self.ensure_cached(a, RequireCache::None, true, - |a| a.as_ref().map_or(self.account_start_nonce, |account| *account.nonce())) - } - - /// Whether the base storage root of an account remains unchanged. - pub fn is_base_storage_root_unchanged(&self, a: &Address) -> TrieResult { - Ok(self.ensure_cached(a, RequireCache::None, true, - |a| a.as_ref().map(|account| account.is_base_storage_root_unchanged()))? - .unwrap_or(true)) - } - - /// Get the storage root of account `a`. - pub fn storage_root(&self, a: &Address) -> TrieResult> { - self.ensure_cached(a, RequireCache::None, true, - |a| a.as_ref().and_then(|account| account.storage_root())) - } - - /// Get the original storage root since last commit of account `a`. - pub fn original_storage_root(&self, a: &Address) -> TrieResult { - Ok(self.ensure_cached(a, RequireCache::None, true, - |a| a.as_ref().map(|account| account.original_storage_root()))? - .unwrap_or(KECCAK_NULL_RLP)) - } - - /// Get the value of storage at a specific checkpoint. - pub fn checkpoint_storage_at(&self, start_checkpoint_index: usize, address: &Address, key: &H256) -> TrieResult> { - #[must_use] - enum ReturnKind { - /// Use original storage at value at this address. - OriginalAt, - /// The checkpoint storage value is the same as the checkpoint storage value at the next checkpoint. - SameAsNext, - } - - let kind = { - let checkpoints = self.checkpoints.borrow(); - - if start_checkpoint_index >= checkpoints.len() { - // The checkpoint was not found. Return None. - return Ok(None); - } - - let mut kind = None; - - for checkpoint in checkpoints.iter().skip(start_checkpoint_index) { - match checkpoint.get(address) { - // The account exists at this checkpoint. - Some(Some(AccountEntry { account: Some(ref account), .. })) => { - if let Some(value) = account.cached_storage_at(key) { - return Ok(Some(value)); - } else { - // This account has checkpoint entry, but the key is not in the entry's cache. We can use - // original_storage_at if current account's original storage root is the same as checkpoint - // account's original storage root. Otherwise, the account must be a newly created contract. - if account.base_storage_root() == self.original_storage_root(address)? { - kind = Some(ReturnKind::OriginalAt); - break - } else { - // If account base storage root is different from the original storage root since last - // commit, then it can only be created from a new contract, where the base storage root - // would always be empty. Note that this branch is actually never called, because - // `cached_storage_at` handled this case. - warn!(target: "state", "Trying to get an account's cached storage value, but base storage root does not equal to original storage root! Assuming the value is empty."); - return Ok(Some(H256::zero())); - } - } - }, - // The account didn't exist at that point. Return empty value. - Some(Some(AccountEntry { account: None, .. })) => return Ok(Some(H256::zero())), - // The value was not cached at that checkpoint, meaning it was not modified at all. - Some(None) => { - kind = Some(ReturnKind::OriginalAt); - break - }, - // This key does not have a checkpoint entry. - None => { - kind = Some(ReturnKind::SameAsNext); - }, - } - } - - kind.expect("start_checkpoint_index is checked to be below checkpoints_len; for loop above must have been executed at least once; it will either early return, or set the kind value to Some; qed") - }; - - match kind { - ReturnKind::SameAsNext => { - // If we reached here, all previous SameAsNext failed to early return. It means that the value we want - // to fetch is the same as current. - Ok(Some(self.storage_at(address, key)?)) - }, - ReturnKind::OriginalAt => Ok(Some(self.original_storage_at(address, key)?)), - } - } - - fn storage_at_inner( - &self, address: &Address, key: &H256, f_cached_at: FCachedStorageAt, f_at: FStorageAt, - ) -> TrieResult where - FCachedStorageAt: Fn(&Account, &H256) -> Option, - FStorageAt: Fn(&Account, &dyn HashDB, &H256) -> TrieResult - { - // Storage key search and update works like this: - // 1. If there's an entry for the account in the local cache check for the key and return it if found. - // 2. If there's an entry for the account in the global cache check for the key or load it into that account. - // 3. If account is missing in the global cache load it into the local cache and cache the key there. - - { - // check local cache first without updating - let local_cache = self.cache.borrow_mut(); - let mut local_account = None; - if let Some(maybe_acc) = local_cache.get(address) { - match maybe_acc.account { - Some(ref account) => { - if let Some(value) = f_cached_at(account, key) { - return Ok(value); - } else { - local_account = Some(maybe_acc); - } - }, - _ => return Ok(H256::zero()), - } - } - // check the global cache and and cache storage key there if found, - let trie_res = self.db.get_cached(address, |acc| match acc { - None => Ok(H256::zero()), - Some(a) => { - let account_db = self.factories.accountdb.readonly(self.db.as_hash_db(), a.address_hash(address)); - f_at(a, account_db.as_hash_db(), key) - } - }); - - if let Some(res) = trie_res { - return res; - } - - // otherwise cache the account localy and cache storage key there. - if let Some(ref mut acc) = local_account { - if let Some(ref account) = acc.account { - let account_db = self.factories.accountdb.readonly(self.db.as_hash_db(), account.address_hash(address)); - return f_at(account, account_db.as_hash_db(), key) - } else { - return Ok(H256::zero()) - } - } - } - - // check if the account could exist before any requests to trie - if self.db.is_known_null(address) { return Ok(H256::zero()) } - - // account is not found in the global cache, get from the DB and insert into local - let db = &self.db.as_hash_db(); - let db = self.factories.trie.readonly(db, &self.root).expect(SEC_TRIE_DB_UNWRAP_STR); - let from_rlp = |b: &[u8]| Account::from_rlp(b).expect("decoding db value failed"); - let maybe_acc = db.get_with(address.as_bytes(), from_rlp)?; - let r = maybe_acc.as_ref().map_or(Ok(H256::zero()), |a| { - let account_db = self.factories.accountdb.readonly(self.db.as_hash_db(), a.address_hash(address)); - f_at(a, account_db.as_hash_db(), key) - }); - self.insert_cache(address, AccountEntry::new_clean(maybe_acc)); - r - } - - /// Mutate storage of account `address` so that it is `value` for `key`. - pub fn storage_at(&self, address: &Address, key: &H256) -> TrieResult { - self.storage_at_inner( - address, - key, - |account, key| { account.cached_storage_at(key) }, - |account, db, key| { account.storage_at(db, key) }, - ) - } - - /// Get the value of storage after last state commitment. - pub fn original_storage_at(&self, address: &Address, key: &H256) -> TrieResult { - self.storage_at_inner( - address, - key, - |account, key| { account.cached_original_storage_at(key) }, - |account, db, key| { account.original_storage_at(db, key) }, - ) - } - - /// Get accounts' code. - pub fn code(&self, a: &Address) -> TrieResult>> { - self.ensure_cached(a, RequireCache::Code, true, - |a| a.as_ref().map_or(None, |a| a.code().clone())) - } - - /// Get an account's code hash. - pub fn code_hash(&self, a: &Address) -> TrieResult> { - self.ensure_cached(a, RequireCache::None, true, - |a| a.as_ref().map(|a| a.code_hash())) - } - - /// Get an account's code version. - pub fn code_version(&self, a: &Address) -> TrieResult { - self.ensure_cached(a, RequireCache::None, true, - |a| a.as_ref().map(|a| *a.code_version()).unwrap_or(U256::zero())) - } - - /// Get accounts' code size. - pub fn code_size(&self, a: &Address) -> TrieResult> { - self.ensure_cached(a, RequireCache::CodeSize, true, - |a| a.as_ref().and_then(|a| a.code_size())) - } - - /// Add `incr` to the balance of account `a`. - pub fn add_balance(&mut self, a: &Address, incr: &U256, cleanup_mode: CleanupMode) -> TrieResult<()> { - trace!(target: "state", "add_balance({}, {}): {}", a, incr, self.balance(a)?); - let is_value_transfer = !incr.is_zero(); - if is_value_transfer || (cleanup_mode == CleanupMode::ForceCreate && !self.exists(a)?) { - self.require(a, false)?.add_balance(incr); - } else if let CleanupMode::TrackTouched(set) = cleanup_mode { - if self.exists(a)? { - set.insert(*a); - self.touch(a)?; - } - } - Ok(()) - } - - /// Subtract `decr` from the balance of account `a`. - pub fn sub_balance(&mut self, a: &Address, decr: &U256, cleanup_mode: &mut CleanupMode) -> TrieResult<()> { - trace!(target: "state", "sub_balance({}, {}): {}", a, decr, self.balance(a)?); - if !decr.is_zero() || !self.exists(a)? { - self.require(a, false)?.sub_balance(decr); - } - if let CleanupMode::TrackTouched(ref mut set) = *cleanup_mode { - set.insert(*a); - } - Ok(()) - } - - /// Subtracts `by` from the balance of `from` and adds it to that of `to`. - pub fn transfer_balance(&mut self, from: &Address, to: &Address, by: &U256, mut cleanup_mode: CleanupMode) -> TrieResult<()> { - self.sub_balance(from, by, &mut cleanup_mode)?; - self.add_balance(to, by, cleanup_mode)?; - Ok(()) - } - - /// Increment the nonce of account `a` by 1. - pub fn inc_nonce(&mut self, a: &Address) -> TrieResult<()> { - self.require(a, false).map(|mut x| x.inc_nonce()) - } - - /// Mutate storage of account `a` so that it is `value` for `key`. - pub fn set_storage(&mut self, a: &Address, key: H256, value: H256) -> TrieResult<()> { - trace!(target: "state", "set_storage({}:{:x} to {:x})", a, key, value); - if self.storage_at(a, &key)? != value { - self.require(a, false)?.set_storage(key, value) - } - - Ok(()) - } - - /// Initialise the code of account `a` so that it is `code`. - /// NOTE: Account should have been created with `new_contract`. - pub fn init_code(&mut self, a: &Address, code: Bytes) -> TrieResult<()> { - self.require_or_from(a, true, || Account::new_contract(0.into(), self.account_start_nonce, 0.into(), KECCAK_NULL_RLP), |_| {})?.init_code(code); - Ok(()) - } - - /// Reset the code of account `a` so that it is `code`. - pub fn reset_code(&mut self, a: &Address, code: Bytes) -> TrieResult<()> { - self.require_or_from(a, true, || Account::new_contract(0.into(), self.account_start_nonce, 0.into(), KECCAK_NULL_RLP), |_| {})?.reset_code(code); - Ok(()) - } - +impl ExecutiveState for State { /// Execute a given transaction, producing a receipt and an optional trace. /// This will change the state accordingly. - pub fn apply(&mut self, env_info: &EnvInfo, machine: &Machine, t: &SignedTransaction, tracing: bool) -> ApplyResult { + fn apply( + &mut self, + env_info: &EnvInfo, + machine: &Machine, + t: &SignedTransaction, + tracing: bool + ) -> ApplyResult { if tracing { let options = TransactOptions::with_tracing(); self.apply_with_tracing(env_info, machine, t, options.tracer, options.vm_tracer) @@ -820,25 +185,26 @@ impl State { /// Execute a given transaction with given tracer and VM tracer producing a receipt and an optional trace. /// This will change the state accordingly. - pub fn apply_with_tracing( + fn apply_with_tracing( &mut self, env_info: &EnvInfo, machine: &Machine, t: &SignedTransaction, tracer: T, vm_tracer: V, - ) -> ApplyResult where - T: trace::Tracer, - V: trace::VMTracer, + ) -> ApplyResult + where + T: trace::Tracer, + V: trace::VMTracer, { let options = TransactOptions::new(tracer, vm_tracer); - let e = self.execute(env_info, machine, t, options, false)?; + let e = execute(self, env_info, machine, t, options, false)?; let params = machine.params(); let eip658 = env_info.number >= params.eip658_transition; let no_intermediate_commits = eip658 || - (env_info.number >= params.eip98_transition && env_info.number >= params.validate_receipts_transition); + (env_info.number >= params.eip98_transition && env_info.number >= params.validate_receipts_transition); let outcome = if no_intermediate_commits { if eip658 { @@ -862,482 +228,31 @@ impl State { vm_trace: e.vm_trace, }) } - - // Execute a given transaction without committing changes. - // - // `virt` signals that we are executing outside of a block set and restrictions like - // gas limits and gas costs should be lifted. - fn execute(&mut self, env_info: &EnvInfo, machine: &Machine, t: &SignedTransaction, options: TransactOptions, virt: bool) - -> Result, ExecutionError> where T: trace::Tracer, V: trace::VMTracer, - { - let schedule = machine.schedule(env_info.number); - let mut e = Executive::new(self, env_info, machine, &schedule); - - match virt { - true => e.transact_virtual(t, options), - false => e.transact(t, options), - } - } - - fn touch(&mut self, a: &Address) -> TrieResult<()> { - self.require(a, false)?; - Ok(()) - } - - /// Commits our cached account changes into the trie. - pub fn commit(&mut self) -> Result<(), Error> { - assert!(self.checkpoints.borrow().is_empty()); - // first, commit the sub trees. - let mut accounts = self.cache.borrow_mut(); - for (address, ref mut a) in accounts.iter_mut().filter(|&(_, ref a)| a.is_dirty()) { - if let Some(ref mut account) = a.account { - let addr_hash = account.address_hash(address); - { - let mut account_db = self.factories.accountdb.create(self.db.as_hash_db_mut(), addr_hash); - account.commit_storage(&self.factories.trie, account_db.as_hash_db_mut())?; - account.commit_code(account_db.as_hash_db_mut()); - } - if !account.is_empty() { - self.db.note_non_null_account(address); - } - } - } - - { - let mut trie = self.factories.trie.from_existing(self.db.as_hash_db_mut(), &mut self.root)?; - for (address, ref mut a) in accounts.iter_mut().filter(|&(_, ref a)| a.is_dirty()) { - a.state = AccountState::Committed; - match a.account { - Some(ref mut account) => { - trie.insert(address.as_bytes(), &account.rlp())?; - }, - None => { - trie.remove(address.as_bytes())?; - }, - }; - } - } - - Ok(()) - } - - /// Propagate local cache into shared canonical state cache. - fn propagate_to_global_cache(&mut self) { - let mut addresses = self.cache.borrow_mut(); - trace!("Committing cache {:?} entries", addresses.len()); - for (address, a) in addresses.drain().filter(|&(_, ref a)| a.state == AccountState::Committed || a.state == AccountState::CleanFresh) { - self.db.add_to_account_cache(address, a.account, a.state == AccountState::Committed); - } - } - - /// Clear state cache - pub fn clear(&mut self) { - assert!(self.checkpoints.borrow().is_empty()); - self.cache.borrow_mut().clear(); - } - - /// Remove any touched empty or dust accounts. - pub fn kill_garbage(&mut self, touched: &HashSet
, remove_empty_touched: bool, min_balance: &Option, kill_contracts: bool) -> TrieResult<()> { - let to_kill: HashSet<_> = { - self.cache.borrow().iter().filter_map(|(address, ref entry)| - if touched.contains(address) && // Check all touched accounts - ((remove_empty_touched && entry.exists_and_is_null()) // Remove all empty touched accounts. - || min_balance.map_or(false, |ref balance| entry.account.as_ref().map_or(false, |account| - (account.is_basic() || kill_contracts) // Remove all basic and optionally contract accounts where balance has been decreased. - && account.balance() < balance && entry.old_balance.as_ref().map_or(false, |b| account.balance() < b)))) { - - Some(address.clone()) - } else { None }).collect() - }; - for address in to_kill { - self.kill_account(&address); - } - Ok(()) - } - - /// Populate the state from `accounts`. - /// Used for tests. - pub fn populate_from(&mut self, accounts: PodState) { - assert!(self.checkpoints.borrow().is_empty()); - for (add, acc) in accounts.drain().into_iter() { - self.cache.borrow_mut().insert(add, AccountEntry::new_dirty(Some(Account::from_pod(acc)))); - } - } - - /// Populate a PodAccount map from this state. - fn to_pod_cache(&self) -> PodState { - assert!(self.checkpoints.borrow().is_empty()); - PodState::from(self.cache.borrow().iter().fold(BTreeMap::new(), |mut m, (add, opt)| { - if let Some(ref acc) = opt.account { - m.insert(*add, acc.to_pod()); - } - m - })) - } - - #[cfg(feature="to-pod-full")] - /// Populate a PodAccount map from this state. - /// Warning this is not for real time use. - /// Use of this method requires FatDB mode to be able - /// to iterate on accounts. - pub fn to_pod_full(&self) -> Result { - - assert!(self.checkpoints.borrow().is_empty()); - assert!(self.factories.trie.is_fat()); - - let mut result = BTreeMap::new(); - - let db = &self.db.as_hash_db(); - let trie = self.factories.trie.readonly(db, &self.root)?; - - // put trie in cache - for item in trie.iter()? { - if let Ok((addr, _dbval)) = item { - let address = Address::from_slice(&addr); - let _ = self.require(&address, true); - } - } - - // Resolve missing part - for (add, opt) in self.cache.borrow().iter() { - if let Some(ref acc) = opt.account { - let pod_account = self.account_to_pod_account(acc, add)?; - result.insert(add.clone(), pod_account); - } - } - - Ok(PodState::from(result)) - } - - /// Create a PodAccount from an account. - /// Differs from existing method by including all storage - /// values of the account to the PodAccount. - /// This function is only intended for use in small tests or with fresh accounts. - /// It requires FatDB. - #[cfg(feature="to-pod-full")] - fn account_to_pod_account(&self, account: &Account, address: &Address) -> Result { - use ethereum_types::BigEndianHash; - - let mut pod_storage = BTreeMap::new(); - let addr_hash = account.address_hash(address); - let accountdb = self.factories.accountdb.readonly(self.db.as_hash_db(), addr_hash); - let root = account.base_storage_root(); - - let accountdb = &accountdb.as_hash_db(); - let trie = self.factories.trie.readonly(accountdb, &root)?; - for o_kv in trie.iter()? { - if let Ok((key, val)) = o_kv { - pod_storage.insert( - H256::from_slice(&key[..]), - BigEndianHash::from_uint( - &rlp::decode::(&val[..]).expect("Decoded from trie which was encoded from the same type; qed") - ), - ); - } - } - - let mut pod_account = account.to_pod(); - // cached one first - pod_storage.append(&mut pod_account.storage); - pod_account.storage = pod_storage; - Ok(pod_account) - } - - /// Populate a PodAccount map from this state, with another state as the account and storage query. - fn to_pod_diff(&mut self, query: &State) -> TrieResult { - assert!(self.checkpoints.borrow().is_empty()); - - // Merge PodAccount::to_pod for cache of self and `query`. - let all_addresses = self.cache.borrow().keys().cloned() - .chain(query.cache.borrow().keys().cloned()) - .collect::>(); - - Ok(PodState::from(all_addresses.into_iter().fold(Ok(BTreeMap::new()), |m: TrieResult<_>, address| { - let mut m = m?; - - let account = self.ensure_cached(&address, RequireCache::Code, true, |acc| { - acc.map(|acc| { - // Merge all modified storage keys. - let all_keys = { - let self_keys = acc.storage_changes().keys().cloned() - .collect::>(); - - if let Some(ref query_storage) = query.cache.borrow().get(&address) - .and_then(|opt| { - Some(opt.account.as_ref()?.storage_changes().keys().cloned() - .collect::>()) - }) - { - self_keys.union(&query_storage).cloned().collect::>() - } else { - self_keys.into_iter().collect::>() - } - }; - - // Storage must be fetched after ensure_cached to avoid borrow problem. - (*acc.balance(), *acc.nonce(), all_keys, acc.code().map(|x| x.to_vec()), *acc.code_version()) - }) - })?; - - if let Some((balance, nonce, storage_keys, code, version)) = account { - let storage = storage_keys.into_iter().fold(Ok(BTreeMap::new()), |s: TrieResult<_>, key| { - let mut s = s?; - - s.insert(key, self.storage_at(&address, &key)?); - Ok(s) - })?; - - m.insert(address, PodAccount { - balance, nonce, storage, code, version - }); - } - - Ok(m) - })?)) - } - - /// Returns a `StateDiff` describing the difference from `orig` to `self`. - /// Consumes self. - pub fn diff_from(&self, mut orig: State) -> TrieResult { - let pod_state_post = self.to_pod_cache(); - let pod_state_pre = orig.to_pod_diff(self)?; - Ok(pod_state::diff_pod(&pod_state_pre, &pod_state_post)) - } - - /// Load required account data from the databases. Returns whether the cache succeeds. - #[must_use] - fn update_account_cache(require: RequireCache, account: &mut Account, state_db: &B, db: &dyn HashDB) -> bool { - if let RequireCache::None = require { - return true; - } - - if account.is_cached() { - return true; - } - - // if there's already code in the global cache, always cache it localy - let hash = account.code_hash(); - match state_db.get_cached_code(&hash) { - Some(code) => { - account.cache_given_code(code); - true - }, - None => match require { - RequireCache::None => true, - RequireCache::Code => { - if let Some(code) = account.cache_code(db) { - // propagate code loaded from the database to - // the global code cache. - state_db.cache_code(hash, code); - true - } else { - false - } - }, - RequireCache::CodeSize => { - account.cache_code_size(db) - } - } - } - } - - /// Check caches for required data - /// First searches for account in the local, then the shared cache. - /// Populates local cache if nothing found. - fn ensure_cached(&self, a: &Address, require: RequireCache, check_null: bool, f: F) -> TrieResult - where F: Fn(Option<&Account>) -> U { - // check local cache first - if let Some(ref mut maybe_acc) = self.cache.borrow_mut().get_mut(a) { - if let Some(ref mut account) = maybe_acc.account { - let accountdb = self.factories.accountdb.readonly(self.db.as_hash_db(), account.address_hash(a)); - if Self::update_account_cache(require, account, &self.db, accountdb.as_hash_db()) { - return Ok(f(Some(account))); - } else { - return Err(Box::new(TrieError::IncompleteDatabase(H256::from(*a)))); - } - } - return Ok(f(None)); - } - // check global cache - let result = self.db.get_cached(a, |mut acc| { - if let Some(ref mut account) = acc { - let accountdb = self.factories.accountdb.readonly(self.db.as_hash_db(), account.address_hash(a)); - if !Self::update_account_cache(require, account, &self.db, accountdb.as_hash_db()) { - return Err(Box::new(TrieError::IncompleteDatabase(H256::from(*a)))); - } - } - Ok(f(acc.map(|a| &*a))) - }); - match result { - Some(r) => Ok(r?), - None => { - // first check if it is not in database for sure - if check_null && self.db.is_known_null(a) { return Ok(f(None)); } - - // not found in the global cache, get from the DB and insert into local - let db = &self.db.as_hash_db(); - let db = self.factories.trie.readonly(db, &self.root)?; - let from_rlp = |b: &[u8]| Account::from_rlp(b).expect("decoding db value failed"); - let mut maybe_acc = db.get_with(a.as_bytes(), from_rlp)?; - if let Some(ref mut account) = maybe_acc.as_mut() { - let accountdb = self.factories.accountdb.readonly(self.db.as_hash_db(), account.address_hash(a)); - if !Self::update_account_cache(require, account, &self.db, accountdb.as_hash_db()) { - return Err(Box::new(TrieError::IncompleteDatabase(H256::from(*a)))); - } - } - let r = f(maybe_acc.as_ref()); - self.insert_cache(a, AccountEntry::new_clean(maybe_acc)); - Ok(r) - } - } - } - - /// Pull account `a` in our cache from the trie DB. `require_code` requires that the code be cached, too. - fn require<'a>(&'a self, a: &Address, require_code: bool) -> TrieResult> { - self.require_or_from(a, require_code, || Account::new_basic(0u8.into(), self.account_start_nonce), |_| {}) - } - - /// Pull account `a` in our cache from the trie DB. `require_code` requires that the code be cached, too. - /// If it doesn't exist, make account equal the evaluation of `default`. - fn require_or_from<'a, F, G>(&'a self, a: &Address, require_code: bool, default: F, not_default: G) -> TrieResult> - where F: FnOnce() -> Account, G: FnOnce(&mut Account), - { - let contains_key = self.cache.borrow().contains_key(a); - if !contains_key { - match self.db.get_cached_account(a) { - Some(acc) => self.insert_cache(a, AccountEntry::new_clean_cached(acc)), - None => { - let maybe_acc = if !self.db.is_known_null(a) { - let db = &self.db.as_hash_db(); - let db = self.factories.trie.readonly(db, &self.root)?; - let from_rlp = |b:&[u8]| { Account::from_rlp(b).expect("decoding db value failed") }; - AccountEntry::new_clean(db.get_with(a.as_bytes(), from_rlp)?) - } else { - AccountEntry::new_clean(None) - }; - self.insert_cache(a, maybe_acc); - } - } - } - self.note_cache(a); - - // at this point the entry is guaranteed to be in the cache. - let mut account = RefMut::map(self.cache.borrow_mut(), |c| { - let entry = c.get_mut(a).expect("entry known to exist in the cache; qed"); - - match &mut entry.account { - &mut Some(ref mut acc) => not_default(acc), - slot => *slot = Some(default()), - } - - // set the dirty flag after changing account data. - entry.state = AccountState::Dirty; - entry.account.as_mut().expect("Required account must always exist; qed") - }); - - if require_code { - let addr_hash = account.address_hash(a); - let accountdb = self.factories.accountdb.readonly(self.db.as_hash_db(), addr_hash); - - if !Self::update_account_cache(RequireCache::Code, &mut account, &self.db, accountdb.as_hash_db()) { - return Err(Box::new(TrieError::IncompleteDatabase(H256::from(*a)))) - } - } - - Ok(account) - } - - /// Replace account code and storage. Creates account if it does not exist. - pub fn patch_account(&self, a: &Address, code: Arc, storage: HashMap) -> TrieResult<()> { - Ok(self.require(a, false)?.reset_code_and_storage(code, storage)) - } -} - -// State proof implementations; useful for light client protocols. -impl State { - /// Prove an account's existence or nonexistence in the state trie. - /// Returns a merkle proof of the account's trie node omitted or an encountered trie error. - /// If the account doesn't exist in the trie, prove that and return defaults. - /// Requires a secure trie to be used for accurate results. - /// `account_key` == keccak(address) - pub fn prove_account(&self, account_key: H256) -> TrieResult<(Vec, BasicAccount)> { - let mut recorder = Recorder::new(); - let db = &self.db.as_hash_db(); - let trie = TrieDB::new(db, &self.root)?; - let maybe_account: Option = { - let panicky_decoder = |bytes: &[u8]| { - ::rlp::decode(bytes).unwrap_or_else(|_| panic!("prove_account, could not query trie for account key={}", &account_key)) - }; - let query = (&mut recorder, panicky_decoder); - trie.get_with(account_key.as_bytes(), query)? - }; - let account = maybe_account.unwrap_or_else(|| BasicAccount { - balance: 0.into(), - nonce: self.account_start_nonce, - code_hash: KECCAK_EMPTY, - storage_root: KECCAK_NULL_RLP, - code_version: 0.into(), - }); - - Ok((recorder.drain().into_iter().map(|r| r.data).collect(), account)) - } - - /// Prove an account's storage key's existence or nonexistence in the state. - /// Returns a merkle proof of the account's storage trie. - /// Requires a secure trie to be used for correctness. - /// `account_key` == keccak(address) - /// `storage_key` == keccak(key) - pub fn prove_storage(&self, account_key: H256, storage_key: H256) -> TrieResult<(Vec, H256)> { - // TODO: probably could look into cache somehow but it's keyed by - // address, not keccak(address). - let db = &self.db.as_hash_db(); - let trie = TrieDB::new(db, &self.root)?; - let from_rlp = |b: &[u8]| Account::from_rlp(b).expect("decoding db value failed"); - let acc = match trie.get_with(account_key.as_bytes(), from_rlp)? { - Some(acc) => acc, - None => return Ok((Vec::new(), H256::zero())), - }; - - let account_db = self.factories.accountdb.readonly(self.db.as_hash_db(), account_key); - acc.prove_storage(account_db.as_hash_db(), storage_key) - } } -impl fmt::Debug for State { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{:?}", self.cache.borrow()) - } -} - -impl State { - /// Get a reference to the underlying state DB. - pub fn db(&self) -> &StateDB { - &self.db - } -} - -// TODO: cloning for `State` shouldn't be possible in general; Remove this and use -// checkpoints where possible. -impl Clone for State { - fn clone(&self) -> State { - let cache = { - let mut cache: HashMap = HashMap::new(); - for (key, val) in self.cache.borrow().iter() { - if let Some(entry) = val.clone_if_dirty() { - cache.insert(key.clone(), entry); - } - } - cache - }; +// Execute a given transaction without committing changes. +// +// `virt` signals that we are executing outside of a block set and restrictions like +// gas limits and gas costs should be lifted. +fn execute( + state: &mut State, + env_info: &EnvInfo, + machine: &Machine, + t: &SignedTransaction, + options: TransactOptions, + virt: bool +) -> Result, ExecutionError> + where + B: Backend, + T: trace::Tracer, + V: trace::VMTracer, +{ + let schedule = machine.schedule(env_info.number); + let mut e = Executive::new(state, env_info, machine, &schedule); - State { - db: self.db.boxed_clone(), - root: self.root.clone(), - cache: RefCell::new(cache), - checkpoints: RefCell::new(Vec::new()), - account_start_nonce: self.account_start_nonce.clone(), - factories: self.factories.clone(), - } + match virt { + true => e.transact_virtual(t, options), + false => e.transact(t, options), } } @@ -1345,6 +260,7 @@ impl Clone for State { mod tests { use std::sync::Arc; use std::str::FromStr; + use std::collections::HashSet; use rustc_hex::FromHex; use hash::{keccak, KECCAK_NULL_RLP}; use super::*; @@ -1357,6 +273,10 @@ mod tests { use types::transaction::*; use trace::{FlatTrace, TraceError, trace}; use evm::CallType; + use pod::{self, PodAccount}; + use pod::PodState; + use executive_state::ExecutiveState; + use account_state::{Account, CleanupMode}; fn secret() -> Secret { keccak("").into() @@ -2111,7 +1031,7 @@ mod tests { }, FlatTrace { trace_address: vec![0].into_iter().collect(), subtraces: 1, - action: trace::Action::Call(trace::Call { + action: trace::Action::Call(trace::Call { from: Address::from_low_u64_be(0xa), to: Address::from_low_u64_be(0xb), value: 0.into(), @@ -2668,8 +1588,6 @@ mod tests { #[test] fn should_trace_diff_suicided_accounts() { - use pod_account; - let a = Address::from_low_u64_be(10); let db = get_temp_state_db(); let (root, db) = { @@ -2688,7 +1606,8 @@ mod tests { assert_eq!(diff_map.len(), 1); assert!(diff_map.get(&a).is_some()); assert_eq!(diff_map.get(&a), - pod_account::diff_pod(Some(&PodAccount { + pod::account::diff_pod( + Some(&PodAccount { balance: U256::from(100), nonce: U256::zero(), code: Some(Default::default()), @@ -2699,8 +1618,6 @@ mod tests { #[test] fn should_trace_diff_unmodified_storage() { - use pod_account; - let a = Address::from_low_u64_be(10); let db = get_temp_state_db(); @@ -2720,24 +1637,23 @@ mod tests { assert_eq!(diff_map.len(), 1); assert!(diff_map.get(&a).is_some()); assert_eq!(diff_map.get(&a), - pod_account::diff_pod(Some(&PodAccount { - balance: U256::zero(), - nonce: U256::zero(), - code: Some(Default::default()), - storage: vec![(BigEndianHash::from_uint(&U256::from(1u64)), BigEndianHash::from_uint(&U256::from(20u64)))] - .into_iter().collect(), - version: U256::zero(), - }), Some(&PodAccount { - balance: U256::zero(), - nonce: U256::zero(), - code: Some(Default::default()), - storage: vec![(BigEndianHash::from_uint(&U256::from(1u64)), BigEndianHash::from_uint(&U256::from(100u64)))] - .into_iter().collect(), - version: U256::zero(), - })).as_ref()); + pod::account::diff_pod( + Some(&PodAccount { + balance: U256::zero(), + nonce: U256::zero(), + code: Some(Default::default()), + storage: vec![(BigEndianHash::from_uint(&U256::from(1u64)), BigEndianHash::from_uint(&U256::from(20u64)))].into_iter().collect(), + version: U256::zero(), + }), + Some(&PodAccount { + balance: U256::zero(), + nonce: U256::zero(), + code: Some(Default::default()), + storage: vec![(BigEndianHash::from_uint(&U256::from(1u64)), BigEndianHash::from_uint(&U256::from(100u64)))].into_iter().collect(), + version: U256::zero(), + })).as_ref()); } - #[cfg(feature="to-pod-full")] #[test] fn should_get_full_pod_storage_values() { use trie::{TrieFactory, TrieSpec}; @@ -2778,7 +1694,5 @@ mod tests { state.set_storage(&a, storage_address.clone(), BigEndianHash::from_uint(&U256::from(0u64))).unwrap(); let dump = state.to_pod_full().unwrap(); assert_eq!(get_pod_state_val(&dump, &a, storage_address.clone()), BigEndianHash::from_uint(&U256::from(0u64))); - } - } diff --git a/ethcore/src/externalities.rs b/ethcore/src/externalities.rs index bf98b86d604..b992fc157a1 100644 --- a/ethcore/src/externalities.rs +++ b/ethcore/src/externalities.rs @@ -19,7 +19,7 @@ use std::cmp; use std::sync::Arc; use ethereum_types::{H256, U256, Address, BigEndianHash}; use bytes::Bytes; -use state::{Backend as StateBackend, State, Substate, CleanupMode}; +use account_state::{Backend as StateBackend, State, Substate, CleanupMode}; use machine::Machine; use executive::*; use vm::{ @@ -435,7 +435,7 @@ impl<'a, T: 'a, V: 'a, B: 'a> Ext for Externalities<'a, T, V, B> mod tests { use ethereum_types::{U256, Address}; use evm::{EnvInfo, Ext, CallType}; - use state::{State, Substate}; + use account_state::{State, Substate}; use test_helpers::get_temp_state; use super::*; use trace::{NoopTracer, NoopVMTracer}; diff --git a/ethcore/src/json_tests/executive.rs b/ethcore/src/json_tests/executive.rs index 6083b6c829d..afc876ced43 100644 --- a/ethcore/src/json_tests/executive.rs +++ b/ethcore/src/json_tests/executive.rs @@ -17,7 +17,7 @@ use std::path::Path; use std::sync::Arc; use super::test_common::*; -use state::{Backend as StateBackend, State, Substate}; +use account_state::{Backend as StateBackend, State, Substate}; use executive::*; use evm::{VMType, Finalize}; use vm::{ diff --git a/ethcore/src/json_tests/state.rs b/ethcore/src/json_tests/state.rs index c51a2c361c2..524010ffeea 100644 --- a/ethcore/src/json_tests/state.rs +++ b/ethcore/src/json_tests/state.rs @@ -16,7 +16,7 @@ use std::path::Path; use super::test_common::*; -use pod_state::PodState; +use pod::PodState; use trace; use client::{EvmTestClient, EvmTestError, TransactErr, TransactSuccess}; use ethjson; diff --git a/ethcore/src/lib.rs b/ethcore/src/lib.rs index cf328feceae..80f593d0655 100644 --- a/ethcore/src/lib.rs +++ b/ethcore/src/lib.rs @@ -69,6 +69,7 @@ extern crate ethcore_miner; extern crate ethereum_types; extern crate ethjson; extern crate ethkey; +extern crate trie_vm_factories; extern crate futures; extern crate hash_db; extern crate itertools; @@ -82,12 +83,11 @@ extern crate kvdb_memorydb; extern crate len_caching_lock; extern crate lru_cache; extern crate memory_cache; -extern crate memory_db; extern crate num_cpus; extern crate parity_bytes as bytes; extern crate parity_snappy as snappy; extern crate parking_lot; -extern crate pod_account; +extern crate pod; extern crate trie_db as trie; extern crate patricia_trie_ethereum as ethtrie; extern crate rand; @@ -98,13 +98,13 @@ extern crate parity_util_mem as malloc_size_of; extern crate rustc_hex; extern crate serde; extern crate stats; -extern crate state_account; +extern crate account_state; extern crate time_utils; +extern crate trace; extern crate triehash_ethereum as triehash; extern crate unexpected; extern crate using_queue; extern crate vm; -extern crate wasm; #[cfg(test)] extern crate rand_xorshift; @@ -138,8 +138,6 @@ extern crate macros; extern crate rlp_derive; #[macro_use] extern crate trace_time; -#[macro_use] -extern crate serde_derive; #[cfg_attr(test, macro_use)] extern crate evm; @@ -157,19 +155,16 @@ pub mod error; pub mod ethereum; pub mod executed; pub mod executive; +pub mod executive_state; pub mod machine; pub mod miner; -pub mod pod_state; pub mod snapshot; pub mod spec; -pub mod state; -pub mod state_db; -pub mod trace; -pub mod transaction_ext; pub mod verification; mod externalities; -mod factory; +mod state_db; +mod transaction_ext; mod tx_filter; #[cfg(test)] @@ -182,3 +177,4 @@ pub mod test_helpers; pub use executive::contract_address; pub use evm::CreateContractAddress; pub use trie::TrieSpec; +pub use state_db::StateDB; diff --git a/ethcore/src/machine.rs b/ethcore/src/machine.rs index 51044256572..ef2ac48a9fc 100644 --- a/ethcore/src/machine.rs +++ b/ethcore/src/machine.rs @@ -35,7 +35,7 @@ use client::BlockInfo; use error::Error; use executive::Executive; use spec::CommonParams; -use state::{CleanupMode, Substate}; +use account_state::{CleanupMode, Substate}; use trace::{NoopTracer, NoopVMTracer}; use tx_filter::TransactionFilter; @@ -108,9 +108,7 @@ impl Machine { pub fn ethash_extensions(&self) -> Option<&EthashExtensions> { self.ethash_extensions.as_ref() } -} -impl Machine { /// Execute a call as the system address. Block environment information passed to the /// VM is modified to have its gas limit bounded at the upper limit of possible used /// gases including this system call, capped at the maximum value able to be @@ -395,8 +393,18 @@ impl Machine { } rlp.as_val().map_err(|e| transaction::Error::InvalidRlp(e.to_string())) } -} + /// Get the balance, in base units, associated with an account. + /// Extracts data from the live block. + pub fn balance(&self, live: &ExecutedBlock, address: &Address) -> Result { + live.state.balance(address).map_err(Into::into) + } + + /// Increment the balance of an account in the state of the live block. + pub fn add_balance(&self, live: &mut ExecutedBlock, address: &Address, amount: &U256) -> Result<(), Error> { + live.state_mut().add_balance(address, amount, CleanupMode::NoEmpty).map_err(Into::into) + } +} /// Auxiliary data fetcher for an Ethereum machine. In Ethereum-like machines /// there are two kinds of auxiliary data: bodies and receipts. #[derive(Default, Clone)] @@ -422,19 +430,6 @@ pub enum AuxiliaryRequest { Both, } -impl Machine { - /// Get the balance, in base units, associated with an account. - /// Extracts data from the live block. - pub fn balance(&self, live: &ExecutedBlock, address: &Address) -> Result { - live.state.balance(address).map_err(Into::into) - } - - /// Increment the balance of an account in the state of the live block. - pub fn add_balance(&self, live: &mut ExecutedBlock, address: &Address, amount: &U256) -> Result<(), Error> { - live.state_mut().add_balance(address, amount, CleanupMode::NoEmpty).map_err(Into::into) - } -} - // Try to round gas_limit a bit so that: // 1) it will still be in desired range // 2) it will be a nearest (with tendency to increase) multiple of PARITY_GAS_LIMIT_DETERMINANT diff --git a/ethcore/src/miner/miner.rs b/ethcore/src/miner/miner.rs index 7f57ec5bc86..33c33883672 100644 --- a/ethcore/src/miner/miner.rs +++ b/ethcore/src/miner/miner.rs @@ -59,7 +59,7 @@ use error::Error; use executed::ExecutionError; use executive::contract_address; use spec::Spec; -use state::State; +use account_state::State; /// Different possible definitions for pending transaction set. #[derive(Debug, PartialEq)] diff --git a/ethcore/src/miner/mod.rs b/ethcore/src/miner/mod.rs index c6397fccc12..10a101ee5d4 100644 --- a/ethcore/src/miner/mod.rs +++ b/ethcore/src/miner/mod.rs @@ -50,7 +50,7 @@ use client::{ AccountData, Nonce, }; use error::Error; -use state::StateInfo; +use account_state::state::StateInfo; /// Provides methods to verify incoming external transactions pub trait TransactionVerifierClient: Send + Sync diff --git a/ethcore/src/snapshot/mod.rs b/ethcore/src/snapshot/mod.rs index 5608dac07eb..4ded83efef1 100644 --- a/ethcore/src/snapshot/mod.rs +++ b/ethcore/src/snapshot/mod.rs @@ -48,7 +48,7 @@ use num_cpus; use self::io::SnapshotWriter; use super::state_db::StateDB; -use super::state::Account as StateAccount; +use account_state::Account as StateAccount; use crossbeam_utils::thread; use rand::{Rng, rngs::OsRng}; diff --git a/ethcore/src/snapshot/tests/helpers.rs b/ethcore/src/snapshot/tests/helpers.rs index a6e516b1b1a..89e6f8730bb 100644 --- a/ethcore/src/snapshot/tests/helpers.rs +++ b/ethcore/src/snapshot/tests/helpers.rs @@ -97,7 +97,7 @@ impl StateProducer { let address_hash = H256(rng.gen()); let balance: usize = rng.gen(); let nonce: usize = rng.gen(); - let acc = ::state::Account::new_basic(balance.into(), nonce.into()).rlp(); + let acc = account_state::Account::new_basic(balance.into(), nonce.into()).rlp(); trie.insert(&address_hash[..], &acc).unwrap(); } } diff --git a/ethcore/src/spec/spec.rs b/ethcore/src/spec/spec.rs index 117561a72b2..31878285883 100644 --- a/ethcore/src/spec/spec.rs +++ b/ethcore/src/spec/spec.rs @@ -40,13 +40,12 @@ use engines::{ }; use error::Error; use executive::Executive; -use factory::Factories; +use trie_vm_factories::Factories; use machine::Machine; -use pod_state::PodState; +use pod::PodState; use spec::Genesis; use spec::seal::Generic as GenericSeal; -use state::backend::Basic as BasicBackend; -use state::{Backend, State, Substate}; +use account_state::{Backend, State, Substate, backend::Basic as BasicBackend}; use trace::{NoopTracer, NoopVMTracer}; pub use ethash::OptimizeFor; @@ -890,7 +889,7 @@ impl Spec { data: d, }.fake_sign(from); - let res = ::state::prove_transaction_virtual( + let res = ::executive_state::prove_transaction_virtual( db.as_hash_db_mut(), *genesis.state_root(), &tx, @@ -999,7 +998,7 @@ impl Spec { #[cfg(test)] mod tests { use super::*; - use state::State; + use account_state::State; use test_helpers::get_temp_state_db; use tempdir::TempDir; use types::view; diff --git a/ethcore/src/state_db.rs b/ethcore/src/state_db.rs index cb00d2132e8..2419502976c 100644 --- a/ethcore/src/state_db.rs +++ b/ethcore/src/state_db.rs @@ -33,7 +33,7 @@ use memory_cache::MemoryLruCache; use parking_lot::Mutex; use types::BlockNumber; -use state::{self, Account}; +use account_state::{self, Account}; /// Value used to initialize bloom bitmap size. /// @@ -125,8 +125,13 @@ pub struct StateDB { commit_number: Option, } -impl StateDB { +impl Clone for StateDB { + fn clone(&self) -> Self { + self.boxed_clone() + } +} +impl StateDB { /// Create a new instance wrapping `JournalDB` and the maximum allowed size /// of the LRU cache in bytes. Actual used memory may (read: will) be higher due to bookkeeping. // TODO: make the cache size actually accurate by moving the account storage cache @@ -284,7 +289,7 @@ impl StateDB { if is_best { let acc = account.account.0; if let Some(&mut Some(ref mut existing)) = cache.accounts.get_mut(&account.address) { - if let Some(new) = acc { + if let Some(new) = acc { if account.modified { existing.overwrite_with(new); } @@ -407,7 +412,7 @@ impl StateDB { } } -impl state::Backend for StateDB { +impl account_state::Backend for StateDB { fn as_hash_db(&self) -> &dyn HashDB { self.db.as_hash_db() } fn as_hash_db_mut(&mut self) -> &mut dyn HashDB { @@ -482,7 +487,7 @@ mod tests { use ethereum_types::{H256, U256, Address}; use kvdb::DBTransaction; use test_helpers::get_temp_state_db; - use state::{Account, Backend}; + use account_state::{Account, Backend}; #[test] fn state_db_smoke() { diff --git a/ethcore/src/test_helpers.rs b/ethcore/src/test_helpers.rs index 5137aade025..5c1db24a247 100644 --- a/ethcore/src/test_helpers.rs +++ b/ethcore/src/test_helpers.rs @@ -41,10 +41,10 @@ use types::views::BlockView; use block::{OpenBlock, Drain}; use client::{Client, ClientConfig, ChainInfo, ImportBlock, ChainNotify, ChainMessageType, PrepareOpenBlock}; -use factory::Factories; +use trie_vm_factories::Factories; use miner::Miner; use spec::Spec; -use state::*; +use account_state::*; use state_db::StateDB; use verification::queue::kind::blocks::Unverified; diff --git a/ethcore/src/tests/client.rs b/ethcore/src/tests/client.rs index 343751b0481..9063df3aef1 100644 --- a/ethcore/src/tests/client.rs +++ b/ethcore/src/tests/client.rs @@ -32,7 +32,7 @@ use ethereum; use executive::{Executive, TransactOptions}; use miner::{Miner, PendingOrdering, MinerService}; use spec::Spec; -use state::{self, State, CleanupMode}; +use account_state::{State, CleanupMode, backend}; use test_helpers::{ self, generate_dummy_client, push_blocks_to_client, get_test_client_with_blocks, get_good_dummy_block_seq, @@ -347,9 +347,9 @@ fn transaction_proof() { }.fake_sign(address); let proof = client.prove_transaction(transaction.clone(), BlockId::Latest).unwrap().1; - let backend = state::backend::ProofCheck::new(&proof); + let backend = backend::ProofCheck::new(&proof); - let mut factories = ::factory::Factories::default(); + let mut factories = ::trie_vm_factories::Factories::default(); factories.accountdb = ::account_db::Factory::Plain; // raw state values, no mangled keys. let root = *client.best_block_header().state_root(); diff --git a/ethcore/src/tests/evm.rs b/ethcore/src/tests/evm.rs index 9758ee0e723..de2a5211e92 100644 --- a/ethcore/src/tests/evm.rs +++ b/ethcore/src/tests/evm.rs @@ -21,7 +21,7 @@ use hash::keccak; use vm::{EnvInfo, ActionParams, ActionValue, CallType, ParamsType}; use evm::{Factory, VMType}; use executive::Executive; -use state::Substate; +use account_state::Substate; use test_helpers::get_temp_state_with_factory; use trace::{NoopVMTracer, NoopTracer}; use types::transaction::SYSTEM_ADDRESS; diff --git a/ethcore/sync/src/chain/mod.rs b/ethcore/sync/src/chain/mod.rs index fb655308d98..0c8d9f8e48c 100644 --- a/ethcore/sync/src/chain/mod.rs +++ b/ethcore/sync/src/chain/mod.rs @@ -109,7 +109,7 @@ use rlp::{RlpStream, DecoderError}; use network::{self, PeerId, PacketId}; use network::client_version::ClientVersion; use ethcore::client::{BlockChainClient, BlockStatus, BlockId, BlockChainInfo, BlockQueueInfo}; -use ethcore::snapshot::{RestorationStatus}; +use ethcore::snapshot::RestorationStatus; use sync_io::SyncIo; use super::{WarpSync, SyncConfig}; use block_sync::{BlockDownloader, DownloadAction}; diff --git a/ethcore/sync/src/tests/private.rs b/ethcore/sync/src/tests/private.rs index dd421cef8aa..4a74447d9c2 100644 --- a/ethcore/sync/src/tests/private.rs +++ b/ethcore/sync/src/tests/private.rs @@ -21,7 +21,7 @@ use types::transaction::{Transaction, Action}; use types::ids::BlockId; use ethcore::CreateContractAddress; use ethcore::client::{ClientIoMessage, BlockChainClient}; -use ethcore::executive::{contract_address}; +use ethcore::executive::contract_address; use ethcore::engines; use ethcore::miner::{self, MinerService}; use ethcore::spec::Spec; diff --git a/ethcore/trace/Cargo.toml b/ethcore/trace/Cargo.toml new file mode 100644 index 00000000000..dffc6c8bf70 --- /dev/null +++ b/ethcore/trace/Cargo.toml @@ -0,0 +1,25 @@ +[package] +description = "Transaction tracing" +name = "trace" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2018" + +[dependencies] +common-types = { path = "../types"} +ethcore-blockchain = { path = "../blockchain" } +ethcore-db = { path = "../db" } +ethereum-types = "0.6" +evm = { path = "../evm" } +kvdb = "0.1.0" +log = "0.4" +parity-bytes = "0.1.0" +parity-util-mem = "0.1" +parking_lot = "0.8.0" +rlp = "0.4.0" +rlp_derive = { path = "../../util/rlp-derive" } +vm = { path = "../vm" } + +[dev-dependencies] +# Used for test helpers +ethcore = { path = "..", features = ["test-helpers"] } diff --git a/ethcore/src/trace/config.rs b/ethcore/trace/src/config.rs similarity index 97% rename from ethcore/src/trace/config.rs rename to ethcore/trace/src/config.rs index 72fc1765511..b0c7cd348fc 100644 --- a/ethcore/src/trace/config.rs +++ b/ethcore/trace/src/config.rs @@ -22,7 +22,7 @@ pub struct Config { /// Indicates if tracing should be enabled or not. /// If it's None, it will be automatically configured. pub enabled: bool, - /// Preferef cache-size. + /// Preferred cache-size. pub pref_cache_size: usize, /// Max cache-size. pub max_cache_size: usize, diff --git a/ethcore/src/trace/db.rs b/ethcore/trace/src/db.rs similarity index 96% rename from ethcore/src/trace/db.rs rename to ethcore/trace/src/db.rs index baeca316665..24c38bfe032 100644 --- a/ethcore/src/trace/db.rs +++ b/ethcore/trace/src/db.rs @@ -17,18 +17,23 @@ //! Trace database. use std::collections::HashMap; use std::sync::Arc; -use parity_util_mem::MallocSizeOfExt; -use blockchain::BlockChainDB; -use db::cache_manager::CacheManager; -use db::{self, Key, Writable, Readable, CacheUpdatePolicy}; +use common_types::BlockNumber; +use ethcore_blockchain::{BlockChainDB, DatabaseExtras}; +use ethcore_db::{ + self as db, + cache_manager::CacheManager, + Key, Writable, Readable, CacheUpdatePolicy, +}; use ethereum_types::{H256, H264}; -use kvdb::{DBTransaction}; +use kvdb::DBTransaction; +use parity_util_mem::MallocSizeOfExt; use parking_lot::RwLock; -use types::BlockNumber; -use trace::{LocalizedTrace, Config, Filter, Database as TraceDatabase, ImportRequest, DatabaseExtras}; -use trace::flat::{FlatTrace, FlatBlockTraces, FlatTransactionTraces}; +use crate::{ + LocalizedTrace, Config, Filter, Database as TraceDatabase, ImportRequest, + flat::{FlatTrace, FlatBlockTraces, FlatTransactionTraces}, +}; const TRACE_DB_VER: &'static [u8] = b"1.0"; @@ -334,17 +339,23 @@ impl TraceDatabase for TraceDB where T: DatabaseExtras { #[cfg(test)] mod tests { - use std::collections::HashMap; - use std::sync::Arc; + use std::{ + collections::HashMap, + sync::Arc, + }; + use common_types::BlockNumber; + use ethcore_blockchain::DatabaseExtras; + use ethcore::test_helpers::new_db; use ethereum_types::{H256, U256, Address}; - use kvdb::{DBTransaction}; - use types::BlockNumber; - use trace::{Config, TraceDB, Database as TraceDatabase, DatabaseExtras, ImportRequest}; - use trace::{Filter, LocalizedTrace, AddressesFilter, TraceError}; - use trace::trace::{Call, Action, Res}; - use trace::flat::{FlatTrace, FlatBlockTraces, FlatTransactionTraces}; use evm::CallType; - use test_helpers::new_db; + use kvdb::DBTransaction; + + use crate::{ + Config, TraceDB, Database as TraceDatabase, ImportRequest, + Filter, LocalizedTrace, AddressesFilter, TraceError, + trace::{Call, Action, Res}, + flat::{FlatTrace, FlatBlockTraces, FlatTransactionTraces} + }; struct NoopExtras; diff --git a/ethcore/src/trace/executive_tracer.rs b/ethcore/trace/src/executive_tracer.rs similarity index 97% rename from ethcore/src/trace/executive_tracer.rs rename to ethcore/trace/src/executive_tracer.rs index fdd9428b5d2..6ece943fd96 100644 --- a/ethcore/src/trace/executive_tracer.rs +++ b/ethcore/trace/src/executive_tracer.rs @@ -18,8 +18,11 @@ use ethereum_types::{U256, Address}; use vm::{Error as VmError, ActionParams}; -use trace::trace::{Call, Create, Action, Res, CreateResult, CallResult, VMTrace, VMOperation, VMExecutedOperation, MemoryDiff, StorageDiff, Suicide, Reward, RewardType}; -use trace::{Tracer, VMTracer, FlatTrace}; +use log::debug; +use crate::{ + Tracer, VMTracer, FlatTrace, + trace::{Call, Create, Action, Res, CreateResult, CallResult, VMTrace, VMOperation, VMExecutedOperation, MemoryDiff, StorageDiff, Suicide, Reward, RewardType}, +}; /// Simple executive tracer. Traces all calls and creates. Ignores delegatecalls. #[derive(Default)] diff --git a/ethcore/src/trace/import.rs b/ethcore/trace/src/import.rs similarity index 95% rename from ethcore/src/trace/import.rs rename to ethcore/trace/src/import.rs index e9ec9c77bad..a1a6bb1a548 100644 --- a/ethcore/src/trace/import.rs +++ b/ethcore/trace/src/import.rs @@ -16,9 +16,9 @@ //! Traces import request. use ethereum_types::H256; -use types::BlockNumber; +use common_types::BlockNumber; -use trace::FlatBlockTraces; +use crate::FlatBlockTraces; /// Traces import request. pub struct ImportRequest { diff --git a/ethcore/src/trace/mod.rs b/ethcore/trace/src/lib.rs similarity index 78% rename from ethcore/src/trace/mod.rs rename to ethcore/trace/src/lib.rs index d3586614754..1bcdc478225 100644 --- a/ethcore/src/trace/mod.rs +++ b/ethcore/trace/src/lib.rs @@ -16,6 +16,13 @@ //! Tracing +use common_types::BlockNumber; +use ethereum_types::{U256, Address}; +use kvdb::DBTransaction; +use vm::{Error as VmError, ActionParams}; +// The MallocSizeOf derive looks for this in the root +use parity_util_mem as malloc_size_of; + mod config; mod db; mod executive_tracer; @@ -23,23 +30,22 @@ mod import; mod noop_tracer; mod types; -pub use self::config::Config; -pub use self::db::TraceDB; -pub use self::noop_tracer::{NoopTracer, NoopVMTracer}; -pub use self::executive_tracer::{ExecutiveTracer, ExecutiveVMTracer}; -pub use self::import::ImportRequest; -pub use self::localized::LocalizedTrace; - -pub use self::types::{filter, flat, localized, trace, Tracing}; -pub use self::types::error::Error as TraceError; -pub use self::types::trace::{VMTrace, VMOperation, VMExecutedOperation, MemoryDiff, StorageDiff, RewardType}; -pub use self::types::flat::{FlatTrace, FlatTransactionTraces, FlatBlockTraces}; -pub use self::types::filter::{Filter, AddressesFilter}; - -use ethereum_types::{H256, U256, Address}; -use kvdb::DBTransaction; -use vm::{Error as VmError, ActionParams}; -use types::BlockNumber; +pub use crate::{ + config::Config, + db::TraceDB, + localized::LocalizedTrace, + executive_tracer::{ExecutiveTracer, ExecutiveVMTracer}, + import::ImportRequest, + noop_tracer::{NoopTracer, NoopVMTracer}, + types::{ + Tracing, + error::Error as TraceError, + localized, + trace::{self, VMTrace, VMOperation, VMExecutedOperation, MemoryDiff, StorageDiff, RewardType}, + flat::{self, FlatTrace, FlatTransactionTraces, FlatBlockTraces}, + filter::{self, Filter, AddressesFilter}, + } +}; /// This trait is used by executive to build traces. pub trait Tracer: Send { @@ -99,16 +105,6 @@ pub trait VMTracer: Send { } -/// `DbExtras` provides an interface to query extra data which is not stored in tracesdb, -/// but necessary to work correctly. -pub trait DatabaseExtras { - /// Returns hash of given block number. - fn block_hash(&self, block_number: BlockNumber) -> Option; - - /// Returns hash of transaction at given position. - fn transaction_hash(&self, block_number: BlockNumber, tx_position: usize) -> Option; -} - /// Db provides an interface to query tracesdb. pub trait Database { /// Returns true if tracing is enabled. Otherwise false. diff --git a/ethcore/src/trace/noop_tracer.rs b/ethcore/trace/src/noop_tracer.rs similarity index 95% rename from ethcore/src/trace/noop_tracer.rs rename to ethcore/trace/src/noop_tracer.rs index 62cce8e0111..32f7b61cfef 100644 --- a/ethcore/src/trace/noop_tracer.rs +++ b/ethcore/trace/src/noop_tracer.rs @@ -18,8 +18,10 @@ use ethereum_types::{U256, Address}; use vm::{Error as VmError, ActionParams}; -use trace::{Tracer, VMTracer, FlatTrace}; -use trace::trace::{VMTrace, RewardType}; +use crate::{ + Tracer, VMTracer, FlatTrace, + trace::{VMTrace, RewardType} +}; /// Nonoperative tracer. Does not trace anything. pub struct NoopTracer; diff --git a/ethcore/src/trace/types/error.rs b/ethcore/trace/src/types/error.rs similarity index 99% rename from ethcore/src/trace/types/error.rs rename to ethcore/trace/src/types/error.rs index 5c775dcb6ec..b57c1f972cd 100644 --- a/ethcore/src/trace/types/error.rs +++ b/ethcore/trace/src/types/error.rs @@ -137,7 +137,7 @@ impl Decodable for Error { #[cfg(test)] mod tests { - use rlp::*; + use rlp::RlpStream; use super::Error; #[test] diff --git a/ethcore/src/trace/types/filter.rs b/ethcore/trace/src/types/filter.rs similarity index 90% rename from ethcore/src/trace/types/filter.rs rename to ethcore/trace/src/types/filter.rs index f4c8598d98f..936de0c4000 100644 --- a/ethcore/src/trace/types/filter.rs +++ b/ethcore/trace/src/types/filter.rs @@ -18,8 +18,7 @@ use std::ops::Range; use ethereum_types::{Address, Bloom, BloomInput}; -use trace::flat::FlatTrace; -use super::trace::{Action, Res}; +use crate::{flat::FlatTrace, trace::{Action, Res}}; /// Addresses filter. /// @@ -126,10 +125,12 @@ impl Filter { #[cfg(test)] mod tests { use ethereum_types::{Address, Bloom, BloomInput}; - use trace::trace::{Action, Call, Res, Create, CreateResult, Suicide, Reward}; - use trace::flat::FlatTrace; - use trace::{Filter, AddressesFilter, TraceError, RewardType}; use evm::CallType; + use crate::{ + Filter, AddressesFilter, TraceError, RewardType, + trace::{Action, Call, Res, Create, CreateResult, Suicide, Reward}, + flat::FlatTrace, + }; #[test] fn empty_trace_filter_bloom_possibilities() { @@ -386,42 +387,40 @@ mod tests { assert!(f2.matches(&trace)); } - #[test] - fn filter_match_failed_contract_creation_fix_9822() { - - let f0 = Filter { - range: (0..0), - from_address: vec![Address::from_low_u64_be(1)].into(), - to_address: vec![].into(), - }; - - let f1 = Filter { - range: (0..0), - from_address: vec![].into(), - to_address: vec![].into(), - }; - - let f2 = Filter { - range: (0..0), - from_address: vec![].into(), - to_address: vec![Address::from_low_u64_be(2)].into(), - }; - - let trace = FlatTrace { - action: Action::Create(Create { - from: Address::from_low_u64_be(1), - gas: 4.into(), - init: vec![0x5], - value: 3.into(), - }), - result: Res::FailedCall(TraceError::BadInstruction), - trace_address: vec![].into_iter().collect(), - subtraces: 0 - }; - - assert!(f0.matches(&trace)); - assert!(f1.matches(&trace)); - assert!(!f2.matches(&trace)); - } + #[test] + fn filter_match_failed_contract_creation_fix_9822() { + let f0 = Filter { + range: (0..0), + from_address: vec![Address::from_low_u64_be(1)].into(), + to_address: vec![].into(), + }; + + let f1 = Filter { + range: (0..0), + from_address: vec![].into(), + to_address: vec![].into(), + }; + + let f2 = Filter { + range: (0..0), + from_address: vec![].into(), + to_address: vec![Address::from_low_u64_be(2)].into(), + }; + + let trace = FlatTrace { + action: Action::Create(Create { + from: Address::from_low_u64_be(1), + gas: 4.into(), + init: vec![0x5], + value: 3.into(), + }), + result: Res::FailedCall(TraceError::BadInstruction), + trace_address: vec![].into_iter().collect(), + subtraces: 0 + }; + assert!(f0.matches(&trace)); + assert!(f1.matches(&trace)); + assert!(!f2.matches(&trace)); + } } diff --git a/ethcore/src/trace/types/flat.rs b/ethcore/trace/src/types/flat.rs similarity index 95% rename from ethcore/src/trace/types/flat.rs rename to ethcore/trace/src/types/flat.rs index 17ea1d37887..203eb7f5db4 100644 --- a/ethcore/src/trace/types/flat.rs +++ b/ethcore/trace/src/types/flat.rs @@ -17,6 +17,7 @@ //! Flat trace module use rlp::{Rlp, RlpStream, Decodable, Encodable, DecoderError}; +use rlp_derive::{RlpEncodableWrapper, RlpDecodableWrapper}; use parity_util_mem::MallocSizeOf; use ethereum_types::Bloom; use super::trace::{Action, Res}; @@ -73,7 +74,7 @@ impl Decodable for FlatTrace { /// Represents all traces produced by a single transaction. #[derive(Debug, PartialEq, Clone, RlpEncodableWrapper, RlpDecodableWrapper, MallocSizeOf)] -pub struct FlatTransactionTraces(Vec); +pub struct FlatTransactionTraces(pub(crate) Vec); impl From> for FlatTransactionTraces { fn from(v: Vec) -> Self { @@ -96,7 +97,7 @@ impl Into> for FlatTransactionTraces { /// Represents all traces produced by transactions in a single block. #[derive(Debug, PartialEq, Clone, Default, RlpEncodableWrapper, RlpDecodableWrapper, MallocSizeOf)] -pub struct FlatBlockTraces(Vec); +pub struct FlatBlockTraces(pub(crate) Vec); impl From> for FlatBlockTraces { fn from(v: Vec) -> Self { @@ -120,10 +121,11 @@ impl Into> for FlatBlockTraces { #[cfg(test)] mod tests { use rlp::*; - use super::{FlatBlockTraces, FlatTransactionTraces, FlatTrace}; - use trace::trace::{Action, Res, CallResult, Call, Suicide, Reward}; + use crate::{ + FlatBlockTraces, FlatTransactionTraces, FlatTrace, + trace::{Action, Res, CallResult, Call, Suicide, Reward, RewardType} + }; use evm::CallType; - use trace::RewardType; #[test] fn encode_flat_transaction_traces() { diff --git a/ethcore/src/trace/types/localized.rs b/ethcore/trace/src/types/localized.rs similarity index 97% rename from ethcore/src/trace/types/localized.rs rename to ethcore/trace/src/types/localized.rs index 330d23a7289..28a8788e89f 100644 --- a/ethcore/src/trace/types/localized.rs +++ b/ethcore/trace/src/types/localized.rs @@ -18,7 +18,7 @@ use ethereum_types::H256; use super::trace::{Action, Res}; -use types::BlockNumber; +use common_types::BlockNumber; /// Localized trace. #[derive(Debug, PartialEq, Clone)] diff --git a/ethcore/src/trace/types/mod.rs b/ethcore/trace/src/types/mod.rs similarity index 100% rename from ethcore/src/trace/types/mod.rs rename to ethcore/trace/src/types/mod.rs diff --git a/ethcore/src/trace/types/trace.rs b/ethcore/trace/src/types/trace.rs similarity index 99% rename from ethcore/src/trace/types/trace.rs rename to ethcore/trace/src/types/trace.rs index ba10c9069e2..a0a92003157 100644 --- a/ethcore/src/trace/types/trace.rs +++ b/ethcore/trace/src/types/trace.rs @@ -14,12 +14,12 @@ // You should have received a copy of the GNU General Public License // along with Parity Ethereum. If not, see . -//! Tracing datatypes. +//! Tracing data types. use ethereum_types::{U256, Address, Bloom, BloomInput}; -use bytes::Bytes; +use parity_bytes::Bytes; use rlp::{Rlp, RlpStream, Encodable, DecoderError, Decodable}; - +use rlp_derive::{RlpEncodable, RlpDecodable}; use vm::ActionParams; use evm::CallType; use super::error::Error; diff --git a/ethcore/trie-vm-factories/Cargo.toml b/ethcore/trie-vm-factories/Cargo.toml new file mode 100644 index 00000000000..e0ca6b7061b --- /dev/null +++ b/ethcore/trie-vm-factories/Cargo.toml @@ -0,0 +1,15 @@ +[package] +description = "Collection of factories for VM and Tries" +name = "trie-vm-factories" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2018" + +[dependencies] +trie-db = "0.12.4" +ethtrie = { package = "patricia-trie-ethereum", path = "../../util/patricia-trie-ethereum" } +account-db = { path = "../account-db" } +evm = { path = "../evm" } +vm = { path = "../vm" } +wasm = { path = "../wasm" } +keccak-hasher = { path = "../../util/keccak-hasher" } diff --git a/ethcore/src/factory.rs b/ethcore/trie-vm-factories/src/lib.rs similarity index 97% rename from ethcore/src/factory.rs rename to ethcore/trie-vm-factories/src/lib.rs index 670900ca0b7..e1c9ed152af 100644 --- a/ethcore/src/factory.rs +++ b/ethcore/trie-vm-factories/src/lib.rs @@ -14,10 +14,9 @@ // You should have received a copy of the GNU General Public License // along with Parity Ethereum. If not, see . -use trie::TrieFactory; +use trie_db::TrieFactory; use ethtrie::RlpCodec; use account_db::Factory as AccountFactory; -use ethereum_types::U256; use evm::{Factory as EvmFactory, VMType}; use vm::{Exec, ActionParams, VersionedSchedule, Schedule}; use wasm::WasmInterpreter; diff --git a/evmbin/Cargo.toml b/evmbin/Cargo.toml index 57ed25289cb..a835674a460 100644 --- a/evmbin/Cargo.toml +++ b/evmbin/Cargo.toml @@ -12,16 +12,19 @@ path = "./src/main.rs" common-types = { path = "../ethcore/types" } docopt = "1.0" env_logger = "0.5" -ethcore = { path = "../ethcore", features = ["test-helpers", "json-tests", "to-pod-full"] } +ethcore = { path = "../ethcore", features = ["test-helpers", "json-tests"] } ethereum-types = "0.6.0" ethjson = { path = "../json" } evm = { path = "../ethcore/evm" } panic_hook = { path = "../util/panic-hook" } parity-bytes = "0.1" +pod = { path = "../ethcore/pod" } rustc-hex = "1.0" serde = "1.0" serde_derive = "1.0" serde_json = "1.0" +account-state = { path = "../ethcore/account-state" } +trace = { path = "../ethcore/trace" } vm = { path = "../ethcore/vm" } [dev-dependencies] diff --git a/evmbin/src/display/json.rs b/evmbin/src/display/json.rs index 195e00c7379..0989f9a0845 100644 --- a/evmbin/src/display/json.rs +++ b/evmbin/src/display/json.rs @@ -21,7 +21,7 @@ use std::mem; use ethereum_types::{U256, H256, BigEndianHash}; use bytes::ToPretty; -use ethcore::trace; +use trace; use display; use info as vm; diff --git a/evmbin/src/display/simple.rs b/evmbin/src/display/simple.rs index 58d4a704585..68457183538 100644 --- a/evmbin/src/display/simple.rs +++ b/evmbin/src/display/simple.rs @@ -16,7 +16,7 @@ //! Simple VM output. -use ethcore::trace; +use trace; use bytes::ToPretty; use display; diff --git a/evmbin/src/display/std_json.rs b/evmbin/src/display/std_json.rs index 1734c305e24..478cc24a848 100644 --- a/evmbin/src/display/std_json.rs +++ b/evmbin/src/display/std_json.rs @@ -21,8 +21,8 @@ use std::io; use ethereum_types::{H256, U256, BigEndianHash}; use bytes::ToPretty; -use ethcore::{trace, pod_state}; - +use trace; +use pod::PodState; use display; use info as vm; @@ -101,7 +101,7 @@ pub struct MessageFailure<'a> { #[derive(Serialize, Debug)] pub struct DumpData<'a> { root: &'a H256, - accounts: &'a pod_state::PodState, + accounts: &'a PodState, } #[derive(Serialize, Debug)] @@ -154,7 +154,7 @@ impl Informant { } } - fn dump_state_into(trace_sink: &mut Trace, root: H256, end_state: &Option) { + fn dump_state_into(trace_sink: &mut Trace, root: H256, end_state: &Option) { if let Some(ref end_state) = end_state { let dump_data = DumpData { diff --git a/evmbin/src/info.rs b/evmbin/src/info.rs index ca068c36b0d..091adf74ca0 100644 --- a/evmbin/src/info.rs +++ b/evmbin/src/info.rs @@ -19,10 +19,13 @@ use std::time::{Instant, Duration}; use ethereum_types::{H256, U256}; use ethcore::client::{self, EvmTestClient, EvmTestError, TransactErr, TransactSuccess}; -use ethcore::{state, state_db, trace, spec, pod_state, TrieSpec}; +use ethcore::{spec, TrieSpec}; +use trace; use ethjson; +use pod::PodState; use types::transaction; use vm::ActionParams; +use account_state::State; /// VM execution informant pub trait Informant: trace::VMTracer { @@ -52,7 +55,7 @@ pub struct Success { /// Traces pub traces: Option, /// Optional end state dump - pub end_state: Option, + pub end_state: Option, } /// Execution failed @@ -69,7 +72,7 @@ pub struct Failure { /// Traces pub traces: Option, /// Optional end state dump - pub end_state: Option, + pub end_state: Option, } /// EVM Execution result @@ -105,7 +108,7 @@ pub fn run_transaction( name: &str, idx: usize, spec: ðjson::spec::ForkSpec, - pre_state: &pod_state::PodState, + pre_state: &PodState, post_root: H256, env_info: &client::EnvInfo, transaction: transaction::SignedTransaction, @@ -152,19 +155,15 @@ pub fn run_transaction( T::finish(result, &mut sink) } -fn dump_state(state: &state::State) -> Option { - state.to_pod_full().ok() -} - /// Execute VM with given `ActionParams` pub fn run<'a, F, X>( spec: &'a spec::Spec, trie_spec: TrieSpec, initial_gas: U256, - pre_state: &'a pod_state::PodState, + pre_state: &'a PodState, run: F, ) -> RunResult where - F: FnOnce(EvmTestClient) -> (Result, EvmTestError>, H256, Option, Option, Option), + F: FnOnce(EvmTestClient) -> (Result, EvmTestError>, H256, Option, Option, Option), { let do_dump = trie_spec == TrieSpec::Fat; @@ -179,7 +178,7 @@ pub fn run<'a, F, X>( })?; if do_dump { - test_client.set_dump_state_fn(dump_state); + test_client.set_dump_state(); } let start = Instant::now(); diff --git a/evmbin/src/main.rs b/evmbin/src/main.rs index ee75f9174d1..845642c6027 100644 --- a/evmbin/src/main.rs +++ b/evmbin/src/main.rs @@ -41,7 +41,6 @@ extern crate rustc_hex; extern crate serde; #[macro_use] extern crate serde_derive; -#[macro_use] extern crate serde_json; extern crate docopt; extern crate parity_bytes as bytes; @@ -49,7 +48,10 @@ extern crate ethereum_types; extern crate vm; extern crate evm; extern crate panic_hook; +extern crate pod; extern crate env_logger; +extern crate account_state; +extern crate trace; #[cfg(test)] #[macro_use] diff --git a/rpc/Cargo.toml b/rpc/Cargo.toml index 46b43b5ad1d..585fc471dc5 100644 --- a/rpc/Cargo.toml +++ b/rpc/Cargo.toml @@ -60,7 +60,9 @@ parity-runtime = { path = "../util/runtime" } parity-updater = { path = "../updater" } parity-version = { path = "../util/version" } rlp = "0.4.0" +account-state = { path = "../ethcore/account-state" } stats = { path = "../util/stats" } +trace = { path = "../ethcore/trace" } vm = { path = "../ethcore/vm" } [dev-dependencies] @@ -68,6 +70,7 @@ ethcore = { path = "../ethcore", features = ["test-helpers"] } ethcore-accounts = { path = "../accounts" } ethcore-io = { path = "../util/io" } ethcore-network = { path = "../util/network" } +patricia-trie-ethereum = { path = "../util/patricia-trie-ethereum" } fake-fetch = { path = "../util/fake-fetch" } macros = { path = "../util/macros" } pretty_assertions = "0.1" diff --git a/rpc/src/lib.rs b/rpc/src/lib.rs index 6b856eb5291..08142b3636e 100644 --- a/rpc/src/lib.rs +++ b/rpc/src/lib.rs @@ -82,8 +82,10 @@ extern crate parity_updater as updater; extern crate parity_version as version; extern crate eip_712; extern crate rlp; +extern crate account_state; extern crate stats; extern crate tempdir; +extern crate trace; extern crate vm; #[cfg(any(test, feature = "ethcore-accounts"))] @@ -103,6 +105,8 @@ extern crate rand_xorshift; #[cfg(test)] extern crate ethjson; #[cfg(test)] +extern crate patricia_trie_ethereum as ethtrie; +#[cfg(test)] extern crate transaction_pool as txpool; #[cfg(test)] diff --git a/rpc/src/v1/impls/parity.rs b/rpc/src/v1/impls/parity.rs index e040ed52b92..d10e4674b38 100644 --- a/rpc/src/v1/impls/parity.rs +++ b/rpc/src/v1/impls/parity.rs @@ -23,7 +23,7 @@ use ethereum_types::{H64, H160, H256, H512, U64, U256}; use ethcore::client::{BlockChainClient, StateClient, Call}; use ethcore::miner::{self, MinerService, FilterOptions}; use ethcore::snapshot::{SnapshotService, RestorationStatus}; -use ethcore::state::StateInfo; +use account_state::state::StateInfo; use ethcore_logger::RotatingLogger; use ethkey::{crypto::ecies, Brain, Generator}; use ethstore::random_phrase; diff --git a/rpc/src/v1/tests/helpers/miner_service.rs b/rpc/src/v1/tests/helpers/miner_service.rs index 694dd96626b..63dc2fa898b 100644 --- a/rpc/src/v1/tests/helpers/miner_service.rs +++ b/rpc/src/v1/tests/helpers/miner_service.rs @@ -21,14 +21,16 @@ use std::collections::{BTreeMap, BTreeSet, HashMap}; use bytes::Bytes; use ethcore::block::SealedBlock; -use ethcore::client::{Nonce, PrepareOpenBlock, StateClient, EngineInfo}; +use ethcore::client::{Nonce, PrepareOpenBlock, StateClient, EngineInfo, TestState}; use ethcore::engines::{Engine, signer::EngineSigner}; use ethcore::error::Error; use ethcore::miner::{self, MinerService, AuthoringParams, FilterOptions}; use ethereum_types::{H256, U256, Address}; +use ethtrie; use miner::pool::local_transactions::Status as LocalTransactionStatus; use miner::pool::{verifier, VerifiedTransaction, QueueStatus}; use parking_lot::{RwLock, Mutex}; +use account_state::state::StateInfo; use types::transaction::{self, UnverifiedTransaction, SignedTransaction, PendingTransaction}; use txpool; use types::BlockNumber; @@ -87,14 +89,14 @@ impl TestMinerService { impl StateClient for TestMinerService { // State will not be used by test client anyway, since all methods that accept state are mocked - type State = (); + type State = TestState; fn latest_state(&self) -> Self::State { - () + TestState } fn state_at(&self, _id: BlockId) -> Option { - Some(()) + Some(TestState) } } @@ -105,7 +107,7 @@ impl EngineInfo for TestMinerService { } impl MinerService for TestMinerService { - type State = (); + type State = TestState; fn pending_state(&self, _latest_block_number: BlockNumber) -> Option { None diff --git a/rpc/src/v1/tests/mocked/eth.rs b/rpc/src/v1/tests/mocked/eth.rs index e9af200f7ed..39da9aa2abc 100644 --- a/rpc/src/v1/tests/mocked/eth.rs +++ b/rpc/src/v1/tests/mocked/eth.rs @@ -100,13 +100,13 @@ impl EthTester { EthTester { runtime, - client: client, - sync: sync, + client, + sync, accounts_provider: ap, - miner: miner, - snapshot: snapshot, - io: io, - hashrates: hashrates, + miner, + snapshot, + io, + hashrates, } } diff --git a/rpc/src/v1/tests/mocked/traces.rs b/rpc/src/v1/tests/mocked/traces.rs index 8f0f0eaa0c6..d6cb0399b2f 100644 --- a/rpc/src/v1/tests/mocked/traces.rs +++ b/rpc/src/v1/tests/mocked/traces.rs @@ -17,8 +17,8 @@ use std::sync::Arc; use ethcore::executed::{Executed, CallError}; -use ethcore::trace::trace::{Action, Res, Call}; -use ethcore::trace::LocalizedTrace; +use trace::trace::{Action, Res, Call}; +use trace::LocalizedTrace; use ethcore::client::TestBlockChainClient; use ethereum_types::{Address, H256}; diff --git a/rpc/src/v1/types/trace.rs b/rpc/src/v1/types/trace.rs index b8fc2c001b3..ca6c89e9ef6 100644 --- a/rpc/src/v1/types/trace.rs +++ b/rpc/src/v1/types/trace.rs @@ -17,8 +17,8 @@ use std::collections::BTreeMap; use ethcore::client::Executed; -use ethcore::trace as et; -use ethcore::trace::{FlatTrace, LocalizedTrace as EthLocalizedTrace, trace, TraceError}; +use trace as et; +use trace::{FlatTrace, LocalizedTrace as EthLocalizedTrace, trace, TraceError}; use ethereum_types::{H160, H256, U256}; use serde::ser::SerializeStruct; use serde::{Serialize, Serializer}; @@ -656,7 +656,7 @@ mod tests { use serde_json; use std::collections::BTreeMap; use v1::types::Bytes; - use ethcore::trace::TraceError; + use trace::TraceError; use ethereum_types::Address; use super::*;