facelift for traces, added errors (#2042)

* evm errors facelift

* facelift for traces, added errors with description

* additional tests for traces json serialization
This commit is contained in:
Marek Kotewicz 2016-09-05 11:56:44 +02:00 committed by Arkadiy Paronyan
parent 59f18ab958
commit da2c2e5fc6
12 changed files with 391 additions and 160 deletions

View File

@ -16,11 +16,13 @@
//! Evm interface. //! Evm interface.
use common::*; use std::{ops, cmp, fmt};
use util::{U128, U256, U512, Uint};
use action_params::ActionParams;
use evm::Ext; use evm::Ext;
/// Evm errors. /// Evm errors.
#[derive(Debug)] #[derive(Debug, Clone, Copy)]
pub enum Error { pub enum Error {
/// `OutOfGas` is returned when transaction execution runs out of gas. /// `OutOfGas` is returned when transaction execution runs out of gas.
/// The state should be reverted to the state from before the /// The state should be reverted to the state from before the
@ -63,6 +65,21 @@ pub enum Error {
Internal, Internal,
} }
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use self::Error::*;
let message = match *self {
OutOfGas => "Out of gas",
BadJumpDestination { .. } => "Bad jump destination",
BadInstruction { .. } => "Bad instruction",
StackUnderflow { .. } => "Stack underflow",
OutOfStack { .. } => "Out of stack",
Internal => "Internal error",
};
message.fmt(f)
}
}
/// A specialized version of Result over EVM errors. /// A specialized version of Result over EVM errors.
pub type Result<T> = ::std::result::Result<T, Error>; pub type Result<T> = ::std::result::Result<T, Error>;
@ -193,53 +210,55 @@ pub trait Evm {
fn exec(&mut self, params: ActionParams, ext: &mut Ext) -> Result<GasLeft>; fn exec(&mut self, params: ActionParams, ext: &mut Ext) -> Result<GasLeft>;
} }
#[test]
#[cfg(test)] #[cfg(test)]
fn should_calculate_overflow_mul_shr_without_overflow() { mod tests {
// given use util::{U256, Uint};
let num = 1048576; use super::CostType;
// when #[test]
let (res1, o1) = U256::from(num).overflow_mul_shr(U256::from(num), 20); fn should_calculate_overflow_mul_shr_without_overflow() {
let (res2, o2) = num.overflow_mul_shr(num, 20); // given
let num = 1048576;
// then // when
assert_eq!(res1, U256::from(num)); let (res1, o1) = U256::from(num).overflow_mul_shr(U256::from(num), 20);
assert!(!o1); let (res2, o2) = num.overflow_mul_shr(num, 20);
assert_eq!(res2, num);
assert!(!o2); // then
} assert_eq!(res1, U256::from(num));
assert!(!o1);
#[test] assert_eq!(res2, num);
#[cfg(test)] assert!(!o2);
fn should_calculate_overflow_mul_shr_with_overflow() { }
// given
let max = ::std::u64::MAX; #[test]
let num1 = U256([max, max, max, max]); fn should_calculate_overflow_mul_shr_with_overflow() {
let num2 = ::std::usize::MAX; // given
let max = u64::max_value();
// when let num1 = U256([max, max, max, max]);
let (res1, o1) = num1.overflow_mul_shr(num1, 256); let num2 = usize::max_value();
let (res2, o2) = num2.overflow_mul_shr(num2, 64);
// when
// then let (res1, o1) = num1.overflow_mul_shr(num1, 256);
assert_eq!(res2, num2 - 1); let (res2, o2) = num2.overflow_mul_shr(num2, 64);
assert!(o2);
// then
assert_eq!(res1, !U256::zero() - U256::one()); assert_eq!(res2, num2 - 1);
assert!(o1); assert!(o2);
}
assert_eq!(res1, !U256::zero() - U256::one());
#[test] assert!(o1);
#[cfg(test)] }
fn should_validate_u256_to_usize_conversion() {
// given #[test]
let v = U256::from(::std::usize::MAX) + U256::from(1); fn should_validate_u256_to_usize_conversion() {
// given
// when let v = U256::from(usize::max_value()) + U256::from(1);
let res = usize::from_u256(v);
// when
// then let res = usize::from_u256(v);
assert!(res.is_err());
// then
assert!(res.is_err());
}
} }

View File

@ -286,7 +286,7 @@ impl<'a> Executive<'a> {
// just drain the whole gas // just drain the whole gas
self.state.revert_snapshot(); self.state.revert_snapshot();
tracer.trace_failed_call(trace_info, vec![]); tracer.trace_failed_call(trace_info, vec![], evm::Error::OutOfGas.into());
Err(evm::Error::OutOfGas) Err(evm::Error::OutOfGas)
} }
@ -320,7 +320,7 @@ impl<'a> Executive<'a> {
trace_output, trace_output,
traces traces
), ),
_ => tracer.trace_failed_call(trace_info, traces), Err(e) => tracer.trace_failed_call(trace_info, traces, e.into()),
}; };
trace!(target: "executive", "substate={:?}; unconfirmed_substate={:?}\n", substate, unconfirmed_substate); trace!(target: "executive", "substate={:?}; unconfirmed_substate={:?}\n", substate, unconfirmed_substate);
@ -385,7 +385,7 @@ impl<'a> Executive<'a> {
created, created,
subtracer.traces() subtracer.traces()
), ),
_ => tracer.trace_failed_create(trace_info, subtracer.traces()) Err(e) => tracer.trace_failed_create(trace_info, subtracer.traces(), e.into())
}; };
self.enact_result(&res, substate, unconfirmed_substate); self.enact_result(&res, substate, unconfirmed_substate);

View File

@ -444,8 +444,7 @@ use env_info::*;
use spec::*; use spec::*;
use transaction::*; use transaction::*;
use util::log::init_log; use util::log::init_log;
use trace::trace; use trace::{FlatTrace, TraceError, trace};
use trace::FlatTrace;
use types::executed::CallType; use types::executed::CallType;
#[test] #[test]
@ -538,7 +537,7 @@ fn should_trace_failed_create_transaction() {
gas: 78792.into(), gas: 78792.into(),
init: vec![91, 96, 0, 86], init: vec![91, 96, 0, 86],
}), }),
result: trace::Res::FailedCreate, result: trace::Res::FailedCreate(TraceError::OutOfGas),
subtraces: 0 subtraces: 0
}]; }];
@ -869,7 +868,7 @@ fn should_trace_failed_call_transaction() {
input: vec![], input: vec![],
call_type: CallType::Call, call_type: CallType::Call,
}), }),
result: trace::Res::FailedCall, result: trace::Res::FailedCall(TraceError::OutOfGas),
subtraces: 0, subtraces: 0,
}]; }];
@ -1084,7 +1083,7 @@ fn should_trace_failed_subcall_transaction() {
input: vec![], input: vec![],
call_type: CallType::Call, call_type: CallType::Call,
}), }),
result: trace::Res::FailedCall, result: trace::Res::FailedCall(TraceError::OutOfGas),
}]; }];
assert_eq!(result.trace, expected_trace); assert_eq!(result.trace, expected_trace);
@ -1217,7 +1216,7 @@ fn should_trace_failed_subcall_with_subcall_transaction() {
input: vec![], input: vec![],
call_type: CallType::Call, call_type: CallType::Call,
}), }),
result: trace::Res::FailedCall, result: trace::Res::FailedCall(TraceError::OutOfGas),
}, FlatTrace { }, FlatTrace {
trace_address: vec![0, 0].into_iter().collect(), trace_address: vec![0, 0].into_iter().collect(),
subtraces: 0, subtraces: 0,

View File

@ -420,7 +420,7 @@ mod tests {
use devtools::RandomTempPath; use devtools::RandomTempPath;
use header::BlockNumber; use header::BlockNumber;
use trace::{Config, Switch, TraceDB, Database as TraceDatabase, DatabaseExtras, ImportRequest}; use trace::{Config, Switch, TraceDB, Database as TraceDatabase, DatabaseExtras, ImportRequest};
use trace::{Filter, LocalizedTrace, AddressesFilter}; use trace::{Filter, LocalizedTrace, AddressesFilter, TraceError};
use trace::trace::{Call, Action, Res}; use trace::trace::{Call, Action, Res};
use trace::flat::{FlatTrace, FlatBlockTraces, FlatTransactionTraces}; use trace::flat::{FlatTrace, FlatBlockTraces, FlatTransactionTraces};
use types::executed::CallType; use types::executed::CallType;
@ -560,7 +560,7 @@ mod tests {
input: vec![], input: vec![],
call_type: CallType::Call, call_type: CallType::Call,
}), }),
result: Res::FailedCall, result: Res::FailedCall(TraceError::OutOfGas),
}])]), }])]),
block_hash: block_hash.clone(), block_hash: block_hash.clone(),
block_number: block_number, block_number: block_number,
@ -579,7 +579,7 @@ mod tests {
input: vec![], input: vec![],
call_type: CallType::Call, call_type: CallType::Call,
}), }),
result: Res::FailedCall, result: Res::FailedCall(TraceError::OutOfGas),
trace_address: vec![], trace_address: vec![],
subtraces: 0, subtraces: 0,
transaction_number: 0, transaction_number: 0,

View File

@ -19,7 +19,7 @@
use util::{Bytes, Address, U256}; use util::{Bytes, Address, U256};
use action_params::ActionParams; use action_params::ActionParams;
use trace::trace::{Call, Create, Action, Res, CreateResult, CallResult, VMTrace, VMOperation, VMExecutedOperation, MemoryDiff, StorageDiff, Suicide}; use trace::trace::{Call, Create, Action, Res, CreateResult, CallResult, VMTrace, VMOperation, VMExecutedOperation, MemoryDiff, StorageDiff, Suicide};
use trace::{Tracer, VMTracer, FlatTrace}; use trace::{Tracer, VMTracer, FlatTrace, TraceError};
/// Simple executive tracer. Traces all calls and creates. Ignores delegatecalls. /// Simple executive tracer. Traces all calls and creates. Ignores delegatecalls.
#[derive(Default)] #[derive(Default)]
@ -112,23 +112,23 @@ impl Tracer for ExecutiveTracer {
self.traces.extend(update_trace_address(subs)); self.traces.extend(update_trace_address(subs));
} }
fn trace_failed_call(&mut self, call: Option<Call>, subs: Vec<FlatTrace>) { fn trace_failed_call(&mut self, call: Option<Call>, subs: Vec<FlatTrace>, error: TraceError) {
let trace = FlatTrace { let trace = FlatTrace {
trace_address: Default::default(), trace_address: Default::default(),
subtraces: top_level_subtraces(&subs), subtraces: top_level_subtraces(&subs),
action: Action::Call(call.expect("self.prepare_trace_call().is_some(): so we must be tracing: qed")), action: Action::Call(call.expect("self.prepare_trace_call().is_some(): so we must be tracing: qed")),
result: Res::FailedCall, result: Res::FailedCall(error),
}; };
debug!(target: "trace", "Traced failed call {:?}", trace); debug!(target: "trace", "Traced failed call {:?}", trace);
self.traces.push(trace); self.traces.push(trace);
self.traces.extend(update_trace_address(subs)); self.traces.extend(update_trace_address(subs));
} }
fn trace_failed_create(&mut self, create: Option<Create>, subs: Vec<FlatTrace>) { fn trace_failed_create(&mut self, create: Option<Create>, subs: Vec<FlatTrace>, error: TraceError) {
let trace = FlatTrace { let trace = FlatTrace {
subtraces: top_level_subtraces(&subs), subtraces: top_level_subtraces(&subs),
action: Action::Create(create.expect("self.prepare_trace_create().is_some(): so we must be tracing: qed")), action: Action::Create(create.expect("self.prepare_trace_create().is_some(): so we must be tracing: qed")),
result: Res::FailedCreate, result: Res::FailedCreate(error),
trace_address: Default::default(), trace_address: Default::default(),
}; };
debug!(target: "trace", "Traced failed create {:?}", trace); debug!(target: "trace", "Traced failed create {:?}", trace);

View File

@ -24,7 +24,8 @@ mod executive_tracer;
mod import; mod import;
mod noop_tracer; mod noop_tracer;
pub use types::trace_types::*; pub use types::trace_types::{filter, flat, localized, trace};
pub use types::trace_types::error::Error as TraceError;
pub use self::config::{Config, Switch}; pub use self::config::{Config, Switch};
pub use self::db::TraceDB; pub use self::db::TraceDB;
pub use self::error::Error; pub use self::error::Error;
@ -71,10 +72,10 @@ pub trait Tracer: Send {
); );
/// Stores failed call trace. /// Stores failed call trace.
fn trace_failed_call(&mut self, call: Option<Call>, subs: Vec<FlatTrace>); fn trace_failed_call(&mut self, call: Option<Call>, subs: Vec<FlatTrace>, error: TraceError);
/// Stores failed create trace. /// Stores failed create trace.
fn trace_failed_create(&mut self, create: Option<Create>, subs: Vec<FlatTrace>); fn trace_failed_create(&mut self, create: Option<Create>, subs: Vec<FlatTrace>, 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);

View File

@ -18,7 +18,7 @@
use util::{Bytes, Address, U256}; use util::{Bytes, Address, U256};
use action_params::ActionParams; use action_params::ActionParams;
use trace::{Tracer, VMTracer, FlatTrace}; use trace::{Tracer, VMTracer, FlatTrace, TraceError};
use trace::trace::{Call, Create, VMTrace}; use trace::trace::{Call, Create, VMTrace};
/// Nonoperative tracer. Does not trace anything. /// Nonoperative tracer. Does not trace anything.
@ -47,11 +47,11 @@ impl Tracer for NoopTracer {
assert!(code.is_none(), "self.prepare_trace_output().is_none(): so we can't be tracing: qed"); assert!(code.is_none(), "self.prepare_trace_output().is_none(): so we can't be tracing: qed");
} }
fn trace_failed_call(&mut self, call: Option<Call>, _: Vec<FlatTrace>) { fn trace_failed_call(&mut self, call: Option<Call>, _: Vec<FlatTrace>, _: TraceError) {
assert!(call.is_none(), "self.prepare_trace_call().is_none(): so we can't be tracing: qed"); assert!(call.is_none(), "self.prepare_trace_call().is_none(): so we can't be tracing: qed");
} }
fn trace_failed_create(&mut self, create: Option<Create>, _: Vec<FlatTrace>) { fn trace_failed_create(&mut self, create: Option<Create>, _: Vec<FlatTrace>, _: TraceError) {
assert!(create.is_none(), "self.prepare_trace_create().is_none(): so we can't be tracing: qed"); assert!(create.is_none(), "self.prepare_trace_create().is_none(): so we can't be tracing: qed");
} }

View File

@ -0,0 +1,99 @@
// Copyright 2015, 2016 Ethcore (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/>.
//! Trace errors.
use std::fmt;
use rlp::{Encodable, RlpStream, Decodable, Decoder, DecoderError, Stream, View};
use evm::Error as EvmError;
/// Trace evm errors.
#[derive(Debug, PartialEq, Clone, Binary)]
pub enum Error {
/// `OutOfGas` is returned when transaction execution runs out of gas.
OutOfGas,
/// `BadJumpDestination` is returned when execution tried to move
/// to position that wasn't marked with JUMPDEST instruction
BadJumpDestination,
/// `BadInstructions` is returned when given instruction is not supported
BadInstruction,
/// `StackUnderflow` when there is not enough stack elements to execute instruction
StackUnderflow,
/// When execution would exceed defined Stack Limit
OutOfStack,
/// Returned on evm internal error. Should never be ignored during development.
/// Likely to cause consensus issues.
Internal,
}
impl From<EvmError> for Error {
fn from(e: EvmError) -> Self {
match e {
EvmError::OutOfGas => Error::OutOfGas,
EvmError::BadJumpDestination { .. } => Error::BadJumpDestination,
EvmError::BadInstruction { .. } => Error::BadInstruction,
EvmError::StackUnderflow { .. } => Error::StackUnderflow,
EvmError::OutOfStack { .. } => Error::OutOfStack,
EvmError::Internal => Error::Internal,
}
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use self::Error::*;
let message = match *self {
OutOfGas => "Out of gas",
BadJumpDestination => "Bad jump destination",
BadInstruction => "Bad instruction",
StackUnderflow => "Stack underflow",
OutOfStack => "Out of stack",
Internal => "Internal error",
};
message.fmt(f)
}
}
impl Encodable for Error {
fn rlp_append(&self, s: &mut RlpStream) {
use self::Error::*;
let value = match *self {
OutOfGas => 0u8,
BadJumpDestination => 1,
BadInstruction => 2,
StackUnderflow => 3,
OutOfStack => 4,
Internal => 5,
};
s.append(&value);
}
}
impl Decodable for Error {
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
use self::Error::*;
let value: u8 = try!(decoder.as_rlp().as_val());
match value {
0 => Ok(OutOfGas),
1 => Ok(BadJumpDestination),
2 => Ok(BadInstruction),
3 => Ok(StackUnderflow),
4 => Ok(OutOfStack),
5 => Ok(Internal),
_ => Err(DecoderError::Custom("Invalid error type")),
}
}
}

View File

@ -140,7 +140,7 @@ mod tests {
use util::bloom::Bloomable; use util::bloom::Bloomable;
use trace::trace::{Action, Call, Res, Create, CreateResult, Suicide}; use trace::trace::{Action, Call, Res, Create, CreateResult, Suicide};
use trace::flat::FlatTrace; use trace::flat::FlatTrace;
use trace::{Filter, AddressesFilter}; use trace::{Filter, AddressesFilter, TraceError};
use types::executed::CallType; use types::executed::CallType;
#[test] #[test]
@ -286,7 +286,7 @@ mod tests {
input: vec![0x5], input: vec![0x5],
call_type: CallType::Call, call_type: CallType::Call,
}), }),
result: Res::FailedCall, result: Res::FailedCall(TraceError::OutOfGas),
trace_address: vec![0].into_iter().collect(), trace_address: vec![0].into_iter().collect(),
subtraces: 0, subtraces: 0,
}; };

View File

@ -16,6 +16,7 @@
//! Types used in the public api //! Types used in the public api
pub mod error;
pub mod filter; pub mod filter;
pub mod flat; pub mod flat;
pub mod trace; pub mod trace;

View File

@ -24,6 +24,7 @@ use rlp::*;
use action_params::ActionParams; use action_params::ActionParams;
use basic_types::LogBloom; use basic_types::LogBloom;
use types::executed::CallType; use types::executed::CallType;
use super::error::Error;
/// `Call` result. /// `Call` result.
#[derive(Debug, Clone, PartialEq, Default, Binary)] #[derive(Debug, Clone, PartialEq, Default, Binary)]
@ -322,9 +323,9 @@ pub enum Res {
/// Successful create action result. /// Successful create action result.
Create(CreateResult), Create(CreateResult),
/// Failed call. /// Failed call.
FailedCall, FailedCall(Error),
/// Failed create. /// Failed create.
FailedCreate, FailedCreate(Error),
/// None /// None
None, None,
} }
@ -342,13 +343,15 @@ impl Encodable for Res {
s.append(&1u8); s.append(&1u8);
s.append(create); s.append(create);
}, },
Res::FailedCall => { Res::FailedCall(ref err) => {
s.begin_list(1); s.begin_list(2);
s.append(&2u8); s.append(&2u8);
s.append(err);
}, },
Res::FailedCreate => { Res::FailedCreate(ref err) => {
s.begin_list(1); s.begin_list(2);
s.append(&3u8); s.append(&3u8);
s.append(err);
}, },
Res::None => { Res::None => {
s.begin_list(1); s.begin_list(1);
@ -365,8 +368,8 @@ impl Decodable for Res {
match action_type { match action_type {
0 => d.val_at(1).map(Res::Call), 0 => d.val_at(1).map(Res::Call),
1 => d.val_at(1).map(Res::Create), 1 => d.val_at(1).map(Res::Create),
2 => Ok(Res::FailedCall), 2 => d.val_at(1).map(Res::FailedCall),
3 => Ok(Res::FailedCreate), 3 => d.val_at(1).map(Res::FailedCreate),
4 => Ok(Res::None), 4 => Ok(Res::None),
_ => Err(DecoderError::Custom("Invalid result type.")), _ => Err(DecoderError::Custom("Invalid result type.")),
} }
@ -378,7 +381,7 @@ impl Res {
pub fn bloom(&self) -> LogBloom { pub fn bloom(&self) -> LogBloom {
match *self { match *self {
Res::Create(ref create) => create.bloom(), Res::Create(ref create) => create.bloom(),
Res::Call(_) | Res::FailedCall | Res::FailedCreate | Res::None => Default::default(), Res::Call(_) | Res::FailedCall(_) | Res::FailedCreate(_) | Res::None => Default::default(),
} }
} }
} }

View File

@ -16,8 +16,7 @@
use std::collections::BTreeMap; use std::collections::BTreeMap;
use serde::{Serialize, Serializer}; use serde::{Serialize, Serializer};
use ethcore::trace::trace; use ethcore::trace::{FlatTrace, LocalizedTrace as EthLocalizedTrace, trace, TraceError};
use ethcore::trace::{FlatTrace, LocalizedTrace as EthLocalizedTrace};
use ethcore::trace as et; use ethcore::trace as et;
use ethcore::state_diff; use ethcore::state_diff;
use ethcore::account_diff; use ethcore::account_diff;
@ -319,16 +318,13 @@ impl From<trace::Suicide> for Suicide {
} }
/// Action /// Action
#[derive(Debug, Serialize)] #[derive(Debug)]
pub enum Action { pub enum Action {
/// Call /// Call
#[serde(rename="call")]
Call(Call), Call(Call),
/// Create /// Create
#[serde(rename="create")]
Create(Create), Create(Create),
/// Suicide /// Suicide
#[serde(rename="suicide")]
Suicide(Suicide), Suicide(Suicide),
} }
@ -384,22 +380,17 @@ impl From<trace::CreateResult> for CreateResult {
} }
/// Response /// Response
#[derive(Debug, Serialize)] #[derive(Debug)]
pub enum Res { pub enum Res {
/// Call /// Call
#[serde(rename="call")]
Call(CallResult), Call(CallResult),
/// Create /// Create
#[serde(rename="create")]
Create(CreateResult), Create(CreateResult),
/// Call failure /// Call failure
#[serde(rename="failedCall")] FailedCall(TraceError),
FailedCall,
/// Creation failure /// Creation failure
#[serde(rename="failedCreate")] FailedCreate(TraceError),
FailedCreate,
/// None /// None
#[serde(rename="none")]
None, None,
} }
@ -408,39 +399,73 @@ impl From<trace::Res> for Res {
match t { match t {
trace::Res::Call(call) => Res::Call(CallResult::from(call)), trace::Res::Call(call) => Res::Call(CallResult::from(call)),
trace::Res::Create(create) => Res::Create(CreateResult::from(create)), trace::Res::Create(create) => Res::Create(CreateResult::from(create)),
trace::Res::FailedCall => Res::FailedCall, trace::Res::FailedCall(error) => Res::FailedCall(error),
trace::Res::FailedCreate => Res::FailedCreate, trace::Res::FailedCreate(error) => Res::FailedCreate(error),
trace::Res::None => Res::None, trace::Res::None => Res::None,
} }
} }
} }
/// Trace /// Trace
#[derive(Debug, Serialize)] #[derive(Debug)]
pub struct LocalizedTrace { pub struct LocalizedTrace {
/// Action /// Action
action: Action, action: Action,
/// Result /// Result
result: Res, result: Res,
/// Trace address /// Trace address
#[serde(rename="traceAddress")]
trace_address: Vec<U256>, trace_address: Vec<U256>,
/// Subtraces /// Subtraces
subtraces: U256, subtraces: U256,
/// Transaction position /// Transaction position
#[serde(rename="transactionPosition")]
transaction_position: U256, transaction_position: U256,
/// Transaction hash /// Transaction hash
#[serde(rename="transactionHash")]
transaction_hash: H256, transaction_hash: H256,
/// Block Number /// Block Number
#[serde(rename="blockNumber")]
block_number: U256, block_number: U256,
/// Block Hash /// Block Hash
#[serde(rename="blockHash")]
block_hash: H256, block_hash: H256,
} }
impl Serialize for LocalizedTrace {
fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
where S: Serializer
{
let mut state = try!(serializer.serialize_struct("LocalizedTrace", 9));
match self.action {
Action::Call(ref call) => {
try!(serializer.serialize_struct_elt(&mut state, "type", "call"));
try!(serializer.serialize_struct_elt(&mut state, "action", call));
},
Action::Create(ref create) => {
try!(serializer.serialize_struct_elt(&mut state, "type", "create"));
try!(serializer.serialize_struct_elt(&mut state, "action", create));
},
Action::Suicide(ref suicide) => {
try!(serializer.serialize_struct_elt(&mut state, "type", "suicide"));
try!(serializer.serialize_struct_elt(&mut state, "action", suicide));
},
}
match self.result {
Res::Call(ref call) => try!(serializer.serialize_struct_elt(&mut state, "result", call)),
Res::Create(ref create) => try!(serializer.serialize_struct_elt(&mut state, "result", create)),
Res::FailedCall(ref error) => try!(serializer.serialize_struct_elt(&mut state, "error", error.to_string())),
Res::FailedCreate(ref error) => try!(serializer.serialize_struct_elt(&mut state, "error", error.to_string())),
Res::None => try!(serializer.serialize_struct_elt(&mut state, "result", None as Option<u8>)),
}
try!(serializer.serialize_struct_elt(&mut state, "traceAddress", &self.trace_address));
try!(serializer.serialize_struct_elt(&mut state, "subtraces", &self.subtraces));
try!(serializer.serialize_struct_elt(&mut state, "transactionPosition", &self.transaction_position));
try!(serializer.serialize_struct_elt(&mut state, "transactionHash", &self.transaction_hash));
try!(serializer.serialize_struct_elt(&mut state, "blockNumber", &self.block_number));
try!(serializer.serialize_struct_elt(&mut state, "blockHash", &self.block_hash));
serializer.serialize_struct_end(state)
}
}
impl From<EthLocalizedTrace> for LocalizedTrace { impl From<EthLocalizedTrace> for LocalizedTrace {
fn from(t: EthLocalizedTrace) -> Self { fn from(t: EthLocalizedTrace) -> Self {
LocalizedTrace { LocalizedTrace {
@ -457,10 +482,9 @@ impl From<EthLocalizedTrace> for LocalizedTrace {
} }
/// Trace /// Trace
#[derive(Debug, Serialize)] #[derive(Debug)]
pub struct Trace { pub struct Trace {
/// Trace address /// Trace address
#[serde(rename="traceAddress")]
trace_address: Vec<U256>, trace_address: Vec<U256>,
/// Subtraces /// Subtraces
subtraces: U256, subtraces: U256,
@ -470,6 +494,41 @@ pub struct Trace {
result: Res, result: Res,
} }
impl Serialize for Trace {
fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
where S: Serializer
{
let mut state = try!(serializer.serialize_struct("Trace", 4));
match self.action {
Action::Call(ref call) => {
try!(serializer.serialize_struct_elt(&mut state, "type", "call"));
try!(serializer.serialize_struct_elt(&mut state, "action", call));
},
Action::Create(ref create) => {
try!(serializer.serialize_struct_elt(&mut state, "type", "create"));
try!(serializer.serialize_struct_elt(&mut state, "action", create));
},
Action::Suicide(ref suicide) => {
try!(serializer.serialize_struct_elt(&mut state, "type", "suicide"));
try!(serializer.serialize_struct_elt(&mut state, "action", suicide));
},
}
match self.result {
Res::Call(ref call) => try!(serializer.serialize_struct_elt(&mut state, "result", call)),
Res::Create(ref create) => try!(serializer.serialize_struct_elt(&mut state, "result", create)),
Res::FailedCall(ref error) => try!(serializer.serialize_struct_elt(&mut state, "error", error.to_string())),
Res::FailedCreate(ref error) => try!(serializer.serialize_struct_elt(&mut state, "error", error.to_string())),
Res::None => try!(serializer.serialize_struct_elt(&mut state, "result", None as Option<u8>)),
}
try!(serializer.serialize_struct_elt(&mut state, "traceAddress", &self.trace_address));
try!(serializer.serialize_struct_elt(&mut state, "subtraces", &self.subtraces));
serializer.serialize_struct_end(state)
}
}
impl From<FlatTrace> for Trace { impl From<FlatTrace> for Trace {
fn from(t: FlatTrace) -> Self { fn from(t: FlatTrace) -> Self {
Trace { Trace {
@ -511,7 +570,8 @@ impl From<Executed> for TraceResults {
mod tests { mod tests {
use serde_json; use serde_json;
use std::collections::BTreeMap; use std::collections::BTreeMap;
use v1::types::{Bytes, U256, H256, H160}; use v1::types::Bytes;
use ethcore::trace::TraceError;
use super::*; use super::*;
#[test] #[test]
@ -527,29 +587,118 @@ mod tests {
} }
#[test] #[test]
fn test_trace_serialize() { fn test_trace_call_serialize() {
let t = LocalizedTrace { let t = LocalizedTrace {
action: Action::Call(Call { action: Action::Call(Call {
from: H160::from(4), from: 4.into(),
to: H160::from(5), to: 5.into(),
value: U256::from(6), value: 6.into(),
gas: U256::from(7), gas: 7.into(),
input: Bytes::new(vec![0x12, 0x34]), input: Bytes::new(vec![0x12, 0x34]),
call_type: CallType::Call, call_type: CallType::Call,
}), }),
result: Res::Call(CallResult { result: Res::Call(CallResult {
gas_used: U256::from(8), gas_used: 8.into(),
output: vec![0x56, 0x78].into(), output: vec![0x56, 0x78].into(),
}), }),
trace_address: vec![U256::from(10)], trace_address: vec![10.into()],
subtraces: U256::from(1), subtraces: 1.into(),
transaction_position: U256::from(11), transaction_position: 11.into(),
transaction_hash: H256::from(12), transaction_hash: 12.into(),
block_number: U256::from(13), block_number: 13.into(),
block_hash: H256::from(14), block_hash: 14.into(),
}; };
let serialized = serde_json::to_string(&t).unwrap(); let serialized = serde_json::to_string(&t).unwrap();
assert_eq!(serialized, r#"{"action":{"call":{"from":"0x0000000000000000000000000000000000000004","to":"0x0000000000000000000000000000000000000005","value":"0x6","gas":"0x7","input":"0x1234","callType":"call"}},"result":{"call":{"gasUsed":"0x8","output":"0x5678"}},"traceAddress":["0xa"],"subtraces":"0x1","transactionPosition":"0xb","transactionHash":"0x000000000000000000000000000000000000000000000000000000000000000c","blockNumber":"0xd","blockHash":"0x000000000000000000000000000000000000000000000000000000000000000e"}"#); assert_eq!(serialized, r#"{"type":"call","action":{"from":"0x0000000000000000000000000000000000000004","to":"0x0000000000000000000000000000000000000005","value":"0x6","gas":"0x7","input":"0x1234","callType":"call"},"result":{"gasUsed":"0x8","output":"0x5678"},"traceAddress":["0xa"],"subtraces":"0x1","transactionPosition":"0xb","transactionHash":"0x000000000000000000000000000000000000000000000000000000000000000c","blockNumber":"0xd","blockHash":"0x000000000000000000000000000000000000000000000000000000000000000e"}"#);
}
#[test]
fn test_trace_failed_call_serialize() {
let t = LocalizedTrace {
action: Action::Call(Call {
from: 4.into(),
to: 5.into(),
value: 6.into(),
gas: 7.into(),
input: Bytes::new(vec![0x12, 0x34]),
call_type: CallType::Call,
}),
result: Res::FailedCall(TraceError::OutOfGas),
trace_address: vec![10.into()],
subtraces: 1.into(),
transaction_position: 11.into(),
transaction_hash: 12.into(),
block_number: 13.into(),
block_hash: 14.into(),
};
let serialized = serde_json::to_string(&t).unwrap();
assert_eq!(serialized, r#"{"type":"call","action":{"from":"0x0000000000000000000000000000000000000004","to":"0x0000000000000000000000000000000000000005","value":"0x6","gas":"0x7","input":"0x1234","callType":"call"},"error":"Out of gas","traceAddress":["0xa"],"subtraces":"0x1","transactionPosition":"0xb","transactionHash":"0x000000000000000000000000000000000000000000000000000000000000000c","blockNumber":"0xd","blockHash":"0x000000000000000000000000000000000000000000000000000000000000000e"}"#);
}
#[test]
fn test_trace_create_serialize() {
let t = LocalizedTrace {
action: Action::Create(Create {
from: 4.into(),
value: 6.into(),
gas: 7.into(),
init: Bytes::new(vec![0x12, 0x34]),
}),
result: Res::Create(CreateResult {
gas_used: 8.into(),
code: vec![0x56, 0x78].into(),
address: 0xff.into(),
}),
trace_address: vec![10.into()],
subtraces: 1.into(),
transaction_position: 11.into(),
transaction_hash: 12.into(),
block_number: 13.into(),
block_hash: 14.into(),
};
let serialized = serde_json::to_string(&t).unwrap();
assert_eq!(serialized, r#"{"type":"create","action":{"from":"0x0000000000000000000000000000000000000004","value":"0x6","gas":"0x7","init":"0x1234"},"result":{"gasUsed":"0x8","code":"0x5678","address":"0x00000000000000000000000000000000000000ff"},"traceAddress":["0xa"],"subtraces":"0x1","transactionPosition":"0xb","transactionHash":"0x000000000000000000000000000000000000000000000000000000000000000c","blockNumber":"0xd","blockHash":"0x000000000000000000000000000000000000000000000000000000000000000e"}"#);
}
#[test]
fn test_trace_failed_create_serialize() {
let t = LocalizedTrace {
action: Action::Create(Create {
from: 4.into(),
value: 6.into(),
gas: 7.into(),
init: Bytes::new(vec![0x12, 0x34]),
}),
result: Res::FailedCreate(TraceError::OutOfGas),
trace_address: vec![10.into()],
subtraces: 1.into(),
transaction_position: 11.into(),
transaction_hash: 12.into(),
block_number: 13.into(),
block_hash: 14.into(),
};
let serialized = serde_json::to_string(&t).unwrap();
assert_eq!(serialized, r#"{"type":"create","action":{"from":"0x0000000000000000000000000000000000000004","value":"0x6","gas":"0x7","init":"0x1234"},"error":"Out of gas","traceAddress":["0xa"],"subtraces":"0x1","transactionPosition":"0xb","transactionHash":"0x000000000000000000000000000000000000000000000000000000000000000c","blockNumber":"0xd","blockHash":"0x000000000000000000000000000000000000000000000000000000000000000e"}"#);
}
#[test]
fn test_trace_suicide_serialize() {
let t = LocalizedTrace {
action: Action::Suicide(Suicide {
address: 4.into(),
refund_address: 6.into(),
balance: 7.into(),
}),
result: Res::None,
trace_address: vec![10.into()],
subtraces: 1.into(),
transaction_position: 11.into(),
transaction_hash: 12.into(),
block_number: 13.into(),
block_hash: 14.into(),
};
let serialized = serde_json::to_string(&t).unwrap();
assert_eq!(serialized, r#"{"type":"suicide","action":{"address":"0x0000000000000000000000000000000000000004","refundAddress":"0x0000000000000000000000000000000000000006","balance":"0x7"},"result":null,"traceAddress":["0xa"],"subtraces":"0x1","transactionPosition":"0xb","transactionHash":"0x000000000000000000000000000000000000000000000000000000000000000c","blockNumber":"0xd","blockHash":"0x000000000000000000000000000000000000000000000000000000000000000e"}"#);
} }
#[test] #[test]
@ -616,44 +765,4 @@ mod tests {
let serialized = serde_json::to_string(&t).unwrap(); let serialized = serde_json::to_string(&t).unwrap();
assert_eq!(serialized, r#"{"0x000000000000000000000000000000000000002a":{"balance":"=","nonce":{"+":"0x1"},"code":"=","storage":{"0x000000000000000000000000000000000000000000000000000000000000002a":"="}},"0x0000000000000000000000000000000000000045":{"balance":"=","nonce":{"*":{"from":"0x1","to":"0x0"}},"code":{"-":"0x60"},"storage":{}}}"#); assert_eq!(serialized, r#"{"0x000000000000000000000000000000000000002a":{"balance":"=","nonce":{"+":"0x1"},"code":"=","storage":{"0x000000000000000000000000000000000000000000000000000000000000002a":"="}},"0x0000000000000000000000000000000000000045":{"balance":"=","nonce":{"*":{"from":"0x1","to":"0x0"}},"code":{"-":"0x60"},"storage":{}}}"#);
} }
#[test]
fn test_action_serialize() {
let actions = vec![Action::Call(Call {
from: H160::from(1),
to: H160::from(2),
value: U256::from(3),
gas: U256::from(4),
input: vec![0x12, 0x34].into(),
call_type: CallType::Call,
}), Action::Create(Create {
from: H160::from(5),
value: U256::from(6),
gas: U256::from(7),
init: vec![0x56, 0x78].into(),
})];
let serialized = serde_json::to_string(&actions).unwrap();
assert_eq!(serialized, r#"[{"call":{"from":"0x0000000000000000000000000000000000000001","to":"0x0000000000000000000000000000000000000002","value":"0x3","gas":"0x4","input":"0x1234","callType":"call"}},{"create":{"from":"0x0000000000000000000000000000000000000005","value":"0x6","gas":"0x7","init":"0x5678"}}]"#);
}
#[test]
fn test_result_serialize() {
let results = vec![
Res::Call(CallResult {
gas_used: U256::from(1),
output: vec![0x12, 0x34].into(),
}),
Res::Create(CreateResult {
gas_used: U256::from(2),
code: vec![0x45, 0x56].into(),
address: H160::from(3),
}),
Res::FailedCall,
Res::FailedCreate,
];
let serialized = serde_json::to_string(&results).unwrap();
assert_eq!(serialized, r#"[{"call":{"gasUsed":"0x1","output":"0x1234"}},{"create":{"gasUsed":"0x2","code":"0x4556","address":"0x0000000000000000000000000000000000000003"}},"failedCall","failedCreate"]"#);
}
} }