Running state test using parity-evm (#6355)
* Initial version of state tests. * Refactor state to support tracing. * Unify TransactResult. * Add test.
This commit is contained in:
parent
abecd80f54
commit
f9a08e285c
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -935,6 +935,7 @@ dependencies = [
|
|||||||
"docopt 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"docopt 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"ethcore 1.8.0",
|
"ethcore 1.8.0",
|
||||||
"ethcore-util 1.8.0",
|
"ethcore-util 1.8.0",
|
||||||
|
"ethjson 0.1.0",
|
||||||
"evm 0.1.0",
|
"evm 0.1.0",
|
||||||
"panic_hook 0.1.0",
|
"panic_hook 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)",
|
||||||
|
@ -748,7 +748,7 @@ impl Client {
|
|||||||
self.factories.clone(),
|
self.factories.clone(),
|
||||||
).expect("state known to be available for just-imported block; qed");
|
).expect("state known to be available for just-imported block; qed");
|
||||||
|
|
||||||
let options = TransactOptions { tracing: false, vm_tracing: false, check_nonce: false };
|
let options = TransactOptions::with_no_tracing().dont_check_nonce();
|
||||||
let res = Executive::new(&mut state, &env_info, &*self.engine)
|
let res = Executive::new(&mut state, &env_info, &*self.engine)
|
||||||
.transact(&transaction, options);
|
.transact(&transaction, options);
|
||||||
|
|
||||||
@ -1113,18 +1113,39 @@ impl Client {
|
|||||||
}.fake_sign(from)
|
}.fake_sign(from)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn do_call(&self, env_info: &EnvInfo, state: &mut State<StateDB>, t: &SignedTransaction, analytics: CallAnalytics) -> Result<Executed, CallError> {
|
fn do_virtual_call(&self, env_info: &EnvInfo, state: &mut State<StateDB>, t: &SignedTransaction, analytics: CallAnalytics) -> Result<Executed, CallError> {
|
||||||
let original_state = if analytics.state_diffing { Some(state.clone()) } else { None };
|
fn call<E, V, T>(
|
||||||
|
state: &mut State<StateDB>,
|
||||||
|
env_info: &EnvInfo,
|
||||||
|
engine: &E,
|
||||||
|
state_diff: bool,
|
||||||
|
transaction: &SignedTransaction,
|
||||||
|
options: TransactOptions<T, V>,
|
||||||
|
) -> Result<Executed, CallError> where
|
||||||
|
E: Engine + ?Sized,
|
||||||
|
T: trace::Tracer,
|
||||||
|
V: trace::VMTracer,
|
||||||
|
{
|
||||||
|
let options = options.dont_check_nonce();
|
||||||
|
let original_state = if state_diff { Some(state.clone()) } else { None };
|
||||||
|
|
||||||
let options = TransactOptions { tracing: analytics.transaction_tracing, vm_tracing: analytics.vm_tracing, check_nonce: false };
|
let mut ret = Executive::new(state, env_info, engine).transact_virtual(transaction, options)?;
|
||||||
let mut ret = Executive::new(state, env_info, &*self.engine).transact_virtual(t, options)?;
|
|
||||||
|
|
||||||
// TODO gav move this into Executive.
|
if let Some(original) = original_state {
|
||||||
if let Some(original) = original_state {
|
ret.state_diff = Some(state.diff_from(original).map_err(ExecutionError::from)?);
|
||||||
ret.state_diff = Some(state.diff_from(original).map_err(ExecutionError::from)?);
|
}
|
||||||
|
Ok(ret)
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(ret)
|
let state_diff = analytics.state_diffing;
|
||||||
|
let engine = &*self.engine;
|
||||||
|
|
||||||
|
match (analytics.transaction_tracing, analytics.vm_tracing) {
|
||||||
|
(true, true) => call(state, env_info, engine, state_diff, t, TransactOptions::with_tracing_and_vm_tracing()),
|
||||||
|
(true, false) => call(state, env_info, engine, state_diff, t, TransactOptions::with_tracing()),
|
||||||
|
(false, true) => call(state, env_info, engine, state_diff, t, TransactOptions::with_vm_tracing()),
|
||||||
|
(false, false) => call(state, env_info, engine, state_diff, t, TransactOptions::with_no_tracing()),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1157,7 +1178,7 @@ impl BlockChainClient for Client {
|
|||||||
// that's just a copy of the state.
|
// that's just a copy of the state.
|
||||||
let mut state = self.state_at(block).ok_or(CallError::StatePruned)?;
|
let mut state = self.state_at(block).ok_or(CallError::StatePruned)?;
|
||||||
|
|
||||||
self.do_call(&env_info, &mut state, transaction, analytics)
|
self.do_virtual_call(&env_info, &mut state, transaction, analytics)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn call_many(&self, transactions: &[(SignedTransaction, CallAnalytics)], block: BlockId) -> Result<Vec<Executed>, CallError> {
|
fn call_many(&self, transactions: &[(SignedTransaction, CallAnalytics)], block: BlockId) -> Result<Vec<Executed>, CallError> {
|
||||||
@ -1169,7 +1190,7 @@ impl BlockChainClient for Client {
|
|||||||
let mut results = Vec::with_capacity(transactions.len());
|
let mut results = Vec::with_capacity(transactions.len());
|
||||||
|
|
||||||
for &(ref t, analytics) in transactions {
|
for &(ref t, analytics) in transactions {
|
||||||
let ret = self.do_call(&env_info, &mut state, t, analytics)?;
|
let ret = self.do_virtual_call(&env_info, &mut state, t, analytics)?;
|
||||||
env_info.gas_used = ret.cumulative_gas_used;
|
env_info.gas_used = ret.cumulative_gas_used;
|
||||||
results.push(ret);
|
results.push(ret);
|
||||||
}
|
}
|
||||||
@ -1189,7 +1210,7 @@ impl BlockChainClient for Client {
|
|||||||
// that's just a copy of the state.
|
// that's just a copy of the state.
|
||||||
let original_state = self.state_at(block).ok_or(CallError::StatePruned)?;
|
let original_state = self.state_at(block).ok_or(CallError::StatePruned)?;
|
||||||
let sender = t.sender();
|
let sender = t.sender();
|
||||||
let options = TransactOptions { tracing: true, vm_tracing: false, check_nonce: false };
|
let options = || TransactOptions::with_tracing();
|
||||||
|
|
||||||
let cond = |gas| {
|
let cond = |gas| {
|
||||||
let mut tx = t.as_unsigned().clone();
|
let mut tx = t.as_unsigned().clone();
|
||||||
@ -1198,7 +1219,7 @@ impl BlockChainClient for Client {
|
|||||||
|
|
||||||
let mut state = original_state.clone();
|
let mut state = original_state.clone();
|
||||||
Ok(Executive::new(&mut state, &env_info, &*self.engine)
|
Ok(Executive::new(&mut state, &env_info, &*self.engine)
|
||||||
.transact_virtual(&tx, options.clone())
|
.transact_virtual(&tx, options())
|
||||||
.map(|r| r.exception.is_none())
|
.map(|r| r.exception.is_none())
|
||||||
.unwrap_or(false))
|
.unwrap_or(false))
|
||||||
};
|
};
|
||||||
@ -1254,22 +1275,17 @@ impl BlockChainClient for Client {
|
|||||||
return Err(CallError::TransactionNotFound);
|
return Err(CallError::TransactionNotFound);
|
||||||
}
|
}
|
||||||
|
|
||||||
let options = TransactOptions { tracing: analytics.transaction_tracing, vm_tracing: analytics.vm_tracing, check_nonce: false };
|
|
||||||
const PROOF: &'static str = "Transactions fetched from blockchain; blockchain transactions are valid; qed";
|
const PROOF: &'static str = "Transactions fetched from blockchain; blockchain transactions are valid; qed";
|
||||||
let rest = txs.split_off(address.index);
|
let rest = txs.split_off(address.index);
|
||||||
for t in txs {
|
for t in txs {
|
||||||
let t = SignedTransaction::new(t).expect(PROOF);
|
let t = SignedTransaction::new(t).expect(PROOF);
|
||||||
let x = Executive::new(&mut state, &env_info, &*self.engine).transact(&t, Default::default())?;
|
let x = Executive::new(&mut state, &env_info, &*self.engine).transact(&t, TransactOptions::with_no_tracing())?;
|
||||||
env_info.gas_used = env_info.gas_used + x.gas_used;
|
env_info.gas_used = env_info.gas_used + x.gas_used;
|
||||||
}
|
}
|
||||||
let first = rest.into_iter().next().expect("We split off < `address.index`; Length is checked earlier; qed");
|
let first = rest.into_iter().next().expect("We split off < `address.index`; Length is checked earlier; qed");
|
||||||
let t = SignedTransaction::new(first).expect(PROOF);
|
let t = SignedTransaction::new(first).expect(PROOF);
|
||||||
let original_state = if analytics.state_diffing { Some(state.clone()) } else { None };
|
|
||||||
let mut ret = Executive::new(&mut state, &env_info, &*self.engine).transact(&t, options)?;
|
self.do_virtual_call(&env_info, &mut state, &t, analytics)
|
||||||
if let Some(original) = original_state {
|
|
||||||
ret.state_diff = Some(state.diff_from(original).map_err(ExecutionError::from)?)
|
|
||||||
}
|
|
||||||
Ok(ret)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mode(&self) -> IpcMode {
|
fn mode(&self) -> IpcMode {
|
||||||
@ -1951,7 +1967,7 @@ impl ProvingBlockChainClient for Client {
|
|||||||
let backend = state::backend::Proving::new(jdb.as_hashdb_mut());
|
let backend = state::backend::Proving::new(jdb.as_hashdb_mut());
|
||||||
|
|
||||||
let mut state = state.replace_backend(backend);
|
let mut state = state.replace_backend(backend);
|
||||||
let options = TransactOptions { tracing: false, vm_tracing: false, check_nonce: false };
|
let options = TransactOptions::with_no_tracing().dont_check_nonce();
|
||||||
let res = Executive::new(&mut state, &env_info, &*self.engine).transact(&transaction, options);
|
let res = Executive::new(&mut state, &env_info, &*self.engine).transact(&transaction, options);
|
||||||
|
|
||||||
match res {
|
match res {
|
||||||
|
@ -18,9 +18,9 @@
|
|||||||
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use util::{self, U256, journaldb, trie};
|
use util::{self, U256, H256, journaldb, trie};
|
||||||
use util::kvdb::{self, KeyValueDB};
|
use util::kvdb::{self, KeyValueDB};
|
||||||
use {state, state_db, client, executive, trace, db, spec};
|
use {state, state_db, client, executive, trace, transaction, db, spec, pod_state};
|
||||||
use factory::Factories;
|
use factory::Factories;
|
||||||
use evm::{self, VMType};
|
use evm::{self, VMType};
|
||||||
use vm::{self, ActionParams};
|
use vm::{self, ActionParams};
|
||||||
@ -33,9 +33,17 @@ pub enum EvmTestError {
|
|||||||
/// EVM error.
|
/// EVM error.
|
||||||
Evm(vm::Error),
|
Evm(vm::Error),
|
||||||
/// Initialization error.
|
/// Initialization error.
|
||||||
Initialization(::error::Error),
|
ClientError(::error::Error),
|
||||||
/// Low-level database error.
|
/// Low-level database error.
|
||||||
Database(String),
|
Database(String),
|
||||||
|
/// Post-condition failure,
|
||||||
|
PostCondition(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E: Into<::error::Error>> From<E> for EvmTestError {
|
||||||
|
fn from(err: E) -> Self {
|
||||||
|
EvmTestError::ClientError(err.into())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for EvmTestError {
|
impl fmt::Display for EvmTestError {
|
||||||
@ -45,52 +53,114 @@ impl fmt::Display for EvmTestError {
|
|||||||
match *self {
|
match *self {
|
||||||
Trie(ref err) => write!(fmt, "Trie: {}", err),
|
Trie(ref err) => write!(fmt, "Trie: {}", err),
|
||||||
Evm(ref err) => write!(fmt, "EVM: {}", err),
|
Evm(ref err) => write!(fmt, "EVM: {}", err),
|
||||||
Initialization(ref err) => write!(fmt, "Initialization: {}", err),
|
ClientError(ref err) => write!(fmt, "{}", err),
|
||||||
Database(ref err) => write!(fmt, "DB: {}", err),
|
Database(ref err) => write!(fmt, "DB: {}", err),
|
||||||
|
PostCondition(ref err) => write!(fmt, "{}", err),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Simplified, single-block EVM test client.
|
use ethereum;
|
||||||
pub struct EvmTestClient {
|
use ethjson::state::test::ForkSpec;
|
||||||
state_db: state_db::StateDB,
|
|
||||||
factories: Factories,
|
lazy_static! {
|
||||||
spec: spec::Spec,
|
pub static ref FRONTIER: spec::Spec = ethereum::new_frontier_test();
|
||||||
|
pub static ref HOMESTEAD: spec::Spec = ethereum::new_homestead_test();
|
||||||
|
pub static ref EIP150: spec::Spec = ethereum::new_eip150_test();
|
||||||
|
pub static ref EIP161: spec::Spec = ethereum::new_eip161_test();
|
||||||
|
pub static ref _METROPOLIS: spec::Spec = ethereum::new_metropolis_test();
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EvmTestClient {
|
/// Simplified, single-block EVM test client.
|
||||||
/// Creates new EVM test client with in-memory DB initialized with genesis of given Spec.
|
pub struct EvmTestClient<'a> {
|
||||||
pub fn new(spec: spec::Spec) -> Result<Self, EvmTestError> {
|
state: state::State<state_db::StateDB>,
|
||||||
let factories = Factories {
|
spec: &'a spec::Spec,
|
||||||
vm: evm::Factory::new(VMType::Interpreter, 5 * 1024),
|
}
|
||||||
trie: trie::TrieFactory::new(trie::TrieSpec::Secure),
|
|
||||||
accountdb: Default::default(),
|
impl<'a> EvmTestClient<'a> {
|
||||||
};
|
/// Converts a json spec definition into spec.
|
||||||
let db = Arc::new(kvdb::in_memory(db::NUM_COLUMNS.expect("We use column-based DB; qed")));
|
pub fn spec_from_json(spec: &ForkSpec) -> Option<&'static spec::Spec> {
|
||||||
let journal_db = journaldb::new(db.clone(), journaldb::Algorithm::EarlyMerge, db::COL_STATE);
|
match *spec {
|
||||||
let mut state_db = state_db::StateDB::new(journal_db, 5 * 1024 * 1024);
|
ForkSpec::Frontier => Some(&*FRONTIER),
|
||||||
state_db = spec.ensure_db_good(state_db, &factories).map_err(EvmTestError::Initialization)?;
|
ForkSpec::Homestead => Some(&*HOMESTEAD),
|
||||||
// Write DB
|
ForkSpec::EIP150 => Some(&*EIP150),
|
||||||
{
|
ForkSpec::EIP158 => Some(&*EIP161),
|
||||||
let mut batch = kvdb::DBTransaction::new();
|
ForkSpec::Metropolis | ForkSpec::Byzantium | ForkSpec::Constantinople => None,
|
||||||
state_db.journal_under(&mut batch, 0, &spec.genesis_header().hash()).map_err(|e| EvmTestError::Initialization(e.into()))?;
|
|
||||||
db.write(batch).map_err(EvmTestError::Database)?;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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();
|
||||||
|
let state = Self::state_from_spec(spec, &factories)?;
|
||||||
|
|
||||||
Ok(EvmTestClient {
|
Ok(EvmTestClient {
|
||||||
state_db,
|
state,
|
||||||
factories,
|
|
||||||
spec,
|
spec,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Call given contract.
|
/// 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();
|
||||||
|
let state = Self::state_from_pod(spec, &factories, pod_state)?;
|
||||||
|
|
||||||
|
Ok(EvmTestClient {
|
||||||
|
state,
|
||||||
|
spec,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn factories() -> Factories {
|
||||||
|
Factories {
|
||||||
|
vm: evm::Factory::new(VMType::Interpreter, 5 * 1024),
|
||||||
|
trie: trie::TrieFactory::new(trie::TrieSpec::Secure),
|
||||||
|
accountdb: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn state_from_spec(spec: &'a spec::Spec, factories: &Factories) -> Result<state::State<state_db::StateDB>, EvmTestError> {
|
||||||
|
let db = Arc::new(kvdb::in_memory(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);
|
||||||
|
state_db = spec.ensure_db_good(state_db, factories)?;
|
||||||
|
|
||||||
|
let genesis = spec.genesis_header();
|
||||||
|
// Write DB
|
||||||
|
{
|
||||||
|
let mut batch = kvdb::DBTransaction::new();
|
||||||
|
state_db.journal_under(&mut batch, 0, &genesis.hash())?;
|
||||||
|
db.write(batch).map_err(EvmTestError::Database)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
state::State::from_existing(
|
||||||
|
state_db,
|
||||||
|
*genesis.state_root(),
|
||||||
|
spec.engine.account_start_nonce(0),
|
||||||
|
factories.clone()
|
||||||
|
).map_err(EvmTestError::Trie)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn state_from_pod(spec: &'a spec::Spec, factories: &Factories, pod_state: pod_state::PodState) -> Result<state::State<state_db::StateDB>, EvmTestError> {
|
||||||
|
let db = Arc::new(kvdb::in_memory(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(
|
||||||
|
state_db,
|
||||||
|
spec.engine.account_start_nonce(0),
|
||||||
|
factories.clone(),
|
||||||
|
);
|
||||||
|
state.populate_from(pod_state);
|
||||||
|
state.commit()?;
|
||||||
|
Ok(state)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Execute the VM given ActionParams and tracer.
|
||||||
|
/// Returns amount of gas left and the output.
|
||||||
pub fn call<T: trace::VMTracer>(&mut self, params: ActionParams, vm_tracer: &mut T)
|
pub fn call<T: trace::VMTracer>(&mut self, params: ActionParams, vm_tracer: &mut T)
|
||||||
-> Result<(U256, Vec<u8>), EvmTestError>
|
-> Result<(U256, Vec<u8>), EvmTestError>
|
||||||
{
|
{
|
||||||
let genesis = self.spec.genesis_header();
|
let genesis = self.spec.genesis_header();
|
||||||
let mut state = state::State::from_existing(self.state_db.boxed_clone(), *genesis.state_root(), self.spec.engine.account_start_nonce(0), self.factories.clone())
|
|
||||||
.map_err(EvmTestError::Trie)?;
|
|
||||||
let info = client::EnvInfo {
|
let info = client::EnvInfo {
|
||||||
number: genesis.number(),
|
number: genesis.number(),
|
||||||
author: *genesis.author(),
|
author: *genesis.author(),
|
||||||
@ -103,7 +173,7 @@ impl EvmTestClient {
|
|||||||
let mut substate = state::Substate::new();
|
let mut substate = state::Substate::new();
|
||||||
let mut tracer = trace::NoopTracer;
|
let mut tracer = trace::NoopTracer;
|
||||||
let mut output = vec![];
|
let mut output = vec![];
|
||||||
let mut executive = executive::Executive::new(&mut state, &info, &*self.spec.engine);
|
let mut executive = executive::Executive::new(&mut self.state, &info, &*self.spec.engine);
|
||||||
let (gas_left, _) = executive.call(
|
let (gas_left, _) = executive.call(
|
||||||
params,
|
params,
|
||||||
&mut substate,
|
&mut substate,
|
||||||
@ -114,4 +184,59 @@ impl EvmTestClient {
|
|||||||
|
|
||||||
Ok((gas_left, output))
|
Ok((gas_left, output))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Executes a SignedTransaction within context of the provided state and `EnvInfo`.
|
||||||
|
/// Returns the state root, gas left and the output.
|
||||||
|
pub fn transact<T: trace::VMTracer>(
|
||||||
|
&mut self,
|
||||||
|
env_info: &client::EnvInfo,
|
||||||
|
transaction: transaction::SignedTransaction,
|
||||||
|
vm_tracer: T,
|
||||||
|
) -> TransactResult {
|
||||||
|
let initial_gas = transaction.gas;
|
||||||
|
// Verify transaction
|
||||||
|
let is_ok = transaction.verify_basic(true, None, env_info.number >= self.spec.engine.params().eip86_transition);
|
||||||
|
if let Err(error) = is_ok {
|
||||||
|
return TransactResult::Err {
|
||||||
|
state_root: *self.state.root(),
|
||||||
|
error,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply transaction
|
||||||
|
let tracer = trace::NoopTracer;
|
||||||
|
let result = self.state.apply_with_tracing(&env_info, &*self.spec.engine, &transaction, tracer, vm_tracer);
|
||||||
|
|
||||||
|
match result {
|
||||||
|
Ok(result) => TransactResult::Ok {
|
||||||
|
state_root: *self.state.root(),
|
||||||
|
gas_left: initial_gas - result.receipt.gas_used,
|
||||||
|
output: result.output
|
||||||
|
},
|
||||||
|
Err(error) => TransactResult::Err {
|
||||||
|
state_root: *self.state.root(),
|
||||||
|
error,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A result of applying transaction to the state.
|
||||||
|
pub enum TransactResult {
|
||||||
|
/// Successful execution
|
||||||
|
Ok {
|
||||||
|
/// State root
|
||||||
|
state_root: H256,
|
||||||
|
/// Amount of gas left
|
||||||
|
gas_left: U256,
|
||||||
|
/// Output
|
||||||
|
output: Vec<u8>,
|
||||||
|
},
|
||||||
|
/// Transaction failed to run
|
||||||
|
Err {
|
||||||
|
/// State root
|
||||||
|
state_root: H256,
|
||||||
|
/// Execution error
|
||||||
|
error: ::error::Error,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,7 @@ mod client;
|
|||||||
pub use self::client::*;
|
pub use self::client::*;
|
||||||
pub use self::config::{Mode, ClientConfig, DatabaseCompactionProfile, BlockChainConfig, VMType};
|
pub use self::config::{Mode, ClientConfig, DatabaseCompactionProfile, BlockChainConfig, VMType};
|
||||||
pub use self::error::Error;
|
pub use self::error::Error;
|
||||||
pub use self::evm_test_client::{EvmTestClient, EvmTestError};
|
pub use self::evm_test_client::{EvmTestClient, EvmTestError, TransactResult};
|
||||||
pub use self::test_client::{TestBlockChainClient, EachBlockWith};
|
pub use self::test_client::{TestBlockChainClient, EachBlockWith};
|
||||||
pub use self::chain_notify::ChainNotify;
|
pub use self::chain_notify::ChainNotify;
|
||||||
pub use self::traits::{BlockChainClient, MiningBlockChainClient, EngineClient};
|
pub use self::traits::{BlockChainClient, MiningBlockChainClient, EngineClient};
|
||||||
|
@ -26,7 +26,7 @@ use evm::{CallType, Factory, Finalize, FinalizationResult};
|
|||||||
use vm::{self, Ext, CreateContractAddress, ReturnData, CleanDustMode, ActionParams, ActionValue};
|
use vm::{self, Ext, CreateContractAddress, ReturnData, CleanDustMode, ActionParams, ActionValue};
|
||||||
use wasm;
|
use wasm;
|
||||||
use externalities::*;
|
use externalities::*;
|
||||||
use trace::{FlatTrace, Tracer, NoopTracer, ExecutiveTracer, VMTrace, VMTracer, ExecutiveVMTracer, NoopVMTracer};
|
use trace::{self, FlatTrace, VMTrace, Tracer, VMTracer};
|
||||||
use transaction::{Action, SignedTransaction};
|
use transaction::{Action, SignedTransaction};
|
||||||
use crossbeam;
|
use crossbeam;
|
||||||
pub use executed::{Executed, ExecutionResult};
|
pub use executed::{Executed, ExecutionResult};
|
||||||
@ -66,16 +66,77 @@ pub fn contract_address(address_scheme: CreateContractAddress, sender: &Address,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Transaction execution options.
|
/// Transaction execution options.
|
||||||
#[derive(Default, Copy, Clone, PartialEq)]
|
#[derive(Copy, Clone, PartialEq)]
|
||||||
pub struct TransactOptions {
|
pub struct TransactOptions<T, V> {
|
||||||
/// Enable call tracing.
|
/// Enable call tracing.
|
||||||
pub tracing: bool,
|
pub tracer: T,
|
||||||
/// Enable VM tracing.
|
/// Enable VM tracing.
|
||||||
pub vm_tracing: bool,
|
pub vm_tracer: V,
|
||||||
/// Check transaction nonce before execution.
|
/// Check transaction nonce before execution.
|
||||||
pub check_nonce: bool,
|
pub check_nonce: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T, V> TransactOptions<T, V> {
|
||||||
|
/// Create new `TransactOptions` with given tracer and VM tracer.
|
||||||
|
pub fn new(tracer: T, vm_tracer: V) -> Self {
|
||||||
|
TransactOptions {
|
||||||
|
tracer,
|
||||||
|
vm_tracer,
|
||||||
|
check_nonce: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Disables the nonce check
|
||||||
|
pub fn dont_check_nonce(mut self) -> Self {
|
||||||
|
self.check_nonce = false;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TransactOptions<trace::ExecutiveTracer, trace::ExecutiveVMTracer> {
|
||||||
|
/// Creates new `TransactOptions` with default tracing and VM tracing.
|
||||||
|
pub fn with_tracing_and_vm_tracing() -> Self {
|
||||||
|
TransactOptions {
|
||||||
|
tracer: trace::ExecutiveTracer::default(),
|
||||||
|
vm_tracer: trace::ExecutiveVMTracer::toplevel(),
|
||||||
|
check_nonce: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TransactOptions<trace::ExecutiveTracer, trace::NoopVMTracer> {
|
||||||
|
/// Creates new `TransactOptions` with default tracing and no VM tracing.
|
||||||
|
pub fn with_tracing() -> Self {
|
||||||
|
TransactOptions {
|
||||||
|
tracer: trace::ExecutiveTracer::default(),
|
||||||
|
vm_tracer: trace::NoopVMTracer,
|
||||||
|
check_nonce: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TransactOptions<trace::NoopTracer, trace::ExecutiveVMTracer> {
|
||||||
|
/// Creates new `TransactOptions` with no tracing and default VM tracing.
|
||||||
|
pub fn with_vm_tracing() -> Self {
|
||||||
|
TransactOptions {
|
||||||
|
tracer: trace::NoopTracer,
|
||||||
|
vm_tracer: trace::ExecutiveVMTracer::toplevel(),
|
||||||
|
check_nonce: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TransactOptions<trace::NoopTracer, trace::NoopVMTracer> {
|
||||||
|
/// Creates new `TransactOptions` without any tracing.
|
||||||
|
pub fn with_no_tracing() -> Self {
|
||||||
|
TransactOptions {
|
||||||
|
tracer: trace::NoopTracer,
|
||||||
|
vm_tracer: trace::NoopVMTracer,
|
||||||
|
check_nonce: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn executor<E>(engine: &E, vm_factory: &Factory, params: &ActionParams)
|
pub fn executor<E>(engine: &E, vm_factory: &Factory, params: &ActionParams)
|
||||||
-> Box<vm::Vm> where E: Engine + ?Sized
|
-> Box<vm::Vm> where E: Engine + ?Sized
|
||||||
{
|
{
|
||||||
@ -137,24 +198,18 @@ impl<'a, B: 'a + StateBackend, E: Engine + ?Sized> Executive<'a, B, E> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// This function should be used to execute transaction.
|
/// This function should be used to execute transaction.
|
||||||
pub fn transact(&'a mut self, t: &SignedTransaction, options: TransactOptions) -> Result<Executed, ExecutionError> {
|
pub fn transact<T, V>(&'a mut self, t: &SignedTransaction, options: TransactOptions<T, V>)
|
||||||
let check = options.check_nonce;
|
-> Result<Executed, ExecutionError> where T: Tracer, V: VMTracer,
|
||||||
match options.tracing {
|
{
|
||||||
true => match options.vm_tracing {
|
self.transact_with_tracer(t, options.check_nonce, options.tracer, options.vm_tracer)
|
||||||
true => self.transact_with_tracer(t, check, ExecutiveTracer::default(), ExecutiveVMTracer::toplevel()),
|
|
||||||
false => self.transact_with_tracer(t, check, ExecutiveTracer::default(), NoopVMTracer),
|
|
||||||
},
|
|
||||||
false => match options.vm_tracing {
|
|
||||||
true => self.transact_with_tracer(t, check, NoopTracer, ExecutiveVMTracer::toplevel()),
|
|
||||||
false => self.transact_with_tracer(t, check, NoopTracer, NoopVMTracer),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Execute a transaction in a "virtual" context.
|
/// Execute a transaction in a "virtual" context.
|
||||||
/// This will ensure the caller has enough balance to execute the desired transaction.
|
/// This will ensure the caller has enough balance to execute the desired transaction.
|
||||||
/// Used for extra-block executions for things like consensus contracts and RPCs
|
/// Used for extra-block executions for things like consensus contracts and RPCs
|
||||||
pub fn transact_virtual(&'a mut self, t: &SignedTransaction, options: TransactOptions) -> Result<Executed, ExecutionError> {
|
pub fn transact_virtual<T, V>(&'a mut self, t: &SignedTransaction, options: TransactOptions<T, V>)
|
||||||
|
-> Result<Executed, ExecutionError> where T: Tracer, V: VMTracer,
|
||||||
|
{
|
||||||
let sender = t.sender();
|
let sender = t.sender();
|
||||||
let balance = self.state.balance(&sender)?;
|
let balance = self.state.balance(&sender)?;
|
||||||
let needed_balance = t.value.saturating_add(t.gas.saturating_mul(t.gas_price));
|
let needed_balance = t.value.saturating_add(t.gas.saturating_mul(t.gas_price));
|
||||||
@ -167,7 +222,7 @@ impl<'a, B: 'a + StateBackend, E: Engine + ?Sized> Executive<'a, B, E> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Execute transaction/call with tracing enabled
|
/// Execute transaction/call with tracing enabled
|
||||||
pub fn transact_with_tracer<T, V>(
|
fn transact_with_tracer<T, V>(
|
||||||
&'a mut self,
|
&'a mut self,
|
||||||
t: &SignedTransaction,
|
t: &SignedTransaction,
|
||||||
check_nonce: bool,
|
check_nonce: bool,
|
||||||
@ -261,7 +316,7 @@ impl<'a, B: 'a + StateBackend, E: Engine + ?Sized> Executive<'a, B, E> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// finalize here!
|
// finalize here!
|
||||||
Ok(self.finalize(t, substate, result, output, tracer.traces(), vm_tracer.drain())?)
|
Ok(self.finalize(t, substate, result, output, tracer.drain(), vm_tracer.drain())?)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn exec_vm<T, V>(
|
fn exec_vm<T, V>(
|
||||||
@ -399,7 +454,7 @@ impl<'a, B: 'a + StateBackend, E: Engine + ?Sized> Executive<'a, B, E> {
|
|||||||
|
|
||||||
trace!(target: "executive", "res={:?}", res);
|
trace!(target: "executive", "res={:?}", res);
|
||||||
|
|
||||||
let traces = subtracer.traces();
|
let traces = subtracer.drain();
|
||||||
match res {
|
match res {
|
||||||
Ok(ref res) => tracer.trace_call(
|
Ok(ref res) => tracer.trace_call(
|
||||||
trace_info,
|
trace_info,
|
||||||
@ -484,9 +539,9 @@ impl<'a, B: 'a + StateBackend, E: Engine + ?Sized> Executive<'a, B, E> {
|
|||||||
gas - res.gas_left,
|
gas - res.gas_left,
|
||||||
trace_output,
|
trace_output,
|
||||||
created,
|
created,
|
||||||
subtracer.traces()
|
subtracer.drain()
|
||||||
),
|
),
|
||||||
Err(ref e) => tracer.trace_failed_create(trace_info, subtracer.traces(), e.into())
|
Err(ref e) => tracer.trace_failed_create(trace_info, subtracer.drain(), e.into())
|
||||||
};
|
};
|
||||||
|
|
||||||
self.enact_result(&res, substate, unconfirmed_substate);
|
self.enact_result(&res, substate, unconfirmed_substate);
|
||||||
@ -794,7 +849,7 @@ mod tests {
|
|||||||
}),
|
}),
|
||||||
}];
|
}];
|
||||||
|
|
||||||
assert_eq!(tracer.traces(), expected_trace);
|
assert_eq!(tracer.drain(), expected_trace);
|
||||||
|
|
||||||
let expected_vm_trace = VMTrace {
|
let expected_vm_trace = VMTrace {
|
||||||
parent_step: 0,
|
parent_step: 0,
|
||||||
@ -887,7 +942,7 @@ mod tests {
|
|||||||
}),
|
}),
|
||||||
}];
|
}];
|
||||||
|
|
||||||
assert_eq!(tracer.traces(), expected_trace);
|
assert_eq!(tracer.drain(), expected_trace);
|
||||||
|
|
||||||
let expected_vm_trace = VMTrace {
|
let expected_vm_trace = VMTrace {
|
||||||
parent_step: 0,
|
parent_step: 0,
|
||||||
@ -1138,7 +1193,7 @@ mod tests {
|
|||||||
|
|
||||||
let executed = {
|
let executed = {
|
||||||
let mut ex = Executive::new(&mut state, &info, &engine);
|
let mut ex = Executive::new(&mut state, &info, &engine);
|
||||||
let opts = TransactOptions { check_nonce: true, tracing: false, vm_tracing: false };
|
let opts = TransactOptions::with_no_tracing();
|
||||||
ex.transact(&t, opts).unwrap()
|
ex.transact(&t, opts).unwrap()
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1175,7 +1230,7 @@ mod tests {
|
|||||||
|
|
||||||
let res = {
|
let res = {
|
||||||
let mut ex = Executive::new(&mut state, &info, &engine);
|
let mut ex = Executive::new(&mut state, &info, &engine);
|
||||||
let opts = TransactOptions { check_nonce: true, tracing: false, vm_tracing: false };
|
let opts = TransactOptions::with_no_tracing();
|
||||||
ex.transact(&t, opts)
|
ex.transact(&t, opts)
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1208,7 +1263,7 @@ mod tests {
|
|||||||
|
|
||||||
let res = {
|
let res = {
|
||||||
let mut ex = Executive::new(&mut state, &info, &engine);
|
let mut ex = Executive::new(&mut state, &info, &engine);
|
||||||
let opts = TransactOptions { check_nonce: true, tracing: false, vm_tracing: false };
|
let opts = TransactOptions::with_no_tracing();
|
||||||
ex.transact(&t, opts)
|
ex.transact(&t, opts)
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1241,7 +1296,7 @@ mod tests {
|
|||||||
|
|
||||||
let res = {
|
let res = {
|
||||||
let mut ex = Executive::new(&mut state, &info, &engine);
|
let mut ex = Executive::new(&mut state, &info, &engine);
|
||||||
let opts = TransactOptions { check_nonce: true, tracing: false, vm_tracing: false };
|
let opts = TransactOptions::with_no_tracing();
|
||||||
ex.transact(&t, opts)
|
ex.transact(&t, opts)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -15,23 +15,13 @@
|
|||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use super::test_common::*;
|
use super::test_common::*;
|
||||||
use tests::helpers::*;
|
|
||||||
use pod_state::PodState;
|
use pod_state::PodState;
|
||||||
use ethereum;
|
use trace;
|
||||||
use spec::Spec;
|
use client::{EvmTestClient, EvmTestError, TransactResult};
|
||||||
use ethjson;
|
use ethjson;
|
||||||
use ethjson::state::test::ForkSpec;
|
|
||||||
use transaction::SignedTransaction;
|
use transaction::SignedTransaction;
|
||||||
use vm::EnvInfo;
|
use vm::EnvInfo;
|
||||||
|
|
||||||
lazy_static! {
|
|
||||||
pub static ref FRONTIER: Spec = ethereum::new_frontier_test();
|
|
||||||
pub static ref HOMESTEAD: Spec = ethereum::new_homestead_test();
|
|
||||||
pub static ref EIP150: Spec = ethereum::new_eip150_test();
|
|
||||||
pub static ref EIP161: Spec = ethereum::new_eip161_test();
|
|
||||||
pub static ref _METROPOLIS: Spec = ethereum::new_metropolis_test();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn json_chain_test(json_data: &[u8]) -> Vec<String> {
|
pub fn json_chain_test(json_data: &[u8]) -> Vec<String> {
|
||||||
::ethcore_logger::init_log();
|
::ethcore_logger::init_log();
|
||||||
let tests = ethjson::state::test::Test::load(json_data).unwrap();
|
let tests = ethjson::state::test::Test::load(json_data).unwrap();
|
||||||
@ -43,35 +33,49 @@ pub fn json_chain_test(json_data: &[u8]) -> Vec<String> {
|
|||||||
let env: EnvInfo = test.env.into();
|
let env: EnvInfo = test.env.into();
|
||||||
let pre: PodState = test.pre_state.into();
|
let pre: PodState = test.pre_state.into();
|
||||||
|
|
||||||
for (spec, states) in test.post_states {
|
for (spec_name, states) in test.post_states {
|
||||||
let total = states.len();
|
let total = states.len();
|
||||||
let engine = match spec {
|
let spec = match EvmTestClient::spec_from_json(&spec_name) {
|
||||||
ForkSpec::Frontier => &FRONTIER.engine,
|
Some(spec) => spec,
|
||||||
ForkSpec::Homestead => &HOMESTEAD.engine,
|
None => {
|
||||||
ForkSpec::EIP150 => &EIP150.engine,
|
println!(" - {} | {:?} Ignoring tests because of missing spec", name, spec_name);
|
||||||
ForkSpec::EIP158 => &EIP161.engine,
|
continue;
|
||||||
ForkSpec::Metropolis => continue,
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
for (i, state) in states.into_iter().enumerate() {
|
for (i, state) in states.into_iter().enumerate() {
|
||||||
let info = format!(" - {} | {:?} ({}/{}) ...", name, spec, i + 1, total);
|
let info = format!(" - {} | {:?} ({}/{}) ...", name, spec_name, i + 1, total);
|
||||||
|
|
||||||
let post_root: H256 = state.hash.into();
|
let post_root: H256 = state.hash.into();
|
||||||
let transaction: SignedTransaction = multitransaction.select(&state.indexes).into();
|
let transaction: SignedTransaction = multitransaction.select(&state.indexes).into();
|
||||||
let mut state = get_temp_state();
|
|
||||||
state.populate_from(pre.clone());
|
let result = || -> Result<_, EvmTestError> {
|
||||||
if transaction.verify_basic(true, None, env.number >= engine.params().eip86_transition).is_ok() {
|
Ok(EvmTestClient::from_pod_state(spec, pre.clone())?
|
||||||
state.commit().expect(&format!("State test {} failed due to internal error.", name));
|
.transact(&env, transaction, trace::NoopVMTracer))
|
||||||
let _res = state.apply(&env, &**engine, &transaction, false);
|
};
|
||||||
} else {
|
match result() {
|
||||||
let _rest = state.commit();
|
Err(err) => {
|
||||||
}
|
println!("{} !!! Unexpected internal error: {:?}", info, err);
|
||||||
if state.root() != &post_root {
|
flushln!("{} fail", info);
|
||||||
println!("{} !!! State mismatch (got: {}, expect: {}", info, state.root(), post_root);
|
failed.push(name.clone());
|
||||||
flushln!("{} fail", info);
|
},
|
||||||
failed.push(name.clone());
|
Ok(TransactResult::Ok { state_root, .. }) if state_root != post_root => {
|
||||||
} else {
|
println!("{} !!! State mismatch (got: {}, expect: {}", info, state_root, post_root);
|
||||||
flushln!("{} ok", info);
|
flushln!("{} fail", info);
|
||||||
|
failed.push(name.clone());
|
||||||
|
},
|
||||||
|
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);
|
||||||
|
failed.push(name.clone());
|
||||||
|
},
|
||||||
|
Ok(TransactResult::Err { error, .. }) => {
|
||||||
|
flushln!("{} ok ({:?})", info, error);
|
||||||
|
},
|
||||||
|
Ok(_) => {
|
||||||
|
flushln!("{} ok", info);
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,7 +31,7 @@ use vm::EnvInfo;
|
|||||||
use error::Error;
|
use error::Error;
|
||||||
use executive::{Executive, TransactOptions};
|
use executive::{Executive, TransactOptions};
|
||||||
use factory::Factories;
|
use factory::Factories;
|
||||||
use trace::FlatTrace;
|
use trace::{self, FlatTrace, VMTrace};
|
||||||
use pod_account::*;
|
use pod_account::*;
|
||||||
use pod_state::{self, PodState};
|
use pod_state::{self, PodState};
|
||||||
use types::basic_account::BasicAccount;
|
use types::basic_account::BasicAccount;
|
||||||
@ -59,8 +59,12 @@ pub use self::substate::Substate;
|
|||||||
pub struct ApplyOutcome {
|
pub struct ApplyOutcome {
|
||||||
/// The receipt for the applied transaction.
|
/// The receipt for the applied transaction.
|
||||||
pub receipt: Receipt,
|
pub receipt: Receipt,
|
||||||
/// The trace for the applied transaction, if None if tracing is disabled.
|
/// The output of the applied transaction.
|
||||||
|
pub output: Bytes,
|
||||||
|
/// The trace for the applied transaction, empty if tracing was not produced.
|
||||||
pub trace: Vec<FlatTrace>,
|
pub trace: Vec<FlatTrace>,
|
||||||
|
/// The VM trace for the applied transaction, None if tracing was not produced.
|
||||||
|
pub vm_trace: Option<VMTrace>
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Result type for the execution ("application") of a transaction.
|
/// Result type for the execution ("application") of a transaction.
|
||||||
@ -205,7 +209,7 @@ pub fn check_proof(
|
|||||||
Err(_) => return ProvedExecution::BadProof,
|
Err(_) => return ProvedExecution::BadProof,
|
||||||
};
|
};
|
||||||
|
|
||||||
match state.execute(env_info, engine, transaction, false, true) {
|
match state.execute(env_info, engine, transaction, TransactOptions::with_no_tracing(), true) {
|
||||||
Ok(executed) => ProvedExecution::Complete(executed),
|
Ok(executed) => ProvedExecution::Complete(executed),
|
||||||
Err(ExecutionError::Internal(_)) => ProvedExecution::BadProof,
|
Err(ExecutionError::Internal(_)) => ProvedExecution::BadProof,
|
||||||
Err(e) => ProvedExecution::Failed(e),
|
Err(e) => ProvedExecution::Failed(e),
|
||||||
@ -290,7 +294,7 @@ const SEC_TRIE_DB_UNWRAP_STR: &'static str = "A state can only be created with v
|
|||||||
|
|
||||||
impl<B: Backend> State<B> {
|
impl<B: Backend> State<B> {
|
||||||
/// Creates new state with empty state root
|
/// Creates new state with empty state root
|
||||||
#[cfg(test)]
|
/// Used for tests.
|
||||||
pub fn new(mut db: B, account_start_nonce: U256, factories: Factories) -> State<B> {
|
pub fn new(mut db: B, account_start_nonce: U256, factories: Factories) -> State<B> {
|
||||||
let mut root = H256::new();
|
let mut root = H256::new();
|
||||||
{
|
{
|
||||||
@ -623,29 +627,57 @@ impl<B: Backend> State<B> {
|
|||||||
/// Execute a given transaction, producing a receipt and an optional trace.
|
/// Execute a given transaction, producing a receipt and an optional trace.
|
||||||
/// This will change the state accordingly.
|
/// This will change the state accordingly.
|
||||||
pub fn apply(&mut self, env_info: &EnvInfo, engine: &Engine, t: &SignedTransaction, tracing: bool) -> ApplyResult {
|
pub fn apply(&mut self, env_info: &EnvInfo, engine: &Engine, t: &SignedTransaction, tracing: bool) -> ApplyResult {
|
||||||
// let old = self.to_pod();
|
if tracing {
|
||||||
|
let options = TransactOptions::with_tracing();
|
||||||
|
self.apply_with_tracing(env_info, engine, t, options.tracer, options.vm_tracer)
|
||||||
|
} else {
|
||||||
|
let options = TransactOptions::with_no_tracing();
|
||||||
|
self.apply_with_tracing(env_info, engine, t, options.tracer, options.vm_tracer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Execute a given transaction with given tracer and VM tracer producing a receipt and an optional trace.
|
||||||
|
/// This will change the state accordingly.
|
||||||
|
pub fn apply_with_tracing<V, T>(
|
||||||
|
&mut self,
|
||||||
|
env_info: &EnvInfo,
|
||||||
|
engine: &Engine,
|
||||||
|
t: &SignedTransaction,
|
||||||
|
tracer: T,
|
||||||
|
vm_tracer: V,
|
||||||
|
) -> ApplyResult where
|
||||||
|
T: trace::Tracer,
|
||||||
|
V: trace::VMTracer,
|
||||||
|
{
|
||||||
|
let options = TransactOptions::new(tracer, vm_tracer);
|
||||||
|
let e = self.execute(env_info, engine, t, options, false)?;
|
||||||
|
|
||||||
let e = self.execute(env_info, engine, t, tracing, false)?;
|
|
||||||
// trace!("Applied transaction. Diff:\n{}\n", state_diff::diff_pod(&old, &self.to_pod()));
|
|
||||||
let state_root = if env_info.number < engine.params().eip98_transition || env_info.number < engine.params().validate_receipts_transition {
|
let state_root = if env_info.number < engine.params().eip98_transition || env_info.number < engine.params().validate_receipts_transition {
|
||||||
self.commit()?;
|
self.commit()?;
|
||||||
Some(self.root().clone())
|
Some(self.root().clone())
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let output = e.output;
|
||||||
let receipt = Receipt::new(state_root, e.cumulative_gas_used, e.logs);
|
let receipt = Receipt::new(state_root, e.cumulative_gas_used, e.logs);
|
||||||
trace!(target: "state", "Transaction receipt: {:?}", receipt);
|
trace!(target: "state", "Transaction receipt: {:?}", receipt);
|
||||||
Ok(ApplyOutcome{receipt: receipt, trace: e.trace})
|
|
||||||
|
Ok(ApplyOutcome {
|
||||||
|
receipt,
|
||||||
|
output,
|
||||||
|
trace: e.trace,
|
||||||
|
vm_trace: e.vm_trace,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Execute a given transaction without committing changes.
|
// Execute a given transaction without committing changes.
|
||||||
//
|
//
|
||||||
// `virt` signals that we are executing outside of a block set and restrictions like
|
// `virt` signals that we are executing outside of a block set and restrictions like
|
||||||
// gas limits and gas costs should be lifted.
|
// gas limits and gas costs should be lifted.
|
||||||
fn execute(&mut self, env_info: &EnvInfo, engine: &Engine, t: &SignedTransaction, tracing: bool, virt: bool)
|
fn execute<T, V>(&mut self, env_info: &EnvInfo, engine: &Engine, t: &SignedTransaction, options: TransactOptions<T, V>, virt: bool)
|
||||||
-> Result<Executed, ExecutionError>
|
-> Result<Executed, ExecutionError> where T: trace::Tracer, V: trace::VMTracer,
|
||||||
{
|
{
|
||||||
let options = TransactOptions { tracing: tracing, vm_tracing: false, check_nonce: true };
|
|
||||||
let mut e = Executive::new(self, env_info, engine);
|
let mut e = Executive::new(self, env_info, engine);
|
||||||
|
|
||||||
match virt {
|
match virt {
|
||||||
@ -730,9 +762,8 @@ impl<B: Backend> State<B> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
#[cfg(feature = "json-tests")]
|
|
||||||
/// Populate the state from `accounts`.
|
/// Populate the state from `accounts`.
|
||||||
|
/// Used for tests.
|
||||||
pub fn populate_from(&mut self, accounts: PodState) {
|
pub fn populate_from(&mut self, accounts: PodState) {
|
||||||
assert!(self.checkpoints.borrow().is_empty());
|
assert!(self.checkpoints.borrow().is_empty());
|
||||||
for (add, acc) in accounts.drain().into_iter() {
|
for (add, acc) in accounts.drain().into_iter() {
|
||||||
|
@ -19,7 +19,7 @@ use std::sync::Arc;
|
|||||||
use io::IoChannel;
|
use io::IoChannel;
|
||||||
use client::{BlockChainClient, MiningBlockChainClient, Client, ClientConfig, BlockId};
|
use client::{BlockChainClient, MiningBlockChainClient, Client, ClientConfig, BlockId};
|
||||||
use state::{self, State, CleanupMode};
|
use state::{self, State, CleanupMode};
|
||||||
use executive::Executive;
|
use executive::{Executive, TransactOptions};
|
||||||
use ethereum;
|
use ethereum;
|
||||||
use block::IsBlock;
|
use block::IsBlock;
|
||||||
use tests::helpers::*;
|
use tests::helpers::*;
|
||||||
@ -361,7 +361,7 @@ fn transaction_proof() {
|
|||||||
|
|
||||||
let mut state = State::from_existing(backend, root, 0.into(), factories.clone()).unwrap();
|
let mut state = State::from_existing(backend, root, 0.into(), factories.clone()).unwrap();
|
||||||
Executive::new(&mut state, &client.latest_env_info(), &*test_spec.engine)
|
Executive::new(&mut state, &client.latest_env_info(), &*test_spec.engine)
|
||||||
.transact(&transaction, Default::default()).unwrap();
|
.transact(&transaction, TransactOptions::with_no_tracing().dont_check_nonce()).unwrap();
|
||||||
|
|
||||||
assert_eq!(state.balance(&Address::default()).unwrap(), 5.into());
|
assert_eq!(state.balance(&Address::default()).unwrap(), 5.into());
|
||||||
assert_eq!(state.balance(&address).unwrap(), 95.into());
|
assert_eq!(state.balance(&address).unwrap(), 95.into());
|
||||||
|
@ -167,7 +167,7 @@ impl Tracer for ExecutiveTracer {
|
|||||||
ExecutiveTracer::default()
|
ExecutiveTracer::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn traces(self) -> Vec<FlatTrace> {
|
fn drain(self) -> Vec<FlatTrace> {
|
||||||
self.traces
|
self.traces
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -85,7 +85,7 @@ pub trait Tracer: Send {
|
|||||||
fn subtracer(&self) -> Self where Self: Sized;
|
fn subtracer(&self) -> Self where Self: Sized;
|
||||||
|
|
||||||
/// Consumes self and returns all traces.
|
/// Consumes self and returns all traces.
|
||||||
fn traces(self) -> Vec<FlatTrace>;
|
fn drain(self) -> Vec<FlatTrace>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Used by executive to build VM traces.
|
/// Used by executive to build VM traces.
|
||||||
|
@ -62,7 +62,7 @@ impl Tracer for NoopTracer {
|
|||||||
NoopTracer
|
NoopTracer
|
||||||
}
|
}
|
||||||
|
|
||||||
fn traces(self) -> Vec<FlatTrace> {
|
fn drain(self) -> Vec<FlatTrace> {
|
||||||
vec![]
|
vec![]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,7 @@ docopt = "0.8"
|
|||||||
serde = "1.0"
|
serde = "1.0"
|
||||||
serde_derive = "1.0"
|
serde_derive = "1.0"
|
||||||
ethcore = { path = "../ethcore" }
|
ethcore = { path = "../ethcore" }
|
||||||
|
ethjson = { path = "../json" }
|
||||||
ethcore-util = { path = "../util" }
|
ethcore-util = { path = "../util" }
|
||||||
evm = { path = "../ethcore/evm" }
|
evm = { path = "../ethcore/evm" }
|
||||||
vm = { path = "../ethcore/vm" }
|
vm = { path = "../ethcore/vm" }
|
||||||
|
@ -30,10 +30,8 @@ pub struct Informant {
|
|||||||
depth: usize,
|
depth: usize,
|
||||||
pc: usize,
|
pc: usize,
|
||||||
instruction: u8,
|
instruction: u8,
|
||||||
name: &'static str,
|
|
||||||
gas_cost: U256,
|
gas_cost: U256,
|
||||||
gas_used: U256,
|
gas_used: U256,
|
||||||
stack_pop: usize,
|
|
||||||
stack: Vec<U256>,
|
stack: Vec<U256>,
|
||||||
memory: Vec<u8>,
|
memory: Vec<u8>,
|
||||||
storage: HashMap<H256, H256>,
|
storage: HashMap<H256, H256>,
|
||||||
@ -58,11 +56,19 @@ impl Informant {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl vm::Informant for Informant {
|
impl vm::Informant for Informant {
|
||||||
|
fn before_test(&self, name: &str, action: &str) {
|
||||||
|
println!(
|
||||||
|
"{{\"test\":\"{name}\",\"action\":\"{action}\"}}",
|
||||||
|
name = name,
|
||||||
|
action = action,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
fn set_gas(&mut self, gas: U256) {
|
fn set_gas(&mut self, gas: U256) {
|
||||||
self.gas_used = gas;
|
self.gas_used = gas;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn finish(&mut self, result: Result<vm::Success, vm::Failure>) {
|
fn finish(result: Result<vm::Success, vm::Failure>) {
|
||||||
match result {
|
match result {
|
||||||
Ok(success) => println!(
|
Ok(success) => println!(
|
||||||
"{{\"output\":\"0x{output}\",\"gasUsed\":\"{gas:x}\",\"time\":{time}}}",
|
"{{\"output\":\"0x{output}\",\"gasUsed\":\"{gas:x}\",\"time\":{time}}}",
|
||||||
@ -112,7 +118,7 @@ impl trace::VMTracer for Informant {
|
|||||||
self.gas_used = gas_used;
|
self.gas_used = gas_used;
|
||||||
|
|
||||||
let len = self.stack.len();
|
let len = self.stack.len();
|
||||||
self.stack.truncate(len - info.args);
|
self.stack.truncate(if len > info.args { len - info.args } else { 0 });
|
||||||
self.stack.extend_from_slice(stack_push);
|
self.stack.extend_from_slice(stack_push);
|
||||||
|
|
||||||
if let Some((pos, data)) = mem_diff {
|
if let Some((pos, data)) = mem_diff {
|
||||||
|
@ -27,7 +27,11 @@ use info as vm;
|
|||||||
pub struct Informant;
|
pub struct Informant;
|
||||||
|
|
||||||
impl vm::Informant for Informant {
|
impl vm::Informant for Informant {
|
||||||
fn finish(&mut self, result: Result<vm::Success, vm::Failure>) {
|
fn before_test(&self, name: &str, action: &str) {
|
||||||
|
println!("Test: {} ({})", name, action);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn finish(result: Result<vm::Success, vm::Failure>) {
|
||||||
match result {
|
match result {
|
||||||
Ok(success) => {
|
Ok(success) => {
|
||||||
println!("Output: 0x{}", success.output.to_hex());
|
println!("Output: 0x{}", success.output.to_hex());
|
||||||
|
@ -17,17 +17,19 @@
|
|||||||
//! VM runner.
|
//! VM runner.
|
||||||
|
|
||||||
use std::time::{Instant, Duration};
|
use std::time::{Instant, Duration};
|
||||||
use util::U256;
|
use util::{U256, H256};
|
||||||
use ethcore::{trace, spec};
|
use ethcore::{trace, spec, transaction, pod_state};
|
||||||
use ethcore::client::{EvmTestClient, EvmTestError};
|
use ethcore::client::{self, EvmTestClient, EvmTestError, TransactResult};
|
||||||
use vm::ActionParams;
|
use ethjson;
|
||||||
|
|
||||||
/// VM execution informant
|
/// VM execution informant
|
||||||
pub trait Informant: trace::VMTracer {
|
pub trait Informant: trace::VMTracer {
|
||||||
|
/// Display a single run init message
|
||||||
|
fn before_test(&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) {}
|
||||||
/// Display final result.
|
/// Display final result.
|
||||||
fn finish(&mut self, result: Result<Success, Failure>);
|
fn finish(result: Result<Success, Failure>);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Execution finished correctly
|
/// Execution finished correctly
|
||||||
@ -50,17 +52,71 @@ pub struct Failure {
|
|||||||
pub time: Duration,
|
pub time: Duration,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Execute given Transaction and verify resulting state root.
|
||||||
|
pub fn run_transaction<T: Informant>(
|
||||||
|
name: &str,
|
||||||
|
idx: usize,
|
||||||
|
spec: ðjson::state::test::ForkSpec,
|
||||||
|
pre_state: &pod_state::PodState,
|
||||||
|
post_root: H256,
|
||||||
|
env_info: &client::EnvInfo,
|
||||||
|
transaction: transaction::SignedTransaction,
|
||||||
|
mut informant: T,
|
||||||
|
) {
|
||||||
|
let spec_name = format!("{:?}", spec).to_lowercase();
|
||||||
|
let spec = match EvmTestClient::spec_from_json(spec) {
|
||||||
|
Some(spec) => {
|
||||||
|
informant.before_test(&format!("{}:{}:{}", name, spec_name, idx), "starting");
|
||||||
|
spec
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
informant.before_test(&format!("{}:{}:{}", name, spec_name, idx), "skipping because of missing spec");
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
informant.set_gas(env_info.gas_limit);
|
||||||
|
|
||||||
|
let result = run(spec, env_info.gas_limit, pre_state, |mut client| {
|
||||||
|
let result = client.transact(env_info, transaction, informant);
|
||||||
|
match result {
|
||||||
|
TransactResult::Ok { state_root, .. } if state_root != post_root => {
|
||||||
|
Err(EvmTestError::PostCondition(format!(
|
||||||
|
"State root mismatch (got: {}, expected: {})",
|
||||||
|
state_root,
|
||||||
|
post_root,
|
||||||
|
)))
|
||||||
|
},
|
||||||
|
TransactResult::Ok { gas_left, output, .. } => {
|
||||||
|
Ok((gas_left, output))
|
||||||
|
},
|
||||||
|
TransactResult::Err { error, .. } => {
|
||||||
|
Err(EvmTestError::PostCondition(format!(
|
||||||
|
"Unexpected execution error: {:?}", error
|
||||||
|
)))
|
||||||
|
},
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
T::finish(result)
|
||||||
|
}
|
||||||
|
|
||||||
/// Execute VM with given `ActionParams`
|
/// Execute VM with given `ActionParams`
|
||||||
pub fn run<T: trace::VMTracer>(vm_tracer: &mut T, spec: spec::Spec, params: ActionParams) -> Result<Success, Failure> {
|
pub fn run<'a, F, T>(spec: &'a spec::Spec, initial_gas: U256, pre_state: T, run: F) -> Result<Success, Failure> where
|
||||||
let mut test_client = EvmTestClient::new(spec).map_err(|error| Failure {
|
F: FnOnce(EvmTestClient) -> Result<(U256, Vec<u8>), EvmTestError>,
|
||||||
|
T: Into<Option<&'a pod_state::PodState>>,
|
||||||
|
{
|
||||||
|
let test_client = match pre_state.into() {
|
||||||
|
Some(pre_state) => EvmTestClient::from_pod_state(spec, pre_state.clone()),
|
||||||
|
None => EvmTestClient::new(spec),
|
||||||
|
}.map_err(|error| Failure {
|
||||||
gas_used: 0.into(),
|
gas_used: 0.into(),
|
||||||
error,
|
error,
|
||||||
time: Duration::from_secs(0)
|
time: Duration::from_secs(0)
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let initial_gas = params.gas;
|
|
||||||
let start = Instant::now();
|
let start = Instant::now();
|
||||||
let result = test_client.call(params, vm_tracer);
|
let result = run(test_client);
|
||||||
let duration = start.elapsed();
|
let duration = start.elapsed();
|
||||||
|
|
||||||
match result {
|
match result {
|
||||||
|
@ -17,8 +17,9 @@
|
|||||||
//! Parity EVM interpreter binary.
|
//! Parity EVM interpreter binary.
|
||||||
|
|
||||||
#![warn(missing_docs)]
|
#![warn(missing_docs)]
|
||||||
#![allow(dead_code)]
|
|
||||||
extern crate ethcore;
|
extern crate ethcore;
|
||||||
|
extern crate ethjson;
|
||||||
extern crate rustc_hex;
|
extern crate rustc_hex;
|
||||||
extern crate serde;
|
extern crate serde;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
@ -31,6 +32,7 @@ extern crate panic_hook;
|
|||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::{fmt, fs};
|
use std::{fmt, fs};
|
||||||
|
use std::path::PathBuf;
|
||||||
use docopt::Docopt;
|
use docopt::Docopt;
|
||||||
use rustc_hex::FromHex;
|
use rustc_hex::FromHex;
|
||||||
use util::{U256, Bytes, Address};
|
use util::{U256, Bytes, Address};
|
||||||
@ -47,6 +49,7 @@ EVM implementation for Parity.
|
|||||||
Copyright 2016, 2017 Parity Technologies (UK) Ltd
|
Copyright 2016, 2017 Parity Technologies (UK) Ltd
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
|
parity-evm state-test <file> [--json --only NAME --chain CHAIN]
|
||||||
parity-evm stats [options]
|
parity-evm stats [options]
|
||||||
parity-evm [options]
|
parity-evm [options]
|
||||||
parity-evm [-h | --help]
|
parity-evm [-h | --help]
|
||||||
@ -59,6 +62,10 @@ Transaction options:
|
|||||||
--gas GAS Supplied gas as hex (without 0x).
|
--gas GAS Supplied gas as hex (without 0x).
|
||||||
--gas-price WEI Supplied gas price as hex (without 0x).
|
--gas-price WEI Supplied gas price as hex (without 0x).
|
||||||
|
|
||||||
|
State test options:
|
||||||
|
--only NAME Runs only a single test matching the name.
|
||||||
|
--chain CHAIN Run only tests from specific chain.
|
||||||
|
|
||||||
General options:
|
General options:
|
||||||
--json Display verbose results in JSON.
|
--json Display verbose results in JSON.
|
||||||
--chain CHAIN Chain spec file path.
|
--chain CHAIN Chain spec file path.
|
||||||
@ -71,20 +78,67 @@ fn main() {
|
|||||||
|
|
||||||
let args: Args = Docopt::new(USAGE).and_then(|d| d.deserialize()).unwrap_or_else(|e| e.exit());
|
let args: Args = Docopt::new(USAGE).and_then(|d| d.deserialize()).unwrap_or_else(|e| e.exit());
|
||||||
|
|
||||||
if args.flag_json {
|
if args.cmd_state_test {
|
||||||
run(args, display::json::Informant::default())
|
run_state_test(args)
|
||||||
|
} else if args.flag_json {
|
||||||
|
run_call(args, display::json::Informant::default())
|
||||||
} else {
|
} else {
|
||||||
run(args, display::simple::Informant::default())
|
run_call(args, display::simple::Informant::default())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run<T: Informant>(args: Args, mut informant: T) {
|
fn run_state_test(args: Args) {
|
||||||
|
use ethjson::state::test::Test;
|
||||||
|
|
||||||
|
let file = args.arg_file.expect("FILE is required");
|
||||||
|
let mut file = match fs::File::open(&file) {
|
||||||
|
Err(err) => die(format!("Unable to open: {:?}: {}", file, err)),
|
||||||
|
Ok(file) => file,
|
||||||
|
};
|
||||||
|
let state_test = match Test::load(&mut file) {
|
||||||
|
Err(err) => die(format!("Unable to load the test file: {}", err)),
|
||||||
|
Ok(test) => test,
|
||||||
|
};
|
||||||
|
let only_test = args.flag_only.map(|s| s.to_lowercase());
|
||||||
|
let only_chain = args.flag_chain.map(|s| s.to_lowercase());
|
||||||
|
|
||||||
|
for (name, test) in state_test {
|
||||||
|
if let Some(false) = only_test.as_ref().map(|only_test| &name.to_lowercase() == only_test) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let multitransaction = test.transaction;
|
||||||
|
let env_info = test.env.into();
|
||||||
|
let pre = test.pre_state.into();
|
||||||
|
|
||||||
|
for (spec, states) in test.post_states {
|
||||||
|
if let Some(false) = only_chain.as_ref().map(|only_chain| &format!("{:?}", spec).to_lowercase() == only_chain) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (idx, state) in states.into_iter().enumerate() {
|
||||||
|
let post_root = state.hash.into();
|
||||||
|
let transaction = multitransaction.select(&state.indexes).into();
|
||||||
|
|
||||||
|
if args.flag_json {
|
||||||
|
let i = display::json::Informant::default();
|
||||||
|
info::run_transaction(&name, idx, &spec, &pre, post_root, &env_info, transaction, i)
|
||||||
|
} else {
|
||||||
|
let i = display::simple::Informant::default();
|
||||||
|
info::run_transaction(&name, idx, &spec, &pre, post_root, &env_info, transaction, i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_call<T: Informant>(args: Args, mut informant: T) {
|
||||||
let from = arg(args.from(), "--from");
|
let from = arg(args.from(), "--from");
|
||||||
let to = arg(args.to(), "--to");
|
let to = arg(args.to(), "--to");
|
||||||
let code = arg(args.code(), "--code");
|
let code = arg(args.code(), "--code");
|
||||||
let spec = arg(args.spec(), "--chain");
|
let spec = arg(args.spec(), "--chain");
|
||||||
let gas = arg(args.gas(), "--gas");
|
let gas = arg(args.gas(), "--gas");
|
||||||
let gas_price = arg(args.gas(), "--gas-price");
|
let gas_price = arg(args.gas_price(), "--gas-price");
|
||||||
let data = arg(args.data(), "--input");
|
let data = arg(args.data(), "--input");
|
||||||
|
|
||||||
if code.is_none() && to == Address::default() {
|
if code.is_none() && to == Address::default() {
|
||||||
@ -103,13 +157,18 @@ fn run<T: Informant>(args: Args, mut informant: T) {
|
|||||||
params.data = data;
|
params.data = data;
|
||||||
|
|
||||||
informant.set_gas(gas);
|
informant.set_gas(gas);
|
||||||
let result = info::run(&mut informant, spec, params);
|
let result = info::run(&spec, gas, None, |mut client| {
|
||||||
informant.finish(result);
|
client.call(params, &mut informant)
|
||||||
|
});
|
||||||
|
T::finish(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
struct Args {
|
struct Args {
|
||||||
cmd_stats: bool,
|
cmd_stats: bool,
|
||||||
|
cmd_state_test: bool,
|
||||||
|
arg_file: Option<PathBuf>,
|
||||||
|
flag_only: Option<String>,
|
||||||
flag_from: Option<String>,
|
flag_from: Option<String>,
|
||||||
flag_to: Option<String>,
|
flag_to: Option<String>,
|
||||||
flag_code: Option<String>,
|
flag_code: Option<String>,
|
||||||
@ -221,4 +280,22 @@ mod tests {
|
|||||||
assert_eq!(args.data(), Ok(Some(vec![06])));
|
assert_eq!(args.data(), Ok(Some(vec![06])));
|
||||||
assert_eq!(args.flag_chain, Some("./testfile".to_owned()));
|
assert_eq!(args.flag_chain, Some("./testfile".to_owned()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_parse_state_test_command() {
|
||||||
|
let args = run(&[
|
||||||
|
"parity-evm",
|
||||||
|
"state-test",
|
||||||
|
"./file.json",
|
||||||
|
"--chain", "homestead",
|
||||||
|
"--only=add11",
|
||||||
|
"--json",
|
||||||
|
]);
|
||||||
|
|
||||||
|
assert_eq!(args.cmd_state_test, true);
|
||||||
|
assert!(args.arg_file.is_some());
|
||||||
|
assert_eq!(args.flag_json, true);
|
||||||
|
assert_eq!(args.flag_chain, Some("homestead".to_owned()));
|
||||||
|
assert_eq!(args.flag_only, Some("add11".to_owned()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -104,7 +104,10 @@ pub enum ForkSpec {
|
|||||||
EIP158,
|
EIP158,
|
||||||
Frontier,
|
Frontier,
|
||||||
Homestead,
|
Homestead,
|
||||||
|
// TODO [ToDr] Deprecated
|
||||||
Metropolis,
|
Metropolis,
|
||||||
|
Byzantium,
|
||||||
|
Constantinople,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// State test indexes deserialization.
|
/// State test indexes deserialization.
|
||||||
|
Loading…
Reference in New Issue
Block a user