From bd338a5741d5566fddb25f550f637c4aaaddd24b Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Fri, 18 Mar 2016 23:49:12 +0100 Subject: [PATCH] Tracing implemented. TODO: - make it optional; - track output; - usher through to level higher than ExecutionResult. --- ethcore/src/evm/evm.rs | 2 +- ethcore/src/executive.rs | 39 +++++++++++++--- ethcore/src/substate.rs | 97 +++++++++++++++++++++++++++++++++------- 3 files changed, 115 insertions(+), 23 deletions(-) diff --git a/ethcore/src/evm/evm.rs b/ethcore/src/evm/evm.rs index 28eb96f44..c1107f003 100644 --- a/ethcore/src/evm/evm.rs +++ b/ethcore/src/evm/evm.rs @@ -65,7 +65,7 @@ pub enum Error { /// Evm result. /// -/// Returns gas_left if execution is successfull, otherwise error. +/// Returns gas_left if execution is successful, otherwise error. pub type Result = result::Result; /// Evm interface. diff --git a/ethcore/src/executive.rs b/ethcore/src/executive.rs index 782063cb2..9c42f1716 100644 --- a/ethcore/src/executive.rs +++ b/ethcore/src/executive.rs @@ -41,26 +41,34 @@ pub fn contract_address(address: &Address, nonce: &U256) -> Address { pub struct Executed { /// Gas paid up front for execution of transaction. pub gas: U256, + /// Gas used during execution of transaction. pub gas_used: U256, + /// Gas refunded after the execution of transaction. /// To get gas that was required up front, add `refunded` and `gas_used`. pub refunded: U256, + /// Cumulative gas used in current block so far. /// /// `cumulative_gas_used = gas_used(t0) + gas_used(t1) + ... gas_used(tn)` /// /// where `tn` is current transaction. pub cumulative_gas_used: U256, + /// Vector of logs generated by transaction. pub logs: Vec, + /// Addresses of contracts created during execution of transaction. /// Ordered from earliest creation. /// /// eg. sender creates contract A and A in constructor creates contract B /// /// B creation ends first, and it will be the first element of the vector. - pub contracts_created: Vec
+ pub contracts_created: Vec
, + + /// The trace of this transaction. + pub trace: Vec, } /// Transaction execution result. @@ -71,7 +79,7 @@ pub struct Executive<'a> { state: &'a mut State, info: &'a EnvInfo, engine: &'a Engine, - depth: usize + depth: usize, } impl<'a> Executive<'a> { @@ -243,13 +251,20 @@ impl<'a> Executive<'a> { // part of substate that may be reverted let mut unconfirmed_substate = Substate::new(); + let mut action = 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); + } + 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); + + self.enact_result(&res, substate, unconfirmed_substate, action); trace!("exec: new substate={:?}\n", substate); res } else { @@ -278,10 +293,18 @@ impl<'a> Executive<'a> { self.state.new_contract(¶ms.address, prev_bal); } + let mut action = TraceAction::from_create(¶ms); + let created = params.address.clone(); + let res = { self.exec_vm(params, &mut unconfirmed_substate, OutputPolicy::InitContract) }; - self.enact_result(&res, substate, unconfirmed_substate); + + if let TraceAction::Create(ref mut c) = action { + c.result = res.as_ref().ok().map(|gas_left| (c.gas - *gas_left, created)); + } + + self.enact_result(&res, substate, unconfirmed_substate, action); res } @@ -326,7 +349,8 @@ impl<'a> Executive<'a> { refunded: U256::zero(), cumulative_gas_used: self.info.gas_used + t.gas, logs: vec![], - contracts_created: vec![] + contracts_created: vec![], + trace: substate.trace, }) }, _ => { @@ -337,12 +361,13 @@ impl<'a> Executive<'a> { cumulative_gas_used: self.info.gas_used + gas_used, logs: substate.logs, contracts_created: substate.contracts_created, + trace: substate.trace, }) }, } } - fn enact_result(&mut self, result: &evm::Result, substate: &mut Substate, un_substate: Substate) { + fn enact_result(&mut self, result: &evm::Result, substate: &mut Substate, un_substate: Substate, action: TraceAction) { match *result { Err(evm::Error::OutOfGas) | Err(evm::Error::BadJumpDestination {..}) @@ -353,7 +378,7 @@ impl<'a> Executive<'a> { }, Ok(_) | Err(evm::Error::Internal) => { self.state.clear_snapshot(); - substate.accrue(un_substate) + substate.accrue(un_substate, action) } } } diff --git a/ethcore/src/substate.rs b/ethcore/src/substate.rs index 57e35ad2e..08905ac1a 100644 --- a/ethcore/src/substate.rs +++ b/ethcore/src/substate.rs @@ -17,43 +17,110 @@ //! 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)] +#[derive(Debug, Default)] pub struct Substate { /// Any accounts that have suicided. pub suicides: HashSet
, + /// Any logs. pub logs: Vec, + /// Refund counter of SSTORE nonzero -> zero. pub sstore_clears_count: U256, - /// Created contracts. - pub contracts_created: Vec
-} -impl Default for Substate { - fn default() -> Self { - Substate::new() - } + /// Created contracts. + pub contracts_created: Vec
, + + /// The trace during this execution. + pub trace: Vec, } impl Substate { /// Creates new substate. pub fn new() -> Self { - Substate { - suicides: HashSet::new(), - logs: vec![], - sstore_clears_count: U256::zero(), - contracts_created: vec![] - } + Substate::default() } /// Merge secondary substate `s` into self, accruing each element correspondingly. - pub fn accrue(&mut self, s: Substate) { + pub fn accrue(&mut self, s: Substate, action: TraceAction) { self.suicides.extend(s.suicides.into_iter()); self.logs.extend(s.logs.into_iter()); self.sstore_clears_count = self.sstore_clears_count + s.sstore_clears_count; self.contracts_created.extend(s.contracts_created.into_iter()); + self.trace.push(TraceItem { + action: action, + subs: s.trace, + }); + } +} + + + +impl TraceAction { + pub fn from_call(p: &ActionParams) -> TraceAction { + TraceAction::Call(TraceCall { + from: p.sender.clone(), + to: p.address.clone(), + value: match p.value { ActionValue::Transfer(ref x) | ActionValue::Apparent(ref x) => x.clone() }, + gas: p.gas.clone(), + input: p.data.clone().unwrap_or(vec![]), + result: None, + }) + } + + pub fn from_create(p: &ActionParams) -> TraceAction { + TraceAction::Create(TraceCreate { + from: p.sender.clone(), + value: match p.value { ActionValue::Transfer(ref x) | ActionValue::Apparent(ref x) => x.clone() }, + gas: p.gas.clone(), + init: p.data.clone().unwrap_or(vec![]), + result: None, + }) } }