Skip to content
This repository has been archived by the owner on Nov 6, 2020. It is now read-only.

Add a optional json dump state to evm-bin #9706

Merged
merged 16 commits into from
Nov 25, 2018
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 5 additions & 1 deletion ethcore/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ unexpected = { path = "../util/unexpected" }
journaldb = { path = "../util/journaldb" }
keccak-hasher = { path = "../util/keccak-hasher" }
kvdb-rocksdb = "0.1.3"
serde = "1.0"
serde_derive = "1.0"
tempdir = {version="0.3", optional = true}

[target.'cfg(any(target_os = "linux", target_os = "macos", target_os = "windows", target_os = "android"))'.dependencies]
Expand Down Expand Up @@ -101,7 +103,7 @@ evm-debug-tests = ["evm-debug", "evm/evm-debug-tests"]
# EVM debug traces are printed.
slow-blocks = []
# Run JSON consensus tests.
json-tests = ["ethcore-transaction/json-tests", "test-helpers", "tempdir"]
json-tests = ["ethcore-transaction/json-tests", "test-helpers", "tempdir", "to-pod-full"]
# Skip JSON consensus tests with pending issues.
ci-skip-issue = []
# Run memory/cpu heavy tests.
Expand All @@ -110,3 +112,5 @@ test-heavy = []
benches = []
# Compile test helpers
test-helpers = ["tempdir"]
# Enables slow 'to-pod-full' method for use in tests and evmbin.
to-pod-full = []
56 changes: 46 additions & 10 deletions ethcore/src/client/evm_test_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,11 @@ use ethjson::spec::ForkSpec;
pub struct EvmTestClient<'a> {
state: state::State<state_db::StateDB>,
spec: &'a spec::Spec,
dump_state: fn(&state::State<state_db::StateDB>) -> Option<pod_state::PodState>,
}

fn no_dump_state(_: &state::State<state_db::StateDB>) -> Option<pod_state::PodState> {
None
}

impl<'a> fmt::Debug for EvmTestClient<'a> {
Expand All @@ -92,32 +97,51 @@ 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<state_db::StateDB>) -> Option<pod_state::PodState>) {
self.dump_state = dump_state;
}

/// Creates new EVM test client with in-memory DB initialized with genesis of given Spec.
pub fn new(spec: &'a spec::Spec) -> Result<Self, EvmTestError> {
let factories = Self::factories();
/// Takes a `TrieSpec` to set the type of trie.
pub fn new_with_trie(spec: &'a spec::Spec, trie_spec: trie::TrieSpec) -> Result<Self, EvmTestError> {
let factories = Self::factories(trie_spec);
let state = Self::state_from_spec(spec, &factories)?;

Ok(EvmTestClient {
state,
spec,
dump_state: no_dump_state,
})
}

/// Creates new EVM test client with in-memory DB initialized with given PodState.
pub fn from_pod_state(spec: &'a spec::Spec, pod_state: pod_state::PodState) -> Result<Self, EvmTestError> {
let factories = Self::factories();
/// Creates new EVM test client with an in-memory DB initialized with genesis of given chain Spec.
pub fn new(spec: &'a spec::Spec) -> Result<Self, EvmTestError> {
Self::new_with_trie(spec, trie::TrieSpec::Secure)
}

/// 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<Self, EvmTestError> {
let factories = Self::factories(trie_spec);
let state = Self::state_from_pod(spec, &factories, pod_state)?;

Ok(EvmTestClient {
state,
spec,
dump_state: no_dump_state,
})
}

fn factories() -> Factories {
/// 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<Self, EvmTestError> {
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),
trie: trie::TrieFactory::new(trie::TrieSpec::Secure),
trie: trie::TrieFactory::new(trie_spec),
accountdb: Default::default(),
}
}
Expand Down Expand Up @@ -223,6 +247,7 @@ impl<'a> EvmTestClient<'a> {
return TransactResult::Err {
state_root: *self.state.root(),
error: error.into(),
end_state: (self.dump_state)(&self.state),
};
}

Expand All @@ -247,12 +272,17 @@ impl<'a> EvmTestClient<'a> {
&None,
false
).ok();

self.state.commit().ok();

let state_root = *self.state.root();

let end_state = (self.dump_state)(&self.state);

match result {
Ok(result) => {
TransactResult::Ok {
state_root: *self.state.root(),
state_root,
gas_left: initial_gas - result.receipt.gas_used,
outcome: result.receipt.outcome,
output: result.output,
Expand All @@ -263,12 +293,14 @@ impl<'a> EvmTestClient<'a> {
Some(executive::contract_address(scheme, &transaction.sender(), &transaction.nonce, &transaction.data).0)
} else {
None
}
},
end_state,
}
},
Err(error) => TransactResult::Err {
state_root: *self.state.root(),
state_root,
error,
end_state,
},
}
}
Expand All @@ -295,12 +327,16 @@ pub enum TransactResult<T, V> {
logs: Vec<log_entry::LogEntry>,
/// outcome
outcome: receipt::TransactionOutcome,
/// end state if needed
end_state: Option<pod_state::PodState>,
},
/// Transaction failed to run
Err {
/// State root
state_root: H256,
/// Execution error
error: ::error::Error,
/// end state if needed
end_state: Option<pod_state::PodState>,
},
}
2 changes: 1 addition & 1 deletion ethcore/src/json_tests/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ pub fn json_chain_test<H: FnMut(&str, HookType)>(json_data: &[u8], start_stop_ho
flushln!("{} fail", info);
failed.push(name.clone());
},
Ok(TransactResult::Err { state_root, ref error }) if state_root != post_root => {
Ok(TransactResult::Err { state_root, ref error, .. }) if state_root != post_root => {
println!("{} !!! State mismatch (got: {}, expect: {}", info, state_root, post_root);
println!("{} !!! Execution error: {:?}", info, error);
flushln!("{} fail", info);
Expand Down
4 changes: 4 additions & 0 deletions ethcore/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ extern crate vm;
extern crate wasm;
extern crate memory_cache;
extern crate journaldb;
extern crate serde;
#[cfg(any(test, feature = "json-tests", feature = "test-helpers"))]
extern crate tempdir;

Expand All @@ -135,6 +136,8 @@ 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;
Expand Down Expand Up @@ -185,3 +188,4 @@ pub use types::*;
pub use executive::contract_address;
pub use evm::CreateContractAddress;
pub use blockchain::{BlockChainDB, BlockChainDBHandler};
pub use trie::TrieSpec;
11 changes: 10 additions & 1 deletion ethcore/src/pod_account.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,21 +31,30 @@ use state::Account;
use ethjson;
use types::account_diff::*;
use rlp::{self, RlpStream};
use serde::Serializer;
use rustc_hex::ToHex;

#[derive(Debug, Clone, PartialEq, Eq)]
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
/// An account, expressed as Plain-Old-Data (hence the name).
/// Does not have a DB overlay cache, code hash or anything like that.
pub struct PodAccount {
/// The balance of the account.
pub balance: U256,
/// The nonce of the account.
pub nonce: U256,
#[serde(serialize_with="opt_bytes_to_hex")]
/// The code of the account or `None` in the special case that it is unknown.
pub code: Option<Bytes>,
/// The storage of the account.
pub storage: BTreeMap<H256, H256>,
}

fn opt_bytes_to_hex<S>(opt_bytes: &Option<Bytes>, serializer: S) -> Result<S::Ok, S::Error>
where S: Serializer
{
serializer.collect_str(&format_args!("0x{}",opt_bytes.as_ref().map(|b|b.to_hex()).unwrap_or("".to_string())))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bytes does not impl ToLowerHex (so you can use {:#x})? If not that's something we should fix.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It does not implement it, alas Bytes is only a type alias for Vec<u8>, so it should be quite a refact to fix it.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider to replace map().unwrap_or() with map_or_else!

}

impl PodAccount {
/// Convert Account to a PodAccount.
/// NOTE: This will silently fail unless the account is fully cached.
Expand Down
4 changes: 2 additions & 2 deletions ethcore/src/pod_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ use types::state_diff::StateDiff;
use ethjson;

/// State of all accounts in the system expressed in Plain Old Data.
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct PodState (BTreeMap<Address, PodAccount>);
#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize)]
pub struct PodState(BTreeMap<Address, PodAccount>);

impl PodState {
/// Contruct a new object from the `m`.
Expand Down
2 changes: 1 addition & 1 deletion ethcore/src/state/account.rs
Original file line number Diff line number Diff line change
Expand Up @@ -620,7 +620,7 @@ mod tests {
assert!(raw.len() > compact_vec.len());
let again_raw = decompress(&compact_vec, snapshot_swapper());
assert_eq!(raw, again_raw.into_vec());
}
}

#[test]
fn storage_at() {
Expand Down
Loading