Merge pull request #772 from ethcore/tracing

Tracing implemented.
This commit is contained in:
Gav Wood 2016-03-19 23:52:23 +01:00
commit c729f9d9ca
15 changed files with 456 additions and 127 deletions

View File

@ -24,7 +24,6 @@ use state::*;
use verification::PreverifiedBlock; use verification::PreverifiedBlock;
/// A block, encoded as it is on the block chain. /// A block, encoded as it is on the block chain.
// TODO: rename to Block
#[derive(Default, Debug, Clone)] #[derive(Default, Debug, Clone)]
pub struct Block { pub struct Block {
/// The header of this block. /// The header of this block.
@ -76,8 +75,6 @@ impl Decodable for Block {
} }
/// Internal type for a block's common elements. /// Internal type for a block's common elements.
// TODO: rename to ExecutedBlock
// TODO: use BareBlock
#[derive(Debug)] #[derive(Debug)]
pub struct ExecutedBlock { pub struct ExecutedBlock {
base: Block, base: Block,
@ -85,6 +82,7 @@ pub struct ExecutedBlock {
receipts: Vec<Receipt>, receipts: Vec<Receipt>,
transactions_set: HashSet<H256>, transactions_set: HashSet<H256>,
state: State, state: State,
traces: Option<Vec<Trace>>,
} }
/// A set of references to `ExecutedBlock` fields that are publicly accessible. /// A set of references to `ExecutedBlock` fields that are publicly accessible.
@ -99,11 +97,21 @@ pub struct BlockRefMut<'a> {
pub receipts: &'a Vec<Receipt>, pub receipts: &'a Vec<Receipt>,
/// State. /// State.
pub state: &'a mut State, pub state: &'a mut State,
/// Traces.
pub traces: &'a Option<Vec<Trace>>,
} }
impl ExecutedBlock { impl ExecutedBlock {
/// Create a new block from the given `state`. /// 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. /// Get a structure containing individual references to all public fields.
pub fn fields(&mut self) -> BlockRefMut { pub fn fields(&mut self) -> BlockRefMut {
@ -113,6 +121,7 @@ impl ExecutedBlock {
uncles: &self.base.uncles, uncles: &self.base.uncles,
state: &mut self.state, state: &mut self.state,
receipts: &self.receipts, receipts: &self.receipts,
traces: &self.traces,
} }
} }
} }
@ -134,6 +143,9 @@ pub trait IsBlock {
/// Get all information on receipts in this block. /// Get all information on receipts in this block.
fn receipts(&self) -> &Vec<Receipt> { &self.block().receipts } fn receipts(&self) -> &Vec<Receipt> { &self.block().receipts }
/// Get all information concerning transaction tracing in this block.
fn traces(&self) -> &Option<Vec<Trace>> { &self.block().traces }
/// Get all uncles in this block. /// Get all uncles in this block.
fn uncles(&self) -> &Vec<Header> { &self.block().base.uncles } fn uncles(&self) -> &Vec<Header> { &self.block().base.uncles }
} }
@ -171,9 +183,9 @@ pub struct SealedBlock {
impl<'x> OpenBlock<'x> { impl<'x> OpenBlock<'x> {
/// Create a new OpenBlock ready for transaction pushing. /// Create a new OpenBlock ready for transaction pushing.
pub fn new(engine: &'x Engine, db: Box<JournalDB>, 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<JournalDB>, parent: &Header, last_hashes: LastHashes, author: Address, gas_floor_target: U256, extra_data: Bytes) -> Self {
let mut r = OpenBlock { 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, engine: engine,
last_hashes: last_hashes, last_hashes: last_hashes,
}; };
@ -249,11 +261,13 @@ impl<'x> OpenBlock<'x> {
pub fn push_transaction(&mut self, t: SignedTransaction, h: Option<H256>) -> Result<&Receipt, Error> { pub fn push_transaction(&mut self, t: SignedTransaction, h: Option<H256>) -> Result<&Receipt, Error> {
let env_info = self.env_info(); let env_info = self.env_info();
// info!("env_info says gas_used={}", env_info.gas_used); // info!("env_info says gas_used={}", env_info.gas_used);
match self.block.state.apply(&env_info, self.engine, &t) { match self.block.state.apply(&env_info, self.engine, &t, self.block.traces.is_some()) {
Ok(receipt) => { Ok(outcome) => {
self.block.transactions_set.insert(h.unwrap_or_else(||t.hash())); self.block.transactions_set.insert(h.unwrap_or_else(||t.hash()));
self.block.base.transactions.push(t); 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()) Ok(&self.block.receipts.last().unwrap())
} }
Err(x) => Err(From::from(x)) Err(x) => Err(From::from(x))
@ -339,7 +353,7 @@ impl IsBlock for SealedBlock {
} }
/// Enact the block given by block header, transactions and uncles /// Enact the block given by block header, transactions and uncles
pub fn enact(header: &Header, transactions: &[SignedTransaction], uncles: &[Header], engine: &Engine, db: Box<JournalDB>, parent: &Header, last_hashes: LastHashes) -> Result<ClosedBlock, Error> { pub fn enact(header: &Header, transactions: &[SignedTransaction], uncles: &[Header], engine: &Engine, tracing: bool, db: Box<JournalDB>, parent: &Header, last_hashes: LastHashes) -> Result<ClosedBlock, Error> {
{ {
if ::log::max_log_level() >= ::log::LogLevel::Trace { if ::log::max_log_level() >= ::log::LogLevel::Trace {
let s = State::from_existing(db.spawn(), parent.state_root().clone(), engine.account_start_nonce()); let s = State::from_existing(db.spawn(), parent.state_root().clone(), engine.account_start_nonce());
@ -347,7 +361,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_difficulty(*header.difficulty());
b.set_gas_limit(*header.gas_limit()); b.set_gas_limit(*header.gas_limit());
b.set_timestamp(header.timestamp()); b.set_timestamp(header.timestamp());
@ -357,22 +371,22 @@ pub fn enact(header: &Header, transactions: &[SignedTransaction], uncles: &[Head
} }
/// Enact the block given by `block_bytes` using `engine` on the database `db` with given `parent` block header /// Enact the block given by `block_bytes` using `engine` on the database `db` with given `parent` block header
pub fn enact_bytes(block_bytes: &[u8], engine: &Engine, db: Box<JournalDB>, parent: &Header, last_hashes: LastHashes) -> Result<ClosedBlock, Error> { pub fn enact_bytes(block_bytes: &[u8], engine: &Engine, tracing: bool, db: Box<JournalDB>, parent: &Header, last_hashes: LastHashes) -> Result<ClosedBlock, Error> {
let block = BlockView::new(block_bytes); let block = BlockView::new(block_bytes);
let header = block.header(); let header = block.header();
enact(&header, &block.transactions(), &block.uncles(), engine, db, parent, last_hashes) enact(&header, &block.transactions(), &block.uncles(), engine, tracing, db, parent, last_hashes)
} }
/// Enact the block given by `block_bytes` using `engine` on the database `db` with given `parent` block header /// Enact the block given by `block_bytes` using `engine` on the database `db` with given `parent` block header
pub fn enact_verified(block: &PreverifiedBlock, engine: &Engine, db: Box<JournalDB>, parent: &Header, last_hashes: LastHashes) -> Result<ClosedBlock, Error> { pub fn enact_verified(block: &PreverifiedBlock, engine: &Engine, tracing: bool, db: Box<JournalDB>, parent: &Header, last_hashes: LastHashes) -> Result<ClosedBlock, Error> {
let view = BlockView::new(&block.bytes); let view = BlockView::new(&block.bytes);
enact(&block.header, &block.transactions, &view.uncles(), engine, db, parent, last_hashes) enact(&block.header, &block.transactions, &view.uncles(), engine, tracing, db, parent, last_hashes)
} }
/// Enact the block given by `block_bytes` using `engine` on the database `db` with given `parent` block header. Seal the block aferwards /// Enact the block given by `block_bytes` using `engine` on the database `db` with given `parent` block header. Seal the block aferwards
pub fn enact_and_seal(block_bytes: &[u8], engine: &Engine, db: Box<JournalDB>, parent: &Header, last_hashes: LastHashes) -> Result<SealedBlock, Error> { pub fn enact_and_seal(block_bytes: &[u8], engine: &Engine, tracing: bool, db: Box<JournalDB>, parent: &Header, last_hashes: LastHashes) -> Result<SealedBlock, Error> {
let header = BlockView::new(block_bytes).header_view(); let header = BlockView::new(block_bytes).header_view();
Ok(try!(try!(enact_bytes(block_bytes, engine, db, parent, last_hashes)).seal(engine, header.seal()))) Ok(try!(try!(enact_bytes(block_bytes, engine, tracing, db, parent, last_hashes)).seal(engine, header.seal())))
} }
#[cfg(test)] #[cfg(test)]
@ -391,7 +405,7 @@ mod tests {
let mut db = db_result.take(); let mut db = db_result.take();
engine.spec().ensure_db_good(db.as_hashdb_mut()); engine.spec().ensure_db_good(db.as_hashdb_mut());
let last_hashes = vec![genesis_header.hash()]; 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 = b.close();
let _ = b.seal(engine.deref(), vec![]); let _ = b.seal(engine.deref(), vec![]);
} }
@ -405,14 +419,14 @@ mod tests {
let mut db_result = get_temp_journal_db(); let mut db_result = get_temp_journal_db();
let mut db = db_result.take(); let mut db = db_result.take();
engine.spec().ensure_db_good(db.as_hashdb_mut()); 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_bytes = b.rlp_bytes();
let orig_db = b.drain(); let orig_db = b.drain();
let mut db_result = get_temp_journal_db(); let mut db_result = get_temp_journal_db();
let mut db = db_result.take(); let mut db = db_result.take();
engine.spec().ensure_db_good(db.as_hashdb_mut()); engine.spec().ensure_db_good(db.as_hashdb_mut());
let e = enact_and_seal(&orig_bytes, engine.deref(), db, &genesis_header, vec![genesis_header.hash()]).unwrap(); let e = enact_and_seal(&orig_bytes, engine.deref(), false, db, &genesis_header, vec![genesis_header.hash()]).unwrap();
assert_eq!(e.rlp_bytes(), orig_bytes); assert_eq!(e.rlp_bytes(), orig_bytes);
@ -430,7 +444,7 @@ mod tests {
let mut db_result = get_temp_journal_db(); let mut db_result = get_temp_journal_db();
let mut db = db_result.take(); let mut db = db_result.take();
engine.spec().ensure_db_good(db.as_hashdb_mut()); 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(); let mut uncle1_header = Header::new();
uncle1_header.extra_data = b"uncle1".to_vec(); uncle1_header.extra_data = b"uncle1".to_vec();
let mut uncle2_header = Header::new(); let mut uncle2_header = Header::new();
@ -445,7 +459,7 @@ mod tests {
let mut db_result = get_temp_journal_db(); let mut db_result = get_temp_journal_db();
let mut db = db_result.take(); let mut db = db_result.take();
engine.spec().ensure_db_good(db.as_hashdb_mut()); engine.spec().ensure_db_good(db.as_hashdb_mut());
let e = enact_and_seal(&orig_bytes, engine.deref(), db, &genesis_header, vec![genesis_header.hash()]).unwrap(); let e = enact_and_seal(&orig_bytes, engine.deref(), false, db, &genesis_header, vec![genesis_header.hash()]).unwrap();
let bytes = e.rlp_bytes(); let bytes = e.rlp_bytes();
assert_eq!(bytes, orig_bytes); assert_eq!(bytes, orig_bytes);

View File

@ -54,6 +54,9 @@ impl Default for BlockChainConfig {
/// Interface for querying blocks by hash and by number. /// Interface for querying blocks by hash and by number.
pub trait BlockProvider { pub trait BlockProvider {
/// True if we store full tracing information for transactions.
fn have_tracing(&self) -> bool;
/// Returns true if the given block is known /// Returns true if the given block is known
/// (though not necessarily a part of the canon chain). /// (though not necessarily a part of the canon chain).
fn is_known(&self, hash: &H256) -> bool; fn is_known(&self, hash: &H256) -> bool;
@ -177,6 +180,9 @@ impl BlockProvider for BlockChain {
self.query_extras_exist(hash, &self.block_details) self.query_extras_exist(hash, &self.block_details)
} }
// We do not store tracing information.
fn have_tracing(&self) -> bool { false }
/// Get raw block data /// Get raw block data
fn block(&self, hash: &H256) -> Option<Bytes> { fn block(&self, hash: &H256) -> Option<Bytes> {
{ {

View File

@ -211,7 +211,7 @@ impl<V> Client<V> where V: Verifier {
let last_hashes = self.build_last_hashes(header.parent_hash.clone()); let last_hashes = self.build_last_hashes(header.parent_hash.clone());
let db = self.state_db.lock().unwrap().spawn(); let db = self.state_db.lock().unwrap().spawn();
let enact_result = enact_verified(&block, engine, db, &parent, last_hashes); let enact_result = enact_verified(&block, engine, self.chain.have_tracing(), db, &parent, last_hashes);
if let Err(e) = enact_result { if let Err(e) = enact_result {
warn!(target: "client", "Block import failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e); warn!(target: "client", "Block import failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e);
return Err(()); return Err(());
@ -398,6 +398,7 @@ impl<V> BlockChainClient for Client<V> where V: Verifier {
let mut b = OpenBlock::new( let mut b = OpenBlock::new(
engine, engine,
false, // TODO: this will need to be parameterised once we want to do immediate mining insertion.
self.state_db.lock().unwrap().spawn(), self.state_db.lock().unwrap().spawn(),
match self.chain.block_header(&h) { Some(ref x) => x, None => {return None} }, match self.chain.block_header(&h) { Some(ref x) => x, None => {return None} },
self.build_last_hashes(h.clone()), self.build_last_hashes(h.clone()),

View File

@ -26,3 +26,4 @@ pub use transaction::*;
pub use log_entry::*; pub use log_entry::*;
pub use receipt::*; pub use receipt::*;
pub use action_params::*; pub use action_params::*;
pub use trace::*;

View File

@ -299,7 +299,7 @@ mod tests {
let mut db = db_result.take(); let mut db = db_result.take();
engine.spec().ensure_db_good(db.as_hashdb_mut()); engine.spec().ensure_db_good(db.as_hashdb_mut());
let last_hashes = vec![genesis_header.hash()]; 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 = b.close();
assert_eq!(b.state().balance(&Address::zero()), U256::from_str("4563918244f40000").unwrap()); assert_eq!(b.state().balance(&Address::zero()), U256::from_str("4563918244f40000").unwrap());
} }
@ -312,7 +312,7 @@ mod tests {
let mut db = db_result.take(); let mut db = db_result.take();
engine.spec().ensure_db_good(db.as_hashdb_mut()); engine.spec().ensure_db_good(db.as_hashdb_mut());
let last_hashes = vec![genesis_header.hash()]; 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 mut uncle = Header::new();
let uncle_author = address_from_hex("ef2d6d194084c2de36e0dabfce45d046b37d1106"); let uncle_author = address_from_hex("ef2d6d194084c2de36e0dabfce45d046b37d1106");
uncle.author = uncle_author.clone(); uncle.author = uncle_author.clone();

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: Option<Trace>,
} }
/// Transaction execution result. /// Transaction execution result.
@ -71,38 +79,37 @@ 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> {
/// Basic constructor. /// Basic constructor.
pub fn new(state: &'a mut State, info: &'a EnvInfo, engine: &'a Engine) -> Self { 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 { Executive {
state: state, state: state,
info: info, info: info,
engine: engine, 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,
} }
} }
/// Creates `Externalities` from `Executive`. /// Creates `Externalities` from `Executive`.
pub fn as_externalities<'_>(&'_ mut self, origin_info: OriginInfo, substate: &'_ mut Substate, output: OutputPolicy<'_>) -> Externalities { pub fn as_externalities<'_>(&'_ mut self, origin_info: OriginInfo, substate: &'_ mut Substate, output: OutputPolicy<'_, '_>) -> Externalities {
Externalities::new(self.state, self.info, self.engine, self.depth, origin_info, substate, output) Externalities::new(self.state, self.info, self.engine, self.depth, origin_info, substate, output)
} }
/// This funtion should be used to execute transaction. /// This funtion should be used to execute transaction.
pub fn transact(&'a mut self, t: &SignedTransaction) -> Result<Executed, Error> { pub fn transact(&'a mut self, t: &SignedTransaction, tracing: bool) -> Result<Executed, Error> {
let sender = try!(t.sender()); let sender = try!(t.sender());
let nonce = self.state.nonce(&sender); let nonce = self.state.nonce(&sender);
@ -143,7 +150,7 @@ impl<'a> Executive<'a> {
self.state.inc_nonce(&sender); self.state.inc_nonce(&sender);
self.state.sub_balance(&sender, &U256::from(gas_cost)); 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 { let res = match t.action {
Action::Create => { Action::Create => {
@ -241,15 +248,27 @@ impl<'a> Executive<'a> {
// if destination is a contract, do normal message call // if destination is a contract, do normal message call
// 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(substate.subtraces.is_some());
// transaction tracing stuff. None if there's no tracing.
let mut trace_info = substate.subtraces.as_ref().map(|_| (TraceAction::from_call(&params), self.depth));
let mut trace_output = trace_info.as_ref().map(|_| vec![]);
let res = { let res = {
self.exec_vm(params, &mut unconfirmed_substate, OutputPolicy::Return(output)) self.exec_vm(params, &mut unconfirmed_substate, OutputPolicy::Return(output, trace_output.as_mut()))
}; };
// if there's tracing, make up trace_info's result with trace_output and some arithmetic.
if let Some((TraceAction::Call(ref mut c), _)) = trace_info {
if let Some(output) = trace_output {
c.result = res.as_ref().ok().map(|gas_left| (c.gas - *gas_left, output));
}
}
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, trace_info);
trace!("exec: new substate={:?}\n", substate); trace!("exec: new substate={:?}\n", substate);
res res
} else { } else {
@ -267,7 +286,7 @@ impl<'a> Executive<'a> {
self.state.snapshot(); self.state.snapshot();
// 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(substate.subtraces.is_some());
// create contract and transfer value to it if necessary // create contract and transfer value to it if necessary
let prev_bal = self.state.balance(&params.address); let prev_bal = self.state.balance(&params.address);
@ -278,10 +297,21 @@ impl<'a> Executive<'a> {
self.state.new_contract(&params.address, prev_bal); self.state.new_contract(&params.address, prev_bal);
} }
let mut trace_info = substate.subtraces.as_ref().map(|_| (TraceAction::from_create(&params), self.depth));
let mut trace_output = trace_info.as_ref().map(|_| vec![]);
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(trace_output.as_mut()))
}; };
self.enact_result(&res, substate, unconfirmed_substate);
if let Some((TraceAction::Create(ref mut c), _)) = trace_info {
if let Some(output) = trace_output {
c.result = res.as_ref().ok().map(|gas_left| (c.gas - *gas_left, created, output));
}
}
self.enact_result(&res, substate, unconfirmed_substate, trace_info);
res res
} }
@ -326,7 +356,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: None,
}) })
}, },
_ => { _ => {
@ -337,12 +368,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.subtraces.and_then(|mut v| v.pop()),
}) })
}, },
} }
} }
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, maybe_info: Option<(TraceAction, usize)>) {
match *result { match *result {
Err(evm::Error::OutOfGas) Err(evm::Error::OutOfGas)
| Err(evm::Error::BadJumpDestination {..}) | Err(evm::Error::BadJumpDestination {..})
@ -353,7 +385,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, maybe_info)
} }
} }
} }
@ -391,7 +423,7 @@ mod tests {
state.add_balance(&sender, &U256::from(0x100u64)); state.add_balance(&sender, &U256::from(0x100u64));
let info = EnvInfo::default(); let info = EnvInfo::default();
let engine = TestEngine::new(0, factory); let engine = TestEngine::new(0, factory);
let mut substate = Substate::new(); let mut substate = Substate::new(false);
let gas_left = { let gas_left = {
let mut ex = Executive::new(&mut state, &info, &engine); let mut ex = Executive::new(&mut state, &info, &engine);
@ -408,8 +440,8 @@ mod tests {
// TODO: just test state root. // TODO: just test state root.
} }
evm_test!{test_create_contract: test_create_contract_jit, test_create_contract_int} evm_test!{test_create_contract_out_of_depth: test_create_contract_out_of_depth_jit, test_create_contract_out_of_depth_int}
fn test_create_contract(factory: Factory) { fn test_create_contract_out_of_depth(factory: Factory) {
// code: // code:
// //
// 7c 601080600c6000396000f3006000355415600957005b60203560003555 - push 29 bytes? // 7c 601080600c6000396000f3006000355415600957005b60203560003555 - push 29 bytes?
@ -450,7 +482,7 @@ mod tests {
state.add_balance(&sender, &U256::from(100)); state.add_balance(&sender, &U256::from(100));
let info = EnvInfo::default(); let info = EnvInfo::default();
let engine = TestEngine::new(0, factory); let engine = TestEngine::new(0, factory);
let mut substate = Substate::new(); let mut substate = Substate::new(false);
let gas_left = { let gas_left = {
let mut ex = Executive::new(&mut state, &info, &engine); let mut ex = Executive::new(&mut state, &info, &engine);
@ -462,6 +494,135 @@ mod tests {
assert_eq!(substate.contracts_created.len(), 0); assert_eq!(substate.contracts_created.len(), 0);
} }
evm_test!{test_call_to_create: test_call_to_create_jit, test_call_to_create_int}
fn test_call_to_create(factory: Factory) {
// code:
//
// 7c 601080600c6000396000f3006000355415600957005b60203560003555 - push 29 bytes?
// 60 00 - push 0
// 52
// 60 1d - push 29
// 60 03 - push 3
// 60 17 - push 17
// f0 - create
// 60 00 - push 0
// 55 sstore
//
// other code:
//
// 60 10 - push 16
// 80 - duplicate first stack item
// 60 0c - push 12
// 60 00 - push 0
// 39 - copy current code to memory
// 60 00 - push 0
// f3 - return
let code = "7c601080600c6000396000f3006000355415600957005b60203560003555600052601d60036017f0600055".from_hex().unwrap();
let sender = Address::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap();
let address = contract_address(&sender, &U256::zero());
// TODO: add tests for 'callcreate'
//let next_address = contract_address(&address, &U256::zero());
let mut params = ActionParams::default();
params.address = address.clone();
params.sender = sender.clone();
params.origin = sender.clone();
params.gas = U256::from(100_000);
params.code = Some(code.clone());
params.value = ActionValue::Transfer(U256::from(100));
let mut state_result = get_temp_state();
let mut state = state_result.reference_mut();
state.add_balance(&sender, &U256::from(100));
let info = EnvInfo::default();
let engine = TestEngine::new(5, factory);
let mut substate = Substate::new(true);
let gas_left = {
let mut ex = Executive::new(&mut state, &info, &engine);
let output = BytesRef::Fixed(&mut[0u8;0]);
ex.call(params, &mut substate, output).unwrap()
};
println!("trace: {:?}", substate.subtraces);
let expected_trace = Some(vec![ Trace {
depth: 0,
action: TraceAction::Call(TraceCall {
from: x!("cd1722f3947def4cf144679da39c4c32bdc35681"),
to: x!("b010143a42d5980c7e5ef0e4a4416dc098a4fed3"),
value: x!(100),
gas: x!(100000),
input: vec![],
result: Some((x!(55248), vec![]))
}),
subs: vec![Trace {
depth: 1,
action: TraceAction::Create(TraceCreate {
from: x!("b010143a42d5980c7e5ef0e4a4416dc098a4fed3"),
value: x!(23),
gas: x!(67979),
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: Some((x!(3224), x!("c6d80f262ae5e0f164e5fde365044d7ada2bfa34"), vec![96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53])),
}),
subs: vec![]
}]
} ]);
assert_eq!(substate.subtraces, expected_trace);
assert_eq!(gas_left, U256::from(44_752));
}
evm_test!{test_create_contract: test_create_contract_jit, test_create_contract_int}
fn test_create_contract(factory: Factory) {
// code:
//
// 60 10 - push 16
// 80 - duplicate first stack item
// 60 0c - push 12
// 60 00 - push 0
// 39 - copy current code to memory
// 60 00 - push 0
// f3 - return
let code = "601080600c6000396000f3006000355415600957005b60203560003555".from_hex().unwrap();
let sender = Address::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap();
let address = contract_address(&sender, &U256::zero());
// TODO: add tests for 'callcreate'
//let next_address = contract_address(&address, &U256::zero());
let mut params = ActionParams::default();
params.address = address.clone();
params.sender = sender.clone();
params.origin = sender.clone();
params.gas = U256::from(100_000);
params.code = Some(code.clone());
params.value = ActionValue::Transfer(x!(100));
let mut state_result = get_temp_state();
let mut state = state_result.reference_mut();
state.add_balance(&sender, &U256::from(100));
let info = EnvInfo::default();
let engine = TestEngine::new(5, factory);
let mut substate = Substate::new(true);
let gas_left = {
let mut ex = Executive::new(&mut state, &info, &engine);
ex.create(params.clone(), &mut substate).unwrap()
};
println!("trace: {:?}", substate.subtraces);
let expected_trace = Some(vec![Trace {
depth: 0,
action: TraceAction::Create(TraceCreate {
from: params.sender,
value: x!(100),
gas: params.gas,
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: Some((x!(3224), params.address, vec![96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53])),
}),
subs: vec![]
} ]);
assert_eq!(substate.subtraces, expected_trace);
assert_eq!(gas_left, U256::from(96_776));
}
evm_test!{test_create_contract_value_too_high: test_create_contract_value_too_high_jit, test_create_contract_value_too_high_int} evm_test!{test_create_contract_value_too_high: test_create_contract_value_too_high_jit, test_create_contract_value_too_high_int}
fn test_create_contract_value_too_high(factory: Factory) { fn test_create_contract_value_too_high(factory: Factory) {
// code: // code:
@ -504,7 +665,7 @@ mod tests {
state.add_balance(&sender, &U256::from(100)); state.add_balance(&sender, &U256::from(100));
let info = EnvInfo::default(); let info = EnvInfo::default();
let engine = TestEngine::new(0, factory); let engine = TestEngine::new(0, factory);
let mut substate = Substate::new(); let mut substate = Substate::new(false);
let gas_left = { let gas_left = {
let mut ex = Executive::new(&mut state, &info, &engine); let mut ex = Executive::new(&mut state, &info, &engine);
@ -556,7 +717,7 @@ mod tests {
state.add_balance(&sender, &U256::from(100)); state.add_balance(&sender, &U256::from(100));
let info = EnvInfo::default(); let info = EnvInfo::default();
let engine = TestEngine::new(1024, factory); let engine = TestEngine::new(1024, factory);
let mut substate = Substate::new(); let mut substate = Substate::new(false);
{ {
let mut ex = Executive::new(&mut state, &info, &engine); let mut ex = Executive::new(&mut state, &info, &engine);
@ -617,7 +778,7 @@ mod tests {
let info = EnvInfo::default(); let info = EnvInfo::default();
let engine = TestEngine::new(0, factory); let engine = TestEngine::new(0, factory);
let mut substate = Substate::new(); let mut substate = Substate::new(false);
let gas_left = { let gas_left = {
let mut ex = Executive::new(&mut state, &info, &engine); let mut ex = Executive::new(&mut state, &info, &engine);
@ -662,7 +823,7 @@ mod tests {
state.init_code(&address, code.clone()); state.init_code(&address, code.clone());
let info = EnvInfo::default(); let info = EnvInfo::default();
let engine = TestEngine::new(0, factory); let engine = TestEngine::new(0, factory);
let mut substate = Substate::new(); let mut substate = Substate::new(false);
let gas_left = { let gas_left = {
let mut ex = Executive::new(&mut state, &info, &engine); let mut ex = Executive::new(&mut state, &info, &engine);
@ -699,7 +860,7 @@ mod tests {
let executed = { let executed = {
let mut ex = Executive::new(&mut state, &info, &engine); let mut ex = Executive::new(&mut state, &info, &engine);
ex.transact(&t).unwrap() ex.transact(&t, false).unwrap()
}; };
assert_eq!(executed.gas, U256::from(100_000)); assert_eq!(executed.gas, U256::from(100_000));
@ -732,7 +893,7 @@ mod tests {
let res = { let res = {
let mut ex = Executive::new(&mut state, &info, &engine); let mut ex = Executive::new(&mut state, &info, &engine);
ex.transact(&t) ex.transact(&t, false)
}; };
match res { match res {
@ -763,7 +924,7 @@ mod tests {
let res = { let res = {
let mut ex = Executive::new(&mut state, &info, &engine); let mut ex = Executive::new(&mut state, &info, &engine);
ex.transact(&t) ex.transact(&t, false)
}; };
match res { match res {
@ -796,7 +957,7 @@ mod tests {
let res = { let res = {
let mut ex = Executive::new(&mut state, &info, &engine); let mut ex = Executive::new(&mut state, &info, &engine);
ex.transact(&t) ex.transact(&t, false)
}; };
match res { match res {
@ -829,7 +990,7 @@ mod tests {
let res = { let res = {
let mut ex = Executive::new(&mut state, &info, &engine); let mut ex = Executive::new(&mut state, &info, &engine);
ex.transact(&t) ex.transact(&t, false)
}; };
match res { match res {
@ -859,7 +1020,7 @@ mod tests {
state.add_balance(&sender, &U256::from_str("152d02c7e14af6800000").unwrap()); state.add_balance(&sender, &U256::from_str("152d02c7e14af6800000").unwrap());
let info = EnvInfo::default(); let info = EnvInfo::default();
let engine = TestEngine::new(0, factory); let engine = TestEngine::new(0, factory);
let mut substate = Substate::new(); let mut substate = Substate::new(false);
let result = { let result = {
let mut ex = Executive::new(&mut state, &info, &engine); let mut ex = Executive::new(&mut state, &info, &engine);

View File

@ -23,12 +23,12 @@ use evm::{self, Schedule, Ext, ContractCreateResult, MessageCallResult};
use substate::*; use substate::*;
/// Policy for handling output data on `RETURN` opcode. /// Policy for handling output data on `RETURN` opcode.
pub enum OutputPolicy<'a> { pub enum OutputPolicy<'a, 'b> {
/// Return reference to fixed sized output. /// Return reference to fixed sized output.
/// Used for message calls. /// Used for message calls.
Return(BytesRef<'a>), Return(BytesRef<'a>, Option<&'b mut Bytes>),
/// Init new contract as soon as `RETURN` is called. /// Init new contract as soon as `RETURN` is called.
InitContract InitContract(Option<&'b mut Bytes>),
} }
/// Transaction properties that externalities need to know about. /// Transaction properties that externalities need to know about.
@ -62,18 +62,19 @@ pub struct Externalities<'a> {
origin_info: OriginInfo, origin_info: OriginInfo,
substate: &'a mut Substate, substate: &'a mut Substate,
schedule: Schedule, schedule: Schedule,
output: OutputPolicy<'a> output: OutputPolicy<'a, 'a>
} }
impl<'a> Externalities<'a> { impl<'a> Externalities<'a> {
/// Basic `Externalities` constructor. /// Basic `Externalities` constructor.
pub fn new(state: &'a mut State, pub fn new(state: &'a mut State,
env_info: &'a EnvInfo, env_info: &'a EnvInfo,
engine: &'a Engine, engine: &'a Engine,
depth: usize, depth: usize,
origin_info: OriginInfo, origin_info: OriginInfo,
substate: &'a mut Substate, substate: &'a mut Substate,
output: OutputPolicy<'a>) -> Self { output: OutputPolicy<'a, 'a>
) -> Self {
Externalities { Externalities {
state: state, state: state,
env_info: env_info, env_info: env_info,
@ -190,20 +191,31 @@ impl<'a> Ext for Externalities<'a> {
#[cfg_attr(feature="dev", allow(match_ref_pats))] #[cfg_attr(feature="dev", allow(match_ref_pats))]
fn ret(&mut self, gas: &U256, data: &[u8]) -> Result<U256, evm::Error> { fn ret(&mut self, gas: &U256, data: &[u8]) -> Result<U256, evm::Error> {
match &mut self.output { let handle_copy = |to: &mut Option<&mut Bytes>| {
&mut OutputPolicy::Return(BytesRef::Fixed(ref mut slice)) => unsafe { to.as_mut().map(|b| **b = data.to_owned());
};
match self.output {
OutputPolicy::Return(BytesRef::Fixed(ref mut slice), ref mut copy) => {
handle_copy(copy);
let len = cmp::min(slice.len(), data.len()); let len = cmp::min(slice.len(), data.len());
ptr::copy(data.as_ptr(), slice.as_mut_ptr(), len); unsafe {
ptr::copy(data.as_ptr(), slice.as_mut_ptr(), len);
}
Ok(*gas) Ok(*gas)
}, },
&mut OutputPolicy::Return(BytesRef::Flexible(ref mut vec)) => unsafe { OutputPolicy::Return(BytesRef::Flexible(ref mut vec), ref mut copy) => {
handle_copy(copy);
vec.clear(); vec.clear();
vec.reserve(data.len()); vec.reserve(data.len());
ptr::copy(data.as_ptr(), vec.as_mut_ptr(), data.len()); unsafe {
vec.set_len(data.len()); ptr::copy(data.as_ptr(), vec.as_mut_ptr(), data.len());
vec.set_len(data.len());
}
Ok(*gas) Ok(*gas)
}, },
&mut OutputPolicy::InitContract => { OutputPolicy::InitContract(ref mut copy) => {
let return_cost = U256::from(data.len()) * U256::from(self.schedule.create_data_gas); let return_cost = U256::from(data.len()) * U256::from(self.schedule.create_data_gas);
if return_cost > *gas { if return_cost > *gas {
return match self.schedule.exceptional_failed_code_deposit { return match self.schedule.exceptional_failed_code_deposit {
@ -211,14 +223,16 @@ impl<'a> Ext for Externalities<'a> {
false => Ok(*gas) false => Ok(*gas)
} }
} }
handle_copy(copy);
let mut code = vec![]; let mut code = vec![];
code.reserve(data.len()); code.reserve(data.len());
unsafe { unsafe {
ptr::copy(data.as_ptr(), code.as_mut_ptr(), data.len()); ptr::copy(data.as_ptr(), code.as_mut_ptr(), data.len());
code.set_len(data.len()); code.set_len(data.len());
} }
let address = &self.origin_info.address; self.state.init_code(&self.origin_info.address, code);
self.state.init_code(address, code);
Ok(*gas - return_cost) Ok(*gas - return_cost)
} }
} }
@ -312,7 +326,7 @@ mod tests {
TestSetup { TestSetup {
state: get_temp_state(), state: get_temp_state(),
engine: get_test_spec().to_engine().unwrap(), engine: get_test_spec().to_engine().unwrap(),
sub_state: Substate::new(), sub_state: Substate::new(false),
env_info: get_test_env_info() env_info: get_test_env_info()
} }
} }
@ -323,7 +337,7 @@ mod tests {
let mut setup = TestSetup::new(); let mut setup = TestSetup::new();
let state = setup.state.reference_mut(); let state = setup.state.reference_mut();
let ext = Externalities::new(state, &setup.env_info, &*setup.engine, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract); let ext = Externalities::new(state, &setup.env_info, &*setup.engine, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None));
assert_eq!(ext.env_info().number, 100); assert_eq!(ext.env_info().number, 100);
} }
@ -332,7 +346,7 @@ mod tests {
fn can_return_block_hash_no_env() { fn can_return_block_hash_no_env() {
let mut setup = TestSetup::new(); let mut setup = TestSetup::new();
let state = setup.state.reference_mut(); let state = setup.state.reference_mut();
let ext = Externalities::new(state, &setup.env_info, &*setup.engine, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract); let ext = Externalities::new(state, &setup.env_info, &*setup.engine, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None));
let hash = ext.blockhash(&U256::from_str("0000000000000000000000000000000000000000000000000000000000120000").unwrap()); let hash = ext.blockhash(&U256::from_str("0000000000000000000000000000000000000000000000000000000000120000").unwrap());
@ -351,7 +365,7 @@ mod tests {
env_info.last_hashes.push(test_hash.clone()); env_info.last_hashes.push(test_hash.clone());
} }
let state = setup.state.reference_mut(); let state = setup.state.reference_mut();
let ext = Externalities::new(state, &setup.env_info, &*setup.engine, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract); let ext = Externalities::new(state, &setup.env_info, &*setup.engine, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None));
let hash = ext.blockhash(&U256::from_str("0000000000000000000000000000000000000000000000000000000000120000").unwrap()); let hash = ext.blockhash(&U256::from_str("0000000000000000000000000000000000000000000000000000000000120000").unwrap());
@ -363,7 +377,7 @@ mod tests {
fn can_call_fail_empty() { fn can_call_fail_empty() {
let mut setup = TestSetup::new(); let mut setup = TestSetup::new();
let state = setup.state.reference_mut(); let state = setup.state.reference_mut();
let mut ext = Externalities::new(state, &setup.env_info, &*setup.engine, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract); let mut ext = Externalities::new(state, &setup.env_info, &*setup.engine, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None));
let mut output = vec![]; let mut output = vec![];
@ -387,7 +401,7 @@ mod tests {
let state = setup.state.reference_mut(); let state = setup.state.reference_mut();
{ {
let mut ext = Externalities::new(state, &setup.env_info, &*setup.engine, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract); let mut ext = Externalities::new(state, &setup.env_info, &*setup.engine, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None));
ext.log(log_topics, &log_data); ext.log(log_topics, &log_data);
} }
@ -402,7 +416,7 @@ mod tests {
let state = setup.state.reference_mut(); let state = setup.state.reference_mut();
{ {
let mut ext = Externalities::new(state, &setup.env_info, &*setup.engine, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract); let mut ext = Externalities::new(state, &setup.env_info, &*setup.engine, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None));
ext.suicide(&refund_account); ext.suicide(&refund_account);
} }

View File

@ -75,7 +75,7 @@ impl<'a> TestExt<'a> {
depth: usize, depth: usize,
origin_info: OriginInfo, origin_info: OriginInfo,
substate: &'a mut Substate, substate: &'a mut Substate,
output: OutputPolicy<'a>, output: OutputPolicy<'a, 'a>,
address: Address) -> Self { address: Address) -> Self {
TestExt { TestExt {
contract_address: contract_address(&address, &state.nonce(&address)), contract_address: contract_address(&address, &state.nonce(&address)),
@ -227,19 +227,21 @@ fn do_json_test_for(vm: &VMType, json_data: &[u8]) -> Vec<String> {
let out_of_gas = test.find("callcreates").map(|_calls| { let out_of_gas = test.find("callcreates").map(|_calls| {
}).is_none(); }).is_none();
let mut substate = Substate::new(); let mut substate = Substate::new(false);
let mut output = vec![]; let mut output = vec![];
// execute // execute
let (res, callcreates) = { let (res, callcreates) = {
let mut ex = TestExt::new(&mut state, let mut ex = TestExt::new(
&info, &mut state,
&engine, &info,
0, &engine,
OriginInfo::from(&params), 0,
&mut substate, OriginInfo::from(&params),
OutputPolicy::Return(BytesRef::Flexible(&mut output)), &mut substate,
params.address.clone()); OutputPolicy::Return(BytesRef::Flexible(&mut output), None),
params.address.clone()
);
let evm = engine.vm_factory().create(); let evm = engine.vm_factory().create();
let res = evm.exec(params, &mut ex); let res = evm.exec(params, &mut ex);
(res, ex.callcreates) (res, ex.callcreates)

View File

@ -69,7 +69,7 @@ pub fn json_chain_test(json_data: &[u8], era: ChainEra) -> Vec<String> {
let mut state = state_result.reference_mut(); let mut state = state_result.reference_mut();
state.populate_from(pre); state.populate_from(pre);
state.commit(); state.commit();
let res = state.apply(&env, engine.deref(), &t); let res = state.apply(&env, engine.deref(), &t, false);
if fail_unless(state.root() == &post_state_root) { if fail_unless(state.root() == &post_state_root) {
println!("!!! {}: State mismatch (got: {}, expect: {}):", name, state.root(), post_state_root); println!("!!! {}: State mismatch (got: {}, expect: {}):", name, state.root(), post_state_root);
@ -80,9 +80,9 @@ pub fn json_chain_test(json_data: &[u8], era: ChainEra) -> Vec<String> {
} }
if let Ok(r) = res { if let Ok(r) = res {
if fail_unless(logs == r.logs) { if fail_unless(logs == r.receipt.logs) {
println!("!!! {}: Logs mismatch:", name); println!("!!! {}: Logs mismatch:", name);
println!("Got:\n{:?}", r.logs); println!("Got:\n{:?}", r.receipt.logs);
println!("Expect:\n{:?}", logs); println!("Expect:\n{:?}", logs);
} }
} }

View File

@ -97,6 +97,7 @@ pub mod filter;
pub mod header; pub mod header;
pub mod service; pub mod service;
pub mod log_entry; pub mod log_entry;
pub mod trace;
pub mod spec; pub mod spec;
pub mod transaction; pub mod transaction;
pub mod views; pub mod views;

View File

@ -26,8 +26,16 @@ use pod_account::*;
use pod_state::PodState; use pod_state::PodState;
//use state_diff::*; // TODO: uncomment once to_pod() works correctly. //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<Trace>,
}
/// Result type for the execution ("application") of a transaction. /// Result type for the execution ("application") of a transaction.
pub type ApplyResult = Result<Receipt, Error>; pub type ApplyResult = Result<ApplyOutcome, Error>;
/// Representation of the entire state of all accounts in the system. /// Representation of the entire state of all accounts in the system.
pub struct State { pub struct State {
@ -209,17 +217,17 @@ impl State {
/// Execute a given transaction. /// Execute a given transaction.
/// This will change the state accordingly. /// 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 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. // TODO uncomment once to_pod() works correctly.
// trace!("Applied transaction. Diff:\n{}\n", StateDiff::diff_pod(&old, &self.to_pod())); // trace!("Applied transaction. Diff:\n{}\n", StateDiff::diff_pod(&old, &self.to_pod()));
self.commit(); self.commit();
let receipt = Receipt::new(self.root().clone(), e.cumulative_gas_used, e.logs); let receipt = Receipt::new(self.root().clone(), e.cumulative_gas_used, e.logs);
// trace!("Transaction receipt: {:?}", receipt); // 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. /// Commit accounts to SecTrieDBMut. This is similar to cpp-ethereum's dev::eth::commit.

View File

@ -23,37 +23,45 @@ use common::*;
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 or `None` if we're not tracing.
pub subtraces: Option<Vec<Trace>>,
} }
impl Substate { impl Substate {
/// Creates new substate. /// Creates new substate.
pub fn new() -> Self { pub fn new(tracing: bool) -> Self {
Substate { Substate {
suicides: HashSet::new(), suicides: Default::default(),
logs: vec![], logs: Default::default(),
sstore_clears_count: U256::zero(), sstore_clears_count: Default::default(),
contracts_created: vec![] contracts_created: Default::default(),
subtraces: if tracing {Some(vec![])} else {None},
} }
} }
/// 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, maybe_info: Option<(TraceAction, usize)>) {
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());
if let Some(info) = maybe_info {
self.subtraces.as_mut().expect("maybe_action is Some: so we must be tracing: qed").push(Trace {
action: info.0,
depth: info.1,
subs: s.subtraces.expect("maybe_action is Some: so we must be tracing: qed"),
});
}
} }
} }
@ -64,13 +72,13 @@ mod tests {
#[test] #[test]
fn created() { fn created() {
let sub_state = Substate::new(); let sub_state = Substate::new(false);
assert_eq!(sub_state.suicides.len(), 0); assert_eq!(sub_state.suicides.len(), 0);
} }
#[test] #[test]
fn accrue() { fn accrue() {
let mut sub_state = Substate::new(); let mut sub_state = Substate::new(false);
sub_state.contracts_created.push(address_from_u64(1u64)); sub_state.contracts_created.push(address_from_u64(1u64));
sub_state.logs.push(LogEntry { sub_state.logs.push(LogEntry {
address: address_from_u64(1u64), address: address_from_u64(1u64),
@ -80,7 +88,7 @@ mod tests {
sub_state.sstore_clears_count = x!(5); sub_state.sstore_clears_count = x!(5);
sub_state.suicides.insert(address_from_u64(10u64)); sub_state.suicides.insert(address_from_u64(10u64));
let mut sub_state_2 = Substate::new(); let mut sub_state_2 = Substate::new(false);
sub_state_2.contracts_created.push(address_from_u64(2u64)); sub_state_2.contracts_created.push(address_from_u64(2u64));
sub_state_2.logs.push(LogEntry { sub_state_2.logs.push(LogEntry {
address: address_from_u64(1u64), address: address_from_u64(1u64),
@ -89,7 +97,7 @@ mod tests {
}); });
sub_state_2.sstore_clears_count = x!(7); sub_state_2.sstore_clears_count = x!(7);
sub_state.accrue(sub_state_2); sub_state.accrue(sub_state_2, None);
assert_eq!(sub_state.contracts_created.len(), 2); assert_eq!(sub_state.contracts_created.len(), 2);
assert_eq!(sub_state.sstore_clears_count, x!(12)); assert_eq!(sub_state.sstore_clears_count, x!(12));
assert_eq!(sub_state.suicides.len(), 1); assert_eq!(sub_state.suicides.len(), 1);

111
ethcore/src/trace.rs Normal file
View File

@ -0,0 +1,111 @@
// 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/>.
//! Tracing datatypes.
use common::*;
/// Description of a _call_ action, either a `CALL` operation or a message transction.
#[derive(Debug, Clone, PartialEq)]
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, PartialEq)]
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, PartialEq)]
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, PartialEq)]
/// 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: TraceAction,
/// The sub traces for each interior action performed as part of this call.
pub subs: Vec<Trace>,
}
impl Default for Trace {
fn default() -> Trace {
Trace {
depth: 0,
action: TraceAction::Unknown,
subs: vec![],
}
}
}
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(),
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,
})
}
/// 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(),
value: match p.value { ActionValue::Transfer(ref x) | ActionValue::Apparent(ref x) => x.clone() },
gas: p.gas.clone(),
init: p.code.clone().unwrap_or(vec![]),
result: None,
})
}
}

View File

@ -278,6 +278,8 @@ mod tests {
} }
impl BlockProvider for TestBlockChain { impl BlockProvider for TestBlockChain {
fn have_tracing(&self) -> bool { false }
fn is_known(&self, hash: &H256) -> bool { fn is_known(&self, hash: &H256) -> bool {
self.blocks.contains_key(hash) self.blocks.contains_key(hash)
} }