Add a optional json dump state to evm-bin (#9706)
* Add a dump of the state at the end of transaction for --json-test. Also fixes json-test output on finish, and allow to put both on err or out (--out-only and --err-only). * Dump state resolution from trie, function behind evm-bin feature to avoid misuse. * Rename 'slow' method to 'to_pod_full'. Use cache first in 'to_pod_full', for in between commits case. Change dump activation to use a function pointer instead. * Fix tests. * Query and add storage values to dump. * Switch to use `require` method, even if less efficient it is better in this case to reuse existing code. Reuse of `storage_at` was not easy in this case (could not iterate and use the method at the same time (refcell mutable borrow panics) so keeping code as is. * Switch to returning error. Use 'base_storage_root' instead of 'storage_root'. Added a test, it will only execute with json-test in ci, or when launch with the feature. * Renaming of command line parameters. Comments fixes. Minor code changes. * Fix evmbin cmd parsing test. * README update. * Fix extra space and avoid clone call on copiable address. * Revert test submodule. * Revert wasm-test submodule. * Use map_or instead of map + unwrap_or * restore tests submodule
This commit is contained in:
parent
34d22a35dd
commit
832c4a7565
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -685,6 +685,8 @@ dependencies = [
|
|||||||
"rlp_compress 0.1.0",
|
"rlp_compress 0.1.0",
|
||||||
"rlp_derive 0.1.0",
|
"rlp_derive 0.1.0",
|
||||||
"rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"serde 1.0.78 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"serde_derive 1.0.78 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"stats 0.1.0",
|
"stats 0.1.0",
|
||||||
"stop-guard 0.1.0",
|
"stop-guard 0.1.0",
|
||||||
"tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
"tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
@ -67,6 +67,8 @@ unexpected = { path = "../util/unexpected" }
|
|||||||
journaldb = { path = "../util/journaldb" }
|
journaldb = { path = "../util/journaldb" }
|
||||||
keccak-hasher = { path = "../util/keccak-hasher" }
|
keccak-hasher = { path = "../util/keccak-hasher" }
|
||||||
kvdb-rocksdb = "0.1.3"
|
kvdb-rocksdb = "0.1.3"
|
||||||
|
serde = "1.0"
|
||||||
|
serde_derive = "1.0"
|
||||||
tempdir = {version="0.3", optional = true}
|
tempdir = {version="0.3", optional = true}
|
||||||
|
|
||||||
[target.'cfg(any(target_os = "linux", target_os = "macos", target_os = "windows", target_os = "android"))'.dependencies]
|
[target.'cfg(any(target_os = "linux", target_os = "macos", target_os = "windows", target_os = "android"))'.dependencies]
|
||||||
@ -103,13 +105,15 @@ evm-debug-tests = ["evm-debug", "evm/evm-debug-tests"]
|
|||||||
# EVM debug traces are printed.
|
# EVM debug traces are printed.
|
||||||
slow-blocks = []
|
slow-blocks = []
|
||||||
# Run JSON consensus tests.
|
# 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.
|
# Skip JSON consensus tests with pending issues.
|
||||||
ci-skip-issue = []
|
ci-skip-issue = []
|
||||||
# Run memory/cpu heavy tests.
|
# Run memory/cpu heavy tests.
|
||||||
test-heavy = []
|
test-heavy = []
|
||||||
# Compile test helpers
|
# Compile test helpers
|
||||||
test-helpers = ["tempdir"]
|
test-helpers = ["tempdir"]
|
||||||
|
# Enables slow 'to-pod-full' method for use in tests and evmbin.
|
||||||
|
to-pod-full = []
|
||||||
|
|
||||||
[[bench]]
|
[[bench]]
|
||||||
name = "builtin"
|
name = "builtin"
|
||||||
|
@ -66,6 +66,11 @@ use ethjson::spec::ForkSpec;
|
|||||||
pub struct EvmTestClient<'a> {
|
pub struct EvmTestClient<'a> {
|
||||||
state: state::State<state_db::StateDB>,
|
state: state::State<state_db::StateDB>,
|
||||||
spec: &'a spec::Spec,
|
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> {
|
impl<'a> fmt::Debug for EvmTestClient<'a> {
|
||||||
@ -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.
|
/// 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> {
|
/// Takes a `TrieSpec` to set the type of trie.
|
||||||
let factories = Self::factories();
|
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)?;
|
let state = Self::state_from_spec(spec, &factories)?;
|
||||||
|
|
||||||
Ok(EvmTestClient {
|
Ok(EvmTestClient {
|
||||||
state,
|
state,
|
||||||
spec,
|
spec,
|
||||||
|
dump_state: no_dump_state,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates new EVM test client with in-memory DB initialized with given PodState.
|
/// Creates new EVM test client with an in-memory DB initialized with genesis of given chain Spec.
|
||||||
pub fn from_pod_state(spec: &'a spec::Spec, pod_state: pod_state::PodState) -> Result<Self, EvmTestError> {
|
pub fn new(spec: &'a spec::Spec) -> Result<Self, EvmTestError> {
|
||||||
let factories = Self::factories();
|
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)?;
|
let state = Self::state_from_pod(spec, &factories, pod_state)?;
|
||||||
|
|
||||||
Ok(EvmTestClient {
|
Ok(EvmTestClient {
|
||||||
state,
|
state,
|
||||||
spec,
|
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 {
|
Factories {
|
||||||
vm: factory::VmFactory::new(VMType::Interpreter, 5 * 1024),
|
vm: factory::VmFactory::new(VMType::Interpreter, 5 * 1024),
|
||||||
trie: trie::TrieFactory::new(trie::TrieSpec::Secure),
|
trie: trie::TrieFactory::new(trie_spec),
|
||||||
accountdb: Default::default(),
|
accountdb: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -223,6 +247,7 @@ impl<'a> EvmTestClient<'a> {
|
|||||||
return TransactResult::Err {
|
return TransactResult::Err {
|
||||||
state_root: *self.state.root(),
|
state_root: *self.state.root(),
|
||||||
error: error.into(),
|
error: error.into(),
|
||||||
|
end_state: (self.dump_state)(&self.state),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -247,12 +272,17 @@ impl<'a> EvmTestClient<'a> {
|
|||||||
&None,
|
&None,
|
||||||
false
|
false
|
||||||
).ok();
|
).ok();
|
||||||
|
|
||||||
self.state.commit().ok();
|
self.state.commit().ok();
|
||||||
|
|
||||||
|
let state_root = *self.state.root();
|
||||||
|
|
||||||
|
let end_state = (self.dump_state)(&self.state);
|
||||||
|
|
||||||
match result {
|
match result {
|
||||||
Ok(result) => {
|
Ok(result) => {
|
||||||
TransactResult::Ok {
|
TransactResult::Ok {
|
||||||
state_root: *self.state.root(),
|
state_root,
|
||||||
gas_left: initial_gas - result.receipt.gas_used,
|
gas_left: initial_gas - result.receipt.gas_used,
|
||||||
outcome: result.receipt.outcome,
|
outcome: result.receipt.outcome,
|
||||||
output: result.output,
|
output: result.output,
|
||||||
@ -263,12 +293,14 @@ impl<'a> EvmTestClient<'a> {
|
|||||||
Some(executive::contract_address(scheme, &transaction.sender(), &transaction.nonce, &transaction.data).0)
|
Some(executive::contract_address(scheme, &transaction.sender(), &transaction.nonce, &transaction.data).0)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
},
|
||||||
|
end_state,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Err(error) => TransactResult::Err {
|
Err(error) => TransactResult::Err {
|
||||||
state_root: *self.state.root(),
|
state_root,
|
||||||
error,
|
error,
|
||||||
|
end_state,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -295,6 +327,8 @@ pub enum TransactResult<T, V> {
|
|||||||
logs: Vec<log_entry::LogEntry>,
|
logs: Vec<log_entry::LogEntry>,
|
||||||
/// outcome
|
/// outcome
|
||||||
outcome: receipt::TransactionOutcome,
|
outcome: receipt::TransactionOutcome,
|
||||||
|
/// end state if needed
|
||||||
|
end_state: Option<pod_state::PodState>,
|
||||||
},
|
},
|
||||||
/// Transaction failed to run
|
/// Transaction failed to run
|
||||||
Err {
|
Err {
|
||||||
@ -302,5 +336,7 @@ pub enum TransactResult<T, V> {
|
|||||||
state_root: H256,
|
state_root: H256,
|
||||||
/// Execution error
|
/// Execution error
|
||||||
error: ::error::Error,
|
error: ::error::Error,
|
||||||
|
/// end state if needed
|
||||||
|
end_state: Option<pod_state::PodState>,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -95,7 +95,7 @@ pub fn json_chain_test<H: FnMut(&str, HookType)>(json_data: &[u8], start_stop_ho
|
|||||||
flushln!("{} fail", info);
|
flushln!("{} fail", info);
|
||||||
failed.push(name.clone());
|
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!("{} !!! State mismatch (got: {}, expect: {}", info, state_root, post_root);
|
||||||
println!("{} !!! Execution error: {:?}", info, error);
|
println!("{} !!! Execution error: {:?}", info, error);
|
||||||
flushln!("{} fail", info);
|
flushln!("{} fail", info);
|
||||||
|
@ -109,6 +109,7 @@ extern crate vm;
|
|||||||
extern crate wasm;
|
extern crate wasm;
|
||||||
extern crate memory_cache;
|
extern crate memory_cache;
|
||||||
extern crate journaldb;
|
extern crate journaldb;
|
||||||
|
extern crate serde;
|
||||||
#[cfg(any(test, feature = "json-tests", feature = "test-helpers"))]
|
#[cfg(any(test, feature = "json-tests", feature = "test-helpers"))]
|
||||||
extern crate tempdir;
|
extern crate tempdir;
|
||||||
|
|
||||||
@ -134,6 +135,8 @@ extern crate macros;
|
|||||||
extern crate rlp_derive;
|
extern crate rlp_derive;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate trace_time;
|
extern crate trace_time;
|
||||||
|
#[macro_use]
|
||||||
|
extern crate serde_derive;
|
||||||
|
|
||||||
#[cfg_attr(test, macro_use)]
|
#[cfg_attr(test, macro_use)]
|
||||||
extern crate evm;
|
extern crate evm;
|
||||||
@ -187,3 +190,4 @@ pub use types::*;
|
|||||||
pub use executive::contract_address;
|
pub use executive::contract_address;
|
||||||
pub use evm::CreateContractAddress;
|
pub use evm::CreateContractAddress;
|
||||||
pub use blockchain::{BlockChainDB, BlockChainDBHandler};
|
pub use blockchain::{BlockChainDB, BlockChainDBHandler};
|
||||||
|
pub use trie::TrieSpec;
|
||||||
|
@ -32,8 +32,10 @@ use state::Account;
|
|||||||
use ethjson;
|
use ethjson;
|
||||||
use types::account_diff::*;
|
use types::account_diff::*;
|
||||||
use rlp::{self, RlpStream};
|
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).
|
/// An account, expressed as Plain-Old-Data (hence the name).
|
||||||
/// Does not have a DB overlay cache, code hash or anything like that.
|
/// Does not have a DB overlay cache, code hash or anything like that.
|
||||||
pub struct PodAccount {
|
pub struct PodAccount {
|
||||||
@ -41,12 +43,19 @@ pub struct PodAccount {
|
|||||||
pub balance: U256,
|
pub balance: U256,
|
||||||
/// The nonce of the account.
|
/// The nonce of the account.
|
||||||
pub nonce: U256,
|
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.
|
/// The code of the account or `None` in the special case that it is unknown.
|
||||||
pub code: Option<Bytes>,
|
pub code: Option<Bytes>,
|
||||||
/// The storage of the account.
|
/// The storage of the account.
|
||||||
pub storage: BTreeMap<H256, H256>,
|
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_or("".to_string(), |b|b.to_hex())))
|
||||||
|
}
|
||||||
|
|
||||||
impl PodAccount {
|
impl PodAccount {
|
||||||
/// Convert Account to a PodAccount.
|
/// Convert Account to a PodAccount.
|
||||||
/// NOTE: This will silently fail unless the account is fully cached.
|
/// NOTE: This will silently fail unless the account is fully cached.
|
||||||
|
@ -26,8 +26,8 @@ use types::state_diff::StateDiff;
|
|||||||
use ethjson;
|
use ethjson;
|
||||||
|
|
||||||
/// State of all accounts in the system expressed in Plain Old Data.
|
/// State of all accounts in the system expressed in Plain Old Data.
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Default)]
|
#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize)]
|
||||||
pub struct PodState (BTreeMap<Address, PodAccount>);
|
pub struct PodState(BTreeMap<Address, PodAccount>);
|
||||||
|
|
||||||
impl PodState {
|
impl PodState {
|
||||||
/// Contruct a new object from the `m`.
|
/// Contruct a new object from the `m`.
|
||||||
|
@ -624,7 +624,7 @@ mod tests {
|
|||||||
assert!(raw.len() > compact_vec.len());
|
assert!(raw.len() > compact_vec.len());
|
||||||
let again_raw = decompress(&compact_vec, snapshot_swapper());
|
let again_raw = decompress(&compact_vec, snapshot_swapper());
|
||||||
assert_eq!(raw, again_raw.into_vec());
|
assert_eq!(raw, again_raw.into_vec());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn storage_at() {
|
fn storage_at() {
|
||||||
|
@ -947,20 +947,78 @@ impl<B: Backend> State<B> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Populate a PodAccount map from this state.
|
/// Populate a PodAccount map from this state.
|
||||||
pub fn to_pod(&self) -> PodState {
|
fn to_pod_cache(&self) -> PodState {
|
||||||
assert!(self.checkpoints.borrow().is_empty());
|
assert!(self.checkpoints.borrow().is_empty());
|
||||||
// TODO: handle database rather than just the cache.
|
|
||||||
// will need fat db.
|
|
||||||
PodState::from(self.cache.borrow().iter().fold(BTreeMap::new(), |mut m, (add, opt)| {
|
PodState::from(self.cache.borrow().iter().fold(BTreeMap::new(), |mut m, (add, opt)| {
|
||||||
if let Some(ref acc) = opt.account {
|
if let Some(ref acc) = opt.account {
|
||||||
m.insert(add.clone(), PodAccount::from_account(acc));
|
m.insert(*add, PodAccount::from_account(acc));
|
||||||
}
|
}
|
||||||
m
|
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<PodState, Error> {
|
||||||
|
|
||||||
|
assert!(self.checkpoints.borrow().is_empty());
|
||||||
|
assert!(self.factories.trie.is_fat());
|
||||||
|
|
||||||
|
let mut result = BTreeMap::new();
|
||||||
|
|
||||||
|
let trie = self.factories.trie.readonly(self.db.as_hashdb(), &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<PodAccount, Error> {
|
||||||
|
let mut pod_storage = BTreeMap::new();
|
||||||
|
let addr_hash = account.address_hash(address);
|
||||||
|
let accountdb = self.factories.accountdb.readonly(self.db.as_hashdb(), addr_hash);
|
||||||
|
let root = account.base_storage_root();
|
||||||
|
|
||||||
|
let trie = self.factories.trie.readonly(accountdb.as_hashdb(), &root)?;
|
||||||
|
for o_kv in trie.iter()? {
|
||||||
|
if let Ok((key, val)) = o_kv {
|
||||||
|
pod_storage.insert(key[..].into(), U256::from(&val[..]).into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut pod_account = PodAccount::from_account(&account);
|
||||||
|
// 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.
|
/// Populate a PodAccount map from this state, with another state as the account and storage query.
|
||||||
pub fn to_pod_diff<X: Backend>(&mut self, query: &State<X>) -> TrieResult<PodState> {
|
fn to_pod_diff<X: Backend>(&mut self, query: &State<X>) -> TrieResult<PodState> {
|
||||||
assert!(self.checkpoints.borrow().is_empty());
|
assert!(self.checkpoints.borrow().is_empty());
|
||||||
|
|
||||||
// Merge PodAccount::to_pod for cache of self and `query`.
|
// Merge PodAccount::to_pod for cache of self and `query`.
|
||||||
@ -1015,7 +1073,7 @@ impl<B: Backend> State<B> {
|
|||||||
/// Returns a `StateDiff` describing the difference from `orig` to `self`.
|
/// Returns a `StateDiff` describing the difference from `orig` to `self`.
|
||||||
/// Consumes self.
|
/// Consumes self.
|
||||||
pub fn diff_from<X: Backend>(&self, mut orig: State<X>) -> TrieResult<StateDiff> {
|
pub fn diff_from<X: Backend>(&self, mut orig: State<X>) -> TrieResult<StateDiff> {
|
||||||
let pod_state_post = self.to_pod();
|
let pod_state_post = self.to_pod_cache();
|
||||||
let pod_state_pre = orig.to_pod_diff(self)?;
|
let pod_state_pre = orig.to_pod_diff(self)?;
|
||||||
Ok(pod_state::diff_pod(&pod_state_pre, &pod_state_post))
|
Ok(pod_state::diff_pod(&pod_state_pre, &pod_state_post))
|
||||||
}
|
}
|
||||||
@ -2593,12 +2651,12 @@ mod tests {
|
|||||||
assert_eq!(diff_map.len(), 1);
|
assert_eq!(diff_map.len(), 1);
|
||||||
assert!(diff_map.get(&a).is_some());
|
assert!(diff_map.get(&a).is_some());
|
||||||
assert_eq!(diff_map.get(&a),
|
assert_eq!(diff_map.get(&a),
|
||||||
pod_account::diff_pod(Some(&PodAccount {
|
pod_account::diff_pod(Some(&PodAccount {
|
||||||
balance: U256::from(100),
|
balance: U256::from(100),
|
||||||
nonce: U256::zero(),
|
nonce: U256::zero(),
|
||||||
code: Some(Default::default()),
|
code: Some(Default::default()),
|
||||||
storage: Default::default()
|
storage: Default::default()
|
||||||
}), None).as_ref());
|
}), None).as_ref());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -2624,18 +2682,64 @@ mod tests {
|
|||||||
assert_eq!(diff_map.len(), 1);
|
assert_eq!(diff_map.len(), 1);
|
||||||
assert!(diff_map.get(&a).is_some());
|
assert!(diff_map.get(&a).is_some());
|
||||||
assert_eq!(diff_map.get(&a),
|
assert_eq!(diff_map.get(&a),
|
||||||
pod_account::diff_pod(Some(&PodAccount {
|
pod_account::diff_pod(Some(&PodAccount {
|
||||||
balance: U256::zero(),
|
balance: U256::zero(),
|
||||||
nonce: U256::zero(),
|
nonce: U256::zero(),
|
||||||
code: Some(Default::default()),
|
code: Some(Default::default()),
|
||||||
storage: vec![(H256::from(&U256::from(1u64)), H256::from(&U256::from(20u64)))]
|
storage: vec![(H256::from(&U256::from(1u64)), H256::from(&U256::from(20u64)))]
|
||||||
.into_iter().collect(),
|
.into_iter().collect(),
|
||||||
}), Some(&PodAccount {
|
}), Some(&PodAccount {
|
||||||
balance: U256::zero(),
|
balance: U256::zero(),
|
||||||
nonce: U256::zero(),
|
nonce: U256::zero(),
|
||||||
code: Some(Default::default()),
|
code: Some(Default::default()),
|
||||||
storage: vec![(H256::from(&U256::from(1u64)), H256::from(&U256::from(100u64)))]
|
storage: vec![(H256::from(&U256::from(1u64)), H256::from(&U256::from(100u64)))]
|
||||||
.into_iter().collect(),
|
.into_iter().collect(),
|
||||||
})).as_ref());
|
})).as_ref());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature="to-pod-full")]
|
||||||
|
#[test]
|
||||||
|
fn should_get_full_pod_storage_values() {
|
||||||
|
use trie::{TrieFactory, TrieSpec};
|
||||||
|
|
||||||
|
let a = 10.into();
|
||||||
|
let db = get_temp_state_db();
|
||||||
|
|
||||||
|
let factories = Factories {
|
||||||
|
vm: Default::default(),
|
||||||
|
trie: TrieFactory::new(TrieSpec::Fat),
|
||||||
|
accountdb: Default::default(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let get_pod_state_val = |pod_state : &PodState, ak, k| {
|
||||||
|
pod_state.get().get(ak).unwrap().storage.get(&k).unwrap().clone()
|
||||||
|
};
|
||||||
|
|
||||||
|
let storage_address = H256::from(&U256::from(1u64));
|
||||||
|
|
||||||
|
let (root, db) = {
|
||||||
|
let mut state = State::new(db, U256::from(0), factories.clone());
|
||||||
|
state.set_storage(&a, storage_address.clone(), H256::from(&U256::from(20u64))).unwrap();
|
||||||
|
let dump = state.to_pod_full().unwrap();
|
||||||
|
assert_eq!(get_pod_state_val(&dump, &a, storage_address.clone()), H256::from(&U256::from(20u64)));
|
||||||
|
state.commit().unwrap();
|
||||||
|
let dump = state.to_pod_full().unwrap();
|
||||||
|
assert_eq!(get_pod_state_val(&dump, &a, storage_address.clone()), H256::from(&U256::from(20u64)));
|
||||||
|
state.drop()
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut state = State::from_existing(db, root, U256::from(0u8), factories).unwrap();
|
||||||
|
let dump = state.to_pod_full().unwrap();
|
||||||
|
assert_eq!(get_pod_state_val(&dump, &a, storage_address.clone()), H256::from(&U256::from(20u64)));
|
||||||
|
state.set_storage(&a, storage_address.clone(), H256::from(&U256::from(21u64))).unwrap();
|
||||||
|
let dump = state.to_pod_full().unwrap();
|
||||||
|
assert_eq!(get_pod_state_val(&dump, &a, storage_address.clone()), H256::from(&U256::from(21u64)));
|
||||||
|
state.commit().unwrap();
|
||||||
|
state.set_storage(&a, storage_address.clone(), H256::from(&U256::from(0u64))).unwrap();
|
||||||
|
let dump = state.to_pod_full().unwrap();
|
||||||
|
assert_eq!(get_pod_state_val(&dump, &a, storage_address.clone()), H256::from(&U256::from(0u64)));
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -96,6 +96,7 @@ pub trait VMTracer: Send {
|
|||||||
|
|
||||||
/// Consumes self and returns the VM trace.
|
/// Consumes self and returns the VM trace.
|
||||||
fn drain(self) -> Option<Self::Output>;
|
fn drain(self) -> Option<Self::Output>;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `DbExtras` provides an interface to query extra data which is not stored in tracesdb,
|
/// `DbExtras` provides an interface to query extra data which is not stored in tracesdb,
|
||||||
|
@ -66,7 +66,7 @@ impl Fail {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for Fail {
|
impl fmt::Display for Fail {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
use self::Fail::*;
|
use self::Fail::*;
|
||||||
match *self {
|
match *self {
|
||||||
Return { ref expected, ref actual } =>
|
Return { ref expected, ref actual } =>
|
||||||
|
@ -11,7 +11,7 @@ path = "./src/main.rs"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
docopt = "1.0"
|
docopt = "1.0"
|
||||||
env_logger = "0.5"
|
env_logger = "0.5"
|
||||||
ethcore = { path = "../ethcore", features = ["test-helpers", "json-tests"] }
|
ethcore = { path = "../ethcore", features = ["test-helpers", "json-tests", "to-pod-full"] }
|
||||||
ethjson = { path = "../json" }
|
ethjson = { path = "../json" }
|
||||||
parity-bytes = "0.1"
|
parity-bytes = "0.1"
|
||||||
ethcore-transaction = { path = "../ethcore/transaction" }
|
ethcore-transaction = { path = "../ethcore/transaction" }
|
||||||
|
@ -9,7 +9,7 @@ EVM implementation for Parity.
|
|||||||
Copyright 2015-2018 Parity Technologies (UK) Ltd.
|
Copyright 2015-2018 Parity Technologies (UK) Ltd.
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
parity-evm state-test <file> [--json --std-json --only NAME --chain CHAIN]
|
parity-evm state-test <file> [--json --std-json --std-dump-json --only NAME --chain CHAIN --std-out-only --std-err-only]
|
||||||
parity-evm stats [options]
|
parity-evm stats [options]
|
||||||
parity-evm stats-jsontests-vm <file>
|
parity-evm stats-jsontests-vm <file>
|
||||||
parity-evm [options]
|
parity-evm [options]
|
||||||
@ -36,6 +36,11 @@ State test options:
|
|||||||
General options:
|
General options:
|
||||||
--json Display verbose results in JSON.
|
--json Display verbose results in JSON.
|
||||||
--std-json Display results in standardized JSON format.
|
--std-json Display results in standardized JSON format.
|
||||||
|
--std-err-only With --std-json redirect to err output only.
|
||||||
|
--std-out-only With --std-json redirect to out output only.
|
||||||
|
--std-dump-json Display results in standardized JSON format
|
||||||
|
with additional state dump.
|
||||||
|
Display result state dump in standardized JSON format.
|
||||||
--chain CHAIN Chain spec file path.
|
--chain CHAIN Chain spec file path.
|
||||||
-h, --help Display this message and exit.
|
-h, --help Display this message and exit.
|
||||||
```
|
```
|
||||||
|
@ -74,6 +74,8 @@ impl Informant {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl vm::Informant for Informant {
|
impl vm::Informant for Informant {
|
||||||
|
type Sink = ();
|
||||||
|
|
||||||
fn before_test(&mut self, name: &str, action: &str) {
|
fn before_test(&mut self, name: &str, action: &str) {
|
||||||
println!("{}", json!({"action": action, "test": name}));
|
println!("{}", json!({"action": action, "test": name}));
|
||||||
}
|
}
|
||||||
@ -82,7 +84,9 @@ impl vm::Informant for Informant {
|
|||||||
self.gas_used = gas;
|
self.gas_used = gas;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn finish(result: vm::RunResult<Self::Output>) {
|
fn clone_sink(&self) -> Self::Sink { () }
|
||||||
|
|
||||||
|
fn finish(result: vm::RunResult<Self::Output>, _sink: &mut Self::Sink) {
|
||||||
match result {
|
match result {
|
||||||
Ok(success) => {
|
Ok(success) => {
|
||||||
for trace in success.traces.unwrap_or_else(Vec::new) {
|
for trace in success.traces.unwrap_or_else(Vec::new) {
|
||||||
|
@ -27,11 +27,16 @@ use info as vm;
|
|||||||
pub struct Informant;
|
pub struct Informant;
|
||||||
|
|
||||||
impl vm::Informant for Informant {
|
impl vm::Informant for Informant {
|
||||||
|
|
||||||
|
type Sink = ();
|
||||||
|
|
||||||
fn before_test(&mut self, name: &str, action: &str) {
|
fn before_test(&mut self, name: &str, action: &str) {
|
||||||
println!("Test: {} ({})", name, action);
|
println!("Test: {} ({})", name, action);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn finish(result: vm::RunResult<Self::Output>) {
|
fn clone_sink(&self) -> Self::Sink { () }
|
||||||
|
|
||||||
|
fn finish(result: vm::RunResult<Self::Output>, _sink: &mut Self::Sink) {
|
||||||
match result {
|
match result {
|
||||||
Ok(success) => {
|
Ok(success) => {
|
||||||
println!("Output: 0x{}", success.output.to_hex());
|
println!("Output: 0x{}", success.output.to_hex());
|
||||||
|
@ -21,7 +21,7 @@ use std::io;
|
|||||||
|
|
||||||
use ethereum_types::{H256, U256};
|
use ethereum_types::{H256, U256};
|
||||||
use bytes::ToPretty;
|
use bytes::ToPretty;
|
||||||
use ethcore::trace;
|
use ethcore::{trace, pod_state};
|
||||||
|
|
||||||
use display;
|
use display;
|
||||||
use info as vm;
|
use info as vm;
|
||||||
@ -52,7 +52,7 @@ impl Writer for io::Stderr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// JSON formatting informant.
|
/// JSON formatting informant.
|
||||||
pub struct Informant<Trace = io::Stderr, Out = io::Stdout> {
|
pub struct Informant<Trace, Out> {
|
||||||
code: Vec<u8>,
|
code: Vec<u8>,
|
||||||
instruction: u8,
|
instruction: u8,
|
||||||
depth: usize,
|
depth: usize,
|
||||||
@ -64,13 +64,28 @@ pub struct Informant<Trace = io::Stderr, Out = io::Stdout> {
|
|||||||
out_sink: Out,
|
out_sink: Out,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Informant {
|
impl Default for Informant<io::Stderr, io::Stdout> {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self::new(io::stderr(), io::stdout())
|
Self::new(io::stderr(), io::stdout())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Informant<io::Stdout, io::Stdout> {
|
||||||
|
/// std json informant using out only.
|
||||||
|
pub fn out_only() -> Self {
|
||||||
|
Self::new(io::stdout(), io::stdout())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Informant<io::Stderr, io::Stderr> {
|
||||||
|
/// std json informant using err only.
|
||||||
|
pub fn err_only() -> Self {
|
||||||
|
Self::new(io::stderr(), io::stderr())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<Trace: Writer, Out: Writer> Informant<Trace, Out> {
|
impl<Trace: Writer, Out: Writer> Informant<Trace, Out> {
|
||||||
|
|
||||||
pub fn new(trace_sink: Trace, out_sink: Out) -> Self {
|
pub fn new(trace_sink: Trace, out_sink: Out) -> Self {
|
||||||
Informant {
|
Informant {
|
||||||
code: Default::default(),
|
code: Default::default(),
|
||||||
@ -91,9 +106,24 @@ impl<Trace: Writer, Out: Writer> Informant<Trace, Out> {
|
|||||||
Self::with_informant_in_depth(informant.subinfos.last_mut().expect("prepare/done_trace are not balanced"), depth - 1, f);
|
Self::with_informant_in_depth(informant.subinfos.last_mut().expect("prepare/done_trace are not balanced"), depth - 1, f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn dump_state_into(trace_sink: &mut Trace, root: H256, end_state: &Option<pod_state::PodState>) {
|
||||||
|
if let Some(ref end_state) = end_state {
|
||||||
|
let dump_data = json!({
|
||||||
|
"root": root,
|
||||||
|
"accounts": end_state,
|
||||||
|
});
|
||||||
|
writeln!(trace_sink, "{}", dump_data).expect("The sink must be writeable.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Trace: Writer, Out: Writer> vm::Informant for Informant<Trace, Out> {
|
impl<Trace: Writer, Out: Writer> vm::Informant for Informant<Trace, Out> {
|
||||||
|
|
||||||
|
type Sink = (Trace, Out);
|
||||||
|
|
||||||
fn before_test(&mut self, name: &str, action: &str) {
|
fn before_test(&mut self, name: &str, action: &str) {
|
||||||
let out_data = json!({
|
let out_data = json!({
|
||||||
"action": action,
|
"action": action,
|
||||||
@ -105,23 +135,26 @@ impl<Trace: Writer, Out: Writer> vm::Informant for Informant<Trace, Out> {
|
|||||||
|
|
||||||
fn set_gas(&mut self, _gas: U256) {}
|
fn set_gas(&mut self, _gas: U256) {}
|
||||||
|
|
||||||
fn finish(result: vm::RunResult<<Self as trace::VMTracer>::Output>) {
|
fn clone_sink(&self) -> Self::Sink {
|
||||||
let mut trace_sink = Trace::default();
|
(self.trace_sink.clone(), self.out_sink.clone())
|
||||||
let mut out_sink = Out::default();
|
}
|
||||||
|
fn finish(result: vm::RunResult<<Self as trace::VMTracer>::Output>, (ref mut trace_sink, ref mut out_sink): &mut Self::Sink) {
|
||||||
|
|
||||||
match result {
|
match result {
|
||||||
Ok(success) => {
|
Ok(success) => {
|
||||||
let trace_data = json!({"stateRoot": success.state_root});
|
let trace_data = json!({"stateRoot": success.state_root});
|
||||||
writeln!(&mut trace_sink, "{}", trace_data)
|
writeln!(trace_sink, "{}", trace_data)
|
||||||
.expect("The sink must be writeable.");
|
.expect("The sink must be writeable.");
|
||||||
|
|
||||||
|
Self::dump_state_into(trace_sink, success.state_root, &success.end_state);
|
||||||
|
|
||||||
let out_data = json!({
|
let out_data = json!({
|
||||||
"output": format!("0x{}", success.output.to_hex()),
|
"output": format!("0x{}", success.output.to_hex()),
|
||||||
"gasUsed": format!("{:#x}", success.gas_used),
|
"gasUsed": format!("{:#x}", success.gas_used),
|
||||||
"time": display::as_micros(&success.time),
|
"time": display::as_micros(&success.time),
|
||||||
});
|
});
|
||||||
|
|
||||||
writeln!(&mut out_sink, "{}", out_data).expect("The sink must be writeable.");
|
writeln!(out_sink, "{}", out_data).expect("The sink must be writeable.");
|
||||||
},
|
},
|
||||||
Err(failure) => {
|
Err(failure) => {
|
||||||
let out_data = json!({
|
let out_data = json!({
|
||||||
@ -130,7 +163,9 @@ impl<Trace: Writer, Out: Writer> vm::Informant for Informant<Trace, Out> {
|
|||||||
"time": display::as_micros(&failure.time),
|
"time": display::as_micros(&failure.time),
|
||||||
});
|
});
|
||||||
|
|
||||||
writeln!(&mut out_sink, "{}", out_data).expect("The sink must be writeable.");
|
Self::dump_state_into(trace_sink, failure.state_root, &failure.end_state);
|
||||||
|
|
||||||
|
writeln!(out_sink, "{}", out_data).expect("The sink must be writeable.");
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -200,6 +235,7 @@ impl<Trace: Writer, Out: Writer> trace::VMTracer for Informant<Trace, Out> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn drain(self) -> Option<Self::Output> { None }
|
fn drain(self) -> Option<Self::Output> { None }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -19,19 +19,23 @@
|
|||||||
use std::time::{Instant, Duration};
|
use std::time::{Instant, Duration};
|
||||||
use ethereum_types::{H256, U256};
|
use ethereum_types::{H256, U256};
|
||||||
use ethcore::client::{self, EvmTestClient, EvmTestError, TransactResult};
|
use ethcore::client::{self, EvmTestClient, EvmTestError, TransactResult};
|
||||||
use ethcore::{trace, spec, pod_state};
|
use ethcore::{state, state_db, trace, spec, pod_state, TrieSpec};
|
||||||
use ethjson;
|
use ethjson;
|
||||||
use transaction;
|
use transaction;
|
||||||
use vm::ActionParams;
|
use vm::ActionParams;
|
||||||
|
|
||||||
/// VM execution informant
|
/// VM execution informant
|
||||||
pub trait Informant: trace::VMTracer {
|
pub trait Informant: trace::VMTracer {
|
||||||
|
/// Sink to use with finish
|
||||||
|
type Sink;
|
||||||
/// Display a single run init message
|
/// Display a single run init message
|
||||||
fn before_test(&mut self, test: &str, action: &str);
|
fn before_test(&mut self, test: &str, action: &str);
|
||||||
/// Set initial gas.
|
/// Set initial gas.
|
||||||
fn set_gas(&mut self, _gas: U256) {}
|
fn set_gas(&mut self, _gas: U256) {}
|
||||||
|
/// Clone sink.
|
||||||
|
fn clone_sink(&self) -> Self::Sink;
|
||||||
/// Display final result.
|
/// Display final result.
|
||||||
fn finish(result: RunResult<Self::Output>);
|
fn finish(result: RunResult<Self::Output>, &mut Self::Sink);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Execution finished correctly
|
/// Execution finished correctly
|
||||||
@ -47,11 +51,15 @@ pub struct Success<T> {
|
|||||||
pub time: Duration,
|
pub time: Duration,
|
||||||
/// Traces
|
/// Traces
|
||||||
pub traces: Option<T>,
|
pub traces: Option<T>,
|
||||||
|
/// Optional end state dump
|
||||||
|
pub end_state: Option<pod_state::PodState>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Execution failed
|
/// Execution failed
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Failure<T> {
|
pub struct Failure<T> {
|
||||||
|
/// State root
|
||||||
|
pub state_root: H256,
|
||||||
/// Used gas
|
/// Used gas
|
||||||
pub gas_used: U256,
|
pub gas_used: U256,
|
||||||
/// Internal error
|
/// Internal error
|
||||||
@ -60,6 +68,8 @@ pub struct Failure<T> {
|
|||||||
pub time: Duration,
|
pub time: Duration,
|
||||||
/// Traces
|
/// Traces
|
||||||
pub traces: Option<T>,
|
pub traces: Option<T>,
|
||||||
|
/// Optional end state dump
|
||||||
|
pub end_state: Option<pod_state::PodState>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// EVM Execution result
|
/// EVM Execution result
|
||||||
@ -70,6 +80,7 @@ pub fn run_action<T: Informant>(
|
|||||||
spec: &spec::Spec,
|
spec: &spec::Spec,
|
||||||
mut params: ActionParams,
|
mut params: ActionParams,
|
||||||
mut informant: T,
|
mut informant: T,
|
||||||
|
trie_spec: TrieSpec,
|
||||||
) -> RunResult<T::Output> {
|
) -> RunResult<T::Output> {
|
||||||
informant.set_gas(params.gas);
|
informant.set_gas(params.gas);
|
||||||
|
|
||||||
@ -80,12 +91,12 @@ pub fn run_action<T: Informant>(
|
|||||||
params.code_hash = None;
|
params.code_hash = None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
run(spec, params.gas, spec.genesis_state(), |mut client| {
|
run(spec, trie_spec, params.gas, spec.genesis_state(), |mut client| {
|
||||||
let result = match client.call(params, &mut trace::NoopTracer, &mut informant) {
|
let result = match client.call(params, &mut trace::NoopTracer, &mut informant) {
|
||||||
Ok(r) => (Ok((0.into(), r.return_data.to_vec())), Some(r.gas_left)),
|
Ok(r) => (Ok(r.return_data.to_vec()), Some(r.gas_left)),
|
||||||
Err(err) => (Err(err), None),
|
Err(err) => (Err(err), None),
|
||||||
};
|
};
|
||||||
(result.0, result.1, informant.drain())
|
(result.0, 0.into(), None, result.1, informant.drain())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -99,6 +110,7 @@ pub fn run_transaction<T: Informant>(
|
|||||||
env_info: &client::EnvInfo,
|
env_info: &client::EnvInfo,
|
||||||
transaction: transaction::SignedTransaction,
|
transaction: transaction::SignedTransaction,
|
||||||
mut informant: T,
|
mut informant: T,
|
||||||
|
trie_spec: TrieSpec,
|
||||||
) {
|
) {
|
||||||
let spec_name = format!("{:?}", spec).to_lowercase();
|
let spec_name = format!("{:?}", spec).to_lowercase();
|
||||||
let spec = match EvmTestClient::spec_from_json(spec) {
|
let spec = match EvmTestClient::spec_from_json(spec) {
|
||||||
@ -114,64 +126,82 @@ pub fn run_transaction<T: Informant>(
|
|||||||
|
|
||||||
informant.set_gas(env_info.gas_limit);
|
informant.set_gas(env_info.gas_limit);
|
||||||
|
|
||||||
let result = run(&spec, transaction.gas, pre_state, |mut client| {
|
let mut sink = informant.clone_sink();
|
||||||
|
let result = run(&spec, trie_spec, transaction.gas, pre_state, |mut client| {
|
||||||
let result = client.transact(env_info, transaction, trace::NoopTracer, informant);
|
let result = client.transact(env_info, transaction, trace::NoopTracer, informant);
|
||||||
match result {
|
match result {
|
||||||
TransactResult::Ok { state_root, gas_left, .. } if state_root != post_root => {
|
TransactResult::Ok { state_root, gas_left, output, vm_trace, end_state, .. } => {
|
||||||
(Err(EvmTestError::PostCondition(format!(
|
if state_root != post_root {
|
||||||
"State root mismatch (got: 0x{:x}, expected: 0x{:x})",
|
(Err(EvmTestError::PostCondition(format!(
|
||||||
state_root,
|
"State root mismatch (got: {:#x}, expected: {:#x})",
|
||||||
post_root,
|
state_root,
|
||||||
))), Some(gas_left), None)
|
post_root,
|
||||||
|
))), state_root, end_state, Some(gas_left), None)
|
||||||
|
} else {
|
||||||
|
(Ok(output), state_root, end_state, Some(gas_left), vm_trace)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
TransactResult::Ok { state_root, gas_left, output, vm_trace, .. } => {
|
TransactResult::Err { state_root, error, end_state } => {
|
||||||
(Ok((state_root, output)), Some(gas_left), vm_trace)
|
|
||||||
},
|
|
||||||
TransactResult::Err { error, .. } => {
|
|
||||||
(Err(EvmTestError::PostCondition(format!(
|
(Err(EvmTestError::PostCondition(format!(
|
||||||
"Unexpected execution error: {:?}", error
|
"Unexpected execution error: {:?}", error
|
||||||
))), None, None)
|
))), state_root, end_state, None, None)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
T::finish(result)
|
T::finish(result, &mut sink)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dump_state(state: &state::State<state_db::StateDB>) -> Option<pod_state::PodState> {
|
||||||
|
state.to_pod_full().ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Execute VM with given `ActionParams`
|
/// Execute VM with given `ActionParams`
|
||||||
pub fn run<'a, F, X>(
|
pub fn run<'a, F, X>(
|
||||||
spec: &'a spec::Spec,
|
spec: &'a spec::Spec,
|
||||||
|
trie_spec: TrieSpec,
|
||||||
initial_gas: U256,
|
initial_gas: U256,
|
||||||
pre_state: &'a pod_state::PodState,
|
pre_state: &'a pod_state::PodState,
|
||||||
run: F,
|
run: F,
|
||||||
) -> RunResult<X> where
|
) -> RunResult<X> where
|
||||||
F: FnOnce(EvmTestClient) -> (Result<(H256, Vec<u8>), EvmTestError>, Option<U256>, Option<X>),
|
F: FnOnce(EvmTestClient) -> (Result<Vec<u8>, EvmTestError>, H256, Option<pod_state::PodState>, Option<U256>, Option<X>),
|
||||||
{
|
{
|
||||||
let test_client = EvmTestClient::from_pod_state(spec, pre_state.clone())
|
let do_dump = trie_spec == TrieSpec::Fat;
|
||||||
|
|
||||||
|
let mut test_client = EvmTestClient::from_pod_state_with_trie(spec, pre_state.clone(), trie_spec)
|
||||||
.map_err(|error| Failure {
|
.map_err(|error| Failure {
|
||||||
gas_used: 0.into(),
|
gas_used: 0.into(),
|
||||||
error,
|
error,
|
||||||
time: Duration::from_secs(0),
|
time: Duration::from_secs(0),
|
||||||
traces: None,
|
traces: None,
|
||||||
|
state_root: H256::default(),
|
||||||
|
end_state: None,
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
if do_dump {
|
||||||
|
test_client.set_dump_state_fn(dump_state);
|
||||||
|
}
|
||||||
|
|
||||||
let start = Instant::now();
|
let start = Instant::now();
|
||||||
let result = run(test_client);
|
let result = run(test_client);
|
||||||
let time = start.elapsed();
|
let time = start.elapsed();
|
||||||
|
|
||||||
match result {
|
match result {
|
||||||
(Ok((state_root, output)), gas_left, traces) => Ok(Success {
|
(Ok(output), state_root, end_state, gas_left, traces) => Ok(Success {
|
||||||
state_root,
|
state_root,
|
||||||
gas_used: gas_left.map(|gas_left| initial_gas - gas_left).unwrap_or(initial_gas),
|
gas_used: gas_left.map(|gas_left| initial_gas - gas_left).unwrap_or(initial_gas),
|
||||||
output,
|
output,
|
||||||
time,
|
time,
|
||||||
traces,
|
traces,
|
||||||
|
end_state,
|
||||||
}),
|
}),
|
||||||
(Err(error), gas_left, traces) => Err(Failure {
|
(Err(error), state_root, end_state, gas_left, traces) => Err(Failure {
|
||||||
gas_used: gas_left.map(|gas_left| initial_gas - gas_left).unwrap_or(initial_gas),
|
gas_used: gas_left.map(|gas_left| initial_gas - gas_left).unwrap_or(initial_gas),
|
||||||
error,
|
error,
|
||||||
time,
|
time,
|
||||||
traces,
|
traces,
|
||||||
|
state_root,
|
||||||
|
end_state,
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -200,7 +230,7 @@ pub mod tests {
|
|||||||
|
|
||||||
let tempdir = TempDir::new("").unwrap();
|
let tempdir = TempDir::new("").unwrap();
|
||||||
let spec = ::ethcore::ethereum::new_foundation(&tempdir.path());
|
let spec = ::ethcore::ethereum::new_foundation(&tempdir.path());
|
||||||
let result = run_action(&spec, params, informant);
|
let result = run_action(&spec, params, informant, TrieSpec::Secure);
|
||||||
match result {
|
match result {
|
||||||
Ok(Success { traces, .. }) => {
|
Ok(Success { traces, .. }) => {
|
||||||
compare(traces, expected)
|
compare(traces, expected)
|
||||||
@ -221,7 +251,7 @@ pub mod tests {
|
|||||||
params.gas = 0xffff.into();
|
params.gas = 0xffff.into();
|
||||||
|
|
||||||
let spec = ::ethcore::ethereum::load(None, include_bytes!("../res/testchain.json"));
|
let spec = ::ethcore::ethereum::load(None, include_bytes!("../res/testchain.json"));
|
||||||
let _result = run_action(&spec, params, inf);
|
let _result = run_action(&spec, params, inf, TrieSpec::Secure);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
&String::from_utf8_lossy(&**res.lock().unwrap()),
|
&String::from_utf8_lossy(&**res.lock().unwrap()),
|
||||||
|
@ -49,7 +49,7 @@ use docopt::Docopt;
|
|||||||
use rustc_hex::FromHex;
|
use rustc_hex::FromHex;
|
||||||
use ethereum_types::{U256, Address};
|
use ethereum_types::{U256, Address};
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use ethcore::{spec, json_tests};
|
use ethcore::{spec, json_tests, TrieSpec};
|
||||||
use vm::{ActionParams, CallType};
|
use vm::{ActionParams, CallType};
|
||||||
|
|
||||||
mod info;
|
mod info;
|
||||||
@ -62,7 +62,7 @@ EVM implementation for Parity.
|
|||||||
Copyright 2015-2018 Parity Technologies (UK) Ltd.
|
Copyright 2015-2018 Parity Technologies (UK) Ltd.
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
parity-evm state-test <file> [--json --std-json --only NAME --chain CHAIN]
|
parity-evm state-test <file> [--json --std-json --std-dump-json --only NAME --chain CHAIN --std-out-only --std-err-only]
|
||||||
parity-evm stats [options]
|
parity-evm stats [options]
|
||||||
parity-evm stats-jsontests-vm <file>
|
parity-evm stats-jsontests-vm <file>
|
||||||
parity-evm [options]
|
parity-evm [options]
|
||||||
@ -89,6 +89,11 @@ State test options:
|
|||||||
General options:
|
General options:
|
||||||
--json Display verbose results in JSON.
|
--json Display verbose results in JSON.
|
||||||
--std-json Display results in standardized JSON format.
|
--std-json Display results in standardized JSON format.
|
||||||
|
--std-err-only With --std-json redirect to err output only.
|
||||||
|
--std-out-only With --std-json redirect to out output only.
|
||||||
|
--std-dump-json Display results in standardized JSON format
|
||||||
|
with additional state dump.
|
||||||
|
Display result state dump in standardized JSON format.
|
||||||
--chain CHAIN Chain spec file path.
|
--chain CHAIN Chain spec file path.
|
||||||
-h, --help Display this message and exit.
|
-h, --help Display this message and exit.
|
||||||
"#;
|
"#;
|
||||||
@ -105,8 +110,14 @@ fn main() {
|
|||||||
run_stats_jsontests_vm(args)
|
run_stats_jsontests_vm(args)
|
||||||
} else if args.flag_json {
|
} else if args.flag_json {
|
||||||
run_call(args, display::json::Informant::default())
|
run_call(args, display::json::Informant::default())
|
||||||
} else if args.flag_std_json {
|
} else if args.flag_std_dump_json || args.flag_std_json {
|
||||||
run_call(args, display::std_json::Informant::default())
|
if args.flag_std_err_only {
|
||||||
|
run_call(args, display::std_json::Informant::err_only())
|
||||||
|
} else if args.flag_std_out_only {
|
||||||
|
run_call(args, display::std_json::Informant::out_only())
|
||||||
|
} else {
|
||||||
|
run_call(args, display::std_json::Informant::default())
|
||||||
|
};
|
||||||
} else {
|
} else {
|
||||||
run_call(args, display::simple::Informant::default())
|
run_call(args, display::simple::Informant::default())
|
||||||
}
|
}
|
||||||
@ -179,15 +190,23 @@ fn run_state_test(args: Args) {
|
|||||||
let post_root = state.hash.into();
|
let post_root = state.hash.into();
|
||||||
let transaction = multitransaction.select(&state.indexes).into();
|
let transaction = multitransaction.select(&state.indexes).into();
|
||||||
|
|
||||||
if args.flag_json {
|
let trie_spec = if args.flag_std_dump_json {
|
||||||
let i = display::json::Informant::default();
|
TrieSpec::Fat
|
||||||
info::run_transaction(&name, idx, &spec, &pre, post_root, &env_info, transaction, i)
|
|
||||||
} else if args.flag_std_json {
|
|
||||||
let i = display::std_json::Informant::default();
|
|
||||||
info::run_transaction(&name, idx, &spec, &pre, post_root, &env_info, transaction, i)
|
|
||||||
} else {
|
} else {
|
||||||
let i = display::simple::Informant::default();
|
TrieSpec::Secure
|
||||||
info::run_transaction(&name, idx, &spec, &pre, post_root, &env_info, transaction, i)
|
};
|
||||||
|
if args.flag_json {
|
||||||
|
info::run_transaction(&name, idx, &spec, &pre, post_root, &env_info, transaction, display::json::Informant::default(), trie_spec)
|
||||||
|
} else if args.flag_std_dump_json || args.flag_std_json {
|
||||||
|
if args.flag_std_err_only {
|
||||||
|
info::run_transaction(&name, idx, &spec, &pre, post_root, &env_info, transaction, display::std_json::Informant::err_only(), trie_spec)
|
||||||
|
} else if args.flag_std_out_only {
|
||||||
|
info::run_transaction(&name, idx, &spec, &pre, post_root, &env_info, transaction, display::std_json::Informant::out_only(), trie_spec)
|
||||||
|
} else {
|
||||||
|
info::run_transaction(&name, idx, &spec, &pre, post_root, &env_info, transaction, display::std_json::Informant::default(), trie_spec)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
info::run_transaction(&name, idx, &spec, &pre, post_root, &env_info, transaction, display::simple::Informant::default(), trie_spec)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -218,8 +237,13 @@ fn run_call<T: Informant>(args: Args, informant: T) {
|
|||||||
params.code = code.map(Arc::new);
|
params.code = code.map(Arc::new);
|
||||||
params.data = data;
|
params.data = data;
|
||||||
|
|
||||||
let result = info::run_action(&spec, params, informant);
|
let mut sink = informant.clone_sink();
|
||||||
T::finish(result);
|
let result = if args.flag_std_dump_json {
|
||||||
|
info::run_action(&spec, params, informant, TrieSpec::Fat)
|
||||||
|
} else {
|
||||||
|
info::run_action(&spec, params, informant, TrieSpec::Secure)
|
||||||
|
};
|
||||||
|
T::finish(result, &mut sink);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
@ -238,6 +262,9 @@ struct Args {
|
|||||||
flag_chain: Option<String>,
|
flag_chain: Option<String>,
|
||||||
flag_json: bool,
|
flag_json: bool,
|
||||||
flag_std_json: bool,
|
flag_std_json: bool,
|
||||||
|
flag_std_dump_json: bool,
|
||||||
|
flag_std_err_only: bool,
|
||||||
|
flag_std_out_only: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Args {
|
impl Args {
|
||||||
@ -285,7 +312,7 @@ impl Args {
|
|||||||
|
|
||||||
pub fn spec(&self) -> Result<spec::Spec, String> {
|
pub fn spec(&self) -> Result<spec::Spec, String> {
|
||||||
Ok(match self.flag_chain {
|
Ok(match self.flag_chain {
|
||||||
Some(ref filename) => {
|
Some(ref filename) => {
|
||||||
let file = fs::File::open(filename).map_err(|e| format!("{}", e))?;
|
let file = fs::File::open(filename).map_err(|e| format!("{}", e))?;
|
||||||
spec::Spec::load(&::std::env::temp_dir(), file)?
|
spec::Spec::load(&::std::env::temp_dir(), file)?
|
||||||
},
|
},
|
||||||
@ -324,17 +351,21 @@ mod tests {
|
|||||||
"parity-evm",
|
"parity-evm",
|
||||||
"--json",
|
"--json",
|
||||||
"--std-json",
|
"--std-json",
|
||||||
|
"--std-dump-json",
|
||||||
"--gas", "1",
|
"--gas", "1",
|
||||||
"--gas-price", "2",
|
"--gas-price", "2",
|
||||||
"--from", "0000000000000000000000000000000000000003",
|
"--from", "0000000000000000000000000000000000000003",
|
||||||
"--to", "0000000000000000000000000000000000000004",
|
"--to", "0000000000000000000000000000000000000004",
|
||||||
"--code", "05",
|
"--code", "05",
|
||||||
"--input", "06",
|
"--input", "06",
|
||||||
"--chain", "./testfile",
|
"--chain", "./testfile", "--std-err-only", "--std-out-only"
|
||||||
]);
|
]);
|
||||||
|
|
||||||
assert_eq!(args.flag_json, true);
|
assert_eq!(args.flag_json, true);
|
||||||
assert_eq!(args.flag_std_json, true);
|
assert_eq!(args.flag_std_json, true);
|
||||||
|
assert_eq!(args.flag_std_dump_json, true);
|
||||||
|
assert_eq!(args.flag_std_err_only, true);
|
||||||
|
assert_eq!(args.flag_std_out_only, true);
|
||||||
assert_eq!(args.gas(), Ok(1.into()));
|
assert_eq!(args.gas(), Ok(1.into()));
|
||||||
assert_eq!(args.gas_price(), Ok(2.into()));
|
assert_eq!(args.gas_price(), Ok(2.into()));
|
||||||
assert_eq!(args.from(), Ok(3.into()));
|
assert_eq!(args.from(), Ok(3.into()));
|
||||||
@ -353,13 +384,15 @@ mod tests {
|
|||||||
"--chain", "homestead",
|
"--chain", "homestead",
|
||||||
"--only=add11",
|
"--only=add11",
|
||||||
"--json",
|
"--json",
|
||||||
"--std-json"
|
"--std-json",
|
||||||
|
"--std-dump-json"
|
||||||
]);
|
]);
|
||||||
|
|
||||||
assert_eq!(args.cmd_state_test, true);
|
assert_eq!(args.cmd_state_test, true);
|
||||||
assert!(args.arg_file.is_some());
|
assert!(args.arg_file.is_some());
|
||||||
assert_eq!(args.flag_json, true);
|
assert_eq!(args.flag_json, true);
|
||||||
assert_eq!(args.flag_std_json, true);
|
assert_eq!(args.flag_std_json, true);
|
||||||
|
assert_eq!(args.flag_std_dump_json, true);
|
||||||
assert_eq!(args.flag_chain, Some("homestead".to_owned()));
|
assert_eq!(args.flag_chain, Some("homestead".to_owned()));
|
||||||
assert_eq!(args.flag_only, Some("add11".to_owned()));
|
assert_eq!(args.flag_only, Some("add11".to_owned()));
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user