diff --git a/Cargo.lock b/Cargo.lock index b38433244..de14f0765 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -940,6 +940,7 @@ dependencies = [ "ethjson 0.1.0", "evm 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)", "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)", diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index b65e698df..f3f06438f 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -1142,7 +1142,7 @@ impl Client { state_diff: bool, transaction: &SignedTransaction, options: TransactOptions, - ) -> Result where + ) -> Result, CallError> where T: trace::Tracer, V: trace::VMTracer, { diff --git a/ethcore/src/client/evm_test_client.rs b/ethcore/src/client/evm_test_client.rs index 366735d56..814e119ba 100644 --- a/ethcore/src/client/evm_test_client.rs +++ b/ethcore/src/client/evm_test_client.rs @@ -197,7 +197,7 @@ impl<'a> EvmTestClient<'a> { env_info: &client::EnvInfo, transaction: transaction::SignedTransaction, vm_tracer: T, - ) -> TransactResult { + ) -> TransactResult { let initial_gas = transaction.gas; // Verify transaction 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(), gas_left: initial_gas - result.receipt.gas_used, output: result.output, + vm_trace: result.vm_trace, } }, Err(error) => TransactResult::Err { @@ -230,7 +231,7 @@ impl<'a> EvmTestClient<'a> { } /// A result of applying transaction to the state. -pub enum TransactResult { +pub enum TransactResult { /// Successful execution Ok { /// State root @@ -239,6 +240,8 @@ pub enum TransactResult { gas_left: U256, /// Output output: Vec, + /// VM Traces + vm_trace: Option, }, /// Transaction failed to run Err { diff --git a/ethcore/src/executed.rs b/ethcore/src/executed.rs index 490681dfb..5035e5eb8 100644 --- a/ethcore/src/executed.rs +++ b/ethcore/src/executed.rs @@ -29,7 +29,7 @@ use std::fmt; /// Transaction execution receipt. #[derive(Debug, PartialEq, Clone)] -pub struct Executed { +pub struct Executed { /// True if the outer call/create resulted in an exceptional exit. pub exception: Option, @@ -63,9 +63,9 @@ pub struct Executed { /// Transaction output. pub output: Bytes, /// The trace of this transaction. - pub trace: Vec, + pub trace: Vec, /// The VM trace of this transaction. - pub vm_trace: Option, + pub vm_trace: Option, /// The state diff, if we traced it. pub state_diff: Option, } diff --git a/ethcore/src/executive.rs b/ethcore/src/executive.rs index 3709ea449..a23f60007 100644 --- a/ethcore/src/executive.rs +++ b/ethcore/src/executive.rs @@ -30,7 +30,7 @@ use evm::{CallType, Factory, Finalize, FinalizationResult}; use vm::{self, Ext, CreateContractAddress, ReturnData, CleanDustMode, ActionParams, ActionValue}; use wasm; use externalities::*; -use trace::{self, FlatTrace, VMTrace, Tracer, VMTracer}; +use trace::{self, Tracer, VMTracer}; use transaction::{Action, SignedTransaction}; use crossbeam; 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. pub fn transact(&'a mut self, t: &SignedTransaction, options: TransactOptions) - -> Result where T: Tracer, V: VMTracer, + -> Result, ExecutionError> where T: Tracer, V: VMTracer, { 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. /// Used for extra-block executions for things like consensus contracts and RPCs pub fn transact_virtual(&'a mut self, t: &SignedTransaction, options: TransactOptions) - -> Result where T: Tracer, V: VMTracer, + -> Result, ExecutionError> where T: Tracer, V: VMTracer, { let sender = t.sender(); let balance = self.state.balance(&sender)?; @@ -244,7 +244,7 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> { output_from_create: bool, mut tracer: T, mut vm_tracer: V - ) -> Result where T: Tracer, V: VMTracer { + ) -> Result, ExecutionError> where T: Tracer, V: VMTracer { let sender = t.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). - fn finalize( + fn finalize( &mut self, t: &SignedTransaction, mut substate: Substate, result: vm::Result, output: Bytes, - trace: Vec, - vm_trace: Option - ) -> ExecutionResult { + trace: Vec, + vm_trace: Option + ) -> Result, ExecutionError> { let schedule = self.machine.schedule(self.info.number); // refunds from SSTORE nonzero -> zero diff --git a/ethcore/src/state/mod.rs b/ethcore/src/state/mod.rs index dfde05921..a7a5a303b 100644 --- a/ethcore/src/state/mod.rs +++ b/ethcore/src/state/mod.rs @@ -62,19 +62,19 @@ pub use self::backend::Backend; pub use self::substate::Substate; /// Used to return information about an `State::apply` operation. -pub struct ApplyOutcome { +pub struct ApplyOutcome { /// The receipt for the applied transaction. pub receipt: Receipt, /// The output of the applied transaction. pub output: Bytes, /// The trace for the applied transaction, empty if tracing was not produced. - pub trace: Vec, + pub trace: Vec, /// The VM trace for the applied transaction, None if tracing was not produced. - pub vm_trace: Option + pub vm_trace: Option } /// Result type for the execution ("application") of a transaction. -pub type ApplyResult = Result; +pub type ApplyResult = Result, Error>; /// Return type of proof validity check. #[derive(Debug, Clone)] @@ -668,7 +668,7 @@ impl State { /// Execute a given transaction, producing a receipt and an optional trace. /// 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 { if tracing { let options = TransactOptions::with_tracing(); self.apply_with_tracing(env_info, machine, t, options.tracer, options.vm_tracer) @@ -687,7 +687,7 @@ impl State { t: &SignedTransaction, tracer: T, vm_tracer: V, - ) -> ApplyResult where + ) -> ApplyResult where T: trace::Tracer, V: trace::VMTracer, { @@ -728,7 +728,7 @@ impl State { // `virt` signals that we are executing outside of a block set and restrictions like // gas limits and gas costs should be lifted. fn execute(&mut self, env_info: &EnvInfo, machine: &Machine, t: &SignedTransaction, options: TransactOptions, virt: bool) - -> Result where T: trace::Tracer, V: trace::VMTracer, + -> Result, ExecutionError> where T: trace::Tracer, V: trace::VMTracer, { let mut e = Executive::new(self, env_info, machine); diff --git a/ethcore/src/trace/executive_tracer.rs b/ethcore/src/trace/executive_tracer.rs index 38c134e6b..2137d679d 100644 --- a/ethcore/src/trace/executive_tracer.rs +++ b/ethcore/src/trace/executive_tracer.rs @@ -83,6 +83,8 @@ fn should_prefix_address_properly() { } impl Tracer for ExecutiveTracer { + type Output = FlatTrace; + fn prepare_trace_call(&self, params: &ActionParams) -> Option { Some(Call::from(params.clone())) } @@ -201,6 +203,8 @@ impl ExecutiveVMTracer { } impl VMTracer for ExecutiveVMTracer { + type Output = VMTrace; + 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) { diff --git a/ethcore/src/trace/mod.rs b/ethcore/src/trace/mod.rs index f86daf13f..991e434fc 100644 --- a/ethcore/src/trace/mod.rs +++ b/ethcore/src/trace/mod.rs @@ -48,6 +48,9 @@ use header::BlockNumber; /// This trait is used by executive to build traces. pub trait Tracer: Send { + /// Data returned when draining the Tracer. + type Output; + /// Prepares call trace for given params. Noop tracer should return None. fn prepare_trace_call(&self, params: &ActionParams) -> Option; @@ -63,7 +66,7 @@ pub trait Tracer: Send { call: Option, gas_used: U256, output: Option, - subs: Vec, + subs: Vec, ); /// Stores trace create info. @@ -73,14 +76,14 @@ pub trait Tracer: Send { gas_used: U256, code: Option, address: Address, - subs: Vec + subs: Vec ); /// Stores failed call trace. - fn trace_failed_call(&mut self, call: Option, subs: Vec, error: TraceError); + fn trace_failed_call(&mut self, call: Option, subs: Vec, error: TraceError); /// Stores failed create trace. - fn trace_failed_create(&mut self, create: Option, subs: Vec, error: TraceError); + fn trace_failed_create(&mut self, create: Option, subs: Vec, error: TraceError); /// Stores suicide info. 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; /// Consumes self and returns all traces. - fn drain(self) -> Vec; + fn drain(self) -> Vec; } /// Used by executive to build VM traces. pub trait VMTracer: Send { + /// Data returned when draining the VMTracer. + type Output; + /// Trace the progression of interpreter to next instruction. /// If tracer returns `false` it won't be called again. /// @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; /// Consumes self and returns the VM trace. - fn drain(self) -> Option; + fn drain(self) -> Option; } /// `DbExtras` provides an interface to query extra data which is not stored in tracesdb, diff --git a/ethcore/src/trace/noop_tracer.rs b/ethcore/src/trace/noop_tracer.rs index 6def53c6c..bff8b9d44 100644 --- a/ethcore/src/trace/noop_tracer.rs +++ b/ethcore/src/trace/noop_tracer.rs @@ -27,6 +27,8 @@ use trace::trace::{Call, Create, VMTrace, RewardType}; pub struct NoopTracer; impl Tracer for NoopTracer { + type Output = FlatTrace; + fn prepare_trace_call(&self, _: &ActionParams) -> Option { None } @@ -76,6 +78,8 @@ impl Tracer for NoopTracer { pub struct NoopVMTracer; impl VMTracer for NoopVMTracer { + type Output = VMTrace; + 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) {} diff --git a/evmbin/Cargo.toml b/evmbin/Cargo.toml index efb6ba591..200e5a735 100644 --- a/evmbin/Cargo.toml +++ b/evmbin/Cargo.toml @@ -22,5 +22,8 @@ evm = { path = "../ethcore/evm" } vm = { path = "../ethcore/vm" } panic_hook = { path = "../panic_hook" } +[dev-dependencies] +pretty_assertions = "0.1" + [features] evm-debug = ["ethcore/evm-debug-tests"] diff --git a/evmbin/src/display/json.rs b/evmbin/src/display/json.rs index 3de0d261c..badd16d2a 100644 --- a/evmbin/src/display/json.rs +++ b/evmbin/src/display/json.rs @@ -16,11 +16,13 @@ //! JSON VM output. -use ethcore::trace; use std::collections::HashMap; -use bigint::prelude::U256; +use std::mem; + use bigint::hash::H256; +use bigint::prelude::U256; use bytes::ToPretty; +use ethcore::trace; use display; use info as vm; @@ -39,6 +41,7 @@ pub struct Informant { storage: HashMap, traces: Vec, subtraces: Vec, + unmatched: bool, } 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 { fn before_test(&self, name: &str, action: &str) { println!( @@ -80,28 +75,43 @@ impl vm::Informant for Informant { self.gas_used = gas; } - fn finish(result: Result) { + fn finish(result: vm::RunResult) { match result { - Ok(success) => println!( - "{{\"output\":\"0x{output}\",\"gasUsed\":\"{gas:x}\",\"time\":{time}}}", - output = success.output.to_hex(), - gas = success.gas_used, - time = display::as_micros(&success.time), - ), - Err(failure) => println!( - "{{\"error\":\"{error}\",\"gasUsed\":\"{gas:x}\",\"time\":{time}}}", - error = failure.error, - gas = failure.gas_used, - time = display::as_micros(&failure.time), - ), + Ok(success) => { + for trace in success.traces.unwrap_or_else(Vec::new) { + println!("{}", trace); + } + + println!( + "{{\"output\":\"0x{output}\",\"gasUsed\":\"{gas:x}\",\"time\":{time}}}", + output = success.output.to_hex(), + gas = success.gas_used, + time = display::as_micros(&success.time), + ) + }, + Err(failure) => { + for trace in failure.traces.unwrap_or_else(Vec::new) { + println!("{}", trace); + } + + println!( + "{{\"error\":\"{error}\",\"gasUsed\":\"{gas:x}\",\"time\":{time}}}", + error = failure.error, + gas = failure.gas_used, + time = display::as_micros(&failure.time), + ) + }, } } } impl trace::VMTracer for Informant { + type Output = Vec; + fn trace_next_instruction(&mut self, pc: usize, instruction: u8) -> bool { self.pc = pc; self.instruction = instruction; + self.unmatched = true; true } @@ -128,6 +138,7 @@ impl trace::VMTracer for Informant { ); self.traces.push(trace); + self.unmatched = false; self.gas_used = gas_used; let len = self.stack.len(); @@ -147,7 +158,7 @@ impl trace::VMTracer for Informant { 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 } - fn done_subtrace(&mut self, mut sub: Self) { - let subtraces = ::std::mem::replace(&mut sub.traces, vec![]); - if sub.depth == 1 { - 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 done_subtrace(&mut self, sub: Self) { + if let Some(subtraces) = sub.drain() { + self.subtraces.extend(subtraces); } } - fn drain(self) -> Option { - println!("Drain called."); - for trace in &self.traces { - println!("{}", trace); + fn drain(mut self) -> Option { + if self.unmatched { + // print last line with final state: + 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) } } diff --git a/evmbin/src/display/simple.rs b/evmbin/src/display/simple.rs index 9f6773734..6e9959a8e 100644 --- a/evmbin/src/display/simple.rs +++ b/evmbin/src/display/simple.rs @@ -31,7 +31,7 @@ impl vm::Informant for Informant { println!("Test: {} ({})", name, action); } - fn finish(result: Result) { + fn finish(result: vm::RunResult) { match result { Ok(success) => { println!("Output: 0x{}", success.output.to_hex()); @@ -47,7 +47,9 @@ impl vm::Informant for Informant { } impl trace::VMTracer for Informant { + type Output = (); + fn prepare_subtrace(&self, _code: &[u8]) -> Self where Self: Sized { Default::default() } fn done_subtrace(&mut self, _sub: Self) {} - fn drain(self) -> Option { None } + fn drain(self) -> Option<()> { None } } diff --git a/evmbin/src/info.rs b/evmbin/src/info.rs index 4af30db71..218a3c419 100644 --- a/evmbin/src/info.rs +++ b/evmbin/src/info.rs @@ -22,6 +22,7 @@ use bigint::hash::H256; use ethcore::{trace, spec, transaction, pod_state}; use ethcore::client::{self, EvmTestClient, EvmTestError, TransactResult}; use ethjson; +use vm::ActionParams; /// VM execution informant pub trait Informant: trace::VMTracer { @@ -30,27 +31,51 @@ pub trait Informant: trace::VMTracer { /// Set initial gas. fn set_gas(&mut self, _gas: U256) {} /// Display final result. - fn finish(result: Result); + fn finish(result: RunResult); } /// Execution finished correctly -pub struct Success { +#[derive(Debug)] +pub struct Success { /// Used gas pub gas_used: U256, /// Output as bytes pub output: Vec, /// Time Taken pub time: Duration, + /// Traces + pub traces: Option, } /// Execution failed -pub struct Failure { +#[derive(Debug)] +pub struct Failure { /// Used gas pub gas_used: U256, /// Internal error pub error: EvmTestError, /// Duration pub time: Duration, + /// Traces + pub traces: Option, +} + +/// EVM Execution result +pub type RunResult = Result, Failure>; + +/// Execute given `ActionParams` and return the result. +pub fn run_action( + spec: &spec::Spec, + params: ActionParams, + mut informant: T, +) -> RunResult { + 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. @@ -82,19 +107,19 @@ pub fn run_transaction( let result = client.transact(env_info, transaction, informant); match result { TransactResult::Ok { state_root, .. } if state_root != post_root => { - Err(EvmTestError::PostCondition(format!( + (Err(EvmTestError::PostCondition(format!( "State root mismatch (got: {}, expected: {})", state_root, post_root, - ))) + ))), None) }, - TransactResult::Ok { gas_left, output, .. } => { - Ok((gas_left, output)) + TransactResult::Ok { gas_left, output, vm_trace, .. } => { + (Ok((gas_left, output)), vm_trace) }, TransactResult::Err { error, .. } => { - Err(EvmTestError::PostCondition(format!( + (Err(EvmTestError::PostCondition(format!( "Unexpected execution error: {:?}", error - ))) + ))), None) }, } }); @@ -103,8 +128,13 @@ pub fn run_transaction( } /// Execute VM with given `ActionParams` -pub fn run<'a, F, T>(spec: &'a spec::Spec, initial_gas: U256, pre_state: T, run: F) -> Result where - F: FnOnce(EvmTestClient) -> Result<(U256, Vec), EvmTestError>, +pub fn run<'a, F, T, X>( + spec: &'a spec::Spec, + initial_gas: U256, + pre_state: T, + run: F, +) -> RunResult where + F: FnOnce(EvmTestClient) -> (Result<(U256, Vec), EvmTestError>, Option), T: 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 { gas_used: 0.into(), error, - time: Duration::from_secs(0) + time: Duration::from_secs(0), + traces: None, })?; let start = Instant::now(); let result = run(test_client); - let duration = start.elapsed(); + let time = start.elapsed(); match result { - Ok((gas_left, output)) => Ok(Success { + (Ok((gas_left, output)), traces) => Ok(Success { gas_used: initial_gas - gas_left, - output: output, - time: duration, + output, + time, + traces, }), - Err(e) => Err(Failure { + (Err(error), traces) => Err(Failure { gas_used: initial_gas, - error: e, - time: duration, + error, + 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>( + 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::>(); + 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); + } + } + } + } +} diff --git a/evmbin/src/main.rs b/evmbin/src/main.rs index b37654c04..a32af49e0 100644 --- a/evmbin/src/main.rs +++ b/evmbin/src/main.rs @@ -32,6 +32,10 @@ extern crate vm; extern crate evm; extern crate panic_hook; +#[cfg(test)] +#[macro_use] +extern crate pretty_assertions; + use std::sync::Arc; use std::{fmt, fs}; use std::path::PathBuf; @@ -136,7 +140,7 @@ fn run_state_test(args: Args) { } } -fn run_call(args: Args, mut informant: T) { +fn run_call(args: Args, informant: T) { let from = arg(args.from(), "--from"); let to = arg(args.to(), "--to"); let code = arg(args.code(), "--code"); @@ -160,10 +164,7 @@ fn run_call(args: Args, mut informant: T) { params.code = code.map(Arc::new); params.data = data; - informant.set_gas(gas); - let result = info::run(&spec, gas, None, |mut client| { - client.call(params, &mut informant).map(|r| (r.gas_left, r.return_data.to_vec())) - }); + let result = info::run_action(&spec, params, informant); T::finish(result); } diff --git a/rpc/Cargo.toml b/rpc/Cargo.toml index dc70406c7..360a7b776 100644 --- a/rpc/Cargo.toml +++ b/rpc/Cargo.toml @@ -60,9 +60,9 @@ hash = { path = "../util/hash" } hardware-wallet = { path = "../hw" } clippy = { version = "0.0.103", optional = true} -pretty_assertions = "0.1" [dev-dependencies] +pretty_assertions = "0.1" macros = { path = "../util/macros" } ethcore-network = { path = "../util/network" } kvdb-memorydb = { path = "../util/kvdb-memorydb" }