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 config;
|
||||
mod error;
|
||||
mod evm_test_client;
|
||||
mod test_client;
|
||||
mod trace;
|
||||
mod client;
|
||||
@ -26,6 +27,7 @@ mod client;
|
||||
pub use self::client::*;
|
||||
pub use self::config::{Mode, ClientConfig, DatabaseCompactionProfile, BlockChainConfig, VMType};
|
||||
pub use self::error::Error;
|
||||
pub use self::evm_test_client::{EvmTestClient, EvmTestError};
|
||||
pub use self::test_client::{TestBlockChainClient, EachBlockWith};
|
||||
pub use self::chain_notify::ChainNotify;
|
||||
pub use self::traits::{BlockChainClient, MiningBlockChainClient, EngineClient};
|
||||
|
@ -131,7 +131,7 @@ pub trait Ext {
|
||||
fn inc_sstore_clears(&mut self);
|
||||
|
||||
/// 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.
|
||||
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
|
||||
let requirements = gasometer.requirements(ext, instruction, info, &stack, self.mem.size())?;
|
||||
// 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)?;
|
||||
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();
|
||||
}
|
||||
|
||||
fn trace_prepare_execute(&mut self, pc: usize, instruction: u8, gas_cost: &U256) -> bool {
|
||||
self.vm_tracer.trace_prepare_execute(pc, instruction, gas_cost)
|
||||
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, 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)>) {
|
||||
|
@ -192,7 +192,7 @@ impl 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 {
|
||||
pc: pc,
|
||||
instruction: instruction,
|
||||
|
@ -89,7 +89,7 @@ pub trait Tracer: Send {
|
||||
pub trait VMTracer: Send {
|
||||
/// Trace the preparation to execute a single instruction.
|
||||
/// @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.
|
||||
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.
|
||||
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;
|
||||
|
||||
/// Consumes self and returns the VM trace.
|
||||
|
@ -72,7 +72,7 @@ pub struct NoopVMTracer;
|
||||
|
||||
impl VMTracer for NoopVMTracer {
|
||||
/// 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.
|
||||
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 ethcore_util as util;
|
||||
|
||||
mod ext;
|
||||
|
||||
use std::sync::Arc;
|
||||
use std::time::{Instant, Duration};
|
||||
use std::fmt;
|
||||
use std::str::FromStr;
|
||||
use std::{fmt, fs};
|
||||
use docopt::Docopt;
|
||||
use util::{U256, FromHex, Bytes};
|
||||
use ethcore::evm::{self, Factory, VMType, Finalize};
|
||||
use util::{U256, FromHex, Bytes, Address};
|
||||
use ethcore::spec;
|
||||
use ethcore::action_params::ActionParams;
|
||||
|
||||
mod vm;
|
||||
mod display;
|
||||
|
||||
use vm::Informant;
|
||||
|
||||
const USAGE: &'static str = r#"
|
||||
EVM implementation for Parity.
|
||||
Copyright 2016, 2017 Parity Technologies (UK) Ltd
|
||||
|
||||
Usage:
|
||||
evmbin stats [options]
|
||||
evmbin [options]
|
||||
evmbin [-h | --help]
|
||||
|
||||
Transaction options:
|
||||
--code CODE Contract code as hex (without 0x)
|
||||
--input DATA Input data as hex (without 0x)
|
||||
--gas GAS Supplied gas as hex (without 0x)
|
||||
--code CODE Contract code as hex (without 0x).
|
||||
--from ADDRESS Sender address (without 0x).
|
||||
--input DATA Input data as hex (without 0x).
|
||||
--gas GAS Supplied gas as hex (without 0x).
|
||||
|
||||
General options:
|
||||
--json Display verbose results in JSON.
|
||||
--chain CHAIN Chain spec file path.
|
||||
-h, --help Display this message and exit.
|
||||
"#;
|
||||
|
||||
@ -55,107 +60,93 @@ General options:
|
||||
fn main() {
|
||||
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();
|
||||
params.gas = args.gas();
|
||||
params.code = Some(Arc::new(args.code()));
|
||||
params.data = args.data();
|
||||
params.sender = from;
|
||||
params.origin = from;
|
||||
params.gas = gas;
|
||||
params.code = Some(Arc::new(code));
|
||||
params.data = data;
|
||||
|
||||
let result = run_vm(params);
|
||||
match 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(())
|
||||
}
|
||||
let result = vm::run(&mut informant, spec, params);
|
||||
informant.finish(result);
|
||||
}
|
||||
|
||||
#[derive(Debug, RustcDecodable)]
|
||||
struct Args {
|
||||
cmd_stats: bool,
|
||||
flag_from: Option<String>,
|
||||
flag_code: Option<String>,
|
||||
flag_gas: Option<String>,
|
||||
flag_input: Option<String>,
|
||||
flag_spec: Option<String>,
|
||||
flag_json: bool,
|
||||
}
|
||||
|
||||
impl Args {
|
||||
pub fn gas(&self) -> U256 {
|
||||
self.flag_gas
|
||||
.clone()
|
||||
.and_then(|g| U256::from_str(&g).ok())
|
||||
.unwrap_or_else(|| !U256::zero())
|
||||
pub fn gas(&self) -> Result<U256, String> {
|
||||
match self.flag_gas {
|
||||
Some(ref gas) => gas.parse().map_err(to_string),
|
||||
None => Ok(!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 from(&self) -> Result<Address, String> {
|
||||
match self.flag_from {
|
||||
Some(ref from) => from.parse().map_err(to_string),
|
||||
None => Ok(Address::default()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn data(&self) -> Option<Bytes> {
|
||||
self.flag_input
|
||||
.clone()
|
||||
.and_then(|d| d.from_hex().ok())
|
||||
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 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);
|
||||
::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