Tracing implemented.

TODO:
- make it optional;
- track output;
- usher through to level higher than ExecutionResult.
This commit is contained in:
Gav Wood 2016-03-18 23:49:12 +01:00
parent 2309e19fd9
commit bd338a5741
3 changed files with 115 additions and 23 deletions

View File

@ -65,7 +65,7 @@ pub enum Error {
/// Evm result. /// 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<U256, Error>; pub type Result = result::Result<U256, Error>;
/// Evm interface. /// Evm interface.

View File

@ -41,26 +41,34 @@ pub fn contract_address(address: &Address, nonce: &U256) -> Address {
pub struct Executed { pub struct Executed {
/// Gas paid up front for execution of transaction. /// Gas paid up front for execution of transaction.
pub gas: U256, pub gas: U256,
/// Gas used during execution of transaction. /// Gas used during execution of transaction.
pub gas_used: U256, pub gas_used: U256,
/// Gas refunded after the execution of transaction. /// Gas refunded after the execution of transaction.
/// To get gas that was required up front, add `refunded` and `gas_used`. /// To get gas that was required up front, add `refunded` and `gas_used`.
pub refunded: U256, pub refunded: U256,
/// Cumulative gas used in current block so far. /// Cumulative gas used in current block so far.
/// ///
/// `cumulative_gas_used = gas_used(t0) + gas_used(t1) + ... gas_used(tn)` /// `cumulative_gas_used = gas_used(t0) + gas_used(t1) + ... gas_used(tn)`
/// ///
/// where `tn` is current transaction. /// where `tn` is current transaction.
pub cumulative_gas_used: U256, pub cumulative_gas_used: U256,
/// Vector of logs generated by transaction. /// Vector of logs generated by transaction.
pub logs: Vec<LogEntry>, pub logs: Vec<LogEntry>,
/// Addresses of contracts created during execution of transaction. /// Addresses of contracts created during execution of transaction.
/// Ordered from earliest creation. /// Ordered from earliest creation.
/// ///
/// eg. sender creates contract A and A in constructor creates contract B /// 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. /// B creation ends first, and it will be the first element of the vector.
pub contracts_created: Vec<Address> pub contracts_created: Vec<Address>,
/// The trace of this transaction.
pub trace: Vec<TraceItem>,
} }
/// Transaction execution result. /// Transaction execution result.
@ -71,7 +79,7 @@ pub struct Executive<'a> {
state: &'a mut State, state: &'a mut State,
info: &'a EnvInfo, info: &'a EnvInfo,
engine: &'a Engine, engine: &'a Engine,
depth: usize depth: usize,
} }
impl<'a> Executive<'a> { impl<'a> Executive<'a> {
@ -243,13 +251,20 @@ impl<'a> Executive<'a> {
// part of substate that may be reverted // part of substate that may be reverted
let mut unconfirmed_substate = Substate::new(); let mut unconfirmed_substate = Substate::new();
let mut action = TraceAction::from_call(&params);
let res = { let res = {
self.exec_vm(params, &mut unconfirmed_substate, OutputPolicy::Return(output)) 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: sstore-clears={}\n", unconfirmed_substate.sstore_clears_count);
trace!("exec: substate={:?}; unconfirmed_substate={:?}\n", substate, unconfirmed_substate); 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); trace!("exec: new substate={:?}\n", substate);
res res
} else { } else {
@ -278,10 +293,18 @@ impl<'a> Executive<'a> {
self.state.new_contract(&params.address, prev_bal); self.state.new_contract(&params.address, prev_bal);
} }
let mut action = TraceAction::from_create(&params);
let created = params.address.clone();
let res = { let res = {
self.exec_vm(params, &mut unconfirmed_substate, OutputPolicy::InitContract) 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 res
} }
@ -326,7 +349,8 @@ impl<'a> Executive<'a> {
refunded: U256::zero(), refunded: U256::zero(),
cumulative_gas_used: self.info.gas_used + t.gas, cumulative_gas_used: self.info.gas_used + t.gas,
logs: vec![], 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, cumulative_gas_used: self.info.gas_used + gas_used,
logs: substate.logs, logs: substate.logs,
contracts_created: substate.contracts_created, 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 { match *result {
Err(evm::Error::OutOfGas) Err(evm::Error::OutOfGas)
| Err(evm::Error::BadJumpDestination {..}) | Err(evm::Error::BadJumpDestination {..})
@ -353,7 +378,7 @@ impl<'a> Executive<'a> {
}, },
Ok(_) | Err(evm::Error::Internal) => { Ok(_) | Err(evm::Error::Internal) => {
self.state.clear_snapshot(); self.state.clear_snapshot();
substate.accrue(un_substate) substate.accrue(un_substate, action)
} }
} }
} }

View File

@ -17,43 +17,110 @@
//! Execution environment substate. //! Execution environment substate.
use common::*; 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<U256>,
// 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<TraceItem>,
}
impl Default for TraceItem {
fn default() -> TraceItem {
TraceItem {
action: TraceAction::Unknown,
subs: vec![],
}
}
}
/// State changes which should be applied in finalize, /// State changes which should be applied in finalize,
/// after transaction is fully executed. /// after transaction is fully executed.
#[derive(Debug)] #[derive(Debug, Default)]
pub struct Substate { pub struct Substate {
/// Any accounts that have suicided. /// Any accounts that have suicided.
pub suicides: HashSet<Address>, pub suicides: HashSet<Address>,
/// Any logs. /// Any logs.
pub logs: Vec<LogEntry>, pub logs: Vec<LogEntry>,
/// Refund counter of SSTORE nonzero -> zero. /// Refund counter of SSTORE nonzero -> zero.
pub sstore_clears_count: U256, pub sstore_clears_count: U256,
/// Created contracts.
pub contracts_created: Vec<Address>
}
impl Default for Substate { /// Created contracts.
fn default() -> Self { pub contracts_created: Vec<Address>,
Substate::new()
} /// The trace during this execution.
pub trace: Vec<TraceItem>,
} }
impl Substate { impl Substate {
/// Creates new substate. /// Creates new substate.
pub fn new() -> Self { pub fn new() -> Self {
Substate { Substate::default()
suicides: HashSet::new(),
logs: vec![],
sstore_clears_count: U256::zero(),
contracts_created: vec![]
}
} }
/// Merge secondary substate `s` into self, accruing each element correspondingly. /// 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.suicides.extend(s.suicides.into_iter());
self.logs.extend(s.logs.into_iter()); self.logs.extend(s.logs.into_iter());
self.sstore_clears_count = self.sstore_clears_count + s.sstore_clears_count; self.sstore_clears_count = self.sstore_clears_count + s.sstore_clears_count;
self.contracts_created.extend(s.contracts_created.into_iter()); 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,
})
} }
} }