Capture traces and write some tests.

This commit is contained in:
Tomasz Drwięga 2017-10-20 15:40:25 +02:00
parent 4ac406da6b
commit 2d222a5920
No known key found for this signature in database
GPG Key ID: D066F497E62CAF66
15 changed files with 267 additions and 93 deletions

1
Cargo.lock generated
View File

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

View File

@ -1142,7 +1142,7 @@ impl Client {
state_diff: bool,
transaction: &SignedTransaction,
options: TransactOptions<T, V>,
) -> Result<Executed, CallError> where
) -> Result<Executed<T::Output, V::Output>, CallError> where
T: trace::Tracer,
V: trace::VMTracer,
{

View File

@ -197,7 +197,7 @@ impl<'a> EvmTestClient<'a> {
env_info: &client::EnvInfo,
transaction: transaction::SignedTransaction,
vm_tracer: T,
) -> TransactResult {
) -> TransactResult<T::Output> {
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<T> {
/// Successful execution
Ok {
/// State root
@ -239,6 +240,8 @@ pub enum TransactResult {
gas_left: U256,
/// Output
output: Vec<u8>,
/// VM Traces
vm_trace: Option<T>,
},
/// Transaction failed to run
Err {

View File

@ -29,7 +29,7 @@ use std::fmt;
/// Transaction execution receipt.
#[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.
pub exception: Option<vm::Error>,
@ -63,9 +63,9 @@ pub struct Executed {
/// Transaction output.
pub output: Bytes,
/// The trace of this transaction.
pub trace: Vec<FlatTrace>,
pub trace: Vec<T>,
/// The VM trace of this transaction.
pub vm_trace: Option<VMTrace>,
pub vm_trace: Option<V>,
/// The state diff, if we traced it.
pub state_diff: Option<StateDiff>,
}

View File

@ -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<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)
}
@ -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<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 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<Executed, ExecutionError> where T: Tracer, V: VMTracer {
) -> Result<Executed<T::Output, V::Output>, 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<T, V>(
&mut self,
t: &SignedTransaction,
mut substate: Substate,
result: vm::Result<FinalizationResult>,
output: Bytes,
trace: Vec<FlatTrace>,
vm_trace: Option<VMTrace>
) -> ExecutionResult {
trace: Vec<T>,
vm_trace: Option<V>
) -> Result<Executed<T, V>, ExecutionError> {
let schedule = self.machine.schedule(self.info.number);
// refunds from SSTORE nonzero -> zero

View File

@ -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<T, V> {
/// 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<FlatTrace>,
pub trace: Vec<T>,
/// 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.
pub type ApplyResult = Result<ApplyOutcome, Error>;
pub type ApplyResult<T, V> = Result<ApplyOutcome<T, V>, Error>;
/// Return type of proof validity check.
#[derive(Debug, Clone)]
@ -668,7 +668,7 @@ impl<B: Backend> State<B> {
/// 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<FlatTrace, VMTrace> {
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<B: Backend> State<B> {
t: &SignedTransaction,
tracer: T,
vm_tracer: V,
) -> ApplyResult where
) -> ApplyResult<T::Output, V::Output> where
T: trace::Tracer,
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
// 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)
-> 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);

View File

@ -83,6 +83,8 @@ fn should_prefix_address_properly() {
}
impl Tracer for ExecutiveTracer {
type Output = FlatTrace;
fn prepare_trace_call(&self, params: &ActionParams) -> Option<Call> {
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) {

View File

@ -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<Call>;
@ -63,7 +66,7 @@ pub trait Tracer: Send {
call: Option<Call>,
gas_used: U256,
output: Option<Bytes>,
subs: Vec<FlatTrace>,
subs: Vec<Self::Output>,
);
/// Stores trace create info.
@ -73,14 +76,14 @@ pub trait Tracer: Send {
gas_used: U256,
code: Option<Bytes>,
address: Address,
subs: Vec<FlatTrace>
subs: Vec<Self::Output>
);
/// 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.
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.
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<FlatTrace>;
fn drain(self) -> Vec<Self::Output>;
}
/// 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<VMTrace>;
fn drain(self) -> Option<Self::Output>;
}
/// `DbExtras` provides an interface to query extra data which is not stored in tracesdb,

View File

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

View File

@ -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"]

View File

@ -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<H256, H256>,
traces: Vec<String>,
subtraces: Vec<String>,
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<vm::Success, vm::Failure>) {
fn finish(result: vm::RunResult<Self::Output>) {
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<String>;
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<trace::VMTrace> {
println!("Drain called.");
for trace in &self.traces {
println!("{}", trace);
fn drain(mut self) -> Option<Self::Output> {
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)
}
}

View File

@ -31,7 +31,7 @@ impl vm::Informant for Informant {
println!("Test: {} ({})", name, action);
}
fn finish(result: Result<vm::Success, vm::Failure>) {
fn finish(result: vm::RunResult<Self::Output>) {
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<trace::VMTrace> { None }
fn drain(self) -> Option<()> { None }
}

View File

@ -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<Success, Failure>);
fn finish(result: RunResult<Self::Output>);
}
/// Execution finished correctly
pub struct Success {
#[derive(Debug)]
pub struct Success<T> {
/// Used gas
pub gas_used: U256,
/// Output as bytes
pub output: Vec<u8>,
/// Time Taken
pub time: Duration,
/// Traces
pub traces: Option<T>,
}
/// Execution failed
pub struct Failure {
#[derive(Debug)]
pub struct Failure<T> {
/// Used gas
pub gas_used: U256,
/// Internal error
pub error: EvmTestError,
/// 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.
@ -82,19 +107,19 @@ pub fn run_transaction<T: Informant>(
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<T: Informant>(
}
/// 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
F: FnOnce(EvmTestClient) -> Result<(U256, Vec<u8>), EvmTestError>,
pub fn run<'a, F, T, X>(
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>>,
{
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<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);
}
}
}
}
}

View File

@ -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<T: Informant>(args: Args, mut informant: T) {
fn run_call<T: Informant>(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<T: Informant>(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);
}

View File

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