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.
///
/// 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>;
/// Evm interface.

View File

@ -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<LogEntry>,
/// 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<Address>
pub contracts_created: Vec<Address>,
/// The trace of this transaction.
pub trace: Vec<TraceItem>,
}
/// 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(&params);
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(&params.address, prev_bal);
}
let mut action = TraceAction::from_create(&params);
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)
}
}
}

View File

@ -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<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,
/// after transaction is fully executed.
#[derive(Debug)]
#[derive(Debug, Default)]
pub struct Substate {
/// Any accounts that have suicided.
pub suicides: HashSet<Address>,
/// Any logs.
pub logs: Vec<LogEntry>,
/// Refund counter of SSTORE nonzero -> zero.
pub sstore_clears_count: U256,
/// Created contracts.
pub contracts_created: Vec<Address>
}
impl Default for Substate {
fn default() -> Self {
Substate::new()
}
/// Created contracts.
pub contracts_created: Vec<Address>,
/// The trace during this execution.
pub trace: Vec<TraceItem>,
}
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,
})
}
}