From 31373cf011482d5d026481944ec6d2c5b8c8d4d4 Mon Sep 17 00:00:00 2001 From: Marek Kotewicz Date: Fri, 22 Jul 2016 14:47:23 +0200 Subject: [PATCH 1/6] Suicides tracing (#1688) * tracing suicide * fixed #1635 * fixed typo --- ethcore/src/externalities.rs | 2 + ethcore/src/state.rs | 54 ++++++++++++++++++- ethcore/src/trace/executive_tracer.rs | 16 +++++- ethcore/src/trace/mod.rs | 3 ++ ethcore/src/trace/noop_tracer.rs | 3 ++ ethcore/src/types/trace_types/filter.rs | 36 ++++++++++--- ethcore/src/types/trace_types/trace.rs | 72 ++++++++++++++++++++++++- rpc/src/v1/types/trace.rs | 34 +++++++++++- 8 files changed, 209 insertions(+), 11 deletions(-) diff --git a/ethcore/src/externalities.rs b/ethcore/src/externalities.rs index 5eccc717b59..bdadaa764af 100644 --- a/ethcore/src/externalities.rs +++ b/ethcore/src/externalities.rs @@ -262,6 +262,8 @@ impl<'a, T, V> Ext for Externalities<'a, T, V> where T: 'a + Tracer, V: 'a + VMT trace!("Suiciding {} -> {} (xfer: {})", address, refund_address, balance); self.state.transfer_balance(&address, refund_address, &balance); } + + self.tracer.trace_suicide(address, balance, refund_address.clone(), self.depth + 1); self.substate.suicides.insert(address); } diff --git a/ethcore/src/state.rs b/ethcore/src/state.rs index 4b4e47ace1b..65758f613ff 100644 --- a/ethcore/src/state.rs +++ b/ethcore/src/state.rs @@ -217,7 +217,7 @@ impl State { /// Reset the code of account `a` so that it is `code`. pub fn reset_code(&mut self, a: &Address, code: Bytes) { self.require_or_from(a, true, || Account::new_contract(0.into(), self.account_start_nonce), |_|{}).reset_code(code); - } + } /// Execute a given transaction. /// This will change the state accordingly. @@ -1141,6 +1141,58 @@ fn should_trace_failed_subcall_with_subcall_transaction() { assert_eq!(result.trace, expected_trace); } +#[test] +fn should_trace_suicide() { + init_log(); + + let temp = RandomTempPath::new(); + let mut state = get_temp_state_in(temp.as_path()); + + let mut info = EnvInfo::default(); + info.gas_limit = 1_000_000.into(); + let engine = TestEngine::new(5); + + let t = Transaction { + nonce: 0.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Call(0xa.into()), + value: 100.into(), + data: vec![], + }.sign(&"".sha3()); + + state.init_code(&0xa.into(), FromHex::from_hex("73000000000000000000000000000000000000000bff").unwrap()); + state.add_balance(&0xa.into(), &50.into()); + state.add_balance(t.sender().as_ref().unwrap(), &100.into()); + let vm_factory = Default::default(); + let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap(); + let expected_trace = Some(Trace { + depth: 0, + action: trace::Action::Call(trace::Call { + from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), + to: 0xa.into(), + value: 100.into(), + gas: 79000.into(), + input: vec![], + }), + result: trace::Res::Call(trace::CallResult { + gas_used: 3.into(), + output: vec![] + }), + subs: vec![Trace { + depth: 1, + action: trace::Action::Suicide(trace::Suicide { + address: 0xa.into(), + refund_address: 0xb.into(), + balance: 150.into(), + }), + result: trace::Res::None, + subs: vec![] + }] + }); + assert_eq!(result.trace, expected_trace); +} + #[test] fn code_from_database() { let a = Address::zero(); diff --git a/ethcore/src/trace/executive_tracer.rs b/ethcore/src/trace/executive_tracer.rs index ff7c6a62fb3..af8183c0a70 100644 --- a/ethcore/src/trace/executive_tracer.rs +++ b/ethcore/src/trace/executive_tracer.rs @@ -18,7 +18,7 @@ use util::{Bytes, Address, U256}; use action_params::ActionParams; -use trace::trace::{Trace, Call, Create, Action, Res, CreateResult, CallResult, VMTrace, VMOperation, VMExecutedOperation, MemoryDiff, StorageDiff}; +use trace::trace::{Trace, Call, Create, Action, Res, CreateResult, CallResult, VMTrace, VMOperation, VMExecutedOperation, MemoryDiff, StorageDiff, Suicide}; use trace::{Tracer, VMTracer}; /// Simple executive tracer. Traces all calls and creates. Ignores delegatecalls. @@ -97,6 +97,20 @@ impl Tracer for ExecutiveTracer { self.traces.push(trace); } + fn trace_suicide(&mut self, address: Address, balance: U256, refund_address: Address, depth: usize) { + let trace = Trace { + depth: depth, + subs: vec![], + action: Action::Suicide(Suicide { + address: address, + refund_address: refund_address, + balance: balance, + }), + result: Res::None, + }; + self.traces.push(trace); + } + fn subtracer(&self) -> Self { ExecutiveTracer::default() } diff --git a/ethcore/src/trace/mod.rs b/ethcore/src/trace/mod.rs index 8f6c0b3f6b6..67fec2b97fc 100644 --- a/ethcore/src/trace/mod.rs +++ b/ethcore/src/trace/mod.rs @@ -81,6 +81,9 @@ pub trait Tracer: Send { /// Stores failed create trace. fn trace_failed_create(&mut self, create: Option, depth: usize, subs: Vec); + /// Stores suicide info. + fn trace_suicide(&mut self, address: Address, balance: U256, refund_address: Address, depth: usize); + /// Spawn subtracer which will be used to trace deeper levels of execution. fn subtracer(&self) -> Self where Self: Sized; diff --git a/ethcore/src/trace/noop_tracer.rs b/ethcore/src/trace/noop_tracer.rs index b6e6ca9fdec..290fb236731 100644 --- a/ethcore/src/trace/noop_tracer.rs +++ b/ethcore/src/trace/noop_tracer.rs @@ -55,6 +55,9 @@ impl Tracer for NoopTracer { assert!(create.is_none(), "self.prepare_trace_create().is_none(): so we can't be tracing: qed"); } + fn trace_suicide(&mut self, _address: Address, _balance: U256, _refund_address: Address, _depth: usize) { + } + fn subtracer(&self) -> Self { NoopTracer } diff --git a/ethcore/src/types/trace_types/filter.rs b/ethcore/src/types/trace_types/filter.rs index d57efa5f38a..91d1c421df0 100644 --- a/ethcore/src/types/trace_types/filter.rs +++ b/ethcore/src/types/trace_types/filter.rs @@ -121,15 +121,20 @@ impl Filter { let to_matches = self.to_address.matches_all(); from_matches && to_matches } + Action::Suicide(ref suicide) => { + let from_matches = self.from_address.matches(&suicide.address); + let to_matches = self.to_address.matches(&suicide.refund_address); + from_matches && to_matches + } } } } #[cfg(test)] mod tests { - use util::{FixedHash, Address, U256}; + use util::{FixedHash, Address}; use util::sha3::Hashable; - use trace::trace::{Action, Call, Res}; + use trace::trace::{Action, Call, Res, Suicide}; use trace::flat::FlatTrace; use trace::{Filter, AddressesFilter}; use basic_types::LogBloom; @@ -270,10 +275,10 @@ mod tests { let trace = FlatTrace { action: Action::Call(Call { - from: Address::from(1), - to: Address::from(2), - value: U256::from(3), - gas: U256::from(4), + from: 1.into(), + to: 2.into(), + value: 3.into(), + gas: 4.into(), input: vec![0x5], }), result: Res::FailedCall, @@ -288,5 +293,24 @@ mod tests { assert!(f4.matches(&trace)); assert!(f5.matches(&trace)); assert!(!f6.matches(&trace)); + + let trace = FlatTrace { + action: Action::Suicide(Suicide { + address: 1.into(), + refund_address: 2.into(), + balance: 3.into(), + }), + result: Res::None, + trace_address: vec![], + subtraces: 0 + }; + + assert!(f0.matches(&trace)); + assert!(f1.matches(&trace)); + assert!(f2.matches(&trace)); + assert!(f3.matches(&trace)); + assert!(f4.matches(&trace)); + assert!(f5.matches(&trace)); + assert!(!f6.matches(&trace)); } } diff --git a/ethcore/src/types/trace_types/trace.rs b/ethcore/src/types/trace_types/trace.rs index 243acaf64f7..db90e068c8f 100644 --- a/ethcore/src/types/trace_types/trace.rs +++ b/ethcore/src/types/trace_types/trace.rs @@ -205,6 +205,48 @@ impl Create { } } +/// Suicide action. +#[derive(Debug, Clone, PartialEq, Binary)] +pub struct Suicide { + /// Suicided address. + pub address: Address, + /// Suicided contract heir. + pub refund_address: Address, + /// Balance of the contract just before suicide. + pub balance: U256, +} + +impl Suicide { + /// Return suicide action bloom. + pub fn bloom(&self) -> LogBloom { + LogBloom::from_bloomed(&self.address.sha3()) + .with_bloomed(&self.refund_address.sha3()) + } +} + +impl Encodable for Suicide { + fn rlp_append(&self, s: &mut RlpStream) { + s.begin_list(3); + s.append(&self.address); + s.append(&self.refund_address); + s.append(&self.balance); + } +} + +impl Decodable for Suicide { + fn decode(decoder: &D) -> Result where D: Decoder { + let d = decoder.as_rlp(); + let res = Suicide { + address: try!(d.val_at(0)), + refund_address: try!(d.val_at(1)), + balance: try!(d.val_at(3)), + }; + + Ok(res) + } +} + + /// Description of an action that we trace; will be either a call or a create. #[derive(Debug, Clone, PartialEq, Binary)] pub enum Action { @@ -212,6 +254,8 @@ pub enum Action { Call(Call), /// It's a create action. Create(Create), + /// Suicide. + Suicide(Suicide), } impl Encodable for Action { @@ -225,6 +269,10 @@ impl Encodable for Action { Action::Create(ref create) => { s.append(&1u8); s.append(create); + }, + Action::Suicide(ref suicide) => { + s.append(&2u8); + s.append(suicide); } } } @@ -237,6 +285,7 @@ impl Decodable for Action { match action_type { 0 => d.val_at(1).map(Action::Call), 1 => d.val_at(1).map(Action::Create), + 2 => d.val_at(2).map(Action::Suicide), _ => Err(DecoderError::Custom("Invalid action type.")), } } @@ -248,6 +297,7 @@ impl Action { match *self { Action::Call(ref call) => call.bloom(), Action::Create(ref create) => create.bloom(), + Action::Suicide(ref suicide) => suicide.bloom(), } } } @@ -263,6 +313,8 @@ pub enum Res { FailedCall, /// Failed create. FailedCreate, + /// None + None, } impl Encodable for Res { @@ -285,6 +337,10 @@ impl Encodable for Res { Res::FailedCreate => { s.begin_list(1); s.append(&3u8); + }, + Res::None => { + s.begin_list(1); + s.append(&4u8); } } } @@ -299,6 +355,7 @@ impl Decodable for Res { 1 => d.val_at(1).map(Res::Create), 2 => Ok(Res::FailedCall), 3 => Ok(Res::FailedCreate), + 4 => Ok(Res::None), _ => Err(DecoderError::Custom("Invalid result type.")), } } @@ -518,7 +575,7 @@ mod tests { use util::{Address, U256, FixedHash}; use util::rlp::{encode, decode}; use util::sha3::Hashable; - use trace::trace::{Call, CallResult, Create, Res, Action, Trace}; + use trace::trace::{Call, CallResult, Create, Res, Action, Trace, Suicide}; #[test] fn traces_rlp() { @@ -577,6 +634,16 @@ mod tests { }), subs: vec![], result: Res::FailedCreate + }, + Trace { + depth: 3, + action: Action::Suicide(Suicide { + address: 101.into(), + refund_address: 102.into(), + balance: 0.into(), + }), + subs: vec![], + result: Res::None, } ], result: Res::Call(CallResult { @@ -592,5 +659,8 @@ mod tests { assert!(bloom.contains_bloomed(&Address::from(2).sha3())); assert!(!bloom.contains_bloomed(&Address::from(20).sha3())); assert!(bloom.contains_bloomed(&Address::from(6).sha3())); + assert!(bloom.contains_bloomed(&Address::from(101).sha3())); + assert!(bloom.contains_bloomed(&Address::from(102).sha3())); + assert!(!bloom.contains_bloomed(&Address::from(103).sha3())); } } diff --git a/rpc/src/v1/types/trace.rs b/rpc/src/v1/types/trace.rs index d6226aea093..c1e2643367b 100644 --- a/rpc/src/v1/types/trace.rs +++ b/rpc/src/v1/types/trace.rs @@ -260,6 +260,28 @@ impl From for Call { } } +/// Suicide +#[derive(Debug, Serialize)] +pub struct Suicide { + /// Address. + pub address: H160, + /// Refund address. + #[serde(rename="refundAddress")] + pub refund_address: H160, + /// Balance. + pub balance: U256, +} + +impl From for Suicide { + fn from(s: trace::Suicide) -> Self { + Suicide { + address: s.address.into(), + refund_address: s.refund_address.into(), + balance: s.balance.into(), + } + } +} + /// Action #[derive(Debug, Serialize)] pub enum Action { @@ -269,13 +291,17 @@ pub enum Action { /// Create #[serde(rename="create")] Create(Create), + /// Suicide + #[serde(rename="suicide")] + Suicide(Suicide), } impl From for Action { fn from(c: trace::Action) -> Self { match c { - trace::Action::Call(call) => Action::Call(Call::from(call)), - trace::Action::Create(create) => Action::Create(Create::from(create)), + trace::Action::Call(call) => Action::Call(call.into()), + trace::Action::Create(create) => Action::Create(create.into()), + trace::Action::Suicide(suicide) => Action::Suicide(suicide.into()), } } } @@ -336,6 +362,9 @@ pub enum Res { /// Creation failure #[serde(rename="failedCreate")] FailedCreate, + /// None + #[serde(rename="none")] + None, } impl From for Res { @@ -345,6 +374,7 @@ impl From for Res { trace::Res::Create(create) => Res::Create(CreateResult::from(create)), trace::Res::FailedCall => Res::FailedCall, trace::Res::FailedCreate => Res::FailedCreate, + trace::Res::None => Res::None, } } } From 22af46b9aab2e887065cf9cad2a6782f2086dfe8 Mon Sep 17 00:00:00 2001 From: Marek Kotewicz Date: Sun, 24 Jul 2016 00:20:21 +0200 Subject: [PATCH 2/6] Stackoverflow #1686 (#1698) * flat trace serialization * tracing finds transaction which creates contract * flatten traces before inserting them to the db --- ethcore/src/trace/db.rs | 15 ++-- ethcore/src/trace/flat.rs | 99 +++++++++++++++++++++++++ ethcore/src/types/trace_types/filter.rs | 2 +- 3 files changed, 106 insertions(+), 10 deletions(-) diff --git a/ethcore/src/trace/db.rs b/ethcore/src/trace/db.rs index 5e5ddefde21..eb8eb694f02 100644 --- a/ethcore/src/trace/db.rs +++ b/ethcore/src/trace/db.rs @@ -40,7 +40,7 @@ enum TraceDBIndex { BloomGroups = 1, } -impl Key for H256 { +impl Key for H256 { type Target = H264; fn key(&self) -> H264 { @@ -91,7 +91,7 @@ impl Key for TraceGroupPosition { /// Trace database. pub struct TraceDB where T: DatabaseExtras { // cache - traces: RwLock>, + traces: RwLock>, blooms: RwLock>, // db tracesdb: Database, @@ -153,15 +153,13 @@ impl TraceDB where T: DatabaseExtras { } /// Returns traces for block with hash. - fn traces(&self, block_hash: &H256) -> Option { + fn traces(&self, block_hash: &H256) -> Option { self.tracesdb.read_with_cache(&self.traces, block_hash) } /// Returns vector of transaction traces for given block. fn transactions_traces(&self, block_hash: &H256) -> Option> { - self.traces(block_hash) - .map(FlatBlockTraces::from) - .map(Into::into) + self.traces(block_hash).map(Into::into) } fn matching_block_traces( @@ -232,7 +230,7 @@ impl TraceDatabase for TraceDB where T: DatabaseExtras { let mut traces = self.traces.write().unwrap(); // it's important to use overwrite here, // cause this value might be queried by hash later - batch.write_with_cache(traces.deref_mut(), request.block_hash, request.traces, CacheUpdatePolicy::Overwrite); + batch.write_with_cache(traces.deref_mut(), request.block_hash, request.traces.into(), CacheUpdatePolicy::Overwrite); } // now let's rebuild the blooms @@ -353,8 +351,7 @@ impl TraceDatabase for TraceDB where T: DatabaseExtras { .expect("Expected to find block hash. Extras db is probably corrupted"); let traces = self.traces(&hash) .expect("Expected to find a trace. Db is probably corrupted."); - let flat_block = FlatBlockTraces::from(traces); - self.matching_block_traces(filter, flat_block, hash, number) + self.matching_block_traces(filter, traces, hash, number) }) .collect() } diff --git a/ethcore/src/trace/flat.rs b/ethcore/src/trace/flat.rs index f9d3b083178..f39af4423d8 100644 --- a/ethcore/src/trace/flat.rs +++ b/ethcore/src/trace/flat.rs @@ -16,12 +16,15 @@ //! Flat trace module +use util::rlp::*; use trace::BlockTraces; +use basic_types::LogBloom; use super::trace::{Trace, Action, Res}; /// Trace localized in vector of traces produced by a single transaction. /// /// Parent and children indexes refer to positions in this vector. +#[derive(Debug, PartialEq, Clone)] pub struct FlatTrace { /// Type of action performed by a transaction. pub action: Action, @@ -35,9 +38,59 @@ pub struct FlatTrace { pub trace_address: Vec, } +impl FlatTrace { + /// Returns bloom of the trace. + pub fn bloom(&self) -> LogBloom { + self.action.bloom() | self.result.bloom() + } +} + +impl Encodable for FlatTrace { + fn rlp_append(&self, s: &mut RlpStream) { + s.begin_list(4); + s.append(&self.action); + s.append(&self.result); + s.append(&self.subtraces); + s.append(&self.trace_address); + } +} + +impl Decodable for FlatTrace { + fn decode(decoder: &D) -> Result where D: Decoder { + let d = decoder.as_rlp(); + let res = FlatTrace { + action: try!(d.val_at(0)), + result: try!(d.val_at(1)), + subtraces: try!(d.val_at(2)), + trace_address: try!(d.val_at(3)), + }; + + Ok(res) + } +} + /// Represents all traces produced by a single transaction. +#[derive(Debug, PartialEq, Clone)] pub struct FlatTransactionTraces(Vec); +impl FlatTransactionTraces { + pub fn bloom(&self) -> LogBloom { + self.0.iter().fold(Default::default(), | bloom, trace | bloom | trace.bloom()) + } +} + +impl Encodable for FlatTransactionTraces { + fn rlp_append(&self, s: &mut RlpStream) { + s.append(&self.0); + } +} + +impl Decodable for FlatTransactionTraces { + fn decode(decoder: &D) -> Result where D: Decoder { + Ok(FlatTransactionTraces(try!(Decodable::decode(decoder)))) + } +} + impl Into> for FlatTransactionTraces { fn into(self) -> Vec { self.0 @@ -45,8 +98,27 @@ impl Into> for FlatTransactionTraces { } /// Represents all traces produced by transactions in a single block. +#[derive(Debug, PartialEq, Clone)] pub struct FlatBlockTraces(Vec); +impl FlatBlockTraces { + pub fn bloom(&self) -> LogBloom { + self.0.iter().fold(Default::default(), | bloom, tx_traces | bloom | tx_traces.bloom()) + } +} + +impl Encodable for FlatBlockTraces { + fn rlp_append(&self, s: &mut RlpStream) { + s.append(&self.0); + } +} + +impl Decodable for FlatBlockTraces { + fn decode(decoder: &D) -> Result where D: Decoder { + Ok(FlatBlockTraces(try!(Decodable::decode(decoder)))) + } +} + impl From for FlatBlockTraces { fn from(block_traces: BlockTraces) -> Self { let traces: Vec = block_traces.into(); @@ -180,4 +252,31 @@ mod tests { assert_eq!(ordered_traces[4].trace_address, vec![1]); assert_eq!(ordered_traces[4].subtraces, 0); } + + #[test] + fn test_trace_serialization() { + use util::rlp; + + let flat_trace = FlatTrace { + action: Action::Call(Call { + from: 1.into(), + to: 2.into(), + value: 3.into(), + gas: 4.into(), + input: vec![0x5] + }), + result: Res::Call(CallResult { + gas_used: 10.into(), + output: vec![0x11, 0x12] + }), + trace_address: Vec::new(), + subtraces: 0, + }; + + let block_traces = FlatBlockTraces(vec![FlatTransactionTraces(vec![flat_trace])]); + + let encoded = rlp::encode(&block_traces); + let decoded = rlp::decode(&encoded); + assert_eq!(block_traces, decoded); + } } diff --git a/ethcore/src/types/trace_types/filter.rs b/ethcore/src/types/trace_types/filter.rs index 91d1c421df0..c01847e4335 100644 --- a/ethcore/src/types/trace_types/filter.rs +++ b/ethcore/src/types/trace_types/filter.rs @@ -120,7 +120,7 @@ impl Filter { let from_matches = self.from_address.matches(&create.from); let to_matches = self.to_address.matches_all(); from_matches && to_matches - } + }, Action::Suicide(ref suicide) => { let from_matches = self.from_address.matches(&suicide.address); let to_matches = self.to_address.matches(&suicide.refund_address); From 692d1fc0324451a67494965f57202389b979d8be Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Wed, 27 Jul 2016 17:41:21 +0200 Subject: [PATCH 3/6] Trace other types of calls (#1727) * Trace through DELEGATECALL and CALLCODE Add them to the JSON output and RLP database store. * Fix tests. * Fix all tests. * Fix one more test. --- ethcore/src/action_params.rs | 13 ++++-- ethcore/src/evm/ext.rs | 17 +++++--- ethcore/src/evm/interpreter.rs | 11 ++--- ethcore/src/evm/tests.rs | 7 ++- ethcore/src/executive.rs | 23 +++++----- ethcore/src/externalities.rs | 11 ++++- ethcore/src/json_tests/executive.rs | 17 +++++--- ethcore/src/state.rs | 58 +++++++++++++++++++++++-- ethcore/src/trace/db.rs | 3 ++ ethcore/src/trace/executive_tracer.rs | 14 +----- ethcore/src/trace/flat.rs | 7 ++- ethcore/src/trace/mod.rs | 5 +-- ethcore/src/trace/noop_tracer.rs | 4 +- ethcore/src/types/executed.rs | 47 ++++++++++++++++++++ ethcore/src/types/trace_types/filter.rs | 2 + ethcore/src/types/trace_types/trace.rs | 17 ++++++-- rpc/src/v1/types/trace.rs | 43 ++++++++++++++++-- 17 files changed, 232 insertions(+), 67 deletions(-) diff --git a/ethcore/src/action_params.rs b/ethcore/src/action_params.rs index 57100b2c599..1886c3d369a 100644 --- a/ethcore/src/action_params.rs +++ b/ethcore/src/action_params.rs @@ -17,6 +17,7 @@ //! Evm input params. use common::*; use ethjson; +use types::executed::CallType; /// Transaction value #[derive(Clone, Debug)] @@ -58,7 +59,10 @@ pub struct ActionParams { /// Code being executed. pub code: Option, /// Input data. - pub data: Option + pub data: Option, + /// Type of call + pub call_type: CallType, + } impl Default for ActionParams { @@ -73,16 +77,18 @@ impl Default for ActionParams { gas_price: U256::zero(), value: ActionValue::Transfer(U256::zero()), code: None, - data: None + data: None, + call_type: CallType::None, } } } impl From for ActionParams { fn from(t: ethjson::vm::Transaction) -> Self { + let address: Address = t.address.into(); ActionParams { code_address: Address::new(), - address: t.address.into(), + address: address, sender: t.sender.into(), origin: t.origin.into(), code: Some(t.code.into()), @@ -90,6 +96,7 @@ impl From for ActionParams { gas: t.gas.into(), gas_price: t.gas_price.into(), value: ActionValue::Transfer(t.value.into()), + call_type: match address.is_zero() { true => CallType::None, false => CallType::Call }, // TODO @debris is this correct? } } } diff --git a/ethcore/src/evm/ext.rs b/ethcore/src/evm/ext.rs index 0aaa4dac68a..ffc1887dead 100644 --- a/ethcore/src/evm/ext.rs +++ b/ethcore/src/evm/ext.rs @@ -18,6 +18,7 @@ use util::common::*; use evm::{self, Schedule}; +use types::executed::CallType; use env_info::*; /// Result of externalities create function. @@ -69,13 +70,15 @@ pub trait Ext { /// and true if subcall was successfull. #[cfg_attr(feature="dev", allow(too_many_arguments))] fn call(&mut self, - gas: &U256, - sender_address: &Address, - receive_address: &Address, - value: Option, - data: &[u8], - code_address: &Address, - output: &mut [u8]) -> MessageCallResult; + gas: &U256, + sender_address: &Address, + receive_address: &Address, + value: Option, + data: &[u8], + code_address: &Address, + output: &mut [u8], + call_type: CallType + ) -> MessageCallResult; /// Returns code at given address fn extcode(&self, address: &Address) -> Bytes; diff --git a/ethcore/src/evm/interpreter.rs b/ethcore/src/evm/interpreter.rs index 5b97b66afdc..ef5b5693714 100644 --- a/ethcore/src/evm/interpreter.rs +++ b/ethcore/src/evm/interpreter.rs @@ -20,6 +20,7 @@ use common::*; use super::instructions as instructions; use super::instructions::{Instruction, get_info}; use std::marker::Copy; +use types::executed::CallType; use evm::{self, MessageCallResult, ContractCreateResult, GasLeft}; #[cfg(not(feature = "evm-debug"))] @@ -648,16 +649,16 @@ impl Interpreter { }); // Get sender & receive addresses, check if we have balance - let (sender_address, receive_address, has_balance) = match instruction { + let (sender_address, receive_address, has_balance, call_type) = match instruction { instructions::CALL => { let has_balance = ext.balance(¶ms.address) >= value.unwrap(); - (¶ms.address, &code_address, has_balance) + (¶ms.address, &code_address, has_balance, CallType::Call) }, instructions::CALLCODE => { let has_balance = ext.balance(¶ms.address) >= value.unwrap(); - (¶ms.address, ¶ms.address, has_balance) + (¶ms.address, ¶ms.address, has_balance, CallType::CallCode) }, - instructions::DELEGATECALL => (¶ms.sender, ¶ms.address, true), + instructions::DELEGATECALL => (¶ms.sender, ¶ms.address, true, CallType::DelegateCall), _ => panic!(format!("Unexpected instruction {} in CALL branch.", instruction)) }; @@ -672,7 +673,7 @@ impl Interpreter { // and we don't want to copy let input = unsafe { ::std::mem::transmute(self.mem.read_slice(in_off, in_size)) }; let output = self.mem.writeable_slice(out_off, out_size); - ext.call(&call_gas, sender_address, receive_address, value, input, &code_address, output) + ext.call(&call_gas, sender_address, receive_address, value, input, &code_address, output, call_type) }; return match call_result { diff --git a/ethcore/src/evm/tests.rs b/ethcore/src/evm/tests.rs index ba156e6dd01..b00b1e0dee1 100644 --- a/ethcore/src/evm/tests.rs +++ b/ethcore/src/evm/tests.rs @@ -15,6 +15,7 @@ // along with Parity. If not, see . use common::*; +use types::executed::CallType; use evm::{self, Ext, Schedule, Factory, GasLeft, VMType, ContractCreateResult, MessageCallResult}; use std::fmt::Debug; @@ -36,7 +37,7 @@ struct FakeCall { receive_address: Option
, value: Option, data: Bytes, - code_address: Option
+ code_address: Option
, } /// Fake externalities test structure. @@ -119,7 +120,9 @@ impl Ext for FakeExt { value: Option, data: &[u8], code_address: &Address, - _output: &mut [u8]) -> MessageCallResult { + _output: &mut [u8], + _call_type: CallType + ) -> MessageCallResult { self.calls.insert(FakeCall { call_type: FakeCallType::Call, diff --git a/ethcore/src/executive.rs b/ethcore/src/executive.rs index 4525d6a2412..137d3b78ff6 100644 --- a/ethcore/src/executive.rs +++ b/ethcore/src/executive.rs @@ -18,6 +18,7 @@ use common::*; use state::*; use engine::*; +use types::executed::CallType; use evm::{self, Ext, Factory, Finalize}; use externalities::*; use substate::*; @@ -173,6 +174,7 @@ impl<'a> Executive<'a> { value: ActionValue::Transfer(t.value), code: Some(t.data.clone()), data: None, + call_type: CallType::None, }; (self.create(params, &mut substate, &mut tracer, &mut vm_tracer), vec![]) }, @@ -187,6 +189,7 @@ impl<'a> Executive<'a> { value: ActionValue::Transfer(t.value), code: self.state.code(address), data: Some(t.data.clone()), + call_type: CallType::Call, }; // TODO: move output upstream let mut out = vec![]; @@ -248,8 +251,6 @@ impl<'a> Executive<'a> { } trace!("Executive::call(params={:?}) self.env_info={:?}", params, self.info); - let delegate_call = params.code_address != params.address; - if self.engine.is_builtin(¶ms.code_address) { // if destination is builtin, try to execute it @@ -276,19 +277,15 @@ impl<'a> Executive<'a> { cost, trace_output, self.depth, - vec![], - delegate_call + vec![] ); } - Ok(params.gas - cost) }, // just drain the whole gas false => { self.state.revert_snapshot(); - - tracer.trace_failed_call(trace_info, self.depth, vec![], delegate_call); - + tracer.trace_failed_call(trace_info, self.depth, vec![]); Err(evm::Error::OutOfGas) } } @@ -321,10 +318,9 @@ impl<'a> Executive<'a> { gas - gas_left, trace_output, self.depth, - traces, - delegate_call + traces ), - _ => tracer.trace_failed_call(trace_info, self.depth, traces, delegate_call), + _ => tracer.trace_failed_call(trace_info, self.depth, traces), }; trace!(target: "executive", "substate={:?}; unconfirmed_substate={:?}\n", substate, unconfirmed_substate); @@ -336,7 +332,7 @@ impl<'a> Executive<'a> { // otherwise it's just a basic transaction, only do tracing, if necessary. self.state.clear_snapshot(); - tracer.trace_call(trace_info, U256::zero(), trace_output, self.depth, vec![], delegate_call); + tracer.trace_call(trace_info, U256::zero(), trace_output, self.depth, vec![]); Ok(params.gas) } } @@ -498,6 +494,7 @@ mod tests { use trace::trace; use trace::{Trace, Tracer, NoopTracer, ExecutiveTracer}; use trace::{VMTrace, VMOperation, VMExecutedOperation, MemoryDiff, StorageDiff, VMTracer, NoopVMTracer, ExecutiveVMTracer}; + use types::executed::CallType; #[test] fn test_contract_address() { @@ -631,6 +628,7 @@ mod tests { params.gas = U256::from(100_000); params.code = Some(code.clone()); params.value = ActionValue::Transfer(U256::from(100)); + params.call_type = CallType::Call; let mut state_result = get_temp_state(); let mut state = state_result.reference_mut(); state.add_balance(&sender, &U256::from(100)); @@ -656,6 +654,7 @@ mod tests { value: 100.into(), gas: 100000.into(), input: vec![], + call_type: CallType::Call, }), result: trace::Res::Call(trace::CallResult { gas_used: U256::from(55_248), diff --git a/ethcore/src/externalities.rs b/ethcore/src/externalities.rs index bdadaa764af..7d55f164869 100644 --- a/ethcore/src/externalities.rs +++ b/ethcore/src/externalities.rs @@ -21,6 +21,7 @@ use engine::*; use executive::*; use evm::{self, Schedule, Ext, ContractCreateResult, MessageCallResult, Factory}; use substate::*; +use types::executed::CallType; use trace::{Tracer, VMTracer}; /// Policy for handling output data on `RETURN` opcode. @@ -148,6 +149,7 @@ impl<'a, T, V> Ext for Externalities<'a, T, V> where T: 'a + Tracer, V: 'a + VMT value: ActionValue::Transfer(*value), code: Some(code.to_vec()), data: None, + call_type: CallType::None, }; self.state.inc_nonce(&self.origin_info.address); @@ -170,7 +172,8 @@ impl<'a, T, V> Ext for Externalities<'a, T, V> where T: 'a + Tracer, V: 'a + VMT value: Option, data: &[u8], code_address: &Address, - output: &mut [u8] + output: &mut [u8], + call_type: CallType ) -> MessageCallResult { trace!(target: "externalities", "call"); @@ -184,6 +187,7 @@ impl<'a, T, V> Ext for Externalities<'a, T, V> where T: 'a + Tracer, V: 'a + VMT gas_price: self.origin_info.gas_price, code: self.state.code(code_address), data: Some(data.to_vec()), + call_type: call_type, }; if let Some(value) = value { @@ -302,6 +306,7 @@ mod tests { use tests::helpers::*; use super::*; use trace::{NoopTracer, NoopVMTracer}; + use types::executed::CallType; fn get_test_origin() -> OriginInfo { OriginInfo { @@ -420,7 +425,9 @@ mod tests { Some(U256::from_str("0000000000000000000000000000000000000000000000000000000000150000").unwrap()), &[], &Address::new(), - &mut output); + &mut output, + CallType::Call + ); } #[test] diff --git a/ethcore/src/json_tests/executive.rs b/ethcore/src/json_tests/executive.rs index f4a34a33e3e..8bc42a7e751 100644 --- a/ethcore/src/json_tests/executive.rs +++ b/ethcore/src/json_tests/executive.rs @@ -22,6 +22,7 @@ use evm; use evm::{Schedule, Ext, Factory, Finalize, VMType, ContractCreateResult, MessageCallResult}; use externalities::*; use substate::*; +use types::executed::CallType; use tests::helpers::*; use ethjson; use trace::{Tracer, NoopTracer}; @@ -109,13 +110,15 @@ impl<'a, T, V> Ext for TestExt<'a, T, V> where T: Tracer, V: VMTracer { } fn call(&mut self, - gas: &U256, - _sender_address: &Address, - receive_address: &Address, - value: Option, - data: &[u8], - _code_address: &Address, - _output: &mut [u8]) -> MessageCallResult { + gas: &U256, + _sender_address: &Address, + receive_address: &Address, + value: Option, + data: &[u8], + _code_address: &Address, + _output: &mut [u8], + _call_type: CallType + ) -> MessageCallResult { self.callcreates.push(CallCreate { data: data.to_vec(), destination: Some(receive_address.clone()), diff --git a/ethcore/src/state.rs b/ethcore/src/state.rs index 65758f613ff..9ab04b08468 100644 --- a/ethcore/src/state.rs +++ b/ethcore/src/state.rs @@ -390,6 +390,7 @@ use transaction::*; use util::log::init_log; use trace::trace; use trace::trace::{Trace}; +use types::executed::CallType; #[test] fn should_apply_create_transaction() { @@ -522,6 +523,7 @@ fn should_trace_call_transaction() { value: 100.into(), gas: 79000.into(), input: vec![], + call_type: CallType::Call, }), result: trace::Res::Call(trace::CallResult { gas_used: U256::from(3), @@ -564,6 +566,7 @@ fn should_trace_basic_call_transaction() { value: 100.into(), gas: 79000.into(), input: vec![], + call_type: CallType::Call, }), result: trace::Res::Call(trace::CallResult { gas_used: U256::from(0), @@ -606,6 +609,7 @@ fn should_trace_call_transaction_to_builtin() { value: 0.into(), gas: 79_000.into(), input: vec![], + call_type: CallType::Call, }), result: trace::Res::Call(trace::CallResult { gas_used: U256::from(3000), @@ -647,6 +651,7 @@ fn should_not_trace_subcall_transaction_to_builtin() { value: 0.into(), gas: 79000.into(), input: vec![], + call_type: CallType::Call, }), result: trace::Res::Call(trace::CallResult { gas_used: U256::from(28_061), @@ -690,12 +695,28 @@ fn should_not_trace_callcode() { value: 0.into(), gas: 79000.into(), input: vec![], + call_type: CallType::Call, }), result: trace::Res::Call(trace::CallResult { - gas_used: U256::from(64), + gas_used: 64.into(), output: vec![] }), - subs: vec![] + subs: vec![Trace { + depth: 1, + action: trace::Action::Call(trace::Call { + from: 0xa.into(), + to: 0xa.into(), + value: 0.into(), + gas: 4096.into(), + input: vec![], + call_type: CallType::CallCode, + }), + subs: vec![], + result: trace::Res::Call(trace::CallResult { + gas_used: 3.into(), + output: vec![], + }), + }], }); assert_eq!(result.trace, expected_trace); } @@ -736,12 +757,28 @@ fn should_not_trace_delegatecall() { value: 0.into(), gas: 79000.into(), input: vec![], + call_type: CallType::Call, }), result: trace::Res::Call(trace::CallResult { gas_used: U256::from(61), output: vec![] }), - subs: vec![] + subs: vec![Trace { + depth: 1, + action: trace::Action::Call(trace::Call { + from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), + to: 0xa.into(), + value: 0.into(), + gas: 32768.into(), + input: vec![], + call_type: CallType::DelegateCall, + }), + subs: vec![], + result: trace::Res::Call(trace::CallResult { + gas_used: 3.into(), + output: vec![], + }), + }], }); assert_eq!(result.trace, expected_trace); } @@ -778,6 +815,7 @@ fn should_trace_failed_call_transaction() { value: 100.into(), gas: 79000.into(), input: vec![], + call_type: CallType::Call, }), result: trace::Res::FailedCall, subs: vec![] @@ -821,6 +859,7 @@ fn should_trace_call_with_subcall_transaction() { value: 100.into(), gas: 79000.into(), input: vec![], + call_type: CallType::Call, }), result: trace::Res::Call(trace::CallResult { gas_used: U256::from(69), @@ -834,6 +873,7 @@ fn should_trace_call_with_subcall_transaction() { value: 0.into(), gas: 78934.into(), input: vec![], + call_type: CallType::Call, }), result: trace::Res::Call(trace::CallResult { gas_used: U256::from(3), @@ -878,6 +918,7 @@ fn should_trace_call_with_basic_subcall_transaction() { value: 100.into(), gas: 79000.into(), input: vec![], + call_type: CallType::Call, }), result: trace::Res::Call(trace::CallResult { gas_used: U256::from(31761), @@ -891,6 +932,7 @@ fn should_trace_call_with_basic_subcall_transaction() { value: 69.into(), gas: 2300.into(), input: vec![], + call_type: CallType::Call, }), result: trace::Res::Call(trace::CallResult::default()), subs: vec![] @@ -932,6 +974,7 @@ fn should_not_trace_call_with_invalid_basic_subcall_transaction() { value: 100.into(), gas: 79000.into(), input: vec![], + call_type: CallType::Call, }), result: trace::Res::Call(trace::CallResult { gas_used: U256::from(31761), @@ -976,6 +1019,7 @@ fn should_trace_failed_subcall_transaction() { value: 100.into(), gas: 79000.into(), input: vec![], + call_type: CallType::Call, }), result: trace::Res::Call(trace::CallResult { gas_used: U256::from(79_000), @@ -989,6 +1033,7 @@ fn should_trace_failed_subcall_transaction() { value: 0.into(), gas: 78934.into(), input: vec![], + call_type: CallType::Call, }), result: trace::Res::FailedCall, subs: vec![] @@ -1032,6 +1077,7 @@ fn should_trace_call_with_subcall_with_subcall_transaction() { value: 100.into(), gas: 79000.into(), input: vec![], + call_type: CallType::Call, }), result: trace::Res::Call(trace::CallResult { gas_used: U256::from(135), @@ -1045,6 +1091,7 @@ fn should_trace_call_with_subcall_with_subcall_transaction() { value: 0.into(), gas: 78934.into(), input: vec![], + call_type: CallType::Call, }), result: trace::Res::Call(trace::CallResult { gas_used: U256::from(69), @@ -1058,6 +1105,7 @@ fn should_trace_call_with_subcall_with_subcall_transaction() { value: 0.into(), gas: 78868.into(), input: vec![], + call_type: CallType::Call, }), result: trace::Res::Call(trace::CallResult { gas_used: U256::from(3), @@ -1105,6 +1153,7 @@ fn should_trace_failed_subcall_with_subcall_transaction() { value: 100.into(), gas: 79000.into(), input: vec![], + call_type: CallType::Call, }), result: trace::Res::Call(trace::CallResult { gas_used: U256::from(79_000), @@ -1118,6 +1167,7 @@ fn should_trace_failed_subcall_with_subcall_transaction() { value: 0.into(), gas: 78934.into(), input: vec![], + call_type: CallType::Call, }), result: trace::Res::FailedCall, subs: vec![Trace { @@ -1128,6 +1178,7 @@ fn should_trace_failed_subcall_with_subcall_transaction() { value: 0.into(), gas: 78868.into(), input: vec![], + call_type: CallType::Call, }), result: trace::Res::Call(trace::CallResult { gas_used: U256::from(3), @@ -1174,6 +1225,7 @@ fn should_trace_suicide() { value: 100.into(), gas: 79000.into(), input: vec![], + call_type: CallType::Call, }), result: trace::Res::Call(trace::CallResult { gas_used: 3.into(), diff --git a/ethcore/src/trace/db.rs b/ethcore/src/trace/db.rs index eb8eb694f02..7379f9ad90b 100644 --- a/ethcore/src/trace/db.rs +++ b/ethcore/src/trace/db.rs @@ -367,6 +367,7 @@ mod tests { use trace::{Config, Switch, TraceDB, Database, DatabaseExtras, ImportRequest}; use trace::{BlockTraces, Trace, Filter, LocalizedTrace, AddressesFilter}; use trace::trace::{Call, Action, Res}; + use types::executed::CallType; struct NoopExtras; @@ -492,6 +493,7 @@ mod tests { value: U256::from(3), gas: U256::from(4), input: vec![], + call_type: CallType::Call, }), result: Res::FailedCall, subs: vec![], @@ -511,6 +513,7 @@ mod tests { value: U256::from(3), gas: U256::from(4), input: vec![], + call_type: CallType::Call, }), result: Res::FailedCall, trace_address: vec![], diff --git a/ethcore/src/trace/executive_tracer.rs b/ethcore/src/trace/executive_tracer.rs index af8183c0a70..83761ea2388 100644 --- a/ethcore/src/trace/executive_tracer.rs +++ b/ethcore/src/trace/executive_tracer.rs @@ -40,12 +40,7 @@ impl Tracer for ExecutiveTracer { Some(vec![]) } - fn trace_call(&mut self, call: Option, gas_used: U256, output: Option, depth: usize, subs: Vec, delegate_call: bool) { - // don't trace if it's DELEGATECALL or CALLCODE. - if delegate_call { - return; - } - + fn trace_call(&mut self, call: Option, gas_used: U256, output: Option, depth: usize, subs: Vec) { let trace = Trace { depth: depth, subs: subs, @@ -72,12 +67,7 @@ impl Tracer for ExecutiveTracer { self.traces.push(trace); } - fn trace_failed_call(&mut self, call: Option, depth: usize, subs: Vec, delegate_call: bool) { - // don't trace if it's DELEGATECALL or CALLCODE. - if delegate_call { - return; - } - + fn trace_failed_call(&mut self, call: Option, depth: usize, subs: Vec) { let trace = Trace { depth: depth, subs: subs, diff --git a/ethcore/src/trace/flat.rs b/ethcore/src/trace/flat.rs index f39af4423d8..da303816747 100644 --- a/ethcore/src/trace/flat.rs +++ b/ethcore/src/trace/flat.rs @@ -169,6 +169,7 @@ mod tests { use util::{U256, Address}; use trace::trace::{Action, Res, CallResult, Call, Create, Trace}; use trace::BlockTraces; + use types::executed::CallType; #[test] fn test_block_from() { @@ -179,7 +180,8 @@ mod tests { to: Address::from(2), value: U256::from(3), gas: U256::from(4), - input: vec![0x5] + input: vec![0x5], + call_type: CallType::Call, }), subs: vec![ Trace { @@ -263,7 +265,8 @@ mod tests { to: 2.into(), value: 3.into(), gas: 4.into(), - input: vec![0x5] + input: vec![0x5], + call_type: CallType::Call, }), result: Res::Call(CallResult { gas_used: 10.into(), diff --git a/ethcore/src/trace/mod.rs b/ethcore/src/trace/mod.rs index 67fec2b97fc..0e4d9f626da 100644 --- a/ethcore/src/trace/mod.rs +++ b/ethcore/src/trace/mod.rs @@ -60,8 +60,7 @@ pub trait Tracer: Send { gas_used: U256, output: Option, depth: usize, - subs: Vec, - delegate_call: bool + subs: Vec ); /// Stores trace create info. @@ -76,7 +75,7 @@ pub trait Tracer: Send { ); /// Stores failed call trace. - fn trace_failed_call(&mut self, call: Option, depth: usize, subs: Vec, delegate_call: bool); + fn trace_failed_call(&mut self, call: Option, depth: usize, subs: Vec); /// Stores failed create trace. fn trace_failed_create(&mut self, create: Option, depth: usize, subs: Vec); diff --git a/ethcore/src/trace/noop_tracer.rs b/ethcore/src/trace/noop_tracer.rs index 290fb236731..0a3ec1c978d 100644 --- a/ethcore/src/trace/noop_tracer.rs +++ b/ethcore/src/trace/noop_tracer.rs @@ -37,7 +37,7 @@ impl Tracer for NoopTracer { None } - fn trace_call(&mut self, call: Option, _: U256, output: Option, _: usize, _: Vec, _: bool) { + fn trace_call(&mut self, call: Option, _: U256, output: Option, _: usize, _: Vec) { assert!(call.is_none(), "self.prepare_trace_call().is_none(): so we can't be tracing: qed"); assert!(output.is_none(), "self.prepare_trace_output().is_none(): so we can't be tracing: qed"); } @@ -47,7 +47,7 @@ impl Tracer for NoopTracer { 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, _: usize, _: Vec, _: bool) { + fn trace_failed_call(&mut self, call: Option, _: usize, _: Vec) { assert!(call.is_none(), "self.prepare_trace_call().is_none(): so we can't be tracing: qed"); } diff --git a/ethcore/src/types/executed.rs b/ethcore/src/types/executed.rs index 4d31b9fe532..c211a7bc0f5 100644 --- a/ethcore/src/types/executed.rs +++ b/ethcore/src/types/executed.rs @@ -18,6 +18,7 @@ use util::numbers::*; use util::Bytes; +use util::rlp::*; use trace::{Trace, VMTrace}; use types::log_entry::LogEntry; use types::state_diff::StateDiff; @@ -26,6 +27,43 @@ use std::fmt; use std::mem; use std::collections::VecDeque; +/// The type of the call-like instruction. +#[derive(Debug, PartialEq, Clone, Binary)] +pub enum CallType { + /// Not a CALL. + None, + /// CALL. + Call, + /// CALLCODE. + CallCode, + /// DELEGATECALL. + DelegateCall, +} + +impl Encodable for CallType { + fn rlp_append(&self, s: &mut RlpStream) { + let v = match *self { + CallType::None => 0u32, + CallType::Call => 1, + CallType::CallCode => 2, + CallType::DelegateCall => 3, + }; + s.append(&v); + } +} + +impl Decodable for CallType { + fn decode(decoder: &D) -> Result where D: Decoder { + decoder.as_rlp().as_val().and_then(|v| Ok(match v { + 0u32 => CallType::None, + 1 => CallType::Call, + 2 => CallType::CallCode, + 3 => CallType::DelegateCall, + _ => return Err(DecoderError::Custom("Invalid value of CallType item")), + })) + } +} + /// Transaction execution receipt. #[derive(Debug, PartialEq, Clone)] pub struct Executed { @@ -135,3 +173,12 @@ impl fmt::Display for ExecutionError { /// Transaction execution result. pub type ExecutionResult = Result; + +#[test] +fn should_encode_and_decode_call_type() { + use util::rlp; + let original = CallType::Call; + let encoded = rlp::encode(&original); + let decoded = rlp::decode(&encoded); + assert_eq!(original, decoded); +} diff --git a/ethcore/src/types/trace_types/filter.rs b/ethcore/src/types/trace_types/filter.rs index c01847e4335..abc2f1a51d4 100644 --- a/ethcore/src/types/trace_types/filter.rs +++ b/ethcore/src/types/trace_types/filter.rs @@ -138,6 +138,7 @@ mod tests { use trace::flat::FlatTrace; use trace::{Filter, AddressesFilter}; use basic_types::LogBloom; + use types::executed::CallType; #[test] fn empty_trace_filter_bloom_possibilities() { @@ -280,6 +281,7 @@ mod tests { value: 3.into(), gas: 4.into(), input: vec![0x5], + call_type: CallType::Call, }), result: Res::FailedCall, trace_address: vec![0], diff --git a/ethcore/src/types/trace_types/trace.rs b/ethcore/src/types/trace_types/trace.rs index db90e068c8f..1f23a0f250a 100644 --- a/ethcore/src/types/trace_types/trace.rs +++ b/ethcore/src/types/trace_types/trace.rs @@ -21,6 +21,7 @@ use util::rlp::*; use util::sha3::Hashable; use action_params::ActionParams; use basic_types::LogBloom; +use types::executed::CallType; use ipc::binary::BinaryConvertError; use std::mem; use std::collections::VecDeque; @@ -100,6 +101,8 @@ pub struct Call { pub gas: U256, /// The input data provided to the call. pub input: Bytes, + /// The type of the call. + pub call_type: CallType, } impl From for Call { @@ -110,18 +113,20 @@ impl From for Call { value: p.value.value(), gas: p.gas, input: p.data.unwrap_or_else(Vec::new), + call_type: p.call_type, } } } impl Encodable for Call { fn rlp_append(&self, s: &mut RlpStream) { - s.begin_list(5); + s.begin_list(6); s.append(&self.from); s.append(&self.to); s.append(&self.value); s.append(&self.gas); s.append(&self.input); + s.append(&self.call_type); } } @@ -134,6 +139,7 @@ impl Decodable for Call { value: try!(d.val_at(2)), gas: try!(d.val_at(3)), input: try!(d.val_at(4)), + call_type: try!(d.val_at(5)), }; Ok(res) @@ -575,7 +581,8 @@ mod tests { use util::{Address, U256, FixedHash}; use util::rlp::{encode, decode}; use util::sha3::Hashable; - use trace::trace::{Call, CallResult, Create, Res, Action, Trace, Suicide}; + use trace::trace::{Call, CallResult, Create, Res, Action, Trace, Suicide, CreateResult}; + use types::executed::CallType; #[test] fn traces_rlp() { @@ -586,7 +593,8 @@ mod tests { to: Address::from(2), value: U256::from(3), gas: U256::from(4), - input: vec![0x5] + input: vec![0x5], + call_type: CallType::Call, }), subs: vec![ Trace { @@ -621,7 +629,8 @@ mod tests { to: Address::from(2), value: U256::from(3), gas: U256::from(4), - input: vec![0x5] + input: vec![0x5], + call_type: CallType::Call, }), subs: vec![ Trace { diff --git a/rpc/src/v1/types/trace.rs b/rpc/src/v1/types/trace.rs index c1e2643367b..45d13bd4cff 100644 --- a/rpc/src/v1/types/trace.rs +++ b/rpc/src/v1/types/trace.rs @@ -22,7 +22,10 @@ use ethcore::trace::{Trace as EthTrace, LocalizedTrace as EthLocalizedTrace}; use ethcore::trace as et; use ethcore::state_diff; use ethcore::account_diff; -use v1::types::Bytes; +use ethcore::executed; +use ethcore::client::Executed; +use util::Uint; +use v1::types::{Bytes, H160, H256, U256}; #[derive(Debug, Serialize)] /// A diff of some chunk of memory. @@ -233,6 +236,34 @@ impl From for Create { } } +/// Call type. +#[derive(Debug, Serialize)] +pub enum CallType { + /// None + #[serde(rename="none")] + None, + /// Call + #[serde(rename="call")] + Call, + /// Call code + #[serde(rename="callcode")] + CallCode, + /// Delegate call + #[serde(rename="delegatecall")] + DelegateCall, +} + +impl From for CallType { + fn from(c: executed::CallType) -> Self { + match c { + executed::CallType::None => CallType::None, + executed::CallType::Call => CallType::Call, + executed::CallType::CallCode => CallType::CallCode, + executed::CallType::DelegateCall => CallType::DelegateCall, + } + } +} + /// Call response #[derive(Debug, Serialize)] pub struct Call { @@ -246,6 +277,9 @@ pub struct Call { gas: U256, /// Input data input: Bytes, + /// The type of the call. + #[serde(rename="callType")] + call_type: CallType, } impl From for Call { @@ -256,6 +290,7 @@ impl From for Call { value: c.value, gas: c.gas, input: Bytes::new(c.input), + call_type: c.call_type, } } } @@ -461,6 +496,7 @@ mod tests { value: U256::from(6), gas: U256::from(7), input: Bytes::new(vec![0x12, 0x34]), + call_type: CallType::Call, }), result: Res::Call(CallResult { gas_used: U256::from(8), @@ -474,7 +510,7 @@ mod tests { block_hash: H256::from(14), }; let serialized = serde_json::to_string(&t).unwrap(); - assert_eq!(serialized, r#"{"action":{"call":{"from":"0x0000000000000000000000000000000000000004","to":"0x0000000000000000000000000000000000000005","value":"0x06","gas":"0x07","input":"0x1234"}},"result":{"call":{"gasUsed":"0x08","output":"0x5678"}},"traceAddress":["0x0a"],"subtraces":"0x01","transactionPosition":"0x0b","transactionHash":"0x000000000000000000000000000000000000000000000000000000000000000c","blockNumber":"0x0d","blockHash":"0x000000000000000000000000000000000000000000000000000000000000000e"}"#); + assert_eq!(serialized, r#"{"action":{"call":{"from":"0x0000000000000000000000000000000000000004","to":"0x0000000000000000000000000000000000000005","value":"0x06","gas":"0x07","input":"0x1234","callType":{"call":[]}}},"result":{"call":{"gasUsed":"0x08","output":"0x5678"}},"traceAddress":["0x0a"],"subtraces":"0x01","transactionPosition":"0x0b","transactionHash":"0x000000000000000000000000000000000000000000000000000000000000000c","blockNumber":"0x0d","blockHash":"0x000000000000000000000000000000000000000000000000000000000000000e"}"#); } #[test] @@ -550,6 +586,7 @@ mod tests { value: U256::from(3), gas: U256::from(4), input: Bytes::new(vec![0x12, 0x34]), + call_type: CallType::Call, }), Action::Create(Create { from: Address::from(5), value: U256::from(6), @@ -558,7 +595,7 @@ mod tests { })]; let serialized = serde_json::to_string(&actions).unwrap(); - assert_eq!(serialized, r#"[{"call":{"from":"0x0000000000000000000000000000000000000001","to":"0x0000000000000000000000000000000000000002","value":"0x03","gas":"0x04","input":"0x1234"}},{"create":{"from":"0x0000000000000000000000000000000000000005","value":"0x06","gas":"0x07","init":"0x5678"}}]"#); + assert_eq!(serialized, r#"[{"call":{"from":"0x0000000000000000000000000000000000000001","to":"0x0000000000000000000000000000000000000002","value":"0x03","gas":"0x04","input":"0x1234","callType":{"call":[]}}},{"create":{"from":"0x0000000000000000000000000000000000000005","value":"0x06","gas":"0x07","init":"0x5678"}}]"#); } #[test] From a44780db88dc47cba58ae39bab4fea27717674c7 Mon Sep 17 00:00:00 2001 From: Marek Kotewicz Date: Sat, 23 Jul 2016 18:50:20 +0200 Subject: [PATCH 4/6] filtering transactions toAddress includes contract creation (#1697) * tracing finds transaction which creates contract * comma cleanup Remove when following `}`s, add to final entries. --- ethcore/src/types/trace_types/filter.rs | 41 +++++++++++++++++++++---- ethcore/src/types/trace_types/trace.rs | 26 ++++++++++++++-- rpc/src/v1/types/trace.rs | 10 +++--- 3 files changed, 63 insertions(+), 14 deletions(-) diff --git a/ethcore/src/types/trace_types/filter.rs b/ethcore/src/types/trace_types/filter.rs index abc2f1a51d4..4344d1500bc 100644 --- a/ethcore/src/types/trace_types/filter.rs +++ b/ethcore/src/types/trace_types/filter.rs @@ -22,7 +22,7 @@ use util::{Address, FixedHash}; use util::sha3::Hashable; use basic_types::LogBloom; use trace::flat::FlatTrace; -use types::trace_types::trace::Action; +use types::trace_types::trace::{Action, Res}; use ipc::binary::BinaryConvertError; use std::mem; use std::collections::VecDeque; @@ -58,7 +58,7 @@ impl AddressesFilter { true => vec![LogBloom::new()], false => self.list.iter() .map(|address| LogBloom::from_bloomed(&address.sha3())) - .collect() + .collect(), } } @@ -71,7 +71,7 @@ impl AddressesFilter { .flat_map(|bloom| self.list.iter() .map(|address| bloom.with_bloomed(&address.sha3())) .collect::>()) - .collect() + .collect(), } } } @@ -110,12 +110,12 @@ impl Filter { /// Returns true if given trace matches the filter. pub fn matches(&self, trace: &FlatTrace) -> bool { - match trace.action { + let action = match trace.action { Action::Call(ref call) => { let from_matches = self.from_address.matches(&call.from); let to_matches = self.to_address.matches(&call.to); from_matches && to_matches - }, + } Action::Create(ref create) => { let from_matches = self.from_address.matches(&create.from); let to_matches = self.to_address.matches_all(); @@ -126,6 +126,11 @@ impl Filter { let to_matches = self.to_address.matches(&suicide.refund_address); from_matches && to_matches } + }; + + action || match trace.result { + Res::Create(ref create) => self.to_address.matches(&create.address), + _ => false } } } @@ -134,7 +139,7 @@ impl Filter { mod tests { use util::{FixedHash, Address}; use util::sha3::Hashable; - use trace::trace::{Action, Call, Res, Suicide}; + use trace::trace::{Action, Call, Res, Create, CreateResult, Suicide}; use trace::flat::FlatTrace; use trace::{Filter, AddressesFilter}; use basic_types::LogBloom; @@ -296,6 +301,30 @@ mod tests { assert!(f5.matches(&trace)); assert!(!f6.matches(&trace)); + let trace = FlatTrace { + action: Action::Create(Create { + from: 1.into(), + value: 3.into(), + gas: 4.into(), + init: vec![0x5], + }), + result: Res::Create(CreateResult { + gas_used: 10.into(), + code: vec![], + address: 2.into(), + }), + trace_address: vec![0], + subtraces: 0, + }; + + assert!(f0.matches(&trace)); + assert!(f1.matches(&trace)); + assert!(f2.matches(&trace)); + assert!(f3.matches(&trace)); + assert!(f4.matches(&trace)); + assert!(f5.matches(&trace)); + assert!(!f6.matches(&trace)); + let trace = FlatTrace { action: Action::Suicide(Suicide { address: 1.into(), diff --git a/ethcore/src/types/trace_types/trace.rs b/ethcore/src/types/trace_types/trace.rs index 1f23a0f250a..3736f267365 100644 --- a/ethcore/src/types/trace_types/trace.rs +++ b/ethcore/src/types/trace_types/trace.rs @@ -88,6 +88,13 @@ impl Decodable for CreateResult { } } +impl CreateResult { + /// Returns bloom. + pub fn bloom(&self) -> LogBloom { + LogBloom::from_bloomed(&self.address.sha3()) + } +} + /// Description of a _call_ action, either a `CALL` operation or a message transction. #[derive(Debug, Clone, PartialEq, Binary)] pub struct Call { @@ -367,6 +374,16 @@ impl Decodable for Res { } } +impl Res { + /// Returns result bloom. + pub fn bloom(&self) -> LogBloom { + match *self { + Res::Create(ref create) => create.bloom(), + Res::Call(_) | Res::FailedCall | Res::FailedCreate | Res::None => Default::default(), + } + } +} + #[derive(Debug, Clone, PartialEq, Binary)] /// A trace; includes a description of the action being traced and sub traces of each interior action. pub struct Trace { @@ -408,7 +425,7 @@ impl Decodable for Trace { impl Trace { /// Returns trace bloom. pub fn bloom(&self) -> LogBloom { - self.subs.iter().fold(self.action.bloom(), |b, s| b | s.bloom()) + self.subs.iter().fold(self.action.bloom() | self.result.bloom(), |b, s| b | s.bloom()) } } @@ -642,7 +659,11 @@ mod tests { init: vec![0x9] }), subs: vec![], - result: Res::FailedCreate + result: Res::Create(CreateResult { + gas_used: 10.into(), + code: vec![], + address: 15.into(), + }), }, Trace { depth: 3, @@ -668,6 +689,7 @@ mod tests { assert!(bloom.contains_bloomed(&Address::from(2).sha3())); assert!(!bloom.contains_bloomed(&Address::from(20).sha3())); assert!(bloom.contains_bloomed(&Address::from(6).sha3())); + assert!(bloom.contains_bloomed(&Address::from(15).sha3())); assert!(bloom.contains_bloomed(&Address::from(101).sha3())); assert!(bloom.contains_bloomed(&Address::from(102).sha3())); assert!(!bloom.contains_bloomed(&Address::from(103).sha3())); diff --git a/rpc/src/v1/types/trace.rs b/rpc/src/v1/types/trace.rs index 45d13bd4cff..354c88cfc0f 100644 --- a/rpc/src/v1/types/trace.rs +++ b/rpc/src/v1/types/trace.rs @@ -23,9 +23,7 @@ use ethcore::trace as et; use ethcore::state_diff; use ethcore::account_diff; use ethcore::executed; -use ethcore::client::Executed; -use util::Uint; -use v1::types::{Bytes, H160, H256, U256}; +use v1::types::{Bytes}; #[derive(Debug, Serialize)] /// A diff of some chunk of memory. @@ -290,7 +288,7 @@ impl From for Call { value: c.value, gas: c.gas, input: Bytes::new(c.input), - call_type: c.call_type, + call_type: c.call_type.into(), } } } @@ -299,10 +297,10 @@ impl From for Call { #[derive(Debug, Serialize)] pub struct Suicide { /// Address. - pub address: H160, + pub address: Address, /// Refund address. #[serde(rename="refundAddress")] - pub refund_address: H160, + pub refund_address: Address, /// Balance. pub balance: U256, } From 3de962cf0650071f2db82b9af04eaf595d8288f7 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Tue, 26 Jul 2016 16:48:50 +0200 Subject: [PATCH 5/6] Various improvements to tracing & diagnostics. (#1707) * Various improvements to tracing & diagnostics. - Manage possibility of `Account` not having code for `PodAccount` - New RPC: `trace_sendRawTransaction` - See raw transaction dump when inspecting over RPC * Fix test * Remove one of the dupe error messages * Remove unneeded `&`s * Reformat and extremely minor optimisation * Minor optimisation * Remove unneeded let * Fix tests. * Additional fix. * Minor rename. [ci:skip] * Bowing to the pressure. --- ethcore/src/account.rs | 4 +-- ethcore/src/pod_account.rs | 50 ++++++++++++++++++++------------- ethcore/src/trace/db.rs | 2 +- ethcore/src/trace/flat.rs | 2 ++ rpc/src/v1/impls/traces.rs | 42 ++++++++++++++++----------- rpc/src/v1/tests/mocked/eth.rs | 2 +- rpc/src/v1/traits/traces.rs | 4 +++ rpc/src/v1/types/block.rs | 2 +- rpc/src/v1/types/mod.rs.in | 2 +- rpc/src/v1/types/trace.rs | 42 ++++++++++++++++++++++++++- rpc/src/v1/types/transaction.rs | 7 ++++- 11 files changed, 114 insertions(+), 45 deletions(-) diff --git a/ethcore/src/account.rs b/ethcore/src/account.rs index 2edbf87ae5e..2083dcd5954 100644 --- a/ethcore/src/account.rs +++ b/ethcore/src/account.rs @@ -58,8 +58,8 @@ impl Account { nonce: pod.nonce, storage_root: SHA3_NULL_RLP, storage_overlay: RefCell::new(pod.storage.into_iter().map(|(k, v)| (k, (Filth::Dirty, v))).collect()), - code_hash: Some(pod.code.sha3()), - code_cache: pod.code + code_hash: pod.code.as_ref().map(|c| c.sha3()), + code_cache: pod.code.as_ref().map_or_else(|| { warn!("POD account with unknown code is being created! Assuming no code."); vec![] }, |c| c.clone()), } } diff --git a/ethcore/src/pod_account.rs b/ethcore/src/pod_account.rs index d04833caba7..9b6d953b25c 100644 --- a/ethcore/src/pod_account.rs +++ b/ethcore/src/pod_account.rs @@ -28,8 +28,8 @@ pub struct PodAccount { pub balance: U256, /// The nonce of the account. pub nonce: U256, - /// The code of the account. - pub code: Bytes, + /// The code of the account or `None` in the special case that it is unknown. + pub code: Option, /// The storage of the account. pub storage: BTreeMap, } @@ -38,7 +38,7 @@ impl PodAccount { /// Construct new object. #[cfg(test)] pub fn new(balance: U256, nonce: U256, code: Bytes, storage: BTreeMap) -> PodAccount { - PodAccount { balance: balance, nonce: nonce, code: code, storage: storage } + PodAccount { balance: balance, nonce: nonce, code: Some(code), storage: storage } } /// Convert Account to a PodAccount. @@ -48,7 +48,7 @@ impl PodAccount { balance: *acc.balance(), nonce: *acc.nonce(), storage: acc.storage_overlay().iter().fold(BTreeMap::new(), |mut m, (k, &(_, ref v))| {m.insert(k.clone(), v.clone()); m}), - code: acc.code().unwrap().to_vec(), + code: acc.code().map(|x| x.to_vec()), } } @@ -58,14 +58,15 @@ impl PodAccount { stream.append(&self.nonce); stream.append(&self.balance); stream.append(&sec_trie_root(self.storage.iter().map(|(k, v)| (k.to_vec(), encode(&U256::from(v.as_slice())).to_vec())).collect())); - stream.append(&self.code.sha3()); + stream.append(&self.code.as_ref().unwrap_or(&vec![]).sha3()); stream.out() } /// Place additional data into given hash DB. pub fn insert_additional(&self, db: &mut AccountDBMut) { - if !self.code.is_empty() { - db.insert(&self.code); + match self.code { + Some(ref c) if !c.is_empty() => { db.insert(c); } + _ => {} } let mut r = H256::new(); let mut t = SecTrieDBMut::new(db, &mut r); @@ -80,7 +81,7 @@ impl From for PodAccount { PodAccount { balance: a.balance.into(), nonce: a.nonce.into(), - code: a.code.into(), + code: Some(a.code.into()), storage: a.storage.into_iter().map(|(key, value)| { let key: U256 = key.into(); let value: U256 = value.into(); @@ -95,7 +96,7 @@ impl From for PodAccount { PodAccount { balance: a.balance.map_or_else(U256::zero, Into::into), nonce: a.nonce.map_or_else(U256::zero, Into::into), - code: vec![], + code: Some(vec![]), storage: BTreeMap::new() } } @@ -103,7 +104,13 @@ impl From for PodAccount { impl fmt::Display for PodAccount { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "(bal={}; nonce={}; code={} bytes, #{}; storage={} items)", self.balance, self.nonce, self.code.len(), self.code.sha3(), self.storage.len()) + write!(f, "(bal={}; nonce={}; code={} bytes, #{}; storage={} items)", + self.balance, + self.nonce, + self.code.as_ref().map_or(0, |c| c.len()), + self.code.as_ref().map_or_else(H256::new, |c| c.sha3()), + self.storage.len() + ) } } @@ -114,13 +121,13 @@ pub fn diff_pod(pre: Option<&PodAccount>, post: Option<&PodAccount>) -> Option Some(AccountDiff { balance: Diff::Born(x.balance), nonce: Diff::Born(x.nonce), - code: Diff::Born(x.code.clone()), + code: Diff::Born(x.code.as_ref().expect("account is newly created; newly created accounts must be given code; all caches should remain in place; qed").clone()), storage: x.storage.iter().map(|(k, v)| (k.clone(), Diff::Born(v.clone()))).collect(), }), (Some(x), None) => Some(AccountDiff { balance: Diff::Died(x.balance), nonce: Diff::Died(x.nonce), - code: Diff::Died(x.code.clone()), + code: Diff::Died(x.code.as_ref().expect("account is deleted; only way to delete account is running SUICIDE; account must have had own code cached to make operation; all caches should remain in place; qed").clone()), storage: x.storage.iter().map(|(k, v)| (k.clone(), Diff::Died(v.clone()))).collect(), }), (Some(pre), Some(post)) => { @@ -130,7 +137,10 @@ pub fn diff_pod(pre: Option<&PodAccount>, post: Option<&PodAccount>) -> Option Diff::new(pre_code, post_code), + _ => Diff::Same, + }, storage: storage.into_iter().map(|k| (k.clone(), Diff::new( pre.storage.get(&k).cloned().unwrap_or_else(H256::new), @@ -156,7 +166,7 @@ mod test { #[test] fn existence() { - let a = PodAccount{balance: 69.into(), nonce: 0.into(), code: vec![], storage: map![]}; + let a = PodAccount{balance: 69.into(), nonce: 0.into(), code: Some(vec![]), storage: map![]}; assert_eq!(diff_pod(Some(&a), Some(&a)), None); assert_eq!(diff_pod(None, Some(&a)), Some(AccountDiff{ balance: Diff::Born(69.into()), @@ -168,8 +178,8 @@ mod test { #[test] fn basic() { - let a = PodAccount{balance: 69.into(), nonce: 0.into(), code: vec![], storage: map![]}; - let b = PodAccount{balance: 42.into(), nonce: 1.into(), code: vec![], storage: map![]}; + let a = PodAccount{balance: 69.into(), nonce: 0.into(), code: Some(vec![]), storage: map![]}; + let b = PodAccount{balance: 42.into(), nonce: 1.into(), code: Some(vec![]), storage: map![]}; assert_eq!(diff_pod(Some(&a), Some(&b)), Some(AccountDiff { balance: Diff::Changed(69.into(), 42.into()), nonce: Diff::Changed(0.into(), 1.into()), @@ -180,8 +190,8 @@ mod test { #[test] fn code() { - let a = PodAccount{balance: 0.into(), nonce: 0.into(), code: vec![], storage: map![]}; - let b = PodAccount{balance: 0.into(), nonce: 1.into(), code: vec![0], storage: map![]}; + let a = PodAccount{balance: 0.into(), nonce: 0.into(), code: Some(vec![]), storage: map![]}; + let b = PodAccount{balance: 0.into(), nonce: 1.into(), code: Some(vec![0]), storage: map![]}; assert_eq!(diff_pod(Some(&a), Some(&b)), Some(AccountDiff { balance: Diff::Same, nonce: Diff::Changed(0.into(), 1.into()), @@ -195,13 +205,13 @@ mod test { let a = PodAccount { balance: 0.into(), nonce: 0.into(), - code: vec![], + code: Some(vec![]), storage: map_into![1 => 1, 2 => 2, 3 => 3, 4 => 4, 5 => 0, 6 => 0, 7 => 0] }; let b = PodAccount { balance: 0.into(), nonce: 0.into(), - code: vec![], + code: Some(vec![]), storage: map_into![1 => 1, 2 => 3, 3 => 0, 5 => 0, 7 => 7, 8 => 0, 9 => 9] }; assert_eq!(diff_pod(Some(&a), Some(&b)), Some(AccountDiff { diff --git a/ethcore/src/trace/db.rs b/ethcore/src/trace/db.rs index 7379f9ad90b..69ea95bd2d2 100644 --- a/ethcore/src/trace/db.rs +++ b/ethcore/src/trace/db.rs @@ -23,7 +23,7 @@ use bloomchain::{Number, Config as BloomConfig}; use bloomchain::group::{BloomGroupDatabase, BloomGroupChain, GroupPosition, BloomGroup}; use util::{H256, H264, Database, DatabaseConfig, DBTransaction}; use header::BlockNumber; -use trace::{BlockTraces, LocalizedTrace, Config, Switch, Filter, Database as TraceDatabase, ImportRequest, +use trace::{LocalizedTrace, Config, Switch, Filter, Database as TraceDatabase, ImportRequest, DatabaseExtras, Error}; use db::{Key, Writable, Readable, CacheUpdatePolicy}; use blooms; diff --git a/ethcore/src/trace/flat.rs b/ethcore/src/trace/flat.rs index da303816747..c746a2e2aca 100644 --- a/ethcore/src/trace/flat.rs +++ b/ethcore/src/trace/flat.rs @@ -74,6 +74,7 @@ impl Decodable for FlatTrace { pub struct FlatTransactionTraces(Vec); impl FlatTransactionTraces { + /// Returns bloom of the trace. pub fn bloom(&self) -> LogBloom { self.0.iter().fold(Default::default(), | bloom, trace | bloom | trace.bloom()) } @@ -102,6 +103,7 @@ impl Into> for FlatTransactionTraces { pub struct FlatBlockTraces(Vec); impl FlatBlockTraces { + /// Returns bloom of the trace. pub fn bloom(&self) -> LogBloom { self.0.iter().fold(Default::default(), | bloom, tx_traces | bloom | tx_traces.bloom()) } diff --git a/rpc/src/v1/impls/traces.rs b/rpc/src/v1/impls/traces.rs index caf549c8449..e973a8a2656 100644 --- a/rpc/src/v1/impls/traces.rs +++ b/rpc/src/v1/impls/traces.rs @@ -18,13 +18,13 @@ use std::sync::{Weak, Arc}; use jsonrpc_core::*; -use std::collections::BTreeMap; use util::H256; +use util::rlp::{UntrustedRlp, View}; use ethcore::client::{BlockChainClient, CallAnalytics, TransactionID, TraceId}; use ethcore::miner::MinerService; use ethcore::transaction::{Transaction as EthTransaction, SignedTransaction, Action}; use v1::traits::Traces; -use v1::types::{TraceFilter, LocalizedTrace, Trace, BlockNumber, Index, CallRequest, Bytes, StateDiff, VMTrace}; +use v1::types::{TraceFilter, LocalizedTrace, BlockNumber, Index, CallRequest, Bytes, TraceResults}; /// Traces api implementation. pub struct TracesClient where C: BlockChainClient, M: MinerService { @@ -113,22 +113,30 @@ impl Traces for TracesClient where C: BlockChainClient + 'static, M: state_diffing: flags.contains(&("stateDiff".to_owned())), }; let signed = try!(self.sign_call(request)); - let r = take_weak!(self.client).call(&signed, analytics); - if let Ok(executed) = r { - // TODO maybe add other stuff to this? - let mut ret = map!["output".to_owned() => to_value(&Bytes(executed.output)).unwrap()]; - if let Some(trace) = executed.trace { - ret.insert("trace".to_owned(), to_value(&Trace::from(trace)).unwrap()); - } - if let Some(vm_trace) = executed.vm_trace { - ret.insert("vmTrace".to_owned(), to_value(&VMTrace::from(vm_trace)).unwrap()); - } - if let Some(state_diff) = executed.state_diff { - ret.insert("stateDiff".to_owned(), to_value(&StateDiff::from(state_diff)).unwrap()); - } - return Ok(Value::Object(ret)) + match take_weak!(self.client).call(&signed, analytics) { + Ok(e) => to_value(&TraceResults::from(e)), + _ => Ok(Value::Null), + } + }) + } + + fn raw_transaction(&self, params: Params) -> Result { + trace!(target: "jsonrpc", "call: {:?}", params); + from_params::<(Bytes, Vec)>(params) + .and_then(|(raw_transaction, flags)| { + let raw_transaction = raw_transaction.to_vec(); + let analytics = CallAnalytics { + transaction_tracing: flags.contains(&("trace".to_owned())), + vm_tracing: flags.contains(&("vmTrace".to_owned())), + state_diffing: flags.contains(&("stateDiff".to_owned())), + }; + match UntrustedRlp::new(&raw_transaction).as_val() { + Ok(signed) => match take_weak!(self.client).call(&signed, analytics) { + Ok(e) => to_value(&TraceResults::from(e)), + _ => Ok(Value::Null), + }, + Err(_) => Err(Error::invalid_params()), } - Ok(Value::Null) }) } } diff --git a/rpc/src/v1/tests/mocked/eth.rs b/rpc/src/v1/tests/mocked/eth.rs index 29c10a92b1c..0c4a13d94b9 100644 --- a/rpc/src/v1/tests/mocked/eth.rs +++ b/rpc/src/v1/tests/mocked/eth.rs @@ -366,7 +366,7 @@ fn rpc_eth_pending_transaction_by_hash() { tester.miner.pending_transactions.lock().unwrap().insert(H256::zero(), tx); } - let response = r#"{"jsonrpc":"2.0","result":{"blockHash":null,"blockNumber":null,"creates":null,"from":"0x0f65fe9276bc9a24ae7083ae28e2660ef72df99e","gas":"0x5208","gasPrice":"0x01","hash":"0x41df922fd0d4766fcc02e161f8295ec28522f329ae487f14d811e4b64c8d6e31","input":"0x","nonce":"0x00","to":"0x095e7baea6a6c7c4c2dfeb977efac326af552d87","transactionIndex":null,"value":"0x0a"},"id":1}"#; + let response = r#"{"jsonrpc":"2.0","result":{"blockHash":null,"blockNumber":null,"creates":null,"from":"0x0f65fe9276bc9a24ae7083ae28e2660ef72df99e","gas":"0x5208","gasPrice":"0x01","hash":"0x41df922fd0d4766fcc02e161f8295ec28522f329ae487f14d811e4b64c8d6e31","input":"0x","nonce":"0x00","raw":"0xf85f800182520894095e7baea6a6c7c4c2dfeb977efac326af552d870a801ba048b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353a0efffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804","to":"0x095e7baea6a6c7c4c2dfeb977efac326af552d87","transactionIndex":null,"value":"0x0a"},"id":1}"#; let request = r#"{ "jsonrpc": "2.0", "method": "eth_getTransactionByHash", diff --git a/rpc/src/v1/traits/traces.rs b/rpc/src/v1/traits/traces.rs index 45fa916bef4..a05b2de01a0 100644 --- a/rpc/src/v1/traits/traces.rs +++ b/rpc/src/v1/traits/traces.rs @@ -35,6 +35,9 @@ pub trait Traces: Sized + Send + Sync + 'static { /// Executes the given call and returns a number of possible traces for it. fn call(&self, _: Params) -> Result; + /// Executes the given raw transaction and returns a number of possible traces for it. + fn raw_transaction(&self, _: Params) -> Result; + /// Should be used to convert object to io delegate. fn to_delegate(self) -> IoDelegate { let mut delegate = IoDelegate::new(Arc::new(self)); @@ -43,6 +46,7 @@ pub trait Traces: Sized + Send + Sync + 'static { delegate.add_method("trace_transaction", Traces::transaction_traces); delegate.add_method("trace_block", Traces::block_traces); delegate.add_method("trace_call", Traces::call); + delegate.add_method("trace_rawTransaction", Traces::raw_transaction); delegate } diff --git a/rpc/src/v1/types/block.rs b/rpc/src/v1/types/block.rs index b86723357de..a1cf738c6b3 100644 --- a/rpc/src/v1/types/block.rs +++ b/rpc/src/v1/types/block.rs @@ -103,7 +103,7 @@ mod tests { fn test_serialize_block_transactions() { let t = BlockTransactions::Full(vec![Transaction::default()]); let serialized = serde_json::to_string(&t).unwrap(); - assert_eq!(serialized, r#"[{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x00","blockHash":null,"blockNumber":null,"transactionIndex":null,"from":"0x0000000000000000000000000000000000000000","to":null,"value":"0x00","gasPrice":"0x00","gas":"0x00","input":"0x","creates":null}]"#); + assert_eq!(serialized, r#"[{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x00","blockHash":null,"blockNumber":null,"transactionIndex":null,"from":"0x0000000000000000000000000000000000000000","to":null,"value":"0x00","gasPrice":"0x00","gas":"0x00","input":"0x","creates":null,"raw":"0x"}]"#); let t = BlockTransactions::Hashes(vec![H256::default()]); let serialized = serde_json::to_string(&t).unwrap(); diff --git a/rpc/src/v1/types/mod.rs.in b/rpc/src/v1/types/mod.rs.in index 3f07bfb31ed..f749fe5c2ba 100644 --- a/rpc/src/v1/types/mod.rs.in +++ b/rpc/src/v1/types/mod.rs.in @@ -41,5 +41,5 @@ pub use self::transaction::Transaction; pub use self::transaction_request::{TransactionRequest, TransactionConfirmation, TransactionModification}; pub use self::call_request::CallRequest; pub use self::receipt::Receipt; -pub use self::trace::{Trace, LocalizedTrace, StateDiff, VMTrace}; +pub use self::trace::{LocalizedTrace, TraceResults}; pub use self::trace_filter::TraceFilter; diff --git a/rpc/src/v1/types/trace.rs b/rpc/src/v1/types/trace.rs index 354c88cfc0f..d4183994e69 100644 --- a/rpc/src/v1/types/trace.rs +++ b/rpc/src/v1/types/trace.rs @@ -22,8 +22,9 @@ use ethcore::trace::{Trace as EthTrace, LocalizedTrace as EthLocalizedTrace}; use ethcore::trace as et; use ethcore::state_diff; use ethcore::account_diff; -use ethcore::executed; use v1::types::{Bytes}; +use ethcore::client::Executed; +use ethcore::executed; #[derive(Debug, Serialize)] /// A diff of some chunk of memory. @@ -194,6 +195,7 @@ impl From for AccountDiff { } } +#[derive(Debug)] /// Serde-friendly `StateDiff` shadow. pub struct StateDiff(BTreeMap); @@ -477,6 +479,32 @@ impl From for Trace { } } +#[derive(Debug, Serialize)] +/// A diff of some chunk of memory. +pub struct TraceResults { + /// The output of the call/create + pub output: Vec, + /// The transaction trace. + pub trace: Option, + /// The transaction trace. + #[serde(rename="vmTrace")] + pub vm_trace: Option, + /// The transaction trace. + #[serde(rename="stateDiff")] + pub state_diff: Option, +} + +impl From for TraceResults { + fn from(t: Executed) -> Self { + TraceResults { + output: t.output.into(), + trace: t.trace.map(Into::into), + vm_trace: t.vm_trace.map(Into::into), + state_diff: t.state_diff.map(Into::into), + } + } +} + #[cfg(test)] mod tests { use serde_json; @@ -485,6 +513,18 @@ mod tests { use v1::types::Bytes; use super::*; + #[test] + fn should_serialize_trace_results() { + let r = TraceResults { + output: vec![0x60], + trace: None, + vm_trace: None, + state_diff: None, + }; + let serialized = serde_json::to_string(&r).unwrap(); + assert_eq!(serialized, r#"{"output":[96],"trace":null,"vmTrace":null,"stateDiff":null}"#); + } + #[test] fn test_trace_serialize() { let t = LocalizedTrace { diff --git a/rpc/src/v1/types/transaction.rs b/rpc/src/v1/types/transaction.rs index 6a9f0e5903f..8b67af808b1 100644 --- a/rpc/src/v1/types/transaction.rs +++ b/rpc/src/v1/types/transaction.rs @@ -15,6 +15,7 @@ // along with Parity. If not, see . use util::numbers::*; +use util::rlp::encode; use ethcore::contract_address; use ethcore::transaction::{LocalizedTransaction, Action, SignedTransaction}; use v1::types::{Bytes, OptionalValue}; @@ -50,6 +51,8 @@ pub struct Transaction { pub input: Bytes, /// Creates contract pub creates: OptionalValue
, + /// Raw transaction data + pub raw: Bytes, } impl From for Transaction { @@ -73,6 +76,7 @@ impl From for Transaction { Action::Create => OptionalValue::Value(contract_address(&t.sender().unwrap(), &t.nonce)), Action::Call(_) => OptionalValue::Null, }, + raw: encode(&t.signed).to_vec().into(), } } } @@ -98,6 +102,7 @@ impl From for Transaction { Action::Create => OptionalValue::Value(contract_address(&t.sender().unwrap(), &t.nonce)), Action::Call(_) => OptionalValue::Null, }, + raw: encode(&t).to_vec().into(), } } } @@ -111,7 +116,7 @@ mod tests { fn test_transaction_serialize() { let t = Transaction::default(); let serialized = serde_json::to_string(&t).unwrap(); - assert_eq!(serialized, r#"{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x00","blockHash":null,"blockNumber":null,"transactionIndex":null,"from":"0x0000000000000000000000000000000000000000","to":null,"value":"0x00","gasPrice":"0x00","gas":"0x00","input":"0x","creates":null}"#); + assert_eq!(serialized, r#"{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x00","blockHash":null,"blockNumber":null,"transactionIndex":null,"from":"0x0000000000000000000000000000000000000000","to":null,"value":"0x00","gasPrice":"0x00","gas":"0x00","input":"0x","creates":null,"raw":"0x"}"#); } } From 7b0eda9eaf79eb95d8c05e2e7e6b6cdc4148727d Mon Sep 17 00:00:00 2001 From: Marek Kotewicz Date: Thu, 28 Jul 2016 20:31:29 +0200 Subject: [PATCH 6/6] Stackoverflow fix (#1742) * executive tracer builds flat traces without intermediate struct * temporarilt commented out tests for traces * fixed new way of building trace address * fixed new way of building trace address * updating state tests with flat tracing in progress * fixed flat tracing tests * fixed compiling ethcore-rpc with new flat traces * removed warnings from ethcore module * remove unused data structures --- ethcore/src/block.rs | 12 +- ethcore/src/client/client.rs | 14 +- ethcore/src/executive.rs | 61 ++- ethcore/src/externalities.rs | 2 +- ethcore/src/state.rs | 414 +++++++++--------- ethcore/src/trace/block.rs | 58 --- ethcore/src/trace/db.rs | 32 +- ethcore/src/trace/executive_tracer.rs | 96 ++-- ethcore/src/trace/import.rs | 4 +- ethcore/src/trace/mod.rs | 20 +- ethcore/src/trace/noop_tracer.rs | 16 +- ethcore/src/types/executed.rs | 4 +- ethcore/src/types/trace_types/filter.rs | 6 +- .../src/{trace => types/trace_types}/flat.rs | 155 +------ ethcore/src/types/trace_types/mod.rs | 1 + ethcore/src/types/trace_types/trace.rs | 147 ------- ipc/rpc/src/binary.rs | 68 +++ rpc/src/v1/tests/mocked/eth.rs | 8 +- rpc/src/v1/types/trace.rs | 27 +- 19 files changed, 478 insertions(+), 667 deletions(-) delete mode 100644 ethcore/src/trace/block.rs rename ethcore/src/{trace => types/trace_types}/flat.rs (52%) diff --git a/ethcore/src/block.rs b/ethcore/src/block.rs index 0e33df013fd..bda55776743 100644 --- a/ethcore/src/block.rs +++ b/ethcore/src/block.rs @@ -22,7 +22,7 @@ use common::*; use engine::*; use state::*; use verification::PreverifiedBlock; -use trace::Trace; +use trace::FlatTrace; use evm::Factory as EvmFactory; /// A block, encoded as it is on the block chain. @@ -78,7 +78,7 @@ pub struct ExecutedBlock { receipts: Vec, transactions_set: HashSet, state: State, - traces: Option>, + traces: Option>>, } /// A set of references to `ExecutedBlock` fields that are publicly accessible. @@ -94,7 +94,7 @@ pub struct BlockRefMut<'a> { /// State. pub state: &'a mut State, /// Traces. - pub traces: &'a Option>, + pub traces: &'a Option>>, } /// A set of immutable references to `ExecutedBlock` fields that are publicly accessible. @@ -110,7 +110,7 @@ pub struct BlockRef<'a> { /// State. pub state: &'a State, /// Traces. - pub traces: &'a Option>, + pub traces: &'a Option>>, } impl ExecutedBlock { @@ -171,7 +171,7 @@ pub trait IsBlock { fn receipts(&self) -> &Vec { &self.block().receipts } /// Get all information concerning transaction tracing in this block. - fn traces(&self) -> &Option> { &self.block().traces } + fn traces(&self) -> &Option>> { &self.block().traces } /// Get all uncles in this block. fn uncles(&self) -> &Vec
{ &self.block().base.uncles } @@ -329,7 +329,7 @@ impl<'x> OpenBlock<'x> { self.block.transactions_set.insert(h.unwrap_or_else(||t.hash())); self.block.base.transactions.push(t); let t = outcome.trace; - self.block.traces.as_mut().map(|traces| traces.push(t.expect("self.block.traces.is_some(): so we must be tracing: qed"))); + self.block.traces.as_mut().map(|traces| traces.push(t)); self.block.receipts.push(outcome.receipt); Ok(&self.block.receipts.last().unwrap()) } diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index c550163e976..54b2d77db08 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -48,6 +48,9 @@ use receipt::LocalizedReceipt; pub use blockchain::CacheSize as BlockChainCacheSize; use trace::{TraceDB, ImportRequest as TraceImportRequest, LocalizedTrace, Database as TraceDatabase}; use trace; +use trace::FlatTransactionTraces; + +// re-export pub use types::blockchain_info::BlockChainInfo; pub use types::block_status::BlockStatus; use evm::Factory as EvmFactory; @@ -361,8 +364,13 @@ impl Client { }; // Commit results - let receipts = block.receipts().clone(); - let traces = From::from(block.traces().clone().unwrap_or_else(Vec::new)); + let receipts = block.receipts().to_owned(); + let traces = block.traces().clone().unwrap_or_else(Vec::new); + let traces: Vec = traces.into_iter() + .map(Into::into) + .collect(); + + //let traces = From::from(block.traces().clone().unwrap_or_else(Vec::new)); // CHECK! I *think* this is fine, even if the state_root is equal to another // already-imported block of the same number. @@ -373,7 +381,7 @@ impl Client { // (when something is in chain but you are not able to fetch details) let route = self.chain.insert_block(block_data, receipts); self.tracedb.import(TraceImportRequest { - traces: traces, + traces: traces.into(), block_hash: hash.clone(), block_number: number, enacted: route.enacted.clone(), diff --git a/ethcore/src/executive.rs b/ethcore/src/executive.rs index 137d3b78ff6..8ca95b97efd 100644 --- a/ethcore/src/executive.rs +++ b/ethcore/src/executive.rs @@ -22,7 +22,7 @@ use types::executed::CallType; use evm::{self, Ext, Factory, Finalize}; use externalities::*; use substate::*; -use trace::{Trace, Tracer, NoopTracer, ExecutiveTracer, VMTrace, VMTracer, ExecutiveVMTracer, NoopVMTracer}; +use trace::{FlatTrace, Tracer, NoopTracer, ExecutiveTracer, VMTrace, VMTracer, ExecutiveVMTracer, NoopVMTracer}; use crossbeam; pub use types::executed::{Executed, ExecutionResult}; @@ -198,7 +198,7 @@ impl<'a> Executive<'a> { }; // finalize here! - Ok(try!(self.finalize(t, substate, gas_left, output, tracer.traces().pop(), vm_tracer.drain()))) + Ok(try!(self.finalize(t, substate, gas_left, output, tracer.traces(), vm_tracer.drain()))) } fn exec_vm( @@ -276,7 +276,6 @@ impl<'a> Executive<'a> { trace_info, cost, trace_output, - self.depth, vec![] ); } @@ -285,7 +284,7 @@ impl<'a> Executive<'a> { // just drain the whole gas false => { self.state.revert_snapshot(); - tracer.trace_failed_call(trace_info, self.depth, vec![]); + tracer.trace_failed_call(trace_info, vec![]); Err(evm::Error::OutOfGas) } } @@ -317,10 +316,9 @@ impl<'a> Executive<'a> { trace_info, gas - gas_left, trace_output, - self.depth, traces ), - _ => tracer.trace_failed_call(trace_info, self.depth, traces), + _ => tracer.trace_failed_call(trace_info, traces), }; trace!(target: "executive", "substate={:?}; unconfirmed_substate={:?}\n", substate, unconfirmed_substate); @@ -332,7 +330,7 @@ impl<'a> Executive<'a> { // otherwise it's just a basic transaction, only do tracing, if necessary. self.state.clear_snapshot(); - tracer.trace_call(trace_info, U256::zero(), trace_output, self.depth, vec![]); + tracer.trace_call(trace_info, U256::zero(), trace_output, vec![]); Ok(params.gas) } } @@ -383,10 +381,9 @@ impl<'a> Executive<'a> { gas - gas_left, trace_output, created, - self.depth, subtracer.traces() ), - _ => tracer.trace_failed_create(trace_info, self.depth, subtracer.traces()) + _ => tracer.trace_failed_create(trace_info, subtracer.traces()) }; self.enact_result(&res, substate, unconfirmed_substate); @@ -400,7 +397,7 @@ impl<'a> Executive<'a> { substate: Substate, result: evm::Result, output: Bytes, - trace: Option, + trace: Vec, vm_trace: Option ) -> ExecutionResult { let schedule = self.engine.schedule(self.info); @@ -492,7 +489,7 @@ mod tests { use substate::*; use tests::helpers::*; use trace::trace; - use trace::{Trace, Tracer, NoopTracer, ExecutiveTracer}; + use trace::{FlatTrace, Tracer, NoopTracer, ExecutiveTracer}; use trace::{VMTrace, VMOperation, VMExecutedOperation, MemoryDiff, StorageDiff, VMTracer, NoopVMTracer, ExecutiveVMTracer}; use types::executed::CallType; @@ -646,8 +643,9 @@ mod tests { assert_eq!(gas_left, U256::from(44_752)); - let expected_trace = vec![ Trace { - depth: 0, + let expected_trace = vec![FlatTrace { + trace_address: Default::default(), + subtraces: 1, action: trace::Action::Call(trace::Call { from: "cd1722f3947def4cf144679da39c4c32bdc35681".into(), to: "b010143a42d5980c7e5ef0e4a4416dc098a4fed3".into(), @@ -660,22 +658,22 @@ mod tests { gas_used: U256::from(55_248), output: vec![], }), - subs: vec![Trace { - depth: 1, - action: trace::Action::Create(trace::Create { - from: "b010143a42d5980c7e5ef0e4a4416dc098a4fed3".into(), - value: 23.into(), - gas: 67979.into(), - init: vec![96, 16, 128, 96, 12, 96, 0, 57, 96, 0, 243, 0, 96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53, 85] - }), - result: trace::Res::Create(trace::CreateResult { - gas_used: U256::from(3224), - address: Address::from_str("c6d80f262ae5e0f164e5fde365044d7ada2bfa34").unwrap(), - code: vec![96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53] - }), - subs: vec![] - }] + }, FlatTrace { + trace_address: vec![0].into_iter().collect(), + subtraces: 0, + action: trace::Action::Create(trace::Create { + from: "b010143a42d5980c7e5ef0e4a4416dc098a4fed3".into(), + value: 23.into(), + gas: 67979.into(), + init: vec![96, 16, 128, 96, 12, 96, 0, 57, 96, 0, 243, 0, 96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53, 85] + }), + result: trace::Res::Create(trace::CreateResult { + gas_used: U256::from(3224), + address: Address::from_str("c6d80f262ae5e0f164e5fde365044d7ada2bfa34").unwrap(), + code: vec![96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53] + }), }]; + assert_eq!(tracer.traces(), expected_trace); let expected_vm_trace = VMTrace { @@ -753,8 +751,9 @@ mod tests { assert_eq!(gas_left, U256::from(96_776)); - let expected_trace = vec![Trace { - depth: 0, + let expected_trace = vec![FlatTrace { + trace_address: Default::default(), + subtraces: 0, action: trace::Action::Create(trace::Create { from: params.sender, value: 100.into(), @@ -766,8 +765,8 @@ mod tests { address: params.address, code: vec![96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53] }), - subs: vec![] }]; + assert_eq!(tracer.traces(), expected_trace); let expected_vm_trace = VMTrace { diff --git a/ethcore/src/externalities.rs b/ethcore/src/externalities.rs index 7d55f164869..2dbd5f8ee9f 100644 --- a/ethcore/src/externalities.rs +++ b/ethcore/src/externalities.rs @@ -267,7 +267,7 @@ impl<'a, T, V> Ext for Externalities<'a, T, V> where T: 'a + Tracer, V: 'a + VMT self.state.transfer_balance(&address, refund_address, &balance); } - self.tracer.trace_suicide(address, balance, refund_address.clone(), self.depth + 1); + self.tracer.trace_suicide(address, balance, refund_address.clone()); self.substate.suicides.insert(address); } diff --git a/ethcore/src/state.rs b/ethcore/src/state.rs index 9ab04b08468..0cf8a8f08cc 100644 --- a/ethcore/src/state.rs +++ b/ethcore/src/state.rs @@ -19,7 +19,7 @@ use engine::Engine; use executive::{Executive, TransactOptions}; use evm::Factory as EvmFactory; use account_db::*; -use trace::Trace; +use trace::FlatTrace; use pod_account::*; use pod_state::{self, PodState}; use types::state_diff::StateDiff; @@ -29,7 +29,7 @@ pub struct ApplyOutcome { /// The receipt for the applied transaction. pub receipt: Receipt, /// The trace for the applied transaction, if None if tracing is disabled. - pub trace: Option, + pub trace: Vec, } /// Result type for the execution ("application") of a transaction. @@ -389,7 +389,7 @@ use spec::*; use transaction::*; use util::log::init_log; use trace::trace; -use trace::trace::{Trace}; +use trace::FlatTrace; use types::executed::CallType; #[test] @@ -415,8 +415,9 @@ fn should_apply_create_transaction() { state.add_balance(t.sender().as_ref().unwrap(), &(100.into())); let vm_factory = Default::default(); let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap(); - let expected_trace = Some(Trace { - depth: 0, + let expected_trace = vec![FlatTrace { + trace_address: Default::default(), + subtraces: 0, action: trace::Action::Create(trace::Create { from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), value: 100.into(), @@ -428,8 +429,7 @@ fn should_apply_create_transaction() { address: Address::from_str("8988167e088c87cd314df6d3c2b83da5acb93ace").unwrap(), code: vec![96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53] }), - subs: vec![] - }); + }]; assert_eq!(result.trace, expected_trace); } @@ -476,8 +476,8 @@ fn should_trace_failed_create_transaction() { state.add_balance(t.sender().as_ref().unwrap(), &(100.into())); let vm_factory = Default::default(); let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap(); - let expected_trace = Some(Trace { - depth: 0, + let expected_trace = vec![FlatTrace { + trace_address: Default::default(), action: trace::Action::Create(trace::Create { from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), value: 100.into(), @@ -485,8 +485,8 @@ fn should_trace_failed_create_transaction() { init: vec![91, 96, 0, 86], }), result: trace::Res::FailedCreate, - subs: vec![] - }); + subtraces: 0 + }]; assert_eq!(result.trace, expected_trace); } @@ -515,8 +515,8 @@ fn should_trace_call_transaction() { state.add_balance(t.sender().as_ref().unwrap(), &(100.into())); let vm_factory = Default::default(); let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap(); - let expected_trace = Some(Trace { - depth: 0, + let expected_trace = vec![FlatTrace { + trace_address: Default::default(), action: trace::Action::Call(trace::Call { from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), to: 0xa.into(), @@ -529,8 +529,8 @@ fn should_trace_call_transaction() { gas_used: U256::from(3), output: vec![] }), - subs: vec![] - }); + subtraces: 0, + }]; assert_eq!(result.trace, expected_trace); } @@ -558,8 +558,8 @@ fn should_trace_basic_call_transaction() { state.add_balance(t.sender().as_ref().unwrap(), &(100.into())); let vm_factory = Default::default(); let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap(); - let expected_trace = Some(Trace { - depth: 0, + let expected_trace = vec![FlatTrace { + trace_address: Default::default(), action: trace::Action::Call(trace::Call { from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), to: 0xa.into(), @@ -572,8 +572,8 @@ fn should_trace_basic_call_transaction() { gas_used: U256::from(0), output: vec![] }), - subs: vec![] - }); + subtraces: 0, + }]; assert_eq!(result.trace, expected_trace); } @@ -601,8 +601,8 @@ fn should_trace_call_transaction_to_builtin() { let vm_factory = Default::default(); let result = state.apply(&info, engine.deref(), &vm_factory, &t, true).unwrap(); - assert_eq!(result.trace, Some(Trace { - depth: 0, + let expected_trace = vec![FlatTrace { + trace_address: Default::default(), action: trace::Action::Call(trace::Call { from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), to: "0000000000000000000000000000000000000001".into(), @@ -615,8 +615,10 @@ fn should_trace_call_transaction_to_builtin() { gas_used: U256::from(3000), output: vec![] }), - subs: vec![] - })); + subtraces: 0, + }]; + + assert_eq!(result.trace, expected_trace); } #[test] @@ -643,8 +645,8 @@ fn should_not_trace_subcall_transaction_to_builtin() { let vm_factory = Default::default(); let result = state.apply(&info, engine.deref(), &vm_factory, &t, true).unwrap(); - let expected_trace = Some(Trace { - depth: 0, + let expected_trace = vec![FlatTrace { + trace_address: Default::default(), action: trace::Action::Call(trace::Call { from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), to: 0xa.into(), @@ -657,8 +659,9 @@ fn should_not_trace_subcall_transaction_to_builtin() { gas_used: U256::from(28_061), output: vec![] }), - subs: vec![] - }); + subtraces: 0, + }]; + assert_eq!(result.trace, expected_trace); } @@ -687,8 +690,9 @@ fn should_not_trace_callcode() { let vm_factory = Default::default(); let result = state.apply(&info, engine.deref(), &vm_factory, &t, true).unwrap(); - let expected_trace = Some(Trace { - depth: 0, + let expected_trace = vec![FlatTrace { + trace_address: Default::default(), + subtraces: 1, action: trace::Action::Call(trace::Call { from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), to: 0xa.into(), @@ -701,23 +705,23 @@ fn should_not_trace_callcode() { gas_used: 64.into(), output: vec![] }), - subs: vec![Trace { - depth: 1, - action: trace::Action::Call(trace::Call { - from: 0xa.into(), - to: 0xa.into(), - value: 0.into(), - gas: 4096.into(), - input: vec![], - call_type: CallType::CallCode, - }), - subs: vec![], - result: trace::Res::Call(trace::CallResult { - gas_used: 3.into(), - output: vec![], - }), - }], - }); + }, FlatTrace { + trace_address: vec![0].into_iter().collect(), + subtraces: 0, + action: trace::Action::Call(trace::Call { + from: 0xa.into(), + to: 0xa.into(), + value: 0.into(), + gas: 4096.into(), + input: vec![], + call_type: CallType::CallCode, + }), + result: trace::Res::Call(trace::CallResult { + gas_used: 3.into(), + output: vec![], + }), + }]; + assert_eq!(result.trace, expected_trace); } @@ -749,8 +753,9 @@ fn should_not_trace_delegatecall() { let vm_factory = Default::default(); let result = state.apply(&info, engine.deref(), &vm_factory, &t, true).unwrap(); - let expected_trace = Some(Trace { - depth: 0, + let expected_trace = vec![FlatTrace { + trace_address: Default::default(), + subtraces: 1, action: trace::Action::Call(trace::Call { from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), to: 0xa.into(), @@ -763,23 +768,23 @@ fn should_not_trace_delegatecall() { gas_used: U256::from(61), output: vec![] }), - subs: vec![Trace { - depth: 1, - action: trace::Action::Call(trace::Call { - from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), - to: 0xa.into(), - value: 0.into(), - gas: 32768.into(), - input: vec![], - call_type: CallType::DelegateCall, - }), - subs: vec![], - result: trace::Res::Call(trace::CallResult { - gas_used: 3.into(), - output: vec![], - }), - }], - }); + }, FlatTrace { + trace_address: vec![0].into_iter().collect(), + subtraces: 0, + action: trace::Action::Call(trace::Call { + from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), + to: 0xa.into(), + value: 0.into(), + gas: 32768.into(), + input: vec![], + call_type: CallType::DelegateCall, + }), + result: trace::Res::Call(trace::CallResult { + gas_used: 3.into(), + output: vec![], + }), + }]; + assert_eq!(result.trace, expected_trace); } @@ -807,8 +812,8 @@ fn should_trace_failed_call_transaction() { state.add_balance(t.sender().as_ref().unwrap(), &(100.into())); let vm_factory = Default::default(); let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap(); - let expected_trace = Some(Trace { - depth: 0, + let expected_trace = vec![FlatTrace { + trace_address: Default::default(), action: trace::Action::Call(trace::Call { from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), to: 0xa.into(), @@ -818,10 +823,8 @@ fn should_trace_failed_call_transaction() { call_type: CallType::Call, }), result: trace::Res::FailedCall, - subs: vec![] - }); - - println!("trace: {:?}", result.trace); + subtraces: 0, + }]; assert_eq!(result.trace, expected_trace); } @@ -851,8 +854,10 @@ fn should_trace_call_with_subcall_transaction() { state.add_balance(t.sender().as_ref().unwrap(), &(100.into())); let vm_factory = Default::default(); let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap(); - let expected_trace = Some(Trace { - depth: 0, + + let expected_trace = vec![FlatTrace { + trace_address: Default::default(), + subtraces: 1, action: trace::Action::Call(trace::Call { from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), to: 0xa.into(), @@ -865,23 +870,22 @@ fn should_trace_call_with_subcall_transaction() { gas_used: U256::from(69), output: vec![] }), - subs: vec![Trace { - depth: 1, - action: trace::Action::Call(trace::Call { - from: 0xa.into(), - to: 0xb.into(), - value: 0.into(), - gas: 78934.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::Call(trace::CallResult { - gas_used: U256::from(3), - output: vec![] - }), - subs: vec![] - }] - }); + }, FlatTrace { + trace_address: vec![0].into_iter().collect(), + subtraces: 0, + action: trace::Action::Call(trace::Call { + from: 0xa.into(), + to: 0xb.into(), + value: 0.into(), + gas: 78934.into(), + input: vec![], + call_type: CallType::Call, + }), + result: trace::Res::Call(trace::CallResult { + gas_used: U256::from(3), + output: vec![] + }), + }]; assert_eq!(result.trace, expected_trace); } @@ -910,8 +914,9 @@ fn should_trace_call_with_basic_subcall_transaction() { state.add_balance(t.sender().as_ref().unwrap(), &(100.into())); let vm_factory = Default::default(); let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap(); - let expected_trace = Some(Trace { - depth: 0, + let expected_trace = vec![FlatTrace { + trace_address: Default::default(), + subtraces: 1, action: trace::Action::Call(trace::Call { from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), to: 0xa.into(), @@ -924,20 +929,19 @@ fn should_trace_call_with_basic_subcall_transaction() { gas_used: U256::from(31761), output: vec![] }), - subs: vec![Trace { - depth: 1, - action: trace::Action::Call(trace::Call { - from: 0xa.into(), - to: 0xb.into(), - value: 69.into(), - gas: 2300.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::Call(trace::CallResult::default()), - subs: vec![] - }] - }); + }, FlatTrace { + trace_address: vec![0].into_iter().collect(), + subtraces: 0, + action: trace::Action::Call(trace::Call { + from: 0xa.into(), + to: 0xb.into(), + value: 69.into(), + gas: 2300.into(), + input: vec![], + call_type: CallType::Call, + }), + result: trace::Res::Call(trace::CallResult::default()), + }]; assert_eq!(result.trace, expected_trace); } @@ -966,8 +970,9 @@ fn should_not_trace_call_with_invalid_basic_subcall_transaction() { state.add_balance(t.sender().as_ref().unwrap(), &(100.into())); let vm_factory = Default::default(); let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap(); - let expected_trace = Some(Trace { - depth: 0, + let expected_trace = vec![FlatTrace { + trace_address: Default::default(), + subtraces: 0, action: trace::Action::Call(trace::Call { from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), to: 0xa.into(), @@ -980,8 +985,7 @@ fn should_not_trace_call_with_invalid_basic_subcall_transaction() { gas_used: U256::from(31761), output: vec![] }), - subs: vec![] - }); + }]; assert_eq!(result.trace, expected_trace); } @@ -1011,8 +1015,9 @@ fn should_trace_failed_subcall_transaction() { state.add_balance(t.sender().as_ref().unwrap(), &(100.into())); let vm_factory = Default::default(); let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap(); - let expected_trace = Some(Trace { - depth: 0, + let expected_trace = vec![FlatTrace { + trace_address: Default::default(), + subtraces: 1, action: trace::Action::Call(trace::Call { from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), to: 0xa.into(), @@ -1025,20 +1030,19 @@ fn should_trace_failed_subcall_transaction() { gas_used: U256::from(79_000), output: vec![] }), - subs: vec![Trace { - depth: 1, - action: trace::Action::Call(trace::Call { - from: 0xa.into(), - to: 0xb.into(), - value: 0.into(), - gas: 78934.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::FailedCall, - subs: vec![] - }] - }); + }, FlatTrace { + trace_address: vec![0].into_iter().collect(), + subtraces: 0, + action: trace::Action::Call(trace::Call { + from: 0xa.into(), + to: 0xb.into(), + value: 0.into(), + gas: 78934.into(), + input: vec![], + call_type: CallType::Call, + }), + result: trace::Res::FailedCall, + }]; assert_eq!(result.trace, expected_trace); } @@ -1069,8 +1073,9 @@ fn should_trace_call_with_subcall_with_subcall_transaction() { state.add_balance(t.sender().as_ref().unwrap(), &(100.into())); let vm_factory = Default::default(); let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap(); - let expected_trace = Some(Trace { - depth: 0, + let expected_trace = vec![FlatTrace { + trace_address: Default::default(), + subtraces: 1, action: trace::Action::Call(trace::Call { from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), to: 0xa.into(), @@ -1083,38 +1088,37 @@ fn should_trace_call_with_subcall_with_subcall_transaction() { gas_used: U256::from(135), output: vec![] }), - subs: vec![Trace { - depth: 1, - action: trace::Action::Call(trace::Call { - from: 0xa.into(), - to: 0xb.into(), - value: 0.into(), - gas: 78934.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::Call(trace::CallResult { - gas_used: U256::from(69), - output: vec![] - }), - subs: vec![Trace { - depth: 2, - action: trace::Action::Call(trace::Call { - from: 0xb.into(), - to: 0xc.into(), - value: 0.into(), - gas: 78868.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::Call(trace::CallResult { - gas_used: U256::from(3), - output: vec![] - }), - subs: vec![] - }] - }] - }); + }, FlatTrace { + trace_address: vec![0].into_iter().collect(), + subtraces: 1, + action: trace::Action::Call(trace::Call { + from: 0xa.into(), + to: 0xb.into(), + value: 0.into(), + gas: 78934.into(), + input: vec![], + call_type: CallType::Call, + }), + result: trace::Res::Call(trace::CallResult { + gas_used: U256::from(69), + output: vec![] + }), + }, FlatTrace { + trace_address: vec![0, 0].into_iter().collect(), + subtraces: 0, + action: trace::Action::Call(trace::Call { + from: 0xb.into(), + to: 0xc.into(), + value: 0.into(), + gas: 78868.into(), + input: vec![], + call_type: CallType::Call, + }), + result: trace::Res::Call(trace::CallResult { + gas_used: U256::from(3), + output: vec![] + }), + }]; assert_eq!(result.trace, expected_trace); } @@ -1145,8 +1149,10 @@ fn should_trace_failed_subcall_with_subcall_transaction() { state.add_balance(t.sender().as_ref().unwrap(), &(100.into())); let vm_factory = Default::default(); let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap(); - let expected_trace = Some(Trace { - depth: 0, + + let expected_trace = vec![FlatTrace { + trace_address: Default::default(), + subtraces: 1, action: trace::Action::Call(trace::Call { from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), to: 0xa.into(), @@ -1158,36 +1164,35 @@ fn should_trace_failed_subcall_with_subcall_transaction() { result: trace::Res::Call(trace::CallResult { gas_used: U256::from(79_000), output: vec![] - }), - subs: vec![Trace { - depth: 1, + }) + }, FlatTrace { + trace_address: vec![0].into_iter().collect(), + subtraces: 1, action: trace::Action::Call(trace::Call { - from: 0xa.into(), - to: 0xb.into(), - value: 0.into(), - gas: 78934.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::FailedCall, - subs: vec![Trace { - depth: 2, - action: trace::Action::Call(trace::Call { - from: 0xb.into(), - to: 0xc.into(), - value: 0.into(), - gas: 78868.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::Call(trace::CallResult { - gas_used: U256::from(3), - output: vec![] - }), - subs: vec![] - }] - }] - }); + from: 0xa.into(), + to: 0xb.into(), + value: 0.into(), + gas: 78934.into(), + input: vec![], + call_type: CallType::Call, + }), + result: trace::Res::FailedCall, + }, FlatTrace { + trace_address: vec![0, 0].into_iter().collect(), + subtraces: 0, + action: trace::Action::Call(trace::Call { + from: 0xb.into(), + to: 0xc.into(), + value: 0.into(), + gas: 78868.into(), + call_type: CallType::Call, + input: vec![], + }), + result: trace::Res::Call(trace::CallResult { + gas_used: U256::from(3), + output: vec![] + }), + }]; assert_eq!(result.trace, expected_trace); } @@ -1217,8 +1222,9 @@ fn should_trace_suicide() { state.add_balance(t.sender().as_ref().unwrap(), &100.into()); let vm_factory = Default::default(); let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap(); - let expected_trace = Some(Trace { - depth: 0, + let expected_trace = vec![FlatTrace { + trace_address: Default::default(), + subtraces: 1, action: trace::Action::Call(trace::Call { from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), to: 0xa.into(), @@ -1231,17 +1237,17 @@ fn should_trace_suicide() { gas_used: 3.into(), output: vec![] }), - subs: vec![Trace { - depth: 1, - action: trace::Action::Suicide(trace::Suicide { - address: 0xa.into(), - refund_address: 0xb.into(), - balance: 150.into(), - }), - result: trace::Res::None, - subs: vec![] - }] - }); + }, FlatTrace { + trace_address: vec![0].into_iter().collect(), + subtraces: 0, + action: trace::Action::Suicide(trace::Suicide { + address: 0xa.into(), + refund_address: 0xb.into(), + balance: 150.into(), + }), + result: trace::Res::None, + }]; + assert_eq!(result.trace, expected_trace); } diff --git a/ethcore/src/trace/block.rs b/ethcore/src/trace/block.rs deleted file mode 100644 index bc53f77e2c6..00000000000 --- a/ethcore/src/trace/block.rs +++ /dev/null @@ -1,58 +0,0 @@ -// 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 . - -use util::rlp::*; -use basic_types::LogBloom; -use super::Trace; - -/// Traces created by transactions from the same block. -#[derive(Clone)] -pub struct BlockTraces(Vec); - -impl From> for BlockTraces { - fn from(traces: Vec) -> Self { - BlockTraces(traces) - } -} - -impl Into> for BlockTraces { - fn into(self) -> Vec { - self.0 - } -} - -impl Decodable for BlockTraces { - fn decode(decoder: &D) -> Result where D: Decoder { - let traces = try!(Decodable::decode(decoder)); - let block_traces = BlockTraces(traces); - Ok(block_traces) - } -} - -impl Encodable for BlockTraces { - fn rlp_append(&self, s: &mut RlpStream) { - Encodable::rlp_append(&self.0, s) - } -} - -impl BlockTraces { - /// Returns bloom of all traces in given block. - pub fn bloom(&self) -> LogBloom { - self.0.iter() - .fold(LogBloom::default(), |acc, trace| acc | trace.bloom()) - } -} - diff --git a/ethcore/src/trace/db.rs b/ethcore/src/trace/db.rs index 69ea95bd2d2..f68cb6cf17c 100644 --- a/ethcore/src/trace/db.rs +++ b/ethcore/src/trace/db.rs @@ -197,7 +197,7 @@ impl TraceDB where T: DatabaseExtras { action: trace.action, result: trace.result, subtraces: trace.subtraces, - trace_address: trace.trace_address, + trace_address: trace.trace_address.into_iter().collect(), transaction_number: tx_number, transaction_hash: tx_hash.clone(), block_number: block_number, @@ -230,7 +230,7 @@ impl TraceDatabase for TraceDB where T: DatabaseExtras { let mut traces = self.traces.write().unwrap(); // it's important to use overwrite here, // cause this value might be queried by hash later - batch.write_with_cache(traces.deref_mut(), request.block_hash, request.traces.into(), CacheUpdatePolicy::Overwrite); + batch.write_with_cache(traces.deref_mut(), request.block_hash, request.traces, CacheUpdatePolicy::Overwrite); } // now let's rebuild the blooms @@ -263,12 +263,13 @@ impl TraceDatabase for TraceDB where T: DatabaseExtras { } fn trace(&self, block_number: BlockNumber, tx_position: usize, trace_position: Vec) -> Option { + let trace_position_deq = trace_position.into_iter().collect(); self.extras.block_hash(block_number) .and_then(|block_hash| self.transactions_traces(&block_hash) .and_then(|traces| traces.into_iter().nth(tx_position)) .map(Into::>::into) // this may and should be optimized - .and_then(|traces| traces.into_iter().find(|trace| trace.trace_address == trace_position)) + .and_then(|traces| traces.into_iter().find(|trace| trace.trace_address == trace_position_deq)) .map(|trace| { let tx_hash = self.extras.transaction_hash(block_number, tx_position) .expect("Expected to find transaction hash. Database is probably corrupted"); @@ -277,7 +278,7 @@ impl TraceDatabase for TraceDB where T: DatabaseExtras { action: trace.action, result: trace.result, subtraces: trace.subtraces, - trace_address: trace.trace_address, + trace_address: trace.trace_address.into_iter().collect(), transaction_number: tx_position, transaction_hash: tx_hash, block_number: block_number, @@ -301,7 +302,7 @@ impl TraceDatabase for TraceDB where T: DatabaseExtras { action: trace.action, result: trace.result, subtraces: trace.subtraces, - trace_address: trace.trace_address, + trace_address: trace.trace_address.into_iter().collect(), transaction_number: tx_position, transaction_hash: tx_hash.clone(), block_number: block_number, @@ -328,7 +329,7 @@ impl TraceDatabase for TraceDB where T: DatabaseExtras { action: trace.action, result: trace.result, subtraces: trace.subtraces, - trace_address: trace.trace_address, + trace_address: trace.trace_address.into_iter().collect(), transaction_number: tx_position, transaction_hash: tx_hash.clone(), block_number: block_number, @@ -365,8 +366,9 @@ mod tests { use devtools::RandomTempPath; use header::BlockNumber; use trace::{Config, Switch, TraceDB, Database, DatabaseExtras, ImportRequest}; - use trace::{BlockTraces, Trace, Filter, LocalizedTrace, AddressesFilter}; + use trace::{Filter, LocalizedTrace, AddressesFilter}; use trace::trace::{Call, Action, Res}; + use trace::flat::{FlatTrace, FlatBlockTraces, FlatTransactionTraces}; use types::executed::CallType; struct NoopExtras; @@ -485,19 +487,19 @@ mod tests { fn create_simple_import_request(block_number: BlockNumber, block_hash: H256) -> ImportRequest { ImportRequest { - traces: BlockTraces::from(vec![Trace { - depth: 0, + traces: FlatBlockTraces::from(vec![FlatTransactionTraces::from(vec![FlatTrace { + trace_address: Default::default(), + subtraces: 0, action: Action::Call(Call { - from: Address::from(1), - to: Address::from(2), - value: U256::from(3), - gas: U256::from(4), + from: 1.into(), + to: 2.into(), + value: 3.into(), + gas: 4.into(), input: vec![], call_type: CallType::Call, }), result: Res::FailedCall, - subs: vec![], - }]), + }])]), block_hash: block_hash.clone(), block_number: block_number, enacted: vec![block_hash], diff --git a/ethcore/src/trace/executive_tracer.rs b/ethcore/src/trace/executive_tracer.rs index 83761ea2388..1ada91c4881 100644 --- a/ethcore/src/trace/executive_tracer.rs +++ b/ethcore/src/trace/executive_tracer.rs @@ -18,13 +18,53 @@ use util::{Bytes, Address, U256}; use action_params::ActionParams; -use trace::trace::{Trace, Call, Create, Action, Res, CreateResult, CallResult, VMTrace, VMOperation, VMExecutedOperation, MemoryDiff, StorageDiff, Suicide}; -use trace::{Tracer, VMTracer}; +use trace::trace::{Call, Create, Action, Res, CreateResult, CallResult, VMTrace, VMOperation, VMExecutedOperation, MemoryDiff, StorageDiff, Suicide}; +use trace::{Tracer, VMTracer, FlatTrace}; /// Simple executive tracer. Traces all calls and creates. Ignores delegatecalls. #[derive(Default)] pub struct ExecutiveTracer { - traces: Vec, + traces: Vec, +} + +fn top_level_subtraces(traces: &[FlatTrace]) -> usize { + traces.iter().filter(|t| t.trace_address.is_empty()).count() +} + +fn update_trace_address(traces: Vec) -> Vec { + // input traces are expected to be ordered like + // [] + // [0] + // [0, 0] + // [0, 1] + // [] + // [0] + // + // so they can be transformed to + // + // [0] + // [0, 0] + // [0, 0, 0] + // [0, 0, 1] + // [1] + // [1, 0] + let mut top_subtrace_index = 0; + let mut subtrace_subtraces_left = 0; + traces.into_iter().map(|mut trace| { + let is_top_subtrace = trace.trace_address.is_empty(); + trace.trace_address.push_front(top_subtrace_index); + + if is_top_subtrace { + subtrace_subtraces_left = trace.subtraces; + } else { + subtrace_subtraces_left -= 1; + } + + if subtrace_subtraces_left == 0 { + top_subtrace_index += 1; + } + trace + }).collect() } impl Tracer for ExecutiveTracer { @@ -40,63 +80,67 @@ impl Tracer for ExecutiveTracer { Some(vec![]) } - fn trace_call(&mut self, call: Option, gas_used: U256, output: Option, depth: usize, subs: Vec) { - let trace = Trace { - depth: depth, - subs: subs, + fn trace_call(&mut self, call: Option, gas_used: U256, output: Option, subs: Vec) { + let trace = FlatTrace { + trace_address: Default::default(), + subtraces: top_level_subtraces(&subs), action: Action::Call(call.expect("self.prepare_trace_call().is_some(): so we must be tracing: qed")), result: Res::Call(CallResult { gas_used: gas_used, output: output.expect("self.prepare_trace_output().is_some(): so we must be tracing: qed") - }) + }), }; self.traces.push(trace); + self.traces.extend(update_trace_address(subs)); } - fn trace_create(&mut self, create: Option, gas_used: U256, code: Option, address: Address, depth: usize, subs: Vec) { - let trace = Trace { - depth: depth, - subs: subs, + fn trace_create(&mut self, create: Option, gas_used: U256, code: Option, address: Address, subs: Vec) { + let trace = FlatTrace { + subtraces: top_level_subtraces(&subs), action: Action::Create(create.expect("self.prepare_trace_create().is_some(): so we must be tracing: qed")), result: Res::Create(CreateResult { gas_used: gas_used, code: code.expect("self.prepare_trace_output.is_some(): so we must be tracing: qed"), address: address - }) + }), + trace_address: Default::default(), }; self.traces.push(trace); + self.traces.extend(update_trace_address(subs)); } - fn trace_failed_call(&mut self, call: Option, depth: usize, subs: Vec) { - let trace = Trace { - depth: depth, - subs: subs, + fn trace_failed_call(&mut self, call: Option, subs: Vec) { + let trace = FlatTrace { + trace_address: Default::default(), + subtraces: top_level_subtraces(&subs), action: Action::Call(call.expect("self.prepare_trace_call().is_some(): so we must be tracing: qed")), result: Res::FailedCall, }; self.traces.push(trace); + self.traces.extend(update_trace_address(subs)); } - fn trace_failed_create(&mut self, create: Option, depth: usize, subs: Vec) { - let trace = Trace { - depth: depth, - subs: subs, + fn trace_failed_create(&mut self, create: Option, subs: Vec) { + let trace = FlatTrace { + subtraces: top_level_subtraces(&subs), action: Action::Create(create.expect("self.prepare_trace_create().is_some(): so we must be tracing: qed")), result: Res::FailedCreate, + trace_address: Default::default(), }; self.traces.push(trace); + self.traces.extend(update_trace_address(subs)); } - fn trace_suicide(&mut self, address: Address, balance: U256, refund_address: Address, depth: usize) { - let trace = Trace { - depth: depth, - subs: vec![], + fn trace_suicide(&mut self, address: Address, balance: U256, refund_address: Address) { + let trace = FlatTrace { + subtraces: 0, action: Action::Suicide(Suicide { address: address, refund_address: refund_address, balance: balance, }), result: Res::None, + trace_address: Default::default(), }; self.traces.push(trace); } @@ -105,7 +149,7 @@ impl Tracer for ExecutiveTracer { ExecutiveTracer::default() } - fn traces(self) -> Vec { + fn traces(self) -> Vec { self.traces } } diff --git a/ethcore/src/trace/import.rs b/ethcore/src/trace/import.rs index a6b4a29bbbe..7da3e5fe27a 100644 --- a/ethcore/src/trace/import.rs +++ b/ethcore/src/trace/import.rs @@ -17,12 +17,12 @@ //! Traces import request. use util::H256; use header::BlockNumber; -use trace::BlockTraces; +use trace::FlatBlockTraces; /// Traces import request. pub struct ImportRequest { /// Traces to import. - pub traces: BlockTraces, + pub traces: FlatBlockTraces, /// Hash of traces block. pub block_hash: H256, /// Number of traces block. diff --git a/ethcore/src/trace/mod.rs b/ethcore/src/trace/mod.rs index 0e4d9f626da..3a3e241928d 100644 --- a/ethcore/src/trace/mod.rs +++ b/ethcore/src/trace/mod.rs @@ -16,22 +16,20 @@ //! Tracing -mod block; mod bloom; mod config; mod db; mod error; mod executive_tracer; -pub mod flat; mod import; mod noop_tracer; pub use types::trace_types::*; -pub use self::block::BlockTraces; pub use self::config::{Config, Switch}; pub use self::db::TraceDB; pub use self::error::Error; -pub use types::trace_types::trace::{Trace, VMTrace, VMOperation, VMExecutedOperation, MemoryDiff, StorageDiff}; +pub use types::trace_types::trace::{VMTrace, VMOperation, VMExecutedOperation, MemoryDiff, StorageDiff}; +pub use types::trace_types::flat::{FlatTrace, FlatTransactionTraces, FlatBlockTraces}; pub use self::noop_tracer::{NoopTracer, NoopVMTracer}; pub use self::executive_tracer::{ExecutiveTracer, ExecutiveVMTracer}; pub use types::trace_types::filter::{Filter, AddressesFilter}; @@ -59,8 +57,7 @@ pub trait Tracer: Send { call: Option, gas_used: U256, output: Option, - depth: usize, - subs: Vec + subs: Vec, ); /// Stores trace create info. @@ -70,24 +67,23 @@ pub trait Tracer: Send { gas_used: U256, code: Option, address: Address, - depth: usize, - subs: Vec + subs: Vec ); /// Stores failed call trace. - fn trace_failed_call(&mut self, call: Option, depth: usize, subs: Vec); + fn trace_failed_call(&mut self, call: Option, subs: Vec); /// Stores failed create trace. - fn trace_failed_create(&mut self, create: Option, depth: usize, subs: Vec); + fn trace_failed_create(&mut self, create: Option, subs: Vec); /// Stores suicide info. - fn trace_suicide(&mut self, address: Address, balance: U256, refund_address: Address, depth: usize); + fn trace_suicide(&mut self, address: Address, balance: U256, refund_address: Address); /// Spawn subtracer which will be used to trace deeper levels of execution. fn subtracer(&self) -> Self where Self: Sized; /// Consumes self and returns all traces. - fn traces(self) -> Vec; + fn traces(self) -> Vec; } /// Used by executive to build VM traces. diff --git a/ethcore/src/trace/noop_tracer.rs b/ethcore/src/trace/noop_tracer.rs index 0a3ec1c978d..9ae8e2561be 100644 --- a/ethcore/src/trace/noop_tracer.rs +++ b/ethcore/src/trace/noop_tracer.rs @@ -18,8 +18,8 @@ use util::{Bytes, Address, U256}; use action_params::ActionParams; -use trace::{Tracer, VMTracer}; -use trace::trace::{Trace, Call, Create, VMTrace}; +use trace::{Tracer, VMTracer, FlatTrace}; +use trace::trace::{Call, Create, VMTrace}; /// Nonoperative tracer. Does not trace anything. pub struct NoopTracer; @@ -37,32 +37,32 @@ impl Tracer for NoopTracer { None } - fn trace_call(&mut self, call: Option, _: U256, output: Option, _: usize, _: Vec) { + fn trace_call(&mut self, call: Option, _: U256, output: Option, _: Vec) { assert!(call.is_none(), "self.prepare_trace_call().is_none(): so we can't be tracing: qed"); assert!(output.is_none(), "self.prepare_trace_output().is_none(): so we can't be tracing: qed"); } - fn trace_create(&mut self, create: Option, _: U256, code: Option, _: Address, _: usize, _: Vec) { + fn trace_create(&mut self, create: Option, _: U256, code: Option, _: Address, _: Vec) { assert!(create.is_none(), "self.prepare_trace_create().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, _: usize, _: Vec) { + fn trace_failed_call(&mut self, call: Option, _: Vec) { 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, _: usize, _: Vec) { + fn trace_failed_create(&mut self, create: Option, _: Vec) { assert!(create.is_none(), "self.prepare_trace_create().is_none(): so we can't be tracing: qed"); } - fn trace_suicide(&mut self, _address: Address, _balance: U256, _refund_address: Address, _depth: usize) { + fn trace_suicide(&mut self, _address: Address, _balance: U256, _refund_address: Address) { } fn subtracer(&self) -> Self { NoopTracer } - fn traces(self) -> Vec { + fn traces(self) -> Vec { vec![] } } diff --git a/ethcore/src/types/executed.rs b/ethcore/src/types/executed.rs index c211a7bc0f5..77f6443b9aa 100644 --- a/ethcore/src/types/executed.rs +++ b/ethcore/src/types/executed.rs @@ -19,7 +19,7 @@ use util::numbers::*; use util::Bytes; use util::rlp::*; -use trace::{Trace, VMTrace}; +use trace::{VMTrace, FlatTrace}; use types::log_entry::LogEntry; use types::state_diff::StateDiff; use ipc::binary::BinaryConvertError; @@ -97,7 +97,7 @@ pub struct Executed { /// Transaction output. pub output: Bytes, /// The trace of this transaction. - pub trace: Option, + pub trace: Vec, /// The VM trace of this transaction. pub vm_trace: Option, /// The state diff, if we traced it. diff --git a/ethcore/src/types/trace_types/filter.rs b/ethcore/src/types/trace_types/filter.rs index 4344d1500bc..8b9357cac70 100644 --- a/ethcore/src/types/trace_types/filter.rs +++ b/ethcore/src/types/trace_types/filter.rs @@ -289,7 +289,7 @@ mod tests { call_type: CallType::Call, }), result: Res::FailedCall, - trace_address: vec![0], + trace_address: vec![0].into_iter().collect(), subtraces: 0, }; @@ -313,7 +313,7 @@ mod tests { code: vec![], address: 2.into(), }), - trace_address: vec![0], + trace_address: vec![0].into_iter().collect(), subtraces: 0, }; @@ -332,7 +332,7 @@ mod tests { balance: 3.into(), }), result: Res::None, - trace_address: vec![], + trace_address: vec![].into_iter().collect(), subtraces: 0 }; diff --git a/ethcore/src/trace/flat.rs b/ethcore/src/types/trace_types/flat.rs similarity index 52% rename from ethcore/src/trace/flat.rs rename to ethcore/src/types/trace_types/flat.rs index c746a2e2aca..18c2b3b92e8 100644 --- a/ethcore/src/trace/flat.rs +++ b/ethcore/src/types/trace_types/flat.rs @@ -16,15 +16,17 @@ //! Flat trace module +use std::collections::VecDeque; +use std::mem; +use ipc::binary::BinaryConvertError; use util::rlp::*; -use trace::BlockTraces; use basic_types::LogBloom; -use super::trace::{Trace, Action, Res}; +use super::trace::{Action, Res}; /// Trace localized in vector of traces produced by a single transaction. /// /// Parent and children indexes refer to positions in this vector. -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug, PartialEq, Clone, Binary)] pub struct FlatTrace { /// Type of action performed by a transaction. pub action: Action, @@ -35,7 +37,7 @@ pub struct FlatTrace { /// Exact location of trace. /// /// [index in root, index in first CALL, index in second CALL, ...] - pub trace_address: Vec, + pub trace_address: VecDeque, } impl FlatTrace { @@ -51,18 +53,19 @@ impl Encodable for FlatTrace { s.append(&self.action); s.append(&self.result); s.append(&self.subtraces); - s.append(&self.trace_address); + s.append(&self.trace_address.clone().into_iter().collect::>()); } } impl Decodable for FlatTrace { fn decode(decoder: &D) -> Result where D: Decoder { let d = decoder.as_rlp(); + let v: Vec = try!(d.val_at(3)); let res = FlatTrace { action: try!(d.val_at(0)), result: try!(d.val_at(1)), subtraces: try!(d.val_at(2)), - trace_address: try!(d.val_at(3)), + trace_address: v.into_iter().collect(), }; Ok(res) @@ -73,6 +76,12 @@ impl Decodable for FlatTrace { #[derive(Debug, PartialEq, Clone)] pub struct FlatTransactionTraces(Vec); +impl From> for FlatTransactionTraces { + fn from(v: Vec) -> Self { + FlatTransactionTraces(v) + } +} + impl FlatTransactionTraces { /// Returns bloom of the trace. pub fn bloom(&self) -> LogBloom { @@ -102,6 +111,12 @@ impl Into> for FlatTransactionTraces { #[derive(Debug, PartialEq, Clone)] pub struct FlatBlockTraces(Vec); +impl From> for FlatBlockTraces { + fn from(v: Vec) -> Self { + FlatBlockTraces(v) + } +} + impl FlatBlockTraces { /// Returns bloom of the trace. pub fn bloom(&self) -> LogBloom { @@ -121,142 +136,18 @@ impl Decodable for FlatBlockTraces { } } -impl From for FlatBlockTraces { - fn from(block_traces: BlockTraces) -> Self { - let traces: Vec = block_traces.into(); - let ordered = traces.into_iter() - .map(|trace| FlatBlockTraces::flatten(vec![], trace)) - .map(FlatTransactionTraces) - .collect(); - FlatBlockTraces(ordered) - } -} - impl Into> for FlatBlockTraces { fn into(self) -> Vec { self.0 } } -impl FlatBlockTraces { - /// Helper function flattening nested tree structure to vector of ordered traces. - fn flatten(address: Vec, trace: Trace) -> Vec { - let subtraces = trace.subs.len(); - let all_subs = trace.subs - .into_iter() - .enumerate() - .flat_map(|(index, subtrace)| { - let mut subtrace_address = address.clone(); - subtrace_address.push(index); - FlatBlockTraces::flatten(subtrace_address, subtrace) - }) - .collect::>(); - - let ordered = FlatTrace { - action: trace.action, - result: trace.result, - subtraces: subtraces, - trace_address: address, - }; - - let mut result = vec![ordered]; - result.extend(all_subs); - result - } -} - #[cfg(test)] mod tests { use super::{FlatBlockTraces, FlatTransactionTraces, FlatTrace}; - use util::{U256, Address}; - use trace::trace::{Action, Res, CallResult, Call, Create, Trace}; - use trace::BlockTraces; + use trace::trace::{Action, Res, CallResult, Call}; use types::executed::CallType; - #[test] - fn test_block_from() { - let trace = Trace { - depth: 2, - action: Action::Call(Call { - from: Address::from(1), - to: Address::from(2), - value: U256::from(3), - gas: U256::from(4), - input: vec![0x5], - call_type: CallType::Call, - }), - subs: vec![ - Trace { - depth: 3, - action: Action::Create(Create { - from: Address::from(6), - value: U256::from(7), - gas: U256::from(8), - init: vec![0x9] - }), - subs: vec![ - Trace { - depth: 3, - action: Action::Create(Create { - from: Address::from(6), - value: U256::from(7), - gas: U256::from(8), - init: vec![0x9] - }), - subs: vec![ - ], - result: Res::FailedCreate - }, - Trace { - depth: 3, - action: Action::Create(Create { - from: Address::from(6), - value: U256::from(7), - gas: U256::from(8), - init: vec![0x9] - }), - subs: vec![ - ], - result: Res::FailedCreate - } - ], - result: Res::FailedCreate - }, - Trace { - depth: 3, - action: Action::Create(Create { - from: Address::from(6), - value: U256::from(7), - gas: U256::from(8), - init: vec![0x9] - }), - subs: vec![], - result: Res::FailedCreate, - } - ], - result: Res::Call(CallResult { - gas_used: U256::from(10), - output: vec![0x11, 0x12] - }) - }; - - let block_traces = FlatBlockTraces::from(BlockTraces::from(vec![trace])); - let transaction_traces: Vec = block_traces.into(); - assert_eq!(transaction_traces.len(), 1); - let ordered_traces: Vec = transaction_traces.into_iter().nth(0).unwrap().into(); - assert_eq!(ordered_traces.len(), 5); - assert_eq!(ordered_traces[0].trace_address, vec![]); - assert_eq!(ordered_traces[0].subtraces, 2); - assert_eq!(ordered_traces[1].trace_address, vec![0]); - assert_eq!(ordered_traces[1].subtraces, 2); - assert_eq!(ordered_traces[2].trace_address, vec![0, 0]); - assert_eq!(ordered_traces[2].subtraces, 0); - assert_eq!(ordered_traces[3].trace_address, vec![0, 1]); - assert_eq!(ordered_traces[3].subtraces, 0); - assert_eq!(ordered_traces[4].trace_address, vec![1]); - assert_eq!(ordered_traces[4].subtraces, 0); - } - #[test] fn test_trace_serialization() { use util::rlp; @@ -274,7 +165,7 @@ mod tests { gas_used: 10.into(), output: vec![0x11, 0x12] }), - trace_address: Vec::new(), + trace_address: Default::default(), subtraces: 0, }; diff --git a/ethcore/src/types/trace_types/mod.rs b/ethcore/src/types/trace_types/mod.rs index db429a8f4b6..7b5c937904b 100644 --- a/ethcore/src/types/trace_types/mod.rs +++ b/ethcore/src/types/trace_types/mod.rs @@ -17,5 +17,6 @@ //! Types used in the public api pub mod filter; +pub mod flat; pub mod trace; pub mod localized; diff --git a/ethcore/src/types/trace_types/trace.rs b/ethcore/src/types/trace_types/trace.rs index 3736f267365..ddd64af2145 100644 --- a/ethcore/src/types/trace_types/trace.rs +++ b/ethcore/src/types/trace_types/trace.rs @@ -384,51 +384,6 @@ impl Res { } } -#[derive(Debug, Clone, PartialEq, Binary)] -/// A trace; includes a description of the action being traced and sub traces of each interior action. -pub struct Trace { - /// The number of EVM execution environments active when this action happened; 0 if it's - /// the outer action of the transaction. - pub depth: usize, - /// The action being performed. - pub action: Action, - /// The sub traces for each interior action performed as part of this call. - pub subs: Vec, - /// The result of the performed action. - pub result: Res, -} - -impl Encodable for Trace { - fn rlp_append(&self, s: &mut RlpStream) { - s.begin_list(4); - s.append(&self.depth); - s.append(&self.action); - s.append(&self.subs); - s.append(&self.result); - } -} - -impl Decodable for Trace { - fn decode(decoder: &D) -> Result where D: Decoder { - let d = decoder.as_rlp(); - let res = Trace { - depth: try!(d.val_at(0)), - action: try!(d.val_at(1)), - subs: try!(d.val_at(2)), - result: try!(d.val_at(3)), - }; - - Ok(res) - } -} - -impl Trace { - /// Returns trace bloom. - pub fn bloom(&self) -> LogBloom { - self.subs.iter().fold(self.action.bloom() | self.result.bloom(), |b, s| b | s.bloom()) - } -} - #[derive(Debug, Clone, PartialEq, Binary)] /// A diff of some chunk of memory. pub struct MemoryDiff { @@ -593,105 +548,3 @@ impl Decodable for VMTrace { } } -#[cfg(test)] -mod tests { - use util::{Address, U256, FixedHash}; - use util::rlp::{encode, decode}; - use util::sha3::Hashable; - use trace::trace::{Call, CallResult, Create, Res, Action, Trace, Suicide, CreateResult}; - use types::executed::CallType; - - #[test] - fn traces_rlp() { - let trace = Trace { - depth: 2, - action: Action::Call(Call { - from: Address::from(1), - to: Address::from(2), - value: U256::from(3), - gas: U256::from(4), - input: vec![0x5], - call_type: CallType::Call, - }), - subs: vec![ - Trace { - depth: 3, - action: Action::Create(Create { - from: Address::from(6), - value: U256::from(7), - gas: U256::from(8), - init: vec![0x9] - }), - subs: vec![], - result: Res::FailedCreate - } - ], - result: Res::Call(CallResult { - gas_used: U256::from(10), - output: vec![0x11, 0x12] - }) - }; - - let encoded = encode(&trace); - let decoded: Trace = decode(&encoded); - assert_eq!(trace, decoded); - } - - #[test] - fn traces_bloom() { - let trace = Trace { - depth: 2, - action: Action::Call(Call { - from: Address::from(1), - to: Address::from(2), - value: U256::from(3), - gas: U256::from(4), - input: vec![0x5], - call_type: CallType::Call, - }), - subs: vec![ - Trace { - depth: 3, - action: Action::Create(Create { - from: Address::from(6), - value: U256::from(7), - gas: U256::from(8), - init: vec![0x9] - }), - subs: vec![], - result: Res::Create(CreateResult { - gas_used: 10.into(), - code: vec![], - address: 15.into(), - }), - }, - Trace { - depth: 3, - action: Action::Suicide(Suicide { - address: 101.into(), - refund_address: 102.into(), - balance: 0.into(), - }), - subs: vec![], - result: Res::None, - } - ], - result: Res::Call(CallResult { - gas_used: U256::from(10), - output: vec![0x11, 0x12] - }) - }; - - let bloom = trace.bloom(); - - // right now only addresses are bloomed - assert!(bloom.contains_bloomed(&Address::from(1).sha3())); - assert!(bloom.contains_bloomed(&Address::from(2).sha3())); - assert!(!bloom.contains_bloomed(&Address::from(20).sha3())); - assert!(bloom.contains_bloomed(&Address::from(6).sha3())); - assert!(bloom.contains_bloomed(&Address::from(15).sha3())); - assert!(bloom.contains_bloomed(&Address::from(101).sha3())); - assert!(bloom.contains_bloomed(&Address::from(102).sha3())); - assert!(!bloom.contains_bloomed(&Address::from(103).sha3())); - } -} diff --git a/ipc/rpc/src/binary.rs b/ipc/rpc/src/binary.rs index 62a3c43b076..8e65c63eef4 100644 --- a/ipc/rpc/src/binary.rs +++ b/ipc/rpc/src/binary.rs @@ -139,6 +139,74 @@ impl BinaryConvertable for Result BinaryConvertable for VecDeque where T: BinaryConvertable { + fn size(&self) -> usize { + match T::len_params() { + 0 => mem::size_of::() * self.len(), + _ => self.iter().fold(0usize, |acc, t| acc + t.size()), + } + } + + fn to_bytes(&self, buffer: &mut [u8], length_stack: &mut VecDeque) -> Result<(), BinaryConvertError> { + let mut offset = 0usize; + for item in self.iter() { + let next_size = match T::len_params() { + 0 => mem::size_of::(), + _ => { let size = item.size(); length_stack.push_back(size); size }, + }; + if next_size > 0 { + let item_end = offset + next_size; + try!(item.to_bytes(&mut buffer[offset..item_end], length_stack)); + offset = item_end; + } + } + Ok(()) + } + + fn from_bytes(buffer: &[u8], length_stack: &mut VecDeque) -> Result { + let mut index = 0; + let mut result = Self::with_capacity( + match T::len_params() { + 0 => buffer.len() / mem::size_of::(), + _ => 128, + }); + + if buffer.len() == 0 { return Ok(result); } + + loop { + let next_size = match T::len_params() { + 0 => mem::size_of::(), + _ => try!(length_stack.pop_front().ok_or(BinaryConvertError)), + }; + let item = if next_size == 0 { + try!(T::from_empty_bytes()) + } + else { + try!(T::from_bytes(&buffer[index..index+next_size], length_stack)) + }; + result.push_back(item); + + index = index + next_size; + if index == buffer.len() { break; } + if index + next_size > buffer.len() { + return Err(BinaryConvertError) + } + } + + Ok(result) + } + + fn from_empty_bytes() -> Result { + Ok(Self::new()) + } + + fn len_params() -> usize { + 1 + } +} + +// + impl BinaryConvertable for Vec where T: BinaryConvertable { fn size(&self) -> usize { match T::len_params() { diff --git a/rpc/src/v1/tests/mocked/eth.rs b/rpc/src/v1/tests/mocked/eth.rs index 0c4a13d94b9..42472d21bbe 100644 --- a/rpc/src/v1/tests/mocked/eth.rs +++ b/rpc/src/v1/tests/mocked/eth.rs @@ -430,7 +430,7 @@ fn rpc_eth_call() { logs: vec![], contracts_created: vec![], output: vec![0x12, 0x34, 0xff], - trace: None, + trace: vec![], vm_trace: None, state_diff: None, }); @@ -465,7 +465,7 @@ fn rpc_eth_call_default_block() { logs: vec![], contracts_created: vec![], output: vec![0x12, 0x34, 0xff], - trace: None, + trace: vec![], vm_trace: None, state_diff: None, }); @@ -499,7 +499,7 @@ fn rpc_eth_estimate_gas() { logs: vec![], contracts_created: vec![], output: vec![0x12, 0x34, 0xff], - trace: None, + trace: vec![], vm_trace: None, state_diff: None, }); @@ -534,7 +534,7 @@ fn rpc_eth_estimate_gas_default_block() { logs: vec![], contracts_created: vec![], output: vec![0x12, 0x34, 0xff], - trace: None, + trace: vec![], vm_trace: None, state_diff: None, }); diff --git a/rpc/src/v1/types/trace.rs b/rpc/src/v1/types/trace.rs index d4183994e69..21c88ccaeef 100644 --- a/rpc/src/v1/types/trace.rs +++ b/rpc/src/v1/types/trace.rs @@ -18,7 +18,7 @@ use std::collections::BTreeMap; use util::{Address, U256, H256, Uint}; use serde::{Serialize, Serializer}; use ethcore::trace::trace; -use ethcore::trace::{Trace as EthTrace, LocalizedTrace as EthLocalizedTrace}; +use ethcore::trace::{FlatTrace, LocalizedTrace as EthLocalizedTrace}; use ethcore::trace as et; use ethcore::state_diff; use ethcore::account_diff; @@ -458,23 +458,24 @@ impl From for LocalizedTrace { /// Trace #[derive(Debug, Serialize)] pub struct Trace { - /// Depth within the call trace tree. - depth: usize, + /// Trace address + #[serde(rename="traceAddress")] + trace_address: Vec, + /// Subtraces + subtraces: U256, /// Action action: Action, /// Result result: Res, - /// Subtraces - subtraces: Vec, } -impl From for Trace { - fn from(t: EthTrace) -> Self { +impl From for Trace { + fn from(t: FlatTrace) -> Self { Trace { - depth: t.depth.into(), + trace_address: t.trace_address.into_iter().map(Into::into).collect(), + subtraces: t.subtraces.into(), action: t.action.into(), result: t.result.into(), - subtraces: t.subs.into_iter().map(From::from).collect(), } } } @@ -485,7 +486,7 @@ pub struct TraceResults { /// The output of the call/create pub output: Vec, /// The transaction trace. - pub trace: Option, + pub trace: Vec, /// The transaction trace. #[serde(rename="vmTrace")] pub vm_trace: Option, @@ -498,7 +499,7 @@ impl From for TraceResults { fn from(t: Executed) -> Self { TraceResults { output: t.output.into(), - trace: t.trace.map(Into::into), + trace: t.trace.into_iter().map(Into::into).collect(), vm_trace: t.vm_trace.map(Into::into), state_diff: t.state_diff.map(Into::into), } @@ -517,12 +518,12 @@ mod tests { fn should_serialize_trace_results() { let r = TraceResults { output: vec![0x60], - trace: None, + trace: vec![], vm_trace: None, state_diff: None, }; let serialized = serde_json::to_string(&r).unwrap(); - assert_eq!(serialized, r#"{"output":[96],"trace":null,"vmTrace":null,"stateDiff":null}"#); + assert_eq!(serialized, r#"{"output":[96],"trace":[],"vmTrace":null,"stateDiff":null}"#); } #[test]