Revamping parity-evmbin (#5696)
* Revamping evmbin to support spec files, json format and subcalls. * Fix formatting of usage.
This commit is contained in:
parent
bbbdd02a00
commit
543965411e
117
ethcore/src/client/evm_test_client.rs
Normal file
117
ethcore/src/client/evm_test_client.rs
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
//! Simple Client used for EVM tests.
|
||||||
|
|
||||||
|
use std::fmt;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use util::{self, U256, journaldb, trie};
|
||||||
|
use util::kvdb::{self, KeyValueDB};
|
||||||
|
use {state, state_db, client, executive, trace, db, spec};
|
||||||
|
use factory::Factories;
|
||||||
|
use evm::{self, VMType};
|
||||||
|
use action_params::ActionParams;
|
||||||
|
|
||||||
|
/// EVM test Error.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum EvmTestError {
|
||||||
|
/// Trie integrity error.
|
||||||
|
Trie(util::TrieError),
|
||||||
|
/// EVM error.
|
||||||
|
Evm(evm::Error),
|
||||||
|
/// Initialization error.
|
||||||
|
Initialization(::error::Error),
|
||||||
|
/// Low-level database error.
|
||||||
|
Database(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for EvmTestError {
|
||||||
|
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
use self::EvmTestError::*;
|
||||||
|
|
||||||
|
match *self {
|
||||||
|
Trie(ref err) => write!(fmt, "Trie: {}", err),
|
||||||
|
Evm(ref err) => write!(fmt, "EVM: {}", err),
|
||||||
|
Initialization(ref err) => write!(fmt, "Initialization: {}", err),
|
||||||
|
Database(ref err) => write!(fmt, "DB: {}", err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Simplified, single-block EVM test client.
|
||||||
|
pub struct EvmTestClient {
|
||||||
|
state_db: state_db::StateDB,
|
||||||
|
factories: Factories,
|
||||||
|
spec: spec::Spec,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EvmTestClient {
|
||||||
|
/// Creates new EVM test client with in-memory DB initialized with genesis of given Spec.
|
||||||
|
pub fn new(spec: spec::Spec) -> Result<Self, EvmTestError> {
|
||||||
|
let factories = Factories {
|
||||||
|
vm: evm::Factory::new(VMType::Interpreter, 5 * 1024),
|
||||||
|
trie: trie::TrieFactory::new(trie::TrieSpec::Secure),
|
||||||
|
accountdb: Default::default(),
|
||||||
|
};
|
||||||
|
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).map_err(EvmTestError::Initialization)?;
|
||||||
|
// Write DB
|
||||||
|
{
|
||||||
|
let mut batch = kvdb::DBTransaction::new();
|
||||||
|
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)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(EvmTestClient {
|
||||||
|
state_db,
|
||||||
|
factories,
|
||||||
|
spec,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Call given contract.
|
||||||
|
pub fn call<T: trace::VMTracer>(&mut self, params: ActionParams, vm_tracer: &mut T)
|
||||||
|
-> Result<(U256, Vec<u8>), EvmTestError>
|
||||||
|
{
|
||||||
|
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(), self.factories.clone())
|
||||||
|
.map_err(EvmTestError::Trie)?;
|
||||||
|
let info = client::EnvInfo {
|
||||||
|
number: genesis.number(),
|
||||||
|
author: *genesis.author(),
|
||||||
|
timestamp: genesis.timestamp(),
|
||||||
|
difficulty: *genesis.difficulty(),
|
||||||
|
last_hashes: Arc::new([util::H256::default(); 256].to_vec()),
|
||||||
|
gas_used: 0.into(),
|
||||||
|
gas_limit: *genesis.gas_limit(),
|
||||||
|
};
|
||||||
|
let mut substate = state::Substate::new();
|
||||||
|
let mut tracer = trace::NoopTracer;
|
||||||
|
let mut output = vec![];
|
||||||
|
let mut executive = executive::Executive::new(&mut state, &info, &*self.spec.engine, &self.factories.vm);
|
||||||
|
let gas_left = executive.call(
|
||||||
|
params,
|
||||||
|
&mut substate,
|
||||||
|
util::BytesRef::Flexible(&mut output),
|
||||||
|
&mut tracer,
|
||||||
|
vm_tracer,
|
||||||
|
).map_err(EvmTestError::Evm)?;
|
||||||
|
|
||||||
|
Ok((gas_left, output))
|
||||||
|
}
|
||||||
|
}
|
@ -19,6 +19,7 @@
|
|||||||
mod ancient_import;
|
mod ancient_import;
|
||||||
mod config;
|
mod config;
|
||||||
mod error;
|
mod error;
|
||||||
|
mod evm_test_client;
|
||||||
mod test_client;
|
mod test_client;
|
||||||
mod trace;
|
mod trace;
|
||||||
mod client;
|
mod client;
|
||||||
@ -26,6 +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::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};
|
||||||
|
@ -131,7 +131,7 @@ pub trait Ext {
|
|||||||
fn inc_sstore_clears(&mut self);
|
fn inc_sstore_clears(&mut self);
|
||||||
|
|
||||||
/// Prepare to trace an operation. Passthrough for the VM trace.
|
/// Prepare to trace an operation. Passthrough for the VM trace.
|
||||||
fn trace_prepare_execute(&mut self, _pc: usize, _instruction: u8, _gas_cost: &U256) -> bool { false }
|
fn trace_prepare_execute(&mut self, _pc: usize, _instruction: u8, _stack_pop: usize, _gas_cost: &U256) -> bool { false }
|
||||||
|
|
||||||
/// Trace the finalised execution of a single instruction.
|
/// Trace the finalised execution of a single instruction.
|
||||||
fn trace_executed(&mut self, _gas_used: U256, _stack_push: &[U256], _mem_diff: Option<(usize, &[u8])>, _store_diff: Option<(U256, U256)>) {}
|
fn trace_executed(&mut self, _gas_used: U256, _stack_push: &[U256], _mem_diff: Option<(usize, &[u8])>, _store_diff: Option<(U256, U256)>) {}
|
||||||
|
@ -129,7 +129,7 @@ impl<Cost: CostType> evm::Evm for Interpreter<Cost> {
|
|||||||
// Calculate gas cost
|
// Calculate gas cost
|
||||||
let requirements = gasometer.requirements(ext, instruction, info, &stack, self.mem.size())?;
|
let requirements = gasometer.requirements(ext, instruction, info, &stack, self.mem.size())?;
|
||||||
// TODO: make compile-time removable if too much of a performance hit.
|
// TODO: make compile-time removable if too much of a performance hit.
|
||||||
let trace_executed = ext.trace_prepare_execute(reader.position - 1, instruction, &requirements.gas_cost.as_u256());
|
let trace_executed = ext.trace_prepare_execute(reader.position - 1, instruction, info.args, &requirements.gas_cost.as_u256());
|
||||||
|
|
||||||
gasometer.verify_gas(&requirements.gas_cost)?;
|
gasometer.verify_gas(&requirements.gas_cost)?;
|
||||||
self.mem.expand(requirements.memory_required_size);
|
self.mem.expand(requirements.memory_required_size);
|
||||||
|
@ -333,8 +333,8 @@ impl<'a, T: 'a, V: 'a, B: 'a> Ext for Externalities<'a, T, V, B>
|
|||||||
self.substate.sstore_clears_count = self.substate.sstore_clears_count + U256::one();
|
self.substate.sstore_clears_count = self.substate.sstore_clears_count + U256::one();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn trace_prepare_execute(&mut self, pc: usize, instruction: u8, gas_cost: &U256) -> bool {
|
fn trace_prepare_execute(&mut self, pc: usize, instruction: u8, stack_pop: usize, gas_cost: &U256) -> bool {
|
||||||
self.vm_tracer.trace_prepare_execute(pc, instruction, gas_cost)
|
self.vm_tracer.trace_prepare_execute(pc, instruction, stack_pop, gas_cost)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn trace_executed(&mut self, gas_used: U256, stack_push: &[U256], mem_diff: Option<(usize, &[u8])>, store_diff: Option<(U256, U256)>) {
|
fn trace_executed(&mut self, gas_used: U256, stack_push: &[U256], mem_diff: Option<(usize, &[u8])>, store_diff: Option<(U256, U256)>) {
|
||||||
|
@ -192,7 +192,7 @@ impl ExecutiveVMTracer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl VMTracer for ExecutiveVMTracer {
|
impl VMTracer for ExecutiveVMTracer {
|
||||||
fn trace_prepare_execute(&mut self, pc: usize, instruction: u8, gas_cost: &U256) -> bool {
|
fn trace_prepare_execute(&mut self, pc: usize, instruction: u8, _stack_pop: usize, gas_cost: &U256) -> bool {
|
||||||
self.data.operations.push(VMOperation {
|
self.data.operations.push(VMOperation {
|
||||||
pc: pc,
|
pc: pc,
|
||||||
instruction: instruction,
|
instruction: instruction,
|
||||||
|
@ -89,7 +89,7 @@ pub trait Tracer: Send {
|
|||||||
pub trait VMTracer: Send {
|
pub trait VMTracer: Send {
|
||||||
/// Trace the preparation to execute a single instruction.
|
/// Trace the preparation to execute a single instruction.
|
||||||
/// @returns true if `trace_executed` should be called.
|
/// @returns true if `trace_executed` should be called.
|
||||||
fn trace_prepare_execute(&mut self, _pc: usize, _instruction: u8, _gas_cost: &U256) -> bool { false }
|
fn trace_prepare_execute(&mut self, _pc: usize, _instruction: u8, _stack_pop: usize, _gas_cost: &U256) -> bool { false }
|
||||||
|
|
||||||
/// Trace the finalised execution of a single instruction.
|
/// Trace the finalised execution of a single instruction.
|
||||||
fn trace_executed(&mut self, _gas_used: U256, _stack_push: &[U256], _mem_diff: Option<(usize, &[u8])>, _store_diff: Option<(U256, U256)>) {}
|
fn trace_executed(&mut self, _gas_used: U256, _stack_push: &[U256], _mem_diff: Option<(usize, &[u8])>, _store_diff: Option<(U256, U256)>) {}
|
||||||
@ -97,7 +97,7 @@ pub trait VMTracer: Send {
|
|||||||
/// Spawn subtracer which will be used to trace deeper levels of execution.
|
/// Spawn subtracer which will be used to trace deeper levels of execution.
|
||||||
fn prepare_subtrace(&self, code: &[u8]) -> Self where Self: Sized;
|
fn prepare_subtrace(&self, code: &[u8]) -> Self where Self: Sized;
|
||||||
|
|
||||||
/// Spawn subtracer which will be used to trace deeper levels of execution.
|
/// Finalize subtracer.
|
||||||
fn done_subtrace(&mut self, sub: Self) where Self: Sized;
|
fn done_subtrace(&mut self, sub: Self) where Self: Sized;
|
||||||
|
|
||||||
/// Consumes self and returns the VM trace.
|
/// Consumes self and returns the VM trace.
|
||||||
|
@ -72,7 +72,7 @@ pub struct NoopVMTracer;
|
|||||||
|
|
||||||
impl VMTracer for NoopVMTracer {
|
impl VMTracer for NoopVMTracer {
|
||||||
/// Trace the preparation to execute a single instruction.
|
/// Trace the preparation to execute a single instruction.
|
||||||
fn trace_prepare_execute(&mut self, _pc: usize, _instruction: u8, _gas_cost: &U256) -> bool { false }
|
fn trace_prepare_execute(&mut self, _pc: usize, _instruction: u8, _stack_pop: usize, _gas_cost: &U256) -> bool { false }
|
||||||
|
|
||||||
/// Trace the finalised execution of a single instruction.
|
/// Trace the finalised execution of a single instruction.
|
||||||
fn trace_executed(&mut self, _gas_used: U256, _stack_push: &[U256], _mem_diff: Option<(usize, &[u8])>, _store_diff: Option<(U256, U256)>) {}
|
fn trace_executed(&mut self, _gas_used: U256, _stack_push: &[U256], _mem_diff: Option<(usize, &[u8])>, _store_diff: Option<(U256, U256)>) {}
|
||||||
|
116
evmbin/src/display/json.rs
Normal file
116
evmbin/src/display/json.rs
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
//! JSON VM output.
|
||||||
|
|
||||||
|
use ethcore::trace;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use util::{U256, H256, ToPretty};
|
||||||
|
|
||||||
|
use display;
|
||||||
|
use vm;
|
||||||
|
|
||||||
|
/// JSON formatting informant.
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct Informant {
|
||||||
|
depth: usize,
|
||||||
|
pc: usize,
|
||||||
|
instruction: u8,
|
||||||
|
gas_cost: U256,
|
||||||
|
stack: Vec<U256>,
|
||||||
|
memory: Vec<u8>,
|
||||||
|
storage: HashMap<H256, H256>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Informant {
|
||||||
|
fn memory(&self) -> String {
|
||||||
|
format!("\"0x{}\"", self.memory.to_hex())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn stack(&self) -> String {
|
||||||
|
let items = self.stack.iter().map(display::u256_as_str).collect::<Vec<_>>();
|
||||||
|
format!("[{}]", items.join(","))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn storage(&self) -> String {
|
||||||
|
let vals = self.storage.iter()
|
||||||
|
.map(|(k, v)| format!("\"0x{:?}\": \"0x{:?}\"", k, v))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
format!("{{{}}}", vals.join(","))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl vm::Informant for Informant {
|
||||||
|
fn finish(&mut self, result: Result<vm::Success, vm::Failure>) {
|
||||||
|
match result {
|
||||||
|
Ok(success) => println!(
|
||||||
|
"{{\"output\":\"0x{output}\",\"gasUsed\":\"{gas:x}\",\"time\":\"{time}\"}}",
|
||||||
|
output = success.output.to_hex(),
|
||||||
|
gas = success.gas_used,
|
||||||
|
time = display::format_time(&success.time),
|
||||||
|
),
|
||||||
|
Err(failure) => println!(
|
||||||
|
"{{\"error\":\"{error}\",\"time\":\"{time}\"}}",
|
||||||
|
error = failure.error,
|
||||||
|
time = display::format_time(&failure.time),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl trace::VMTracer for Informant {
|
||||||
|
fn trace_prepare_execute(&mut self, pc: usize, instruction: u8, stack_pop: usize, gas_cost: &U256) -> bool {
|
||||||
|
self.pc = pc;
|
||||||
|
self.instruction = instruction;
|
||||||
|
self.gas_cost = *gas_cost;
|
||||||
|
let len = self.stack.len();
|
||||||
|
self.stack.truncate(len - stack_pop);
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn trace_executed(&mut self, gas_used: U256, stack_push: &[U256], mem_diff: Option<(usize, &[u8])>, store_diff: Option<(U256, U256)>) {
|
||||||
|
self.stack.extend_from_slice(stack_push);
|
||||||
|
|
||||||
|
if let Some((pos, data)) = mem_diff {
|
||||||
|
self.memory[pos..pos + data.len()].copy_from_slice(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some((pos, val)) = store_diff {
|
||||||
|
self.storage.insert(pos.into(), val.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
println!(
|
||||||
|
"{{\"pc\":{pc},\"op\":{op},\"gas\":{gas},\"gasCost\":{gas_cost},\"memory\":{memory},\"stack\":{stack},\"storage\":{storage},\"depth\":{depth}}}",
|
||||||
|
pc = self.pc,
|
||||||
|
op = self.instruction,
|
||||||
|
gas = display::u256_as_str(&gas_used),
|
||||||
|
gas_cost = display::u256_as_str(&self.gas_cost),
|
||||||
|
memory = self.memory(),
|
||||||
|
stack = self.stack(),
|
||||||
|
storage = self.storage(),
|
||||||
|
depth = self.depth,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn prepare_subtrace(&self, _code: &[u8]) -> Self where Self: Sized {
|
||||||
|
let mut vm = Informant::default();
|
||||||
|
vm.depth = self.depth + 1;
|
||||||
|
vm
|
||||||
|
}
|
||||||
|
|
||||||
|
fn done_subtrace(&mut self, _sub: Self) where Self: Sized {}
|
||||||
|
fn drain(self) -> Option<trace::VMTrace> { None }
|
||||||
|
}
|
38
evmbin/src/display/mod.rs
Normal file
38
evmbin/src/display/mod.rs
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
//! VM Output display utils.
|
||||||
|
|
||||||
|
use std::time::Duration;
|
||||||
|
use util::U256;
|
||||||
|
|
||||||
|
pub mod json;
|
||||||
|
pub mod simple;
|
||||||
|
|
||||||
|
/// Formats duration into human readable format.
|
||||||
|
pub fn format_time(time: &Duration) -> String {
|
||||||
|
format!("{}.{:.9}s", time.as_secs(), time.subsec_nanos())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Converts U256 into string.
|
||||||
|
/// TODO Overcomes: https://github.com/paritytech/bigint/issues/13
|
||||||
|
pub fn u256_as_str(v: &U256) -> String {
|
||||||
|
if v.is_zero() {
|
||||||
|
"\"0x0\"".into()
|
||||||
|
} else {
|
||||||
|
format!("\"{:x}\"", v)
|
||||||
|
}
|
||||||
|
}
|
49
evmbin/src/display/simple.rs
Normal file
49
evmbin/src/display/simple.rs
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
//! Simple VM output.
|
||||||
|
|
||||||
|
use ethcore::trace;
|
||||||
|
use util::ToPretty;
|
||||||
|
|
||||||
|
use display;
|
||||||
|
use vm;
|
||||||
|
|
||||||
|
/// Simple formatting informant.
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct Informant;
|
||||||
|
|
||||||
|
impl vm::Informant for Informant {
|
||||||
|
fn finish(&mut self, result: Result<vm::Success, vm::Failure>) {
|
||||||
|
match result {
|
||||||
|
Ok(success) => {
|
||||||
|
println!("Output: 0x{}", success.output.to_hex());
|
||||||
|
println!("Gas used: {:x}", success.gas_used);
|
||||||
|
println!("Time: {}", display::format_time(&success.time));
|
||||||
|
},
|
||||||
|
Err(failure) => {
|
||||||
|
println!("Error: {}", failure.error);
|
||||||
|
println!("Time: {}", display::format_time(&failure.time));
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl trace::VMTracer for Informant {
|
||||||
|
fn prepare_subtrace(&self, _code: &[u8]) -> Self where Self: Sized { Default::default() }
|
||||||
|
fn done_subtrace(&mut self, _sub: Self) where Self: Sized {}
|
||||||
|
fn drain(self) -> Option<trace::VMTrace> { None }
|
||||||
|
}
|
@ -1,123 +0,0 @@
|
|||||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
|
||||||
// This file is part of Parity.
|
|
||||||
|
|
||||||
// Parity is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
|
|
||||||
// Parity is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
//! Externalities implementation.
|
|
||||||
|
|
||||||
use std::sync::Arc;
|
|
||||||
use std::collections::HashMap;
|
|
||||||
use util::{U256, H256, Address, Bytes, trie};
|
|
||||||
use ethcore::client::EnvInfo;
|
|
||||||
use ethcore::evm::{self, Ext, ContractCreateResult, MessageCallResult, Schedule, CallType, CreateContractAddress};
|
|
||||||
|
|
||||||
pub struct FakeExt {
|
|
||||||
schedule: Schedule,
|
|
||||||
store: HashMap<H256, H256>,
|
|
||||||
depth: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for FakeExt {
|
|
||||||
fn default() -> Self {
|
|
||||||
FakeExt {
|
|
||||||
schedule: Schedule::new_post_eip150(usize::max_value(), true, true, true, true),
|
|
||||||
store: HashMap::new(),
|
|
||||||
depth: 1,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Ext for FakeExt {
|
|
||||||
fn storage_at(&self, key: &H256) -> trie::Result<H256> {
|
|
||||||
Ok(self.store.get(key).unwrap_or(&H256::new()).clone())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_storage(&mut self, key: H256, value: H256) -> trie::Result<()> {
|
|
||||||
self.store.insert(key, value);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn exists(&self, _address: &Address) -> trie::Result<bool> {
|
|
||||||
unimplemented!();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn exists_and_not_null(&self, _address: &Address) -> trie::Result<bool> {
|
|
||||||
unimplemented!();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn origin_balance(&self) -> trie::Result<U256> {
|
|
||||||
unimplemented!();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn balance(&self, _address: &Address) -> trie::Result<U256> {
|
|
||||||
unimplemented!();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn blockhash(&self, _number: &U256) -> H256 {
|
|
||||||
unimplemented!();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn create(&mut self, _gas: &U256, _value: &U256, _code: &[u8], _address: CreateContractAddress) -> ContractCreateResult {
|
|
||||||
unimplemented!();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn call(&mut self,
|
|
||||||
_gas: &U256,
|
|
||||||
_sender_address: &Address,
|
|
||||||
_receive_address: &Address,
|
|
||||||
_value: Option<U256>,
|
|
||||||
_data: &[u8],
|
|
||||||
_code_address: &Address,
|
|
||||||
_output: &mut [u8],
|
|
||||||
_call_type: CallType) -> MessageCallResult {
|
|
||||||
unimplemented!();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn extcode(&self, _address: &Address) -> trie::Result<Arc<Bytes>> {
|
|
||||||
unimplemented!();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn extcodesize(&self, _address: &Address) -> trie::Result<usize> {
|
|
||||||
unimplemented!();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn log(&mut self, _topics: Vec<H256>, _data: &[u8]) {
|
|
||||||
unimplemented!();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn ret(self, gas: &U256, _data: &[u8]) -> evm::Result<U256> {
|
|
||||||
Ok(*gas)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn suicide(&mut self, _refund_address: &Address) -> trie::Result<()> {
|
|
||||||
unimplemented!();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn schedule(&self) -> &Schedule {
|
|
||||||
&self.schedule
|
|
||||||
}
|
|
||||||
|
|
||||||
fn env_info(&self) -> &EnvInfo {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn depth(&self) -> usize {
|
|
||||||
self.depth
|
|
||||||
}
|
|
||||||
|
|
||||||
fn inc_sstore_clears(&mut self) {
|
|
||||||
unimplemented!();
|
|
||||||
// self.sstore_clears += 1;
|
|
||||||
}
|
|
||||||
}
|
|
@ -23,31 +23,36 @@ extern crate rustc_serialize;
|
|||||||
extern crate docopt;
|
extern crate docopt;
|
||||||
extern crate ethcore_util as util;
|
extern crate ethcore_util as util;
|
||||||
|
|
||||||
mod ext;
|
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::time::{Instant, Duration};
|
use std::{fmt, fs};
|
||||||
use std::fmt;
|
|
||||||
use std::str::FromStr;
|
|
||||||
use docopt::Docopt;
|
use docopt::Docopt;
|
||||||
use util::{U256, FromHex, Bytes};
|
use util::{U256, FromHex, Bytes, Address};
|
||||||
use ethcore::evm::{self, Factory, VMType, Finalize};
|
use ethcore::spec;
|
||||||
use ethcore::action_params::ActionParams;
|
use ethcore::action_params::ActionParams;
|
||||||
|
|
||||||
|
mod vm;
|
||||||
|
mod display;
|
||||||
|
|
||||||
|
use vm::Informant;
|
||||||
|
|
||||||
const USAGE: &'static str = r#"
|
const USAGE: &'static str = r#"
|
||||||
EVM implementation for Parity.
|
EVM implementation for Parity.
|
||||||
Copyright 2016, 2017 Parity Technologies (UK) Ltd
|
Copyright 2016, 2017 Parity Technologies (UK) Ltd
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
evmbin stats [options]
|
evmbin stats [options]
|
||||||
|
evmbin [options]
|
||||||
evmbin [-h | --help]
|
evmbin [-h | --help]
|
||||||
|
|
||||||
Transaction options:
|
Transaction options:
|
||||||
--code CODE Contract code as hex (without 0x)
|
--code CODE Contract code as hex (without 0x).
|
||||||
--input DATA Input data as hex (without 0x)
|
--from ADDRESS Sender address (without 0x).
|
||||||
--gas GAS Supplied gas as hex (without 0x)
|
--input DATA Input data as hex (without 0x).
|
||||||
|
--gas GAS Supplied gas as hex (without 0x).
|
||||||
|
|
||||||
General options:
|
General options:
|
||||||
|
--json Display verbose results in JSON.
|
||||||
|
--chain CHAIN Chain spec file path.
|
||||||
-h, --help Display this message and exit.
|
-h, --help Display this message and exit.
|
||||||
"#;
|
"#;
|
||||||
|
|
||||||
@ -55,107 +60,93 @@ General options:
|
|||||||
fn main() {
|
fn main() {
|
||||||
let args: Args = Docopt::new(USAGE).and_then(|d| d.decode()).unwrap_or_else(|e| e.exit());
|
let args: Args = Docopt::new(USAGE).and_then(|d| d.decode()).unwrap_or_else(|e| e.exit());
|
||||||
|
|
||||||
|
if args.flag_json {
|
||||||
|
run(args, display::json::Informant::default())
|
||||||
|
} else {
|
||||||
|
run(args, display::simple::Informant::default())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run<T: Informant>(args: Args, mut informant: T) {
|
||||||
|
let from = arg(args.from(), "--from");
|
||||||
|
let code = arg(args.code(), "--code");
|
||||||
|
let spec = arg(args.spec(), "--chain");
|
||||||
|
let gas = arg(args.gas(), "--gas");
|
||||||
|
let data = arg(args.data(), "--input");
|
||||||
|
|
||||||
let mut params = ActionParams::default();
|
let mut params = ActionParams::default();
|
||||||
params.gas = args.gas();
|
params.sender = from;
|
||||||
params.code = Some(Arc::new(args.code()));
|
params.origin = from;
|
||||||
params.data = args.data();
|
params.gas = gas;
|
||||||
|
params.code = Some(Arc::new(code));
|
||||||
|
params.data = data;
|
||||||
|
|
||||||
let result = run_vm(params);
|
let result = vm::run(&mut informant, spec, params);
|
||||||
match result {
|
informant.finish(result);
|
||||||
Ok(success) => println!("{}", success),
|
|
||||||
Err(failure) => println!("{}", failure),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Execute VM with given `ActionParams`
|
|
||||||
pub fn run_vm(params: ActionParams) -> Result<Success, Failure> {
|
|
||||||
let initial_gas = params.gas;
|
|
||||||
let factory = Factory::new(VMType::Interpreter, 1024);
|
|
||||||
let mut vm = factory.create(params.gas);
|
|
||||||
let mut ext = ext::FakeExt::default();
|
|
||||||
|
|
||||||
let start = Instant::now();
|
|
||||||
let res = vm.exec(params, &mut ext).finalize(ext);
|
|
||||||
let duration = start.elapsed();
|
|
||||||
|
|
||||||
match res {
|
|
||||||
Ok(res) => Ok(Success {
|
|
||||||
gas_used: initial_gas - res.gas_left,
|
|
||||||
// TODO [ToDr] get output from ext
|
|
||||||
output: Vec::new(),
|
|
||||||
time: duration,
|
|
||||||
}),
|
|
||||||
Err(e) => Err(Failure {
|
|
||||||
error: e,
|
|
||||||
time: duration,
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Execution finished correctly
|
|
||||||
pub struct Success {
|
|
||||||
/// Used gas
|
|
||||||
gas_used: U256,
|
|
||||||
/// Output as bytes
|
|
||||||
output: Vec<u8>,
|
|
||||||
/// Time Taken
|
|
||||||
time: Duration,
|
|
||||||
}
|
|
||||||
impl fmt::Display for Success {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
|
||||||
writeln!(f, "Gas used: {:?}", self.gas_used)?;
|
|
||||||
writeln!(f, "Output: {:?}", self.output)?;
|
|
||||||
writeln!(f, "Time: {}.{:.9}s", self.time.as_secs(), self.time.subsec_nanos())?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Execution failed
|
|
||||||
pub struct Failure {
|
|
||||||
/// Internal error
|
|
||||||
error: evm::Error,
|
|
||||||
/// Duration
|
|
||||||
time: Duration,
|
|
||||||
}
|
|
||||||
impl fmt::Display for Failure {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
|
||||||
writeln!(f, "Error: {:?}", self.error)?;
|
|
||||||
writeln!(f, "Time: {}.{:.9}s", self.time.as_secs(), self.time.subsec_nanos())?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, RustcDecodable)]
|
#[derive(Debug, RustcDecodable)]
|
||||||
struct Args {
|
struct Args {
|
||||||
cmd_stats: bool,
|
cmd_stats: bool,
|
||||||
|
flag_from: Option<String>,
|
||||||
flag_code: Option<String>,
|
flag_code: Option<String>,
|
||||||
flag_gas: Option<String>,
|
flag_gas: Option<String>,
|
||||||
flag_input: Option<String>,
|
flag_input: Option<String>,
|
||||||
|
flag_spec: Option<String>,
|
||||||
|
flag_json: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Args {
|
impl Args {
|
||||||
pub fn gas(&self) -> U256 {
|
pub fn gas(&self) -> Result<U256, String> {
|
||||||
self.flag_gas
|
match self.flag_gas {
|
||||||
.clone()
|
Some(ref gas) => gas.parse().map_err(to_string),
|
||||||
.and_then(|g| U256::from_str(&g).ok())
|
None => Ok(!U256::zero()),
|
||||||
.unwrap_or_else(|| !U256::zero())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn code(&self) -> Bytes {
|
|
||||||
self.flag_code
|
|
||||||
.clone()
|
|
||||||
.and_then(|c| c.from_hex().ok())
|
|
||||||
.unwrap_or_else(|| die("Code is required."))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn data(&self) -> Option<Bytes> {
|
|
||||||
self.flag_input
|
|
||||||
.clone()
|
|
||||||
.and_then(|d| d.from_hex().ok())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn die(msg: &'static str) -> ! {
|
pub fn from(&self) -> Result<Address, String> {
|
||||||
|
match self.flag_from {
|
||||||
|
Some(ref from) => from.parse().map_err(to_string),
|
||||||
|
None => Ok(Address::default()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn code(&self) -> Result<Bytes, String> {
|
||||||
|
match self.flag_code {
|
||||||
|
Some(ref code) => code.from_hex().map_err(to_string),
|
||||||
|
None => Err("Code is required!".into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn data(&self) -> Result<Option<Bytes>, String> {
|
||||||
|
match self.flag_input {
|
||||||
|
Some(ref input) => input.from_hex().map_err(to_string).map(Some),
|
||||||
|
None => Ok(None),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn spec(&self) -> Result<spec::Spec, String> {
|
||||||
|
Ok(match self.flag_spec {
|
||||||
|
Some(ref filename) => {
|
||||||
|
let file = fs::File::open(filename).map_err(|e| format!("{}", e))?;
|
||||||
|
spec::Spec::load(file)?
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
spec::Spec::new_instant()
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn arg<T>(v: Result<T, String>, param: &str) -> T {
|
||||||
|
v.unwrap_or_else(|e| die(format!("Invalid {}: {}", param, e)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_string<T: fmt::Display>(msg: T) -> String {
|
||||||
|
format!("{}", msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn die<T: fmt::Display>(msg: T) -> ! {
|
||||||
println!("{}", msg);
|
println!("{}", msg);
|
||||||
::std::process::exit(-1)
|
::std::process::exit(-1)
|
||||||
}
|
}
|
||||||
|
72
evmbin/src/vm.rs
Normal file
72
evmbin/src/vm.rs
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
//! VM runner.
|
||||||
|
|
||||||
|
use std::time::{Instant, Duration};
|
||||||
|
use util::U256;
|
||||||
|
use ethcore::{trace, spec};
|
||||||
|
use ethcore::client::{EvmTestClient, EvmTestError};
|
||||||
|
use ethcore::action_params::ActionParams;
|
||||||
|
|
||||||
|
/// VM execution informant
|
||||||
|
pub trait Informant: trace::VMTracer {
|
||||||
|
/// Display final result.
|
||||||
|
fn finish(&mut self, result: Result<Success, Failure>);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Execution finished correctly
|
||||||
|
pub struct Success {
|
||||||
|
/// Used gas
|
||||||
|
pub gas_used: U256,
|
||||||
|
/// Output as bytes
|
||||||
|
pub output: Vec<u8>,
|
||||||
|
/// Time Taken
|
||||||
|
pub time: Duration,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Execution failed
|
||||||
|
pub struct Failure {
|
||||||
|
/// Internal error
|
||||||
|
pub error: EvmTestError,
|
||||||
|
/// Duration
|
||||||
|
pub time: Duration,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Execute VM with given `ActionParams`
|
||||||
|
pub fn run<T: trace::VMTracer>(vm_tracer: &mut T, spec: spec::Spec, params: ActionParams) -> Result<Success, Failure> {
|
||||||
|
let mut test_client = EvmTestClient::new(spec).map_err(|error| Failure {
|
||||||
|
error,
|
||||||
|
time: Duration::from_secs(0)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let initial_gas = params.gas;
|
||||||
|
let start = Instant::now();
|
||||||
|
let result = test_client.call(params, vm_tracer);
|
||||||
|
let duration = start.elapsed();
|
||||||
|
|
||||||
|
match result {
|
||||||
|
Ok((gas_left, output)) => Ok(Success {
|
||||||
|
gas_used: initial_gas - gas_left,
|
||||||
|
output: output,
|
||||||
|
time: duration,
|
||||||
|
}),
|
||||||
|
Err(e) => Err(Failure {
|
||||||
|
error: e,
|
||||||
|
time: duration,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user