Stackoverflow fix (#1742)

* executive tracer builds flat traces without intermediate struct

* temporarilt commented out tests for traces

* fixed new way of building trace address

* fixed new way of building trace address

* updating state tests with flat tracing in progress

* fixed flat tracing tests

* fixed compiling ethcore-rpc with new flat traces

* removed warnings from ethcore module

* remove unused data structures
This commit is contained in:
Marek Kotewicz 2016-07-28 20:31:29 +02:00 committed by Gav Wood
parent f33cd60dc2
commit 9746b944f1
19 changed files with 475 additions and 666 deletions

View File

@ -20,7 +20,7 @@ use common::*;
use engine::*;
use state::*;
use verification::PreverifiedBlock;
use trace::Trace;
use trace::FlatTrace;
use evm::Factory as EvmFactory;
/// A block, encoded as it is on the block chain.
@ -76,7 +76,7 @@ pub struct ExecutedBlock {
receipts: Vec<Receipt>,
transactions_set: HashSet<H256>,
state: State,
traces: Option<Vec<Trace>>,
traces: Option<Vec<Vec<FlatTrace>>>,
}
/// A set of references to `ExecutedBlock` fields that are publicly accessible.
@ -92,7 +92,7 @@ pub struct BlockRefMut<'a> {
/// State.
pub state: &'a mut State,
/// Traces.
pub traces: &'a Option<Vec<Trace>>,
pub traces: &'a Option<Vec<Vec<FlatTrace>>>,
}
/// A set of immutable references to `ExecutedBlock` fields that are publicly accessible.
@ -108,7 +108,7 @@ pub struct BlockRef<'a> {
/// State.
pub state: &'a State,
/// Traces.
pub traces: &'a Option<Vec<Trace>>,
pub traces: &'a Option<Vec<Vec<FlatTrace>>>,
}
impl ExecutedBlock {
@ -169,7 +169,7 @@ pub trait IsBlock {
fn receipts(&self) -> &[Receipt] { &self.block().receipts }
/// Get all information concerning transaction tracing in this block.
fn traces(&self) -> &Option<Vec<Trace>> { &self.block().traces }
fn traces(&self) -> &Option<Vec<Vec<FlatTrace>>> { &self.block().traces }
/// Get all uncles in this block.
fn uncles(&self) -> &[Header] { &self.block().base.uncles }
@ -337,7 +337,7 @@ impl<'x> OpenBlock<'x> {
self.block.transactions_set.insert(h.unwrap_or_else(||t.hash()));
self.block.base.transactions.push(t);
let t = outcome.trace;
self.block.traces.as_mut().map(|traces| traces.push(t.expect("self.block.traces.is_some(): so we must be tracing: qed")));
self.block.traces.as_mut().map(|traces| traces.push(t));
self.block.receipts.push(outcome.receipt);
Ok(self.block.receipts.last().unwrap())
}

View File

@ -61,6 +61,7 @@ use executive::{Executive, Executed, TransactOptions, contract_address};
use receipt::LocalizedReceipt;
use trace::{TraceDB, ImportRequest as TraceImportRequest, LocalizedTrace, Database as TraceDatabase};
use trace;
use trace::FlatTransactionTraces;
use evm::Factory as EvmFactory;
use miner::{Miner, MinerService};
use util::TrieFactory;
@ -430,7 +431,12 @@ impl Client {
// Commit results
let receipts = block.receipts().to_owned();
let traces = From::from(block.traces().clone().unwrap_or_else(Vec::new));
let traces = block.traces().clone().unwrap_or_else(Vec::new);
let traces: Vec<FlatTransactionTraces> = traces.into_iter()
.map(Into::into)
.collect();
//let traces = From::from(block.traces().clone().unwrap_or_else(Vec::new));
// CHECK! I *think* this is fine, even if the state_root is equal to another
// already-imported block of the same number.
@ -441,7 +447,7 @@ impl Client {
// (when something is in chain but you are not able to fetch details)
let route = self.chain.insert_block(block_data, receipts);
self.tracedb.import(TraceImportRequest {
traces: traces,
traces: traces.into(),
block_hash: hash.clone(),
block_number: number,
enacted: route.enacted.clone(),

View File

@ -22,7 +22,7 @@ use types::executed::CallType;
use evm::{self, Ext, Factory, Finalize};
use externalities::*;
use substate::*;
use trace::{Trace, Tracer, NoopTracer, ExecutiveTracer, VMTrace, VMTracer, ExecutiveVMTracer, NoopVMTracer};
use trace::{FlatTrace, Tracer, NoopTracer, ExecutiveTracer, VMTrace, VMTracer, ExecutiveVMTracer, NoopVMTracer};
use crossbeam;
pub use types::executed::{Executed, ExecutionResult};
@ -199,7 +199,7 @@ impl<'a> Executive<'a> {
};
// finalize here!
Ok(try!(self.finalize(t, substate, gas_left, output, tracer.traces().pop(), vm_tracer.drain())))
Ok(try!(self.finalize(t, substate, gas_left, output, tracer.traces(), vm_tracer.drain())))
}
fn exec_vm<T, V>(
@ -276,7 +276,6 @@ impl<'a> Executive<'a> {
trace_info,
cost,
trace_output,
self.depth,
vec![]
);
}
@ -286,7 +285,7 @@ impl<'a> Executive<'a> {
// just drain the whole gas
self.state.revert_snapshot();
tracer.trace_failed_call(trace_info, self.depth, vec![]);
tracer.trace_failed_call(trace_info, vec![]);
Err(evm::Error::OutOfGas)
}
@ -318,10 +317,9 @@ impl<'a> Executive<'a> {
trace_info,
gas - gas_left,
trace_output,
self.depth,
traces
),
_ => tracer.trace_failed_call(trace_info, self.depth, traces),
_ => tracer.trace_failed_call(trace_info, traces),
};
trace!(target: "executive", "substate={:?}; unconfirmed_substate={:?}\n", substate, unconfirmed_substate);
@ -333,7 +331,7 @@ impl<'a> Executive<'a> {
// otherwise it's just a basic transaction, only do tracing, if necessary.
self.state.clear_snapshot();
tracer.trace_call(trace_info, U256::zero(), trace_output, self.depth, vec![]);
tracer.trace_call(trace_info, U256::zero(), trace_output, vec![]);
Ok(params.gas)
}
}
@ -384,10 +382,9 @@ impl<'a> Executive<'a> {
gas - gas_left,
trace_output,
created,
self.depth,
subtracer.traces()
),
_ => tracer.trace_failed_create(trace_info, self.depth, subtracer.traces())
_ => tracer.trace_failed_create(trace_info, subtracer.traces())
};
self.enact_result(&res, substate, unconfirmed_substate);
@ -401,7 +398,7 @@ impl<'a> Executive<'a> {
substate: Substate,
result: evm::Result<U256>,
output: Bytes,
trace: Option<Trace>,
trace: Vec<FlatTrace>,
vm_trace: Option<VMTrace>
) -> ExecutionResult {
let schedule = self.engine.schedule(self.info);
@ -493,7 +490,7 @@ mod tests {
use substate::*;
use tests::helpers::*;
use trace::trace;
use trace::{Trace, Tracer, NoopTracer, ExecutiveTracer};
use trace::{FlatTrace, Tracer, NoopTracer, ExecutiveTracer};
use trace::{VMTrace, VMOperation, VMExecutedOperation, MemoryDiff, StorageDiff, VMTracer, NoopVMTracer, ExecutiveVMTracer};
use types::executed::CallType;
@ -647,8 +644,9 @@ mod tests {
assert_eq!(gas_left, U256::from(44_752));
let expected_trace = vec![ Trace {
depth: 0,
let expected_trace = vec![FlatTrace {
trace_address: Default::default(),
subtraces: 1,
action: trace::Action::Call(trace::Call {
from: "cd1722f3947def4cf144679da39c4c32bdc35681".into(),
to: "b010143a42d5980c7e5ef0e4a4416dc098a4fed3".into(),
@ -661,22 +659,22 @@ mod tests {
gas_used: U256::from(55_248),
output: vec![],
}),
subs: vec![Trace {
depth: 1,
action: trace::Action::Create(trace::Create {
from: "b010143a42d5980c7e5ef0e4a4416dc098a4fed3".into(),
value: 23.into(),
gas: 67979.into(),
init: vec![96, 16, 128, 96, 12, 96, 0, 57, 96, 0, 243, 0, 96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53, 85]
}),
result: trace::Res::Create(trace::CreateResult {
gas_used: U256::from(3224),
address: Address::from_str("c6d80f262ae5e0f164e5fde365044d7ada2bfa34").unwrap(),
code: vec![96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53]
}),
subs: vec![]
}]
}, FlatTrace {
trace_address: vec![0].into_iter().collect(),
subtraces: 0,
action: trace::Action::Create(trace::Create {
from: "b010143a42d5980c7e5ef0e4a4416dc098a4fed3".into(),
value: 23.into(),
gas: 67979.into(),
init: vec![96, 16, 128, 96, 12, 96, 0, 57, 96, 0, 243, 0, 96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53, 85]
}),
result: trace::Res::Create(trace::CreateResult {
gas_used: U256::from(3224),
address: Address::from_str("c6d80f262ae5e0f164e5fde365044d7ada2bfa34").unwrap(),
code: vec![96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53]
}),
}];
assert_eq!(tracer.traces(), expected_trace);
let expected_vm_trace = VMTrace {
@ -754,8 +752,9 @@ mod tests {
assert_eq!(gas_left, U256::from(96_776));
let expected_trace = vec![Trace {
depth: 0,
let expected_trace = vec![FlatTrace {
trace_address: Default::default(),
subtraces: 0,
action: trace::Action::Create(trace::Create {
from: params.sender,
value: 100.into(),
@ -767,8 +766,8 @@ mod tests {
address: params.address,
code: vec![96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53]
}),
subs: vec![]
}];
assert_eq!(tracer.traces(), expected_trace);
let expected_vm_trace = VMTrace {

View File

@ -267,7 +267,7 @@ impl<'a, T, V> Ext for Externalities<'a, T, V> where T: 'a + Tracer, V: 'a + VMT
self.state.transfer_balance(&address, refund_address, &balance);
}
self.tracer.trace_suicide(address, balance, refund_address.clone(), self.depth + 1);
self.tracer.trace_suicide(address, balance, refund_address.clone());
self.substate.suicides.insert(address);
}

View File

@ -19,7 +19,7 @@ use engine::Engine;
use executive::{Executive, TransactOptions};
use evm::Factory as EvmFactory;
use account_db::*;
use trace::Trace;
use trace::FlatTrace;
use pod_account::*;
use pod_state::{self, PodState};
use types::state_diff::StateDiff;
@ -29,7 +29,7 @@ pub struct ApplyOutcome {
/// The receipt for the applied transaction.
pub receipt: Receipt,
/// The trace for the applied transaction, if None if tracing is disabled.
pub trace: Option<Trace>,
pub trace: Vec<FlatTrace>,
}
/// Result type for the execution ("application") of a transaction.
@ -402,7 +402,7 @@ use spec::*;
use transaction::*;
use util::log::init_log;
use trace::trace;
use trace::trace::{Trace};
use trace::FlatTrace;
use types::executed::CallType;
#[test]
@ -428,8 +428,9 @@ fn should_apply_create_transaction() {
state.add_balance(t.sender().as_ref().unwrap(), &(100.into()));
let vm_factory = Default::default();
let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap();
let expected_trace = Some(Trace {
depth: 0,
let expected_trace = vec![FlatTrace {
trace_address: Default::default(),
subtraces: 0,
action: trace::Action::Create(trace::Create {
from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(),
value: 100.into(),
@ -441,8 +442,7 @@ fn should_apply_create_transaction() {
address: Address::from_str("8988167e088c87cd314df6d3c2b83da5acb93ace").unwrap(),
code: vec![96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53]
}),
subs: vec![]
});
}];
assert_eq!(result.trace, expected_trace);
}
@ -489,8 +489,8 @@ fn should_trace_failed_create_transaction() {
state.add_balance(t.sender().as_ref().unwrap(), &(100.into()));
let vm_factory = Default::default();
let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap();
let expected_trace = Some(Trace {
depth: 0,
let expected_trace = vec![FlatTrace {
trace_address: Default::default(),
action: trace::Action::Create(trace::Create {
from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(),
value: 100.into(),
@ -498,8 +498,8 @@ fn should_trace_failed_create_transaction() {
init: vec![91, 96, 0, 86],
}),
result: trace::Res::FailedCreate,
subs: vec![]
});
subtraces: 0
}];
assert_eq!(result.trace, expected_trace);
}
@ -528,8 +528,8 @@ fn should_trace_call_transaction() {
state.add_balance(t.sender().as_ref().unwrap(), &(100.into()));
let vm_factory = Default::default();
let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap();
let expected_trace = Some(Trace {
depth: 0,
let expected_trace = vec![FlatTrace {
trace_address: Default::default(),
action: trace::Action::Call(trace::Call {
from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(),
to: 0xa.into(),
@ -542,8 +542,8 @@ fn should_trace_call_transaction() {
gas_used: U256::from(3),
output: vec![]
}),
subs: vec![]
});
subtraces: 0,
}];
assert_eq!(result.trace, expected_trace);
}
@ -571,8 +571,8 @@ fn should_trace_basic_call_transaction() {
state.add_balance(t.sender().as_ref().unwrap(), &(100.into()));
let vm_factory = Default::default();
let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap();
let expected_trace = Some(Trace {
depth: 0,
let expected_trace = vec![FlatTrace {
trace_address: Default::default(),
action: trace::Action::Call(trace::Call {
from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(),
to: 0xa.into(),
@ -585,8 +585,8 @@ fn should_trace_basic_call_transaction() {
gas_used: U256::from(0),
output: vec![]
}),
subs: vec![]
});
subtraces: 0,
}];
assert_eq!(result.trace, expected_trace);
}
@ -614,8 +614,8 @@ fn should_trace_call_transaction_to_builtin() {
let vm_factory = Default::default();
let result = state.apply(&info, engine.deref(), &vm_factory, &t, true).unwrap();
assert_eq!(result.trace, Some(Trace {
depth: 0,
let expected_trace = vec![FlatTrace {
trace_address: Default::default(),
action: trace::Action::Call(trace::Call {
from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(),
to: "0000000000000000000000000000000000000001".into(),
@ -628,8 +628,10 @@ fn should_trace_call_transaction_to_builtin() {
gas_used: U256::from(3000),
output: vec![]
}),
subs: vec![]
}));
subtraces: 0,
}];
assert_eq!(result.trace, expected_trace);
}
#[test]
@ -656,8 +658,8 @@ fn should_not_trace_subcall_transaction_to_builtin() {
let vm_factory = Default::default();
let result = state.apply(&info, engine.deref(), &vm_factory, &t, true).unwrap();
let expected_trace = Some(Trace {
depth: 0,
let expected_trace = vec![FlatTrace {
trace_address: Default::default(),
action: trace::Action::Call(trace::Call {
from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(),
to: 0xa.into(),
@ -670,8 +672,9 @@ fn should_not_trace_subcall_transaction_to_builtin() {
gas_used: U256::from(28_061),
output: vec![]
}),
subs: vec![]
});
subtraces: 0,
}];
assert_eq!(result.trace, expected_trace);
}
@ -700,8 +703,9 @@ fn should_not_trace_callcode() {
let vm_factory = Default::default();
let result = state.apply(&info, engine.deref(), &vm_factory, &t, true).unwrap();
let expected_trace = Some(Trace {
depth: 0,
let expected_trace = vec![FlatTrace {
trace_address: Default::default(),
subtraces: 1,
action: trace::Action::Call(trace::Call {
from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(),
to: 0xa.into(),
@ -714,23 +718,23 @@ fn should_not_trace_callcode() {
gas_used: 64.into(),
output: vec![]
}),
subs: vec![Trace {
depth: 1,
action: trace::Action::Call(trace::Call {
from: 0xa.into(),
to: 0xa.into(),
value: 0.into(),
gas: 4096.into(),
input: vec![],
call_type: CallType::CallCode,
}),
subs: vec![],
result: trace::Res::Call(trace::CallResult {
gas_used: 3.into(),
output: vec![],
}),
}],
});
}, FlatTrace {
trace_address: vec![0].into_iter().collect(),
subtraces: 0,
action: trace::Action::Call(trace::Call {
from: 0xa.into(),
to: 0xa.into(),
value: 0.into(),
gas: 4096.into(),
input: vec![],
call_type: CallType::CallCode,
}),
result: trace::Res::Call(trace::CallResult {
gas_used: 3.into(),
output: vec![],
}),
}];
assert_eq!(result.trace, expected_trace);
}
@ -762,8 +766,9 @@ fn should_not_trace_delegatecall() {
let vm_factory = Default::default();
let result = state.apply(&info, engine.deref(), &vm_factory, &t, true).unwrap();
let expected_trace = Some(Trace {
depth: 0,
let expected_trace = vec![FlatTrace {
trace_address: Default::default(),
subtraces: 1,
action: trace::Action::Call(trace::Call {
from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(),
to: 0xa.into(),
@ -776,23 +781,23 @@ fn should_not_trace_delegatecall() {
gas_used: U256::from(61),
output: vec![]
}),
subs: vec![Trace {
depth: 1,
action: trace::Action::Call(trace::Call {
from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(),
to: 0xa.into(),
value: 0.into(),
gas: 32768.into(),
input: vec![],
call_type: CallType::DelegateCall,
}),
subs: vec![],
result: trace::Res::Call(trace::CallResult {
gas_used: 3.into(),
output: vec![],
}),
}],
});
}, FlatTrace {
trace_address: vec![0].into_iter().collect(),
subtraces: 0,
action: trace::Action::Call(trace::Call {
from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(),
to: 0xa.into(),
value: 0.into(),
gas: 32768.into(),
input: vec![],
call_type: CallType::DelegateCall,
}),
result: trace::Res::Call(trace::CallResult {
gas_used: 3.into(),
output: vec![],
}),
}];
assert_eq!(result.trace, expected_trace);
}
@ -820,8 +825,8 @@ fn should_trace_failed_call_transaction() {
state.add_balance(t.sender().as_ref().unwrap(), &(100.into()));
let vm_factory = Default::default();
let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap();
let expected_trace = Some(Trace {
depth: 0,
let expected_trace = vec![FlatTrace {
trace_address: Default::default(),
action: trace::Action::Call(trace::Call {
from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(),
to: 0xa.into(),
@ -831,10 +836,8 @@ fn should_trace_failed_call_transaction() {
call_type: CallType::Call,
}),
result: trace::Res::FailedCall,
subs: vec![]
});
println!("trace: {:?}", result.trace);
subtraces: 0,
}];
assert_eq!(result.trace, expected_trace);
}
@ -864,8 +867,10 @@ fn should_trace_call_with_subcall_transaction() {
state.add_balance(t.sender().as_ref().unwrap(), &(100.into()));
let vm_factory = Default::default();
let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap();
let expected_trace = Some(Trace {
depth: 0,
let expected_trace = vec![FlatTrace {
trace_address: Default::default(),
subtraces: 1,
action: trace::Action::Call(trace::Call {
from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(),
to: 0xa.into(),
@ -878,23 +883,22 @@ fn should_trace_call_with_subcall_transaction() {
gas_used: U256::from(69),
output: vec![]
}),
subs: vec![Trace {
depth: 1,
action: trace::Action::Call(trace::Call {
from: 0xa.into(),
to: 0xb.into(),
value: 0.into(),
gas: 78934.into(),
input: vec![],
call_type: CallType::Call,
}),
result: trace::Res::Call(trace::CallResult {
gas_used: U256::from(3),
output: vec![]
}),
subs: vec![]
}]
});
}, FlatTrace {
trace_address: vec![0].into_iter().collect(),
subtraces: 0,
action: trace::Action::Call(trace::Call {
from: 0xa.into(),
to: 0xb.into(),
value: 0.into(),
gas: 78934.into(),
input: vec![],
call_type: CallType::Call,
}),
result: trace::Res::Call(trace::CallResult {
gas_used: U256::from(3),
output: vec![]
}),
}];
assert_eq!(result.trace, expected_trace);
}
@ -923,8 +927,9 @@ fn should_trace_call_with_basic_subcall_transaction() {
state.add_balance(t.sender().as_ref().unwrap(), &(100.into()));
let vm_factory = Default::default();
let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap();
let expected_trace = Some(Trace {
depth: 0,
let expected_trace = vec![FlatTrace {
trace_address: Default::default(),
subtraces: 1,
action: trace::Action::Call(trace::Call {
from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(),
to: 0xa.into(),
@ -937,20 +942,19 @@ fn should_trace_call_with_basic_subcall_transaction() {
gas_used: U256::from(31761),
output: vec![]
}),
subs: vec![Trace {
depth: 1,
action: trace::Action::Call(trace::Call {
from: 0xa.into(),
to: 0xb.into(),
value: 69.into(),
gas: 2300.into(),
input: vec![],
call_type: CallType::Call,
}),
result: trace::Res::Call(trace::CallResult::default()),
subs: vec![]
}]
});
}, FlatTrace {
trace_address: vec![0].into_iter().collect(),
subtraces: 0,
action: trace::Action::Call(trace::Call {
from: 0xa.into(),
to: 0xb.into(),
value: 69.into(),
gas: 2300.into(),
input: vec![],
call_type: CallType::Call,
}),
result: trace::Res::Call(trace::CallResult::default()),
}];
assert_eq!(result.trace, expected_trace);
}
@ -979,8 +983,9 @@ fn should_not_trace_call_with_invalid_basic_subcall_transaction() {
state.add_balance(t.sender().as_ref().unwrap(), &(100.into()));
let vm_factory = Default::default();
let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap();
let expected_trace = Some(Trace {
depth: 0,
let expected_trace = vec![FlatTrace {
trace_address: Default::default(),
subtraces: 0,
action: trace::Action::Call(trace::Call {
from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(),
to: 0xa.into(),
@ -993,8 +998,7 @@ fn should_not_trace_call_with_invalid_basic_subcall_transaction() {
gas_used: U256::from(31761),
output: vec![]
}),
subs: vec![]
});
}];
assert_eq!(result.trace, expected_trace);
}
@ -1024,8 +1028,9 @@ fn should_trace_failed_subcall_transaction() {
state.add_balance(t.sender().as_ref().unwrap(), &(100.into()));
let vm_factory = Default::default();
let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap();
let expected_trace = Some(Trace {
depth: 0,
let expected_trace = vec![FlatTrace {
trace_address: Default::default(),
subtraces: 1,
action: trace::Action::Call(trace::Call {
from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(),
to: 0xa.into(),
@ -1038,20 +1043,19 @@ fn should_trace_failed_subcall_transaction() {
gas_used: U256::from(79_000),
output: vec![]
}),
subs: vec![Trace {
depth: 1,
action: trace::Action::Call(trace::Call {
from: 0xa.into(),
to: 0xb.into(),
value: 0.into(),
gas: 78934.into(),
input: vec![],
call_type: CallType::Call,
}),
result: trace::Res::FailedCall,
subs: vec![]
}]
});
}, FlatTrace {
trace_address: vec![0].into_iter().collect(),
subtraces: 0,
action: trace::Action::Call(trace::Call {
from: 0xa.into(),
to: 0xb.into(),
value: 0.into(),
gas: 78934.into(),
input: vec![],
call_type: CallType::Call,
}),
result: trace::Res::FailedCall,
}];
assert_eq!(result.trace, expected_trace);
}
@ -1082,8 +1086,9 @@ fn should_trace_call_with_subcall_with_subcall_transaction() {
state.add_balance(t.sender().as_ref().unwrap(), &(100.into()));
let vm_factory = Default::default();
let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap();
let expected_trace = Some(Trace {
depth: 0,
let expected_trace = vec![FlatTrace {
trace_address: Default::default(),
subtraces: 1,
action: trace::Action::Call(trace::Call {
from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(),
to: 0xa.into(),
@ -1096,38 +1101,37 @@ fn should_trace_call_with_subcall_with_subcall_transaction() {
gas_used: U256::from(135),
output: vec![]
}),
subs: vec![Trace {
depth: 1,
action: trace::Action::Call(trace::Call {
from: 0xa.into(),
to: 0xb.into(),
value: 0.into(),
gas: 78934.into(),
input: vec![],
call_type: CallType::Call,
}),
result: trace::Res::Call(trace::CallResult {
gas_used: U256::from(69),
output: vec![]
}),
subs: vec![Trace {
depth: 2,
action: trace::Action::Call(trace::Call {
from: 0xb.into(),
to: 0xc.into(),
value: 0.into(),
gas: 78868.into(),
input: vec![],
call_type: CallType::Call,
}),
result: trace::Res::Call(trace::CallResult {
gas_used: U256::from(3),
output: vec![]
}),
subs: vec![]
}]
}]
});
}, FlatTrace {
trace_address: vec![0].into_iter().collect(),
subtraces: 1,
action: trace::Action::Call(trace::Call {
from: 0xa.into(),
to: 0xb.into(),
value: 0.into(),
gas: 78934.into(),
input: vec![],
call_type: CallType::Call,
}),
result: trace::Res::Call(trace::CallResult {
gas_used: U256::from(69),
output: vec![]
}),
}, FlatTrace {
trace_address: vec![0, 0].into_iter().collect(),
subtraces: 0,
action: trace::Action::Call(trace::Call {
from: 0xb.into(),
to: 0xc.into(),
value: 0.into(),
gas: 78868.into(),
input: vec![],
call_type: CallType::Call,
}),
result: trace::Res::Call(trace::CallResult {
gas_used: U256::from(3),
output: vec![]
}),
}];
assert_eq!(result.trace, expected_trace);
}
@ -1158,8 +1162,10 @@ fn should_trace_failed_subcall_with_subcall_transaction() {
state.add_balance(t.sender().as_ref().unwrap(), &(100.into()));
let vm_factory = Default::default();
let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap();
let expected_trace = Some(Trace {
depth: 0,
let expected_trace = vec![FlatTrace {
trace_address: Default::default(),
subtraces: 1,
action: trace::Action::Call(trace::Call {
from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(),
to: 0xa.into(),
@ -1171,36 +1177,35 @@ fn should_trace_failed_subcall_with_subcall_transaction() {
result: trace::Res::Call(trace::CallResult {
gas_used: U256::from(79_000),
output: vec![]
}),
subs: vec![Trace {
depth: 1,
})
}, FlatTrace {
trace_address: vec![0].into_iter().collect(),
subtraces: 1,
action: trace::Action::Call(trace::Call {
from: 0xa.into(),
to: 0xb.into(),
value: 0.into(),
gas: 78934.into(),
input: vec![],
call_type: CallType::Call,
}),
result: trace::Res::FailedCall,
subs: vec![Trace {
depth: 2,
action: trace::Action::Call(trace::Call {
from: 0xb.into(),
to: 0xc.into(),
value: 0.into(),
gas: 78868.into(),
input: vec![],
call_type: CallType::Call,
}),
result: trace::Res::Call(trace::CallResult {
gas_used: U256::from(3),
output: vec![]
}),
subs: vec![]
}]
}]
});
from: 0xa.into(),
to: 0xb.into(),
value: 0.into(),
gas: 78934.into(),
input: vec![],
call_type: CallType::Call,
}),
result: trace::Res::FailedCall,
}, FlatTrace {
trace_address: vec![0, 0].into_iter().collect(),
subtraces: 0,
action: trace::Action::Call(trace::Call {
from: 0xb.into(),
to: 0xc.into(),
value: 0.into(),
gas: 78868.into(),
call_type: CallType::Call,
input: vec![],
}),
result: trace::Res::Call(trace::CallResult {
gas_used: U256::from(3),
output: vec![]
}),
}];
assert_eq!(result.trace, expected_trace);
}
@ -1230,8 +1235,9 @@ fn should_trace_suicide() {
state.add_balance(t.sender().as_ref().unwrap(), &100.into());
let vm_factory = Default::default();
let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap();
let expected_trace = Some(Trace {
depth: 0,
let expected_trace = vec![FlatTrace {
trace_address: Default::default(),
subtraces: 1,
action: trace::Action::Call(trace::Call {
from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(),
to: 0xa.into(),
@ -1244,17 +1250,17 @@ fn should_trace_suicide() {
gas_used: 3.into(),
output: vec![]
}),
subs: vec![Trace {
depth: 1,
action: trace::Action::Suicide(trace::Suicide {
address: 0xa.into(),
refund_address: 0xb.into(),
balance: 150.into(),
}),
result: trace::Res::None,
subs: vec![]
}]
});
}, FlatTrace {
trace_address: vec![0].into_iter().collect(),
subtraces: 0,
action: trace::Action::Suicide(trace::Suicide {
address: 0xa.into(),
refund_address: 0xb.into(),
balance: 150.into(),
}),
result: trace::Res::None,
}];
assert_eq!(result.trace, expected_trace);
}

View File

@ -1,58 +0,0 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
use util::rlp::*;
use basic_types::LogBloom;
use super::Trace;
/// Traces created by transactions from the same block.
#[derive(Clone)]
pub struct BlockTraces(Vec<Trace>);
impl From<Vec<Trace>> for BlockTraces {
fn from(traces: Vec<Trace>) -> Self {
BlockTraces(traces)
}
}
impl Into<Vec<Trace>> for BlockTraces {
fn into(self) -> Vec<Trace> {
self.0
}
}
impl Decodable for BlockTraces {
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> 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())
}
}

View File

@ -197,7 +197,7 @@ impl<T> TraceDB<T> where T: DatabaseExtras {
action: trace.action,
result: trace.result,
subtraces: trace.subtraces,
trace_address: trace.trace_address,
trace_address: trace.trace_address.into_iter().collect(),
transaction_number: tx_number,
transaction_hash: tx_hash.clone(),
block_number: block_number,
@ -230,7 +230,7 @@ impl<T> TraceDatabase for TraceDB<T> where T: DatabaseExtras {
let mut traces = self.traces.write();
// it's important to use overwrite here,
// cause this value might be queried by hash later
batch.write_with_cache(traces.deref_mut(), request.block_hash, request.traces.into(), CacheUpdatePolicy::Overwrite);
batch.write_with_cache(traces.deref_mut(), request.block_hash, request.traces, CacheUpdatePolicy::Overwrite);
}
// now let's rebuild the blooms
@ -263,12 +263,13 @@ impl<T> TraceDatabase for TraceDB<T> where T: DatabaseExtras {
}
fn trace(&self, block_number: BlockNumber, tx_position: usize, trace_position: Vec<usize>) -> Option<LocalizedTrace> {
let trace_position_deq = trace_position.into_iter().collect();
self.extras.block_hash(block_number)
.and_then(|block_hash| self.transactions_traces(&block_hash)
.and_then(|traces| traces.into_iter().nth(tx_position))
.map(Into::<Vec<FlatTrace>>::into)
// this may and should be optimized
.and_then(|traces| traces.into_iter().find(|trace| trace.trace_address == trace_position))
.and_then(|traces| traces.into_iter().find(|trace| trace.trace_address == trace_position_deq))
.map(|trace| {
let tx_hash = self.extras.transaction_hash(block_number, tx_position)
.expect("Expected to find transaction hash. Database is probably corrupted");
@ -277,7 +278,7 @@ impl<T> TraceDatabase for TraceDB<T> where T: DatabaseExtras {
action: trace.action,
result: trace.result,
subtraces: trace.subtraces,
trace_address: trace.trace_address,
trace_address: trace.trace_address.into_iter().collect(),
transaction_number: tx_position,
transaction_hash: tx_hash,
block_number: block_number,
@ -301,7 +302,7 @@ impl<T> TraceDatabase for TraceDB<T> where T: DatabaseExtras {
action: trace.action,
result: trace.result,
subtraces: trace.subtraces,
trace_address: trace.trace_address,
trace_address: trace.trace_address.into_iter().collect(),
transaction_number: tx_position,
transaction_hash: tx_hash.clone(),
block_number: block_number,
@ -328,7 +329,7 @@ impl<T> TraceDatabase for TraceDB<T> where T: DatabaseExtras {
action: trace.action,
result: trace.result,
subtraces: trace.subtraces,
trace_address: trace.trace_address,
trace_address: trace.trace_address.into_iter().collect(),
transaction_number: tx_position,
transaction_hash: tx_hash.clone(),
block_number: block_number,
@ -365,8 +366,9 @@ mod tests {
use devtools::RandomTempPath;
use header::BlockNumber;
use trace::{Config, Switch, TraceDB, Database, DatabaseExtras, ImportRequest};
use trace::{BlockTraces, Trace, Filter, LocalizedTrace, AddressesFilter};
use trace::{Filter, LocalizedTrace, AddressesFilter};
use trace::trace::{Call, Action, Res};
use trace::flat::{FlatTrace, FlatBlockTraces, FlatTransactionTraces};
use types::executed::CallType;
struct NoopExtras;
@ -485,19 +487,19 @@ mod tests {
fn create_simple_import_request(block_number: BlockNumber, block_hash: H256) -> ImportRequest {
ImportRequest {
traces: BlockTraces::from(vec![Trace {
depth: 0,
traces: FlatBlockTraces::from(vec![FlatTransactionTraces::from(vec![FlatTrace {
trace_address: Default::default(),
subtraces: 0,
action: Action::Call(Call {
from: Address::from(1),
to: Address::from(2),
value: U256::from(3),
gas: U256::from(4),
from: 1.into(),
to: 2.into(),
value: 3.into(),
gas: 4.into(),
input: vec![],
call_type: CallType::Call,
}),
result: Res::FailedCall,
subs: vec![],
}]),
}])]),
block_hash: block_hash.clone(),
block_number: block_number,
enacted: vec![block_hash],

View File

@ -18,13 +18,53 @@
use util::{Bytes, Address, U256};
use action_params::ActionParams;
use trace::trace::{Trace, Call, Create, Action, Res, CreateResult, CallResult, VMTrace, VMOperation, VMExecutedOperation, MemoryDiff, StorageDiff, Suicide};
use trace::{Tracer, VMTracer};
use trace::trace::{Call, Create, Action, Res, CreateResult, CallResult, VMTrace, VMOperation, VMExecutedOperation, MemoryDiff, StorageDiff, Suicide};
use trace::{Tracer, VMTracer, FlatTrace};
/// Simple executive tracer. Traces all calls and creates. Ignores delegatecalls.
#[derive(Default)]
pub struct ExecutiveTracer {
traces: Vec<Trace>,
traces: Vec<FlatTrace>,
}
fn top_level_subtraces(traces: &[FlatTrace]) -> usize {
traces.iter().filter(|t| t.trace_address.is_empty()).count()
}
fn update_trace_address(traces: Vec<FlatTrace>) -> Vec<FlatTrace> {
// input traces are expected to be ordered like
// []
// [0]
// [0, 0]
// [0, 1]
// []
// [0]
//
// so they can be transformed to
//
// [0]
// [0, 0]
// [0, 0, 0]
// [0, 0, 1]
// [1]
// [1, 0]
let mut top_subtrace_index = 0;
let mut subtrace_subtraces_left = 0;
traces.into_iter().map(|mut trace| {
let is_top_subtrace = trace.trace_address.is_empty();
trace.trace_address.push_front(top_subtrace_index);
if is_top_subtrace {
subtrace_subtraces_left = trace.subtraces;
} else {
subtrace_subtraces_left -= 1;
}
if subtrace_subtraces_left == 0 {
top_subtrace_index += 1;
}
trace
}).collect()
}
impl Tracer for ExecutiveTracer {
@ -40,67 +80,71 @@ impl Tracer for ExecutiveTracer {
Some(vec![])
}
fn trace_call(&mut self, call: Option<Call>, gas_used: U256, output: Option<Bytes>, depth: usize, subs: Vec<Trace>) {
let trace = Trace {
depth: depth,
subs: subs,
fn trace_call(&mut self, call: Option<Call>, gas_used: U256, output: Option<Bytes>, subs: Vec<FlatTrace>) {
let trace = FlatTrace {
trace_address: Default::default(),
subtraces: top_level_subtraces(&subs),
action: Action::Call(call.expect("self.prepare_trace_call().is_some(): so we must be tracing: qed")),
result: Res::Call(CallResult {
gas_used: gas_used,
output: output.expect("self.prepare_trace_output().is_some(): so we must be tracing: qed")
})
}),
};
debug!(target: "trace", "Traced call {:?}", trace);
self.traces.push(trace);
self.traces.extend(update_trace_address(subs));
}
fn trace_create(&mut self, create: Option<Create>, gas_used: U256, code: Option<Bytes>, address: Address, depth: usize, subs: Vec<Trace>) {
let trace = Trace {
depth: depth,
subs: subs,
fn trace_create(&mut self, create: Option<Create>, gas_used: U256, code: Option<Bytes>, address: Address, subs: Vec<FlatTrace>) {
let trace = FlatTrace {
subtraces: top_level_subtraces(&subs),
action: Action::Create(create.expect("self.prepare_trace_create().is_some(): so we must be tracing: qed")),
result: Res::Create(CreateResult {
gas_used: gas_used,
code: code.expect("self.prepare_trace_output.is_some(): so we must be tracing: qed"),
address: address
})
}),
trace_address: Default::default(),
};
debug!(target: "trace", "Traced create {:?}", trace);
self.traces.push(trace);
self.traces.extend(update_trace_address(subs));
}
fn trace_failed_call(&mut self, call: Option<Call>, depth: usize, subs: Vec<Trace>) {
let trace = Trace {
depth: depth,
subs: subs,
fn trace_failed_call(&mut self, call: Option<Call>, subs: Vec<FlatTrace>) {
let trace = FlatTrace {
trace_address: Default::default(),
subtraces: top_level_subtraces(&subs),
action: Action::Call(call.expect("self.prepare_trace_call().is_some(): so we must be tracing: qed")),
result: Res::FailedCall,
};
debug!(target: "trace", "Traced failed call {:?}", trace);
self.traces.push(trace);
self.traces.extend(update_trace_address(subs));
}
fn trace_failed_create(&mut self, create: Option<Create>, depth: usize, subs: Vec<Trace>) {
let trace = Trace {
depth: depth,
subs: subs,
fn trace_failed_create(&mut self, create: Option<Create>, subs: Vec<FlatTrace>) {
let trace = FlatTrace {
subtraces: top_level_subtraces(&subs),
action: Action::Create(create.expect("self.prepare_trace_create().is_some(): so we must be tracing: qed")),
result: Res::FailedCreate,
trace_address: Default::default(),
};
debug!(target: "trace", "Traced failed create {:?}", trace);
self.traces.push(trace);
self.traces.extend(update_trace_address(subs));
}
fn trace_suicide(&mut self, address: Address, balance: U256, refund_address: Address, depth: usize) {
let trace = Trace {
depth: depth,
subs: vec![],
fn trace_suicide(&mut self, address: Address, balance: U256, refund_address: Address) {
let trace = FlatTrace {
subtraces: 0,
action: Action::Suicide(Suicide {
address: address,
refund_address: refund_address,
balance: balance,
}),
result: Res::None,
trace_address: Default::default(),
};
debug!(target: "trace", "Traced failed suicide {:?}", trace);
self.traces.push(trace);
@ -110,7 +154,7 @@ impl Tracer for ExecutiveTracer {
ExecutiveTracer::default()
}
fn traces(self) -> Vec<Trace> {
fn traces(self) -> Vec<FlatTrace> {
self.traces
}
}

View File

@ -17,12 +17,12 @@
//! Traces import request.
use util::H256;
use header::BlockNumber;
use trace::BlockTraces;
use trace::FlatBlockTraces;
/// Traces import request.
pub struct ImportRequest {
/// Traces to import.
pub traces: BlockTraces,
pub traces: FlatBlockTraces,
/// Hash of traces block.
pub block_hash: H256,
/// Number of traces block.

View File

@ -16,22 +16,20 @@
//! Tracing
mod block;
mod bloom;
mod config;
mod db;
mod error;
mod executive_tracer;
pub mod flat;
mod import;
mod noop_tracer;
pub use types::trace_types::*;
pub use self::block::BlockTraces;
pub use self::config::{Config, Switch};
pub use self::db::TraceDB;
pub use self::error::Error;
pub use types::trace_types::trace::{Trace, VMTrace, VMOperation, VMExecutedOperation, MemoryDiff, StorageDiff};
pub use types::trace_types::trace::{VMTrace, VMOperation, VMExecutedOperation, MemoryDiff, StorageDiff};
pub use types::trace_types::flat::{FlatTrace, FlatTransactionTraces, FlatBlockTraces};
pub use self::noop_tracer::{NoopTracer, NoopVMTracer};
pub use self::executive_tracer::{ExecutiveTracer, ExecutiveVMTracer};
pub use types::trace_types::filter::{Filter, AddressesFilter};
@ -59,8 +57,7 @@ pub trait Tracer: Send {
call: Option<Call>,
gas_used: U256,
output: Option<Bytes>,
depth: usize,
subs: Vec<Trace>
subs: Vec<FlatTrace>,
);
/// Stores trace create info.
@ -70,24 +67,23 @@ pub trait Tracer: Send {
gas_used: U256,
code: Option<Bytes>,
address: Address,
depth: usize,
subs: Vec<Trace>
subs: Vec<FlatTrace>
);
/// Stores failed call trace.
fn trace_failed_call(&mut self, call: Option<Call>, depth: usize, subs: Vec<Trace>);
fn trace_failed_call(&mut self, call: Option<Call>, subs: Vec<FlatTrace>);
/// Stores failed create trace.
fn trace_failed_create(&mut self, create: Option<Create>, depth: usize, subs: Vec<Trace>);
fn trace_failed_create(&mut self, create: Option<Create>, subs: Vec<FlatTrace>);
/// Stores suicide info.
fn trace_suicide(&mut self, address: Address, balance: U256, refund_address: Address, depth: usize);
fn trace_suicide(&mut self, address: Address, balance: U256, refund_address: Address);
/// Spawn subtracer which will be used to trace deeper levels of execution.
fn subtracer(&self) -> Self where Self: Sized;
/// Consumes self and returns all traces.
fn traces(self) -> Vec<Trace>;
fn traces(self) -> Vec<FlatTrace>;
}
/// Used by executive to build VM traces.

View File

@ -18,8 +18,8 @@
use util::{Bytes, Address, U256};
use action_params::ActionParams;
use trace::{Tracer, VMTracer};
use trace::trace::{Trace, Call, Create, VMTrace};
use trace::{Tracer, VMTracer, FlatTrace};
use trace::trace::{Call, Create, VMTrace};
/// Nonoperative tracer. Does not trace anything.
pub struct NoopTracer;
@ -37,32 +37,32 @@ impl Tracer for NoopTracer {
None
}
fn trace_call(&mut self, call: Option<Call>, _: U256, output: Option<Bytes>, _: usize, _: Vec<Trace>) {
fn trace_call(&mut self, call: Option<Call>, _: U256, output: Option<Bytes>, _: Vec<FlatTrace>) {
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<Create>, _: U256, code: Option<Bytes>, _: Address, _: usize, _: Vec<Trace>) {
fn trace_create(&mut self, create: Option<Create>, _: U256, code: Option<Bytes>, _: Address, _: Vec<FlatTrace>) {
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<Call>, _: usize, _: Vec<Trace>) {
fn trace_failed_call(&mut self, call: Option<Call>, _: Vec<FlatTrace>) {
assert!(call.is_none(), "self.prepare_trace_call().is_none(): so we can't be tracing: qed");
}
fn trace_failed_create(&mut self, create: Option<Create>, _: usize, _: Vec<Trace>) {
fn trace_failed_create(&mut self, create: Option<Create>, _: Vec<FlatTrace>) {
assert!(create.is_none(), "self.prepare_trace_create().is_none(): so we can't be tracing: qed");
}
fn trace_suicide(&mut self, _address: Address, _balance: U256, _refund_address: Address, _depth: usize) {
fn trace_suicide(&mut self, _address: Address, _balance: U256, _refund_address: Address) {
}
fn subtracer(&self) -> Self {
NoopTracer
}
fn traces(self) -> Vec<Trace> {
fn traces(self) -> Vec<FlatTrace> {
vec![]
}
}

View File

@ -19,7 +19,7 @@
use util::numbers::*;
use util::Bytes;
use util::rlp::*;
use trace::{Trace, VMTrace};
use trace::{VMTrace, FlatTrace};
use types::log_entry::LogEntry;
use types::state_diff::StateDiff;
use ipc::binary::BinaryConvertError;
@ -97,7 +97,7 @@ pub struct Executed {
/// Transaction output.
pub output: Bytes,
/// The trace of this transaction.
pub trace: Option<Trace>,
pub trace: Vec<FlatTrace>,
/// The VM trace of this transaction.
pub vm_trace: Option<VMTrace>,
/// The state diff, if we traced it.

View File

@ -289,7 +289,7 @@ mod tests {
call_type: CallType::Call,
}),
result: Res::FailedCall,
trace_address: vec![0],
trace_address: vec![0].into_iter().collect(),
subtraces: 0,
};
@ -313,7 +313,7 @@ mod tests {
code: vec![],
address: 2.into(),
}),
trace_address: vec![0],
trace_address: vec![0].into_iter().collect(),
subtraces: 0,
};
@ -332,7 +332,7 @@ mod tests {
balance: 3.into(),
}),
result: Res::None,
trace_address: vec![],
trace_address: vec![].into_iter().collect(),
subtraces: 0
};

View File

@ -16,15 +16,17 @@
//! Flat trace module
use std::collections::VecDeque;
use std::mem;
use ipc::binary::BinaryConvertError;
use util::rlp::*;
use trace::BlockTraces;
use basic_types::LogBloom;
use super::trace::{Trace, Action, Res};
use super::trace::{Action, Res};
/// Trace localized in vector of traces produced by a single transaction.
///
/// Parent and children indexes refer to positions in this vector.
#[derive(Debug, PartialEq, Clone)]
#[derive(Debug, PartialEq, Clone, Binary)]
pub struct FlatTrace {
/// Type of action performed by a transaction.
pub action: Action,
@ -35,7 +37,7 @@ pub struct FlatTrace {
/// Exact location of trace.
///
/// [index in root, index in first CALL, index in second CALL, ...]
pub trace_address: Vec<usize>,
pub trace_address: VecDeque<usize>,
}
impl FlatTrace {
@ -51,18 +53,19 @@ impl Encodable for FlatTrace {
s.append(&self.action);
s.append(&self.result);
s.append(&self.subtraces);
s.append(&self.trace_address);
s.append(&self.trace_address.clone().into_iter().collect::<Vec<_>>());
}
}
impl Decodable for FlatTrace {
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
let d = decoder.as_rlp();
let v: Vec<usize> = try!(d.val_at(3));
let res = FlatTrace {
action: try!(d.val_at(0)),
result: try!(d.val_at(1)),
subtraces: try!(d.val_at(2)),
trace_address: try!(d.val_at(3)),
trace_address: v.into_iter().collect(),
};
Ok(res)
@ -73,6 +76,12 @@ impl Decodable for FlatTrace {
#[derive(Debug, PartialEq, Clone)]
pub struct FlatTransactionTraces(Vec<FlatTrace>);
impl From<Vec<FlatTrace>> for FlatTransactionTraces {
fn from(v: Vec<FlatTrace>) -> Self {
FlatTransactionTraces(v)
}
}
impl FlatTransactionTraces {
/// Returns bloom of all traces in the collection.
pub fn bloom(&self) -> LogBloom {
@ -102,6 +111,12 @@ impl Into<Vec<FlatTrace>> for FlatTransactionTraces {
#[derive(Debug, PartialEq, Clone)]
pub struct FlatBlockTraces(Vec<FlatTransactionTraces>);
impl From<Vec<FlatTransactionTraces>> for FlatBlockTraces {
fn from(v: Vec<FlatTransactionTraces>) -> Self {
FlatBlockTraces(v)
}
}
impl FlatBlockTraces {
/// Returns bloom of all traces in the block.
pub fn bloom(&self) -> LogBloom {
@ -121,142 +136,18 @@ impl Decodable for FlatBlockTraces {
}
}
impl From<BlockTraces> for FlatBlockTraces {
fn from(block_traces: BlockTraces) -> Self {
let traces: Vec<Trace> = block_traces.into();
let ordered = traces.into_iter()
.map(|trace| FlatBlockTraces::flatten(vec![], trace))
.map(FlatTransactionTraces)
.collect();
FlatBlockTraces(ordered)
}
}
impl Into<Vec<FlatTransactionTraces>> for FlatBlockTraces {
fn into(self) -> Vec<FlatTransactionTraces> {
self.0
}
}
impl FlatBlockTraces {
/// Helper function flattening nested tree structure to vector of ordered traces.
fn flatten(address: Vec<usize>, trace: Trace) -> Vec<FlatTrace> {
let subtraces = trace.subs.len();
let all_subs = trace.subs
.into_iter()
.enumerate()
.flat_map(|(index, subtrace)| {
let mut subtrace_address = address.clone();
subtrace_address.push(index);
FlatBlockTraces::flatten(subtrace_address, subtrace)
})
.collect::<Vec<_>>();
let ordered = FlatTrace {
action: trace.action,
result: trace.result,
subtraces: subtraces,
trace_address: address,
};
let mut result = vec![ordered];
result.extend(all_subs);
result
}
}
#[cfg(test)]
mod tests {
use super::{FlatBlockTraces, FlatTransactionTraces, FlatTrace};
use util::{U256, Address};
use trace::trace::{Action, Res, CallResult, Call, Create, Trace};
use trace::BlockTraces;
use trace::trace::{Action, Res, CallResult, Call};
use types::executed::CallType;
#[test]
fn test_block_from() {
let trace = Trace {
depth: 2,
action: Action::Call(Call {
from: Address::from(1),
to: Address::from(2),
value: U256::from(3),
gas: U256::from(4),
input: vec![0x5],
call_type: CallType::Call,
}),
subs: vec![
Trace {
depth: 3,
action: Action::Create(Create {
from: Address::from(6),
value: U256::from(7),
gas: U256::from(8),
init: vec![0x9]
}),
subs: vec![
Trace {
depth: 3,
action: Action::Create(Create {
from: Address::from(6),
value: U256::from(7),
gas: U256::from(8),
init: vec![0x9]
}),
subs: vec![
],
result: Res::FailedCreate
},
Trace {
depth: 3,
action: Action::Create(Create {
from: Address::from(6),
value: U256::from(7),
gas: U256::from(8),
init: vec![0x9]
}),
subs: vec![
],
result: Res::FailedCreate
}
],
result: Res::FailedCreate
},
Trace {
depth: 3,
action: Action::Create(Create {
from: Address::from(6),
value: U256::from(7),
gas: U256::from(8),
init: vec![0x9]
}),
subs: vec![],
result: Res::FailedCreate,
}
],
result: Res::Call(CallResult {
gas_used: U256::from(10),
output: vec![0x11, 0x12]
})
};
let block_traces = FlatBlockTraces::from(BlockTraces::from(vec![trace]));
let transaction_traces: Vec<FlatTransactionTraces> = block_traces.into();
assert_eq!(transaction_traces.len(), 1);
let ordered_traces: Vec<FlatTrace> = transaction_traces.into_iter().nth(0).unwrap().into();
assert_eq!(ordered_traces.len(), 5);
assert_eq!(ordered_traces[0].trace_address, vec![]);
assert_eq!(ordered_traces[0].subtraces, 2);
assert_eq!(ordered_traces[1].trace_address, vec![0]);
assert_eq!(ordered_traces[1].subtraces, 2);
assert_eq!(ordered_traces[2].trace_address, vec![0, 0]);
assert_eq!(ordered_traces[2].subtraces, 0);
assert_eq!(ordered_traces[3].trace_address, vec![0, 1]);
assert_eq!(ordered_traces[3].subtraces, 0);
assert_eq!(ordered_traces[4].trace_address, vec![1]);
assert_eq!(ordered_traces[4].subtraces, 0);
}
#[test]
fn test_trace_serialization() {
use util::rlp;
@ -274,7 +165,7 @@ mod tests {
gas_used: 10.into(),
output: vec![0x11, 0x12]
}),
trace_address: Vec::new(),
trace_address: Default::default(),
subtraces: 0,
};

View File

@ -17,5 +17,6 @@
//! Types used in the public api
pub mod filter;
pub mod flat;
pub mod trace;
pub mod localized;

View File

@ -384,51 +384,6 @@ impl Res {
}
}
#[derive(Debug, Clone, PartialEq, Binary)]
/// A trace; includes a description of the action being traced and sub traces of each interior action.
pub struct Trace {
/// The number of EVM execution environments active when this action happened; 0 if it's
/// the outer action of the transaction.
pub depth: usize,
/// The action being performed.
pub action: Action,
/// The sub traces for each interior action performed as part of this call.
pub subs: Vec<Trace>,
/// The result of the performed action.
pub result: Res,
}
impl Encodable for Trace {
fn rlp_append(&self, s: &mut RlpStream) {
s.begin_list(4);
s.append(&self.depth);
s.append(&self.action);
s.append(&self.subs);
s.append(&self.result);
}
}
impl Decodable for Trace {
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
let d = decoder.as_rlp();
let res = Trace {
depth: try!(d.val_at(0)),
action: try!(d.val_at(1)),
subs: try!(d.val_at(2)),
result: try!(d.val_at(3)),
};
Ok(res)
}
}
impl Trace {
/// Returns trace bloom.
pub fn bloom(&self) -> LogBloom {
self.subs.iter().fold(self.action.bloom() | self.result.bloom(), |b, s| b | s.bloom())
}
}
#[derive(Debug, Clone, PartialEq, Binary)]
/// A diff of some chunk of memory.
pub struct MemoryDiff {
@ -593,105 +548,3 @@ impl Decodable for VMTrace {
}
}
#[cfg(test)]
mod tests {
use util::{Address, U256, FixedHash};
use util::rlp::{encode, decode};
use util::sha3::Hashable;
use trace::trace::{Call, CallResult, Create, Res, Action, Trace, Suicide, CreateResult};
use types::executed::CallType;
#[test]
fn traces_rlp() {
let trace = Trace {
depth: 2,
action: Action::Call(Call {
from: Address::from(1),
to: Address::from(2),
value: U256::from(3),
gas: U256::from(4),
input: vec![0x5],
call_type: CallType::Call,
}),
subs: vec![
Trace {
depth: 3,
action: Action::Create(Create {
from: Address::from(6),
value: U256::from(7),
gas: U256::from(8),
init: vec![0x9]
}),
subs: vec![],
result: Res::FailedCreate
}
],
result: Res::Call(CallResult {
gas_used: U256::from(10),
output: vec![0x11, 0x12]
})
};
let encoded = encode(&trace);
let decoded: Trace = decode(&encoded);
assert_eq!(trace, decoded);
}
#[test]
fn traces_bloom() {
let trace = Trace {
depth: 2,
action: Action::Call(Call {
from: Address::from(1),
to: Address::from(2),
value: U256::from(3),
gas: U256::from(4),
input: vec![0x5],
call_type: CallType::Call,
}),
subs: vec![
Trace {
depth: 3,
action: Action::Create(Create {
from: Address::from(6),
value: U256::from(7),
gas: U256::from(8),
init: vec![0x9]
}),
subs: vec![],
result: Res::Create(CreateResult {
gas_used: 10.into(),
code: vec![],
address: 15.into(),
}),
},
Trace {
depth: 3,
action: Action::Suicide(Suicide {
address: 101.into(),
refund_address: 102.into(),
balance: 0.into(),
}),
subs: vec![],
result: Res::None,
}
],
result: Res::Call(CallResult {
gas_used: U256::from(10),
output: vec![0x11, 0x12]
})
};
let bloom = trace.bloom();
// right now only addresses are bloomed
assert!(bloom.contains_bloomed(&Address::from(1).sha3()));
assert!(bloom.contains_bloomed(&Address::from(2).sha3()));
assert!(!bloom.contains_bloomed(&Address::from(20).sha3()));
assert!(bloom.contains_bloomed(&Address::from(6).sha3()));
assert!(bloom.contains_bloomed(&Address::from(15).sha3()));
assert!(bloom.contains_bloomed(&Address::from(101).sha3()));
assert!(bloom.contains_bloomed(&Address::from(102).sha3()));
assert!(!bloom.contains_bloomed(&Address::from(103).sha3()));
}
}

View File

@ -322,6 +322,74 @@ impl<K, V> BinaryConvertable for BTreeMap<K, V> where K : BinaryConvertable + Or
}
}
impl<T> BinaryConvertable for VecDeque<T> where T: BinaryConvertable {
fn size(&self) -> usize {
match T::len_params() {
0 => mem::size_of::<T>() * self.len(),
_ => self.iter().fold(0usize, |acc, t| acc + t.size()),
}
}
fn to_bytes(&self, buffer: &mut [u8], length_stack: &mut VecDeque<usize>) -> Result<(), BinaryConvertError> {
let mut offset = 0usize;
for item in self.iter() {
let next_size = match T::len_params() {
0 => mem::size_of::<T>(),
_ => { let size = item.size(); length_stack.push_back(size); size },
};
if next_size > 0 {
let item_end = offset + next_size;
try!(item.to_bytes(&mut buffer[offset..item_end], length_stack));
offset = item_end;
}
}
Ok(())
}
fn from_bytes(buffer: &[u8], length_stack: &mut VecDeque<usize>) -> Result<Self, BinaryConvertError> {
let mut index = 0;
let mut result = Self::with_capacity(
match T::len_params() {
0 => buffer.len() / mem::size_of::<T>(),
_ => 128,
});
if buffer.len() == 0 { return Ok(result); }
loop {
let next_size = match T::len_params() {
0 => mem::size_of::<T>(),
_ => try!(length_stack.pop_front().ok_or(BinaryConvertError::length())),
};
let item = if next_size == 0 {
try!(T::from_empty_bytes())
}
else {
try!(T::from_bytes(&buffer[index..index+next_size], length_stack))
};
result.push_back(item);
index = index + next_size;
if index == buffer.len() { break; }
if index + next_size > buffer.len() {
return Err(BinaryConvertError::boundaries())
}
}
Ok(result)
}
fn from_empty_bytes() -> Result<Self, BinaryConvertError> {
Ok(Self::new())
}
fn len_params() -> usize {
1
}
}
//
impl<T> BinaryConvertable for Vec<T> where T: BinaryConvertable {
fn size(&self) -> usize {
match T::len_params() {

View File

@ -431,7 +431,7 @@ fn rpc_eth_call() {
logs: vec![],
contracts_created: vec![],
output: vec![0x12, 0x34, 0xff],
trace: None,
trace: vec![],
vm_trace: None,
state_diff: None,
});
@ -466,7 +466,7 @@ fn rpc_eth_call_default_block() {
logs: vec![],
contracts_created: vec![],
output: vec![0x12, 0x34, 0xff],
trace: None,
trace: vec![],
vm_trace: None,
state_diff: None,
});
@ -500,7 +500,7 @@ fn rpc_eth_estimate_gas() {
logs: vec![],
contracts_created: vec![],
output: vec![0x12, 0x34, 0xff],
trace: None,
trace: vec![],
vm_trace: None,
state_diff: None,
});
@ -535,7 +535,7 @@ fn rpc_eth_estimate_gas_default_block() {
logs: vec![],
contracts_created: vec![],
output: vec![0x12, 0x34, 0xff],
trace: None,
trace: vec![],
vm_trace: None,
state_diff: None,
});

View File

@ -17,7 +17,7 @@
use std::collections::BTreeMap;
use serde::{Serialize, Serializer};
use ethcore::trace::trace;
use ethcore::trace::{Trace as EthTrace, LocalizedTrace as EthLocalizedTrace};
use ethcore::trace::{FlatTrace, LocalizedTrace as EthLocalizedTrace};
use ethcore::trace as et;
use ethcore::state_diff;
use ethcore::account_diff;
@ -458,23 +458,24 @@ impl From<EthLocalizedTrace> for LocalizedTrace {
/// Trace
#[derive(Debug, Serialize)]
pub struct Trace {
/// Depth within the call trace tree.
depth: usize,
/// Trace address
#[serde(rename="traceAddress")]
trace_address: Vec<U256>,
/// Subtraces
subtraces: U256,
/// Action
action: Action,
/// Result
result: Res,
/// Subtraces
subtraces: Vec<Trace>,
}
impl From<EthTrace> for Trace {
fn from(t: EthTrace) -> Self {
impl From<FlatTrace> for Trace {
fn from(t: FlatTrace) -> Self {
Trace {
depth: t.depth.into(),
trace_address: t.trace_address.into_iter().map(Into::into).collect(),
subtraces: t.subtraces.into(),
action: t.action.into(),
result: t.result.into(),
subtraces: t.subs.into_iter().map(Into::into).collect(),
}
}
}
@ -485,7 +486,7 @@ pub struct TraceResults {
/// The output of the call/create
pub output: Vec<u8>,
/// The transaction trace.
pub trace: Option<Trace>,
pub trace: Vec<Trace>,
/// The transaction trace.
#[serde(rename="vmTrace")]
pub vm_trace: Option<VMTrace>,
@ -498,7 +499,7 @@ impl From<Executed> for TraceResults {
fn from(t: Executed) -> Self {
TraceResults {
output: t.output.into(),
trace: t.trace.map(Into::into),
trace: t.trace.into_iter().map(Into::into).collect(),
vm_trace: t.vm_trace.map(Into::into),
state_diff: t.state_diff.map(Into::into),
}
@ -516,12 +517,12 @@ mod tests {
fn should_serialize_trace_results() {
let r = TraceResults {
output: vec![0x60],
trace: None,
trace: vec![],
vm_trace: None,
state_diff: None,
};
let serialized = serde_json::to_string(&r).unwrap();
assert_eq!(serialized, r#"{"output":[96],"trace":null,"vmTrace":null,"stateDiff":null}"#);
assert_eq!(serialized, r#"{"output":[96],"trace":[],"vmTrace":null,"stateDiff":null}"#);
}
#[test]