diff --git a/ethcore/src/block.rs b/ethcore/src/block.rs index d4aa35445..144605f89 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)) @@ -339,7 +353,7 @@ 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 { +pub fn enact(header: &Header, transactions: &[SignedTransaction], uncles: &[Header], engine: &Engine, tracing: bool, db: Box, parent: &Header, last_hashes: LastHashes) -> Result { { 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 +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_gas_limit(*header.gas_limit()); 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 -pub fn enact_bytes(block_bytes: &[u8], engine: &Engine, db: Box, parent: &Header, last_hashes: LastHashes) -> Result { +pub fn enact_bytes(block_bytes: &[u8], engine: &Engine, tracing: bool, db: Box, parent: &Header, last_hashes: LastHashes) -> Result { let block = BlockView::new(block_bytes); 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 -pub fn enact_verified(block: &PreverifiedBlock, engine: &Engine, db: Box, parent: &Header, last_hashes: LastHashes) -> Result { +pub fn enact_verified(block: &PreverifiedBlock, engine: &Engine, tracing: bool, db: Box, parent: &Header, last_hashes: LastHashes) -> Result { 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 -pub fn enact_and_seal(block_bytes: &[u8], engine: &Engine, db: Box, parent: &Header, last_hashes: LastHashes) -> Result { +pub fn enact_and_seal(block_bytes: &[u8], engine: &Engine, tracing: bool, db: Box, parent: &Header, last_hashes: LastHashes) -> Result { 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)] @@ -391,7 +405,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,14 +419,14 @@ 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(); let mut db_result = get_temp_journal_db(); let mut db = db_result.take(); 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); @@ -430,7 +444,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(); @@ -445,7 +459,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 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(); assert_eq!(bytes, orig_bytes); diff --git a/ethcore/src/blockchain/blockchain.rs b/ethcore/src/blockchain/blockchain.rs index 40b01c6f9..7e1a8e23f 100644 --- a/ethcore/src/blockchain/blockchain.rs +++ b/ethcore/src/blockchain/blockchain.rs @@ -54,6 +54,9 @@ impl Default for BlockChainConfig { /// Interface for querying blocks by hash and by number. 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 /// (though not necessarily a part of the canon chain). fn is_known(&self, hash: &H256) -> bool; @@ -177,6 +180,9 @@ impl BlockProvider for BlockChain { self.query_extras_exist(hash, &self.block_details) } + // We do not store tracing information. + fn have_tracing(&self) -> bool { false } + /// Get raw block data fn block(&self, hash: &H256) -> Option { { diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index c62364dce..614a74798 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -211,7 +211,7 @@ impl Client where V: Verifier { let last_hashes = self.build_last_hashes(header.parent_hash.clone()); 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 { warn!(target: "client", "Block import failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e); return Err(()); @@ -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/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..2421150e5 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: Option, } /// Transaction execution result. @@ -71,38 +79,37 @@ pub struct Executive<'a> { state: &'a mut State, info: &'a EnvInfo, engine: &'a Engine, - depth: usize + depth: usize, } 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, } } /// 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) } /// 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); @@ -143,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 => { @@ -241,15 +248,27 @@ 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()); + + // transaction tracing stuff. None if there's no tracing. + let mut trace_info = substate.subtraces.as_ref().map(|_| (TraceAction::from_call(¶ms), self.depth)); + let mut trace_output = trace_info.as_ref().map(|_| vec![]); 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: 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); res } else { @@ -267,7 +286,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); @@ -278,10 +297,21 @@ impl<'a> Executive<'a> { self.state.new_contract(¶ms.address, prev_bal); } + let mut trace_info = substate.subtraces.as_ref().map(|_| (TraceAction::from_create(¶ms), self.depth)); + let mut trace_output = trace_info.as_ref().map(|_| vec![]); + let created = params.address.clone(); + 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 } @@ -326,7 +356,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: None, }) }, _ => { @@ -337,12 +368,13 @@ impl<'a> Executive<'a> { cumulative_gas_used: self.info.gas_used + gas_used, logs: substate.logs, 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 { Err(evm::Error::OutOfGas) | Err(evm::Error::BadJumpDestination {..}) @@ -353,7 +385,7 @@ impl<'a> Executive<'a> { }, Ok(_) | Err(evm::Error::Internal) => { 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)); let info = EnvInfo::default(); let engine = TestEngine::new(0, factory); - let mut substate = Substate::new(); + let mut substate = Substate::new(false); let gas_left = { let mut ex = Executive::new(&mut state, &info, &engine); @@ -408,8 +440,8 @@ mod tests { // TODO: just test state root. } - evm_test!{test_create_contract: test_create_contract_jit, test_create_contract_int} - fn test_create_contract(factory: Factory) { + 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_out_of_depth(factory: Factory) { // code: // // 7c 601080600c6000396000f3006000355415600957005b60203560003555 - push 29 bytes? @@ -450,7 +482,7 @@ mod tests { state.add_balance(&sender, &U256::from(100)); let info = EnvInfo::default(); let engine = TestEngine::new(0, factory); - let mut substate = Substate::new(); + let mut substate = Substate::new(false); let gas_left = { let mut ex = Executive::new(&mut state, &info, &engine); @@ -462,6 +494,135 @@ mod tests { 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} fn test_create_contract_value_too_high(factory: Factory) { // code: @@ -504,7 +665,7 @@ mod tests { state.add_balance(&sender, &U256::from(100)); let info = EnvInfo::default(); let engine = TestEngine::new(0, factory); - let mut substate = Substate::new(); + let mut substate = Substate::new(false); let gas_left = { let mut ex = Executive::new(&mut state, &info, &engine); @@ -556,7 +717,7 @@ mod tests { state.add_balance(&sender, &U256::from(100)); let info = EnvInfo::default(); 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); @@ -617,7 +778,7 @@ mod tests { let info = EnvInfo::default(); let engine = TestEngine::new(0, factory); - let mut substate = Substate::new(); + let mut substate = Substate::new(false); let gas_left = { let mut ex = Executive::new(&mut state, &info, &engine); @@ -662,7 +823,7 @@ mod tests { state.init_code(&address, code.clone()); let info = EnvInfo::default(); let engine = TestEngine::new(0, factory); - let mut substate = Substate::new(); + let mut substate = Substate::new(false); let gas_left = { let mut ex = Executive::new(&mut state, &info, &engine); @@ -699,7 +860,7 @@ mod tests { let executed = { 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)); @@ -732,7 +893,7 @@ mod tests { let res = { let mut ex = Executive::new(&mut state, &info, &engine); - ex.transact(&t) + ex.transact(&t, false) }; match res { @@ -763,7 +924,7 @@ mod tests { let res = { let mut ex = Executive::new(&mut state, &info, &engine); - ex.transact(&t) + ex.transact(&t, false) }; match res { @@ -796,7 +957,7 @@ mod tests { let res = { let mut ex = Executive::new(&mut state, &info, &engine); - ex.transact(&t) + ex.transact(&t, false) }; match res { @@ -829,7 +990,7 @@ mod tests { let res = { let mut ex = Executive::new(&mut state, &info, &engine); - ex.transact(&t) + ex.transact(&t, false) }; match res { @@ -859,7 +1020,7 @@ mod tests { state.add_balance(&sender, &U256::from_str("152d02c7e14af6800000").unwrap()); let info = EnvInfo::default(); let engine = TestEngine::new(0, factory); - let mut substate = Substate::new(); + let mut substate = Substate::new(false); let result = { let mut ex = Executive::new(&mut state, &info, &engine); diff --git a/ethcore/src/externalities.rs b/ethcore/src/externalities.rs index d37bc20fb..a6f7e4f36 100644 --- a/ethcore/src/externalities.rs +++ b/ethcore/src/externalities.rs @@ -23,12 +23,12 @@ use evm::{self, Schedule, Ext, ContractCreateResult, MessageCallResult}; use substate::*; /// Policy for handling output data on `RETURN` opcode. -pub enum OutputPolicy<'a> { +pub enum OutputPolicy<'a, 'b> { /// Return reference to fixed sized output. /// Used for message calls. - Return(BytesRef<'a>), + Return(BytesRef<'a>, Option<&'b mut Bytes>), /// Init new contract as soon as `RETURN` is called. - InitContract + InitContract(Option<&'b mut Bytes>), } /// Transaction properties that externalities need to know about. @@ -62,18 +62,19 @@ pub struct Externalities<'a> { origin_info: OriginInfo, substate: &'a mut Substate, schedule: Schedule, - output: OutputPolicy<'a> + output: OutputPolicy<'a, 'a> } impl<'a> Externalities<'a> { /// Basic `Externalities` constructor. pub fn new(state: &'a mut State, - env_info: &'a EnvInfo, - engine: &'a Engine, - depth: usize, - origin_info: OriginInfo, - substate: &'a mut Substate, - output: OutputPolicy<'a>) -> Self { + env_info: &'a EnvInfo, + engine: &'a Engine, + depth: usize, + origin_info: OriginInfo, + substate: &'a mut Substate, + output: OutputPolicy<'a, 'a> + ) -> Self { Externalities { state: state, env_info: env_info, @@ -190,20 +191,31 @@ impl<'a> Ext for Externalities<'a> { #[cfg_attr(feature="dev", allow(match_ref_pats))] fn ret(&mut self, gas: &U256, data: &[u8]) -> Result { - match &mut self.output { - &mut OutputPolicy::Return(BytesRef::Fixed(ref mut slice)) => unsafe { + let handle_copy = |to: &mut Option<&mut Bytes>| { + 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()); - ptr::copy(data.as_ptr(), slice.as_mut_ptr(), len); + unsafe { + ptr::copy(data.as_ptr(), slice.as_mut_ptr(), len); + } 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.reserve(data.len()); - ptr::copy(data.as_ptr(), vec.as_mut_ptr(), data.len()); - vec.set_len(data.len()); + unsafe { + ptr::copy(data.as_ptr(), vec.as_mut_ptr(), data.len()); + vec.set_len(data.len()); + } Ok(*gas) }, - &mut OutputPolicy::InitContract => { + OutputPolicy::InitContract(ref mut copy) => { let return_cost = U256::from(data.len()) * U256::from(self.schedule.create_data_gas); if return_cost > *gas { return match self.schedule.exceptional_failed_code_deposit { @@ -211,14 +223,16 @@ impl<'a> Ext for Externalities<'a> { false => Ok(*gas) } } + + handle_copy(copy); + let mut code = vec![]; code.reserve(data.len()); unsafe { ptr::copy(data.as_ptr(), code.as_mut_ptr(), data.len()); code.set_len(data.len()); } - let address = &self.origin_info.address; - self.state.init_code(address, code); + self.state.init_code(&self.origin_info.address, code); Ok(*gas - return_cost) } } @@ -312,7 +326,7 @@ mod tests { TestSetup { state: get_temp_state(), engine: get_test_spec().to_engine().unwrap(), - sub_state: Substate::new(), + sub_state: Substate::new(false), env_info: get_test_env_info() } } @@ -323,7 +337,7 @@ mod tests { let mut setup = TestSetup::new(); 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); } @@ -332,7 +346,7 @@ mod tests { fn can_return_block_hash_no_env() { let mut setup = TestSetup::new(); 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()); @@ -351,7 +365,7 @@ mod tests { env_info.last_hashes.push(test_hash.clone()); } 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()); @@ -363,7 +377,7 @@ mod tests { fn can_call_fail_empty() { let mut setup = TestSetup::new(); 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![]; @@ -387,7 +401,7 @@ mod tests { 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); } @@ -402,7 +416,7 @@ mod tests { 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); } diff --git a/ethcore/src/json_tests/executive.rs b/ethcore/src/json_tests/executive.rs index b08257a92..dbb9bb6ab 100644 --- a/ethcore/src/json_tests/executive.rs +++ b/ethcore/src/json_tests/executive.rs @@ -75,7 +75,7 @@ impl<'a> TestExt<'a> { depth: usize, origin_info: OriginInfo, substate: &'a mut Substate, - output: OutputPolicy<'a>, + output: OutputPolicy<'a, 'a>, address: Address) -> Self { TestExt { contract_address: contract_address(&address, &state.nonce(&address)), @@ -227,19 +227,21 @@ fn do_json_test_for(vm: &VMType, json_data: &[u8]) -> Vec { let out_of_gas = test.find("callcreates").map(|_calls| { }).is_none(); - let mut substate = Substate::new(); + let mut substate = Substate::new(false); let mut output = vec![]; // execute let (res, callcreates) = { - let mut ex = TestExt::new(&mut state, - &info, - &engine, - 0, - OriginInfo::from(¶ms), - &mut substate, - OutputPolicy::Return(BytesRef::Flexible(&mut output)), - params.address.clone()); + let mut ex = TestExt::new( + &mut state, + &info, + &engine, + 0, + OriginInfo::from(¶ms), + &mut substate, + OutputPolicy::Return(BytesRef::Flexible(&mut output), None), + params.address.clone() + ); let evm = engine.vm_factory().create(); let res = evm.exec(params, &mut ex); (res, ex.callcreates) diff --git a/ethcore/src/json_tests/state.rs b/ethcore/src/json_tests/state.rs index f6b5751a7..4c001007a 100644 --- a/ethcore/src/json_tests/state.rs +++ b/ethcore/src/json_tests/state.rs @@ -69,7 +69,7 @@ pub fn json_chain_test(json_data: &[u8], era: ChainEra) -> Vec { let mut state = state_result.reference_mut(); state.populate_from(pre); 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) { 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 { } if let Ok(r) = res { - if fail_unless(logs == r.logs) { + if fail_unless(logs == r.receipt.logs) { println!("!!! {}: Logs mismatch:", name); - println!("Got:\n{:?}", r.logs); + println!("Got:\n{:?}", r.receipt.logs); println!("Expect:\n{:?}", logs); } } diff --git a/ethcore/src/lib.rs b/ethcore/src/lib.rs index dfa321639..f8fb223db 100644 --- a/ethcore/src/lib.rs +++ b/ethcore/src/lib.rs @@ -97,6 +97,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 57e35ad2e..0610bffb8 100644 --- a/ethcore/src/substate.rs +++ b/ethcore/src/substate.rs @@ -23,37 +23,45 @@ use common::*; 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 or `None` if we're not tracing. + pub subtraces: Option>, } impl Substate { /// Creates new substate. - pub fn new() -> Self { + pub fn new(tracing: bool) -> Self { Substate { - suicides: HashSet::new(), - logs: vec![], - sstore_clears_count: U256::zero(), - contracts_created: vec![] + 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. - 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.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()); + 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] fn created() { - let sub_state = Substate::new(); + let sub_state = Substate::new(false); assert_eq!(sub_state.suicides.len(), 0); } #[test] 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.logs.push(LogEntry { address: address_from_u64(1u64), @@ -80,7 +88,7 @@ mod tests { sub_state.sstore_clears_count = x!(5); 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.logs.push(LogEntry { address: address_from_u64(1u64), @@ -89,7 +97,7 @@ mod tests { }); 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.sstore_clears_count, x!(12)); assert_eq!(sub_state.suicides.len(), 1); diff --git a/ethcore/src/trace.rs b/ethcore/src/trace.rs new file mode 100644 index 000000000..858230bcd --- /dev/null +++ b/ethcore/src/trace.rs @@ -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 . + +//! 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, +} + +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, + }) + } +} + diff --git a/ethcore/src/verification/verification.rs b/ethcore/src/verification/verification.rs index 60cbed56c..6e79d737e 100644 --- a/ethcore/src/verification/verification.rs +++ b/ethcore/src/verification/verification.rs @@ -278,6 +278,8 @@ mod tests { } impl BlockProvider for TestBlockChain { + fn have_tracing(&self) -> bool { false } + fn is_known(&self, hash: &H256) -> bool { self.blocks.contains_key(hash) }