Revamping parity-evmbin (#5696)
* Revamping evmbin to support spec files, json format and subcalls. * Fix formatting of usage.
This commit is contained in:
committed by
Arkadiy Paronyan
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)>) {}
|
||||
|
||||
Reference in New Issue
Block a user