From 1bfcbca8af3ce08be7687c54d5d4015cc397463d Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sat, 19 Mar 2016 12:54:34 +0100 Subject: [PATCH] Add doumentation, make tracing optional and expose at OpenBlock level. --- ethcore/src/block.rs | 42 ++++++++++++------ ethcore/src/client/client.rs | 1 + ethcore/src/common.rs | 3 +- ethcore/src/ethereum/ethash.rs | 4 +- ethcore/src/executive.rs | 53 +++++++++++----------- ethcore/src/lib.rs | 1 + ethcore/src/state.rs | 16 +++++-- ethcore/src/substate.rs | 67 +++++++--------------------- ethcore/src/trace.rs | 81 ++++++++++++++++++++++++++++++++++ 9 files changed, 169 insertions(+), 99 deletions(-) create mode 100644 ethcore/src/trace.rs diff --git a/ethcore/src/block.rs b/ethcore/src/block.rs index d4aa35445..5645989eb 100644 --- a/ethcore/src/block.rs +++ b/ethcore/src/block.rs @@ -24,7 +24,6 @@ use state::*; use verification::PreverifiedBlock; /// A block, encoded as it is on the block chain. -// TODO: rename to Block #[derive(Default, Debug, Clone)] pub struct Block { /// The header of this block. @@ -76,8 +75,6 @@ impl Decodable for Block { } /// Internal type for a block's common elements. -// TODO: rename to ExecutedBlock -// TODO: use BareBlock #[derive(Debug)] pub struct ExecutedBlock { base: Block, @@ -85,6 +82,7 @@ pub struct ExecutedBlock { receipts: Vec, transactions_set: HashSet, state: State, + traces: Option>, } /// A set of references to `ExecutedBlock` fields that are publicly accessible. @@ -99,11 +97,21 @@ pub struct BlockRefMut<'a> { pub receipts: &'a Vec, /// State. pub state: &'a mut State, + /// Traces. + pub traces: &'a Option>, } impl ExecutedBlock { /// Create a new block from the given `state`. - fn new(state: State) -> ExecutedBlock { ExecutedBlock { base: Default::default(), receipts: Default::default(), transactions_set: Default::default(), state: state } } + fn new(state: State, tracing: bool) -> ExecutedBlock { + ExecutedBlock { + base: Default::default(), + receipts: Default::default(), + transactions_set: Default::default(), + state: state, + traces: if tracing {Some(Vec::new())} else {None}, + } + } /// Get a structure containing individual references to all public fields. pub fn fields(&mut self) -> BlockRefMut { @@ -113,6 +121,7 @@ impl ExecutedBlock { uncles: &self.base.uncles, state: &mut self.state, receipts: &self.receipts, + traces: &self.traces, } } } @@ -134,6 +143,9 @@ pub trait IsBlock { /// Get all information on receipts in this block. fn receipts(&self) -> &Vec { &self.block().receipts } + /// Get all information concerning transaction tracing in this block. + fn traces(&self) -> &Option> { &self.block().traces } + /// Get all uncles in this block. fn uncles(&self) -> &Vec
{ &self.block().base.uncles } } @@ -171,9 +183,9 @@ pub struct SealedBlock { impl<'x> OpenBlock<'x> { /// Create a new OpenBlock ready for transaction pushing. - pub fn new(engine: &'x Engine, db: Box, parent: &Header, last_hashes: LastHashes, author: Address, gas_floor_target: U256, extra_data: Bytes) -> Self { + pub fn new(engine: &'x Engine, tracing: bool, db: Box, parent: &Header, last_hashes: LastHashes, author: Address, gas_floor_target: U256, extra_data: Bytes) -> Self { let mut r = OpenBlock { - block: ExecutedBlock::new(State::from_existing(db, parent.state_root().clone(), engine.account_start_nonce())), + block: ExecutedBlock::new(State::from_existing(db, parent.state_root().clone(), engine.account_start_nonce()), tracing), engine: engine, last_hashes: last_hashes, }; @@ -249,11 +261,13 @@ impl<'x> OpenBlock<'x> { pub fn push_transaction(&mut self, t: SignedTransaction, h: Option) -> Result<&Receipt, Error> { let env_info = self.env_info(); // info!("env_info says gas_used={}", env_info.gas_used); - match self.block.state.apply(&env_info, self.engine, &t) { - Ok(receipt) => { + match self.block.state.apply(&env_info, self.engine, &t, self.block.traces.is_some()) { + Ok(outcome) => { self.block.transactions_set.insert(h.unwrap_or_else(||t.hash())); self.block.base.transactions.push(t); - self.block.receipts.push(receipt); + 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.receipts.push(outcome.receipt); Ok(&self.block.receipts.last().unwrap()) } Err(x) => Err(From::from(x)) @@ -340,6 +354,8 @@ impl IsBlock for SealedBlock { /// Enact the block given by block header, transactions and uncles pub fn enact(header: &Header, transactions: &[SignedTransaction], uncles: &[Header], engine: &Engine, db: Box, parent: &Header, last_hashes: LastHashes) -> Result { + let tracing = true; // TODO: make param + { if ::log::max_log_level() >= ::log::LogLevel::Trace { let s = State::from_existing(db.spawn(), parent.state_root().clone(), engine.account_start_nonce()); @@ -347,7 +363,7 @@ pub fn enact(header: &Header, transactions: &[SignedTransaction], uncles: &[Head } } - let mut b = OpenBlock::new(engine, db, parent, last_hashes, header.author().clone(), x!(3141562), header.extra_data().clone()); + let mut b = OpenBlock::new(engine, tracing, db, parent, last_hashes, header.author().clone(), x!(3141562), header.extra_data().clone()); b.set_difficulty(*header.difficulty()); b.set_gas_limit(*header.gas_limit()); b.set_timestamp(header.timestamp()); @@ -391,7 +407,7 @@ mod tests { let mut db = db_result.take(); engine.spec().ensure_db_good(db.as_hashdb_mut()); let last_hashes = vec![genesis_header.hash()]; - let b = OpenBlock::new(engine.deref(), db, &genesis_header, last_hashes, Address::zero(), x!(3141562), vec![]); + let b = OpenBlock::new(engine.deref(), false, db, &genesis_header, last_hashes, Address::zero(), x!(3141562), vec![]); let b = b.close(); let _ = b.seal(engine.deref(), vec![]); } @@ -405,7 +421,7 @@ mod tests { let mut db_result = get_temp_journal_db(); let mut db = db_result.take(); engine.spec().ensure_db_good(db.as_hashdb_mut()); - let b = OpenBlock::new(engine.deref(), db, &genesis_header, vec![genesis_header.hash()], Address::zero(), x!(3141562), vec![]).close().seal(engine.deref(), vec![]).unwrap(); + let b = OpenBlock::new(engine.deref(), false, db, &genesis_header, vec![genesis_header.hash()], Address::zero(), x!(3141562), vec![]).close().seal(engine.deref(), vec![]).unwrap(); let orig_bytes = b.rlp_bytes(); let orig_db = b.drain(); @@ -430,7 +446,7 @@ mod tests { let mut db_result = get_temp_journal_db(); let mut db = db_result.take(); engine.spec().ensure_db_good(db.as_hashdb_mut()); - let mut open_block = OpenBlock::new(engine.deref(), db, &genesis_header, vec![genesis_header.hash()], Address::zero(), x!(3141562), vec![]); + let mut open_block = OpenBlock::new(engine.deref(), false, db, &genesis_header, vec![genesis_header.hash()], Address::zero(), x!(3141562), vec![]); let mut uncle1_header = Header::new(); uncle1_header.extra_data = b"uncle1".to_vec(); let mut uncle2_header = Header::new(); diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index c62364dce..4a52429f6 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -398,6 +398,7 @@ impl BlockChainClient for Client where V: Verifier { let mut b = OpenBlock::new( engine, + false, // TODO: this will need to be parameterised once we want to do immediate mining insertion. self.state_db.lock().unwrap().spawn(), match self.chain.block_header(&h) { Some(ref x) => x, None => {return None} }, self.build_last_hashes(h.clone()), diff --git a/ethcore/src/common.rs b/ethcore/src/common.rs index 5235e9f58..e91501217 100644 --- a/ethcore/src/common.rs +++ b/ethcore/src/common.rs @@ -25,4 +25,5 @@ pub use account::*; pub use transaction::*; pub use log_entry::*; pub use receipt::*; -pub use action_params::*; \ No newline at end of file +pub use action_params::*; +pub use trace::*; \ No newline at end of file diff --git a/ethcore/src/ethereum/ethash.rs b/ethcore/src/ethereum/ethash.rs index 406777251..6f854921d 100644 --- a/ethcore/src/ethereum/ethash.rs +++ b/ethcore/src/ethereum/ethash.rs @@ -299,7 +299,7 @@ mod tests { let mut db = db_result.take(); engine.spec().ensure_db_good(db.as_hashdb_mut()); let last_hashes = vec![genesis_header.hash()]; - let b = OpenBlock::new(engine.deref(), db, &genesis_header, last_hashes, Address::zero(), x!(3141562), vec![]); + let b = OpenBlock::new(engine.deref(), false, db, &genesis_header, last_hashes, Address::zero(), x!(3141562), vec![]); let b = b.close(); assert_eq!(b.state().balance(&Address::zero()), U256::from_str("4563918244f40000").unwrap()); } @@ -312,7 +312,7 @@ mod tests { let mut db = db_result.take(); engine.spec().ensure_db_good(db.as_hashdb_mut()); let last_hashes = vec![genesis_header.hash()]; - let mut b = OpenBlock::new(engine.deref(), db, &genesis_header, last_hashes, Address::zero(), x!(3141562), vec![]); + let mut b = OpenBlock::new(engine.deref(), false, db, &genesis_header, last_hashes, Address::zero(), x!(3141562), vec![]); let mut uncle = Header::new(); let uncle_author = address_from_hex("ef2d6d194084c2de36e0dabfce45d046b37d1106"); uncle.author = uncle_author.clone(); diff --git a/ethcore/src/executive.rs b/ethcore/src/executive.rs index d16bc94d9..28f963d65 100644 --- a/ethcore/src/executive.rs +++ b/ethcore/src/executive.rs @@ -68,7 +68,7 @@ pub struct Executed { pub contracts_created: Vec
, /// The trace of this transaction. - pub trace: Vec, + pub trace: Option, } /// Transaction execution result. @@ -85,22 +85,21 @@ pub struct Executive<'a> { impl<'a> Executive<'a> { /// Basic constructor. pub fn new(state: &'a mut State, info: &'a EnvInfo, engine: &'a Engine) -> Self { - Executive::new_with_depth(state, info, engine, 0) - } - - /// Populates executive from parent properties. Increments executive depth. - pub fn from_parent(state: &'a mut State, info: &'a EnvInfo, engine: &'a Engine, depth: usize) -> Self { - Executive::new_with_depth(state, info, engine, depth + 1) - } - - /// Helper constructor. Should be used to create `Executive` with desired depth. - /// Private. - fn new_with_depth(state: &'a mut State, info: &'a EnvInfo, engine: &'a Engine, depth: usize) -> Self { Executive { state: state, info: info, engine: engine, - depth: depth + depth: 0, + } + } + + /// Populates executive from parent properties. Increments executive depth. + pub fn from_parent(state: &'a mut State, info: &'a EnvInfo, engine: &'a Engine, parent_depth: usize) -> Self { + Executive { + state: state, + info: info, + engine: engine, + depth: parent_depth + 1, } } @@ -110,7 +109,7 @@ impl<'a> Executive<'a> { } /// This funtion should be used to execute transaction. - pub fn transact(&'a mut self, t: &SignedTransaction) -> Result { + pub fn transact(&'a mut self, t: &SignedTransaction, tracing: bool) -> Result { let sender = try!(t.sender()); let nonce = self.state.nonce(&sender); @@ -151,7 +150,7 @@ impl<'a> Executive<'a> { self.state.inc_nonce(&sender); self.state.sub_balance(&sender, &U256::from(gas_cost)); - let mut substate = Substate::new(); + let mut substate = Substate::new(tracing); let res = match t.action { Action::Create => { @@ -249,22 +248,22 @@ impl<'a> Executive<'a> { // if destination is a contract, do normal message call // part of substate that may be reverted - let mut unconfirmed_substate = Substate::new(); + let mut unconfirmed_substate = Substate::new(substate.subtraces.is_some()); - let mut action = TraceAction::from_call(¶ms); + let mut action = substate.subtraces.as_ref().map(|_| TraceAction::from_call(¶ms)); let res = { self.exec_vm(params, &mut unconfirmed_substate, OutputPolicy::Return(output)) }; - if let TraceAction::Call(ref mut c) = action { - c.result = res.as_ref().ok().map(|gas_left| c.gas - *gas_left); + if let Some(TraceAction::Call(ref mut c)) = action { + c.result = res.as_ref().ok().map(|gas_left| (c.gas - *gas_left, vec![])); } trace!("exec: sstore-clears={}\n", unconfirmed_substate.sstore_clears_count); trace!("exec: substate={:?}; unconfirmed_substate={:?}\n", substate, unconfirmed_substate); - self.enact_result(&res, substate, unconfirmed_substate, Some(action)); + self.enact_result(&res, substate, unconfirmed_substate, action); trace!("exec: new substate={:?}\n", substate); res } else { @@ -282,7 +281,7 @@ impl<'a> Executive<'a> { self.state.snapshot(); // part of substate that may be reverted - let mut unconfirmed_substate = Substate::new(); + let mut unconfirmed_substate = Substate::new(substate.subtraces.is_some()); // create contract and transfer value to it if necessary let prev_bal = self.state.balance(¶ms.address); @@ -293,18 +292,18 @@ impl<'a> Executive<'a> { self.state.new_contract(¶ms.address, prev_bal); } - let mut action = TraceAction::from_create(¶ms); + let mut action = substate.subtraces.as_ref().map(|_| TraceAction::from_create(¶ms)); let created = params.address.clone(); let res = { self.exec_vm(params, &mut unconfirmed_substate, OutputPolicy::InitContract) }; - if let TraceAction::Create(ref mut c) = action { - c.result = res.as_ref().ok().map(|gas_left| (c.gas - *gas_left, created)); + if let Some(TraceAction::Create(ref mut c)) = action { + c.result = res.as_ref().ok().map(|gas_left| (c.gas - *gas_left, created, vec![])); } - self.enact_result(&res, substate, unconfirmed_substate, Some(action)); + self.enact_result(&res, substate, unconfirmed_substate, action); res } @@ -350,7 +349,7 @@ impl<'a> Executive<'a> { cumulative_gas_used: self.info.gas_used + t.gas, logs: vec![], contracts_created: vec![], - trace: substate.trace, + trace: None, }) }, _ => { @@ -361,7 +360,7 @@ impl<'a> Executive<'a> { cumulative_gas_used: self.info.gas_used + gas_used, logs: substate.logs, contracts_created: substate.contracts_created, - trace: substate.trace, + trace: substate.subtraces.and_then(|mut v| v.pop()), }) }, } diff --git a/ethcore/src/lib.rs b/ethcore/src/lib.rs index 572cda2fa..ffb4ed905 100644 --- a/ethcore/src/lib.rs +++ b/ethcore/src/lib.rs @@ -96,6 +96,7 @@ pub mod filter; pub mod header; pub mod service; pub mod log_entry; +pub mod trace; pub mod spec; pub mod transaction; pub mod views; diff --git a/ethcore/src/state.rs b/ethcore/src/state.rs index cb54654e6..cad29b678 100644 --- a/ethcore/src/state.rs +++ b/ethcore/src/state.rs @@ -26,8 +26,16 @@ use pod_account::*; use pod_state::PodState; //use state_diff::*; // TODO: uncomment once to_pod() works correctly. +/// Used to return information about an `State::apply` operation. +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, +} + /// Result type for the execution ("application") of a transaction. -pub type ApplyResult = Result; +pub type ApplyResult = Result; /// Representation of the entire state of all accounts in the system. pub struct State { @@ -209,17 +217,17 @@ impl State { /// Execute a given transaction. /// This will change the state accordingly. - pub fn apply(&mut self, env_info: &EnvInfo, engine: &Engine, t: &SignedTransaction) -> ApplyResult { + pub fn apply(&mut self, env_info: &EnvInfo, engine: &Engine, t: &SignedTransaction, tracing: bool) -> ApplyResult { // let old = self.to_pod(); - let e = try!(Executive::new(self, env_info, engine).transact(t)); + let e = try!(Executive::new(self, env_info, engine).transact(t, tracing)); // TODO uncomment once to_pod() works correctly. // trace!("Applied transaction. Diff:\n{}\n", StateDiff::diff_pod(&old, &self.to_pod())); self.commit(); let receipt = Receipt::new(self.root().clone(), e.cumulative_gas_used, e.logs); // trace!("Transaction receipt: {:?}", receipt); - Ok(receipt) + Ok(ApplyOutcome{receipt: receipt, trace: e.trace}) } /// Commit accounts to SecTrieDBMut. This is similar to cpp-ethereum's dev::eth::commit. diff --git a/ethcore/src/substate.rs b/ethcore/src/substate.rs index 9880fcd16..4a69cf492 100644 --- a/ethcore/src/substate.rs +++ b/ethcore/src/substate.rs @@ -17,52 +17,9 @@ //! Execution environment substate. use common::*; -#[derive(Debug, Clone)] -pub struct TraceCall { - pub from: Address, - pub to: Address, - pub value: U256, - pub gas: U256, - pub input: Bytes, - pub result: Option, -// pub output: Bytes, -} - -#[derive(Debug, Clone)] -pub struct TraceCreate { - pub from: Address, - pub value: U256, - pub gas: U256, - pub init: Bytes, - pub result: Option<(U256, Address)>, -// pub output: Bytes, -} - -#[derive(Debug, Clone)] -pub enum TraceAction { - Unknown, - Call(TraceCall), - Create(TraceCreate), -} - -#[derive(Debug, Clone)] -pub struct TraceItem { - pub action: TraceAction, - pub subs: Vec, -} - -impl Default for TraceItem { - fn default() -> TraceItem { - TraceItem { - action: TraceAction::Unknown, - subs: vec![], - } - } -} - /// State changes which should be applied in finalize, /// after transaction is fully executed. -#[derive(Debug, Default)] +#[derive(Debug)] pub struct Substate { /// Any accounts that have suicided. pub suicides: HashSet
, @@ -76,14 +33,20 @@ pub struct Substate { /// Created contracts. pub contracts_created: Vec
, - /// The trace during this execution. - pub trace: Vec, + /// The trace during this execution or `None` if we're not tracing. + pub subtraces: Option>, } impl Substate { /// Creates new substate. - pub fn new() -> Self { - Substate::default() + pub fn new(tracing: bool) -> Self { + Substate { + suicides: Default::default(), + logs: Default::default(), + sstore_clears_count: Default::default(), + contracts_created: Default::default(), + subtraces: if tracing {Some(vec![])} else {None}, + } } /// Merge secondary substate `s` into self, accruing each element correspondingly. @@ -93,17 +56,16 @@ impl Substate { self.sstore_clears_count = self.sstore_clears_count + s.sstore_clears_count; self.contracts_created.extend(s.contracts_created.into_iter()); if let Some(action) = maybe_action { - self.trace.push(TraceItem { + self.subtraces.as_mut().expect("maybe_action is Some: so we must be tracing: qed").push(Trace { action: action, - subs: s.trace, + subs: s.subtraces.expect("maybe_action is Some: so we must be tracing: qed"), }); } } } - - impl TraceAction { + /// Compose a `TraceAction` from an `ActionParams`, knowing that the action is a call. pub fn from_call(p: &ActionParams) -> TraceAction { TraceAction::Call(TraceCall { from: p.sender.clone(), @@ -115,6 +77,7 @@ impl TraceAction { }) } + /// Compose a `TraceAction` from an `ActionParams`, knowing that the action is a create. pub fn from_create(p: &ActionParams) -> TraceAction { TraceAction::Create(TraceCreate { from: p.sender.clone(), diff --git a/ethcore/src/trace.rs b/ethcore/src/trace.rs new file mode 100644 index 000000000..546404e96 --- /dev/null +++ b/ethcore/src/trace.rs @@ -0,0 +1,81 @@ +// 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 . + +//! Tracing datatypes. +use common::*; + +/// Description of a _call_ action, either a `CALL` operation or a message transction. +#[derive(Debug, Clone)] +pub struct TraceCall { + /// The sending account. + pub from: Address, + /// The destination account. + pub to: Address, + /// The value transferred to the destination account. + pub value: U256, + /// The gas available for executing the call. + pub gas: U256, + /// The input data provided to the call. + pub input: Bytes, + /// The result of the operation; the gas used and the output data of the call. + pub result: Option<(U256, Bytes)>, +} + +/// Description of a _create_ action, either a `CREATE` operation or a create transction. +#[derive(Debug, Clone)] +pub struct TraceCreate { + /// The address of the creator. + pub from: Address, + /// The value with which the new account is endowed. + pub value: U256, + /// The gas available for the creation init code. + pub gas: U256, + /// The init code. + pub init: Bytes, + /// The result of the operation; tuple of the gas used, the address of the newly created account and its code. + /// NOTE: Presently failed operations are not reported so this will always be `Some`. + pub result: Option<(U256, Address, Bytes)>, +// pub output: Bytes, +} + +/// Description of an action that we trace; will be either a call or a create. +#[derive(Debug, Clone)] +pub enum TraceAction { + /// Action isn't yet known. + Unknown, + /// It's a call action. + Call(TraceCall), + /// It's a create action. + Create(TraceCreate), +} + +#[derive(Debug, Clone)] +/// A trace; includes a description of the action being traced and sub traces of each interior action. +pub struct Trace { + /// The action being performed. + pub action: TraceAction, + /// The sub traces for each interior action performed as part of this call. + pub subs: Vec, +} + +impl Default for Trace { + fn default() -> Trace { + Trace { + action: TraceAction::Unknown, + subs: vec![], + } + } +}