From 9746b944f107dd356a7a6aa8a4d8bc0ecc661fde Mon Sep 17 00:00:00 2001 From: Marek Kotewicz Date: Thu, 28 Jul 2016 20:31:29 +0200 Subject: [PATCH] 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 | 10 +- 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, 475 insertions(+), 666 deletions(-) delete mode 100644 ethcore/src/trace/block.rs rename ethcore/src/{trace => types/trace_types}/flat.rs (53%) diff --git a/ethcore/src/block.rs b/ethcore/src/block.rs index d0f62b4a1..7ec8ed4ce 100644 --- a/ethcore/src/block.rs +++ b/ethcore/src/block.rs @@ -20,7 +20,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. @@ -76,7 +76,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. @@ -92,7 +92,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. @@ -108,7 +108,7 @@ pub struct BlockRef<'a> { /// State. pub state: &'a State, /// Traces. - pub traces: &'a Option>, + pub traces: &'a Option>>, } impl ExecutedBlock { @@ -169,7 +169,7 @@ pub trait IsBlock { fn receipts(&self) -> &[Receipt] { &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) -> &[Header] { &self.block().base.uncles } @@ -337,7 +337,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 ad2e952e7..06ff5bd8c 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -61,6 +61,7 @@ use executive::{Executive, Executed, TransactOptions, contract_address}; use receipt::LocalizedReceipt; use trace::{TraceDB, ImportRequest as TraceImportRequest, LocalizedTrace, Database as TraceDatabase}; use trace; +use trace::FlatTransactionTraces; use evm::Factory as EvmFactory; use miner::{Miner, MinerService}; use util::TrieFactory; @@ -430,7 +431,12 @@ impl Client { // Commit results let receipts = block.receipts().to_owned(); - let traces = From::from(block.traces().clone().unwrap_or_else(Vec::new)); + 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. @@ -441,7 +447,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 0fffb3d1d..5a7d6da10 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}; @@ -199,7 +199,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![] ); } @@ -286,7 +285,7 @@ impl<'a> Executive<'a> { // just drain the whole gas self.state.revert_snapshot(); - tracer.trace_failed_call(trace_info, self.depth, vec![]); + tracer.trace_failed_call(trace_info, vec![]); Err(evm::Error::OutOfGas) } @@ -318,10 +317,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); @@ -333,7 +331,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) } } @@ -384,10 +382,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); @@ -401,7 +398,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); @@ -493,7 +490,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; @@ -647,8 +644,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(), @@ -661,22 +659,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 { @@ -754,8 +752,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(), @@ -767,8 +766,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 881114e4c..47c4d9c60 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 82a3682d5..1da70df3a 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. @@ -402,7 +402,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] @@ -428,8 +428,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(), @@ -441,8 +442,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); } @@ -489,8 +489,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(), @@ -498,8 +498,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); } @@ -528,8 +528,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(), @@ -542,8 +542,8 @@ fn should_trace_call_transaction() { gas_used: U256::from(3), output: vec![] }), - subs: vec![] - }); + subtraces: 0, + }]; assert_eq!(result.trace, expected_trace); } @@ -571,8 +571,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(), @@ -585,8 +585,8 @@ fn should_trace_basic_call_transaction() { gas_used: U256::from(0), output: vec![] }), - subs: vec![] - }); + subtraces: 0, + }]; assert_eq!(result.trace, expected_trace); } @@ -614,8 +614,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(), @@ -628,8 +628,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] @@ -656,8 +658,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(), @@ -670,8 +672,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); } @@ -700,8 +703,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(), @@ -714,23 +718,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); } @@ -762,8 +766,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(), @@ -776,23 +781,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); } @@ -820,8 +825,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(), @@ -831,10 +836,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); } @@ -864,8 +867,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(), @@ -878,23 +883,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); } @@ -923,8 +927,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(), @@ -937,20 +942,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); } @@ -979,8 +983,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(), @@ -993,8 +998,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); } @@ -1024,8 +1028,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(), @@ -1038,20 +1043,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); } @@ -1082,8 +1086,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(), @@ -1096,38 +1101,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); } @@ -1158,8 +1162,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(), @@ -1171,36 +1177,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); } @@ -1230,8 +1235,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(), @@ -1244,17 +1250,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 bc53f77e2..000000000 --- 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 91c9da444..8fb0f531f 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(); // 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 7eb7f3489..a64664095 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,67 +80,71 @@ 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") - }) + }), }; debug!(target: "trace", "Traced call {:?}", trace); 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(), }; debug!(target: "trace", "Traced create {:?}", trace); 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, }; debug!(target: "trace", "Traced failed call {:?}", trace); 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(), }; debug!(target: "trace", "Traced failed create {:?}", trace); 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(), }; debug!(target: "trace", "Traced failed suicide {:?}", trace); self.traces.push(trace); @@ -110,7 +154,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 a6b4a29bb..7da3e5fe2 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 0e4d9f626..3a3e24192 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 0a3ec1c97..9ae8e2561 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 abf71627e..efc4da9e2 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 4344d1500..8b9357cac 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 53% rename from ethcore/src/trace/flat.rs rename to ethcore/src/types/trace_types/flat.rs index 914fbaad6..ad1e9bace 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 all traces in the collection. 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 all traces in the block. 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 db429a8f4..7b5c93790 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 3736f2673..ddd64af21 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 949acfd80..81cd8cf7f 100644 --- a/ipc/rpc/src/binary.rs +++ b/ipc/rpc/src/binary.rs @@ -322,6 +322,74 @@ impl BinaryConvertable for BTreeMap where K : BinaryConvertable + Or } } +impl 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::length())), + }; + 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::boundaries()) + } + } + + 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 85452691d..b04e48133 100644 --- a/rpc/src/v1/tests/mocked/eth.rs +++ b/rpc/src/v1/tests/mocked/eth.rs @@ -431,7 +431,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, }); @@ -466,7 +466,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, }); @@ -500,7 +500,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, }); @@ -535,7 +535,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 fd8b60720..b48fb88bf 100644 --- a/rpc/src/v1/types/trace.rs +++ b/rpc/src/v1/types/trace.rs @@ -17,7 +17,7 @@ use std::collections::BTreeMap; 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(Into::into).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), } @@ -516,12 +517,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]