Capture traces and write some tests.
This commit is contained in:
parent
4ac406da6b
commit
2d222a5920
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -940,6 +940,7 @@ dependencies = [
|
|||||||
"ethjson 0.1.0",
|
"ethjson 0.1.0",
|
||||||
"evm 0.1.0",
|
"evm 0.1.0",
|
||||||
"panic_hook 0.1.0",
|
"panic_hook 0.1.0",
|
||||||
|
"pretty_assertions 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde_derive 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde_derive 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
@ -1142,7 +1142,7 @@ impl Client {
|
|||||||
state_diff: bool,
|
state_diff: bool,
|
||||||
transaction: &SignedTransaction,
|
transaction: &SignedTransaction,
|
||||||
options: TransactOptions<T, V>,
|
options: TransactOptions<T, V>,
|
||||||
) -> Result<Executed, CallError> where
|
) -> Result<Executed<T::Output, V::Output>, CallError> where
|
||||||
T: trace::Tracer,
|
T: trace::Tracer,
|
||||||
V: trace::VMTracer,
|
V: trace::VMTracer,
|
||||||
{
|
{
|
||||||
|
@ -197,7 +197,7 @@ impl<'a> EvmTestClient<'a> {
|
|||||||
env_info: &client::EnvInfo,
|
env_info: &client::EnvInfo,
|
||||||
transaction: transaction::SignedTransaction,
|
transaction: transaction::SignedTransaction,
|
||||||
vm_tracer: T,
|
vm_tracer: T,
|
||||||
) -> TransactResult {
|
) -> TransactResult<T::Output> {
|
||||||
let initial_gas = transaction.gas;
|
let initial_gas = transaction.gas;
|
||||||
// Verify transaction
|
// Verify transaction
|
||||||
let is_ok = transaction.verify_basic(true, None, env_info.number >= self.spec.engine.params().eip86_transition);
|
let is_ok = transaction.verify_basic(true, None, env_info.number >= self.spec.engine.params().eip86_transition);
|
||||||
@ -219,6 +219,7 @@ impl<'a> EvmTestClient<'a> {
|
|||||||
state_root: *self.state.root(),
|
state_root: *self.state.root(),
|
||||||
gas_left: initial_gas - result.receipt.gas_used,
|
gas_left: initial_gas - result.receipt.gas_used,
|
||||||
output: result.output,
|
output: result.output,
|
||||||
|
vm_trace: result.vm_trace,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Err(error) => TransactResult::Err {
|
Err(error) => TransactResult::Err {
|
||||||
@ -230,7 +231,7 @@ impl<'a> EvmTestClient<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// A result of applying transaction to the state.
|
/// A result of applying transaction to the state.
|
||||||
pub enum TransactResult {
|
pub enum TransactResult<T> {
|
||||||
/// Successful execution
|
/// Successful execution
|
||||||
Ok {
|
Ok {
|
||||||
/// State root
|
/// State root
|
||||||
@ -239,6 +240,8 @@ pub enum TransactResult {
|
|||||||
gas_left: U256,
|
gas_left: U256,
|
||||||
/// Output
|
/// Output
|
||||||
output: Vec<u8>,
|
output: Vec<u8>,
|
||||||
|
/// VM Traces
|
||||||
|
vm_trace: Option<T>,
|
||||||
},
|
},
|
||||||
/// Transaction failed to run
|
/// Transaction failed to run
|
||||||
Err {
|
Err {
|
||||||
|
@ -29,7 +29,7 @@ use std::fmt;
|
|||||||
|
|
||||||
/// Transaction execution receipt.
|
/// Transaction execution receipt.
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
pub struct Executed {
|
pub struct Executed<T = FlatTrace, V = VMTrace> {
|
||||||
/// True if the outer call/create resulted in an exceptional exit.
|
/// True if the outer call/create resulted in an exceptional exit.
|
||||||
pub exception: Option<vm::Error>,
|
pub exception: Option<vm::Error>,
|
||||||
|
|
||||||
@ -63,9 +63,9 @@ pub struct Executed {
|
|||||||
/// Transaction output.
|
/// Transaction output.
|
||||||
pub output: Bytes,
|
pub output: Bytes,
|
||||||
/// The trace of this transaction.
|
/// The trace of this transaction.
|
||||||
pub trace: Vec<FlatTrace>,
|
pub trace: Vec<T>,
|
||||||
/// The VM trace of this transaction.
|
/// The VM trace of this transaction.
|
||||||
pub vm_trace: Option<VMTrace>,
|
pub vm_trace: Option<V>,
|
||||||
/// The state diff, if we traced it.
|
/// The state diff, if we traced it.
|
||||||
pub state_diff: Option<StateDiff>,
|
pub state_diff: Option<StateDiff>,
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,7 @@ use evm::{CallType, Factory, Finalize, FinalizationResult};
|
|||||||
use vm::{self, Ext, CreateContractAddress, ReturnData, CleanDustMode, ActionParams, ActionValue};
|
use vm::{self, Ext, CreateContractAddress, ReturnData, CleanDustMode, ActionParams, ActionValue};
|
||||||
use wasm;
|
use wasm;
|
||||||
use externalities::*;
|
use externalities::*;
|
||||||
use trace::{self, FlatTrace, VMTrace, Tracer, VMTracer};
|
use trace::{self, Tracer, VMTracer};
|
||||||
use transaction::{Action, SignedTransaction};
|
use transaction::{Action, SignedTransaction};
|
||||||
use crossbeam;
|
use crossbeam;
|
||||||
pub use executed::{Executed, ExecutionResult};
|
pub use executed::{Executed, ExecutionResult};
|
||||||
@ -214,7 +214,7 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> {
|
|||||||
|
|
||||||
/// This function should be used to execute transaction.
|
/// This function should be used to execute transaction.
|
||||||
pub fn transact<T, V>(&'a mut self, t: &SignedTransaction, options: TransactOptions<T, V>)
|
pub fn transact<T, V>(&'a mut self, t: &SignedTransaction, options: TransactOptions<T, V>)
|
||||||
-> Result<Executed, ExecutionError> where T: Tracer, V: VMTracer,
|
-> Result<Executed<T::Output, V::Output>, ExecutionError> where T: Tracer, V: VMTracer,
|
||||||
{
|
{
|
||||||
self.transact_with_tracer(t, options.check_nonce, options.output_from_init_contract, options.tracer, options.vm_tracer)
|
self.transact_with_tracer(t, options.check_nonce, options.output_from_init_contract, options.tracer, options.vm_tracer)
|
||||||
}
|
}
|
||||||
@ -223,7 +223,7 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> {
|
|||||||
/// This will ensure the caller has enough balance to execute the desired transaction.
|
/// This will ensure the caller has enough balance to execute the desired transaction.
|
||||||
/// Used for extra-block executions for things like consensus contracts and RPCs
|
/// Used for extra-block executions for things like consensus contracts and RPCs
|
||||||
pub fn transact_virtual<T, V>(&'a mut self, t: &SignedTransaction, options: TransactOptions<T, V>)
|
pub fn transact_virtual<T, V>(&'a mut self, t: &SignedTransaction, options: TransactOptions<T, V>)
|
||||||
-> Result<Executed, ExecutionError> where T: Tracer, V: VMTracer,
|
-> Result<Executed<T::Output, V::Output>, ExecutionError> where T: Tracer, V: VMTracer,
|
||||||
{
|
{
|
||||||
let sender = t.sender();
|
let sender = t.sender();
|
||||||
let balance = self.state.balance(&sender)?;
|
let balance = self.state.balance(&sender)?;
|
||||||
@ -244,7 +244,7 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> {
|
|||||||
output_from_create: bool,
|
output_from_create: bool,
|
||||||
mut tracer: T,
|
mut tracer: T,
|
||||||
mut vm_tracer: V
|
mut vm_tracer: V
|
||||||
) -> Result<Executed, ExecutionError> where T: Tracer, V: VMTracer {
|
) -> Result<Executed<T::Output, V::Output>, ExecutionError> where T: Tracer, V: VMTracer {
|
||||||
let sender = t.sender();
|
let sender = t.sender();
|
||||||
let nonce = self.state.nonce(&sender)?;
|
let nonce = self.state.nonce(&sender)?;
|
||||||
|
|
||||||
@ -587,15 +587,15 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Finalizes the transaction (does refunds and suicides).
|
/// Finalizes the transaction (does refunds and suicides).
|
||||||
fn finalize(
|
fn finalize<T, V>(
|
||||||
&mut self,
|
&mut self,
|
||||||
t: &SignedTransaction,
|
t: &SignedTransaction,
|
||||||
mut substate: Substate,
|
mut substate: Substate,
|
||||||
result: vm::Result<FinalizationResult>,
|
result: vm::Result<FinalizationResult>,
|
||||||
output: Bytes,
|
output: Bytes,
|
||||||
trace: Vec<FlatTrace>,
|
trace: Vec<T>,
|
||||||
vm_trace: Option<VMTrace>
|
vm_trace: Option<V>
|
||||||
) -> ExecutionResult {
|
) -> Result<Executed<T, V>, ExecutionError> {
|
||||||
let schedule = self.machine.schedule(self.info.number);
|
let schedule = self.machine.schedule(self.info.number);
|
||||||
|
|
||||||
// refunds from SSTORE nonzero -> zero
|
// refunds from SSTORE nonzero -> zero
|
||||||
|
@ -62,19 +62,19 @@ pub use self::backend::Backend;
|
|||||||
pub use self::substate::Substate;
|
pub use self::substate::Substate;
|
||||||
|
|
||||||
/// Used to return information about an `State::apply` operation.
|
/// Used to return information about an `State::apply` operation.
|
||||||
pub struct ApplyOutcome {
|
pub struct ApplyOutcome<T, V> {
|
||||||
/// The receipt for the applied transaction.
|
/// The receipt for the applied transaction.
|
||||||
pub receipt: Receipt,
|
pub receipt: Receipt,
|
||||||
/// The output of the applied transaction.
|
/// The output of the applied transaction.
|
||||||
pub output: Bytes,
|
pub output: Bytes,
|
||||||
/// The trace for the applied transaction, empty if tracing was not produced.
|
/// The trace for the applied transaction, empty if tracing was not produced.
|
||||||
pub trace: Vec<FlatTrace>,
|
pub trace: Vec<T>,
|
||||||
/// The VM trace for the applied transaction, None if tracing was not produced.
|
/// The VM trace for the applied transaction, None if tracing was not produced.
|
||||||
pub vm_trace: Option<VMTrace>
|
pub vm_trace: Option<V>
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Result type for the execution ("application") of a transaction.
|
/// Result type for the execution ("application") of a transaction.
|
||||||
pub type ApplyResult = Result<ApplyOutcome, Error>;
|
pub type ApplyResult<T, V> = Result<ApplyOutcome<T, V>, Error>;
|
||||||
|
|
||||||
/// Return type of proof validity check.
|
/// Return type of proof validity check.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
@ -668,7 +668,7 @@ impl<B: Backend> State<B> {
|
|||||||
|
|
||||||
/// Execute a given transaction, producing a receipt and an optional trace.
|
/// Execute a given transaction, producing a receipt and an optional trace.
|
||||||
/// This will change the state accordingly.
|
/// This will change the state accordingly.
|
||||||
pub fn apply(&mut self, env_info: &EnvInfo, machine: &Machine, t: &SignedTransaction, tracing: bool) -> ApplyResult {
|
pub fn apply(&mut self, env_info: &EnvInfo, machine: &Machine, t: &SignedTransaction, tracing: bool) -> ApplyResult<FlatTrace, VMTrace> {
|
||||||
if tracing {
|
if tracing {
|
||||||
let options = TransactOptions::with_tracing();
|
let options = TransactOptions::with_tracing();
|
||||||
self.apply_with_tracing(env_info, machine, t, options.tracer, options.vm_tracer)
|
self.apply_with_tracing(env_info, machine, t, options.tracer, options.vm_tracer)
|
||||||
@ -687,7 +687,7 @@ impl<B: Backend> State<B> {
|
|||||||
t: &SignedTransaction,
|
t: &SignedTransaction,
|
||||||
tracer: T,
|
tracer: T,
|
||||||
vm_tracer: V,
|
vm_tracer: V,
|
||||||
) -> ApplyResult where
|
) -> ApplyResult<T::Output, V::Output> where
|
||||||
T: trace::Tracer,
|
T: trace::Tracer,
|
||||||
V: trace::VMTracer,
|
V: trace::VMTracer,
|
||||||
{
|
{
|
||||||
@ -728,7 +728,7 @@ impl<B: Backend> State<B> {
|
|||||||
// `virt` signals that we are executing outside of a block set and restrictions like
|
// `virt` signals that we are executing outside of a block set and restrictions like
|
||||||
// gas limits and gas costs should be lifted.
|
// gas limits and gas costs should be lifted.
|
||||||
fn execute<T, V>(&mut self, env_info: &EnvInfo, machine: &Machine, t: &SignedTransaction, options: TransactOptions<T, V>, virt: bool)
|
fn execute<T, V>(&mut self, env_info: &EnvInfo, machine: &Machine, t: &SignedTransaction, options: TransactOptions<T, V>, virt: bool)
|
||||||
-> Result<Executed, ExecutionError> where T: trace::Tracer, V: trace::VMTracer,
|
-> Result<Executed<T::Output, V::Output>, ExecutionError> where T: trace::Tracer, V: trace::VMTracer,
|
||||||
{
|
{
|
||||||
let mut e = Executive::new(self, env_info, machine);
|
let mut e = Executive::new(self, env_info, machine);
|
||||||
|
|
||||||
|
@ -83,6 +83,8 @@ fn should_prefix_address_properly() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Tracer for ExecutiveTracer {
|
impl Tracer for ExecutiveTracer {
|
||||||
|
type Output = FlatTrace;
|
||||||
|
|
||||||
fn prepare_trace_call(&self, params: &ActionParams) -> Option<Call> {
|
fn prepare_trace_call(&self, params: &ActionParams) -> Option<Call> {
|
||||||
Some(Call::from(params.clone()))
|
Some(Call::from(params.clone()))
|
||||||
}
|
}
|
||||||
@ -201,6 +203,8 @@ impl ExecutiveVMTracer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl VMTracer for ExecutiveVMTracer {
|
impl VMTracer for ExecutiveVMTracer {
|
||||||
|
type Output = VMTrace;
|
||||||
|
|
||||||
fn trace_next_instruction(&mut self, _pc: usize, _instruction: u8) -> bool { true }
|
fn trace_next_instruction(&mut self, _pc: usize, _instruction: u8) -> bool { true }
|
||||||
|
|
||||||
fn trace_prepare_execute(&mut self, pc: usize, instruction: u8, gas_cost: U256) {
|
fn trace_prepare_execute(&mut self, pc: usize, instruction: u8, gas_cost: U256) {
|
||||||
|
@ -48,6 +48,9 @@ use header::BlockNumber;
|
|||||||
|
|
||||||
/// This trait is used by executive to build traces.
|
/// This trait is used by executive to build traces.
|
||||||
pub trait Tracer: Send {
|
pub trait Tracer: Send {
|
||||||
|
/// Data returned when draining the Tracer.
|
||||||
|
type Output;
|
||||||
|
|
||||||
/// Prepares call trace for given params. Noop tracer should return None.
|
/// Prepares call trace for given params. Noop tracer should return None.
|
||||||
fn prepare_trace_call(&self, params: &ActionParams) -> Option<Call>;
|
fn prepare_trace_call(&self, params: &ActionParams) -> Option<Call>;
|
||||||
|
|
||||||
@ -63,7 +66,7 @@ pub trait Tracer: Send {
|
|||||||
call: Option<Call>,
|
call: Option<Call>,
|
||||||
gas_used: U256,
|
gas_used: U256,
|
||||||
output: Option<Bytes>,
|
output: Option<Bytes>,
|
||||||
subs: Vec<FlatTrace>,
|
subs: Vec<Self::Output>,
|
||||||
);
|
);
|
||||||
|
|
||||||
/// Stores trace create info.
|
/// Stores trace create info.
|
||||||
@ -73,14 +76,14 @@ pub trait Tracer: Send {
|
|||||||
gas_used: U256,
|
gas_used: U256,
|
||||||
code: Option<Bytes>,
|
code: Option<Bytes>,
|
||||||
address: Address,
|
address: Address,
|
||||||
subs: Vec<FlatTrace>
|
subs: Vec<Self::Output>
|
||||||
);
|
);
|
||||||
|
|
||||||
/// Stores failed call trace.
|
/// Stores failed call trace.
|
||||||
fn trace_failed_call(&mut self, call: Option<Call>, subs: Vec<FlatTrace>, error: TraceError);
|
fn trace_failed_call(&mut self, call: Option<Call>, subs: Vec<Self::Output>, error: TraceError);
|
||||||
|
|
||||||
/// Stores failed create trace.
|
/// Stores failed create trace.
|
||||||
fn trace_failed_create(&mut self, create: Option<Create>, subs: Vec<FlatTrace>, error: TraceError);
|
fn trace_failed_create(&mut self, create: Option<Create>, subs: Vec<Self::Output>, error: TraceError);
|
||||||
|
|
||||||
/// Stores suicide info.
|
/// Stores suicide info.
|
||||||
fn trace_suicide(&mut self, address: Address, balance: U256, refund_address: Address);
|
fn trace_suicide(&mut self, address: Address, balance: U256, refund_address: Address);
|
||||||
@ -92,12 +95,15 @@ pub trait Tracer: Send {
|
|||||||
fn subtracer(&self) -> Self where Self: Sized;
|
fn subtracer(&self) -> Self where Self: Sized;
|
||||||
|
|
||||||
/// Consumes self and returns all traces.
|
/// Consumes self and returns all traces.
|
||||||
fn drain(self) -> Vec<FlatTrace>;
|
fn drain(self) -> Vec<Self::Output>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Used by executive to build VM traces.
|
/// Used by executive to build VM traces.
|
||||||
pub trait VMTracer: Send {
|
pub trait VMTracer: Send {
|
||||||
|
|
||||||
|
/// Data returned when draining the VMTracer.
|
||||||
|
type Output;
|
||||||
|
|
||||||
/// Trace the progression of interpreter to next instruction.
|
/// Trace the progression of interpreter to next instruction.
|
||||||
/// If tracer returns `false` it won't be called again.
|
/// If tracer returns `false` it won't be called again.
|
||||||
/// @returns true if `trace_prepare_execute` and `trace_executed` should be called.
|
/// @returns true if `trace_prepare_execute` and `trace_executed` should be called.
|
||||||
@ -116,7 +122,7 @@ pub trait VMTracer: Send {
|
|||||||
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.
|
||||||
fn drain(self) -> Option<VMTrace>;
|
fn drain(self) -> Option<Self::Output>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `DbExtras` provides an interface to query extra data which is not stored in tracesdb,
|
/// `DbExtras` provides an interface to query extra data which is not stored in tracesdb,
|
||||||
|
@ -27,6 +27,8 @@ use trace::trace::{Call, Create, VMTrace, RewardType};
|
|||||||
pub struct NoopTracer;
|
pub struct NoopTracer;
|
||||||
|
|
||||||
impl Tracer for NoopTracer {
|
impl Tracer for NoopTracer {
|
||||||
|
type Output = FlatTrace;
|
||||||
|
|
||||||
fn prepare_trace_call(&self, _: &ActionParams) -> Option<Call> {
|
fn prepare_trace_call(&self, _: &ActionParams) -> Option<Call> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
@ -76,6 +78,8 @@ impl Tracer for NoopTracer {
|
|||||||
pub struct NoopVMTracer;
|
pub struct NoopVMTracer;
|
||||||
|
|
||||||
impl VMTracer for NoopVMTracer {
|
impl VMTracer for NoopVMTracer {
|
||||||
|
type Output = VMTrace;
|
||||||
|
|
||||||
fn trace_next_instruction(&mut self, _pc: usize, _instruction: u8) -> bool { false }
|
fn trace_next_instruction(&mut self, _pc: usize, _instruction: u8) -> bool { false }
|
||||||
|
|
||||||
fn trace_prepare_execute(&mut self, _pc: usize, _instruction: u8, _gas_cost: U256) {}
|
fn trace_prepare_execute(&mut self, _pc: usize, _instruction: u8, _gas_cost: U256) {}
|
||||||
|
@ -22,5 +22,8 @@ evm = { path = "../ethcore/evm" }
|
|||||||
vm = { path = "../ethcore/vm" }
|
vm = { path = "../ethcore/vm" }
|
||||||
panic_hook = { path = "../panic_hook" }
|
panic_hook = { path = "../panic_hook" }
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
pretty_assertions = "0.1"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
evm-debug = ["ethcore/evm-debug-tests"]
|
evm-debug = ["ethcore/evm-debug-tests"]
|
||||||
|
@ -16,11 +16,13 @@
|
|||||||
|
|
||||||
//! JSON VM output.
|
//! JSON VM output.
|
||||||
|
|
||||||
use ethcore::trace;
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use bigint::prelude::U256;
|
use std::mem;
|
||||||
|
|
||||||
use bigint::hash::H256;
|
use bigint::hash::H256;
|
||||||
|
use bigint::prelude::U256;
|
||||||
use bytes::ToPretty;
|
use bytes::ToPretty;
|
||||||
|
use ethcore::trace;
|
||||||
|
|
||||||
use display;
|
use display;
|
||||||
use info as vm;
|
use info as vm;
|
||||||
@ -39,6 +41,7 @@ pub struct Informant {
|
|||||||
storage: HashMap<H256, H256>,
|
storage: HashMap<H256, H256>,
|
||||||
traces: Vec<String>,
|
traces: Vec<String>,
|
||||||
subtraces: Vec<String>,
|
subtraces: Vec<String>,
|
||||||
|
unmatched: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Informant {
|
impl Informant {
|
||||||
@ -59,14 +62,6 @@ impl Informant {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for Informant {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
for trace in &self.traces {
|
|
||||||
println!("{}", trace);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl vm::Informant for Informant {
|
impl vm::Informant for Informant {
|
||||||
fn before_test(&self, name: &str, action: &str) {
|
fn before_test(&self, name: &str, action: &str) {
|
||||||
println!(
|
println!(
|
||||||
@ -80,28 +75,43 @@ impl vm::Informant for Informant {
|
|||||||
self.gas_used = gas;
|
self.gas_used = gas;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn finish(result: Result<vm::Success, vm::Failure>) {
|
fn finish(result: vm::RunResult<Self::Output>) {
|
||||||
match result {
|
match result {
|
||||||
Ok(success) => println!(
|
Ok(success) => {
|
||||||
|
for trace in success.traces.unwrap_or_else(Vec::new) {
|
||||||
|
println!("{}", trace);
|
||||||
|
}
|
||||||
|
|
||||||
|
println!(
|
||||||
"{{\"output\":\"0x{output}\",\"gasUsed\":\"{gas:x}\",\"time\":{time}}}",
|
"{{\"output\":\"0x{output}\",\"gasUsed\":\"{gas:x}\",\"time\":{time}}}",
|
||||||
output = success.output.to_hex(),
|
output = success.output.to_hex(),
|
||||||
gas = success.gas_used,
|
gas = success.gas_used,
|
||||||
time = display::as_micros(&success.time),
|
time = display::as_micros(&success.time),
|
||||||
),
|
)
|
||||||
Err(failure) => println!(
|
},
|
||||||
|
Err(failure) => {
|
||||||
|
for trace in failure.traces.unwrap_or_else(Vec::new) {
|
||||||
|
println!("{}", trace);
|
||||||
|
}
|
||||||
|
|
||||||
|
println!(
|
||||||
"{{\"error\":\"{error}\",\"gasUsed\":\"{gas:x}\",\"time\":{time}}}",
|
"{{\"error\":\"{error}\",\"gasUsed\":\"{gas:x}\",\"time\":{time}}}",
|
||||||
error = failure.error,
|
error = failure.error,
|
||||||
gas = failure.gas_used,
|
gas = failure.gas_used,
|
||||||
time = display::as_micros(&failure.time),
|
time = display::as_micros(&failure.time),
|
||||||
),
|
)
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl trace::VMTracer for Informant {
|
impl trace::VMTracer for Informant {
|
||||||
|
type Output = Vec<String>;
|
||||||
|
|
||||||
fn trace_next_instruction(&mut self, pc: usize, instruction: u8) -> bool {
|
fn trace_next_instruction(&mut self, pc: usize, instruction: u8) -> bool {
|
||||||
self.pc = pc;
|
self.pc = pc;
|
||||||
self.instruction = instruction;
|
self.instruction = instruction;
|
||||||
|
self.unmatched = true;
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -128,6 +138,7 @@ impl trace::VMTracer for Informant {
|
|||||||
);
|
);
|
||||||
self.traces.push(trace);
|
self.traces.push(trace);
|
||||||
|
|
||||||
|
self.unmatched = false;
|
||||||
self.gas_used = gas_used;
|
self.gas_used = gas_used;
|
||||||
|
|
||||||
let len = self.stack.len();
|
let len = self.stack.len();
|
||||||
@ -147,7 +158,7 @@ impl trace::VMTracer for Informant {
|
|||||||
|
|
||||||
|
|
||||||
if !self.subtraces.is_empty() {
|
if !self.subtraces.is_empty() {
|
||||||
self.traces.extend(::std::mem::replace(&mut self.subtraces, vec![]));
|
self.traces.extend(mem::replace(&mut self.subtraces, vec![]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -159,24 +170,21 @@ impl trace::VMTracer for Informant {
|
|||||||
vm
|
vm
|
||||||
}
|
}
|
||||||
|
|
||||||
fn done_subtrace(&mut self, mut sub: Self) {
|
fn done_subtrace(&mut self, sub: Self) {
|
||||||
let subtraces = ::std::mem::replace(&mut sub.traces, vec![]);
|
if let Some(subtraces) = sub.drain() {
|
||||||
if sub.depth == 1 {
|
self.subtraces.extend(subtraces);
|
||||||
self.traces.extend(subtraces);
|
|
||||||
// print last line with final state:
|
|
||||||
sub.gas_cost = 0.into();
|
|
||||||
let gas_used = sub.gas_used;
|
|
||||||
trace::VMTracer::trace_executed(&mut sub, gas_used, &[], None, None);
|
|
||||||
} else {
|
|
||||||
self.subtraces = subtraces;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn drain(self) -> Option<trace::VMTrace> {
|
fn drain(mut self) -> Option<Self::Output> {
|
||||||
println!("Drain called.");
|
if self.unmatched {
|
||||||
for trace in &self.traces {
|
// print last line with final state:
|
||||||
println!("{}", trace);
|
self.gas_cost = 0.into();
|
||||||
|
let gas_used = self.gas_used;
|
||||||
|
self.trace_executed(gas_used, &[], None, None);
|
||||||
|
} else if !self.subtraces.is_empty() {
|
||||||
|
self.traces.extend(mem::replace(&mut self.subtraces, vec![]));
|
||||||
}
|
}
|
||||||
None
|
Some(self.traces)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,7 +31,7 @@ impl vm::Informant for Informant {
|
|||||||
println!("Test: {} ({})", name, action);
|
println!("Test: {} ({})", name, action);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn finish(result: Result<vm::Success, vm::Failure>) {
|
fn finish(result: vm::RunResult<Self::Output>) {
|
||||||
match result {
|
match result {
|
||||||
Ok(success) => {
|
Ok(success) => {
|
||||||
println!("Output: 0x{}", success.output.to_hex());
|
println!("Output: 0x{}", success.output.to_hex());
|
||||||
@ -47,7 +47,9 @@ impl vm::Informant for Informant {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl trace::VMTracer for Informant {
|
impl trace::VMTracer for Informant {
|
||||||
|
type Output = ();
|
||||||
|
|
||||||
fn prepare_subtrace(&self, _code: &[u8]) -> Self where Self: Sized { Default::default() }
|
fn prepare_subtrace(&self, _code: &[u8]) -> Self where Self: Sized { Default::default() }
|
||||||
fn done_subtrace(&mut self, _sub: Self) {}
|
fn done_subtrace(&mut self, _sub: Self) {}
|
||||||
fn drain(self) -> Option<trace::VMTrace> { None }
|
fn drain(self) -> Option<()> { None }
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,7 @@ use bigint::hash::H256;
|
|||||||
use ethcore::{trace, spec, transaction, pod_state};
|
use ethcore::{trace, spec, transaction, pod_state};
|
||||||
use ethcore::client::{self, EvmTestClient, EvmTestError, TransactResult};
|
use ethcore::client::{self, EvmTestClient, EvmTestError, TransactResult};
|
||||||
use ethjson;
|
use ethjson;
|
||||||
|
use vm::ActionParams;
|
||||||
|
|
||||||
/// VM execution informant
|
/// VM execution informant
|
||||||
pub trait Informant: trace::VMTracer {
|
pub trait Informant: trace::VMTracer {
|
||||||
@ -30,27 +31,51 @@ pub trait Informant: trace::VMTracer {
|
|||||||
/// Set initial gas.
|
/// Set initial gas.
|
||||||
fn set_gas(&mut self, _gas: U256) {}
|
fn set_gas(&mut self, _gas: U256) {}
|
||||||
/// Display final result.
|
/// Display final result.
|
||||||
fn finish(result: Result<Success, Failure>);
|
fn finish(result: RunResult<Self::Output>);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Execution finished correctly
|
/// Execution finished correctly
|
||||||
pub struct Success {
|
#[derive(Debug)]
|
||||||
|
pub struct Success<T> {
|
||||||
/// Used gas
|
/// Used gas
|
||||||
pub gas_used: U256,
|
pub gas_used: U256,
|
||||||
/// Output as bytes
|
/// Output as bytes
|
||||||
pub output: Vec<u8>,
|
pub output: Vec<u8>,
|
||||||
/// Time Taken
|
/// Time Taken
|
||||||
pub time: Duration,
|
pub time: Duration,
|
||||||
|
/// Traces
|
||||||
|
pub traces: Option<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Execution failed
|
/// Execution failed
|
||||||
pub struct Failure {
|
#[derive(Debug)]
|
||||||
|
pub struct Failure<T> {
|
||||||
/// Used gas
|
/// Used gas
|
||||||
pub gas_used: U256,
|
pub gas_used: U256,
|
||||||
/// Internal error
|
/// Internal error
|
||||||
pub error: EvmTestError,
|
pub error: EvmTestError,
|
||||||
/// Duration
|
/// Duration
|
||||||
pub time: Duration,
|
pub time: Duration,
|
||||||
|
/// Traces
|
||||||
|
pub traces: Option<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// EVM Execution result
|
||||||
|
pub type RunResult<T> = Result<Success<T>, Failure<T>>;
|
||||||
|
|
||||||
|
/// Execute given `ActionParams` and return the result.
|
||||||
|
pub fn run_action<T: Informant>(
|
||||||
|
spec: &spec::Spec,
|
||||||
|
params: ActionParams,
|
||||||
|
mut informant: T,
|
||||||
|
) -> RunResult<T::Output> {
|
||||||
|
informant.set_gas(params.gas);
|
||||||
|
run(spec, params.gas, None, |mut client| {
|
||||||
|
let result = client
|
||||||
|
.call(params, &mut informant)
|
||||||
|
.map(|r| (r.gas_left, r.return_data.to_vec()));
|
||||||
|
(result, informant.drain())
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Execute given Transaction and verify resulting state root.
|
/// Execute given Transaction and verify resulting state root.
|
||||||
@ -82,19 +107,19 @@ pub fn run_transaction<T: Informant>(
|
|||||||
let result = client.transact(env_info, transaction, informant);
|
let result = client.transact(env_info, transaction, informant);
|
||||||
match result {
|
match result {
|
||||||
TransactResult::Ok { state_root, .. } if state_root != post_root => {
|
TransactResult::Ok { state_root, .. } if state_root != post_root => {
|
||||||
Err(EvmTestError::PostCondition(format!(
|
(Err(EvmTestError::PostCondition(format!(
|
||||||
"State root mismatch (got: {}, expected: {})",
|
"State root mismatch (got: {}, expected: {})",
|
||||||
state_root,
|
state_root,
|
||||||
post_root,
|
post_root,
|
||||||
)))
|
))), None)
|
||||||
},
|
},
|
||||||
TransactResult::Ok { gas_left, output, .. } => {
|
TransactResult::Ok { gas_left, output, vm_trace, .. } => {
|
||||||
Ok((gas_left, output))
|
(Ok((gas_left, output)), vm_trace)
|
||||||
},
|
},
|
||||||
TransactResult::Err { error, .. } => {
|
TransactResult::Err { error, .. } => {
|
||||||
Err(EvmTestError::PostCondition(format!(
|
(Err(EvmTestError::PostCondition(format!(
|
||||||
"Unexpected execution error: {:?}", error
|
"Unexpected execution error: {:?}", error
|
||||||
)))
|
))), None)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -103,8 +128,13 @@ pub fn run_transaction<T: Informant>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Execute VM with given `ActionParams`
|
/// Execute VM with given `ActionParams`
|
||||||
pub fn run<'a, F, T>(spec: &'a spec::Spec, initial_gas: U256, pre_state: T, run: F) -> Result<Success, Failure> where
|
pub fn run<'a, F, T, X>(
|
||||||
F: FnOnce(EvmTestClient) -> Result<(U256, Vec<u8>), EvmTestError>,
|
spec: &'a spec::Spec,
|
||||||
|
initial_gas: U256,
|
||||||
|
pre_state: T,
|
||||||
|
run: F,
|
||||||
|
) -> RunResult<X> where
|
||||||
|
F: FnOnce(EvmTestClient) -> (Result<(U256, Vec<u8>), EvmTestError>, Option<X>),
|
||||||
T: Into<Option<&'a pod_state::PodState>>,
|
T: Into<Option<&'a pod_state::PodState>>,
|
||||||
{
|
{
|
||||||
let test_client = match pre_state.into() {
|
let test_client = match pre_state.into() {
|
||||||
@ -113,23 +143,135 @@ pub fn run<'a, F, T>(spec: &'a spec::Spec, initial_gas: U256, pre_state: T, run:
|
|||||||
}.map_err(|error| Failure {
|
}.map_err(|error| Failure {
|
||||||
gas_used: 0.into(),
|
gas_used: 0.into(),
|
||||||
error,
|
error,
|
||||||
time: Duration::from_secs(0)
|
time: Duration::from_secs(0),
|
||||||
|
traces: None,
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let start = Instant::now();
|
let start = Instant::now();
|
||||||
let result = run(test_client);
|
let result = run(test_client);
|
||||||
let duration = start.elapsed();
|
let time = start.elapsed();
|
||||||
|
|
||||||
match result {
|
match result {
|
||||||
Ok((gas_left, output)) => Ok(Success {
|
(Ok((gas_left, output)), traces) => Ok(Success {
|
||||||
gas_used: initial_gas - gas_left,
|
gas_used: initial_gas - gas_left,
|
||||||
output: output,
|
output,
|
||||||
time: duration,
|
time,
|
||||||
|
traces,
|
||||||
}),
|
}),
|
||||||
Err(e) => Err(Failure {
|
(Err(error), traces) => Err(Failure {
|
||||||
gas_used: initial_gas,
|
gas_used: initial_gas,
|
||||||
error: e,
|
error,
|
||||||
time: duration,
|
time,
|
||||||
|
traces,
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use std::sync::Arc;
|
||||||
|
use rustc_hex::FromHex;
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_trace_failure() {
|
||||||
|
run_test(
|
||||||
|
"60F8d6",
|
||||||
|
0xffff,
|
||||||
|
r#"
|
||||||
|
{"pc":0,"op":96,"opName":"PUSH1","gas":"0xffff","gasCost":"0x3","memory":"0x","stack":[],"storage":{},"depth":1}
|
||||||
|
{"pc":2,"op":214,"opName":"","gas":"0xfffc","gasCost":"0x0","memory":"0x","stack":["0xf8"],"storage":{},"depth":1}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
|
||||||
|
run_test(
|
||||||
|
"F8d6",
|
||||||
|
0xffff,
|
||||||
|
r#"
|
||||||
|
{"pc":0,"op":248,"opName":"","gas":"0xffff","gasCost":"0x0","memory":"0x","stack":[],"storage":{},"depth":1}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_trace_create_correctly() {
|
||||||
|
run_test(
|
||||||
|
"32343434345830f138343438323439f0",
|
||||||
|
0xffff,
|
||||||
|
r#"
|
||||||
|
{"pc":0,"op":50,"opName":"ORIGIN","gas":"0xffff","gasCost":"0x2","memory":"0x","stack":[],"storage":{},"depth":1}
|
||||||
|
{"pc":1,"op":52,"opName":"CALLVALUE","gas":"0xfffd","gasCost":"0x2","memory":"0x","stack":["0x0"],"storage":{},"depth":1}
|
||||||
|
{"pc":2,"op":52,"opName":"CALLVALUE","gas":"0xfffb","gasCost":"0x2","memory":"0x","stack":["0x0","0x0"],"storage":{},"depth":1}
|
||||||
|
{"pc":3,"op":52,"opName":"CALLVALUE","gas":"0xfff9","gasCost":"0x2","memory":"0x","stack":["0x0","0x0","0x0"],"storage":{},"depth":1}
|
||||||
|
{"pc":4,"op":52,"opName":"CALLVALUE","gas":"0xfff7","gasCost":"0x2","memory":"0x","stack":["0x0","0x0","0x0","0x0"],"storage":{},"depth":1}
|
||||||
|
{"pc":5,"op":88,"opName":"PC","gas":"0xfff5","gasCost":"0x2","memory":"0x","stack":["0x0","0x0","0x0","0x0","0x0"],"storage":{},"depth":1}
|
||||||
|
{"pc":6,"op":48,"opName":"ADDRESS","gas":"0xfff3","gasCost":"0x2","memory":"0x","stack":["0x0","0x0","0x0","0x0","0x0","0x5"],"storage":{},"depth":1}
|
||||||
|
{"pc":7,"op":241,"opName":"CALL","gas":"0xfff1","gasCost":"0x61d0","memory":"0x","stack":["0x0","0x0","0x0","0x0","0x0","0x5","0x0"],"storage":{},"depth":1}
|
||||||
|
{"pc":8,"op":56,"opName":"CODESIZE","gas":"0x9e21","gasCost":"0x2","memory":"0x","stack":["0x1"],"storage":{},"depth":1}
|
||||||
|
{"pc":9,"op":52,"opName":"CALLVALUE","gas":"0x9e1f","gasCost":"0x2","memory":"0x","stack":["0x1","0x10"],"storage":{},"depth":1}
|
||||||
|
{"pc":10,"op":52,"opName":"CALLVALUE","gas":"0x9e1d","gasCost":"0x2","memory":"0x","stack":["0x1","0x10","0x0"],"storage":{},"depth":1}
|
||||||
|
{"pc":11,"op":56,"opName":"CODESIZE","gas":"0x9e1b","gasCost":"0x2","memory":"0x","stack":["0x1","0x10","0x0","0x0"],"storage":{},"depth":1}
|
||||||
|
{"pc":12,"op":50,"opName":"ORIGIN","gas":"0x9e19","gasCost":"0x2","memory":"0x","stack":["0x1","0x10","0x0","0x0","0x10"],"storage":{},"depth":1}
|
||||||
|
{"pc":13,"op":52,"opName":"CALLVALUE","gas":"0x9e17","gasCost":"0x2","memory":"0x","stack":["0x1","0x10","0x0","0x0","0x10","0x0"],"storage":{},"depth":1}
|
||||||
|
{"pc":14,"op":57,"opName":"CODECOPY","gas":"0x9e15","gasCost":"0x9","memory":"0x","stack":["0x1","0x10","0x0","0x0","0x10","0x0","0x0"],"storage":{},"depth":1}
|
||||||
|
{"pc":15,"op":240,"opName":"CREATE","gas":"0x9e0c","gasCost":"0x9e0c","memory":"0x32343434345830f138343438323439f0","stack":["0x1","0x10","0x0","0x0"],"storage":{},"depth":1}
|
||||||
|
{"pc":0,"op":50,"opName":"ORIGIN","gas":"0x210c","gasCost":"0x2","memory":"0x","stack":[],"storage":{},"depth":2}
|
||||||
|
{"pc":1,"op":52,"opName":"CALLVALUE","gas":"0x210a","gasCost":"0x2","memory":"0x","stack":["0x0"],"storage":{},"depth":2}
|
||||||
|
{"pc":2,"op":52,"opName":"CALLVALUE","gas":"0x2108","gasCost":"0x2","memory":"0x","stack":["0x0","0x0"],"storage":{},"depth":2}
|
||||||
|
{"pc":3,"op":52,"opName":"CALLVALUE","gas":"0x2106","gasCost":"0x2","memory":"0x","stack":["0x0","0x0","0x0"],"storage":{},"depth":2}
|
||||||
|
{"pc":4,"op":52,"opName":"CALLVALUE","gas":"0x2104","gasCost":"0x2","memory":"0x","stack":["0x0","0x0","0x0","0x0"],"storage":{},"depth":2}
|
||||||
|
{"pc":5,"op":88,"opName":"PC","gas":"0x2102","gasCost":"0x2","memory":"0x","stack":["0x0","0x0","0x0","0x0","0x0"],"storage":{},"depth":2}
|
||||||
|
{"pc":6,"op":48,"opName":"ADDRESS","gas":"0x2100","gasCost":"0x2","memory":"0x","stack":["0x0","0x0","0x0","0x0","0x0","0x5"],"storage":{},"depth":2}
|
||||||
|
{"pc":7,"op":241,"opName":"CALL","gas":"0x20fe","gasCost":"0x0","memory":"0x","stack":["0x0","0x0","0x0","0x0","0x0","0x5","0xbd770416a3345f91e4b34576cb804a576fa48eb1"],"storage":{},"depth":2}
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_test<T: Into<U256>>(
|
||||||
|
code: &str,
|
||||||
|
gas: T,
|
||||||
|
expected: &str,
|
||||||
|
) {
|
||||||
|
let mut params = ActionParams::default();
|
||||||
|
params.code = Some(Arc::new(code.from_hex().unwrap()));
|
||||||
|
params.gas = gas.into();
|
||||||
|
|
||||||
|
let spec = ::ethcore::ethereum::new_foundation(&::std::env::temp_dir());
|
||||||
|
let informant = ::display::json::Informant::default();
|
||||||
|
let result = run_action(&spec, params, informant);
|
||||||
|
let expected = expected.split("\n")
|
||||||
|
.map(|x| x.trim())
|
||||||
|
.map(|x| x.to_owned())
|
||||||
|
.filter(|x| !x.is_empty())
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
match result {
|
||||||
|
Ok(Success { traces, .. }) => {
|
||||||
|
assert_traces_eq(&traces.unwrap(), &expected);
|
||||||
|
},
|
||||||
|
Err(Failure { traces, .. }) => {
|
||||||
|
assert_traces_eq(&traces.unwrap(), &expected);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn assert_traces_eq(
|
||||||
|
a: &[String],
|
||||||
|
b: &[String],
|
||||||
|
) {
|
||||||
|
let mut ita = a.iter();
|
||||||
|
let mut itb = b.iter();
|
||||||
|
|
||||||
|
loop {
|
||||||
|
match (ita.next(), itb.next()) {
|
||||||
|
(Some(a), Some(b)) => {
|
||||||
|
assert_eq!(a, b);
|
||||||
|
println!("{}", a);
|
||||||
|
},
|
||||||
|
(None, None) => return,
|
||||||
|
e => {
|
||||||
|
panic!("Traces mismatch: {:?}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -32,6 +32,10 @@ extern crate vm;
|
|||||||
extern crate evm;
|
extern crate evm;
|
||||||
extern crate panic_hook;
|
extern crate panic_hook;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
#[macro_use]
|
||||||
|
extern crate pretty_assertions;
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::{fmt, fs};
|
use std::{fmt, fs};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
@ -136,7 +140,7 @@ fn run_state_test(args: Args) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_call<T: Informant>(args: Args, mut informant: T) {
|
fn run_call<T: Informant>(args: Args, informant: T) {
|
||||||
let from = arg(args.from(), "--from");
|
let from = arg(args.from(), "--from");
|
||||||
let to = arg(args.to(), "--to");
|
let to = arg(args.to(), "--to");
|
||||||
let code = arg(args.code(), "--code");
|
let code = arg(args.code(), "--code");
|
||||||
@ -160,10 +164,7 @@ fn run_call<T: Informant>(args: Args, mut informant: T) {
|
|||||||
params.code = code.map(Arc::new);
|
params.code = code.map(Arc::new);
|
||||||
params.data = data;
|
params.data = data;
|
||||||
|
|
||||||
informant.set_gas(gas);
|
let result = info::run_action(&spec, params, informant);
|
||||||
let result = info::run(&spec, gas, None, |mut client| {
|
|
||||||
client.call(params, &mut informant).map(|r| (r.gas_left, r.return_data.to_vec()))
|
|
||||||
});
|
|
||||||
T::finish(result);
|
T::finish(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,9 +60,9 @@ hash = { path = "../util/hash" }
|
|||||||
hardware-wallet = { path = "../hw" }
|
hardware-wallet = { path = "../hw" }
|
||||||
|
|
||||||
clippy = { version = "0.0.103", optional = true}
|
clippy = { version = "0.0.103", optional = true}
|
||||||
pretty_assertions = "0.1"
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
pretty_assertions = "0.1"
|
||||||
macros = { path = "../util/macros" }
|
macros = { path = "../util/macros" }
|
||||||
ethcore-network = { path = "../util/network" }
|
ethcore-network = { path = "../util/network" }
|
||||||
kvdb-memorydb = { path = "../util/kvdb-memorydb" }
|
kvdb-memorydb = { path = "../util/kvdb-memorydb" }
|
||||||
|
Loading…
Reference in New Issue
Block a user