Revamping parity-evmbin (#5696)

* Revamping evmbin to support spec files, json format and subcalls.

* Fix formatting of usage.
This commit is contained in:
Tomasz Drwięga 2017-05-26 11:06:48 +02:00 committed by Arkadiy Paronyan
parent bbbdd02a00
commit 543965411e
14 changed files with 484 additions and 222 deletions

View 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))
}
}

View File

@ -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};

View File

@ -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)>) {}

View File

@ -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);

View File

@ -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)>) {

View File

@ -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,

View File

@ -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.

View File

@ -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
View 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
View 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)
}
}

View 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 }
}

View File

@ -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;
}
}

View File

@ -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 { pub fn from(&self) -> Result<Address, String> {
self.flag_code match self.flag_from {
.clone() Some(ref from) => from.parse().map_err(to_string),
.and_then(|c| c.from_hex().ok()) None => Ok(Address::default()),
.unwrap_or_else(|| die("Code is required.")) }
} }
pub fn data(&self) -> Option<Bytes> { pub fn code(&self) -> Result<Bytes, String> {
self.flag_input match self.flag_code {
.clone() Some(ref code) => code.from_hex().map_err(to_string),
.and_then(|d| d.from_hex().ok()) 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 die(msg: &'static str) -> ! { 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
View 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,
}),
}
}