From 8e77ac2102fbdba587f0babd39fb70db82e7c94f Mon Sep 17 00:00:00 2001 From: debris Date: Sun, 24 Apr 2016 19:05:44 +0200 Subject: [PATCH 01/24] fixed encoding 0u8 --- util/src/rlp/rlpstream.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/util/src/rlp/rlpstream.rs b/util/src/rlp/rlpstream.rs index 7bf3d3cddd9..bf5450f384b 100644 --- a/util/src/rlp/rlpstream.rs +++ b/util/src/rlp/rlpstream.rs @@ -307,10 +307,18 @@ struct EncodableU8 (u8); impl ByteEncodable for EncodableU8 { fn to_bytes>(&self, out: &mut V) { - out.vec_push(self.0) + if self.0 != 0 { + out.vec_push(self.0) + } } - fn bytes_len(&self) -> usize { 1 } + fn bytes_len(&self) -> usize { + if self.0 == 0 { + 0 + } else { + 1 + } + } } impl RlpEncodable for u8 { From 418d4a4e52dadb697500a0c83e104e58c67657d3 Mon Sep 17 00:00:00 2001 From: debris Date: Sun, 24 Apr 2016 19:15:16 +0200 Subject: [PATCH 02/24] simplified if else stmt --- util/src/rlp/rlpstream.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/util/src/rlp/rlpstream.rs b/util/src/rlp/rlpstream.rs index bf5450f384b..c2ff88c41c9 100644 --- a/util/src/rlp/rlpstream.rs +++ b/util/src/rlp/rlpstream.rs @@ -313,11 +313,7 @@ impl ByteEncodable for EncodableU8 { } fn bytes_len(&self) -> usize { - if self.0 == 0 { - 0 - } else { - 1 - } + match self.0 { 0 => 0, _ => 1 } } } From 5e160f0215f6801e8e0a014812172efc79302d2e Mon Sep 17 00:00:00 2001 From: debris Date: Sun, 24 Apr 2016 20:11:33 +0200 Subject: [PATCH 03/24] tracedb core --- Cargo.lock | 6 + ethcore/Cargo.toml | 1 + ethcore/src/block.rs | 1 + ethcore/src/blockchain/blockchain.rs | 12 +- ethcore/src/blockchain/import_route.rs | 20 +- ethcore/src/client/client.rs | 6 +- ethcore/src/client/config.rs | 3 + ethcore/src/client/mod.rs | 1 + ethcore/src/client/trace.rs | 38 ++ ethcore/src/common.rs | 1 - ethcore/src/executive.rs | 38 +- ethcore/src/externalities.rs | 2 + ethcore/src/json_tests/executive.rs | 1 + ethcore/src/lib.rs | 1 + ethcore/src/state.rs | 94 ++-- ethcore/src/trace/block.rs | 42 ++ ethcore/src/trace/bloom.rs | 119 +++++ ethcore/src/trace/config.rs | 40 ++ ethcore/src/trace/db.rs | 525 +++++++++++++++++++++++ ethcore/src/trace/executive_tracer.rs | 108 +++++ ethcore/src/trace/filter.rs | 255 +++++++++++ ethcore/src/trace/flat.rs | 183 ++++++++ ethcore/src/trace/import.rs | 36 ++ ethcore/src/trace/localized.rs | 44 ++ ethcore/src/trace/mod.rs | 185 +++----- ethcore/src/trace/noop_tracer.rs | 66 +++ ethcore/src/trace/trace.rs | 341 ++++++++++++++- ethcore/src/verification/verification.rs | 2 - util/src/hash.rs | 6 + util/src/rlp/rlperrors.rs | 4 +- util/src/rlp/rlpstream.rs | 8 +- 31 files changed, 1971 insertions(+), 218 deletions(-) create mode 100644 ethcore/src/client/trace.rs create mode 100644 ethcore/src/trace/block.rs create mode 100644 ethcore/src/trace/bloom.rs create mode 100644 ethcore/src/trace/config.rs create mode 100644 ethcore/src/trace/db.rs create mode 100644 ethcore/src/trace/executive_tracer.rs create mode 100644 ethcore/src/trace/filter.rs create mode 100644 ethcore/src/trace/flat.rs create mode 100644 ethcore/src/trace/import.rs create mode 100644 ethcore/src/trace/localized.rs create mode 100644 ethcore/src/trace/noop_tracer.rs diff --git a/Cargo.lock b/Cargo.lock index 3e3520acd23..ec152e01791 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -96,6 +96,11 @@ name = "blastfig" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "bloomchain" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "byteorder" version = "0.5.1" @@ -237,6 +242,7 @@ dependencies = [ name = "ethcore" version = "1.1.0" dependencies = [ + "bloomchain 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "clippy 0.0.63 (registry+https://github.com/rust-lang/crates.io-index)", "crossbeam 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/ethcore/Cargo.toml b/ethcore/Cargo.toml index a43092b2206..2fdca148c9a 100644 --- a/ethcore/Cargo.toml +++ b/ethcore/Cargo.toml @@ -22,6 +22,7 @@ crossbeam = "0.1.5" lazy_static = "0.1" ethcore-devtools = { path = "../devtools" } ethjson = { path = "../json" } +bloomchain = "0.1" [features] jit = ["evmjit"] diff --git a/ethcore/src/block.rs b/ethcore/src/block.rs index b8af3f73362..782a20f52a8 100644 --- a/ethcore/src/block.rs +++ b/ethcore/src/block.rs @@ -22,6 +22,7 @@ use common::*; use engine::*; use state::*; use verification::PreverifiedBlock; +use trace::Trace; /// A block, encoded as it is on the block chain. #[derive(Default, Debug, Clone)] diff --git a/ethcore/src/blockchain/blockchain.rs b/ethcore/src/blockchain/blockchain.rs index 2258db22eb3..7c13ff4a4d6 100644 --- a/ethcore/src/blockchain/blockchain.rs +++ b/ethcore/src/blockchain/blockchain.rs @@ -55,9 +55,6 @@ impl Default for BlockChainConfig { /// Interface for querying blocks by hash and by number. pub trait BlockProvider { - /// True if we store full tracing information for transactions. - fn have_tracing(&self) -> bool; - /// Returns true if the given block is known /// (though not necessarily a part of the canon chain). fn is_known(&self, hash: &H256) -> bool; @@ -186,9 +183,6 @@ impl BlockProvider for BlockChain { self.extras_db.exists_with_cache(&self.block_details, hash) } - // We do not store tracing information. - fn have_tracing(&self) -> bool { false } - /// Get raw block data fn block(&self, hash: &H256) -> Option { { @@ -541,7 +535,7 @@ impl BlockChain { Some(ret) } - /// Get inserted block info which is critical to preapre extras updates. + /// Get inserted block info which is critical to prepare extras updates. fn block_info(&self, block_bytes: &[u8]) -> BlockInfo { let block = BlockView::new(block_bytes); let header = block.header_view(); @@ -950,21 +944,25 @@ mod tests { assert_eq!(ir1, ImportRoute { enacted: vec![b1_hash], retracted: vec![], + ommited: vec![], }); assert_eq!(ir2, ImportRoute { enacted: vec![b2_hash], retracted: vec![], + ommited: vec![], }); assert_eq!(ir3b, ImportRoute { enacted: vec![b3b_hash], retracted: vec![], + ommited: vec![], }); assert_eq!(ir3a, ImportRoute { enacted: vec![b3a_hash], retracted: vec![b3b_hash], + ommited: vec![], }); assert_eq!(bc.best_block_hash(), best_block_hash); diff --git a/ethcore/src/blockchain/import_route.rs b/ethcore/src/blockchain/import_route.rs index 253c4024a9d..eecf4f76d1c 100644 --- a/ethcore/src/blockchain/import_route.rs +++ b/ethcore/src/blockchain/import_route.rs @@ -26,6 +26,8 @@ pub struct ImportRoute { pub retracted: Vec, /// Blocks that were validated by new block. pub enacted: Vec, + /// Blocks which are neither retracted nor enacted. + pub ommited: Vec, } impl ImportRoute { @@ -33,6 +35,7 @@ impl ImportRoute { ImportRoute { retracted: vec![], enacted: vec![], + ommited: vec![], } } } @@ -43,13 +46,19 @@ impl From for ImportRoute { BlockLocation::CanonChain => ImportRoute { retracted: vec![], enacted: vec![info.hash], + ommited: vec![], + }, + BlockLocation::Branch => ImportRoute { + retracted: vec![], + enacted: vec![], + ommited: vec![info.hash], }, - BlockLocation::Branch => ImportRoute::none(), BlockLocation::BranchBecomingCanonChain(mut data) => { data.enacted.push(info.hash); ImportRoute { retracted: data.retracted, enacted: data.enacted, + ommited: vec![], } } } @@ -68,6 +77,7 @@ mod tests { assert_eq!(ImportRoute::none(), ImportRoute { enacted: vec![], retracted: vec![], + ommited: vec![], }); } @@ -80,7 +90,11 @@ mod tests { location: BlockLocation::Branch, }; - assert_eq!(ImportRoute::from(info), ImportRoute::none()); + assert_eq!(ImportRoute::from(info), ImportRoute { + retracted: vec![], + enacted: vec![], + ommited: vec![H256::from(U256::from(1))], + }); } #[test] @@ -95,6 +109,7 @@ mod tests { assert_eq!(ImportRoute::from(info), ImportRoute { retracted: vec![], enacted: vec![H256::from(U256::from(1))], + ommited: vec![], }); } @@ -114,6 +129,7 @@ mod tests { assert_eq!(ImportRoute::from(info), ImportRoute { retracted: vec![H256::from(U256::from(3)), H256::from(U256::from(4))], enacted: vec![H256::from(U256::from(1)), H256::from(U256::from(2))], + ommited: vec![], }); } } diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index c3e8d942e34..099f5901789 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -42,6 +42,7 @@ use env_info::EnvInfo; use executive::{Executive, Executed, TransactOptions, contract_address}; use receipt::LocalizedReceipt; pub use blockchain::CacheSize as BlockChainCacheSize; +use trace::{Tracedb, Database as TraceDatabase}; /// General block status #[derive(Debug, Eq, PartialEq)] @@ -103,6 +104,7 @@ impl ClientReport { /// Call `import_block()` to import a block asynchronously; `flush_queue()` flushes the queue. pub struct Client where V: Verifier { chain: Arc, + tracedb: Arc>, engine: Arc>, state_db: Mutex>, block_queue: BlockQueue, @@ -150,6 +152,7 @@ impl Client where V: Verifier { let path = get_db_path(path, config.pruning, spec.genesis_header().hash()); let gb = spec.genesis_block(); let chain = Arc::new(BlockChain::new(config.blockchain, &gb, &path)); + let tracedb = Arc::new(Tracedb::new(config.tracing, &path, chain.clone())); let mut state_db = journaldb::new(&append_path(&path, "state"), config.pruning); @@ -165,6 +168,7 @@ impl Client where V: Verifier { Arc::new(Client { chain: chain, + tracedb: tracedb, engine: engine, state_db: Mutex::new(state_db), block_queue: block_queue, @@ -225,7 +229,7 @@ impl Client where V: Verifier { let last_hashes = self.build_last_hashes(header.parent_hash.clone()); let db = self.state_db.lock().unwrap().boxed_clone(); - let enact_result = enact_verified(&block, engine, self.chain.have_tracing(), db, &parent, last_hashes); + let enact_result = enact_verified(&block, engine, self.tracedb.tracing_enabled(), db, &parent, last_hashes); if let Err(e) = enact_result { warn!(target: "client", "Block import failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e); return Err(()); diff --git a/ethcore/src/client/config.rs b/ethcore/src/client/config.rs index 89e95ea065b..9d08c88ff65 100644 --- a/ethcore/src/client/config.rs +++ b/ethcore/src/client/config.rs @@ -16,6 +16,7 @@ pub use block_queue::BlockQueueConfig; pub use blockchain::BlockChainConfig; +pub use trace::Config as TraceConfig; use util::journaldb; /// Client configuration. Includes configs for all sub-systems. @@ -25,6 +26,8 @@ pub struct ClientConfig { pub queue: BlockQueueConfig, /// Blockchain configuration. pub blockchain: BlockChainConfig, + /// Trace configuration. + pub tracing: TraceConfig, /// The JournalDB ("pruning") algorithm to use. pub pruning: journaldb::Algorithm, /// The name of the client instance. diff --git a/ethcore/src/client/mod.rs b/ethcore/src/client/mod.rs index c8bfe85ac5d..6f9c90c60fa 100644 --- a/ethcore/src/client/mod.rs +++ b/ethcore/src/client/mod.rs @@ -20,6 +20,7 @@ mod client; mod config; mod ids; mod test_client; +mod trace; pub use self::client::*; pub use self::config::{ClientConfig, BlockQueueConfig, BlockChainConfig}; diff --git a/ethcore/src/client/trace.rs b/ethcore/src/client/trace.rs new file mode 100644 index 00000000000..e7023c83b1f --- /dev/null +++ b/ethcore/src/client/trace.rs @@ -0,0 +1,38 @@ + +//! Bridge between Tracedb and Blockchain. + +use std::ops::Range; +use util::{Address, H256}; +use header::BlockNumber; +use trace::DatabaseExtras as TraceDatabaseExtras; +use blockchain::{BlockChain, BlockProvider}; +use extras::TransactionAddress; +use super::BlockId; + +impl TraceDatabaseExtras for BlockChain { + fn block_hash(&self, block_number: BlockNumber) -> Option { + (self as &BlockProvider).block_hash(block_number) + } + + fn transaction_hash(&self, block_number: BlockNumber, tx_position: usize) -> Option { + (self as &BlockProvider).block_hash(block_number) + .and_then(|block_hash| { + let tx_address = TransactionAddress { + block_hash: block_hash, + index: tx_position + }; + self.transaction(&tx_address) + }) + .map(|tx| tx.hash()) + } +} + +/// Easy to use trace filter. +pub struct Filter { + /// Range of filtering. + pub range: Range, + /// From address. + pub from_address: Vec
, + /// To address. + pub to_address: Vec
, +} diff --git a/ethcore/src/common.rs b/ethcore/src/common.rs index e9150121798..2c66710c0dc 100644 --- a/ethcore/src/common.rs +++ b/ethcore/src/common.rs @@ -26,4 +26,3 @@ pub use transaction::*; pub use log_entry::*; pub use receipt::*; pub use action_params::*; -pub use trace::*; \ No newline at end of file diff --git a/ethcore/src/executive.rs b/ethcore/src/executive.rs index bd30e0fb618..9862d3d049c 100644 --- a/ethcore/src/executive.rs +++ b/ethcore/src/executive.rs @@ -21,6 +21,7 @@ use engine::*; use evm::{self, Ext}; use externalities::*; use substate::*; +use trace::{Trace, Tracer, NoopTracer, ExecutiveTracer}; use crossbeam; /// Max depth to avoid stack overflow (when it's reached we start a new thread with VM) @@ -245,22 +246,44 @@ 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 let default = []; let data = if let Some(ref d) = params.data { d as &[u8] } else { &default as &[u8] }; + let trace_info = tracer.prepare_trace_call(¶ms); + let cost = self.engine.cost_of_builtin(¶ms.code_address, data); match cost <= params.gas { true => { self.engine.execute_builtin(¶ms.code_address, data, &mut output); self.state.clear_snapshot(); + + let mut trace_output = tracer.prepare_trace_output(); + if let Some(mut out) = trace_output.as_mut() { + *out = output.to_owned(); + } + + tracer.trace_call( + trace_info, + cost, + trace_output, + self.depth, + vec![], + delegate_call + ); + 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); + Err(evm::Error::OutOfGas) } } @@ -268,7 +291,6 @@ impl<'a> Executive<'a> { let trace_info = tracer.prepare_trace_call(¶ms); let mut trace_output = tracer.prepare_trace_output(); let mut subtracer = tracer.subtracer(); - let delegate_call = params.code_address != params.address; let gas = params.gas; if params.code.is_some() { @@ -441,6 +463,8 @@ mod tests { use evm::{Factory, VMType}; use substate::*; use tests::helpers::*; + use trace::trace; + use trace::{Trace, Tracer, NoopTracer, ExecutiveTracer}; #[test] fn test_contract_address() { @@ -590,26 +614,26 @@ mod tests { let expected_trace = vec![ Trace { depth: 0, - action: TraceAction::Call(TraceCall { + action: trace::Action::Call(trace::Call { from: x!("cd1722f3947def4cf144679da39c4c32bdc35681"), to: x!("b010143a42d5980c7e5ef0e4a4416dc098a4fed3"), value: x!(100), gas: x!(100000), input: vec![], }), - result: TraceResult::Call(TraceCallResult { + result: trace::Res::Call(trace::CallResult { gas_used: U256::from(55_248), output: vec![], }), subs: vec![Trace { depth: 1, - action: TraceAction::Create(TraceCreate { + action: trace::Action::Create(trace::Create { from: x!("b010143a42d5980c7e5ef0e4a4416dc098a4fed3"), value: x!(23), gas: x!(67979), 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: TraceResult::Create(TraceCreateResult { + 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] @@ -661,13 +685,13 @@ mod tests { let expected_trace = vec![Trace { depth: 0, - action: TraceAction::Create(TraceCreate { + action: trace::Action::Create(trace::Create { from: params.sender, value: x!(100), gas: params.gas, 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: TraceResult::Create(TraceCreateResult { + result: trace::Res::Create(trace::CreateResult { gas_used: U256::from(3224), address: params.address, code: vec![96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53] diff --git a/ethcore/src/externalities.rs b/ethcore/src/externalities.rs index 1c0bb417ffe..4d868cba5dd 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}; use substate::*; +use trace::Tracer; /// Policy for handling output data on `RETURN` opcode. pub enum OutputPolicy<'a, 'b> { @@ -293,6 +294,7 @@ mod tests { use substate::*; use tests::helpers::*; use super::*; + use trace::{NoopTracer}; fn get_test_origin() -> OriginInfo { OriginInfo { diff --git a/ethcore/src/json_tests/executive.rs b/ethcore/src/json_tests/executive.rs index 42786880771..ebffdbb1500 100644 --- a/ethcore/src/json_tests/executive.rs +++ b/ethcore/src/json_tests/executive.rs @@ -24,6 +24,7 @@ use externalities::*; use substate::*; use tests::helpers::*; use ethjson; +use trace::{Tracer, NoopTracer}; #[derive(Debug, PartialEq)] struct CallCreate { diff --git a/ethcore/src/lib.rs b/ethcore/src/lib.rs index d267a08cedc..a7b9125fc12 100644 --- a/ethcore/src/lib.rs +++ b/ethcore/src/lib.rs @@ -84,6 +84,7 @@ extern crate env_logger; extern crate num_cpus; extern crate crossbeam; extern crate ethjson; +extern crate bloomchain; #[cfg(test)] extern crate ethcore_devtools as devtools; #[cfg(feature = "jit" )] extern crate evmjit; diff --git a/ethcore/src/state.rs b/ethcore/src/state.rs index 6a128003db8..afd59008644 100644 --- a/ethcore/src/state.rs +++ b/ethcore/src/state.rs @@ -18,6 +18,7 @@ use common::*; use engine::Engine; use executive::{Executive, TransactOptions}; use account_db::*; +use trace::Trace; #[cfg(test)] #[cfg(feature = "json-tests")] use pod_account::*; @@ -367,7 +368,8 @@ use env_info::*; use spec::*; use transaction::*; use util::log::init_log; -use trace::*; +use trace::trace; +use trace::trace::{Trace}; #[test] fn should_apply_create_transaction() { @@ -393,13 +395,13 @@ fn should_apply_create_transaction() { let result = state.apply(&info, &engine, &t, true).unwrap(); let expected_trace = Some(Trace { depth: 0, - action: TraceAction::Create(TraceCreate { + action: trace::Action::Create(trace::Create { from: x!("9cce34f7ab185c7aba1b7c8140d620b4bda941d6"), value: x!(100), gas: x!(77412), 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: TraceResult::Create(TraceCreateResult { + result: trace::Res::Create(trace::CreateResult { gas_used: U256::from(3224), address: Address::from_str("8988167e088c87cd314df6d3c2b83da5acb93ace").unwrap(), code: vec![96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53] @@ -453,13 +455,13 @@ fn should_trace_failed_create_transaction() { let result = state.apply(&info, &engine, &t, true).unwrap(); let expected_trace = Some(Trace { depth: 0, - action: TraceAction::Create(TraceCreate { + action: trace::Action::Create(trace::Create { from: x!("9cce34f7ab185c7aba1b7c8140d620b4bda941d6"), value: x!(100), gas: x!(78792), init: vec![91, 96, 0, 86], }), - result: TraceResult::FailedCreate, + result: trace::Res::FailedCreate, subs: vec![] }); @@ -491,14 +493,14 @@ fn should_trace_call_transaction() { let result = state.apply(&info, &engine, &t, true).unwrap(); let expected_trace = Some(Trace { depth: 0, - action: TraceAction::Call(TraceCall { + action: trace::Action::Call(trace::Call { from: x!("9cce34f7ab185c7aba1b7c8140d620b4bda941d6"), to: x!(0xa), value: x!(100), gas: x!(79000), input: vec![], }), - result: TraceResult::Call(TraceCallResult { + result: trace::Res::Call(trace::CallResult { gas_used: U256::from(3), output: vec![] }), @@ -532,14 +534,14 @@ fn should_trace_basic_call_transaction() { let result = state.apply(&info, &engine, &t, true).unwrap(); let expected_trace = Some(Trace { depth: 0, - action: TraceAction::Call(TraceCall { + action: trace::Action::Call(trace::Call { from: x!("9cce34f7ab185c7aba1b7c8140d620b4bda941d6"), to: x!(0xa), value: x!(100), gas: x!(79000), input: vec![], }), - result: TraceResult::Call(TraceCallResult { + result: trace::Res::Call(trace::CallResult { gas_used: U256::from(0), output: vec![] }), @@ -550,6 +552,10 @@ fn should_trace_basic_call_transaction() { } #[test] +#[ignore] +// call tracing turned on to make +// number of transactions and traces in a block equal +// debris fn should_not_trace_call_transaction_to_builtin() { init_log(); @@ -575,6 +581,8 @@ fn should_not_trace_call_transaction_to_builtin() { } #[test] +#[ignore] +// call tracing turned on, debris fn should_not_trace_subcall_transaction_to_builtin() { init_log(); @@ -599,14 +607,14 @@ fn should_not_trace_subcall_transaction_to_builtin() { let expected_trace = Some(Trace { depth: 0, - action: TraceAction::Call(TraceCall { + action: trace::Action::Call(trace::Call { from: x!("9cce34f7ab185c7aba1b7c8140d620b4bda941d6"), to: x!(0xa), value: x!(0), gas: x!(79000), input: vec![], }), - result: TraceResult::Call(TraceCallResult { + result: trace::Res::Call(trace::CallResult { gas_used: U256::from(28_061), output: vec![] }), @@ -641,14 +649,14 @@ fn should_not_trace_callcode() { let expected_trace = Some(Trace { depth: 0, - action: TraceAction::Call(TraceCall { + action: trace::Action::Call(trace::Call { from: x!("9cce34f7ab185c7aba1b7c8140d620b4bda941d6"), to: x!(0xa), value: x!(0), gas: x!(79000), input: vec![], }), - result: TraceResult::Call(TraceCallResult { + result: trace::Res::Call(trace::CallResult { gas_used: U256::from(64), output: vec![] }), @@ -686,14 +694,14 @@ fn should_not_trace_delegatecall() { let expected_trace = Some(Trace { depth: 0, - action: TraceAction::Call(TraceCall { + action: trace::Action::Call(trace::Call { from: x!("9cce34f7ab185c7aba1b7c8140d620b4bda941d6"), to: x!(0xa), value: x!(0), gas: x!(79000), input: vec![], }), - result: TraceResult::Call(TraceCallResult { + result: trace::Res::Call(trace::CallResult { gas_used: U256::from(61), output: vec![] }), @@ -727,14 +735,14 @@ fn should_trace_failed_call_transaction() { let result = state.apply(&info, &engine, &t, true).unwrap(); let expected_trace = Some(Trace { depth: 0, - action: TraceAction::Call(TraceCall { + action: trace::Action::Call(trace::Call { from: x!("9cce34f7ab185c7aba1b7c8140d620b4bda941d6"), to: x!(0xa), value: x!(100), gas: x!(79000), input: vec![], }), - result: TraceResult::FailedCall, + result: trace::Res::FailedCall, subs: vec![] }); @@ -769,27 +777,27 @@ fn should_trace_call_with_subcall_transaction() { let result = state.apply(&info, &engine, &t, true).unwrap(); let expected_trace = Some(Trace { depth: 0, - action: TraceAction::Call(TraceCall { + action: trace::Action::Call(trace::Call { from: x!("9cce34f7ab185c7aba1b7c8140d620b4bda941d6"), to: x!(0xa), value: x!(100), gas: x!(79000), input: vec![], }), - result: TraceResult::Call(TraceCallResult { + result: trace::Res::Call(trace::CallResult { gas_used: U256::from(69), output: vec![] }), subs: vec![Trace { depth: 1, - action: TraceAction::Call(TraceCall { + action: trace::Action::Call(trace::Call { from: x!(0xa), to: x!(0xb), value: x!(0), gas: x!(78934), input: vec![], }), - result: TraceResult::Call(TraceCallResult { + result: trace::Res::Call(trace::CallResult { gas_used: U256::from(3), output: vec![] }), @@ -825,27 +833,27 @@ fn should_trace_call_with_basic_subcall_transaction() { let result = state.apply(&info, &engine, &t, true).unwrap(); let expected_trace = Some(Trace { depth: 0, - action: TraceAction::Call(TraceCall { + action: trace::Action::Call(trace::Call { from: x!("9cce34f7ab185c7aba1b7c8140d620b4bda941d6"), to: x!(0xa), value: x!(100), gas: x!(79000), input: vec![], }), - result: TraceResult::Call(TraceCallResult { + result: trace::Res::Call(trace::CallResult { gas_used: U256::from(31761), output: vec![] }), subs: vec![Trace { depth: 1, - action: TraceAction::Call(TraceCall { + action: trace::Action::Call(trace::Call { from: x!(0xa), to: x!(0xb), value: x!(69), gas: x!(2300), input: vec![], }), - result: TraceResult::Call(TraceCallResult::default()), + result: trace::Res::Call(trace::CallResult::default()), subs: vec![] }] }); @@ -878,14 +886,14 @@ fn should_not_trace_call_with_invalid_basic_subcall_transaction() { let result = state.apply(&info, &engine, &t, true).unwrap(); let expected_trace = Some(Trace { depth: 0, - action: TraceAction::Call(TraceCall { + action: trace::Action::Call(trace::Call { from: x!("9cce34f7ab185c7aba1b7c8140d620b4bda941d6"), to: x!(0xa), value: x!(100), gas: x!(79000), input: vec![], }), - result: TraceResult::Call(TraceCallResult { + result: trace::Res::Call(trace::CallResult { gas_used: U256::from(31761), output: vec![] }), @@ -921,27 +929,27 @@ fn should_trace_failed_subcall_transaction() { let result = state.apply(&info, &engine, &t, true).unwrap(); let expected_trace = Some(Trace { depth: 0, - action: TraceAction::Call(TraceCall { + action: trace::Action::Call(trace::Call { from: x!("9cce34f7ab185c7aba1b7c8140d620b4bda941d6"), to: x!(0xa), value: x!(100), gas: x!(79000), input: vec![], }), - result: TraceResult::Call(TraceCallResult { + result: trace::Res::Call(trace::CallResult { gas_used: U256::from(79_000), output: vec![] }), subs: vec![Trace { depth: 1, - action: TraceAction::Call(TraceCall { + action: trace::Action::Call(trace::Call { from: x!(0xa), to: x!(0xb), value: x!(0), gas: x!(78934), input: vec![], }), - result: TraceResult::FailedCall, + result: trace::Res::FailedCall, subs: vec![] }] }); @@ -976,40 +984,40 @@ fn should_trace_call_with_subcall_with_subcall_transaction() { let result = state.apply(&info, &engine, &t, true).unwrap(); let expected_trace = Some(Trace { depth: 0, - action: TraceAction::Call(TraceCall { + action: trace::Action::Call(trace::Call { from: x!("9cce34f7ab185c7aba1b7c8140d620b4bda941d6"), to: x!(0xa), value: x!(100), gas: x!(79000), input: vec![], }), - result: TraceResult::Call(TraceCallResult { + result: trace::Res::Call(trace::CallResult { gas_used: U256::from(135), output: vec![] }), subs: vec![Trace { depth: 1, - action: TraceAction::Call(TraceCall { + action: trace::Action::Call(trace::Call { from: x!(0xa), to: x!(0xb), value: x!(0), gas: x!(78934), input: vec![], }), - result: TraceResult::Call(TraceCallResult { + result: trace::Res::Call(trace::CallResult { gas_used: U256::from(69), output: vec![] }), subs: vec![Trace { depth: 2, - action: TraceAction::Call(TraceCall { + action: trace::Action::Call(trace::Call { from: x!(0xb), to: x!(0xc), value: x!(0), gas: x!(78868), input: vec![], }), - result: TraceResult::Call(TraceCallResult { + result: trace::Res::Call(trace::CallResult { gas_used: U256::from(3), output: vec![] }), @@ -1048,37 +1056,37 @@ fn should_trace_failed_subcall_with_subcall_transaction() { let result = state.apply(&info, &engine, &t, true).unwrap(); let expected_trace = Some(Trace { depth: 0, - action: TraceAction::Call(TraceCall { + action: trace::Action::Call(trace::Call { from: x!("9cce34f7ab185c7aba1b7c8140d620b4bda941d6"), to: x!(0xa), value: x!(100), gas: x!(79000), input: vec![], }), - result: TraceResult::Call(TraceCallResult { + result: trace::Res::Call(trace::CallResult { gas_used: U256::from(79_000), output: vec![] }), subs: vec![Trace { depth: 1, - action: TraceAction::Call(TraceCall { + action: trace::Action::Call(trace::Call { from: x!(0xa), to: x!(0xb), value: x!(0), gas: x!(78934), input: vec![], }), - result: TraceResult::FailedCall, + result: trace::Res::FailedCall, subs: vec![Trace { depth: 2, - action: TraceAction::Call(TraceCall { + action: trace::Action::Call(trace::Call { from: x!(0xb), to: x!(0xc), value: x!(0), gas: x!(78868), input: vec![], }), - result: TraceResult::Call(TraceCallResult { + result: trace::Res::Call(trace::CallResult { gas_used: U256::from(3), output: vec![] }), diff --git a/ethcore/src/trace/block.rs b/ethcore/src/trace/block.rs new file mode 100644 index 00000000000..c85073d96f4 --- /dev/null +++ b/ethcore/src/trace/block.rs @@ -0,0 +1,42 @@ +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/bloom.rs b/ethcore/src/trace/bloom.rs new file mode 100644 index 00000000000..055003e2fab --- /dev/null +++ b/ethcore/src/trace/bloom.rs @@ -0,0 +1,119 @@ +use bloomchain::Bloom; +use bloomchain::group::{BloomGroup, GroupPosition}; +use util::rlp::*; +use util::{H256}; +use basic_types::LogBloom; + +/// Helper structure representing bloom of the trace. +#[derive(Clone)] +pub struct BlockTracesBloom(LogBloom); + +impl From for BlockTracesBloom { + fn from(bloom: LogBloom) -> BlockTracesBloom { + BlockTracesBloom(bloom) + } +} + +impl From for BlockTracesBloom { + fn from(bloom: Bloom) -> BlockTracesBloom { + let bytes: [u8; 256] = bloom.into(); + BlockTracesBloom(LogBloom::from(bytes)) + } +} + +impl Into for BlockTracesBloom { + fn into(self) -> Bloom { + let log = self.0; + Bloom::from(log.0) + } +} + +/// Represents group of X consecutive blooms. +#[derive(Clone)] +pub struct BlockTracesBloomGroup { + blooms: Vec, +} + +impl From for BlockTracesBloomGroup { + fn from(group: BloomGroup) -> Self { + let blooms = group.blooms + .into_iter() + .map(From::from) + .collect(); + + BlockTracesBloomGroup { + blooms: blooms + } + } +} + +impl Into for BlockTracesBloomGroup { + fn into(self) -> BloomGroup { + let blooms = self.blooms + .into_iter() + .map(Into::into) + .collect(); + + BloomGroup { + blooms: blooms + } + } +} + +impl Decodable for BlockTracesBloom { + fn decode(decoder: &D) -> Result where D: Decoder { + Decodable::decode(decoder).map(BlockTracesBloom) + } +} + +impl Encodable for BlockTracesBloom { + fn rlp_append(&self, s: &mut RlpStream) { + Encodable::rlp_append(&self.0, s) + } +} + +impl Decodable for BlockTracesBloomGroup { + fn decode(decoder: &D) -> Result where D: Decoder { + let blooms = try!(Decodable::decode(decoder)); + let group = BlockTracesBloomGroup { + blooms: blooms + }; + Ok(group) + } +} + +impl Encodable for BlockTracesBloomGroup { + fn rlp_append(&self, s: &mut RlpStream) { + Encodable::rlp_append(&self.blooms, s) + } +} + +/// Represents BloomGroup position in database. +#[derive(PartialEq, Eq, Hash, Clone)] +pub struct TraceGroupPosition { + /// Bloom level. + pub level: usize, + /// Group index. + pub index: usize, +} + +impl From for TraceGroupPosition { + fn from(p: GroupPosition) -> Self { + TraceGroupPosition { + level: p.level, + index: p.index, + } + } +} + +impl TraceGroupPosition { + /// Returns trace group position represented as a 256 bit hash. + pub fn hash(&self) -> H256 { + use std::ptr; + let mut hash = H256::default(); + unsafe { + ptr::copy(&[self.level, self.index] as *const usize as *const u8, hash.as_mut_ptr(), 16); + } + hash + } +} diff --git a/ethcore/src/trace/config.rs b/ethcore/src/trace/config.rs new file mode 100644 index 00000000000..2a03a587939 --- /dev/null +++ b/ethcore/src/trace/config.rs @@ -0,0 +1,40 @@ +// 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 . + +//! Traces config. +use bloomchain::Config as BloomConfig; + +///. Traces config. +#[derive(Debug, Clone)] +pub struct Config { + /// Indicates if tracing should be enabled or not. + /// If it's None, it will be automatically configured. + pub enabled: Option, + /// Traces blooms configuration. + pub blooms: BloomConfig, +} + +impl Default for Config { + fn default() -> Self { + Config { + enabled: None, + blooms: BloomConfig { + levels: 3, + elements_per_index: 16, + } + } + } +} diff --git a/ethcore/src/trace/db.rs b/ethcore/src/trace/db.rs new file mode 100644 index 00000000000..ba5b84b86a6 --- /dev/null +++ b/ethcore/src/trace/db.rs @@ -0,0 +1,525 @@ +// 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 . + +//! Fat database. +use std::collections::HashMap; +use std::sync::{RwLock, Arc}; +use std::path::Path; +use bloomchain::Number; +use bloomchain::group::{BloomGroupDatabase, BloomGroupChain, GroupPosition, BloomGroup}; +use util::{FixedHash, H256, H264, Database, DBTransaction}; +use header::BlockNumber; +use trace::{BlockTraces, LocalizedTrace, Config, Filter, Database as TraceDatabase, ImportRequest, +DatabaseExtras}; +use db::{Key, Writable, Readable, CacheUpdatePolicy}; +use super::bloom::{TraceGroupPosition, BlockTracesBloom, BlockTracesBloomGroup}; +use super::flat::{FlatTrace, FlatBlockTraces, FlatTransactionTraces}; + +#[derive(Debug, Copy, Clone)] +pub enum TracedbIndex { + /// Block traces index. + BlockTraces = 0, + /// Trace bloom group index. + BlockTracesBloomGroups = 1, +} + +fn with_index(hash: &H256, i: TracedbIndex) -> H264 { + let mut slice = H264::from_slice(hash); + slice[32] = i as u8; + slice +} + +impl Key for H256 { + fn key(&self) -> H264 { + with_index(self, TracedbIndex::BlockTraces) + } +} + +impl Key for TraceGroupPosition { + fn key(&self) -> H264 { + with_index(&self.hash(), TracedbIndex::BlockTracesBloomGroups) + } +} + +/// Fat database. +pub struct Tracedb where T: DatabaseExtras { + // cache + traces: RwLock>, + blooms: RwLock>, + // db + tracesdb: Database, + // config, + config: Config, + // extras + extras: Arc, +} + +impl BloomGroupDatabase for Tracedb where T: DatabaseExtras { + fn blooms_at(&self, position: &GroupPosition) -> Option { + let position = TraceGroupPosition::from(position.clone()); + self.tracesdb.read_with_cache(&self.blooms, &position).map(Into::into) + } +} + +impl Tracedb where T: DatabaseExtras { + /// Creates new instance of `Tracedb`. + pub fn new(mut config: Config, path: &Path, extras: Arc) -> Self { + let mut tracedb_path = path.to_path_buf(); + tracedb_path.push("tracedb"); + let tracesdb = Database::open_default(tracedb_path.to_str().unwrap()).unwrap(); + + // check if in previously tracing was enabled + let tracing_was_enabled = match tracesdb.get(b"enabled").unwrap() { + Some(ref value) if value as &[u8] == &[0x1] => Some(true), + Some(ref value) if value as &[u8] == &[0x0] => Some(false), + Some(_) => { panic!("tracesdb is malformed") }, + None => None, + }; + + // compare it with the current option. + let tracing = match (tracing_was_enabled, config.enabled) { + (Some(true), Some(true)) => true, + (Some(true), None) => true, + (Some(true), Some(false)) => false, + (Some(false), Some(true)) => { panic!("Tracing can't be enabled. Resync required."); }, + (Some(false), None) => false, + (Some(false), Some(false)) => false, + (None, Some(true)) => true, + _ => false, + }; + + config.enabled = Some(tracing); + + let encoded_tracing= match tracing { + true => [0x1], + false => [0x0] + }; + + tracesdb.put(b"enabled", &encoded_tracing).unwrap(); + + Tracedb { + traces: RwLock::new(HashMap::new()), + blooms: RwLock::new(HashMap::new()), + tracesdb: tracesdb, + config: config, + extras: extras, + } + } + + /// Returns traces for block with hash. + fn traces(&self, block_hash: &H256) -> Option { + self.tracesdb.read_with_cache(&self.traces, block_hash) + } + + fn matching_block_traces( + &self, + filter: &Filter, + traces: FlatBlockTraces, + block_hash: H256, + block_number: BlockNumber + ) -> Vec { + let tx_traces: Vec = traces.into(); + tx_traces.into_iter() + .enumerate() + .flat_map(|(tx_number, tx_trace)| { + self.matching_transaction_traces(filter, tx_trace, block_hash.clone(), block_number, tx_number) + }) + .collect() + } + + fn matching_transaction_traces( + &self, + filter: &Filter, + traces: FlatTransactionTraces, + block_hash: H256, + block_number: BlockNumber, + tx_number: usize + ) -> Vec { + let tx_hash = self.extras.transaction_hash(block_number, tx_number) + .expect("Expected to find transaction hash. Database is probably malformed"); + + let flat_traces: Vec = traces.into(); + flat_traces.into_iter() + .enumerate() + .filter_map(|(index, trace)| { + match filter.matches(&trace) { + true => Some(LocalizedTrace { + parent: trace.parent, + children: trace.children, + depth: trace.depth, + action: trace.action, + result: trace.result, + trace_number: index, + transaction_number: tx_number, + transaction_hash: tx_hash.clone(), + block_number: block_number, + block_hash: block_hash + }), + false => None + } + }) + .collect() + } +} + +impl TraceDatabase for Tracedb where T: DatabaseExtras { + fn tracing_enabled(&self) -> bool { + self.config.enabled.expect("Auto tracing hasn't been properly configured.") + } + + /// Traces of impor request's enacted blocks are expected to be already in database + /// or to be the currenly inserted trace. + fn import(&self, request: ImportRequest) { + // fast return if tracing is disabled + if !self.tracing_enabled() { + return; + } + + let batch = DBTransaction::new(); + + // at first, let's insert new block traces + { + 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(&mut traces, request.block_hash, request.traces, CacheUpdatePolicy::Overwrite); + } + + // now let's rebuild the blooms + { + let range_start = request.block_number as Number + 1 - request.enacted.len(); + let range_end = range_start + request.retracted; + let replaced_range = range_start..range_end; + let enacted_blooms = request.enacted + .iter() + // all traces are expected to be found here. That's why `expect` has been used + // instead of `filter_map`. If some traces haven't been found, it meens that + // traces database is malformed or incomplete. + .map(|block_hash| self.traces(block_hash).expect("Traces database is incomplete.")) + .map(|block_traces| block_traces.bloom()) + .map(BlockTracesBloom::from) + .map(Into::into) + .collect(); + + let chain = BloomGroupChain::new(self.config.blooms, self); + let trace_blooms = chain.replace(&replaced_range, enacted_blooms); + let blooms_to_insert = trace_blooms.into_iter() + .map(|p| (From::from(p.0), From::from(p.1))) + .collect::>(); + + let mut blooms = self.blooms.write().unwrap(); + batch.extend_with_cache(&mut blooms, blooms_to_insert, CacheUpdatePolicy::Remove); + } + + self.tracesdb.write(batch).unwrap(); + } + + fn trace(&self, block_number: BlockNumber, tx_position: usize, trace_position: usize) -> Option { + self.extras.block_hash(block_number) + .and_then(|block_hash| self.traces(&block_hash) + .map(FlatBlockTraces::from) + .map(Into::>::into) + .and_then(|traces| traces.into_iter().nth(tx_position)) + .map(Into::>::into) + .and_then(|traces| traces.into_iter().nth(trace_position)) + .map(|trace| { + let tx_hash = self.extras.transaction_hash(block_number, tx_position) + .expect("Expected to find transaction hash. Database is probably malformed"); + + LocalizedTrace { + parent: trace.parent, + children: trace.children, + depth: trace.depth, + action: trace.action, + result: trace.result, + trace_number: trace_position, + transaction_number: tx_position, + transaction_hash: tx_hash, + block_number: block_number, + block_hash: block_hash, + } + }) + ) + } + + fn transaction_traces(&self, block_number: BlockNumber, tx_position: usize) -> Option> { + self.extras.block_hash(block_number) + .and_then(|block_hash| self.traces(&block_hash) + .map(FlatBlockTraces::from) + .map(Into::>::into) + .and_then(|traces| traces.into_iter().nth(tx_position)) + .map(Into::>::into) + .map(|traces| { + let tx_hash = self.extras.transaction_hash(block_number, tx_position) + .expect("Expected to find transaction hash. Database is probably malformed"); + + traces.into_iter() + .enumerate() + .map(|(i, trace)| LocalizedTrace { + parent: trace.parent, + children: trace.children, + depth: trace.depth, + action: trace.action, + result: trace.result, + trace_number: i, + transaction_number: tx_position, + transaction_hash: tx_hash.clone(), + block_number: block_number, + block_hash: block_hash + }) + .collect() + }) + ) + } + + fn block_traces(&self, block_number: BlockNumber) -> Option> { + self.extras.block_hash(block_number) + .and_then(|block_hash| self.traces(&block_hash) + .map(FlatBlockTraces::from) + .map(Into::>::into) + .map(|traces| { + traces.into_iter() + .map(Into::>::into) + .enumerate() + .flat_map(|(tx_position, traces)| { + let tx_hash = self.extras.transaction_hash(block_number, tx_position) + .expect("Expected to find transaction hash. Database is probably malformed"); + + traces.into_iter() + .enumerate() + .map(|(i, trace)| LocalizedTrace { + parent: trace.parent, + children: trace.children, + depth: trace.depth, + action: trace.action, + result: trace.result, + trace_number: i, + transaction_number: tx_position, + transaction_hash: tx_hash.clone(), + block_number: block_number, + block_hash: block_hash, + }) + .collect::>() + }) + .collect::>() + }) + ) + } + + fn filter(&self, filter: &Filter) -> Vec { + let chain = BloomGroupChain::new(self.config.blooms, self); + let numbers = chain.filter(filter); + numbers.into_iter() + .flat_map(|n| { + let number = n as BlockNumber; + let hash = self.extras.block_hash(number) + .expect("Expected to find block hash. Extras db is probably malformed"); + let traces = self.traces(&hash) + .expect("Expected to find a trace. Db is probably malformed."); + let flat_block = FlatBlockTraces::from(traces); + self.matching_block_traces(filter, flat_block, hash, number) + }) + .collect() + } +} + +#[cfg(test)] +mod tests { + use std::collections::HashMap; + use std::sync::Arc; + use util::{Address, U256, H256}; + use devtools::RandomTempPath; + use header::BlockNumber; + use trace::{Config, Tracedb, Database, DatabaseExtras, ImportRequest, BlockTraces, Trace, Filter, LocalizedTrace}; + use trace::trace::{Call, Action, Res}; + + struct NoopExtras; + + impl DatabaseExtras for NoopExtras { + fn block_hash(&self, _block_number: BlockNumber) -> Option { + unimplemented!(); + } + + fn transaction_hash(&self, _block_number: BlockNumber, _tx_position: usize) -> Option { + unimplemented!(); + } + } + + struct Extras { + block_hashes: HashMap, + transaction_hashes: HashMap>, + } + + impl Default for Extras { + fn default() -> Self { + Extras { + block_hashes: HashMap::new(), + transaction_hashes: HashMap::new(), + } + } + } + + impl DatabaseExtras for Extras { + fn block_hash(&self, block_number: BlockNumber) -> Option { + self.block_hashes.get(&block_number).cloned() + } + + fn transaction_hash(&self, block_number: BlockNumber, tx_position: usize) -> Option { + self.transaction_hashes.get(&block_number) + .and_then(|hashes| hashes.iter().cloned().nth(tx_position)) + } + } + + #[test] + fn test_reopening_db_with_tracing_off() { + let temp = RandomTempPath::new(); + let mut config = Config::default(); + + // set autotracing + config.enabled = None; + + { + let tracedb = Tracedb::new(config.clone(), temp.as_path(), Arc::new(NoopExtras)); + assert_eq!(tracedb.tracing_enabled(), false); + } + + { + let tracedb = Tracedb::new(config.clone(), temp.as_path(), Arc::new(NoopExtras)); + assert_eq!(tracedb.tracing_enabled(), false); + } + + config.enabled = Some(false); + + { + let tracedb = Tracedb::new(config.clone(), temp.as_path(), Arc::new(NoopExtras)); + assert_eq!(tracedb.tracing_enabled(), false); + } + } + + #[test] + fn test_reopeining_db_with_tracing_on() { + let temp = RandomTempPath::new(); + let mut config = Config::default(); + + // set tracing on + config.enabled = Some(true); + + { + let tracedb = Tracedb::new(config.clone(), temp.as_path(), Arc::new(NoopExtras)); + assert_eq!(tracedb.tracing_enabled(), true); + } + + { + let tracedb = Tracedb::new(config.clone(), temp.as_path(), Arc::new(NoopExtras)); + assert_eq!(tracedb.tracing_enabled(), true); + } + + config.enabled = None; + + { + let tracedb = Tracedb::new(config.clone(), temp.as_path(), Arc::new(NoopExtras)); + assert_eq!(tracedb.tracing_enabled(), true); + } + + config.enabled = Some(false); + + { + let tracedb = Tracedb::new(config.clone(), temp.as_path(), Arc::new(NoopExtras)); + assert_eq!(tracedb.tracing_enabled(), false); + } + } + + #[test] + #[should_panic] + fn test_invalid_reopeining_db() { + let temp = RandomTempPath::new(); + let mut config = Config::default(); + + // set tracing on + config.enabled = Some(false); + + { + let tracedb = Tracedb::new(config.clone(), temp.as_path(), Arc::new(NoopExtras)); + assert_eq!(tracedb.tracing_enabled(), true); + } + + config.enabled = Some(true); + Tracedb::new(config.clone(), temp.as_path(), Arc::new(NoopExtras)); // should panic! + } + + #[test] + fn test_import() { + let temp = RandomTempPath::new(); + let mut config = Config::default(); + config.enabled = Some(true); + let block_0 = H256::from(0xa1); + let tx_0 = H256::from(0xff); + + let mut extras = Extras::default(); + extras.block_hashes.insert(0, block_0.clone()); + extras.transaction_hashes.insert(0, vec![tx_0.clone()]); + + let tracedb = Tracedb::new(config, temp.as_path(), Arc::new(extras)); + + let request = ImportRequest { + traces: BlockTraces::from(vec![Trace { + depth: 0, + action: Action::Call(Call { + from: Address::from(1), + to: Address::from(2), + value: U256::from(3), + gas: U256::from(4), + input: vec![], + }), + result: Res::FailedCall, + subs: vec![], + }]), + block_hash: block_0.clone(), + block_number: 0, + enacted: vec![block_0.clone()], + retracted: 0, + }; + + tracedb.import(request); + + let filter = Filter { + range: (0..0), + from_address: vec![Address::from(1)], + to_address: vec![], + }; + + let traces = tracedb.filter(&filter); + assert_eq!(traces.len(), 1); + assert_eq!(traces[0], LocalizedTrace { + parent: None, + children: vec![], + depth: 0, + action: Action::Call(Call { + from: Address::from(1), + to: Address::from(2), + value: U256::from(3), + gas: U256::from(4), + input: vec![], + }), + result: Res::FailedCall, + trace_number: 0, + transaction_number: 0, + transaction_hash: tx_0.clone(), + block_number: 0, + block_hash: block_0.clone(), + }); + } +} diff --git a/ethcore/src/trace/executive_tracer.rs b/ethcore/src/trace/executive_tracer.rs new file mode 100644 index 00000000000..5117181ce87 --- /dev/null +++ b/ethcore/src/trace/executive_tracer.rs @@ -0,0 +1,108 @@ +// 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 . + +//! Simple executive tracer. + +use util::{Bytes, Address, U256}; +use action_params::ActionParams; +use trace::trace::{Trace, Call, Create, Action, Res, CreateResult, CallResult}; +use trace::Tracer; + +/// Simple executive tracer. Traces all calls and creates. Ignores delegatecalls. +#[derive(Default)] +pub struct ExecutiveTracer { + traces: Vec +} + +impl Tracer for ExecutiveTracer { + fn prepare_trace_call(&self, params: &ActionParams) -> Option { + Some(Call::from(params.clone())) + } + + fn prepare_trace_create(&self, params: &ActionParams) -> Option { + Some(Create::from(params.clone())) + } + + fn prepare_trace_output(&self) -> Option { + 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; + } + + let trace = Trace { + depth: depth, + subs: subs, + action: Action::Call(call.expect("Trace call expected to be Some.")), + result: Res::Call(CallResult { + gas_used: gas_used, + output: output.expect("Trace call output expected to be Some.") + }) + }; + self.traces.push(trace); + } + + 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, + action: Action::Create(create.expect("Trace create expected to be Some.")), + result: Res::Create(CreateResult { + gas_used: gas_used, + code: code.expect("Trace create code expected to be Some."), + address: address + }) + }; + 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; + } + + let trace = Trace { + depth: depth, + subs: subs, + action: Action::Call(call.expect("Trace call expected to be Some.")), + result: Res::FailedCall, + }; + self.traces.push(trace); + } + + fn trace_failed_create(&mut self, create: Option, depth: usize, subs: Vec) { + let trace = Trace { + depth: depth, + subs: subs, + action: Action::Create(create.expect("Trace create expected to be Some.")), + result: Res::FailedCreate, + }; + self.traces.push(trace); + } + + fn subtracer(&self) -> Self { + ExecutiveTracer::default() + } + + fn traces(self) -> Vec { + self.traces + } +} diff --git a/ethcore/src/trace/filter.rs b/ethcore/src/trace/filter.rs new file mode 100644 index 00000000000..992ab20ce94 --- /dev/null +++ b/ethcore/src/trace/filter.rs @@ -0,0 +1,255 @@ +// 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 std::ops::Range; +use bloomchain::{Filter as BloomFilter, Bloom, Number}; +use util::{Address, FixedHash}; +use util::sha3::Hashable; +use basic_types::LogBloom; +use super::flat::FlatTrace; +use super::trace::Action; + +/// Traces filter. +pub struct Filter { + /// Block range. + pub range: Range, + + /// From address. If empty, match all, if not, match one of the values. + pub from_address: Vec
, + + /// To address. If empty, match all, if not, match one of the values. + pub to_address: Vec
, +} + +impl BloomFilter for Filter { + fn bloom_possibilities(&self) -> Vec { + self.bloom_possibilities() + .into_iter() + .map(|b| Bloom::from(b.0)) + .collect() + } + + fn range(&self) -> Range { + self.range.clone() + } +} + +impl Filter { + /// Returns combinations of each address. + fn bloom_possibilities(&self) -> Vec { + let blooms = match self.from_address.is_empty() { + true => vec![LogBloom::new()], + false => self.from_address + .iter() + .map(|address| LogBloom::from_bloomed(&address.sha3())) + .collect() + }; + + match self.to_address.is_empty() { + true => blooms, + false => blooms + .into_iter() + .flat_map(|bloom| self.to_address + .iter() + .map(| address | bloom.with_bloomed(&address.sha3())) + .collect::>()) + .collect() + } + } + + /// Returns true if given trace matches the filter. + pub fn matches(&self, trace: &FlatTrace) -> bool { + match trace.action { + Action::Call(ref call) => { + let from_matches = self.from_address.is_empty() || self.from_address.contains(&call.from); + let to_matches = self.to_address.is_empty() || self.to_address.contains(&call.to); + from_matches && to_matches + }, + Action::Create(ref create) => { + let from_matches = self.from_address.is_empty() || self.from_address.contains(&create.from); + let to_matches = self.to_address.is_empty(); + from_matches && to_matches + } + } + } +} + +#[cfg(test)] +mod tests { + use util::{FixedHash, Address, U256}; + use util::sha3::Hashable; + use trace::trace::{Action, Call, Res}; + use trace::flat::FlatTrace; + use trace::Filter; + use basic_types::LogBloom; + + #[test] + fn empty_trace_filter_bloom_possibilies() { + let filter = Filter { + range: (0..0), + from_address: vec![], + to_address: vec![], + }; + + let blooms = filter.bloom_possibilities(); + assert_eq!(blooms, vec![LogBloom::new()]); + } + + #[test] + fn single_trace_filter_bloom_possibility() { + let filter = Filter { + range: (0..0), + from_address: vec![Address::from(1)], + to_address: vec![Address::from(2)], + }; + + let blooms = filter.bloom_possibilities(); + assert_eq!(blooms.len(), 1); + + assert!(blooms[0].contains_bloomed(&Address::from(1).sha3())); + assert!(blooms[0].contains_bloomed(&Address::from(2).sha3())); + assert!(!blooms[0].contains_bloomed(&Address::from(3).sha3())); + } + + #[test] + fn only_from_trace_filter_bloom_possibility() { + let filter = Filter { + range: (0..0), + from_address: vec![Address::from(1)], + to_address: vec![], + }; + + let blooms = filter.bloom_possibilities(); + assert_eq!(blooms.len(), 1); + + assert!(blooms[0].contains_bloomed(&Address::from(1).sha3())); + assert!(!blooms[0].contains_bloomed(&Address::from(2).sha3())); + } + + #[test] + fn only_to_trace_filter_bloom_possibility() { + let filter = Filter { + range: (0..0), + from_address: vec![], + to_address: vec![Address::from(1)], + }; + + let blooms = filter.bloom_possibilities(); + assert_eq!(blooms.len(), 1); + + assert!(blooms[0].contains_bloomed(&Address::from(1).sha3())); + assert!(!blooms[0].contains_bloomed(&Address::from(2).sha3())); + } + + #[test] + fn multiple_trace_filter_bloom_possibility() { + let filter = Filter { + range: (0..0), + from_address: vec![Address::from(1), Address::from(3)], + to_address: vec![Address::from(2), Address::from(4)], + }; + + let blooms = filter.bloom_possibilities(); + assert_eq!(blooms.len(), 4); + + assert!(blooms[0].contains_bloomed(&Address::from(1).sha3())); + assert!(blooms[0].contains_bloomed(&Address::from(2).sha3())); + assert!(!blooms[0].contains_bloomed(&Address::from(3).sha3())); + assert!(!blooms[0].contains_bloomed(&Address::from(4).sha3())); + + assert!(blooms[1].contains_bloomed(&Address::from(1).sha3())); + assert!(blooms[1].contains_bloomed(&Address::from(4).sha3())); + assert!(!blooms[1].contains_bloomed(&Address::from(2).sha3())); + assert!(!blooms[1].contains_bloomed(&Address::from(3).sha3())); + + assert!(blooms[2].contains_bloomed(&Address::from(2).sha3())); + assert!(blooms[2].contains_bloomed(&Address::from(3).sha3())); + assert!(!blooms[2].contains_bloomed(&Address::from(1).sha3())); + assert!(!blooms[2].contains_bloomed(&Address::from(4).sha3())); + + assert!(blooms[3].contains_bloomed(&Address::from(3).sha3())); + assert!(blooms[3].contains_bloomed(&Address::from(4).sha3())); + assert!(!blooms[3].contains_bloomed(&Address::from(1).sha3())); + assert!(!blooms[3].contains_bloomed(&Address::from(2).sha3())); + } + + #[test] + fn filter_matches() { + let f0 = Filter { + range: (0..0), + from_address: vec![Address::from(1)], + to_address: vec![], + }; + + let f1 = Filter { + range: (0..0), + from_address: vec![Address::from(3), Address::from(1)], + to_address: vec![], + }; + + let f2 = Filter { + range: (0..0), + from_address: vec![], + to_address: vec![], + }; + + let f3 = Filter { + range: (0..0), + from_address: vec![], + to_address: vec![Address::from(2)], + }; + + let f4 = Filter { + range: (0..0), + from_address: vec![], + to_address: vec![Address::from(2), Address::from(3)], + }; + + let f5 = Filter { + range: (0..0), + from_address: vec![Address::from(1)], + to_address: vec![Address::from(2), Address::from(3)], + }; + + let f6 = Filter { + range: (0..0), + from_address: vec![Address::from(1)], + to_address: vec![Address::from(4)], + }; + + let trace = FlatTrace { + parent: None, + children: vec![], + depth: 0, + action: Action::Call(Call { + from: Address::from(1), + to: Address::from(2), + value: U256::from(3), + gas: U256::from(4), + input: vec![0x5], + }), + result: Res::FailedCall, + }; + + 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/trace/flat.rs b/ethcore/src/trace/flat.rs new file mode 100644 index 00000000000..60856c8737b --- /dev/null +++ b/ethcore/src/trace/flat.rs @@ -0,0 +1,183 @@ +// 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 trace::BlockTraces; +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. +pub struct FlatTrace { + /// Index of the parent trace within the same transaction. + pub parent: Option, + /// Indexes of child traces within the same transaction. + pub children: Vec, + /// VM depth. + pub depth: usize, + /// Type of action performed by a transaction. + pub action: Action, + /// Result of this action. + pub result: Res, +} + +/// Represents all traces produced by a single transaction. +pub struct FlatTransactionTraces(Vec); + +impl Into> for FlatTransactionTraces { + fn into(self) -> Vec { + self.0 + } +} + +/// Represents all traces produced by transactions in a single block. +pub struct FlatBlockTraces(Vec); + +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(None, 0, 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(parent_index: Option, len: usize, trace: Trace) -> Vec { + let mut children = vec![]; + let mut next_index = len + 1; + let all_subs = trace.subs + .into_iter() + .flat_map(|subtrace| { + let subs = FlatBlockTraces::flatten(Some(len), next_index, subtrace); + children.push(next_index); + next_index = next_index + subs.len(); + subs + }) + .collect::>(); + + let ordered = FlatTrace { + parent: parent_index, + children: children, + depth: trace.depth, + action: trace.action, + result: trace.result, + }; + + 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; + + #[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] + }), + 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].parent, None); + assert_eq!(ordered_traces[0].children, vec![1, 4]); + assert_eq!(ordered_traces[1].parent, Some(0)); + assert_eq!(ordered_traces[1].children, vec![2, 3]); + assert_eq!(ordered_traces[2].parent, Some(1)); + assert_eq!(ordered_traces[2].children, vec![]); + assert_eq!(ordered_traces[3].parent, Some(1)); + assert_eq!(ordered_traces[3].children, vec![]); + assert_eq!(ordered_traces[4].parent, Some(0)); + assert_eq!(ordered_traces[4].children, vec![]); + } +} diff --git a/ethcore/src/trace/import.rs b/ethcore/src/trace/import.rs new file mode 100644 index 00000000000..a6b4a29bbbe --- /dev/null +++ b/ethcore/src/trace/import.rs @@ -0,0 +1,36 @@ +// 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 . + +//! Traces import request. +use util::H256; +use header::BlockNumber; +use trace::BlockTraces; + +/// Traces import request. +pub struct ImportRequest { + /// Traces to import. + pub traces: BlockTraces, + /// Hash of traces block. + pub block_hash: H256, + /// Number of traces block. + pub block_number: BlockNumber, + /// Blocks enacted by this import. + /// + /// They should be ordered from oldest to newest. + pub enacted: Vec, + /// Number of blocks retracted by this import. + pub retracted: usize, +} diff --git a/ethcore/src/trace/localized.rs b/ethcore/src/trace/localized.rs new file mode 100644 index 00000000000..0a9d42f8106 --- /dev/null +++ b/ethcore/src/trace/localized.rs @@ -0,0 +1,44 @@ +// 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::H256; +use super::trace::{Action, Res}; +use header::BlockNumber; + +/// Localized trace. +#[derive(Debug, PartialEq)] +pub struct LocalizedTrace { + /// Index of the parent trace within the same transaction. + pub parent: Option, + /// Indexes of child traces within the same transaction. + pub children: Vec, + /// VM depth. + pub depth: usize, + /// Type of action performed by a transaction. + pub action: Action, + /// Result of this action. + pub result: Res, + /// Trace number within the transaction. + pub trace_number: usize, + /// Transaction number within the block. + pub transaction_number: usize, + /// Signed transaction hash. + pub transaction_hash: H256, + /// Block number. + pub block_number: BlockNumber, + /// Block hash. + pub block_hash: H256, +} diff --git a/ethcore/src/trace/mod.rs b/ethcore/src/trace/mod.rs index 2a5bda6c502..c8a45b88bbb 100644 --- a/ethcore/src/trace/mod.rs +++ b/ethcore/src/trace/mod.rs @@ -16,21 +16,39 @@ //! Tracing -mod trace; - -pub use self::trace::*; -use util::bytes::Bytes; -use util::hash::Address; -use util::numbers::U256; +mod block; +mod bloom; +mod config; +mod db; +mod executive_tracer; +mod filter; +mod flat; +mod import; +mod localized; +mod noop_tracer; +pub mod trace; + +pub use self::block::BlockTraces; +pub use self::config::Config; +pub use self::db::Tracedb; +pub use self::trace::Trace; +pub use self::noop_tracer::NoopTracer; +pub use self::executive_tracer::ExecutiveTracer; +pub use self::filter::Filter; +pub use self::import::ImportRequest; +pub use self::localized::LocalizedTrace; +use util::{Bytes, Address, U256, H256}; +use self::trace::{Call, Create}; use action_params::ActionParams; +use header::BlockNumber; /// This trait is used by executive to build traces. pub trait Tracer: Send { /// Prepares call trace for given params. Noop tracer should return None. - fn prepare_trace_call(&self, params: &ActionParams) -> Option; + fn prepare_trace_call(&self, params: &ActionParams) -> Option; /// Prepares create trace for given params. Noop tracer should return None. - fn prepare_trace_create(&self, params: &ActionParams) -> Option; + fn prepare_trace_create(&self, params: &ActionParams) -> Option; /// Prepare trace output. Noop tracer should return None. fn prepare_trace_output(&self) -> Option; @@ -38,7 +56,7 @@ pub trait Tracer: Send { /// Stores trace call info. fn trace_call( &mut self, - call: Option, + call: Option, gas_used: U256, output: Option, depth: usize, @@ -49,7 +67,7 @@ pub trait Tracer: Send { /// Stores trace create info. fn trace_create( &mut self, - create: Option, + create: Option, gas_used: U256, code: Option, address: Address, @@ -58,10 +76,10 @@ 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, delegate_call: bool); /// Stores failed create trace. - fn trace_failed_create(&mut self, create: Option, depth: usize, subs: Vec); + fn trace_failed_create(&mut self, create: Option, depth: usize, subs: Vec); /// Spawn subracer which will be used to trace deeper levels of execution. fn subtracer(&self) -> Self where Self: Sized; @@ -70,132 +88,33 @@ pub trait Tracer: Send { fn traces(self) -> Vec; } -/// Nonoperative tracer. Does not trace anything. -pub struct NoopTracer; - -impl Tracer for NoopTracer { - fn prepare_trace_call(&self, _: &ActionParams) -> Option { - None - } - - fn prepare_trace_create(&self, _: &ActionParams) -> Option { - None - } - - fn prepare_trace_output(&self) -> Option { - None - } +/// DbExtras provides an interface to query extra data which is not stored in tracesdb, +/// but necessary to work correctly. +pub trait DatabaseExtras { + /// Returns hash of given block number. + fn block_hash(&self, block_number: BlockNumber) -> Option; - fn trace_call(&mut self, call: Option, _: U256, output: Option, _: usize, _: Vec, - _: bool) { - assert!(call.is_none()); - assert!(output.is_none()); - } - - fn trace_create(&mut self, create: Option, _: U256, code: Option, _: Address, _: usize, _: Vec) { - assert!(create.is_none()); - assert!(code.is_none()); - } + /// Returns hash of transaction at given position. + fn transaction_hash(&self, block_number: BlockNumber, tx_position: usize) -> Option; +} - fn trace_failed_call(&mut self, call: Option, _: usize, _: Vec, _: bool) { - assert!(call.is_none()); - } +/// Db provides an interface to query tracesdb. +pub trait Database { + /// Returns true if tracing is enabled. Otherwise false. + fn tracing_enabled(&self) -> bool; - fn trace_failed_create(&mut self, create: Option, _: usize, _: Vec) { - assert!(create.is_none()); - } + /// Imports new block traces. + fn import(&self, request: ImportRequest); - fn subtracer(&self) -> Self { - NoopTracer - } + /// Returns localized trace at given position. + fn trace(&self, block_number: BlockNumber, tx_position: usize, trace_position: usize) -> Option; - fn traces(self) -> Vec { - vec![] - } -} + /// Returns localized traces created by a single transaction. + fn transaction_traces(&self, block_number: BlockNumber, tx_position: usize) -> Option>; -/// Simple executive tracer. Traces all calls and creates. Ignores delegatecalls. -#[derive(Default)] -pub struct ExecutiveTracer { - traces: Vec -} + /// Returns localized traces created in given block. + fn block_traces(&self, block_number: BlockNumber) -> Option>; -impl Tracer for ExecutiveTracer { - fn prepare_trace_call(&self, params: &ActionParams) -> Option { - Some(TraceCall::from(params.clone())) - } - - fn prepare_trace_create(&self, params: &ActionParams) -> Option { - Some(TraceCreate::from(params.clone())) - } - - fn prepare_trace_output(&self) -> Option { - 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; - } - - let trace = Trace { - depth: depth, - subs: subs, - action: TraceAction::Call(call.expect("Trace call expected to be Some.")), - result: TraceResult::Call(TraceCallResult { - gas_used: gas_used, - output: output.expect("Trace call output expected to be Some.") - }) - }; - self.traces.push(trace); - } - - 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, - action: TraceAction::Create(create.expect("Trace create expected to be Some.")), - result: TraceResult::Create(TraceCreateResult { - gas_used: gas_used, - code: code.expect("Trace create code expected to be Some."), - address: address - }) - }; - 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; - } - - let trace = Trace { - depth: depth, - subs: subs, - action: TraceAction::Call(call.expect("Trace call expected to be Some.")), - result: TraceResult::FailedCall, - }; - self.traces.push(trace); - } - - fn trace_failed_create(&mut self, create: Option, depth: usize, subs: Vec) { - let trace = Trace { - depth: depth, - subs: subs, - action: TraceAction::Create(create.expect("Trace create expected to be Some.")), - result: TraceResult::FailedCreate, - }; - self.traces.push(trace); - } - - fn subtracer(&self) -> Self { - ExecutiveTracer::default() - } - - fn traces(self) -> Vec { - self.traces - } + /// Filter traces matching given filter. + fn filter(&self, filter: &Filter) -> Vec; } diff --git a/ethcore/src/trace/noop_tracer.rs b/ethcore/src/trace/noop_tracer.rs new file mode 100644 index 00000000000..c21088b11a8 --- /dev/null +++ b/ethcore/src/trace/noop_tracer.rs @@ -0,0 +1,66 @@ +// 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 . + +//! Nonoperative tracer. + +use util::{Bytes, Address, U256}; +use action_params::ActionParams; +use trace::Tracer; +use trace::trace::{Trace, Call, Create}; + +/// Nonoperative tracer. Does not trace anything. +pub struct NoopTracer; + +impl Tracer for NoopTracer { + fn prepare_trace_call(&self, _: &ActionParams) -> Option { + None + } + + fn prepare_trace_create(&self, _: &ActionParams) -> Option { + None + } + + fn prepare_trace_output(&self) -> Option { + None + } + + fn trace_call(&mut self, call: Option, _: U256, output: Option, _: usize, _: Vec, + _: bool) { + assert!(call.is_none()); + assert!(output.is_none()); + } + + fn trace_create(&mut self, create: Option, _: U256, code: Option, _: Address, _: usize, _: Vec) { + assert!(create.is_none()); + assert!(code.is_none()); + } + + fn trace_failed_call(&mut self, call: Option, _: usize, _: Vec, _: bool) { + assert!(call.is_none()); + } + + fn trace_failed_create(&mut self, create: Option, _: usize, _: Vec) { + assert!(create.is_none()); + } + + fn subtracer(&self) -> Self { + NoopTracer + } + + fn traces(self) -> Vec { + vec![] + } +} diff --git a/ethcore/src/trace/trace.rs b/ethcore/src/trace/trace.rs index 6a90aaf8aef..f7efe9721f1 100644 --- a/ethcore/src/trace/trace.rs +++ b/ethcore/src/trace/trace.rs @@ -15,20 +15,44 @@ // along with Parity. If not, see . //! Tracing datatypes. -use common::*; +use util::{U256, Bytes, Address, FixedHash}; +use util::rlp::*; +use util::sha3::Hashable; +use action_params::ActionParams; +use basic_types::LogBloom; -/// `TraceCall` result. +/// `Call` result. #[derive(Debug, Clone, PartialEq, Default)] -pub struct TraceCallResult { +pub struct CallResult { /// Gas used by call. pub gas_used: U256, /// Call Output. pub output: Bytes, } -/// `TraceCreate` result. +impl Encodable for CallResult { + fn rlp_append(&self, s: &mut RlpStream) { + s.begin_list(2); + s.append(&self.gas_used); + s.append(&self.output); + } +} + +impl Decodable for CallResult { + fn decode(decoder: &D) -> Result where D: Decoder { + let d = decoder.as_rlp(); + let res = CallResult { + gas_used: try!(d.val_at(0)), + output: try!(d.val_at(1)), + }; + + Ok(res) + } +} + +/// `Create` result. #[derive(Debug, Clone, PartialEq)] -pub struct TraceCreateResult { +pub struct CreateResult { /// Gas used by create. pub gas_used: U256, /// Code of the newly created contract. @@ -37,9 +61,31 @@ pub struct TraceCreateResult { pub address: Address, } +impl Encodable for CreateResult { + fn rlp_append(&self, s: &mut RlpStream) { + s.begin_list(3); + s.append(&self.gas_used); + s.append(&self.code); + s.append(&self.address); + } +} + +impl Decodable for CreateResult { + fn decode(decoder: &D) -> Result where D: Decoder { + let d = decoder.as_rlp(); + let res = CreateResult { + gas_used: try!(d.val_at(0)), + code: try!(d.val_at(1)), + address: try!(d.val_at(2)), + }; + + Ok(res) + } +} + /// Description of a _call_ action, either a `CALL` operation or a message transction. #[derive(Debug, Clone, PartialEq)] -pub struct TraceCall { +pub struct Call { /// The sending account. pub from: Address, /// The destination account. @@ -52,9 +98,9 @@ pub struct TraceCall { pub input: Bytes, } -impl From for TraceCall { +impl From for Call { fn from(p: ActionParams) -> Self { - TraceCall { + Call { from: p.sender, to: p.address, value: p.value.value(), @@ -64,9 +110,44 @@ impl From for TraceCall { } } +impl Encodable for Call { + fn rlp_append(&self, s: &mut RlpStream) { + s.begin_list(5); + s.append(&self.from); + s.append(&self.to); + s.append(&self.value); + s.append(&self.gas); + s.append(&self.input); + } +} + +impl Decodable for Call { + fn decode(decoder: &D) -> Result where D: Decoder { + let d = decoder.as_rlp(); + let res = Call { + from: try!(d.val_at(0)), + to: try!(d.val_at(1)), + value: try!(d.val_at(2)), + gas: try!(d.val_at(3)), + input: try!(d.val_at(4)), + }; + + Ok(res) + } +} + +impl Call { + /// Returns call action bloom. + /// The bloom contains from and to addresses. + pub fn bloom(&self) -> LogBloom { + LogBloom::from_bloomed(&self.from.sha3()) + .with_bloomed(&self.to.sha3()) + } +} + /// Description of a _create_ action, either a `CREATE` operation or a create transction. #[derive(Debug, Clone, PartialEq)] -pub struct TraceCreate { +pub struct Create { /// The address of the creator. pub from: Address, /// The value with which the new account is endowed. @@ -77,9 +158,9 @@ pub struct TraceCreate { pub init: Bytes, } -impl From for TraceCreate { +impl From for Create { fn from(p: ActionParams) -> Self { - TraceCreate { + Create { from: p.sender, value: p.value.value(), gas: p.gas, @@ -88,28 +169,137 @@ impl From for TraceCreate { } } +impl Encodable for Create { + fn rlp_append(&self, s: &mut RlpStream) { + s.begin_list(4); + s.append(&self.from); + s.append(&self.value); + s.append(&self.gas); + s.append(&self.init); + } +} + +impl Decodable for Create { + fn decode(decoder: &D) -> Result where D: Decoder { + let d = decoder.as_rlp(); + let res = Create { + from: try!(d.val_at(0)), + value: try!(d.val_at(1)), + gas: try!(d.val_at(2)), + init: try!(d.val_at(3)), + }; + + Ok(res) + } +} + +impl Create { + /// Returns bloom create action bloom. + /// The bloom contains only from address. + pub fn bloom(&self) -> LogBloom { + LogBloom::from_bloomed(&self.from.sha3()) + } +} + /// Description of an action that we trace; will be either a call or a create. #[derive(Debug, Clone, PartialEq)] -pub enum TraceAction { +pub enum Action { /// It's a call action. - Call(TraceCall), + Call(Call), /// It's a create action. - Create(TraceCreate), + Create(Create), +} + +impl Encodable for Action { + fn rlp_append(&self, s: &mut RlpStream) { + s.begin_list(2); + match *self { + Action::Call(ref call) => { + s.append(&0u8); + s.append(call); + }, + Action::Create(ref create) => { + s.append(&1u8); + s.append(create); + } + } + } +} + +impl Decodable for Action { + fn decode(decoder: &D) -> Result where D: Decoder { + let d = decoder.as_rlp(); + let action_type: u8 = try!(d.val_at(0)); + match action_type { + 0 => d.val_at(1).map(Action::Call), + 1 => d.val_at(1).map(Action::Create), + _ => Err(DecoderError::Custom("Invalid action type.")), + } + } +} + +impl Action { + /// Returns action bloom. + pub fn bloom(&self) -> LogBloom { + match *self { + Action::Call(ref call) => call.bloom(), + Action::Create(ref create) => create.bloom(), + } + } } /// The result of the performed action. #[derive(Debug, Clone, PartialEq)] -pub enum TraceResult { +pub enum Res { /// Successful call action result. - Call(TraceCallResult), + Call(CallResult), /// Successful create action result. - Create(TraceCreateResult), + Create(CreateResult), /// Failed call. FailedCall, /// Failed create. FailedCreate, } +impl Encodable for Res { + fn rlp_append(&self, s: &mut RlpStream) { + match *self { + Res::Call(ref call) => { + s.begin_list(2); + s.append(&0u8); + s.append(call); + }, + Res::Create(ref create) => { + s.begin_list(2); + s.append(&1u8); + s.append(create); + }, + Res::FailedCall => { + s.begin_list(1); + s.append(&2u8); + }, + Res::FailedCreate => { + s.begin_list(1); + s.append(&3u8); + } + } + } +} + +impl Decodable for Res { + fn decode(decoder: &D) -> Result where D: Decoder { + let d = decoder.as_rlp(); + let action_type: u8 = try!(d.val_at(0)); + match action_type { + 0 => d.val_at(1).map(Res::Call), + 1 => d.val_at(1).map(Res::Create), + 2 => Ok(Res::FailedCall), + 3 => Ok(Res::FailedCreate), + _ => Err(DecoderError::Custom("Invalid result type.")), + } + } +} + #[derive(Debug, Clone, PartialEq)] /// A trace; includes a description of the action being traced and sub traces of each interior action. pub struct Trace { @@ -117,9 +307,122 @@ pub struct Trace { /// the outer action of the transaction. pub depth: usize, /// The action being performed. - pub action: TraceAction, + 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: TraceResult, + 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(), |b, s| b | s.bloom()) + } +} + +#[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}; + + #[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] + }), + 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] + }), + 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 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())); + } } diff --git a/ethcore/src/verification/verification.rs b/ethcore/src/verification/verification.rs index a3cc2103235..ba7ef3bd49b 100644 --- a/ethcore/src/verification/verification.rs +++ b/ethcore/src/verification/verification.rs @@ -278,8 +278,6 @@ mod tests { } impl BlockProvider for TestBlockChain { - fn have_tracing(&self) -> bool { false } - fn is_known(&self, hash: &H256) -> bool { self.blocks.contains_key(hash) } diff --git a/util/src/hash.rs b/util/src/hash.rs index 1b894d82fbe..69ed17c7975 100644 --- a/util/src/hash.rs +++ b/util/src/hash.rs @@ -78,6 +78,12 @@ macro_rules! impl_hash { /// Unformatted binary data of fixed length. pub struct $from (pub [u8; $size]); + impl From<[u8; $size]> for $from { + fn from(bytes: [u8; $size]) -> Self { + $from(bytes) + } + } + impl Deref for $from { type Target = [u8]; diff --git a/util/src/rlp/rlperrors.rs b/util/src/rlp/rlperrors.rs index 2d821be5fd9..93a82187ee2 100644 --- a/util/src/rlp/rlperrors.rs +++ b/util/src/rlp/rlperrors.rs @@ -40,7 +40,9 @@ pub enum DecoderError { /// Non-canonical (longer than necessary) representation used for data or list. RlpInvalidIndirection, /// Declared length is inconsistent with data specified after. - RlpInconsistentLengthAndData + RlpInconsistentLengthAndData, + /// Custom rlp decoding error. + Custom(&'static str), } impl StdError for DecoderError { diff --git a/util/src/rlp/rlpstream.rs b/util/src/rlp/rlpstream.rs index 7bf3d3cddd9..c2ff88c41c9 100644 --- a/util/src/rlp/rlpstream.rs +++ b/util/src/rlp/rlpstream.rs @@ -307,10 +307,14 @@ struct EncodableU8 (u8); impl ByteEncodable for EncodableU8 { fn to_bytes>(&self, out: &mut V) { - out.vec_push(self.0) + if self.0 != 0 { + out.vec_push(self.0) + } } - fn bytes_len(&self) -> usize { 1 } + fn bytes_len(&self) -> usize { + match self.0 { 0 => 0, _ => 1 } + } } impl RlpEncodable for u8 { From a20a9d82ed9c4b64b8ad3f3c3df193dedd47869b Mon Sep 17 00:00:00 2001 From: debris Date: Mon, 25 Apr 2016 23:51:24 +0200 Subject: [PATCH 04/24] more comprehensive tracedb tests --- ethcore/src/trace/db.rs | 116 ++++++++++++++++++++++++++++------------ 1 file changed, 82 insertions(+), 34 deletions(-) diff --git a/ethcore/src/trace/db.rs b/ethcore/src/trace/db.rs index ba5b84b86a6..025bc582667 100644 --- a/ethcore/src/trace/db.rs +++ b/ethcore/src/trace/db.rs @@ -460,21 +460,8 @@ mod tests { Tracedb::new(config.clone(), temp.as_path(), Arc::new(NoopExtras)); // should panic! } - #[test] - fn test_import() { - let temp = RandomTempPath::new(); - let mut config = Config::default(); - config.enabled = Some(true); - let block_0 = H256::from(0xa1); - let tx_0 = H256::from(0xff); - - let mut extras = Extras::default(); - extras.block_hashes.insert(0, block_0.clone()); - extras.transaction_hashes.insert(0, vec![tx_0.clone()]); - - let tracedb = Tracedb::new(config, temp.as_path(), Arc::new(extras)); - - let request = ImportRequest { + fn create_simple_import_request(block_number: BlockNumber, block_hash: H256) -> ImportRequest { + ImportRequest { traces: BlockTraces::from(vec![Trace { depth: 0, action: Action::Call(Call { @@ -487,23 +474,15 @@ mod tests { result: Res::FailedCall, subs: vec![], }]), - block_hash: block_0.clone(), - block_number: 0, - enacted: vec![block_0.clone()], + block_hash: block_hash.clone(), + block_number: block_number, + enacted: vec![block_hash], retracted: 0, - }; - - tracedb.import(request); - - let filter = Filter { - range: (0..0), - from_address: vec![Address::from(1)], - to_address: vec![], - }; + } + } - let traces = tracedb.filter(&filter); - assert_eq!(traces.len(), 1); - assert_eq!(traces[0], LocalizedTrace { + fn create_simple_localized_trace(block_number: BlockNumber, block_hash: H256, tx_hash: H256) -> LocalizedTrace { + LocalizedTrace { parent: None, children: vec![], depth: 0, @@ -517,9 +496,78 @@ mod tests { result: Res::FailedCall, trace_number: 0, transaction_number: 0, - transaction_hash: tx_0.clone(), - block_number: 0, - block_hash: block_0.clone(), - }); + transaction_hash: tx_hash, + block_number: block_number, + block_hash: block_hash, + } + } + + + #[test] + fn test_import() { + let temp = RandomTempPath::new(); + let mut config = Config::default(); + config.enabled = Some(true); + let block_0 = H256::from(0xa1); + let block_1 = H256::from(0xa2); + let tx_0 = H256::from(0xff); + let tx_1 = H256::from(0xaf); + + let mut extras = Extras::default(); + extras.block_hashes.insert(0, block_0.clone()); + extras.block_hashes.insert(1, block_1.clone()); + extras.transaction_hashes.insert(0, vec![tx_0.clone()]); + extras.transaction_hashes.insert(1, vec![tx_1.clone()]); + + let tracedb = Tracedb::new(config, temp.as_path(), Arc::new(extras)); + + // import block 0 + let request = create_simple_import_request(0, block_0.clone()); + tracedb.import(request); + + let filter = Filter { + range: (0..0), + from_address: vec![Address::from(1)], + to_address: vec![], + }; + + let traces = tracedb.filter(&filter); + assert_eq!(traces.len(), 1); + assert_eq!(traces[0], create_simple_localized_trace(0, block_0.clone(), tx_0.clone())); + + // import block 1 + let request = create_simple_import_request(1, block_1.clone()); + tracedb.import(request); + + let filter = Filter { + range: (0..1), + from_address: vec![Address::from(1)], + to_address: vec![], + }; + + let traces = tracedb.filter(&filter); + assert_eq!(traces.len(), 2); + assert_eq!(traces[0], create_simple_localized_trace(0, block_0.clone(), tx_0.clone())); + assert_eq!(traces[1], create_simple_localized_trace(1, block_1.clone(), tx_1.clone())); + + let traces = tracedb.block_traces(0).unwrap(); + assert_eq!(traces.len(), 1); + assert_eq!(traces[0], create_simple_localized_trace(0, block_0.clone(), tx_0.clone())); + + let traces = tracedb.block_traces(1).unwrap(); + assert_eq!(traces.len(), 1); + assert_eq!(traces[0], create_simple_localized_trace(1, block_1.clone(), tx_1.clone())); + + assert_eq!(None, tracedb.block_traces(2)); + + let traces = tracedb.transaction_traces(0, 0).unwrap(); + assert_eq!(traces.len(), 1); + assert_eq!(traces[0], create_simple_localized_trace(0, block_0.clone(), tx_0.clone())); + + let traces = tracedb.transaction_traces(1, 0).unwrap(); + assert_eq!(traces.len(), 1); + assert_eq!(traces[0], create_simple_localized_trace(1, block_1.clone(), tx_1.clone())); + + assert_eq!(None, tracedb.transaction_traces(1, 1)); } } From 7ee4666bcdaae3c4414c54df4015216678e23233 Mon Sep 17 00:00:00 2001 From: debris Date: Tue, 26 Apr 2016 10:23:10 +0200 Subject: [PATCH 05/24] fixed minor review issues --- ethcore/src/trace/db.rs | 25 +++++++++++++------------ ethcore/src/trace/noop_tracer.rs | 3 +-- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/ethcore/src/trace/db.rs b/ethcore/src/trace/db.rs index 025bc582667..23aae542081 100644 --- a/ethcore/src/trace/db.rs +++ b/ethcore/src/trace/db.rs @@ -124,6 +124,13 @@ impl Tracedb where T: DatabaseExtras { 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) + } + fn matching_block_traces( &self, filter: &Filter, @@ -149,7 +156,7 @@ impl Tracedb where T: DatabaseExtras { tx_number: usize ) -> Vec { let tx_hash = self.extras.transaction_hash(block_number, tx_number) - .expect("Expected to find transaction hash. Database is probably malformed"); + .expect("Expected to find transaction hash. Database is probably malformed"); let flat_traces: Vec = traces.into(); flat_traces.into_iter() @@ -180,8 +187,8 @@ impl TraceDatabase for Tracedb where T: DatabaseExtras { self.config.enabled.expect("Auto tracing hasn't been properly configured.") } - /// Traces of impor request's enacted blocks are expected to be already in database - /// or to be the currenly inserted trace. + /// Traces of import request's enacted blocks are expected to be already in database + /// or to be the currently inserted trace. fn import(&self, request: ImportRequest) { // fast return if tracing is disabled if !self.tracing_enabled() { @@ -229,9 +236,7 @@ impl TraceDatabase for Tracedb where T: DatabaseExtras { fn trace(&self, block_number: BlockNumber, tx_position: usize, trace_position: usize) -> Option { self.extras.block_hash(block_number) - .and_then(|block_hash| self.traces(&block_hash) - .map(FlatBlockTraces::from) - .map(Into::>::into) + .and_then(|block_hash| self.transactions_traces(&block_hash) .and_then(|traces| traces.into_iter().nth(tx_position)) .map(Into::>::into) .and_then(|traces| traces.into_iter().nth(trace_position)) @@ -257,9 +262,7 @@ impl TraceDatabase for Tracedb where T: DatabaseExtras { fn transaction_traces(&self, block_number: BlockNumber, tx_position: usize) -> Option> { self.extras.block_hash(block_number) - .and_then(|block_hash| self.traces(&block_hash) - .map(FlatBlockTraces::from) - .map(Into::>::into) + .and_then(|block_hash| self.transactions_traces(&block_hash) .and_then(|traces| traces.into_iter().nth(tx_position)) .map(Into::>::into) .map(|traces| { @@ -287,9 +290,7 @@ impl TraceDatabase for Tracedb where T: DatabaseExtras { fn block_traces(&self, block_number: BlockNumber) -> Option> { self.extras.block_hash(block_number) - .and_then(|block_hash| self.traces(&block_hash) - .map(FlatBlockTraces::from) - .map(Into::>::into) + .and_then(|block_hash| self.transactions_traces(&block_hash) .map(|traces| { traces.into_iter() .map(Into::>::into) diff --git a/ethcore/src/trace/noop_tracer.rs b/ethcore/src/trace/noop_tracer.rs index c21088b11a8..ab05d2c58b1 100644 --- a/ethcore/src/trace/noop_tracer.rs +++ b/ethcore/src/trace/noop_tracer.rs @@ -37,8 +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, _: bool) { assert!(call.is_none()); assert!(output.is_none()); } From a6bff430983d565050dfc56040f297bbe0347a86 Mon Sep 17 00:00:00 2001 From: debris Date: Tue, 26 Apr 2016 10:58:44 +0200 Subject: [PATCH 06/24] addresses filter --- ethcore/src/trace/db.rs | 11 ++-- ethcore/src/trace/filter.rs | 127 ++++++++++++++++++++++-------------- ethcore/src/trace/mod.rs | 2 +- 3 files changed, 85 insertions(+), 55 deletions(-) diff --git a/ethcore/src/trace/db.rs b/ethcore/src/trace/db.rs index 23aae542081..b7b31851219 100644 --- a/ethcore/src/trace/db.rs +++ b/ethcore/src/trace/db.rs @@ -344,7 +344,8 @@ mod tests { use util::{Address, U256, H256}; use devtools::RandomTempPath; use header::BlockNumber; - use trace::{Config, Tracedb, Database, DatabaseExtras, ImportRequest, BlockTraces, Trace, Filter, LocalizedTrace}; + use trace::{Config, Tracedb, Database, DatabaseExtras, ImportRequest}; + use trace::{BlockTraces, Trace, Filter, LocalizedTrace, AddressesFilter}; use trace::trace::{Call, Action, Res}; struct NoopExtras; @@ -528,8 +529,8 @@ mod tests { let filter = Filter { range: (0..0), - from_address: vec![Address::from(1)], - to_address: vec![], + from_address: AddressesFilter::from(vec![Address::from(1)]), + to_address: AddressesFilter::from(vec![]), }; let traces = tracedb.filter(&filter); @@ -542,8 +543,8 @@ mod tests { let filter = Filter { range: (0..1), - from_address: vec![Address::from(1)], - to_address: vec![], + from_address: AddressesFilter::from(vec![Address::from(1)]), + to_address: AddressesFilter::from(vec![]), }; let traces = tracedb.filter(&filter); diff --git a/ethcore/src/trace/filter.rs b/ethcore/src/trace/filter.rs index 992ab20ce94..c7d855c9046 100644 --- a/ethcore/src/trace/filter.rs +++ b/ethcore/src/trace/filter.rs @@ -22,16 +22,62 @@ use basic_types::LogBloom; use super::flat::FlatTrace; use super::trace::Action; +/// Addresses filter. +/// +/// Used to create bloom possibilities and match filters. +pub struct AddressesFilter(Vec
); + +impl From> for AddressesFilter { + fn from(addresses: Vec
) -> Self { + AddressesFilter(addresses) + } +} + +impl AddressesFilter { + /// Returns true if address matches one of the searched addresses. + pub fn matches(&self, address: &Address) -> bool { + self.matches_all() || self.0.contains(address) + } + + /// Returns true if this address filter matches everything. + pub fn matches_all(&self) -> bool { + self.0.is_empty() + } + + /// Returns blooms of this addresses filter. + pub fn blooms(&self) -> Vec { + match self.0.is_empty() { + true => vec![LogBloom::new()], + false => self.0.iter() + .map(|address| LogBloom::from_bloomed(&address.sha3())) + .collect() + } + } + + /// Returns vector of blooms zipped with blooms of this addresses filter. + pub fn with_blooms(&self, blooms: Vec) -> Vec { + match self.0.is_empty() { + true => blooms, + false => blooms + .into_iter() + .flat_map(|bloom| self.0.iter() + .map(|address| bloom.with_bloomed(&address.sha3())) + .collect::>()) + .collect() + } + } +} + /// Traces filter. pub struct Filter { /// Block range. pub range: Range, /// From address. If empty, match all, if not, match one of the values. - pub from_address: Vec
, + pub from_address: AddressesFilter, /// To address. If empty, match all, if not, match one of the values. - pub to_address: Vec
, + pub to_address: AddressesFilter, } impl BloomFilter for Filter { @@ -50,37 +96,20 @@ impl BloomFilter for Filter { impl Filter { /// Returns combinations of each address. fn bloom_possibilities(&self) -> Vec { - let blooms = match self.from_address.is_empty() { - true => vec![LogBloom::new()], - false => self.from_address - .iter() - .map(|address| LogBloom::from_bloomed(&address.sha3())) - .collect() - }; - - match self.to_address.is_empty() { - true => blooms, - false => blooms - .into_iter() - .flat_map(|bloom| self.to_address - .iter() - .map(| address | bloom.with_bloomed(&address.sha3())) - .collect::>()) - .collect() - } + self.to_address.with_blooms(self.from_address.blooms()) } /// Returns true if given trace matches the filter. pub fn matches(&self, trace: &FlatTrace) -> bool { match trace.action { Action::Call(ref call) => { - let from_matches = self.from_address.is_empty() || self.from_address.contains(&call.from); - let to_matches = self.to_address.is_empty() || self.to_address.contains(&call.to); + 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.is_empty() || self.from_address.contains(&create.from); - let to_matches = self.to_address.is_empty(); + let from_matches = self.from_address.matches(&create.from); + let to_matches = self.to_address.matches_all(); from_matches && to_matches } } @@ -93,15 +122,15 @@ mod tests { use util::sha3::Hashable; use trace::trace::{Action, Call, Res}; use trace::flat::FlatTrace; - use trace::Filter; + use trace::{Filter, AddressesFilter}; use basic_types::LogBloom; #[test] fn empty_trace_filter_bloom_possibilies() { let filter = Filter { range: (0..0), - from_address: vec![], - to_address: vec![], + from_address: AddressesFilter::from(vec![]), + to_address: AddressesFilter::from(vec![]), }; let blooms = filter.bloom_possibilities(); @@ -112,8 +141,8 @@ mod tests { fn single_trace_filter_bloom_possibility() { let filter = Filter { range: (0..0), - from_address: vec![Address::from(1)], - to_address: vec![Address::from(2)], + from_address: AddressesFilter::from(vec![Address::from(1)]), + to_address: AddressesFilter::from(vec![Address::from(2)]), }; let blooms = filter.bloom_possibilities(); @@ -128,8 +157,8 @@ mod tests { fn only_from_trace_filter_bloom_possibility() { let filter = Filter { range: (0..0), - from_address: vec![Address::from(1)], - to_address: vec![], + from_address: AddressesFilter::from(vec![Address::from(1)]), + to_address: AddressesFilter::from(vec![]), }; let blooms = filter.bloom_possibilities(); @@ -143,8 +172,8 @@ mod tests { fn only_to_trace_filter_bloom_possibility() { let filter = Filter { range: (0..0), - from_address: vec![], - to_address: vec![Address::from(1)], + from_address: AddressesFilter::from(vec![]), + to_address: AddressesFilter::from(vec![Address::from(1)]), }; let blooms = filter.bloom_possibilities(); @@ -158,8 +187,8 @@ mod tests { fn multiple_trace_filter_bloom_possibility() { let filter = Filter { range: (0..0), - from_address: vec![Address::from(1), Address::from(3)], - to_address: vec![Address::from(2), Address::from(4)], + from_address: AddressesFilter::from(vec![Address::from(1), Address::from(3)]), + to_address: AddressesFilter::from(vec![Address::from(2), Address::from(4)]), }; let blooms = filter.bloom_possibilities(); @@ -190,44 +219,44 @@ mod tests { fn filter_matches() { let f0 = Filter { range: (0..0), - from_address: vec![Address::from(1)], - to_address: vec![], + from_address: AddressesFilter::from(vec![Address::from(1)]), + to_address: AddressesFilter::from(vec![]), }; let f1 = Filter { range: (0..0), - from_address: vec![Address::from(3), Address::from(1)], - to_address: vec![], + from_address: AddressesFilter::from(vec![Address::from(3), Address::from(1)]), + to_address: AddressesFilter::from(vec![]), }; let f2 = Filter { range: (0..0), - from_address: vec![], - to_address: vec![], + from_address: AddressesFilter::from(vec![]), + to_address: AddressesFilter::from(vec![]), }; let f3 = Filter { range: (0..0), - from_address: vec![], - to_address: vec![Address::from(2)], + from_address: AddressesFilter::from(vec![]), + to_address: AddressesFilter::from(vec![Address::from(2)]), }; let f4 = Filter { range: (0..0), - from_address: vec![], - to_address: vec![Address::from(2), Address::from(3)], + from_address: AddressesFilter::from(vec![]), + to_address: AddressesFilter::from(vec![Address::from(2), Address::from(3)]), }; let f5 = Filter { range: (0..0), - from_address: vec![Address::from(1)], - to_address: vec![Address::from(2), Address::from(3)], + from_address: AddressesFilter::from(vec![Address::from(1)]), + to_address: AddressesFilter::from(vec![Address::from(2), Address::from(3)]), }; let f6 = Filter { range: (0..0), - from_address: vec![Address::from(1)], - to_address: vec![Address::from(4)], + from_address: AddressesFilter::from(vec![Address::from(1)]), + to_address: AddressesFilter::from(vec![Address::from(4)]), }; let trace = FlatTrace { diff --git a/ethcore/src/trace/mod.rs b/ethcore/src/trace/mod.rs index c8a45b88bbb..ac91847c314 100644 --- a/ethcore/src/trace/mod.rs +++ b/ethcore/src/trace/mod.rs @@ -34,7 +34,7 @@ pub use self::db::Tracedb; pub use self::trace::Trace; pub use self::noop_tracer::NoopTracer; pub use self::executive_tracer::ExecutiveTracer; -pub use self::filter::Filter; +pub use self::filter::{Filter, AddressesFilter}; pub use self::import::ImportRequest; pub use self::localized::LocalizedTrace; use util::{Bytes, Address, U256, H256}; From 77bb2f45334ffa209376b77cf16e03df8bac7ca6 Mon Sep 17 00:00:00 2001 From: debris Date: Tue, 26 Apr 2016 11:05:31 +0200 Subject: [PATCH 07/24] fixed typos --- ethcore/src/trace/db.rs | 2 +- ethcore/src/trace/filter.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ethcore/src/trace/db.rs b/ethcore/src/trace/db.rs index b7b31851219..f3c6b2af2e6 100644 --- a/ethcore/src/trace/db.rs +++ b/ethcore/src/trace/db.rs @@ -446,7 +446,7 @@ mod tests { #[test] #[should_panic] - fn test_invalid_reopeining_db() { + fn test_invalid_reopening_db() { let temp = RandomTempPath::new(); let mut config = Config::default(); diff --git a/ethcore/src/trace/filter.rs b/ethcore/src/trace/filter.rs index c7d855c9046..4445c69b38c 100644 --- a/ethcore/src/trace/filter.rs +++ b/ethcore/src/trace/filter.rs @@ -73,10 +73,10 @@ pub struct Filter { /// Block range. pub range: Range, - /// From address. If empty, match all, if not, match one of the values. + /// From address filter. pub from_address: AddressesFilter, - /// To address. If empty, match all, if not, match one of the values. + /// To address filter. pub to_address: AddressesFilter, } From 23418391c21f29114394d6bc25ad2c30610f2736 Mon Sep 17 00:00:00 2001 From: debris Date: Tue, 26 Apr 2016 14:53:21 +0200 Subject: [PATCH 08/24] replace malformed with corrupted --- ethcore/src/trace/db.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/ethcore/src/trace/db.rs b/ethcore/src/trace/db.rs index f3c6b2af2e6..828cfffcbe5 100644 --- a/ethcore/src/trace/db.rs +++ b/ethcore/src/trace/db.rs @@ -85,7 +85,7 @@ impl Tracedb where T: DatabaseExtras { let tracing_was_enabled = match tracesdb.get(b"enabled").unwrap() { Some(ref value) if value as &[u8] == &[0x1] => Some(true), Some(ref value) if value as &[u8] == &[0x0] => Some(false), - Some(_) => { panic!("tracesdb is malformed") }, + Some(_) => { panic!("tracesdb is corrupted") }, None => None, }; @@ -156,7 +156,7 @@ impl Tracedb where T: DatabaseExtras { tx_number: usize ) -> Vec { let tx_hash = self.extras.transaction_hash(block_number, tx_number) - .expect("Expected to find transaction hash. Database is probably malformed"); + .expect("Expected to find transaction hash. Database is probably corrupted"); let flat_traces: Vec = traces.into(); flat_traces.into_iter() @@ -214,7 +214,7 @@ impl TraceDatabase for Tracedb where T: DatabaseExtras { .iter() // all traces are expected to be found here. That's why `expect` has been used // instead of `filter_map`. If some traces haven't been found, it meens that - // traces database is malformed or incomplete. + // traces database is corrupted or incomplete. .map(|block_hash| self.traces(block_hash).expect("Traces database is incomplete.")) .map(|block_traces| block_traces.bloom()) .map(BlockTracesBloom::from) @@ -242,7 +242,7 @@ impl TraceDatabase for Tracedb where T: DatabaseExtras { .and_then(|traces| traces.into_iter().nth(trace_position)) .map(|trace| { let tx_hash = self.extras.transaction_hash(block_number, tx_position) - .expect("Expected to find transaction hash. Database is probably malformed"); + .expect("Expected to find transaction hash. Database is probably corrupted"); LocalizedTrace { parent: trace.parent, @@ -267,7 +267,7 @@ impl TraceDatabase for Tracedb where T: DatabaseExtras { .map(Into::>::into) .map(|traces| { let tx_hash = self.extras.transaction_hash(block_number, tx_position) - .expect("Expected to find transaction hash. Database is probably malformed"); + .expect("Expected to find transaction hash. Database is probably corrupted"); traces.into_iter() .enumerate() @@ -297,7 +297,7 @@ impl TraceDatabase for Tracedb where T: DatabaseExtras { .enumerate() .flat_map(|(tx_position, traces)| { let tx_hash = self.extras.transaction_hash(block_number, tx_position) - .expect("Expected to find transaction hash. Database is probably malformed"); + .expect("Expected to find transaction hash. Database is probably corrupted"); traces.into_iter() .enumerate() @@ -327,9 +327,9 @@ impl TraceDatabase for Tracedb where T: DatabaseExtras { .flat_map(|n| { let number = n as BlockNumber; let hash = self.extras.block_hash(number) - .expect("Expected to find block hash. Extras db is probably malformed"); + .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 malformed."); + .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) }) From 6f034f19b061d9be95e377ae89104f9e2d98ff25 Mon Sep 17 00:00:00 2001 From: debris Date: Tue, 26 Apr 2016 16:22:58 +0200 Subject: [PATCH 09/24] trace switch --- ethcore/src/client/config.rs | 2 +- ethcore/src/trace/config.rs | 41 ++++++++++++++++++++++++++++--- ethcore/src/trace/db.rs | 47 ++++++++++++++---------------------- ethcore/src/trace/mod.rs | 2 +- 4 files changed, 58 insertions(+), 34 deletions(-) diff --git a/ethcore/src/client/config.rs b/ethcore/src/client/config.rs index 9d08c88ff65..df685f0d1c1 100644 --- a/ethcore/src/client/config.rs +++ b/ethcore/src/client/config.rs @@ -16,7 +16,7 @@ pub use block_queue::BlockQueueConfig; pub use blockchain::BlockChainConfig; -pub use trace::Config as TraceConfig; +pub use trace::{Config as TraceConfig, Switch}; use util::journaldb; /// Client configuration. Includes configs for all sub-systems. diff --git a/ethcore/src/trace/config.rs b/ethcore/src/trace/config.rs index 2a03a587939..2d5d589dc67 100644 --- a/ethcore/src/trace/config.rs +++ b/ethcore/src/trace/config.rs @@ -17,12 +17,47 @@ //! Traces config. use bloomchain::Config as BloomConfig; -///. Traces config. +/// 3-value enum. +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum Switch { + /// True. + On, + /// False. + Off, + /// Auto. + Auto, +} + +impl Switch { + /// Tries to turn old switch to new value. + pub fn turn_to(&self, to: Switch) -> Result { + match (*self, to) { + (Switch::On, Switch::On) => Ok(Switch::On), + (Switch::On, Switch::Auto) => Ok(Switch::On), + (Switch::On, Switch::Off) => Ok(Switch::Off), + (Switch::Off, Switch::On) => Err("Tracing can't be enabled"), + (Switch::Off, Switch::Auto) => Ok(Switch::Off), + (Switch::Off, Switch::Off) => Ok(Switch::Off), + (Switch::Auto, Switch::On) => Ok(Switch::On), + _ => Ok(Switch::Off), + } + } + + /// Returns switch boolean switch value. + pub fn as_bool(&self) -> bool { + match *self { + Switch::On => true, + Switch::Off | Switch::Auto => false, + } + } +} + +/// Traces config. #[derive(Debug, Clone)] pub struct Config { /// Indicates if tracing should be enabled or not. /// If it's None, it will be automatically configured. - pub enabled: Option, + pub enabled: Switch, /// Traces blooms configuration. pub blooms: BloomConfig, } @@ -30,7 +65,7 @@ pub struct Config { impl Default for Config { fn default() -> Self { Config { - enabled: None, + enabled: Switch::Auto, blooms: BloomConfig { levels: 3, elements_per_index: 16, diff --git a/ethcore/src/trace/db.rs b/ethcore/src/trace/db.rs index 828cfffcbe5..1c041cfed08 100644 --- a/ethcore/src/trace/db.rs +++ b/ethcore/src/trace/db.rs @@ -22,7 +22,7 @@ use bloomchain::Number; use bloomchain::group::{BloomGroupDatabase, BloomGroupChain, GroupPosition, BloomGroup}; use util::{FixedHash, H256, H264, Database, DBTransaction}; use header::BlockNumber; -use trace::{BlockTraces, LocalizedTrace, Config, Filter, Database as TraceDatabase, ImportRequest, +use trace::{BlockTraces, LocalizedTrace, Config, Switch, Filter, Database as TraceDatabase, ImportRequest, DatabaseExtras}; use db::{Key, Writable, Readable, CacheUpdatePolicy}; use super::bloom::{TraceGroupPosition, BlockTracesBloom, BlockTracesBloomGroup}; @@ -82,28 +82,17 @@ impl Tracedb where T: DatabaseExtras { let tracesdb = Database::open_default(tracedb_path.to_str().unwrap()).unwrap(); // check if in previously tracing was enabled - let tracing_was_enabled = match tracesdb.get(b"enabled").unwrap() { - Some(ref value) if value as &[u8] == &[0x1] => Some(true), - Some(ref value) if value as &[u8] == &[0x0] => Some(false), + let old_tracing = match tracesdb.get(b"enabled").unwrap() { + Some(ref value) if value as &[u8] == &[0x1] => Switch::On, + Some(ref value) if value as &[u8] == &[0x0] => Switch::Off, Some(_) => { panic!("tracesdb is corrupted") }, - None => None, + None => Switch::Auto, }; - // compare it with the current option. - let tracing = match (tracing_was_enabled, config.enabled) { - (Some(true), Some(true)) => true, - (Some(true), None) => true, - (Some(true), Some(false)) => false, - (Some(false), Some(true)) => { panic!("Tracing can't be enabled. Resync required."); }, - (Some(false), None) => false, - (Some(false), Some(false)) => false, - (None, Some(true)) => true, - _ => false, - }; - - config.enabled = Some(tracing); + let tracing = old_tracing.turn_to(config.enabled).expect("Tracing can't be enabled.Resync required."); + config.enabled = tracing; - let encoded_tracing= match tracing { + let encoded_tracing = match tracing.as_bool() { true => [0x1], false => [0x0] }; @@ -184,7 +173,7 @@ impl Tracedb where T: DatabaseExtras { impl TraceDatabase for Tracedb where T: DatabaseExtras { fn tracing_enabled(&self) -> bool { - self.config.enabled.expect("Auto tracing hasn't been properly configured.") + self.config.enabled.as_bool() } /// Traces of import request's enacted blocks are expected to be already in database @@ -344,7 +333,7 @@ mod tests { use util::{Address, U256, H256}; use devtools::RandomTempPath; use header::BlockNumber; - use trace::{Config, Tracedb, Database, DatabaseExtras, ImportRequest}; + use trace::{Config, Switch, Tracedb, Database, DatabaseExtras, ImportRequest}; use trace::{BlockTraces, Trace, Filter, LocalizedTrace, AddressesFilter}; use trace::trace::{Call, Action, Res}; @@ -391,7 +380,7 @@ mod tests { let mut config = Config::default(); // set autotracing - config.enabled = None; + config.enabled = Switch::Auto; { let tracedb = Tracedb::new(config.clone(), temp.as_path(), Arc::new(NoopExtras)); @@ -403,7 +392,7 @@ mod tests { assert_eq!(tracedb.tracing_enabled(), false); } - config.enabled = Some(false); + config.enabled = Switch::Off; { let tracedb = Tracedb::new(config.clone(), temp.as_path(), Arc::new(NoopExtras)); @@ -417,7 +406,7 @@ mod tests { let mut config = Config::default(); // set tracing on - config.enabled = Some(true); + config.enabled = Switch::On; { let tracedb = Tracedb::new(config.clone(), temp.as_path(), Arc::new(NoopExtras)); @@ -429,14 +418,14 @@ mod tests { assert_eq!(tracedb.tracing_enabled(), true); } - config.enabled = None; + config.enabled = Switch::Auto; { let tracedb = Tracedb::new(config.clone(), temp.as_path(), Arc::new(NoopExtras)); assert_eq!(tracedb.tracing_enabled(), true); } - config.enabled = Some(false); + config.enabled = Switch::Off; { let tracedb = Tracedb::new(config.clone(), temp.as_path(), Arc::new(NoopExtras)); @@ -451,14 +440,14 @@ mod tests { let mut config = Config::default(); // set tracing on - config.enabled = Some(false); + config.enabled = Switch::Off; { let tracedb = Tracedb::new(config.clone(), temp.as_path(), Arc::new(NoopExtras)); assert_eq!(tracedb.tracing_enabled(), true); } - config.enabled = Some(true); + config.enabled = Switch::On; Tracedb::new(config.clone(), temp.as_path(), Arc::new(NoopExtras)); // should panic! } @@ -509,7 +498,7 @@ mod tests { fn test_import() { let temp = RandomTempPath::new(); let mut config = Config::default(); - config.enabled = Some(true); + config.enabled = Switch::On; let block_0 = H256::from(0xa1); let block_1 = H256::from(0xa2); let tx_0 = H256::from(0xff); diff --git a/ethcore/src/trace/mod.rs b/ethcore/src/trace/mod.rs index ac91847c314..7fc2e8e6dca 100644 --- a/ethcore/src/trace/mod.rs +++ b/ethcore/src/trace/mod.rs @@ -29,7 +29,7 @@ mod noop_tracer; pub mod trace; pub use self::block::BlockTraces; -pub use self::config::Config; +pub use self::config::{Config, Switch}; pub use self::db::Tracedb; pub use self::trace::Trace; pub use self::noop_tracer::NoopTracer; From 4451110509d215fbc288c787557c1acdfd8a905f Mon Sep 17 00:00:00 2001 From: debris Date: Wed, 27 Apr 2016 11:06:50 +0200 Subject: [PATCH 10/24] db key is generic and can be made smaller --- ethcore/src/blockchain/blockchain.rs | 5 ++-- ethcore/src/db.rs | 45 +++++++++++++++++----------- ethcore/src/extras.rs | 12 ++++++++ ethcore/src/trace/db.rs | 4 +++ 4 files changed, 46 insertions(+), 20 deletions(-) diff --git a/ethcore/src/blockchain/blockchain.rs b/ethcore/src/blockchain/blockchain.rs index f5daf2e7430..7e5f896fdfd 100644 --- a/ethcore/src/blockchain/blockchain.rs +++ b/ethcore/src/blockchain/blockchain.rs @@ -728,9 +728,10 @@ impl BlockChain { self.query_extras(hash, &self.blocks_blooms) } - fn query_extras(&self, hash: &K, cache: &RwLock>) -> Option where + fn query_extras(&self, hash: &K, cache: &RwLock>) -> Option where T: ExtrasIndexable + Clone + Decodable, - K: Key + Eq + Hash + Clone, + K: Key + Eq + Hash + Clone, + R: Deref, H256: From { self.note_used(CacheID::Extras(T::index(), H256::from(hash.clone()))); self.extras_db.read_with_cache(cache, hash) diff --git a/ethcore/src/db.rs b/ethcore/src/db.rs index 3dca6ca191f..5b444ccebfa 100644 --- a/ethcore/src/db.rs +++ b/ethcore/src/db.rs @@ -16,6 +16,7 @@ //! Extras db utils. +use std::ops::Deref; use std::hash::Hash; use std::sync::RwLock; use std::collections::HashMap; @@ -30,19 +31,22 @@ pub enum CacheUpdatePolicy { /// Should be used to get database key associated with given value. pub trait Key { + type Target: Deref; + /// Returns db key. - fn key(&self) -> H264; + fn key(&self) -> Self::Target; } /// Should be used to write value into database. pub trait Writable { /// Writes the value into the database. - fn write(&self, key: &Key, value: &T) where T: Encodable; + fn write(&self, key: &Key, value: &T) where T: Encodable, R: Deref; /// Writes the value into the database and updates the cache. - fn write_with_cache(&self, cache: &mut HashMap, key: K, value: T, policy: CacheUpdatePolicy) where - K: Key + Hash + Eq, - T: Encodable { + fn write_with_cache(&self, cache: &mut HashMap, key: K, value: T, policy: CacheUpdatePolicy) where + K: Key + Hash + Eq, + T: Encodable, + R: Deref { self.write(&key, &value); match policy { CacheUpdatePolicy::Overwrite => { @@ -55,8 +59,10 @@ pub trait Writable { } /// Writes the values into the database and updates the cache. - fn extend_with_cache(&self, cache: &mut HashMap, values: HashMap, policy: CacheUpdatePolicy) - where K: Key + Hash + Eq, T: Encodable { + fn extend_with_cache(&self, cache: &mut HashMap, values: HashMap, policy: CacheUpdatePolicy) where + K: Key + Hash + Eq, + T: Encodable, + R: Deref { match policy { CacheUpdatePolicy::Overwrite => { for (key, value) in values.into_iter() { @@ -77,7 +83,9 @@ pub trait Writable { /// Should be used to read values from database. pub trait Readable { /// Returns value for given key. - fn read(&self, key: &Key) -> Option where T: Decodable; + fn read(&self, key: &Key) -> Option where + T: Decodable, + R: Deref; /// Returns value for given key either in cache or in database. fn read_with_cache(&self, cache: &RwLock>, key: &K) -> Option where @@ -98,11 +106,12 @@ pub trait Readable { } /// Returns true if given value exists. - fn exists(&self, key: &Key) -> bool; + fn exists(&self, key: &Key) -> bool where R: Deref; /// Returns true if given value exists either in cache or in database. - fn exists_with_cache(&self, cache: &RwLock>, key: &K) -> bool where - K: Eq + Hash + Key { + fn exists_with_cache(&self, cache: &RwLock>, key: &K) -> bool where + K: Eq + Hash + Key, + R: Deref { { let read = cache.read().unwrap(); if read.get(key).is_some() { @@ -110,38 +119,38 @@ pub trait Readable { } } - self.exists::(key) + self.exists::(key) } } impl Writable for DBTransaction { - fn write(&self, key: &Key, value: &T) where T: Encodable { + fn write(&self, key: &Key, value: &T) where T: Encodable, R: Deref { let result = self.put(&key.key(), &encode(value)); if let Err(err) = result { - panic!("db put failed, key: {:?}, err: {:?}", key.key(), err); + panic!("db put failed, key: {:?}, err: {:?}", &key.key() as &[u8], err); } } } impl Readable for Database { - fn read(&self, key: &Key) -> Option where T: Decodable { + fn read(&self, key: &Key) -> Option where T: Decodable, R: Deref { let result = self.get(&key.key()); match result { Ok(option) => option.map(|v| decode(&v)), Err(err) => { - panic!("db get failed, key: {:?}, err: {:?}", key.key(), err); + panic!("db get failed, key: {:?}, err: {:?}", &key.key() as &[u8], err); } } } - fn exists(&self, key: &Key) -> bool { + fn exists(&self, key: &Key) -> bool where R: Deref { let result = self.get(&key.key()); match result { Ok(v) => v.is_some(), Err(err) => { - panic!("db get failed, key: {:?}, err: {:?}", key.key(), err); + panic!("db get failed, key: {:?}, err: {:?}", &key.key() as &[u8], err); } } } diff --git a/ethcore/src/extras.rs b/ethcore/src/extras.rs index 49d867dcfc0..6b6bb13b097 100644 --- a/ethcore/src/extras.rs +++ b/ethcore/src/extras.rs @@ -85,36 +85,48 @@ impl ExtrasIndexable for BlockReceipts { } impl Key for BlockNumber { + type Target = H264; + fn key(&self) -> H264 { with_index(&H256::from(*self), ExtrasIndex::BlockHash) } } impl Key for H256 { + type Target = H264; + fn key(&self) -> H264 { with_index(self, ExtrasIndex::BlockDetails) } } impl Key for H256 { + type Target = H264; + fn key(&self) -> H264 { with_index(self, ExtrasIndex::TransactionAddress) } } impl Key for H256 { + type Target = H264; + fn key(&self) -> H264 { with_index(self, ExtrasIndex::BlockLogBlooms) } } impl Key for H256 { + type Target = H264; + fn key(&self) -> H264 { with_index(self, ExtrasIndex::BlocksBlooms) } } impl Key for H256 { + type Target = H264; + fn key(&self) -> H264 { with_index(self, ExtrasIndex::BlockReceipts) } diff --git a/ethcore/src/trace/db.rs b/ethcore/src/trace/db.rs index 1c041cfed08..f1c6110416a 100644 --- a/ethcore/src/trace/db.rs +++ b/ethcore/src/trace/db.rs @@ -43,12 +43,16 @@ fn with_index(hash: &H256, i: TracedbIndex) -> H264 { } impl Key for H256 { + type Target = H264; + fn key(&self) -> H264 { with_index(self, TracedbIndex::BlockTraces) } } impl Key for TraceGroupPosition { + type Target = H264; + fn key(&self) -> H264 { with_index(&self.hash(), TracedbIndex::BlockTracesBloomGroups) } From ec5b4a792f9963b8a0ae5b7355a0a18b503b0981 Mon Sep 17 00:00:00 2001 From: debris Date: Wed, 27 Apr 2016 13:41:20 +0200 Subject: [PATCH 11/24] smaller tracedb keys --- ethcore/src/db.rs | 2 +- ethcore/src/trace/bloom.rs | 23 +++++----------------- ethcore/src/trace/db.rs | 40 +++++++++++++++++++++++++++----------- 3 files changed, 35 insertions(+), 30 deletions(-) diff --git a/ethcore/src/db.rs b/ethcore/src/db.rs index 5b444ccebfa..2c5f7c73ad9 100644 --- a/ethcore/src/db.rs +++ b/ethcore/src/db.rs @@ -20,7 +20,7 @@ use std::ops::Deref; use std::hash::Hash; use std::sync::RwLock; use std::collections::HashMap; -use util::{H264, DBTransaction, Database}; +use util::{DBTransaction, Database}; use util::rlp::{encode, Encodable, decode, Decodable}; #[derive(Clone, Copy)] diff --git a/ethcore/src/trace/bloom.rs b/ethcore/src/trace/bloom.rs index 055003e2fab..6f488c84bbc 100644 --- a/ethcore/src/trace/bloom.rs +++ b/ethcore/src/trace/bloom.rs @@ -1,7 +1,6 @@ use bloomchain::Bloom; use bloomchain::group::{BloomGroup, GroupPosition}; use util::rlp::*; -use util::{H256}; use basic_types::LogBloom; /// Helper structure representing bloom of the trace. @@ -89,31 +88,19 @@ impl Encodable for BlockTracesBloomGroup { } /// Represents BloomGroup position in database. -#[derive(PartialEq, Eq, Hash, Clone)] +#[derive(PartialEq, Eq, Hash, Clone, Debug)] pub struct TraceGroupPosition { /// Bloom level. - pub level: usize, + pub level: u8, /// Group index. - pub index: usize, + pub index: u32, } impl From for TraceGroupPosition { fn from(p: GroupPosition) -> Self { TraceGroupPosition { - level: p.level, - index: p.index, + level: p.level as u8, + index: p.index as u32, } } } - -impl TraceGroupPosition { - /// Returns trace group position represented as a 256 bit hash. - pub fn hash(&self) -> H256 { - use std::ptr; - let mut hash = H256::default(); - unsafe { - ptr::copy(&[self.level, self.index] as *const usize as *const u8, hash.as_mut_ptr(), 16); - } - hash - } -} diff --git a/ethcore/src/trace/db.rs b/ethcore/src/trace/db.rs index f1c6110416a..23e257a56ba 100644 --- a/ethcore/src/trace/db.rs +++ b/ethcore/src/trace/db.rs @@ -15,6 +15,8 @@ // along with Parity. If not, see . //! Fat database. +use std::ptr; +use std::ops::Deref; use std::collections::HashMap; use std::sync::{RwLock, Arc}; use std::path::Path; @@ -36,25 +38,41 @@ pub enum TracedbIndex { BlockTracesBloomGroups = 1, } -fn with_index(hash: &H256, i: TracedbIndex) -> H264 { - let mut slice = H264::from_slice(hash); - slice[32] = i as u8; - slice -} - impl Key for H256 { type Target = H264; fn key(&self) -> H264 { - with_index(self, TracedbIndex::BlockTraces) + let mut result = H264::default(); + result[0] = TracedbIndex::BlockTraces as u8; + unsafe { + ptr::copy(self.as_ptr(), result.as_mut_ptr().offset(1), 32); + } + result } } -impl Key for TraceGroupPosition { - type Target = H264; +/// Helper data structure created cause [u8; 6] does not implement Deref to &[u8]. +pub struct TraceGroupKey([u8; 6]); - fn key(&self) -> H264 { - with_index(&self.hash(), TracedbIndex::BlockTracesBloomGroups) +impl Deref for TraceGroupKey { + type Target = [u8]; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl Key for TraceGroupPosition { + type Target = TraceGroupKey; + + fn key(&self) -> Self::Target { + let mut result = [0u8; 6]; + result[0] = TracedbIndex::BlockTracesBloomGroups as u8; + result[1] = self.level; + unsafe { + ptr::copy(&[self.index] as *const u32 as *const u8, result.as_mut_ptr().offset(2), 4); + } + TraceGroupKey(result) } } From 0b2855d58a82f3b773aec17571ecb52ae9a9a0a1 Mon Sep 17 00:00:00 2001 From: debris Date: Wed, 27 Apr 2016 14:45:44 +0200 Subject: [PATCH 12/24] tracedb version --- ethcore/src/trace/db.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ethcore/src/trace/db.rs b/ethcore/src/trace/db.rs index 23e257a56ba..695affe2f1a 100644 --- a/ethcore/src/trace/db.rs +++ b/ethcore/src/trace/db.rs @@ -30,6 +30,8 @@ use db::{Key, Writable, Readable, CacheUpdatePolicy}; use super::bloom::{TraceGroupPosition, BlockTracesBloom, BlockTracesBloomGroup}; use super::flat::{FlatTrace, FlatBlockTraces, FlatTransactionTraces}; +const TRACE_DB_VER: &'static [u8] = b"1.0"; + #[derive(Debug, Copy, Clone)] pub enum TracedbIndex { /// Block traces index. @@ -120,6 +122,7 @@ impl Tracedb where T: DatabaseExtras { }; tracesdb.put(b"enabled", &encoded_tracing).unwrap(); + tracesdb.put(b"version", TRACE_DB_VER).unwrap(); Tracedb { traces: RwLock::new(HashMap::new()), From 225b3ff96c65ba458b4a80a8248d8dc20455d83b Mon Sep 17 00:00:00 2001 From: debris Date: Wed, 27 Apr 2016 14:59:05 +0200 Subject: [PATCH 13/24] fixed ignored tests --- ethcore/src/state.rs | 38 ++++++++++++++++++++++++++++++-------- 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/ethcore/src/state.rs b/ethcore/src/state.rs index afd59008644..1ec1b6b01ff 100644 --- a/ethcore/src/state.rs +++ b/ethcore/src/state.rs @@ -552,10 +552,6 @@ fn should_trace_basic_call_transaction() { } #[test] -#[ignore] -// call tracing turned on to make -// number of transactions and traces in a block equal -// debris fn should_not_trace_call_transaction_to_builtin() { init_log(); @@ -577,12 +573,24 @@ fn should_not_trace_call_transaction_to_builtin() { let result = state.apply(&info, engine.deref(), &t, true).unwrap(); - assert_eq!(result.trace, None); + assert_eq!(result.trace, Some(Trace { + depth: 0, + action: trace::Action::Call(trace::Call { + from: x!("9cce34f7ab185c7aba1b7c8140d620b4bda941d6"), + to: x!("0000000000000000000000000000000000000001"), + value: x!(0), + gas: x!(79_000), + input: vec![], + }), + result: trace::Res::Call(trace::CallResult { + gas_used: U256::from(3000), + output: vec![] + }), + subs: vec![] + })); } #[test] -#[ignore] -// call tracing turned on, debris fn should_not_trace_subcall_transaction_to_builtin() { init_log(); @@ -618,7 +626,21 @@ fn should_not_trace_subcall_transaction_to_builtin() { gas_used: U256::from(28_061), output: vec![] }), - subs: vec![] + subs: vec![Trace { + depth: 1, + action: trace::Action::Call(trace::Call { + from: x!("000000000000000000000000000000000000000a"), + to: x!("0000000000000000000000000000000000000001"), + value: x!(0), + gas: x!(3040), + input: vec![] + }), + subs: vec![], + result: trace::Res::Call(trace::CallResult { + gas_used: x!(3000), + output: vec![] + }) + }] }); assert_eq!(result.trace, expected_trace); } From 9b745ee1ce9d29332fd2ce82cb39e14ace95b353 Mon Sep 17 00:00:00 2001 From: debris Date: Thu, 28 Apr 2016 15:48:42 +0200 Subject: [PATCH 14/24] rename Tracedb -> TraceDB --- ethcore/src/client/client.rs | 6 +++--- ethcore/src/trace/db.rs | 40 ++++++++++++++++++------------------ ethcore/src/trace/mod.rs | 2 +- 3 files changed, 24 insertions(+), 24 deletions(-) diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index 099f5901789..7984dd8be42 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -42,7 +42,7 @@ use env_info::EnvInfo; use executive::{Executive, Executed, TransactOptions, contract_address}; use receipt::LocalizedReceipt; pub use blockchain::CacheSize as BlockChainCacheSize; -use trace::{Tracedb, Database as TraceDatabase}; +use trace::{TraceDB, Database as TraceDatabase}; /// General block status #[derive(Debug, Eq, PartialEq)] @@ -104,7 +104,7 @@ impl ClientReport { /// Call `import_block()` to import a block asynchronously; `flush_queue()` flushes the queue. pub struct Client where V: Verifier { chain: Arc, - tracedb: Arc>, + tracedb: Arc>, engine: Arc>, state_db: Mutex>, block_queue: BlockQueue, @@ -152,7 +152,7 @@ impl Client where V: Verifier { let path = get_db_path(path, config.pruning, spec.genesis_header().hash()); let gb = spec.genesis_block(); let chain = Arc::new(BlockChain::new(config.blockchain, &gb, &path)); - let tracedb = Arc::new(Tracedb::new(config.tracing, &path, chain.clone())); + let tracedb = Arc::new(TraceDB::new(config.tracing, &path, chain.clone())); let mut state_db = journaldb::new(&append_path(&path, "state"), config.pruning); diff --git a/ethcore/src/trace/db.rs b/ethcore/src/trace/db.rs index 695affe2f1a..75dba4722f6 100644 --- a/ethcore/src/trace/db.rs +++ b/ethcore/src/trace/db.rs @@ -33,7 +33,7 @@ use super::flat::{FlatTrace, FlatBlockTraces, FlatTransactionTraces}; const TRACE_DB_VER: &'static [u8] = b"1.0"; #[derive(Debug, Copy, Clone)] -pub enum TracedbIndex { +enum TraceDBIndex { /// Block traces index. BlockTraces = 0, /// Trace bloom group index. @@ -45,7 +45,7 @@ impl Key for H256 { fn key(&self) -> H264 { let mut result = H264::default(); - result[0] = TracedbIndex::BlockTraces as u8; + result[0] = TraceDBIndex::BlockTraces as u8; unsafe { ptr::copy(self.as_ptr(), result.as_mut_ptr().offset(1), 32); } @@ -69,7 +69,7 @@ impl Key for TraceGroupPosition { fn key(&self) -> Self::Target { let mut result = [0u8; 6]; - result[0] = TracedbIndex::BlockTracesBloomGroups as u8; + result[0] = TraceDBIndex::BlockTracesBloomGroups as u8; result[1] = self.level; unsafe { ptr::copy(&[self.index] as *const u32 as *const u8, result.as_mut_ptr().offset(2), 4); @@ -79,7 +79,7 @@ impl Key for TraceGroupPosition { } /// Fat database. -pub struct Tracedb where T: DatabaseExtras { +pub struct TraceDB where T: DatabaseExtras { // cache traces: RwLock>, blooms: RwLock>, @@ -91,15 +91,15 @@ pub struct Tracedb where T: DatabaseExtras { extras: Arc, } -impl BloomGroupDatabase for Tracedb where T: DatabaseExtras { +impl BloomGroupDatabase for TraceDB where T: DatabaseExtras { fn blooms_at(&self, position: &GroupPosition) -> Option { let position = TraceGroupPosition::from(position.clone()); self.tracesdb.read_with_cache(&self.blooms, &position).map(Into::into) } } -impl Tracedb where T: DatabaseExtras { - /// Creates new instance of `Tracedb`. +impl TraceDB where T: DatabaseExtras { + /// Creates new instance of `TraceDB`. pub fn new(mut config: Config, path: &Path, extras: Arc) -> Self { let mut tracedb_path = path.to_path_buf(); tracedb_path.push("tracedb"); @@ -124,7 +124,7 @@ impl Tracedb where T: DatabaseExtras { tracesdb.put(b"enabled", &encoded_tracing).unwrap(); tracesdb.put(b"version", TRACE_DB_VER).unwrap(); - Tracedb { + TraceDB { traces: RwLock::new(HashMap::new()), blooms: RwLock::new(HashMap::new()), tracesdb: tracesdb, @@ -196,7 +196,7 @@ impl Tracedb where T: DatabaseExtras { } } -impl TraceDatabase for Tracedb where T: DatabaseExtras { +impl TraceDatabase for TraceDB where T: DatabaseExtras { fn tracing_enabled(&self) -> bool { self.config.enabled.as_bool() } @@ -358,7 +358,7 @@ mod tests { use util::{Address, U256, H256}; use devtools::RandomTempPath; use header::BlockNumber; - use trace::{Config, Switch, Tracedb, Database, DatabaseExtras, ImportRequest}; + use trace::{Config, Switch, TraceDB, Database, DatabaseExtras, ImportRequest}; use trace::{BlockTraces, Trace, Filter, LocalizedTrace, AddressesFilter}; use trace::trace::{Call, Action, Res}; @@ -408,19 +408,19 @@ mod tests { config.enabled = Switch::Auto; { - let tracedb = Tracedb::new(config.clone(), temp.as_path(), Arc::new(NoopExtras)); + let tracedb = TraceDB::new(config.clone(), temp.as_path(), Arc::new(NoopExtras)); assert_eq!(tracedb.tracing_enabled(), false); } { - let tracedb = Tracedb::new(config.clone(), temp.as_path(), Arc::new(NoopExtras)); + let tracedb = TraceDB::new(config.clone(), temp.as_path(), Arc::new(NoopExtras)); assert_eq!(tracedb.tracing_enabled(), false); } config.enabled = Switch::Off; { - let tracedb = Tracedb::new(config.clone(), temp.as_path(), Arc::new(NoopExtras)); + let tracedb = TraceDB::new(config.clone(), temp.as_path(), Arc::new(NoopExtras)); assert_eq!(tracedb.tracing_enabled(), false); } } @@ -434,26 +434,26 @@ mod tests { config.enabled = Switch::On; { - let tracedb = Tracedb::new(config.clone(), temp.as_path(), Arc::new(NoopExtras)); + let tracedb = TraceDB::new(config.clone(), temp.as_path(), Arc::new(NoopExtras)); assert_eq!(tracedb.tracing_enabled(), true); } { - let tracedb = Tracedb::new(config.clone(), temp.as_path(), Arc::new(NoopExtras)); + let tracedb = TraceDB::new(config.clone(), temp.as_path(), Arc::new(NoopExtras)); assert_eq!(tracedb.tracing_enabled(), true); } config.enabled = Switch::Auto; { - let tracedb = Tracedb::new(config.clone(), temp.as_path(), Arc::new(NoopExtras)); + let tracedb = TraceDB::new(config.clone(), temp.as_path(), Arc::new(NoopExtras)); assert_eq!(tracedb.tracing_enabled(), true); } config.enabled = Switch::Off; { - let tracedb = Tracedb::new(config.clone(), temp.as_path(), Arc::new(NoopExtras)); + let tracedb = TraceDB::new(config.clone(), temp.as_path(), Arc::new(NoopExtras)); assert_eq!(tracedb.tracing_enabled(), false); } } @@ -468,12 +468,12 @@ mod tests { config.enabled = Switch::Off; { - let tracedb = Tracedb::new(config.clone(), temp.as_path(), Arc::new(NoopExtras)); + let tracedb = TraceDB::new(config.clone(), temp.as_path(), Arc::new(NoopExtras)); assert_eq!(tracedb.tracing_enabled(), true); } config.enabled = Switch::On; - Tracedb::new(config.clone(), temp.as_path(), Arc::new(NoopExtras)); // should panic! + TraceDB::new(config.clone(), temp.as_path(), Arc::new(NoopExtras)); // should panic! } fn create_simple_import_request(block_number: BlockNumber, block_hash: H256) -> ImportRequest { @@ -535,7 +535,7 @@ mod tests { extras.transaction_hashes.insert(0, vec![tx_0.clone()]); extras.transaction_hashes.insert(1, vec![tx_1.clone()]); - let tracedb = Tracedb::new(config, temp.as_path(), Arc::new(extras)); + let tracedb = TraceDB::new(config, temp.as_path(), Arc::new(extras)); // import block 0 let request = create_simple_import_request(0, block_0.clone()); diff --git a/ethcore/src/trace/mod.rs b/ethcore/src/trace/mod.rs index 7fc2e8e6dca..62737b57a9a 100644 --- a/ethcore/src/trace/mod.rs +++ b/ethcore/src/trace/mod.rs @@ -30,7 +30,7 @@ pub mod trace; pub use self::block::BlockTraces; pub use self::config::{Config, Switch}; -pub use self::db::Tracedb; +pub use self::db::TraceDB; pub use self::trace::Trace; pub use self::noop_tracer::NoopTracer; pub use self::executive_tracer::ExecutiveTracer; From c7e62d74087fe68bec8801df1d49a93b8b64e2f5 Mon Sep 17 00:00:00 2001 From: debris Date: Thu, 28 Apr 2016 15:50:32 +0200 Subject: [PATCH 15/24] fixed typos --- ethcore/src/trace/db.rs | 2 +- ethcore/src/trace/filter.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ethcore/src/trace/db.rs b/ethcore/src/trace/db.rs index 75dba4722f6..d98fbe9bf87 100644 --- a/ethcore/src/trace/db.rs +++ b/ethcore/src/trace/db.rs @@ -426,7 +426,7 @@ mod tests { } #[test] - fn test_reopeining_db_with_tracing_on() { + fn test_reopening_db_with_tracing_on() { let temp = RandomTempPath::new(); let mut config = Config::default(); diff --git a/ethcore/src/trace/filter.rs b/ethcore/src/trace/filter.rs index 4445c69b38c..8f700a6c600 100644 --- a/ethcore/src/trace/filter.rs +++ b/ethcore/src/trace/filter.rs @@ -126,7 +126,7 @@ mod tests { use basic_types::LogBloom; #[test] - fn empty_trace_filter_bloom_possibilies() { + fn empty_trace_filter_bloom_possibilities() { let filter = Filter { range: (0..0), from_address: AddressesFilter::from(vec![]), From f2918a3c37a51aa6be2b3eb94d27b492f7f06165 Mon Sep 17 00:00:00 2001 From: debris Date: Thu, 28 Apr 2016 16:00:26 +0200 Subject: [PATCH 16/24] proves --- ethcore/src/trace/executive_tracer.rs | 12 ++++++------ ethcore/src/trace/noop_tracer.rs | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/ethcore/src/trace/executive_tracer.rs b/ethcore/src/trace/executive_tracer.rs index 5117181ce87..a0bff1c72e1 100644 --- a/ethcore/src/trace/executive_tracer.rs +++ b/ethcore/src/trace/executive_tracer.rs @@ -50,10 +50,10 @@ impl Tracer for ExecutiveTracer { let trace = Trace { depth: depth, subs: subs, - action: Action::Call(call.expect("Trace call expected to be Some.")), + 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("Trace call output expected to be Some.") + output: output.expect("self.prepare_trace_output().is_some(): so we must be tracing: qed") }) }; self.traces.push(trace); @@ -63,10 +63,10 @@ impl Tracer for ExecutiveTracer { let trace = Trace { depth: depth, subs: subs, - action: Action::Create(create.expect("Trace create expected to be Some.")), + 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("Trace create code expected to be Some."), + code: code.expect("self.prepare_trace_output.is_some(): so we must be tracing: qed"), address: address }) }; @@ -82,7 +82,7 @@ impl Tracer for ExecutiveTracer { let trace = Trace { depth: depth, subs: subs, - action: Action::Call(call.expect("Trace call expected to be Some.")), + action: Action::Call(call.expect("self.prepare_trace_call().is_some(): so we must be tracing: qed")), result: Res::FailedCall, }; self.traces.push(trace); @@ -92,7 +92,7 @@ impl Tracer for ExecutiveTracer { let trace = Trace { depth: depth, subs: subs, - action: Action::Create(create.expect("Trace create expected to be Some.")), + action: Action::Create(create.expect("self.prepare_trace_create().is_some(): so we must be tracing: qed")), result: Res::FailedCreate, }; self.traces.push(trace); diff --git a/ethcore/src/trace/noop_tracer.rs b/ethcore/src/trace/noop_tracer.rs index ab05d2c58b1..2581e692b91 100644 --- a/ethcore/src/trace/noop_tracer.rs +++ b/ethcore/src/trace/noop_tracer.rs @@ -38,21 +38,21 @@ impl Tracer for NoopTracer { } fn trace_call(&mut self, call: Option, _: U256, output: Option, _: usize, _: Vec, _: bool) { - assert!(call.is_none()); - assert!(output.is_none()); + 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) { - assert!(create.is_none()); - assert!(code.is_none()); + 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, _: bool) { - assert!(call.is_none()); + 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) { - assert!(create.is_none()); + assert!(create.is_none(), "self.prepare_trace_create().is_none(): so we can't be tracing: qed"); } fn subtracer(&self) -> Self { From baf72f5128443d848e2de8365e977204ce75760b Mon Sep 17 00:00:00 2001 From: debris Date: Thu, 28 Apr 2016 17:02:17 +0200 Subject: [PATCH 17/24] trace only top level calls to builtins to avoid DDoS attacks --- ethcore/src/executive.rs | 27 +++++++++++++++------------ ethcore/src/state.rs | 18 ++---------------- 2 files changed, 17 insertions(+), 28 deletions(-) diff --git a/ethcore/src/executive.rs b/ethcore/src/executive.rs index 9862d3d049c..2a8f1801052 100644 --- a/ethcore/src/executive.rs +++ b/ethcore/src/executive.rs @@ -262,20 +262,23 @@ impl<'a> Executive<'a> { self.engine.execute_builtin(¶ms.code_address, data, &mut output); self.state.clear_snapshot(); - let mut trace_output = tracer.prepare_trace_output(); - if let Some(mut out) = trace_output.as_mut() { - *out = output.to_owned(); + // trace only top level calls to builtins to avoid DDoS attacks + if self.depth == 0 { + let mut trace_output = tracer.prepare_trace_output(); + if let Some(mut out) = trace_output.as_mut() { + *out = output.to_owned(); + } + + tracer.trace_call( + trace_info, + cost, + trace_output, + self.depth, + vec![], + delegate_call + ); } - tracer.trace_call( - trace_info, - cost, - trace_output, - self.depth, - vec![], - delegate_call - ); - Ok(params.gas - cost) }, // just drain the whole gas diff --git a/ethcore/src/state.rs b/ethcore/src/state.rs index 1ec1b6b01ff..c44614550da 100644 --- a/ethcore/src/state.rs +++ b/ethcore/src/state.rs @@ -552,7 +552,7 @@ fn should_trace_basic_call_transaction() { } #[test] -fn should_not_trace_call_transaction_to_builtin() { +fn should_trace_call_transaction_to_builtin() { init_log(); let temp = RandomTempPath::new(); @@ -626,21 +626,7 @@ fn should_not_trace_subcall_transaction_to_builtin() { gas_used: U256::from(28_061), output: vec![] }), - subs: vec![Trace { - depth: 1, - action: trace::Action::Call(trace::Call { - from: x!("000000000000000000000000000000000000000a"), - to: x!("0000000000000000000000000000000000000001"), - value: x!(0), - gas: x!(3040), - input: vec![] - }), - subs: vec![], - result: trace::Res::Call(trace::CallResult { - gas_used: x!(3000), - output: vec![] - }) - }] + subs: vec![] }); assert_eq!(result.trace, expected_trace); } From 6b684ed30367b5dfaf5a57822671d65a4108f2ce Mon Sep 17 00:00:00 2001 From: debris Date: Thu, 28 Apr 2016 17:16:43 +0200 Subject: [PATCH 18/24] fixed tracedb config switches --- ethcore/src/trace/config.rs | 19 +++---------------- ethcore/src/trace/db.rs | 20 +++++++++++--------- 2 files changed, 14 insertions(+), 25 deletions(-) diff --git a/ethcore/src/trace/config.rs b/ethcore/src/trace/config.rs index 2d5d589dc67..3390fb2aa09 100644 --- a/ethcore/src/trace/config.rs +++ b/ethcore/src/trace/config.rs @@ -30,24 +30,11 @@ pub enum Switch { impl Switch { /// Tries to turn old switch to new value. - pub fn turn_to(&self, to: Switch) -> Result { + pub fn turn_to(&self, to: Switch) -> Result { match (*self, to) { - (Switch::On, Switch::On) => Ok(Switch::On), - (Switch::On, Switch::Auto) => Ok(Switch::On), - (Switch::On, Switch::Off) => Ok(Switch::Off), + (Switch::On, Switch::On) | (Switch::On, Switch::Auto) | (Switch::Auto, Switch::On) => Ok(true), (Switch::Off, Switch::On) => Err("Tracing can't be enabled"), - (Switch::Off, Switch::Auto) => Ok(Switch::Off), - (Switch::Off, Switch::Off) => Ok(Switch::Off), - (Switch::Auto, Switch::On) => Ok(Switch::On), - _ => Ok(Switch::Off), - } - } - - /// Returns switch boolean switch value. - pub fn as_bool(&self) -> bool { - match *self { - Switch::On => true, - Switch::Off | Switch::Auto => false, + _ => Ok(false), } } } diff --git a/ethcore/src/trace/db.rs b/ethcore/src/trace/db.rs index d98fbe9bf87..efb64e32ceb 100644 --- a/ethcore/src/trace/db.rs +++ b/ethcore/src/trace/db.rs @@ -20,7 +20,7 @@ use std::ops::Deref; use std::collections::HashMap; use std::sync::{RwLock, Arc}; use std::path::Path; -use bloomchain::Number; +use bloomchain::{Number, Config as BloomConfig}; use bloomchain::group::{BloomGroupDatabase, BloomGroupChain, GroupPosition, BloomGroup}; use util::{FixedHash, H256, H264, Database, DBTransaction}; use header::BlockNumber; @@ -86,7 +86,9 @@ pub struct TraceDB where T: DatabaseExtras { // db tracesdb: Database, // config, - config: Config, + bloom_config: BloomConfig, + // tracing enabled + enabled: bool, // extras extras: Arc, } @@ -113,10 +115,9 @@ impl TraceDB where T: DatabaseExtras { None => Switch::Auto, }; - let tracing = old_tracing.turn_to(config.enabled).expect("Tracing can't be enabled.Resync required."); - config.enabled = tracing; + let enabled = old_tracing.turn_to(config.enabled).expect("Tracing can't be enabled. Resync required."); - let encoded_tracing = match tracing.as_bool() { + let encoded_tracing = match enabled { true => [0x1], false => [0x0] }; @@ -128,7 +129,8 @@ impl TraceDB where T: DatabaseExtras { traces: RwLock::new(HashMap::new()), blooms: RwLock::new(HashMap::new()), tracesdb: tracesdb, - config: config, + bloom_config: config.blooms, + enabled: enabled, extras: extras, } } @@ -198,7 +200,7 @@ impl TraceDB where T: DatabaseExtras { impl TraceDatabase for TraceDB where T: DatabaseExtras { fn tracing_enabled(&self) -> bool { - self.config.enabled.as_bool() + self.enabled } /// Traces of import request's enacted blocks are expected to be already in database @@ -235,7 +237,7 @@ impl TraceDatabase for TraceDB where T: DatabaseExtras { .map(Into::into) .collect(); - let chain = BloomGroupChain::new(self.config.blooms, self); + let chain = BloomGroupChain::new(self.bloom_config, self); let trace_blooms = chain.replace(&replaced_range, enacted_blooms); let blooms_to_insert = trace_blooms.into_iter() .map(|p| (From::from(p.0), From::from(p.1))) @@ -335,7 +337,7 @@ impl TraceDatabase for TraceDB where T: DatabaseExtras { } fn filter(&self, filter: &Filter) -> Vec { - let chain = BloomGroupChain::new(self.config.blooms, self); + let chain = BloomGroupChain::new(self.bloom_config, self); let numbers = chain.filter(filter); numbers.into_iter() .flat_map(|n| { From 3ce64ee06c64af25998e373eaff1965bc8a9788c Mon Sep 17 00:00:00 2001 From: debris Date: Thu, 28 Apr 2016 17:27:11 +0200 Subject: [PATCH 19/24] fix comments fat replaced with trace --- ethcore/src/trace/db.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ethcore/src/trace/db.rs b/ethcore/src/trace/db.rs index efb64e32ceb..d46d9c1f06f 100644 --- a/ethcore/src/trace/db.rs +++ b/ethcore/src/trace/db.rs @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -//! Fat database. +//! Trace database. use std::ptr; use std::ops::Deref; use std::collections::HashMap; @@ -78,7 +78,7 @@ impl Key for TraceGroupPosition { } } -/// Fat database. +/// Trace database. pub struct TraceDB where T: DatabaseExtras { // cache traces: RwLock>, From bc8f79b07fe40cb8cb080ccc42deb61d577af270 Mon Sep 17 00:00:00 2001 From: debris Date: Fri, 29 Apr 2016 11:28:25 +0200 Subject: [PATCH 20/24] vector-addressing scheme for localized traces --- ethcore/src/trace/db.rs | 41 ++++++++-------------- ethcore/src/trace/filter.rs | 5 ++- ethcore/src/trace/flat.rs | 63 ++++++++++++++++++---------------- ethcore/src/trace/localized.rs | 14 ++++---- 4 files changed, 56 insertions(+), 67 deletions(-) diff --git a/ethcore/src/trace/db.rs b/ethcore/src/trace/db.rs index d46d9c1f06f..209a4d7bf4c 100644 --- a/ethcore/src/trace/db.rs +++ b/ethcore/src/trace/db.rs @@ -102,7 +102,7 @@ impl BloomGroupDatabase for TraceDB where T: DatabaseExtras { impl TraceDB where T: DatabaseExtras { /// Creates new instance of `TraceDB`. - pub fn new(mut config: Config, path: &Path, extras: Arc) -> Self { + pub fn new(config: Config, path: &Path, extras: Arc) -> Self { let mut tracedb_path = path.to_path_buf(); tracedb_path.push("tracedb"); let tracesdb = Database::open_default(tracedb_path.to_str().unwrap()).unwrap(); @@ -176,16 +176,13 @@ impl TraceDB where T: DatabaseExtras { let flat_traces: Vec = traces.into(); flat_traces.into_iter() - .enumerate() - .filter_map(|(index, trace)| { + .filter_map(|trace| { match filter.matches(&trace) { true => Some(LocalizedTrace { - parent: trace.parent, - children: trace.children, - depth: trace.depth, action: trace.action, result: trace.result, - trace_number: index, + subtraces: trace.subtraces, + trace_address: trace.trace_address, transaction_number: tx_number, transaction_hash: tx_hash.clone(), block_number: block_number, @@ -261,12 +258,10 @@ impl TraceDatabase for TraceDB where T: DatabaseExtras { .expect("Expected to find transaction hash. Database is probably corrupted"); LocalizedTrace { - parent: trace.parent, - children: trace.children, - depth: trace.depth, action: trace.action, result: trace.result, - trace_number: trace_position, + subtraces: trace.subtraces, + trace_address: trace.trace_address, transaction_number: tx_position, transaction_hash: tx_hash, block_number: block_number, @@ -286,14 +281,11 @@ impl TraceDatabase for TraceDB where T: DatabaseExtras { .expect("Expected to find transaction hash. Database is probably corrupted"); traces.into_iter() - .enumerate() - .map(|(i, trace)| LocalizedTrace { - parent: trace.parent, - children: trace.children, - depth: trace.depth, + .map(|trace| LocalizedTrace { action: trace.action, result: trace.result, - trace_number: i, + subtraces: trace.subtraces, + trace_address: trace.trace_address, transaction_number: tx_position, transaction_hash: tx_hash.clone(), block_number: block_number, @@ -316,14 +308,11 @@ impl TraceDatabase for TraceDB where T: DatabaseExtras { .expect("Expected to find transaction hash. Database is probably corrupted"); traces.into_iter() - .enumerate() - .map(|(i, trace)| LocalizedTrace { - parent: trace.parent, - children: trace.children, - depth: trace.depth, + .map(|trace| LocalizedTrace { action: trace.action, result: trace.result, - trace_number: i, + subtraces: trace.subtraces, + trace_address: trace.trace_address, transaction_number: tx_position, transaction_hash: tx_hash.clone(), block_number: block_number, @@ -501,9 +490,6 @@ mod tests { fn create_simple_localized_trace(block_number: BlockNumber, block_hash: H256, tx_hash: H256) -> LocalizedTrace { LocalizedTrace { - parent: None, - children: vec![], - depth: 0, action: Action::Call(Call { from: Address::from(1), to: Address::from(2), @@ -512,7 +498,8 @@ mod tests { input: vec![], }), result: Res::FailedCall, - trace_number: 0, + trace_address: vec![0], + subtraces: 0, transaction_number: 0, transaction_hash: tx_hash, block_number: block_number, diff --git a/ethcore/src/trace/filter.rs b/ethcore/src/trace/filter.rs index 8f700a6c600..d1f699c5248 100644 --- a/ethcore/src/trace/filter.rs +++ b/ethcore/src/trace/filter.rs @@ -260,9 +260,6 @@ mod tests { }; let trace = FlatTrace { - parent: None, - children: vec![], - depth: 0, action: Action::Call(Call { from: Address::from(1), to: Address::from(2), @@ -271,6 +268,8 @@ mod tests { input: vec![0x5], }), result: Res::FailedCall, + trace_address: vec![0], + subtraces: 0, }; assert!(f0.matches(&trace)); diff --git a/ethcore/src/trace/flat.rs b/ethcore/src/trace/flat.rs index 60856c8737b..b0fdec543ae 100644 --- a/ethcore/src/trace/flat.rs +++ b/ethcore/src/trace/flat.rs @@ -21,16 +21,16 @@ use super::trace::{Trace, Action, Res}; /// /// Parent and children indexes refer to positions in this vector. pub struct FlatTrace { - /// Index of the parent trace within the same transaction. - pub parent: Option, - /// Indexes of child traces within the same transaction. - pub children: Vec, - /// VM depth. - pub depth: usize, /// Type of action performed by a transaction. pub action: Action, /// Result of this action. pub result: Res, + /// Number of subtraces. + pub subtraces: usize, + /// Exact location of trace. + /// + /// [index in root, index in first CALL, index in second CALL, ...] + pub trace_address: Vec, } /// Represents all traces produced by a single transaction. @@ -49,7 +49,7 @@ 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(None, 0, trace)) + .map(|trace| FlatBlockTraces::flatten(vec![], 0, trace)) .map(FlatTransactionTraces) .collect(); FlatBlockTraces(ordered) @@ -64,25 +64,20 @@ impl Into> for FlatBlockTraces { impl FlatBlockTraces { /// Helper function flattening nested tree structure to vector of ordered traces. - fn flatten(parent_index: Option, len: usize, trace: Trace) -> Vec { - let mut children = vec![]; - let mut next_index = len + 1; + fn flatten(mut parent_indexes: Vec, current: usize, trace: Trace) -> Vec { + parent_indexes.push(current); + let subtraces = trace.subs.len(); let all_subs = trace.subs .into_iter() - .flat_map(|subtrace| { - let subs = FlatBlockTraces::flatten(Some(len), next_index, subtrace); - children.push(next_index); - next_index = next_index + subs.len(); - subs - }) + .enumerate() + .flat_map(|(index, subtrace)| FlatBlockTraces::flatten(parent_indexes.clone(), index, subtrace)) .collect::>(); let ordered = FlatTrace { - parent: parent_index, - children: children, - depth: trace.depth, action: trace.action, result: trace.result, + subtraces: subtraces, + trace_address: parent_indexes, }; let mut result = vec![ordered]; @@ -169,15 +164,25 @@ mod tests { 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].parent, None); - assert_eq!(ordered_traces[0].children, vec![1, 4]); - assert_eq!(ordered_traces[1].parent, Some(0)); - assert_eq!(ordered_traces[1].children, vec![2, 3]); - assert_eq!(ordered_traces[2].parent, Some(1)); - assert_eq!(ordered_traces[2].children, vec![]); - assert_eq!(ordered_traces[3].parent, Some(1)); - assert_eq!(ordered_traces[3].children, vec![]); - assert_eq!(ordered_traces[4].parent, Some(0)); - assert_eq!(ordered_traces[4].children, vec![]); + //assert_eq!(ordered_traces[0].parent, None); + //assert_eq!(ordered_traces[0].children, vec![1, 4]); + assert_eq!(ordered_traces[0].trace_address, vec![0]); + assert_eq!(ordered_traces[0].subtraces, 2); + //assert_eq!(ordered_traces[1].parent, Some(0)); + //assert_eq!(ordered_traces[1].children, vec![2, 3]); + assert_eq!(ordered_traces[1].trace_address, vec![0, 0]); + assert_eq!(ordered_traces[1].subtraces, 2); + //assert_eq!(ordered_traces[2].parent, Some(1)); + //assert_eq!(ordered_traces[2].children, vec![]); + assert_eq!(ordered_traces[2].trace_address, vec![0, 0, 0]); + assert_eq!(ordered_traces[2].subtraces, 0); + //assert_eq!(ordered_traces[3].parent, Some(1)); + //assert_eq!(ordered_traces[3].children, vec![]); + assert_eq!(ordered_traces[3].trace_address, vec![0, 0, 1]); + assert_eq!(ordered_traces[3].subtraces, 0); + //assert_eq!(ordered_traces[4].parent, Some(0)); + //assert_eq!(ordered_traces[4].children, vec![]); + assert_eq!(ordered_traces[4].trace_address, vec![0, 1]); + assert_eq!(ordered_traces[4].subtraces, 0); } } diff --git a/ethcore/src/trace/localized.rs b/ethcore/src/trace/localized.rs index 0a9d42f8106..ef18d689858 100644 --- a/ethcore/src/trace/localized.rs +++ b/ethcore/src/trace/localized.rs @@ -21,18 +21,16 @@ use header::BlockNumber; /// Localized trace. #[derive(Debug, PartialEq)] pub struct LocalizedTrace { - /// Index of the parent trace within the same transaction. - pub parent: Option, - /// Indexes of child traces within the same transaction. - pub children: Vec, - /// VM depth. - pub depth: usize, /// Type of action performed by a transaction. pub action: Action, /// Result of this action. pub result: Res, - /// Trace number within the transaction. - pub trace_number: usize, + /// Number of subtraces. + pub subtraces: usize, + /// Exact location of trace. + /// + /// [index in root, index in first CALL, index in second CALL, ...] + pub trace_address: Vec, /// Transaction number within the block. pub transaction_number: usize, /// Signed transaction hash. From 2262dc9e5cbf8d71a57c7fcf0e6bad9008d12a93 Mon Sep 17 00:00:00 2001 From: debris Date: Fri, 29 Apr 2016 11:29:25 +0200 Subject: [PATCH 21/24] removed comments --- ethcore/src/trace/flat.rs | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/ethcore/src/trace/flat.rs b/ethcore/src/trace/flat.rs index b0fdec543ae..9ba7a809ddf 100644 --- a/ethcore/src/trace/flat.rs +++ b/ethcore/src/trace/flat.rs @@ -164,24 +164,14 @@ mod tests { 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].parent, None); - //assert_eq!(ordered_traces[0].children, vec![1, 4]); assert_eq!(ordered_traces[0].trace_address, vec![0]); assert_eq!(ordered_traces[0].subtraces, 2); - //assert_eq!(ordered_traces[1].parent, Some(0)); - //assert_eq!(ordered_traces[1].children, vec![2, 3]); assert_eq!(ordered_traces[1].trace_address, vec![0, 0]); assert_eq!(ordered_traces[1].subtraces, 2); - //assert_eq!(ordered_traces[2].parent, Some(1)); - //assert_eq!(ordered_traces[2].children, vec![]); assert_eq!(ordered_traces[2].trace_address, vec![0, 0, 0]); assert_eq!(ordered_traces[2].subtraces, 0); - //assert_eq!(ordered_traces[3].parent, Some(1)); - //assert_eq!(ordered_traces[3].children, vec![]); assert_eq!(ordered_traces[3].trace_address, vec![0, 0, 1]); assert_eq!(ordered_traces[3].subtraces, 0); - //assert_eq!(ordered_traces[4].parent, Some(0)); - //assert_eq!(ordered_traces[4].children, vec![]); assert_eq!(ordered_traces[4].trace_address, vec![0, 1]); assert_eq!(ordered_traces[4].subtraces, 0); } From 59f6357493ab63883cc08ee03524d33a0f8fa644 Mon Sep 17 00:00:00 2001 From: debris Date: Sat, 30 Apr 2016 12:48:14 +0200 Subject: [PATCH 22/24] removed first, redundant 0 from trace address --- ethcore/src/trace/db.rs | 2 +- ethcore/src/trace/flat.rs | 23 +++++++++++++---------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/ethcore/src/trace/db.rs b/ethcore/src/trace/db.rs index 209a4d7bf4c..d52e9852b16 100644 --- a/ethcore/src/trace/db.rs +++ b/ethcore/src/trace/db.rs @@ -498,7 +498,7 @@ mod tests { input: vec![], }), result: Res::FailedCall, - trace_address: vec![0], + trace_address: vec![], subtraces: 0, transaction_number: 0, transaction_hash: tx_hash, diff --git a/ethcore/src/trace/flat.rs b/ethcore/src/trace/flat.rs index 9ba7a809ddf..ae3f220505d 100644 --- a/ethcore/src/trace/flat.rs +++ b/ethcore/src/trace/flat.rs @@ -49,7 +49,7 @@ 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![], 0, trace)) + .map(|trace| FlatBlockTraces::flatten(vec![], trace)) .map(FlatTransactionTraces) .collect(); FlatBlockTraces(ordered) @@ -64,20 +64,23 @@ impl Into> for FlatBlockTraces { impl FlatBlockTraces { /// Helper function flattening nested tree structure to vector of ordered traces. - fn flatten(mut parent_indexes: Vec, current: usize, trace: Trace) -> Vec { - parent_indexes.push(current); + fn flatten(address: Vec, trace: Trace) -> Vec { let subtraces = trace.subs.len(); let all_subs = trace.subs .into_iter() .enumerate() - .flat_map(|(index, subtrace)| FlatBlockTraces::flatten(parent_indexes.clone(), index, subtrace)) + .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: parent_indexes, + trace_address: address, }; let mut result = vec![ordered]; @@ -164,15 +167,15 @@ mod tests { 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![0]); + assert_eq!(ordered_traces[0].trace_address, vec![]); assert_eq!(ordered_traces[0].subtraces, 2); - assert_eq!(ordered_traces[1].trace_address, vec![0, 0]); + 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, 0]); + 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, 0, 1]); + 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![0, 1]); + assert_eq!(ordered_traces[4].trace_address, vec![1]); assert_eq!(ordered_traces[4].subtraces, 0); } } From e99de80d5e826152d578fe80da59b3c4b14cd895 Mon Sep 17 00:00:00 2001 From: debris Date: Sat, 30 Apr 2016 13:04:08 +0200 Subject: [PATCH 23/24] updated db.trace method --- ethcore/src/trace/db.rs | 5 +++-- ethcore/src/trace/mod.rs | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/ethcore/src/trace/db.rs b/ethcore/src/trace/db.rs index d52e9852b16..0e020029220 100644 --- a/ethcore/src/trace/db.rs +++ b/ethcore/src/trace/db.rs @@ -247,12 +247,13 @@ impl TraceDatabase for TraceDB where T: DatabaseExtras { self.tracesdb.write(batch).unwrap(); } - fn trace(&self, block_number: BlockNumber, tx_position: usize, trace_position: usize) -> Option { + fn trace(&self, block_number: BlockNumber, tx_position: usize, trace_position: Vec) -> Option { 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) - .and_then(|traces| traces.into_iter().nth(trace_position)) + // this may and should be optimized + .and_then(|traces| traces.into_iter().find(|trace| trace.trace_address == trace_position)) .map(|trace| { let tx_hash = self.extras.transaction_hash(block_number, tx_position) .expect("Expected to find transaction hash. Database is probably corrupted"); diff --git a/ethcore/src/trace/mod.rs b/ethcore/src/trace/mod.rs index 62737b57a9a..4456893104b 100644 --- a/ethcore/src/trace/mod.rs +++ b/ethcore/src/trace/mod.rs @@ -107,7 +107,7 @@ pub trait Database { fn import(&self, request: ImportRequest); /// Returns localized trace at given position. - fn trace(&self, block_number: BlockNumber, tx_position: usize, trace_position: usize) -> Option; + fn trace(&self, block_number: BlockNumber, tx_position: usize, trace_position: Vec) -> Option; /// Returns localized traces created by a single transaction. fn transaction_traces(&self, block_number: BlockNumber, tx_position: usize) -> Option>; From 55c839faba9be40fdbec7cd892caad5cadcdd8ab Mon Sep 17 00:00:00 2001 From: debris Date: Sat, 30 Apr 2016 13:14:59 +0200 Subject: [PATCH 24/24] additional tests for tracedb.trace() --- ethcore/src/trace/db.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ethcore/src/trace/db.rs b/ethcore/src/trace/db.rs index 0e020029220..0d24688593e 100644 --- a/ethcore/src/trace/db.rs +++ b/ethcore/src/trace/db.rs @@ -575,5 +575,8 @@ mod tests { assert_eq!(traces[0], create_simple_localized_trace(1, block_1.clone(), tx_1.clone())); assert_eq!(None, tracedb.transaction_traces(1, 1)); + + assert_eq!(tracedb.trace(0, 0, vec![]).unwrap(), create_simple_localized_trace(0, block_0.clone(), tx_0.clone())); + assert_eq!(tracedb.trace(1, 0, vec![]).unwrap(), create_simple_localized_trace(1, block_1.clone(), tx_1.clone())); } }