From d7eb92420574dd7042033b2b89fafe5f57c5159e Mon Sep 17 00:00:00 2001 From: Marek Kotewicz Date: Sun, 24 Jul 2016 00:20:21 +0200 Subject: [PATCH] Stackoverflow #1686 (#1698) * flat trace serialization * tracing finds transaction which creates contract * flatten traces before inserting them to the db --- ethcore/src/trace/db.rs | 15 ++-- ethcore/src/trace/flat.rs | 99 +++++++++++++++++++++++++ ethcore/src/types/trace_types/filter.rs | 2 +- 3 files changed, 106 insertions(+), 10 deletions(-) diff --git a/ethcore/src/trace/db.rs b/ethcore/src/trace/db.rs index 5e5ddefde21..eb8eb694f02 100644 --- a/ethcore/src/trace/db.rs +++ b/ethcore/src/trace/db.rs @@ -40,7 +40,7 @@ enum TraceDBIndex { BloomGroups = 1, } -impl Key for H256 { +impl Key for H256 { type Target = H264; fn key(&self) -> H264 { @@ -91,7 +91,7 @@ impl Key for TraceGroupPosition { /// Trace database. pub struct TraceDB where T: DatabaseExtras { // cache - traces: RwLock>, + traces: RwLock>, blooms: RwLock>, // db tracesdb: Database, @@ -153,15 +153,13 @@ impl TraceDB where T: DatabaseExtras { } /// Returns traces for block with hash. - fn traces(&self, block_hash: &H256) -> Option { + fn traces(&self, block_hash: &H256) -> Option { self.tracesdb.read_with_cache(&self.traces, block_hash) } /// Returns vector of transaction traces for given block. fn transactions_traces(&self, block_hash: &H256) -> Option> { - self.traces(block_hash) - .map(FlatBlockTraces::from) - .map(Into::into) + self.traces(block_hash).map(Into::into) } fn matching_block_traces( @@ -232,7 +230,7 @@ impl TraceDatabase for TraceDB where T: DatabaseExtras { let mut traces = self.traces.write().unwrap(); // it's important to use overwrite here, // cause this value might be queried by hash later - batch.write_with_cache(traces.deref_mut(), request.block_hash, request.traces, CacheUpdatePolicy::Overwrite); + batch.write_with_cache(traces.deref_mut(), request.block_hash, request.traces.into(), CacheUpdatePolicy::Overwrite); } // now let's rebuild the blooms @@ -353,8 +351,7 @@ impl TraceDatabase for TraceDB where T: DatabaseExtras { .expect("Expected to find block hash. Extras db is probably corrupted"); let traces = self.traces(&hash) .expect("Expected to find a trace. Db is probably corrupted."); - let flat_block = FlatBlockTraces::from(traces); - self.matching_block_traces(filter, flat_block, hash, number) + self.matching_block_traces(filter, traces, hash, number) }) .collect() } diff --git a/ethcore/src/trace/flat.rs b/ethcore/src/trace/flat.rs index f9d3b083178..f39af4423d8 100644 --- a/ethcore/src/trace/flat.rs +++ b/ethcore/src/trace/flat.rs @@ -16,12 +16,15 @@ //! Flat trace module +use util::rlp::*; use trace::BlockTraces; +use basic_types::LogBloom; use super::trace::{Trace, Action, Res}; /// Trace localized in vector of traces produced by a single transaction. /// /// Parent and children indexes refer to positions in this vector. +#[derive(Debug, PartialEq, Clone)] pub struct FlatTrace { /// Type of action performed by a transaction. pub action: Action, @@ -35,9 +38,59 @@ pub struct FlatTrace { pub trace_address: Vec, } +impl FlatTrace { + /// Returns bloom of the trace. + pub fn bloom(&self) -> LogBloom { + self.action.bloom() | self.result.bloom() + } +} + +impl Encodable for FlatTrace { + fn rlp_append(&self, s: &mut RlpStream) { + s.begin_list(4); + s.append(&self.action); + s.append(&self.result); + s.append(&self.subtraces); + s.append(&self.trace_address); + } +} + +impl Decodable for FlatTrace { + fn decode(decoder: &D) -> Result where D: Decoder { + let d = decoder.as_rlp(); + let res = FlatTrace { + action: try!(d.val_at(0)), + result: try!(d.val_at(1)), + subtraces: try!(d.val_at(2)), + trace_address: try!(d.val_at(3)), + }; + + Ok(res) + } +} + /// Represents all traces produced by a single transaction. +#[derive(Debug, PartialEq, Clone)] pub struct FlatTransactionTraces(Vec); +impl FlatTransactionTraces { + pub fn bloom(&self) -> LogBloom { + self.0.iter().fold(Default::default(), | bloom, trace | bloom | trace.bloom()) + } +} + +impl Encodable for FlatTransactionTraces { + fn rlp_append(&self, s: &mut RlpStream) { + s.append(&self.0); + } +} + +impl Decodable for FlatTransactionTraces { + fn decode(decoder: &D) -> Result where D: Decoder { + Ok(FlatTransactionTraces(try!(Decodable::decode(decoder)))) + } +} + impl Into> for FlatTransactionTraces { fn into(self) -> Vec { self.0 @@ -45,8 +98,27 @@ impl Into> for FlatTransactionTraces { } /// Represents all traces produced by transactions in a single block. +#[derive(Debug, PartialEq, Clone)] pub struct FlatBlockTraces(Vec); +impl FlatBlockTraces { + pub fn bloom(&self) -> LogBloom { + self.0.iter().fold(Default::default(), | bloom, tx_traces | bloom | tx_traces.bloom()) + } +} + +impl Encodable for FlatBlockTraces { + fn rlp_append(&self, s: &mut RlpStream) { + s.append(&self.0); + } +} + +impl Decodable for FlatBlockTraces { + fn decode(decoder: &D) -> Result where D: Decoder { + Ok(FlatBlockTraces(try!(Decodable::decode(decoder)))) + } +} + impl From for FlatBlockTraces { fn from(block_traces: BlockTraces) -> Self { let traces: Vec = block_traces.into(); @@ -180,4 +252,31 @@ mod tests { assert_eq!(ordered_traces[4].trace_address, vec![1]); assert_eq!(ordered_traces[4].subtraces, 0); } + + #[test] + fn test_trace_serialization() { + use util::rlp; + + let flat_trace = FlatTrace { + action: Action::Call(Call { + from: 1.into(), + to: 2.into(), + value: 3.into(), + gas: 4.into(), + input: vec![0x5] + }), + result: Res::Call(CallResult { + gas_used: 10.into(), + output: vec![0x11, 0x12] + }), + trace_address: Vec::new(), + subtraces: 0, + }; + + let block_traces = FlatBlockTraces(vec![FlatTransactionTraces(vec![flat_trace])]); + + let encoded = rlp::encode(&block_traces); + let decoded = rlp::decode(&encoded); + assert_eq!(block_traces, decoded); + } } diff --git a/ethcore/src/types/trace_types/filter.rs b/ethcore/src/types/trace_types/filter.rs index 91d1c421df0..c01847e4335 100644 --- a/ethcore/src/types/trace_types/filter.rs +++ b/ethcore/src/types/trace_types/filter.rs @@ -120,7 +120,7 @@ impl Filter { let from_matches = self.from_address.matches(&create.from); let to_matches = self.to_address.matches_all(); from_matches && to_matches - } + }, Action::Suicide(ref suicide) => { let from_matches = self.from_address.matches(&suicide.address); let to_matches = self.to_address.matches(&suicide.refund_address);