From 42e4c2d51c8802185cc0dcd3998589f8f66a8dc9 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Fri, 27 May 2016 20:41:29 +0200 Subject: [PATCH 01/16] Groundwork for basic VM tracing. --- ethcore/src/evm/ext.rs | 6 ++++++ ethcore/src/evm/interpreter.rs | 16 ++++++++++++---- ethcore/src/evm/mod.rs | 2 +- ethcore/src/externalities.rs | 35 ++++++++++++++++++++++++++++++++-- 4 files changed, 52 insertions(+), 7 deletions(-) diff --git a/ethcore/src/evm/ext.rs b/ethcore/src/evm/ext.rs index 4986b12c8..278b22e23 100644 --- a/ethcore/src/evm/ext.rs +++ b/ethcore/src/evm/ext.rs @@ -40,6 +40,9 @@ pub enum MessageCallResult { Failed } +/// The trace function callback for VM tracing (*not* transaction tracing - that's different). +pub type VMTraceFunctionBox = Box; + /// Externalities interface for EVMs pub trait Ext { /// Returns a value for given key. @@ -105,4 +108,7 @@ pub trait Ext { /// Increments sstore refunds count by 1. fn inc_sstore_clears(&mut self); + + /// Provide a tracer for VM tracing if the VM implementation supports it. + fn vm_tracer(&mut self) -> Option<&mut VMTraceFunctionBox>; } diff --git a/ethcore/src/evm/interpreter.rs b/ethcore/src/evm/interpreter.rs index eb29ef257..3fb59374b 100644 --- a/ethcore/src/evm/interpreter.rs +++ b/ethcore/src/evm/interpreter.rs @@ -311,6 +311,12 @@ impl evm::Evm for Interpreter { ); }); + // Call trace + // TODO: allow to be disabled at build time for max speed + if let Some(ref mut trace_instruction) = ext.vm_tracer() { + (*trace_instruction.deref_mut())(reader.position, instruction, gas_cost, current_gas); + } + // Execute instruction let result = try!(self.exec_instruction( current_gas, ¶ms, ext, instruction, &mut reader, &mut mem, &mut stack @@ -833,10 +839,12 @@ impl Interpreter { } } - fn verify_instructions_requirements(&self, - info: &instructions::InstructionInfo, - stack_limit: usize, - stack: &Stack) -> Result<(), evm::Error> { + fn verify_instructions_requirements( + &self, + info: &instructions::InstructionInfo, + stack_limit: usize, + stack: &Stack + ) -> Result<(), evm::Error> { if !stack.has(info.args) { Err(evm::Error::StackUnderflow { instruction: info.name, diff --git a/ethcore/src/evm/mod.rs b/ethcore/src/evm/mod.rs index b7816b99c..e72328d92 100644 --- a/ethcore/src/evm/mod.rs +++ b/ethcore/src/evm/mod.rs @@ -30,6 +30,6 @@ mod jit; mod tests; pub use self::evm::{Evm, Error, Result}; -pub use self::ext::{Ext, ContractCreateResult, MessageCallResult}; +pub use self::ext::{Ext, ContractCreateResult, MessageCallResult, VMTraceFunctionBox}; pub use self::factory::{Factory, VMType}; pub use self::schedule::Schedule; diff --git a/ethcore/src/externalities.rs b/ethcore/src/externalities.rs index c936ac207..c78ac09a8 100644 --- a/ethcore/src/externalities.rs +++ b/ethcore/src/externalities.rs @@ -19,7 +19,7 @@ use common::*; use state::*; use engine::*; use executive::*; -use evm::{self, Schedule, Ext, ContractCreateResult, MessageCallResult, Factory}; +use evm::{self, Schedule, Ext, ContractCreateResult, MessageCallResult, Factory, VMTraceFunctionBox}; use substate::*; use trace::Tracer; @@ -66,10 +66,10 @@ pub struct Externalities<'a, T> where T: 'a + Tracer { schedule: Schedule, output: OutputPolicy<'a, 'a>, tracer: &'a mut T, + vm_tracer: Option, } impl<'a, T> Externalities<'a, T> where T: 'a + Tracer { - #[cfg_attr(feature="dev", allow(too_many_arguments))] /// Basic `Externalities` constructor. pub fn new(state: &'a mut State, @@ -93,6 +93,35 @@ impl<'a, T> Externalities<'a, T> where T: 'a + Tracer { schedule: engine.schedule(env_info), output: output, tracer: tracer, + vm_tracer: None, + } + } + + #[cfg_attr(feature="dev", allow(too_many_arguments))] + /// Basic `Externalities` constructor. + pub fn with_vm_tracer(state: &'a mut State, + env_info: &'a EnvInfo, + engine: &'a Engine, + vm_factory: &'a Factory, + depth: usize, + origin_info: OriginInfo, + substate: &'a mut Substate, + output: OutputPolicy<'a, 'a>, + tracer: &'a mut T, + vm_tracer: VMTraceFunctionBox, + ) -> Self { + Externalities { + state: state, + env_info: env_info, + engine: engine, + vm_factory: vm_factory, + depth: depth, + origin_info: origin_info, + substate: substate, + schedule: engine.schedule(env_info), + output: output, + tracer: tracer, + vm_tracer: Some(vm_tracer), } } } @@ -286,6 +315,8 @@ impl<'a, T> Ext for Externalities<'a, T> where T: 'a + Tracer { fn inc_sstore_clears(&mut self) { self.substate.sstore_clears_count = self.substate.sstore_clears_count + U256::one(); } + + fn vm_tracer(&mut self) -> Option<&mut VMTraceFunctionBox> { self.vm_tracer.as_mut() } } #[cfg(test)] From c1ed520de0a396569c4e1b0b9d5b289bfd20eaed Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sat, 28 May 2016 16:52:33 +0200 Subject: [PATCH 02/16] RPC endpoint for VM tracing and ser/de types ready. --- ethcore/src/client/client.rs | 4 +- ethcore/src/client/mod.rs | 3 +- ethcore/src/client/test_client.rs | 2 +- ethcore/src/executive.rs | 14 ++-- ethcore/src/state.rs | 2 +- ethcore/src/trace/mod.rs | 2 +- ethcore/src/types/executed.rs | 4 +- ethcore/src/types/trace_types/trace.rs | 88 +++++++++++++++++++++++ miner/src/lib.rs | 2 +- miner/src/miner.rs | 7 +- parity/dapps.rs | 2 +- parity/rpc.rs | 2 +- rpc/src/lib.rs | 1 + rpc/src/v1/impls/eth.rs | 8 +-- rpc/src/v1/impls/ethcore.rs | 50 +++++++++++-- rpc/src/v1/tests/helpers/miner_service.rs | 2 +- rpc/src/v1/traits/ethcore.rs | 4 ++ 17 files changed, 167 insertions(+), 30 deletions(-) diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index 13d678c14..44da0a4b3 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -421,7 +421,7 @@ impl Client where V: Verifier { } impl BlockChainClient for Client where V: Verifier { - fn call(&self, t: &SignedTransaction) -> Result { + fn call(&self, t: &SignedTransaction, vm_tracing: bool) -> Result { let header = self.block_header(BlockID::Latest).unwrap(); let view = HeaderView::new(&header); let last_hashes = self.build_last_hashes(view.hash()); @@ -444,7 +444,7 @@ impl BlockChainClient for Client where V: Verifier { // give the sender max balance state.sub_balance(&sender, &balance); state.add_balance(&sender, &U256::max_value()); - let options = TransactOptions { tracing: false, check_nonce: false }; + let options = TransactOptions { tracing: false, vm_tracing: vm_tracing, check_nonce: false }; Executive::new(&mut state, &env_info, self.engine.deref().deref(), &self.vm_factory).transact(t, options) } diff --git a/ethcore/src/client/mod.rs b/ethcore/src/client/mod.rs index 8e0a7b2dd..2154c12bd 100644 --- a/ethcore/src/client/mod.rs +++ b/ethcore/src/client/mod.rs @@ -164,7 +164,8 @@ pub trait BlockChainClient : Sync + Send { fn try_seal(&self, block: LockedBlock, seal: Vec) -> Result; /// Makes a non-persistent transaction call. - fn call(&self, t: &SignedTransaction) -> Result; + // TODO: should be able to accept blockchain location for call. + fn call(&self, t: &SignedTransaction, vm_tracing: bool) -> Result; /// Returns EvmFactory. fn vm_factory(&self) -> &EvmFactory; diff --git a/ethcore/src/client/test_client.rs b/ethcore/src/client/test_client.rs index de2973029..d1b4408e6 100644 --- a/ethcore/src/client/test_client.rs +++ b/ethcore/src/client/test_client.rs @@ -233,7 +233,7 @@ impl TestBlockChainClient { } impl BlockChainClient for TestBlockChainClient { - fn call(&self, _t: &SignedTransaction) -> Result { + fn call(&self, _t: &SignedTransaction, _vm_tracing: bool) -> Result { Ok(self.execution_result.read().unwrap().clone().unwrap()) } diff --git a/ethcore/src/executive.rs b/ethcore/src/executive.rs index 320a89cb8..666371770 100644 --- a/ethcore/src/executive.rs +++ b/ethcore/src/executive.rs @@ -43,6 +43,8 @@ pub fn contract_address(address: &Address, nonce: &U256) -> Address { pub struct TransactOptions { /// Enable call tracing. pub tracing: bool, + /// Enable VM tracing. + pub vm_tracing: bool, /// Check transaction nonce before execution. pub check_nonce: bool, } @@ -394,6 +396,7 @@ impl<'a> Executive<'a> { contracts_created: vec![], output: output, trace: trace, + vm_trace: None, }) }, _ => { @@ -406,6 +409,7 @@ impl<'a> Executive<'a> { contracts_created: substate.contracts_created, output: output, trace: trace, + vm_trace: None, }) }, } @@ -913,7 +917,7 @@ mod tests { let executed = { let mut ex = Executive::new(&mut state, &info, &engine, &factory); - let opts = TransactOptions { check_nonce: true, tracing: false }; + let opts = TransactOptions { check_nonce: true, tracing: false, vm_tracing: false }; ex.transact(&t, opts).unwrap() }; @@ -947,7 +951,7 @@ mod tests { let res = { let mut ex = Executive::new(&mut state, &info, &engine, &factory); - let opts = TransactOptions { check_nonce: true, tracing: false }; + let opts = TransactOptions { check_nonce: true, tracing: false, vm_tracing: false }; ex.transact(&t, opts) }; @@ -979,7 +983,7 @@ mod tests { let res = { let mut ex = Executive::new(&mut state, &info, &engine, &factory); - let opts = TransactOptions { check_nonce: true, tracing: false }; + let opts = TransactOptions { check_nonce: true, tracing: false, vm_tracing: false }; ex.transact(&t, opts) }; @@ -1013,7 +1017,7 @@ mod tests { let res = { let mut ex = Executive::new(&mut state, &info, &engine, &factory); - let opts = TransactOptions { check_nonce: true, tracing: false }; + let opts = TransactOptions { check_nonce: true, tracing: false, vm_tracing: false }; ex.transact(&t, opts) }; @@ -1047,7 +1051,7 @@ mod tests { let res = { let mut ex = Executive::new(&mut state, &info, &engine, &factory); - let opts = TransactOptions { check_nonce: true, tracing: false }; + let opts = TransactOptions { check_nonce: true, tracing: false, vm_tracing: false }; ex.transact(&t, opts) }; diff --git a/ethcore/src/state.rs b/ethcore/src/state.rs index eabca24a8..87bee5244 100644 --- a/ethcore/src/state.rs +++ b/ethcore/src/state.rs @@ -222,7 +222,7 @@ impl State { pub fn apply(&mut self, env_info: &EnvInfo, engine: &Engine, vm_factory: &EvmFactory, t: &SignedTransaction, tracing: bool) -> ApplyResult { // let old = self.to_pod(); - let options = TransactOptions { tracing: tracing, check_nonce: true }; + let options = TransactOptions { tracing: tracing, vm_tracing: false, check_nonce: true }; let e = try!(Executive::new(self, env_info, engine, vm_factory).transact(t, options)); // TODO uncomment once to_pod() works correctly. diff --git a/ethcore/src/trace/mod.rs b/ethcore/src/trace/mod.rs index f51cf8ee5..dd8cd4d20 100644 --- a/ethcore/src/trace/mod.rs +++ b/ethcore/src/trace/mod.rs @@ -31,7 +31,7 @@ pub use self::block::BlockTraces; pub use self::config::{Config, Switch}; pub use self::db::TraceDB; pub use self::error::Error; -pub use types::trace_types::trace::Trace; +pub use types::trace_types::trace::{Trace, VMTrace}; pub use self::noop_tracer::NoopTracer; pub use self::executive_tracer::ExecutiveTracer; pub use types::trace_types::filter::{Filter, AddressesFilter}; diff --git a/ethcore/src/types/executed.rs b/ethcore/src/types/executed.rs index fcde5a392..823c9dda1 100644 --- a/ethcore/src/types/executed.rs +++ b/ethcore/src/types/executed.rs @@ -18,7 +18,7 @@ use util::numbers::*; use util::Bytes; -use trace::Trace; +use trace::{Trace, VMTrace}; use types::log_entry::LogEntry; use ipc::binary::BinaryConvertError; use std::fmt; @@ -59,6 +59,8 @@ pub struct Executed { pub output: Bytes, /// The trace of this transaction. pub trace: Option, + /// The VM trace of this transaction. + pub vm_trace: Option, } /// Result of executing the transaction. diff --git a/ethcore/src/types/trace_types/trace.rs b/ethcore/src/types/trace_types/trace.rs index f8fe07360..da58f3253 100644 --- a/ethcore/src/types/trace_types/trace.rs +++ b/ethcore/src/types/trace_types/trace.rs @@ -349,6 +349,94 @@ impl Trace { } } +#[derive(Debug, Clone, PartialEq, Binary)] +/// A record of the execution of a single VM operation. +pub struct VMOperation { + /// The program counter. + pub pc: usize, + /// The instruction executed. + pub instruction: u8, + /// The gas cost for this instruction. + pub gas_cost: U256, + /// The total gas used. + pub gas_used: U256, + /// The stack. + pub stack: Vec, + /// Altered storage value. TODO: should be option. +// pub storage_diff: Option<(U256, U256)>, + /// If altered, the new memory image. + pub new_memory: Bytes, +} + +impl Encodable for VMOperation { + fn rlp_append(&self, s: &mut RlpStream) { + s.begin_list(6); + s.append(&self.pc); + s.append(&self.instruction); + s.append(&self.gas_cost); + s.append(&self.gas_used); + s.append(&self.stack); +// s.append(&self.storage_diff); + s.append(&self.new_memory); + } +} + +impl Decodable for VMOperation { + fn decode(decoder: &D) -> Result where D: Decoder { + let d = decoder.as_rlp(); + let res = VMOperation { + pc: try!(d.val_at(0)), + instruction: try!(d.val_at(1)), + gas_cost: try!(d.val_at(2)), + gas_used: try!(d.val_at(3)), + stack: try!(d.val_at(4)), +// storage_diff: try!(d.val_at(5)), + new_memory: try!(d.val_at(6)), + }; + + Ok(res) + } +} + +#[derive(Debug, Clone, PartialEq, Binary)] +/// A record of a full VM trace for a CALL/CREATE. +pub struct VMTrace { + /// 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 code to be executed. + pub code: Bytes, + /// The operations executed. + pub operations: Vec, + /// The sub traces for each interior action performed as part of this call/create. + /// Thre is a 1:1 correspondance between these and a CALL/CREATE/CALLCODE/DELEGATECALL instruction. + pub subs: Vec, +} + +impl Encodable for VMTrace { + fn rlp_append(&self, s: &mut RlpStream) { + s.begin_list(4); + s.append(&self.depth); + s.append(&self.code); + s.append(&self.operations); + s.append(&self.subs); + } +} + +impl Decodable for VMTrace { + fn decode(decoder: &D) -> Result where D: Decoder { + let d = decoder.as_rlp(); + let res = VMTrace { + depth: try!(d.val_at(0)), + code: try!(d.val_at(1)), + operations: try!(d.val_at(2)), + subs: try!(d.val_at(3)), + }; + + Ok(res) + } +} + #[cfg(test)] mod tests { use util::{Address, U256, FixedHash}; diff --git a/miner/src/lib.rs b/miner/src/lib.rs index e9ac7aba2..d20677a25 100644 --- a/miner/src/lib.rs +++ b/miner/src/lib.rs @@ -158,7 +158,7 @@ pub trait MinerService : Send + Sync { fn balance(&self, chain: &BlockChainClient, address: &Address) -> U256; /// Call into contract code using pending state. - fn call(&self, chain: &BlockChainClient, t: &SignedTransaction) -> Result; + fn call(&self, chain: &BlockChainClient, t: &SignedTransaction, vm_tracing: bool) -> Result; /// Get storage value in pending state. fn storage_at(&self, chain: &BlockChainClient, address: &Address, position: &H256) -> H256; diff --git a/miner/src/miner.rs b/miner/src/miner.rs index 3860a79e6..d22921ae3 100644 --- a/miner/src/miner.rs +++ b/miner/src/miner.rs @@ -252,7 +252,7 @@ impl MinerService for Miner { } } - fn call(&self, chain: &BlockChainClient, t: &SignedTransaction) -> Result { + fn call(&self, chain: &BlockChainClient, t: &SignedTransaction, vm_tracing: bool) -> Result { let sealing_work = self.sealing_work.lock().unwrap(); match sealing_work.peek_last_ref() { Some(work) => { @@ -278,12 +278,13 @@ impl MinerService for Miner { // give the sender max balance state.sub_balance(&sender, &balance); state.add_balance(&sender, &U256::max_value()); - let options = TransactOptions { tracing: false, check_nonce: false }; + let options = TransactOptions { tracing: false, vm_tracing: vm_tracing, check_nonce: false }; + // TODO: use vm_trace here. Executive::new(&mut state, &env_info, self.engine(), chain.vm_factory()).transact(t, options) }, None => { - chain.call(t) + chain.call(t, vm_tracing) } } } diff --git a/parity/dapps.rs b/parity/dapps.rs index 986e3dd07..f918c3440 100644 --- a/parity/dapps.rs +++ b/parity/dapps.rs @@ -101,7 +101,7 @@ pub fn setup_dapps_server( server.add_delegate(EthClient::new(&deps.client, &deps.sync, &deps.secret_store, &deps.miner, &deps.external_miner).to_delegate()); server.add_delegate(EthFilterClient::new(&deps.client, &deps.miner).to_delegate()); server.add_delegate(PersonalClient::new(&deps.secret_store, &deps.client, &deps.miner).to_delegate()); - server.add_delegate(EthcoreClient::new(&deps.miner, deps.logger.clone(), deps.settings.clone()).to_delegate()); + server.add_delegate(EthcoreClient::new(&deps.client, &deps.miner, deps.logger.clone(), deps.settings.clone()).to_delegate()); let start_result = match auth { None => { diff --git a/parity/rpc.rs b/parity/rpc.rs index 60766263b..981e529ce 100644 --- a/parity/rpc.rs +++ b/parity/rpc.rs @@ -110,7 +110,7 @@ fn setup_rpc_server(apis: Vec<&str>, deps: &Arc) -> Server { }, "ethcore" => { modules.insert("ethcore".to_owned(), "1.0".to_owned()); - server.add_delegate(EthcoreClient::new(&deps.miner, deps.logger.clone(), deps.settings.clone()).to_delegate()) + server.add_delegate(EthcoreClient::new(&deps.client, &deps.miner, deps.logger.clone(), deps.settings.clone()).to_delegate()) }, "traces" => { modules.insert("traces".to_owned(), "1.0".to_owned()); diff --git a/rpc/src/lib.rs b/rpc/src/lib.rs index 7d9818615..1abc59a7e 100644 --- a/rpc/src/lib.rs +++ b/rpc/src/lib.rs @@ -26,6 +26,7 @@ extern crate serde; extern crate serde_json; extern crate jsonrpc_core; extern crate jsonrpc_http_server; +#[macro_use] extern crate ethcore_util as util; extern crate ethcore; extern crate ethsync; diff --git a/rpc/src/v1/impls/eth.rs b/rpc/src/v1/impls/eth.rs index 7627dd61e..e59ecc566 100644 --- a/rpc/src/v1/impls/eth.rs +++ b/rpc/src/v1/impls/eth.rs @@ -522,8 +522,8 @@ impl Eth for EthClient where .and_then(|(request, block_number,)| { let signed = try!(self.sign_call(request)); let r = match block_number { - BlockNumber::Pending => take_weak!(self.miner).call(take_weak!(self.client).deref(), &signed), - BlockNumber::Latest => take_weak!(self.client).call(&signed), + BlockNumber::Pending => take_weak!(self.miner).call(take_weak!(self.client).deref(), &signed, false), + BlockNumber::Latest => take_weak!(self.client).call(&signed, false), _ => panic!("{:?}", block_number), }; to_value(&r.map(|e| Bytes(e.output)).unwrap_or(Bytes::new(vec![]))) @@ -535,8 +535,8 @@ impl Eth for EthClient where .and_then(|(request, block_number,)| { let signed = try!(self.sign_call(request)); let r = match block_number { - BlockNumber::Pending => take_weak!(self.miner).call(take_weak!(self.client).deref(), &signed), - BlockNumber::Latest => take_weak!(self.client).call(&signed), + BlockNumber::Pending => take_weak!(self.miner).call(take_weak!(self.client).deref(), &signed, false), + BlockNumber::Latest => take_weak!(self.client).call(&signed, false), _ => return Err(Error::invalid_params()), }; to_value(&r.map(|res| res.gas_used + res.refunded).unwrap_or(From::from(0))) diff --git a/rpc/src/v1/impls/ethcore.rs b/rpc/src/v1/impls/ethcore.rs index de458a53f..d34c6d62e 100644 --- a/rpc/src/v1/impls/ethcore.rs +++ b/rpc/src/v1/impls/ethcore.rs @@ -15,7 +15,7 @@ // along with Parity. If not, see . //! Ethcore-specific rpc implementation. -use util::{U256, Address, RotatingLogger}; +use util::{U256, Address, RotatingLogger, FixedHash, Uint}; use util::network_settings::NetworkSettings; use util::misc::version_data; use std::sync::{Arc, Weak}; @@ -23,29 +23,50 @@ use std::ops::Deref; use std::collections::BTreeMap; use jsonrpc_core::*; use ethminer::{MinerService}; +use ethcore::client::{BlockChainClient}; +use ethcore::transaction::{Transaction as EthTransaction, SignedTransaction, Action}; use v1::traits::Ethcore; -use v1::types::Bytes; +use v1::types::{Bytes, CallRequest}; /// Ethcore implementation. -pub struct EthcoreClient - where M: MinerService { +pub struct EthcoreClient where + C: BlockChainClient, + M: MinerService { + + client: Weak, miner: Weak, logger: Arc, settings: Arc, } -impl EthcoreClient where M: MinerService { +impl EthcoreClient where C: BlockChainClient, M: MinerService { /// Creates new `EthcoreClient`. - pub fn new(miner: &Arc, logger: Arc, settings: Arc) -> Self { + pub fn new(client: &Arc, miner: &Arc, logger: Arc, settings: Arc) -> Self { EthcoreClient { + client: Arc::downgrade(client), miner: Arc::downgrade(miner), logger: logger, settings: settings, } } + + // TODO: share with eth.rs + fn sign_call(&self, request: CallRequest) -> Result { + let client = take_weak!(self.client); + let miner = take_weak!(self.miner); + let from = request.from.unwrap_or(Address::zero()); + Ok(EthTransaction { + nonce: request.nonce.unwrap_or_else(|| client.latest_nonce(&from)), + action: request.to.map_or(Action::Create, Action::Call), + gas: request.gas.unwrap_or(U256::from(50_000_000)), + gas_price: request.gas_price.unwrap_or_else(|| miner.sensible_gas_price()), + value: request.value.unwrap_or_else(U256::zero), + data: request.data.map_or_else(Vec::new, |d| d.to_vec()) + }.fake_sign(from)) + } } -impl Ethcore for EthcoreClient where M: MinerService + 'static { +impl Ethcore for EthcoreClient where C: BlockChainClient + 'static, M: MinerService + 'static { fn set_min_gas_price(&self, params: Params) -> Result { from_params::<(U256,)>(params).and_then(|(gas_price,)| { @@ -135,4 +156,19 @@ impl Ethcore for EthcoreClient where M: MinerService + 'static { let version = version_data(); to_value(&Bytes::new(version)) } + + //pub type VMTraceFunctionBox = Box; + + fn vm_trace_call(&self, params: Params) -> Result { + trace!(target: "jsonrpc", "vm_trace_call: {:?}", params); + from_params(params) + .and_then(|(request,)| { + let signed = try!(self.sign_call(request)); + let _ = take_weak!(self.client).call(&signed, true); + // TODO: construct JSON trace from _.vm_trace. + let mut ret = Vec::new(); + ret.push(Value::Object(map!["foo".to_owned() => Value::String("var".to_owned())])); + Ok(Value::Array(ret)) + }) + } } diff --git a/rpc/src/v1/tests/helpers/miner_service.rs b/rpc/src/v1/tests/helpers/miner_service.rs index 5d57d5d61..e6114f04d 100644 --- a/rpc/src/v1/tests/helpers/miner_service.rs +++ b/rpc/src/v1/tests/helpers/miner_service.rs @@ -191,7 +191,7 @@ impl MinerService for TestMinerService { self.latest_closed_block.lock().unwrap().as_ref().map_or_else(U256::zero, |b| b.block().fields().state.balance(address).clone()) } - fn call(&self, _chain: &BlockChainClient, _t: &SignedTransaction) -> Result { + fn call(&self, _chain: &BlockChainClient, _t: &SignedTransaction, _vm_tracing: bool) -> Result { unimplemented!(); } diff --git a/rpc/src/v1/traits/ethcore.rs b/rpc/src/v1/traits/ethcore.rs index 2a2159bd9..3b82aa031 100644 --- a/rpc/src/v1/traits/ethcore.rs +++ b/rpc/src/v1/traits/ethcore.rs @@ -72,6 +72,8 @@ pub trait Ethcore: Sized + Send + Sync + 'static { /// Returns default extra data fn default_extra_data(&self, _: Params) -> Result { rpc_unimplemented!() } + /// Executes the given call and returns the VM trace for it. + fn vm_trace_call(&self, _: Params) -> Result { rpc_unimplemented!() } /// Should be used to convert object to io delegate. fn to_delegate(self) -> IoDelegate { @@ -95,6 +97,8 @@ pub trait Ethcore: Sized + Send + Sync + 'static { delegate.add_method("ethcore_nodeName", Ethcore::node_name); delegate.add_method("ethcore_defaultExtraData", Ethcore::default_extra_data); + delegate.add_method("ethcore_vmTraceCall", Ethcore::vm_trace_call); + delegate } } From d4a06b27ed32ac56fd3409867da8b07d5ed21b63 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sat, 28 May 2016 17:50:20 +0200 Subject: [PATCH 03/16] Create VMTracer trait. --- ethcore/src/trace/executive_tracer.rs | 3 +-- ethcore/src/trace/mod.rs | 14 ++++++++++++- ethcore/src/types/trace_types/trace.rs | 27 +++++++++++++------------- rpc/src/v1/impls/ethcore.rs | 2 -- 4 files changed, 27 insertions(+), 19 deletions(-) diff --git a/ethcore/src/trace/executive_tracer.rs b/ethcore/src/trace/executive_tracer.rs index a0bff1c72..41d8c2389 100644 --- a/ethcore/src/trace/executive_tracer.rs +++ b/ethcore/src/trace/executive_tracer.rs @@ -40,8 +40,7 @@ impl Tracer for ExecutiveTracer { Some(vec![]) } - fn trace_call(&mut self, call: Option, gas_used: U256, output: Option, depth: usize, subs: - Vec, delegate_call: bool) { + fn trace_call(&mut self, call: Option, gas_used: U256, output: Option, depth: usize, subs: Vec, delegate_call: bool) { // don't trace if it's DELEGATECALL or CALLCODE. if delegate_call { return; diff --git a/ethcore/src/trace/mod.rs b/ethcore/src/trace/mod.rs index dd8cd4d20..21827dd58 100644 --- a/ethcore/src/trace/mod.rs +++ b/ethcore/src/trace/mod.rs @@ -81,13 +81,25 @@ pub trait Tracer: Send { /// Stores failed create trace. fn trace_failed_create(&mut self, create: Option, depth: usize, subs: Vec); - /// Spawn subracer which will be used to trace deeper levels of execution. + /// Spawn subtracer which will be used to trace deeper levels of execution. fn subtracer(&self) -> Self where Self: Sized; /// Consumes self and returns all traces. fn traces(self) -> Vec; } +/// Used by executive to build VM traces. +pub trait VMTracer: Send { + /// Trace the preparation to execute a single instruction. + fn trace_prepare_execute(pc: usize, instruction: u8, gas_cost: &U256, stack: &Vec); + + /// Spawn subtracer which will be used to trace deeper levels of execution. + fn subtracer(&self) -> Self where Self: Sized; + + /// Consumes self and returns all VM traces. + fn traces(self) -> Vec; +} + /// `DbExtras` provides an interface to query extra data which is not stored in tracesdb, /// but necessary to work correctly. pub trait DatabaseExtras { diff --git a/ethcore/src/types/trace_types/trace.rs b/ethcore/src/types/trace_types/trace.rs index da58f3253..5b5132643 100644 --- a/ethcore/src/types/trace_types/trace.rs +++ b/ethcore/src/types/trace_types/trace.rs @@ -349,6 +349,17 @@ impl Trace { } } +/*pub struct VMExecutedOperation { + /// The total gas used. + pub gas_used: U256, + /// Altered storage value. + pub storage_diff: Option<(U256, U256)>, + /// If altered, the new memory image. + pub new_memory: Option, +}*/ + /// Information concerning the execution of the operation. +// pub executed: Option, + #[derive(Debug, Clone, PartialEq, Binary)] /// A record of the execution of a single VM operation. pub struct VMOperation { @@ -358,26 +369,17 @@ pub struct VMOperation { pub instruction: u8, /// The gas cost for this instruction. pub gas_cost: U256, - /// The total gas used. - pub gas_used: U256, /// The stack. pub stack: Vec, - /// Altered storage value. TODO: should be option. -// pub storage_diff: Option<(U256, U256)>, - /// If altered, the new memory image. - pub new_memory: Bytes, } impl Encodable for VMOperation { fn rlp_append(&self, s: &mut RlpStream) { - s.begin_list(6); + s.begin_list(4); s.append(&self.pc); s.append(&self.instruction); s.append(&self.gas_cost); - s.append(&self.gas_used); s.append(&self.stack); -// s.append(&self.storage_diff); - s.append(&self.new_memory); } } @@ -388,10 +390,7 @@ impl Decodable for VMOperation { pc: try!(d.val_at(0)), instruction: try!(d.val_at(1)), gas_cost: try!(d.val_at(2)), - gas_used: try!(d.val_at(3)), - stack: try!(d.val_at(4)), -// storage_diff: try!(d.val_at(5)), - new_memory: try!(d.val_at(6)), + stack: try!(d.val_at(3)), }; Ok(res) diff --git a/rpc/src/v1/impls/ethcore.rs b/rpc/src/v1/impls/ethcore.rs index d34c6d62e..4412cdac6 100644 --- a/rpc/src/v1/impls/ethcore.rs +++ b/rpc/src/v1/impls/ethcore.rs @@ -157,8 +157,6 @@ impl Ethcore for EthcoreClient where C: BlockChainClient + 'static, to_value(&Bytes::new(version)) } - //pub type VMTraceFunctionBox = Box; - fn vm_trace_call(&self, params: Params) -> Result { trace!(target: "jsonrpc", "vm_trace_call: {:?}", params); from_params(params) From 86fdcabd0e5efa9836f18293d4b0ad101dd76c66 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sat, 28 May 2016 23:57:16 +0200 Subject: [PATCH 04/16] Rearchitected VM tracing to reflect existing tracing. Should more or less work now. --- ethcore/src/evm/ext.rs | 8 +-- ethcore/src/evm/interpreter.rs | 17 +++-- ethcore/src/evm/jit.rs | 1 + ethcore/src/evm/mod.rs | 2 +- ethcore/src/executive.rs | 98 ++++++++++++++++++++------ ethcore/src/externalities.rs | 51 ++++---------- ethcore/src/trace/executive_tracer.rs | 38 +++++++++- ethcore/src/trace/mod.rs | 15 ++-- ethcore/src/trace/noop_tracer.rs | 21 +++++- ethcore/src/types/trace_types/trace.rs | 11 ++- 10 files changed, 173 insertions(+), 89 deletions(-) diff --git a/ethcore/src/evm/ext.rs b/ethcore/src/evm/ext.rs index 278b22e23..574a11773 100644 --- a/ethcore/src/evm/ext.rs +++ b/ethcore/src/evm/ext.rs @@ -40,9 +40,6 @@ pub enum MessageCallResult { Failed } -/// The trace function callback for VM tracing (*not* transaction tracing - that's different). -pub type VMTraceFunctionBox = Box; - /// Externalities interface for EVMs pub trait Ext { /// Returns a value for given key. @@ -109,6 +106,7 @@ pub trait Ext { /// Increments sstore refunds count by 1. fn inc_sstore_clears(&mut self); - /// Provide a tracer for VM tracing if the VM implementation supports it. - fn vm_tracer(&mut self) -> Option<&mut VMTraceFunctionBox>; + // TODO work out a way of not having this here but go via . + /// Prepare to trace an operation. Passthrough for the VM trace. + fn trace_prepare_execute(&mut self, _pc: usize, _instruction: u8, _gas_cost: &U256, _stack: &[U256]) {} } diff --git a/ethcore/src/evm/interpreter.rs b/ethcore/src/evm/interpreter.rs index 3fb59374b..d4948919f 100644 --- a/ethcore/src/evm/interpreter.rs +++ b/ethcore/src/evm/interpreter.rs @@ -17,6 +17,7 @@ ///! Rust VM implementation use common::*; +use trace::VMTracer; use super::instructions as instructions; use super::instructions::Instruction; use std::marker::Copy; @@ -69,6 +70,8 @@ trait Stack { fn push(&mut self, elem: T); /// Get number of elements on Stack fn size(&self) -> usize; + /// Returns all data on stack. + fn peek_all(&mut self) -> &[T]; } struct VecStack { @@ -131,6 +134,10 @@ impl Stack for VecStack { fn size(&self) -> usize { self.stack.len() } + + fn peek_all(&mut self) -> &[S] { + &self.stack + } } trait Memory { @@ -297,6 +304,10 @@ impl evm::Evm for Interpreter { // Calculate gas cost let (gas_cost, mem_size) = try!(self.get_gas_cost_mem(ext, instruction, &mut mem, &stack)); + + // TODO: make compile-time removable if too much of a performance hit. + ext.trace_prepare_execute(reader.position, instruction, &gas_cost, stack.peek_all()); + try!(self.verify_gas(¤t_gas, &gas_cost)); mem.expand(mem_size); current_gas = current_gas - gas_cost; //TODO: use operator -= @@ -311,12 +322,6 @@ impl evm::Evm for Interpreter { ); }); - // Call trace - // TODO: allow to be disabled at build time for max speed - if let Some(ref mut trace_instruction) = ext.vm_tracer() { - (*trace_instruction.deref_mut())(reader.position, instruction, gas_cost, current_gas); - } - // Execute instruction let result = try!(self.exec_instruction( current_gas, ¶ms, ext, instruction, &mut reader, &mut mem, &mut stack diff --git a/ethcore/src/evm/jit.rs b/ethcore/src/evm/jit.rs index 6a22a1306..694c1668a 100644 --- a/ethcore/src/evm/jit.rs +++ b/ethcore/src/evm/jit.rs @@ -16,6 +16,7 @@ //! Just in time compiler execution environment. use common::*; +use trace::VMTracer; use evmjit; use evm; diff --git a/ethcore/src/evm/mod.rs b/ethcore/src/evm/mod.rs index e72328d92..b7816b99c 100644 --- a/ethcore/src/evm/mod.rs +++ b/ethcore/src/evm/mod.rs @@ -30,6 +30,6 @@ mod jit; mod tests; pub use self::evm::{Evm, Error, Result}; -pub use self::ext::{Ext, ContractCreateResult, MessageCallResult, VMTraceFunctionBox}; +pub use self::ext::{Ext, ContractCreateResult, MessageCallResult}; pub use self::factory::{Factory, VMType}; pub use self::schedule::Schedule; diff --git a/ethcore/src/executive.rs b/ethcore/src/executive.rs index 666371770..f2be36df4 100644 --- a/ethcore/src/executive.rs +++ b/ethcore/src/executive.rs @@ -21,7 +21,7 @@ use engine::*; use evm::{self, Ext, Factory}; use externalities::*; use substate::*; -use trace::{Trace, Tracer, NoopTracer, ExecutiveTracer}; +use trace::{Trace, Tracer, NoopTracer, ExecutiveTracer, VMTrace, VMTracer, ExecutiveVMTracer, NoopVMTracer}; use crossbeam; pub use types::executed::{Executed, ExecutionResult}; @@ -82,21 +82,40 @@ impl<'a> Executive<'a> { } /// Creates `Externalities` from `Executive`. - pub fn as_externalities<'_, T>(&'_ mut self, origin_info: OriginInfo, substate: &'_ mut Substate, output: OutputPolicy<'_, '_>, tracer: &'_ mut T) -> Externalities<'_, T> where T: Tracer { - Externalities::new(self.state, self.info, self.engine, self.vm_factory, self.depth, origin_info, substate, output, tracer) + pub fn as_externalities<'_, T, V>( + &'_ mut self, + origin_info: OriginInfo, + substate: &'_ mut Substate, + output: OutputPolicy<'_, '_>, + tracer: &'_ mut T, + vm_tracer: &'_ mut V + ) -> Externalities<'_, T, V> where T: Tracer, V: VMTracer { + Externalities::new(self.state, self.info, self.engine, self.vm_factory, self.depth, origin_info, substate, output, tracer, vm_tracer) } /// This function should be used to execute transaction. pub fn transact(&'a mut self, t: &SignedTransaction, options: TransactOptions) -> Result { let check = options.check_nonce; match options.tracing { - true => self.transact_with_tracer(t, check, ExecutiveTracer::default()), - false => self.transact_with_tracer(t, check, NoopTracer), + true => match options.vm_tracing { + true => self.transact_with_tracer(t, check, ExecutiveTracer::default(), ExecutiveVMTracer::default()), + false => self.transact_with_tracer(t, check, ExecutiveTracer::default(), NoopVMTracer), + }, + false => match options.vm_tracing { + true => self.transact_with_tracer(t, check, NoopTracer, ExecutiveVMTracer::default()), + false => self.transact_with_tracer(t, check, NoopTracer, NoopVMTracer), + }, } } /// Execute transaction/call with tracing enabled - pub fn transact_with_tracer(&'a mut self, t: &SignedTransaction, check_nonce: bool, mut tracer: T) -> Result where T: Tracer { + pub fn transact_with_tracer( + &'a mut self, + t: &SignedTransaction, + check_nonce: bool, + mut tracer: T, + mut vm_tracer: V + ) -> Result where T: Tracer, V: VMTracer { let sender = try!(t.sender().map_err(|e| { let message = format!("Transaction malformed: {:?}", e); ExecutionError::TransactionMalformed(message) @@ -156,7 +175,7 @@ impl<'a> Executive<'a> { code: Some(t.data.clone()), data: None, }; - (self.create(params, &mut substate, &mut tracer), vec![]) + (self.create(params, &mut substate, &mut tracer, &mut vm_tracer), vec![]) }, Action::Call(ref address) => { let params = ActionParams { @@ -172,20 +191,26 @@ impl<'a> Executive<'a> { }; // TODO: move output upstream let mut out = vec![]; - (self.call(params, &mut substate, BytesRef::Flexible(&mut out), &mut tracer), out) + (self.call(params, &mut substate, BytesRef::Flexible(&mut out), &mut tracer, &mut vm_tracer), out) } }; // finalize here! - Ok(try!(self.finalize(t, substate, gas_left, output, tracer.traces().pop()))) + Ok(try!(self.finalize(t, substate, gas_left, output, tracer.traces().pop(), vm_tracer.drain()))) } - fn exec_vm(&mut self, params: ActionParams, unconfirmed_substate: &mut Substate, output_policy: OutputPolicy, tracer: &mut T) - -> evm::Result where T: Tracer { + fn exec_vm( + &mut self, + params: ActionParams, + unconfirmed_substate: &mut Substate, + output_policy: OutputPolicy, + tracer: &mut T, + vm_tracer: &mut V + ) -> evm::Result where T: Tracer, V: VMTracer { // Ordinary execution - keep VM in same thread if (self.depth + 1) % MAX_VM_DEPTH_FOR_THREAD != 0 { let vm_factory = self.vm_factory; - let mut ext = self.as_externalities(OriginInfo::from(¶ms), unconfirmed_substate, output_policy, tracer); + let mut ext = self.as_externalities(OriginInfo::from(¶ms), unconfirmed_substate, output_policy, tracer, vm_tracer); trace!(target: "executive", "ext.schedule.have_delegate_call: {}", ext.schedule().have_delegate_call); return vm_factory.create().exec(params, &mut ext); } @@ -195,7 +220,7 @@ impl<'a> Executive<'a> { // https://github.com/aturon/crossbeam/issues/16 crossbeam::scope(|scope| { let vm_factory = self.vm_factory; - let mut ext = self.as_externalities(OriginInfo::from(¶ms), unconfirmed_substate, output_policy, tracer); + let mut ext = self.as_externalities(OriginInfo::from(¶ms), unconfirmed_substate, output_policy, tracer, vm_tracer); scope.spawn(move || { vm_factory.create().exec(params, &mut ext) @@ -207,8 +232,14 @@ impl<'a> Executive<'a> { /// NOTE. It does not finalize the transaction (doesn't do refunds, nor suicides). /// Modifies the substate and the output. /// Returns either gas_left or `evm::Error`. - pub fn call(&mut self, params: ActionParams, substate: &mut Substate, mut output: BytesRef, tracer: &mut T) - -> evm::Result where T: Tracer { + pub fn call( + &mut self, + params: ActionParams, + substate: &mut Substate, + mut output: BytesRef, + tracer: &mut T, + vm_tracer: &mut V + ) -> evm::Result where T: Tracer, V: VMTracer { // backup used in case of running out of gas self.state.snapshot(); @@ -266,16 +297,22 @@ impl<'a> Executive<'a> { let trace_info = tracer.prepare_trace_call(¶ms); let mut trace_output = tracer.prepare_trace_output(); let mut subtracer = tracer.subtracer(); + let gas = params.gas; if params.code.is_some() { // part of substate that may be reverted let mut unconfirmed_substate = Substate::new(); + // TODO: make ActionParams pass by ref then avoid copy altogether. + let mut subvmtracer = vm_tracer.prepare_subtrace(params.code.as_ref().expect("scope is protected by params.code.is_some condition")); + let res = { - self.exec_vm(params, &mut unconfirmed_substate, OutputPolicy::Return(output, trace_output.as_mut()), &mut subtracer) + self.exec_vm(params, &mut unconfirmed_substate, OutputPolicy::Return(output, trace_output.as_mut()), &mut subtracer, &mut subvmtracer) }; + vm_tracer.done_subtrace(subvmtracer); + trace!(target: "executive", "res={:?}", res); let traces = subtracer.traces(); @@ -309,8 +346,13 @@ impl<'a> Executive<'a> { /// Creates contract with given contract params. /// NOTE. It does not finalize the transaction (doesn't do refunds, nor suicides). /// Modifies the substate. - pub fn create(&mut self, params: ActionParams, substate: &mut Substate, tracer: &mut T) -> evm::Result where T: - Tracer { + pub fn create( + &mut self, + params: ActionParams, + substate: &mut Substate, + tracer: &mut T, + vm_tracer: &mut V + ) -> evm::Result where T: Tracer, V: VMTracer { // backup used in case of running out of gas self.state.snapshot(); @@ -332,10 +374,14 @@ impl<'a> Executive<'a> { let gas = params.gas; let created = params.address.clone(); + let mut subvmtracer = vm_tracer.prepare_subtrace(¶ms.code.as_ref().expect("two ways into create (Externalities::create and Executive::transact_with_tracer); both place `Some(...)` `code` in `params`; qed")); + let res = { - self.exec_vm(params, &mut unconfirmed_substate, OutputPolicy::InitContract(trace_output.as_mut()), &mut subtracer) + self.exec_vm(params, &mut unconfirmed_substate, OutputPolicy::InitContract(trace_output.as_mut()), &mut subtracer, &mut subvmtracer) }; + vm_tracer.done_subtrace(subvmtracer); + match res { Ok(gas_left) => tracer.trace_create( trace_info, @@ -353,7 +399,15 @@ impl<'a> Executive<'a> { } /// Finalizes the transaction (does refunds and suicides). - fn finalize(&mut self, t: &SignedTransaction, substate: Substate, result: evm::Result, output: Bytes, trace: Option) -> ExecutionResult { + fn finalize( + &mut self, + t: &SignedTransaction, + substate: Substate, + result: evm::Result, + output: Bytes, + trace: Option, + vm_trace: Option + ) -> ExecutionResult { let schedule = self.engine.schedule(self.info); // refunds from SSTORE nonzero -> zero @@ -396,7 +450,7 @@ impl<'a> Executive<'a> { contracts_created: vec![], output: output, trace: trace, - vm_trace: None, + vm_trace: vm_trace, }) }, _ => { @@ -409,7 +463,7 @@ impl<'a> Executive<'a> { contracts_created: substate.contracts_created, output: output, trace: trace, - vm_trace: None, + vm_trace: vm_trace, }) }, } diff --git a/ethcore/src/externalities.rs b/ethcore/src/externalities.rs index c78ac09a8..e7a5adb89 100644 --- a/ethcore/src/externalities.rs +++ b/ethcore/src/externalities.rs @@ -19,9 +19,9 @@ use common::*; use state::*; use engine::*; use executive::*; -use evm::{self, Schedule, Ext, ContractCreateResult, MessageCallResult, Factory, VMTraceFunctionBox}; +use evm::{self, Schedule, Ext, ContractCreateResult, MessageCallResult, Factory}; use substate::*; -use trace::Tracer; +use trace::{Tracer, VMTracer}; /// Policy for handling output data on `RETURN` opcode. pub enum OutputPolicy<'a, 'b> { @@ -55,7 +55,7 @@ impl OriginInfo { } /// Implementation of evm Externalities. -pub struct Externalities<'a, T> where T: 'a + Tracer { +pub struct Externalities<'a, T, V> where T: 'a + Tracer, V: 'a + VMTracer { state: &'a mut State, env_info: &'a EnvInfo, engine: &'a Engine, @@ -66,10 +66,10 @@ pub struct Externalities<'a, T> where T: 'a + Tracer { schedule: Schedule, output: OutputPolicy<'a, 'a>, tracer: &'a mut T, - vm_tracer: Option, + vm_tracer: &'a mut V, } -impl<'a, T> Externalities<'a, T> where T: 'a + Tracer { +impl<'a, T, V> Externalities<'a, T, V> where T: 'a + Tracer, V: 'a + VMTracer { #[cfg_attr(feature="dev", allow(too_many_arguments))] /// Basic `Externalities` constructor. pub fn new(state: &'a mut State, @@ -81,6 +81,7 @@ impl<'a, T> Externalities<'a, T> where T: 'a + Tracer { substate: &'a mut Substate, output: OutputPolicy<'a, 'a>, tracer: &'a mut T, + vm_tracer: &'a mut V, ) -> Self { Externalities { state: state, @@ -93,40 +94,12 @@ impl<'a, T> Externalities<'a, T> where T: 'a + Tracer { schedule: engine.schedule(env_info), output: output, tracer: tracer, - vm_tracer: None, - } - } - - #[cfg_attr(feature="dev", allow(too_many_arguments))] - /// Basic `Externalities` constructor. - pub fn with_vm_tracer(state: &'a mut State, - env_info: &'a EnvInfo, - engine: &'a Engine, - vm_factory: &'a Factory, - depth: usize, - origin_info: OriginInfo, - substate: &'a mut Substate, - output: OutputPolicy<'a, 'a>, - tracer: &'a mut T, - vm_tracer: VMTraceFunctionBox, - ) -> Self { - Externalities { - state: state, - env_info: env_info, - engine: engine, - vm_factory: vm_factory, - depth: depth, - origin_info: origin_info, - substate: substate, - schedule: engine.schedule(env_info), - output: output, - tracer: tracer, - vm_tracer: Some(vm_tracer), + vm_tracer: vm_tracer, } } } -impl<'a, T> Ext for Externalities<'a, T> where T: 'a + Tracer { +impl<'a, T, V> Ext for Externalities<'a, T, V> where T: 'a + Tracer, V: 'a + VMTracer { fn storage_at(&self, key: &H256) -> H256 { self.state.storage_at(&self.origin_info.address, key) } @@ -181,7 +154,7 @@ impl<'a, T> Ext for Externalities<'a, T> where T: 'a + Tracer { let mut ex = Executive::from_parent(self.state, self.env_info, self.engine, self.vm_factory, self.depth); // TODO: handle internal error separately - match ex.create(params, self.substate, self.tracer) { + match ex.create(params, self.substate, self.tracer, self.vm_tracer) { Ok(gas_left) => { self.substate.contracts_created.push(address.clone()); ContractCreateResult::Created(address, gas_left) @@ -219,7 +192,7 @@ impl<'a, T> Ext for Externalities<'a, T> where T: 'a + Tracer { let mut ex = Executive::from_parent(self.state, self.env_info, self.engine, self.vm_factory, self.depth); - match ex.call(params, self.substate, BytesRef::Fixed(output), self.tracer) { + match ex.call(params, self.substate, BytesRef::Fixed(output), self.tracer, self.vm_tracer) { Ok(gas_left) => MessageCallResult::Success(gas_left), _ => MessageCallResult::Failed } @@ -316,7 +289,9 @@ impl<'a, T> Ext for Externalities<'a, T> where T: 'a + Tracer { self.substate.sstore_clears_count = self.substate.sstore_clears_count + U256::one(); } - fn vm_tracer(&mut self) -> Option<&mut VMTraceFunctionBox> { self.vm_tracer.as_mut() } + fn trace_prepare_execute(&mut self, pc: usize, instruction: u8, gas_cost: &U256, stack: &[U256]) { + self.vm_tracer.trace_prepare_execute(pc, instruction, gas_cost, stack); + } } #[cfg(test)] diff --git a/ethcore/src/trace/executive_tracer.rs b/ethcore/src/trace/executive_tracer.rs index 41d8c2389..34d8a6936 100644 --- a/ethcore/src/trace/executive_tracer.rs +++ b/ethcore/src/trace/executive_tracer.rs @@ -18,13 +18,13 @@ use util::{Bytes, Address, U256}; use action_params::ActionParams; -use trace::trace::{Trace, Call, Create, Action, Res, CreateResult, CallResult}; -use trace::Tracer; +use trace::trace::{Trace, Call, Create, Action, Res, CreateResult, CallResult, VMTrace, VMOperation}; +use trace::{Tracer, VMTracer}; /// Simple executive tracer. Traces all calls and creates. Ignores delegatecalls. #[derive(Default)] pub struct ExecutiveTracer { - traces: Vec + traces: Vec, } impl Tracer for ExecutiveTracer { @@ -105,3 +105,35 @@ impl Tracer for ExecutiveTracer { self.traces } } + +/// Simple VM tracer. Traces all operations. Ignores delegatecalls. +#[derive(Default)] +pub struct ExecutiveVMTracer { + data: VMTrace, +} + +impl VMTracer for ExecutiveVMTracer { + fn trace_prepare_execute(&mut self, pc: usize, instruction: u8, gas_cost: &U256, stack: &[U256]) { + self.data.operations.push(VMOperation { + pc: pc, + instruction: instruction, + gas_cost: gas_cost.clone(), + stack: stack.iter().cloned().collect(), + }) + } + + fn prepare_subtrace(&self, code: &Bytes) -> Self { + ExecutiveVMTracer { data: VMTrace { + parent_step: self.data.operations.len(), + code: code.clone(), + operations: vec![], + subs: vec![], + }} + } + + fn done_subtrace(&mut self, sub: Self) { + self.data.subs.push(sub.data); + } + + fn drain(self) -> Option { Some(self.data) } +} diff --git a/ethcore/src/trace/mod.rs b/ethcore/src/trace/mod.rs index 21827dd58..ccf9e192e 100644 --- a/ethcore/src/trace/mod.rs +++ b/ethcore/src/trace/mod.rs @@ -32,8 +32,8 @@ pub use self::config::{Config, Switch}; pub use self::db::TraceDB; pub use self::error::Error; pub use types::trace_types::trace::{Trace, VMTrace}; -pub use self::noop_tracer::NoopTracer; -pub use self::executive_tracer::ExecutiveTracer; +pub use self::noop_tracer::{NoopTracer, NoopVMTracer}; +pub use self::executive_tracer::{ExecutiveTracer, ExecutiveVMTracer}; pub use types::trace_types::filter::{Filter, AddressesFilter}; pub use self::import::ImportRequest; pub use self::localized::LocalizedTrace; @@ -91,13 +91,16 @@ pub trait Tracer: Send { /// Used by executive to build VM traces. pub trait VMTracer: Send { /// Trace the preparation to execute a single instruction. - fn trace_prepare_execute(pc: usize, instruction: u8, gas_cost: &U256, stack: &Vec); + fn trace_prepare_execute(&mut self, pc: usize, instruction: u8, gas_cost: &U256, stack: &[U256]); /// Spawn subtracer which will be used to trace deeper levels of execution. - fn subtracer(&self) -> Self where Self: Sized; + fn prepare_subtrace(&self, code: &Bytes) -> Self where Self: Sized; - /// Consumes self and returns all VM traces. - fn traces(self) -> Vec; + /// Spawn subtracer which will be used to trace deeper levels of execution. + fn done_subtrace(&mut self, sub: Self) where Self: Sized; + + /// Consumes self and returns the VM trace. + fn drain(self) -> Option; } /// `DbExtras` provides an interface to query extra data which is not stored in tracesdb, diff --git a/ethcore/src/trace/noop_tracer.rs b/ethcore/src/trace/noop_tracer.rs index 2581e692b..08eff3c4e 100644 --- a/ethcore/src/trace/noop_tracer.rs +++ b/ethcore/src/trace/noop_tracer.rs @@ -18,8 +18,8 @@ use util::{Bytes, Address, U256}; use action_params::ActionParams; -use trace::Tracer; -use trace::trace::{Trace, Call, Create}; +use trace::{Tracer, VMTracer}; +use trace::trace::{Trace, Call, Create, VMTrace}; /// Nonoperative tracer. Does not trace anything. pub struct NoopTracer; @@ -63,3 +63,20 @@ impl Tracer for NoopTracer { vec![] } } + +/// Nonoperative VM tracer. Does not trace anything. +pub struct NoopVMTracer; + +impl VMTracer for NoopVMTracer { + /// Trace the preparation to execute a single instruction. + fn trace_prepare_execute(&mut self, _pc: usize, _instruction: u8, _gas_cost: &U256, _stack: &[U256]) {} + + /// Spawn subtracer which will be used to trace deeper levels of execution. + fn prepare_subtrace(&self, _code: &Bytes) -> Self { NoopVMTracer } + + /// Spawn subtracer which will be used to trace deeper levels of execution. + fn done_subtrace(&mut self, _sub: Self) {} + + /// Consumes self and returns all VM traces. + fn drain(self) -> Option { None } +} diff --git a/ethcore/src/types/trace_types/trace.rs b/ethcore/src/types/trace_types/trace.rs index 5b5132643..5c5c9af15 100644 --- a/ethcore/src/types/trace_types/trace.rs +++ b/ethcore/src/types/trace_types/trace.rs @@ -397,12 +397,11 @@ impl Decodable for VMOperation { } } -#[derive(Debug, Clone, PartialEq, Binary)] +#[derive(Debug, Clone, PartialEq, Binary, Default)] /// A record of a full VM trace for a CALL/CREATE. pub struct VMTrace { - /// 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 step (i.e. index into operations) at which this trace corresponds. + pub parent_step: usize, /// The code to be executed. pub code: Bytes, /// The operations executed. @@ -415,7 +414,7 @@ pub struct VMTrace { impl Encodable for VMTrace { fn rlp_append(&self, s: &mut RlpStream) { s.begin_list(4); - s.append(&self.depth); + s.append(&self.parent_step); s.append(&self.code); s.append(&self.operations); s.append(&self.subs); @@ -426,7 +425,7 @@ impl Decodable for VMTrace { fn decode(decoder: &D) -> Result where D: Decoder { let d = decoder.as_rlp(); let res = VMTrace { - depth: try!(d.val_at(0)), + parent_step: try!(d.val_at(0)), code: try!(d.val_at(1)), operations: try!(d.val_at(2)), subs: try!(d.val_at(3)), From cd16828fefa022cffd7a34cb467ce71d0ad47c86 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sun, 29 May 2016 00:58:52 +0200 Subject: [PATCH 05/16] Integrated VM tracing into JSONRPC. --- ethcore/src/evm/instructions.rs | 3 +- ethcore/src/evm/mod.rs | 1 + ethcore/src/executive.rs | 2 +- ethcore/src/lib.rs | 1 + ethcore/src/trace/executive_tracer.rs | 2 +- rpc/src/v1/impls/ethcore.rs | 43 +++++++++++++++++++++++---- 6 files changed, 44 insertions(+), 8 deletions(-) diff --git a/ethcore/src/evm/instructions.rs b/ethcore/src/evm/instructions.rs index 6a1a06ba9..c313726f1 100644 --- a/ethcore/src/evm/instructions.rs +++ b/ethcore/src/evm/instructions.rs @@ -124,6 +124,7 @@ pub struct InstructionInfo { pub side_effects: bool, pub tier: GasPriceTier } + impl InstructionInfo { pub fn new(name: &'static str, additional: usize, args: usize, ret: usize, side_effects: bool, tier: GasPriceTier) -> InstructionInfo { InstructionInfo { @@ -139,7 +140,7 @@ impl InstructionInfo { #[cfg_attr(rustfmt, rustfmt_skip)] /// Return details about specific instruction -pub fn get_info (instruction: Instruction) -> InstructionInfo { +pub fn get_info(instruction: Instruction) -> InstructionInfo { match instruction { STOP => InstructionInfo::new("STOP", 0, 0, 0, true, GasPriceTier::Zero), ADD => InstructionInfo::new("ADD", 0, 2, 1, false, GasPriceTier::VeryLow), diff --git a/ethcore/src/evm/mod.rs b/ethcore/src/evm/mod.rs index b7816b99c..06ce5e7e8 100644 --- a/ethcore/src/evm/mod.rs +++ b/ethcore/src/evm/mod.rs @@ -33,3 +33,4 @@ pub use self::evm::{Evm, Error, Result}; pub use self::ext::{Ext, ContractCreateResult, MessageCallResult}; pub use self::factory::{Factory, VMType}; pub use self::schedule::Schedule; +pub use self::instructions::get_info; diff --git a/ethcore/src/executive.rs b/ethcore/src/executive.rs index f2be36df4..057c1e67a 100644 --- a/ethcore/src/executive.rs +++ b/ethcore/src/executive.rs @@ -196,7 +196,7 @@ impl<'a> Executive<'a> { }; // finalize here! - Ok(try!(self.finalize(t, substate, gas_left, output, tracer.traces().pop(), vm_tracer.drain()))) + Ok(try!(self.finalize(t, substate, gas_left, output, tracer.traces().pop(), vm_tracer.drain().and_then(|mut i| i.subs.pop())))) } fn exec_vm( diff --git a/ethcore/src/lib.rs b/ethcore/src/lib.rs index 1d4ddadbc..4e1a5be56 100644 --- a/ethcore/src/lib.rs +++ b/ethcore/src/lib.rs @@ -139,3 +139,4 @@ mod tests; mod json_tests; pub use types::*; +pub use evm::get_info; diff --git a/ethcore/src/trace/executive_tracer.rs b/ethcore/src/trace/executive_tracer.rs index 34d8a6936..b51fbaa71 100644 --- a/ethcore/src/trace/executive_tracer.rs +++ b/ethcore/src/trace/executive_tracer.rs @@ -106,7 +106,7 @@ impl Tracer for ExecutiveTracer { } } -/// Simple VM tracer. Traces all operations. Ignores delegatecalls. +/// Simple VM tracer. Traces all operations. #[derive(Default)] pub struct ExecutiveVMTracer { data: VMTrace, diff --git a/rpc/src/v1/impls/ethcore.rs b/rpc/src/v1/impls/ethcore.rs index 4412cdac6..8ed4f0fec 100644 --- a/rpc/src/v1/impls/ethcore.rs +++ b/rpc/src/v1/impls/ethcore.rs @@ -24,7 +24,9 @@ use std::collections::BTreeMap; use jsonrpc_core::*; use ethminer::{MinerService}; use ethcore::client::{BlockChainClient}; +use ethcore::trace::VMTrace; use ethcore::transaction::{Transaction as EthTransaction, SignedTransaction, Action}; +use ethcore::get_info; use v1::traits::Ethcore; use v1::types::{Bytes, CallRequest}; @@ -66,6 +68,34 @@ impl EthcoreClient where C: BlockChainClient, M: MinerService { } } +fn vm_trace_to_object(t: &VMTrace) -> Value { + let mut ret = BTreeMap::new(); + ret.insert("code".to_owned(), to_value(&t.code).unwrap()); + + let mut subs = t.subs.iter(); + let mut next_sub = subs.next(); + + let ops = t.operations + .iter() + .enumerate() + .map(|(i, op)| { + let mut m = map![ + "pc".to_owned() => to_value(&op.pc).unwrap(), + "inst".to_owned() => to_value(&get_info(op.instruction).name).unwrap(), + "gas_cost".to_owned() => to_value(&op.gas_cost).unwrap(), + "stack".to_owned() => to_value(&op.stack).unwrap() + ]; + if next_sub.is_some() && next_sub.unwrap().parent_step == i { + m.insert("sub".to_owned(), vm_trace_to_object(next_sub.unwrap())); + next_sub = subs.next(); + } + Value::Object(m) + }) + .collect::>(); + ret.insert("ops".to_owned(), Value::Array(ops)); + Value::Object(ret) +} + impl Ethcore for EthcoreClient where C: BlockChainClient + 'static, M: MinerService + 'static { fn set_min_gas_price(&self, params: Params) -> Result { @@ -162,11 +192,14 @@ impl Ethcore for EthcoreClient where C: BlockChainClient + 'static, from_params(params) .and_then(|(request,)| { let signed = try!(self.sign_call(request)); - let _ = take_weak!(self.client).call(&signed, true); - // TODO: construct JSON trace from _.vm_trace. - let mut ret = Vec::new(); - ret.push(Value::Object(map!["foo".to_owned() => Value::String("var".to_owned())])); - Ok(Value::Array(ret)) + let r = take_weak!(self.client).call(&signed, true); + trace!(target: "jsonrpc", "returned {:?}", r); + if let Ok(executed) = r { + if let Some(vm_trace) = executed.vm_trace { + return Ok(vm_trace_to_object(&vm_trace)); + } + } + Ok(Value::Null) }) } } From c20eaf98ec52218c59fe440132dec48114e83cd9 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sun, 29 May 2016 11:37:35 +0200 Subject: [PATCH 06/16] Fix ethcore module tests. --- ethcore/src/executive.rs | 19 ++++++++++--------- ethcore/src/externalities.rs | 20 +++++++++++++------- 2 files changed, 23 insertions(+), 16 deletions(-) diff --git a/ethcore/src/executive.rs b/ethcore/src/executive.rs index 057c1e67a..d961b80c3 100644 --- a/ethcore/src/executive.rs +++ b/ethcore/src/executive.rs @@ -496,6 +496,7 @@ mod tests { use tests::helpers::*; use trace::trace; use trace::{Trace, Tracer, NoopTracer, ExecutiveTracer}; + use trace::{VMTrace, VMTracer, NoopVMTracer, ExecutiveVMTracer}; #[test] fn test_contract_address() { @@ -524,7 +525,7 @@ mod tests { let gas_left = { let mut ex = Executive::new(&mut state, &info, &engine, &factory); - ex.create(params, &mut substate, &mut NoopTracer).unwrap() + ex.create(params, &mut substate, &mut NoopTracer, &mut NoopVMTracer).unwrap() }; assert_eq!(gas_left, U256::from(79_975)); @@ -583,7 +584,7 @@ mod tests { let gas_left = { let mut ex = Executive::new(&mut state, &info, &engine, &factory); - ex.create(params, &mut substate, &mut NoopTracer).unwrap() + ex.create(params, &mut substate, &mut NoopTracer, &mut NoopVMTracer).unwrap() }; assert_eq!(gas_left, U256::from(62_976)); @@ -640,7 +641,7 @@ mod tests { let gas_left = { let mut ex = Executive::new(&mut state, &info, &engine, &factory); let output = BytesRef::Fixed(&mut[0u8;0]); - ex.call(params, &mut substate, output, &mut tracer).unwrap() + ex.call(params, &mut substate, output, &mut tracer, &mut NoopVMTracer).unwrap() }; let expected_trace = vec![ Trace { @@ -711,7 +712,7 @@ mod tests { let gas_left = { let mut ex = Executive::new(&mut state, &info, &engine, &factory); - ex.create(params.clone(), &mut substate, &mut tracer).unwrap() + ex.create(params.clone(), &mut substate, &mut tracer, &mut NoopVMTracer).unwrap() }; let expected_trace = vec![Trace { @@ -780,7 +781,7 @@ mod tests { let gas_left = { let mut ex = Executive::new(&mut state, &info, &engine, &factory); - ex.create(params, &mut substate, &mut NoopTracer).unwrap() + ex.create(params, &mut substate, &mut NoopTracer, &mut NoopVMTracer).unwrap() }; assert_eq!(gas_left, U256::from(62_976)); @@ -832,7 +833,7 @@ mod tests { { let mut ex = Executive::new(&mut state, &info, &engine, &factory); - ex.create(params, &mut substate, &mut NoopTracer).unwrap(); + ex.create(params, &mut substate, &mut NoopTracer, &mut NoopVMTracer).unwrap(); } assert_eq!(substate.contracts_created.len(), 1); @@ -893,7 +894,7 @@ mod tests { let gas_left = { let mut ex = Executive::new(&mut state, &info, &engine, &factory); - ex.call(params, &mut substate, BytesRef::Fixed(&mut []), &mut NoopTracer).unwrap() + ex.call(params, &mut substate, BytesRef::Fixed(&mut []), &mut NoopTracer, &mut NoopVMTracer).unwrap() }; assert_eq!(gas_left, U256::from(73_237)); @@ -938,7 +939,7 @@ mod tests { let gas_left = { let mut ex = Executive::new(&mut state, &info, &engine, &factory); - ex.call(params, &mut substate, BytesRef::Fixed(&mut []), &mut NoopTracer).unwrap() + ex.call(params, &mut substate, BytesRef::Fixed(&mut []), &mut NoopTracer, &mut NoopVMTracer).unwrap() }; assert_eq!(gas_left, U256::from(59_870)); @@ -1140,7 +1141,7 @@ mod tests { let result = { let mut ex = Executive::new(&mut state, &info, &engine, &factory); - ex.create(params, &mut substate, &mut NoopTracer) + ex.create(params, &mut substate, &mut NoopTracer, &mut NoopVMTracer) }; match result { diff --git a/ethcore/src/externalities.rs b/ethcore/src/externalities.rs index e7a5adb89..cd7564f70 100644 --- a/ethcore/src/externalities.rs +++ b/ethcore/src/externalities.rs @@ -303,7 +303,7 @@ mod tests { use substate::*; use tests::helpers::*; use super::*; - use trace::{NoopTracer}; + use trace::{NoopTracer, NoopVMTracer}; fn get_test_origin() -> OriginInfo { OriginInfo { @@ -355,9 +355,10 @@ mod tests { let mut setup = TestSetup::new(); let state = setup.state.reference_mut(); let mut tracer = NoopTracer; + let mut vm_tracer = NoopVMTracer; let vm_factory = Default::default(); - let ext = Externalities::new(state, &setup.env_info, &*setup.engine, &vm_factory, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None), &mut tracer); + let ext = Externalities::new(state, &setup.env_info, &*setup.engine, &vm_factory, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None), &mut tracer, &mut vm_tracer); assert_eq!(ext.env_info().number, 100); } @@ -367,9 +368,10 @@ mod tests { let mut setup = TestSetup::new(); let state = setup.state.reference_mut(); let mut tracer = NoopTracer; + let mut vm_tracer = NoopVMTracer; let vm_factory = Default::default(); - let ext = Externalities::new(state, &setup.env_info, &*setup.engine, &vm_factory, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None), &mut tracer); + let ext = Externalities::new(state, &setup.env_info, &*setup.engine, &vm_factory, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None), &mut tracer, &mut vm_tracer); let hash = ext.blockhash(&U256::from_str("0000000000000000000000000000000000000000000000000000000000120000").unwrap()); @@ -389,9 +391,10 @@ mod tests { } let state = setup.state.reference_mut(); let mut tracer = NoopTracer; + let mut vm_tracer = NoopVMTracer; let vm_factory = Default::default(); - let ext = Externalities::new(state, &setup.env_info, &*setup.engine, &vm_factory, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None), &mut tracer); + let ext = Externalities::new(state, &setup.env_info, &*setup.engine, &vm_factory, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None), &mut tracer, &mut vm_tracer); let hash = ext.blockhash(&U256::from_str("0000000000000000000000000000000000000000000000000000000000120000").unwrap()); @@ -404,9 +407,10 @@ mod tests { let mut setup = TestSetup::new(); let state = setup.state.reference_mut(); let mut tracer = NoopTracer; + let mut vm_tracer = NoopVMTracer; let vm_factory = Default::default(); - let mut ext = Externalities::new(state, &setup.env_info, &*setup.engine, &vm_factory, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None), &mut tracer); + let mut ext = Externalities::new(state, &setup.env_info, &*setup.engine, &vm_factory, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None), &mut tracer, &mut vm_tracer); let mut output = vec![]; @@ -429,10 +433,11 @@ mod tests { let mut setup = TestSetup::new(); let state = setup.state.reference_mut(); let mut tracer = NoopTracer; + let mut vm_tracer = NoopVMTracer; { let vm_factory = Default::default(); - let mut ext = Externalities::new(state, &setup.env_info, &*setup.engine, &vm_factory, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None), &mut tracer); + let mut ext = Externalities::new(state, &setup.env_info, &*setup.engine, &vm_factory, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None), &mut tracer, &mut vm_tracer); ext.log(log_topics, &log_data); } @@ -446,10 +451,11 @@ mod tests { let mut setup = TestSetup::new(); let state = setup.state.reference_mut(); let mut tracer = NoopTracer; + let mut vm_tracer = NoopVMTracer; { let vm_factory = Default::default(); - let mut ext = Externalities::new(state, &setup.env_info, &*setup.engine, &vm_factory, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None), &mut tracer); + let mut ext = Externalities::new(state, &setup.env_info, &*setup.engine, &vm_factory, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None), &mut tracer, &mut vm_tracer); ext.suicide(&refund_account); } From 22c4298bee99b9c5d26900d94a0ac86c3616cef1 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sun, 29 May 2016 13:05:41 +0200 Subject: [PATCH 07/16] Add tests for VM tracing. --- ethcore/src/executive.rs | 68 +++++++++++++++++++++++---- ethcore/src/trace/executive_tracer.rs | 4 +- ethcore/src/trace/mod.rs | 2 +- util/src/common.rs | 7 +++ 4 files changed, 70 insertions(+), 11 deletions(-) diff --git a/ethcore/src/executive.rs b/ethcore/src/executive.rs index d961b80c3..069cd2f35 100644 --- a/ethcore/src/executive.rs +++ b/ethcore/src/executive.rs @@ -196,7 +196,7 @@ impl<'a> Executive<'a> { }; // finalize here! - Ok(try!(self.finalize(t, substate, gas_left, output, tracer.traces().pop(), vm_tracer.drain().and_then(|mut i| i.subs.pop())))) + Ok(try!(self.finalize(t, substate, gas_left, output, tracer.traces().pop(), vm_tracer.drain()))) } fn exec_vm( @@ -496,7 +496,7 @@ mod tests { use tests::helpers::*; use trace::trace; use trace::{Trace, Tracer, NoopTracer, ExecutiveTracer}; - use trace::{VMTrace, VMTracer, NoopVMTracer, ExecutiveVMTracer}; + use trace::{VMTrace, VMOperation, VMTracer, NoopVMTracer, ExecutiveVMTracer}; #[test] fn test_contract_address() { @@ -601,7 +601,7 @@ mod tests { // 52 // 60 1d - push 29 // 60 03 - push 3 - // 60 17 - push 17 + // 60 17 - push 23 // f0 - create // 60 00 - push 0 // 55 sstore @@ -637,13 +637,16 @@ mod tests { let engine = TestEngine::new(5); let mut substate = Substate::new(); let mut tracer = ExecutiveTracer::default(); + let mut vm_tracer = ExecutiveVMTracer::default(); let gas_left = { let mut ex = Executive::new(&mut state, &info, &engine, &factory); let output = BytesRef::Fixed(&mut[0u8;0]); - ex.call(params, &mut substate, output, &mut tracer, &mut NoopVMTracer).unwrap() + ex.call(params, &mut substate, output, &mut tracer, &mut vm_tracer).unwrap() }; + assert_eq!(gas_left, U256::from(44_752)); + let expected_trace = vec![ Trace { depth: 0, action: trace::Action::Call(trace::Call { @@ -674,7 +677,39 @@ mod tests { }] }]; assert_eq!(tracer.traces(), expected_trace); - assert_eq!(gas_left, U256::from(44_752)); + + let expected_vm_trace = VMTrace { + parent_step: 0, + code: vec![124, 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, 96, 0, 82, 96, 29, 96, 3, 96, 23, 240, 96, 0, 85], + operations: vec![ + VMOperation { pc: 1, instruction: 124, gas_cost: x!(3), stack: vecx![] }, + VMOperation { pc: 31, instruction: 96, gas_cost: x!(3), stack: vecx![U256::from_dec_str("2589892687202724018173567190521546555304938078595079151649957320078677").unwrap()] }, + VMOperation { pc: 33, instruction: 82, gas_cost: x!(6), stack: vecx![U256::from_dec_str("2589892687202724018173567190521546555304938078595079151649957320078677").unwrap(), 0] }, + VMOperation { pc: 34, instruction: 96, gas_cost: x!(3), stack: vecx![] }, + VMOperation { pc: 36, instruction: 96, gas_cost: x!(3), stack: vecx![29] }, + VMOperation { pc: 38, instruction: 96, gas_cost: x!(3), stack: vecx![29, 3] }, + VMOperation { pc: 40, instruction: 240, gas_cost: x!(32000), stack: vecx![29, 3, 23] }, + VMOperation { pc: 41, instruction: 96, gas_cost: x!(3), stack: vecx![U256::from_dec_str("1135198453258042933984631383966629874710669425204").unwrap()] }, + VMOperation { pc: 43, instruction: 85, gas_cost: x!(20000), stack: vecx![U256::from_dec_str("1135198453258042933984631383966629874710669425204").unwrap(), 0] } + ], + subs: vec![ + VMTrace { + parent_step: 7, + code: 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], + operations: vec![ + VMOperation { pc: 1, instruction: 96, gas_cost: x!(3), stack: vecx![] }, + VMOperation { pc: 3, instruction: 128, gas_cost: x!(3), stack: vecx![16] }, + VMOperation { pc: 4, instruction: 96, gas_cost: x!(3), stack: vecx![16, 16] }, + VMOperation { pc: 6, instruction: 96, gas_cost: x!(3), stack: vecx![16, 16, 12] }, + VMOperation { pc: 8, instruction: 57, gas_cost: x!(9), stack: vecx![16, 16, 12, 0] }, + VMOperation { pc: 9, instruction: 96, gas_cost: x!(3), stack: vecx![16] }, + VMOperation { pc: 11, instruction: 243, gas_cost: x!(0), stack: vecx![16, 0] } + ], + subs: vec![] + } + ] + }; + assert_eq!(vm_tracer.drain().unwrap(), expected_vm_trace); } evm_test!{test_create_contract: test_create_contract_jit, test_create_contract_int} @@ -709,12 +744,15 @@ mod tests { let engine = TestEngine::new(5); let mut substate = Substate::new(); let mut tracer = ExecutiveTracer::default(); + let mut vm_tracer = ExecutiveVMTracer::default(); let gas_left = { let mut ex = Executive::new(&mut state, &info, &engine, &factory); - ex.create(params.clone(), &mut substate, &mut tracer, &mut NoopVMTracer).unwrap() + ex.create(params.clone(), &mut substate, &mut tracer, &mut vm_tracer).unwrap() }; + assert_eq!(gas_left, U256::from(96_776)); + let expected_trace = vec![Trace { depth: 0, action: trace::Action::Create(trace::Create { @@ -730,9 +768,23 @@ mod tests { }), subs: vec![] }]; - assert_eq!(tracer.traces(), expected_trace); - assert_eq!(gas_left, U256::from(96_776)); + + let expected_vm_trace = VMTrace { + parent_step: 0, + code: 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], + operations: vec![ + VMOperation { pc: 1, instruction: 96, gas_cost: x!(3), stack: vecx![] }, + VMOperation { pc: 3, instruction: 128, gas_cost: x!(3), stack: vecx![16] }, + VMOperation { pc: 4, instruction: 96, gas_cost: x!(3), stack: vecx![16, 16] }, + VMOperation { pc: 6, instruction: 96, gas_cost: x!(3), stack: vecx![16, 16, 12] }, + VMOperation { pc: 8, instruction: 57, gas_cost: x!(9), stack: vecx![16, 16, 12, 0] }, + VMOperation { pc: 9, instruction: 96, gas_cost: x!(3), stack: vecx![16] }, + VMOperation { pc: 11, instruction: 243, gas_cost: x!(0), stack: vecx![16, 0] } + ], + subs: vec![] + }; + assert_eq!(vm_tracer.drain().unwrap(), expected_vm_trace); } evm_test!{test_create_contract_value_too_high: test_create_contract_value_too_high_jit, test_create_contract_value_too_high_int} diff --git a/ethcore/src/trace/executive_tracer.rs b/ethcore/src/trace/executive_tracer.rs index b51fbaa71..5a3ac27cd 100644 --- a/ethcore/src/trace/executive_tracer.rs +++ b/ethcore/src/trace/executive_tracer.rs @@ -117,7 +117,7 @@ impl VMTracer for ExecutiveVMTracer { self.data.operations.push(VMOperation { pc: pc, instruction: instruction, - gas_cost: gas_cost.clone(), + gas_cost: gas_cost.clone(), stack: stack.iter().cloned().collect(), }) } @@ -135,5 +135,5 @@ impl VMTracer for ExecutiveVMTracer { self.data.subs.push(sub.data); } - fn drain(self) -> Option { Some(self.data) } + fn drain(mut self) -> Option { self.data.subs.pop() } } diff --git a/ethcore/src/trace/mod.rs b/ethcore/src/trace/mod.rs index ccf9e192e..eeddc3905 100644 --- a/ethcore/src/trace/mod.rs +++ b/ethcore/src/trace/mod.rs @@ -31,7 +31,7 @@ pub use self::block::BlockTraces; pub use self::config::{Config, Switch}; pub use self::db::TraceDB; pub use self::error::Error; -pub use types::trace_types::trace::{Trace, VMTrace}; +pub use types::trace_types::trace::{Trace, VMTrace, VMOperation}; pub use self::noop_tracer::{NoopTracer, NoopVMTracer}; pub use self::executive_tracer::{ExecutiveTracer, ExecutiveVMTracer}; pub use types::trace_types::filter::{Filter, AddressesFilter}; diff --git a/util/src/common.rs b/util/src/common.rs index a4ba41f82..e299f5be2 100644 --- a/util/src/common.rs +++ b/util/src/common.rs @@ -52,6 +52,13 @@ macro_rules! mapx { } } +#[macro_export] +macro_rules! vecx { + ( $( $x:expr ),* ) => { + vec![ $( From::from($x) ),* ] + } +} + #[macro_export] macro_rules! x { ( $x:expr ) => { From af05939d748ad4b49572f3544e74803b59fd6d0b Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sun, 29 May 2016 14:01:34 +0200 Subject: [PATCH 08/16] Fix consensus test code. --- ethcore/src/json_tests/executive.rs | 36 +++++++++++++++++------------ 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/ethcore/src/json_tests/executive.rs b/ethcore/src/json_tests/executive.rs index 9e9620169..c5d781ab2 100644 --- a/ethcore/src/json_tests/executive.rs +++ b/ethcore/src/json_tests/executive.rs @@ -25,6 +25,7 @@ use substate::*; use tests::helpers::*; use ethjson; use trace::{Tracer, NoopTracer}; +use trace::{VMTracer, NoopVMTracer}; #[derive(Debug, PartialEq)] struct CallCreate { @@ -48,32 +49,35 @@ impl From for CallCreate { /// Tiny wrapper around executive externalities. /// Stores callcreates. -struct TestExt<'a, T> where T: 'a + Tracer { - ext: Externalities<'a, T>, +struct TestExt<'a, T, V> where T: 'a + Tracer, V: 'a + VMTracer { + ext: Externalities<'a, T, V>, callcreates: Vec, contract_address: Address } -impl<'a, T> TestExt<'a, T> where T: 'a + Tracer { - fn new(state: &'a mut State, - info: &'a EnvInfo, - engine: &'a Engine, - vm_factory: &'a Factory, - depth: usize, - origin_info: OriginInfo, - substate: &'a mut Substate, - output: OutputPolicy<'a, 'a>, - address: Address, - tracer: &'a mut T) -> Self { +impl<'a, T, V> TestExt<'a, T, V> where T: 'a + Tracer, V: 'a + VMTracer { + fn new( + state: &'a mut State, + info: &'a EnvInfo, + engine: &'a Engine, + vm_factory: &'a Factory, + depth: usize, + origin_info: OriginInfo, + substate: &'a mut Substate, + output: OutputPolicy<'a, 'a>, + address: Address, + tracer: &'a mut T, + vm_tracer: &'a mut V, + ) -> Self { TestExt { contract_address: contract_address(&address, &state.nonce(&address)), - ext: Externalities::new(state, info, engine, vm_factory, depth, origin_info, substate, output, tracer), + ext: Externalities::new(state, info, engine, vm_factory, depth, origin_info, substate, output, tracer, vm_tracer), callcreates: vec![] } } } -impl<'a, T> Ext for TestExt<'a, T> where T: Tracer { +impl<'a, T, V> Ext for TestExt<'a, T, V> where T: Tracer, V: VMTracer { fn storage_at(&self, key: &H256) -> H256 { self.ext.storage_at(key) } @@ -186,6 +190,7 @@ fn do_json_test_for(vm_type: &VMType, json_data: &[u8]) -> Vec { let mut substate = Substate::new(); let mut tracer = NoopTracer; + let mut vm_tracer = NoopVMTracer; let mut output = vec![]; // execute @@ -201,6 +206,7 @@ fn do_json_test_for(vm_type: &VMType, json_data: &[u8]) -> Vec { OutputPolicy::Return(BytesRef::Flexible(&mut output), None), params.address.clone(), &mut tracer, + &mut vm_tracer, ); let evm = vm_factory.create(); let res = evm.exec(params, &mut ex); From 11f4e8cb73083f6fe8662465ae6df367008b57f4 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 30 May 2016 11:53:20 +0200 Subject: [PATCH 09/16] Fix mock tests. --- rpc/src/v1/impls/ethcore.rs | 4 +- rpc/src/v1/tests/mocked/eth.rs | 4 ++ rpc/src/v1/tests/mocked/ethcore.rs | 61 ++++++++++++++++++++---------- 3 files changed, 47 insertions(+), 22 deletions(-) diff --git a/rpc/src/v1/impls/ethcore.rs b/rpc/src/v1/impls/ethcore.rs index 25d9bd2ae..7c1b30974 100644 --- a/rpc/src/v1/impls/ethcore.rs +++ b/rpc/src/v1/impls/ethcore.rs @@ -26,7 +26,6 @@ use ethminer::MinerService; use ethcore::client::BlockChainClient; use ethcore::trace::VMTrace; use ethcore::transaction::{Transaction as EthTransaction, SignedTransaction, Action}; -use ethcore::get_info; use v1::traits::Ethcore; use v1::types::{Bytes, CallRequest}; @@ -81,8 +80,7 @@ fn vm_trace_to_object(t: &VMTrace) -> Value { .map(|(i, op)| { let mut m = map![ "pc".to_owned() => to_value(&op.pc).unwrap(), - "inst".to_owned() => to_value(&get_info(op.instruction).name).unwrap(), - "gas_cost".to_owned() => to_value(&op.gas_cost).unwrap(), + "cost".to_owned() => to_value(&op.gas_cost).unwrap(), "stack".to_owned() => to_value(&op.stack).unwrap() ]; if next_sub.is_some() && next_sub.unwrap().parent_step == i { diff --git a/rpc/src/v1/tests/mocked/eth.rs b/rpc/src/v1/tests/mocked/eth.rs index 32a2cd99a..3e9c67f46 100644 --- a/rpc/src/v1/tests/mocked/eth.rs +++ b/rpc/src/v1/tests/mocked/eth.rs @@ -353,6 +353,7 @@ fn rpc_eth_call() { contracts_created: vec![], output: vec![0x12, 0x34, 0xff], trace: None, + vm_trace: None, }); let request = r#"{ @@ -386,6 +387,7 @@ fn rpc_eth_call_default_block() { contracts_created: vec![], output: vec![0x12, 0x34, 0xff], trace: None, + vm_trace: None, }); let request = r#"{ @@ -418,6 +420,7 @@ fn rpc_eth_estimate_gas() { contracts_created: vec![], output: vec![0x12, 0x34, 0xff], trace: None, + vm_trace: None, }); let request = r#"{ @@ -451,6 +454,7 @@ fn rpc_eth_estimate_gas_default_block() { contracts_created: vec![], output: vec![0x12, 0x34, 0xff], trace: None, + vm_trace: None, }); let request = r#"{ diff --git a/rpc/src/v1/tests/mocked/ethcore.rs b/rpc/src/v1/tests/mocked/ethcore.rs index cc822daef..74da13535 100644 --- a/rpc/src/v1/tests/mocked/ethcore.rs +++ b/rpc/src/v1/tests/mocked/ethcore.rs @@ -19,16 +19,22 @@ use std::str::FromStr; use jsonrpc_core::IoHandler; use v1::{Ethcore, EthcoreClient}; use ethminer::MinerService; +use ethcore::client::{TestBlockChainClient}; use v1::tests::helpers::TestMinerService; use util::numbers::*; use rustc_serialize::hex::FromHex; use util::log::RotatingLogger; use util::network_settings::NetworkSettings; +fn blockchain_client() -> Arc { + let client = TestBlockChainClient::new(); + Arc::new(client) +} fn miner_service() -> Arc { Arc::new(TestMinerService::default()) } + fn logger() -> Arc { Arc::new(RotatingLogger::new("rpc=trace".to_owned())) } @@ -45,14 +51,15 @@ fn settings() -> Arc { }) } -fn ethcore_client(miner: &Arc) -> EthcoreClient { - EthcoreClient::new(&miner, logger(), settings()) +fn ethcore_client(client: &Arc, miner: &Arc) -> EthcoreClient { + EthcoreClient::new(&client, &miner, logger(), settings()) } #[test] fn rpc_ethcore_extra_data() { let miner = miner_service(); - let ethcore = ethcore_client(&miner).to_delegate(); + let client = blockchain_client(); + let ethcore = ethcore_client(&client, &miner).to_delegate(); let io = IoHandler::new(); io.add_delegate(ethcore); @@ -68,7 +75,8 @@ fn rpc_ethcore_default_extra_data() { use util::ToPretty; let miner = miner_service(); - let ethcore = ethcore_client(&miner).to_delegate(); + let client = blockchain_client(); + let ethcore = ethcore_client(&client, &miner).to_delegate(); let io = IoHandler::new(); io.add_delegate(ethcore); @@ -81,7 +89,8 @@ fn rpc_ethcore_default_extra_data() { #[test] fn rpc_ethcore_gas_floor_target() { let miner = miner_service(); - let ethcore = ethcore_client(&miner).to_delegate(); + let client = blockchain_client(); + let ethcore = ethcore_client(&client, &miner).to_delegate(); let io = IoHandler::new(); io.add_delegate(ethcore); @@ -94,7 +103,8 @@ fn rpc_ethcore_gas_floor_target() { #[test] fn rpc_ethcore_min_gas_price() { let miner = miner_service(); - let ethcore = ethcore_client(&miner).to_delegate(); + let client = blockchain_client(); + let ethcore = ethcore_client(&client, &miner).to_delegate(); let io = IoHandler::new(); io.add_delegate(ethcore); @@ -107,7 +117,8 @@ fn rpc_ethcore_min_gas_price() { #[test] fn rpc_ethcore_set_min_gas_price() { let miner = miner_service(); - let ethcore = ethcore_client(&miner).to_delegate(); + let client = blockchain_client(); + let ethcore = ethcore_client(&client, &miner).to_delegate(); let io = IoHandler::new(); io.add_delegate(ethcore); @@ -121,7 +132,8 @@ fn rpc_ethcore_set_min_gas_price() { #[test] fn rpc_ethcore_set_gas_floor_target() { let miner = miner_service(); - let ethcore = ethcore_client(&miner).to_delegate(); + let client = blockchain_client(); + let ethcore = ethcore_client(&client, &miner).to_delegate(); let io = IoHandler::new(); io.add_delegate(ethcore); @@ -135,7 +147,8 @@ fn rpc_ethcore_set_gas_floor_target() { #[test] fn rpc_ethcore_set_extra_data() { let miner = miner_service(); - let ethcore = ethcore_client(&miner).to_delegate(); + let client = blockchain_client(); + let ethcore = ethcore_client(&client, &miner).to_delegate(); let io = IoHandler::new(); io.add_delegate(ethcore); @@ -149,7 +162,8 @@ fn rpc_ethcore_set_extra_data() { #[test] fn rpc_ethcore_set_author() { let miner = miner_service(); - let ethcore = ethcore_client(&miner).to_delegate(); + let client = blockchain_client(); + let ethcore = ethcore_client(&client, &miner).to_delegate(); let io = IoHandler::new(); io.add_delegate(ethcore); @@ -163,10 +177,11 @@ fn rpc_ethcore_set_author() { #[test] fn rpc_ethcore_dev_logs() { let miner = miner_service(); + let client = blockchain_client(); let logger = logger(); logger.append("a".to_owned()); logger.append("b".to_owned()); - let ethcore = EthcoreClient::new(&miner, logger.clone(), settings()).to_delegate(); + let ethcore = EthcoreClient::new(&client, &miner, logger.clone(), settings()).to_delegate(); let io = IoHandler::new(); io.add_delegate(ethcore); @@ -179,7 +194,8 @@ fn rpc_ethcore_dev_logs() { #[test] fn rpc_ethcore_dev_logs_levels() { let miner = miner_service(); - let ethcore = ethcore_client(&miner).to_delegate(); + let client = blockchain_client(); + let ethcore = ethcore_client(&client, &miner).to_delegate(); let io = IoHandler::new(); io.add_delegate(ethcore); @@ -191,7 +207,8 @@ fn rpc_ethcore_dev_logs_levels() { #[test] fn rpc_ethcore_set_transactions_limit() { let miner = miner_service(); - let ethcore = ethcore_client(&miner).to_delegate(); + let client = blockchain_client(); + let ethcore = ethcore_client(&client, &miner).to_delegate(); let io = IoHandler::new(); io.add_delegate(ethcore); @@ -205,7 +222,8 @@ fn rpc_ethcore_set_transactions_limit() { #[test] fn rpc_ethcore_transactions_limit() { let miner = miner_service(); - let ethcore = ethcore_client(&miner).to_delegate(); + let client = blockchain_client(); + let ethcore = ethcore_client(&client, &miner).to_delegate(); let io = IoHandler::new(); io.add_delegate(ethcore); @@ -218,7 +236,8 @@ fn rpc_ethcore_transactions_limit() { #[test] fn rpc_ethcore_net_chain() { let miner = miner_service(); - let ethcore = ethcore_client(&miner).to_delegate(); + let client = blockchain_client(); + let ethcore = ethcore_client(&client, &miner).to_delegate(); let io = IoHandler::new(); io.add_delegate(ethcore); @@ -231,7 +250,8 @@ fn rpc_ethcore_net_chain() { #[test] fn rpc_ethcore_net_max_peers() { let miner = miner_service(); - let ethcore = ethcore_client(&miner).to_delegate(); + let client = blockchain_client(); + let ethcore = ethcore_client(&client, &miner).to_delegate(); let io = IoHandler::new(); io.add_delegate(ethcore); @@ -244,7 +264,8 @@ fn rpc_ethcore_net_max_peers() { #[test] fn rpc_ethcore_net_port() { let miner = miner_service(); - let ethcore = ethcore_client(&miner).to_delegate(); + let client = blockchain_client(); + let ethcore = ethcore_client(&client, &miner).to_delegate(); let io = IoHandler::new(); io.add_delegate(ethcore); @@ -257,7 +278,8 @@ fn rpc_ethcore_net_port() { #[test] fn rpc_ethcore_rpc_settings() { let miner = miner_service(); - let ethcore = ethcore_client(&miner).to_delegate(); + let client = blockchain_client(); + let ethcore = ethcore_client(&client, &miner).to_delegate(); let io = IoHandler::new(); io.add_delegate(ethcore); @@ -270,7 +292,8 @@ fn rpc_ethcore_rpc_settings() { #[test] fn rpc_ethcore_node_name() { let miner = miner_service(); - let ethcore = ethcore_client(&miner).to_delegate(); + let client = blockchain_client(); + let ethcore = ethcore_client(&client, &miner).to_delegate(); let io = IoHandler::new(); io.add_delegate(ethcore); From 79503e4f149b77411d04d640dcbcbd046af8ee69 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 30 May 2016 17:19:15 +0200 Subject: [PATCH 10/16] Added VM trace information for post-execution stuff. --- ethcore/src/evm/ext.rs | 6 +- ethcore/src/evm/interpreter.rs | 47 ++++++++++-- ethcore/src/externalities.rs | 8 +- ethcore/src/trace/executive_tracer.rs | 19 ++++- ethcore/src/trace/mod.rs | 6 +- ethcore/src/trace/noop_tracer.rs | 5 +- ethcore/src/types/trace_types/trace.rs | 102 ++++++++++++++++++++++--- rpc/src/v1/impls/ethcore.rs | 26 ++++++- util/src/rlp/rlpstream.rs | 12 +++ util/src/rlp/untrusted_rlp.rs | 30 +++----- 10 files changed, 212 insertions(+), 49 deletions(-) diff --git a/ethcore/src/evm/ext.rs b/ethcore/src/evm/ext.rs index 574a11773..1c21e467a 100644 --- a/ethcore/src/evm/ext.rs +++ b/ethcore/src/evm/ext.rs @@ -106,7 +106,9 @@ pub trait Ext { /// Increments sstore refunds count by 1. fn inc_sstore_clears(&mut self); - // TODO work out a way of not having this here but go via . /// Prepare to trace an operation. Passthrough for the VM trace. - fn trace_prepare_execute(&mut self, _pc: usize, _instruction: u8, _gas_cost: &U256, _stack: &[U256]) {} + fn trace_prepare_execute(&mut self, _pc: usize, _instruction: u8, _gas_cost: &U256) -> bool { false } + + /// Trace the finalised execution of a single instruction. + fn trace_executed(&mut self, _gas_used: U256, _stack_push: &[U256], _mem_diff: Option<(usize, &[u8])>, _store_diff: Option<(U256, U256)>) {} } diff --git a/ethcore/src/evm/interpreter.rs b/ethcore/src/evm/interpreter.rs index d4948919f..c63837e49 100644 --- a/ethcore/src/evm/interpreter.rs +++ b/ethcore/src/evm/interpreter.rs @@ -19,7 +19,7 @@ use common::*; use trace::VMTracer; use super::instructions as instructions; -use super::instructions::Instruction; +use super::instructions::{Instruction, get_info}; use std::marker::Copy; use evm::{self, MessageCallResult, ContractCreateResult}; @@ -71,7 +71,7 @@ trait Stack { /// Get number of elements on Stack fn size(&self) -> usize; /// Returns all data on stack. - fn peek_all(&mut self) -> &[T]; + fn peek_top(&mut self, no_of_elems: usize) -> &[T]; } struct VecStack { @@ -135,8 +135,9 @@ impl Stack for VecStack { self.stack.len() } - fn peek_all(&mut self) -> &[S] { - &self.stack + fn peek_top(&mut self, no_from_top: usize) -> &[S] { + assert!(self.stack.len() >= no_from_top, "peek_top asked for more items than exist."); + &self.stack[self.stack.len() - no_from_top .. self.stack.len()] } } @@ -306,7 +307,7 @@ impl evm::Evm for Interpreter { let (gas_cost, mem_size) = try!(self.get_gas_cost_mem(ext, instruction, &mut mem, &stack)); // TODO: make compile-time removable if too much of a performance hit. - ext.trace_prepare_execute(reader.position, instruction, &gas_cost, stack.peek_all()); + let trace_executed = ext.trace_prepare_execute(reader.position, instruction, &gas_cost); try!(self.verify_gas(¤t_gas, &gas_cost)); mem.expand(mem_size); @@ -322,10 +323,19 @@ impl evm::Evm for Interpreter { ); }); + let (mem_written, store_written) = match trace_executed { + true => (Self::mem_written(instruction, &stack), Self::store_written(instruction, &stack)), + false => (None, None), + }; + // Execute instruction let result = try!(self.exec_instruction( current_gas, ¶ms, ext, instruction, &mut reader, &mut mem, &mut stack - )); + )); + + if trace_executed { + ext.trace_executed(current_gas, stack.peek_top(get_info(instruction).ret), mem_written.map(|(o, s)| (o, &(mem[o..(o + s)]))), store_written); + } // Advance match result { @@ -496,6 +506,31 @@ impl Interpreter { } } + fn mem_written( + instruction: Instruction, + stack: &Stack + ) -> Option<(usize, usize)> { + match instruction { + instructions::MSTORE | instructions::MLOAD => Some((stack.peek(0).low_u64() as usize, 32)), + instructions::MSTORE8 => Some((stack.peek(0).low_u64() as usize, 1)), + instructions::CALLDATACOPY | instructions::CODECOPY => Some((stack.peek(0).low_u64() as usize, stack.peek(2).low_u64() as usize)), + instructions::EXTCODECOPY => Some((stack.peek(1).low_u64() as usize, stack.peek(3).low_u64() as usize)), + instructions::CALL | instructions::CALLCODE => Some((stack.peek(5).low_u64() as usize, stack.peek(6).low_u64() as usize)), + instructions::DELEGATECALL => Some((stack.peek(4).low_u64() as usize, stack.peek(5).low_u64() as usize)), + _ => None, + } + } + + fn store_written( + instruction: Instruction, + stack: &Stack + ) -> Option<(U256, U256)> { + match instruction { + instructions::SSTORE => Some((stack.peek(0).clone(), stack.peek(1).clone())), + _ => None, + } + } + fn mem_gas_cost(&self, schedule: &evm::Schedule, current_mem_size: usize, mem_size: &U256) -> Result<(U256, usize), evm::Error> { let gas_for_mem = |mem_size: U256| { let s = mem_size >> 5; diff --git a/ethcore/src/externalities.rs b/ethcore/src/externalities.rs index cd7564f70..d09b1e439 100644 --- a/ethcore/src/externalities.rs +++ b/ethcore/src/externalities.rs @@ -289,8 +289,12 @@ impl<'a, T, V> Ext for Externalities<'a, T, V> where T: 'a + Tracer, V: 'a + VMT self.substate.sstore_clears_count = self.substate.sstore_clears_count + U256::one(); } - fn trace_prepare_execute(&mut self, pc: usize, instruction: u8, gas_cost: &U256, stack: &[U256]) { - self.vm_tracer.trace_prepare_execute(pc, instruction, gas_cost, stack); + fn trace_prepare_execute(&mut self, pc: usize, instruction: u8, gas_cost: &U256) -> bool { + self.vm_tracer.trace_prepare_execute(pc, instruction, gas_cost) + } + + fn trace_executed(&mut self, gas_used: U256, stack_push: &[U256], mem_diff: Option<(usize, &[u8])>, store_diff: Option<(U256, U256)>) { + self.vm_tracer.trace_executed(gas_used, stack_push, mem_diff, store_diff) } } diff --git a/ethcore/src/trace/executive_tracer.rs b/ethcore/src/trace/executive_tracer.rs index 5a3ac27cd..a1a13cf43 100644 --- a/ethcore/src/trace/executive_tracer.rs +++ b/ethcore/src/trace/executive_tracer.rs @@ -18,7 +18,7 @@ use util::{Bytes, Address, U256}; use action_params::ActionParams; -use trace::trace::{Trace, Call, Create, Action, Res, CreateResult, CallResult, VMTrace, VMOperation}; +use trace::trace::{Trace, Call, Create, Action, Res, CreateResult, CallResult, VMTrace, VMOperation, VMExecutedOperation, MemoryDiff, StorageDiff}; use trace::{Tracer, VMTracer}; /// Simple executive tracer. Traces all calls and creates. Ignores delegatecalls. @@ -113,13 +113,24 @@ pub struct ExecutiveVMTracer { } impl VMTracer for ExecutiveVMTracer { - fn trace_prepare_execute(&mut self, pc: usize, instruction: u8, gas_cost: &U256, stack: &[U256]) { + fn trace_prepare_execute(&mut self, pc: usize, instruction: u8, gas_cost: &U256) -> bool { self.data.operations.push(VMOperation { pc: pc, instruction: instruction, gas_cost: gas_cost.clone(), - stack: stack.iter().cloned().collect(), - }) + executed: None, + }); + true + } + + fn trace_executed(&mut self, gas_used: U256, stack_push: &[U256], mem_diff: Option<(usize, &[u8])>, store_diff: Option<(U256, U256)>) { + let ex = VMExecutedOperation { + gas_used: gas_used, + stack_push: stack_push.iter().cloned().collect(), + mem_diff: mem_diff.map(|(s, r)| MemoryDiff{ offset: s, data: r.iter().cloned().collect() }), + store_diff: store_diff.map(|(l, v)| StorageDiff{ location: l, value: v }), + }; + self.data.operations.last_mut().expect("trace_executed is always called after a trace_prepare_execute").executed = Some(ex); } fn prepare_subtrace(&self, code: &Bytes) -> Self { diff --git a/ethcore/src/trace/mod.rs b/ethcore/src/trace/mod.rs index eeddc3905..e86f483c4 100644 --- a/ethcore/src/trace/mod.rs +++ b/ethcore/src/trace/mod.rs @@ -91,7 +91,11 @@ pub trait Tracer: Send { /// Used by executive to build VM traces. pub trait VMTracer: Send { /// Trace the preparation to execute a single instruction. - fn trace_prepare_execute(&mut self, pc: usize, instruction: u8, gas_cost: &U256, stack: &[U256]); + /// @returns true if `trace_executed` should be called. + fn trace_prepare_execute(&mut self, _pc: usize, _instruction: u8, _gas_cost: &U256) -> bool { false } + + /// Trace the finalised execution of a single instruction. + fn trace_executed(&mut self, _gas_used: U256, _stack_push: &[U256], _mem_diff: Option<(usize, &[u8])>, _store_diff: Option<(U256, U256)>) {} /// Spawn subtracer which will be used to trace deeper levels of execution. fn prepare_subtrace(&self, code: &Bytes) -> Self where Self: Sized; diff --git a/ethcore/src/trace/noop_tracer.rs b/ethcore/src/trace/noop_tracer.rs index 08eff3c4e..ed0231b79 100644 --- a/ethcore/src/trace/noop_tracer.rs +++ b/ethcore/src/trace/noop_tracer.rs @@ -69,7 +69,10 @@ pub struct NoopVMTracer; impl VMTracer for NoopVMTracer { /// Trace the preparation to execute a single instruction. - fn trace_prepare_execute(&mut self, _pc: usize, _instruction: u8, _gas_cost: &U256, _stack: &[U256]) {} + fn trace_prepare_execute(&mut self, _pc: usize, _instruction: u8, _gas_cost: &U256) -> bool { false } + + /// Trace the finalised execution of a single instruction. + fn trace_executed(&mut self, _gas_used: U256, _stack_push: &[U256], _mem_diff: Option<(usize, &[u8])>, _store_diff: Option<(U256, U256)>) {} /// Spawn subtracer which will be used to trace deeper levels of execution. fn prepare_subtrace(&self, _code: &Bytes) -> Self { NoopVMTracer } diff --git a/ethcore/src/types/trace_types/trace.rs b/ethcore/src/types/trace_types/trace.rs index 5c5c9af15..baa1241d3 100644 --- a/ethcore/src/types/trace_types/trace.rs +++ b/ethcore/src/types/trace_types/trace.rs @@ -349,16 +349,94 @@ impl Trace { } } -/*pub struct VMExecutedOperation { +#[derive(Debug, Clone, PartialEq, Binary)] +/// A diff of some chunk of memory. +pub struct MemoryDiff { + /// Offset into memory the change begins. + pub offset: usize, + /// The changed data. + pub data: Bytes, +} + +impl Encodable for MemoryDiff { + fn rlp_append(&self, s: &mut RlpStream) { + s.begin_list(2); + s.append(&self.offset); + s.append(&self.data); + } +} + +impl Decodable for MemoryDiff { + fn decode(decoder: &D) -> Result where D: Decoder { + let d = decoder.as_rlp(); + Ok(MemoryDiff { + offset: try!(d.val_at(0)), + data: try!(d.val_at(1)), + }) + } +} + +#[derive(Debug, Clone, PartialEq, Binary)] +/// A diff of some storage value. +pub struct StorageDiff { + /// Which key in storage is changed. + pub location: U256, + /// What the value has been changed to. + pub value: U256, +} + +impl Encodable for StorageDiff { + fn rlp_append(&self, s: &mut RlpStream) { + s.begin_list(2); + s.append(&self.location); + s.append(&self.value); + } +} + +impl Decodable for StorageDiff { + fn decode(decoder: &D) -> Result where D: Decoder { + let d = decoder.as_rlp(); + Ok(StorageDiff { + location: try!(d.val_at(0)), + value: try!(d.val_at(1)), + }) + } +} + +#[derive(Debug, Clone, PartialEq, Binary)] +/// A record of an executed VM operation. +pub struct VMExecutedOperation { /// The total gas used. pub gas_used: U256, - /// Altered storage value. - pub storage_diff: Option<(U256, U256)>, - /// If altered, the new memory image. - pub new_memory: Option, -}*/ - /// Information concerning the execution of the operation. -// pub executed: Option, + /// The stack item placed, if any. + pub stack_push: Vec, + /// If altered, the memory delta, given as (offset, new bytes). + pub mem_diff: Option, + /// The altered storage value. + pub store_diff: Option, +} + +impl Encodable for VMExecutedOperation { + fn rlp_append(&self, s: &mut RlpStream) { + s.begin_list(4); + s.append(&self.gas_used); + s.append(&self.stack_push); + s.append(&self.mem_diff); + s.append(&self.store_diff); + } +} + +impl Decodable for VMExecutedOperation { + fn decode(decoder: &D) -> Result where D: Decoder { + let d = decoder.as_rlp(); + Ok(VMExecutedOperation { + gas_used: try!(d.val_at(0)), + stack_push: try!(d.val_at(1)), + mem_diff: try!(d.val_at(2)), + store_diff: try!(d.val_at(3)), + }) + } +} #[derive(Debug, Clone, PartialEq, Binary)] /// A record of the execution of a single VM operation. @@ -369,8 +447,8 @@ pub struct VMOperation { pub instruction: u8, /// The gas cost for this instruction. pub gas_cost: U256, - /// The stack. - pub stack: Vec, + /// Information concerning the execution of the operation. + pub executed: Option, } impl Encodable for VMOperation { @@ -379,7 +457,7 @@ impl Encodable for VMOperation { s.append(&self.pc); s.append(&self.instruction); s.append(&self.gas_cost); - s.append(&self.stack); + s.append(&self.executed); } } @@ -390,7 +468,7 @@ impl Decodable for VMOperation { pc: try!(d.val_at(0)), instruction: try!(d.val_at(1)), gas_cost: try!(d.val_at(2)), - stack: try!(d.val_at(3)), + executed: try!(d.val_at(3)), }; Ok(res) diff --git a/rpc/src/v1/impls/ethcore.rs b/rpc/src/v1/impls/ethcore.rs index 7c1b30974..3aaa50a0a 100644 --- a/rpc/src/v1/impls/ethcore.rs +++ b/rpc/src/v1/impls/ethcore.rs @@ -80,9 +80,30 @@ fn vm_trace_to_object(t: &VMTrace) -> Value { .map(|(i, op)| { let mut m = map![ "pc".to_owned() => to_value(&op.pc).unwrap(), - "cost".to_owned() => to_value(&op.gas_cost).unwrap(), - "stack".to_owned() => to_value(&op.stack).unwrap() + "cost".to_owned() => match op.gas_cost <= U256::from(!0u64) { + true => to_value(&op.gas_cost.low_u64()), + false => to_value(&op.gas_cost), + }.unwrap() ]; + if let Some(ref ex) = op.executed { + let mut em = map![ + "used".to_owned() => to_value(&ex.gas_used.low_u64()).unwrap(), + "push".to_owned() => to_value(&ex.stack_push).unwrap() + ]; + if let Some(ref md) = ex.mem_diff { + em.insert("mem".to_owned(), Value::Object(map![ + "off".to_owned() => to_value(&md.offset).unwrap(), + "data".to_owned() => to_value(&md.data).unwrap() + ])); + } + if let Some(ref sd) = ex.store_diff { + em.insert("store".to_owned(), Value::Object(map![ + "key".to_owned() => to_value(&sd.location).unwrap(), + "val".to_owned() => to_value(&sd.value).unwrap() + ])); + } + m.insert("executed".to_owned(), Value::Object(em)); + } if next_sub.is_some() && next_sub.unwrap().parent_step == i { m.insert("sub".to_owned(), vm_trace_to_object(next_sub.unwrap())); next_sub = subs.next(); @@ -191,7 +212,6 @@ impl Ethcore for EthcoreClient where C: BlockChainClient + 'static, .and_then(|(request,)| { let signed = try!(self.sign_call(request)); let r = take_weak!(self.client).call(&signed, true); - trace!(target: "jsonrpc", "returned {:?}", r); if let Ok(executed) = r { if let Some(vm_trace) = executed.vm_trace { return Ok(vm_trace_to_object(&vm_trace)); diff --git a/util/src/rlp/rlpstream.rs b/util/src/rlp/rlpstream.rs index c2ff88c41..5da3a3822 100644 --- a/util/src/rlp/rlpstream.rs +++ b/util/src/rlp/rlpstream.rs @@ -338,6 +338,18 @@ impl Encodable for Vec where T: Encodable { } } +impl Encodable for Option where T: Encodable { + fn rlp_append(&self, s: &mut RlpStream) { + match *self { + None => { s.begin_list(0); }, + Some(ref x) => { + s.begin_list(1); + s.append_internal(x); + } + } + } +} + impl RlpEncodable for T where T: Encodable { fn rlp_append(&self, s: &mut RlpStream) { Encodable::rlp_append(self, s) diff --git a/util/src/rlp/untrusted_rlp.rs b/util/src/rlp/untrusted_rlp.rs index 1aa688aba..6109a643b 100644 --- a/util/src/rlp/untrusted_rlp.rs +++ b/util/src/rlp/untrusted_rlp.rs @@ -395,7 +395,7 @@ impl<'a> Decoder for BasicDecoder<'a> { } impl Decodable for T where T: FromBytes { - fn decode(decoder: &D) -> Result where D: Decoder { + fn decode(decoder: &D) -> Result where D: Decoder { decoder.read_value(| bytes | { Ok(try!(T::from_bytes(bytes))) }) @@ -403,13 +403,19 @@ impl Decodable for T where T: FromBytes { } impl Decodable for Vec where T: Decodable { - fn decode(decoder: &D) -> Result where D: Decoder { + fn decode(decoder: &D) -> Result where D: Decoder { decoder.as_rlp().iter().map(|d| T::decode(&BasicDecoder::new(d))).collect() } } +impl Decodable for Option where T: Decodable { + fn decode(decoder: &D) -> Result where D: Decoder { + decoder.as_rlp().iter().map(|d| T::decode(&BasicDecoder::new(d))).collect::, DecoderError>>().map(|mut a| a.pop()) + } +} + impl Decodable for Vec { - fn decode(decoder: &D) -> Result where D: Decoder { + fn decode(decoder: &D) -> Result where D: Decoder { decoder.read_value(| bytes | { let mut res = vec![]; res.extend_from_slice(bytes); @@ -418,22 +424,10 @@ impl Decodable for Vec { } } -impl Decodable for Option where T: Decodable { - fn decode(decoder: &D) -> Result where D: Decoder { - decoder.read_value(| bytes | { - let res = match bytes.len() { - 0 => None, - _ => Some(try!(T::decode(decoder))) - }; - Ok(res) - }) - } -} - macro_rules! impl_array_decodable { ($index_type:ty, $len:expr ) => ( impl Decodable for [T; $len] where T: Decodable { - fn decode(decoder: &D) -> Result where D: Decoder { + fn decode(decoder: &D) -> Result where D: Decoder { let decoders = decoder.as_rlp(); let mut result: [T; $len] = unsafe { ::std::mem::uninitialized() }; @@ -466,7 +460,7 @@ impl_array_decodable_recursive!( ); impl RlpDecodable for T where T: Decodable { - fn decode(decoder: &D) -> Result where D: Decoder { + fn decode(decoder: &D) -> Result where D: Decoder { Decodable::decode(decoder) } } @@ -489,7 +483,7 @@ impl FromBytes for DecodableU8 { } impl RlpDecodable for u8 { - fn decode(decoder: &D) -> Result where D: Decoder { + fn decode(decoder: &D) -> Result where D: Decoder { let u: DecodableU8 = try!(Decodable::decode(decoder)); Ok(u.0) } From 8082fdb3ffee4589617ae40f0380dc67315c42ce Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 30 May 2016 22:27:28 +0200 Subject: [PATCH 11/16] Fix max-value calls and add "creates" field to getTransaction. --- ethcore/res/ethereum/morden.json | 3 ++- ethcore/src/client/client.rs | 4 ++-- ethcore/src/evm/interpreter.rs | 3 ++- ethcore/src/executive.rs | 1 - ethcore/src/lib.rs | 1 + ethcore/src/state.rs | 6 ++---- json/src/state/transaction.rs | 2 +- rpc/src/v1/impls/ethcore.rs | 2 +- rpc/src/v1/types/transaction.rs | 18 +++++++++++++++--- 9 files changed, 26 insertions(+), 14 deletions(-) diff --git a/ethcore/res/ethereum/morden.json b/ethcore/res/ethereum/morden.json index 1e385558d..368923eeb 100644 --- a/ethcore/res/ethereum/morden.json +++ b/ethcore/res/ethereum/morden.json @@ -44,7 +44,8 @@ "enode://e941c58fed2709d792f552f408d2162c3d0a5597d22d1da617a9c9e6181f3251056a96adb45ae22eba70119355227298dc7e6dff805b092bae7da2f8564de422@85.25.217.23:30303", "enode://f4b73c9d11a780293ff0ca7afa12c67797afdc33a4797a7c2ecc5b87e455b32a8b9e9804f2004072bac38350bf82d52521d1a09590d2079705fc8357aef2bf9c@71.202.223.50:56603", "enode://1173eea53e0cb2b8da92423e44cf4cbafbc8ea16c1558cf06e18dfc5a2fc9b140cc802a4362b4c773fb1442541e6f2a225b200bb4c1f6b347e7510a50fa4873f@104.41.138.167:30300", - "enode://1aad341327808738ad34655611f1b13293c4155dde36c8e3788128829f15cc6db2da9435f29520553d4efc134aadc50115690194ac3af519aac7a388b524811e@109.188.125.2:30303" + "enode://1aad341327808738ad34655611f1b13293c4155dde36c8e3788128829f15cc6db2da9435f29520553d4efc134aadc50115690194ac3af519aac7a388b524811e@109.188.125.2:30303", + "enode://237dddd9a5f80c721eed6f3fe6bb87884a2c2b222b8f4b10fbad5e3a632b16d16ee885b11063a2de006a98f1f194d5a07844e8885c870b1da64fe41e55e05c3d@37.194.194.121:30303" ], "accounts": { "0000000000000000000000000000000000000001": { "balance": "1", "nonce": "1048576", "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } }, diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index 44da0a4b3..902a4a12c 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -441,9 +441,9 @@ impl BlockChainClient for Client where V: Verifier { ExecutionError::TransactionMalformed(message) })); let balance = state.balance(&sender); - // give the sender max balance + // give the sender a decent balance state.sub_balance(&sender, &balance); - state.add_balance(&sender, &U256::max_value()); + state.add_balance(&sender, &(U256::from(1) << 200)); let options = TransactOptions { tracing: false, vm_tracing: vm_tracing, check_nonce: false }; Executive::new(&mut state, &env_info, self.engine.deref().deref(), &self.vm_factory).transact(t, options) } diff --git a/ethcore/src/evm/interpreter.rs b/ethcore/src/evm/interpreter.rs index c63837e49..9cf4d6034 100644 --- a/ethcore/src/evm/interpreter.rs +++ b/ethcore/src/evm/interpreter.rs @@ -301,7 +301,6 @@ impl evm::Evm for Interpreter { while reader.position < code.len() { let instruction = code[reader.position]; - reader.position += 1; // Calculate gas cost let (gas_cost, mem_size) = try!(self.get_gas_cost_mem(ext, instruction, &mut mem, &stack)); @@ -309,6 +308,8 @@ impl evm::Evm for Interpreter { // TODO: make compile-time removable if too much of a performance hit. let trace_executed = ext.trace_prepare_execute(reader.position, instruction, &gas_cost); + reader.position += 1; + try!(self.verify_gas(¤t_gas, &gas_cost)); mem.expand(mem_size); current_gas = current_gas - gas_cost; //TODO: use operator -= diff --git a/ethcore/src/executive.rs b/ethcore/src/executive.rs index 069cd2f35..c3ef955de 100644 --- a/ethcore/src/executive.rs +++ b/ethcore/src/executive.rs @@ -23,7 +23,6 @@ use externalities::*; use substate::*; use trace::{Trace, Tracer, NoopTracer, ExecutiveTracer, VMTrace, VMTracer, ExecutiveVMTracer, NoopVMTracer}; use crossbeam; - pub use types::executed::{Executed, ExecutionResult}; /// Max depth to avoid stack overflow (when it's reached we start a new thread with VM) diff --git a/ethcore/src/lib.rs b/ethcore/src/lib.rs index 4e1a5be56..809f86517 100644 --- a/ethcore/src/lib.rs +++ b/ethcore/src/lib.rs @@ -140,3 +140,4 @@ mod json_tests; pub use types::*; pub use evm::get_info; +pub use executive::contract_address; \ No newline at end of file diff --git a/ethcore/src/state.rs b/ethcore/src/state.rs index 87bee5244..bff9483f9 100644 --- a/ethcore/src/state.rs +++ b/ethcore/src/state.rs @@ -183,16 +183,14 @@ impl State { /// Add `incr` to the balance of account `a`. pub fn add_balance(&mut self, a: &Address, incr: &U256) { - let old = self.balance(a); + trace!(target: "state", "add_balance({}, {}): {}", a, incr, self.balance(a)); self.require(a, false).add_balance(incr); - trace!("state: add_balance({}, {}): {} -> {}\n", a, incr, old, self.balance(a)); } /// Subtract `decr` from the balance of account `a`. pub fn sub_balance(&mut self, a: &Address, decr: &U256) { - let old = self.balance(a); + trace!(target: "state", "sub_balance({}, {}): {}", a, decr, self.balance(a)); self.require(a, false).sub_balance(decr); - trace!("state: sub_balance({}, {}): {} -> {}\n", a, decr, old, self.balance(a)); } /// Subtracts `by` from the balance of `from` and adds it to that of `to`. diff --git a/json/src/state/transaction.rs b/json/src/state/transaction.rs index 15626c224..62b8577d4 100644 --- a/json/src/state/transaction.rs +++ b/json/src/state/transaction.rs @@ -40,7 +40,7 @@ pub struct Transaction { /// To. pub to: MaybeEmpty
, /// Value. - pub value: Uint + pub value: Uint, } #[cfg(test)] diff --git a/rpc/src/v1/impls/ethcore.rs b/rpc/src/v1/impls/ethcore.rs index 3aaa50a0a..56598eb7e 100644 --- a/rpc/src/v1/impls/ethcore.rs +++ b/rpc/src/v1/impls/ethcore.rs @@ -102,7 +102,7 @@ fn vm_trace_to_object(t: &VMTrace) -> Value { "val".to_owned() => to_value(&sd.value).unwrap() ])); } - m.insert("executed".to_owned(), Value::Object(em)); + m.insert("ex".to_owned(), Value::Object(em)); } if next_sub.is_some() && next_sub.unwrap().parent_step == i { m.insert("sub".to_owned(), vm_trace_to_object(next_sub.unwrap())); diff --git a/rpc/src/v1/types/transaction.rs b/rpc/src/v1/types/transaction.rs index 1c9a41084..4193f6d81 100644 --- a/rpc/src/v1/types/transaction.rs +++ b/rpc/src/v1/types/transaction.rs @@ -15,6 +15,7 @@ // along with Parity. If not, see . use util::numbers::*; +use ethcore::contract_address; use ethcore::transaction::{LocalizedTransaction, Action, SignedTransaction}; use v1::types::{Bytes, OptionalValue}; @@ -46,7 +47,9 @@ pub struct Transaction { /// Gas pub gas: U256, /// Data - pub input: Bytes + pub input: Bytes, + /// Creates contract + pub creates: OptionalValue
, } impl From for Transaction { @@ -65,7 +68,11 @@ impl From for Transaction { value: t.value, gas_price: t.gas_price, gas: t.gas, - input: Bytes::new(t.data.clone()) + input: Bytes::new(t.data.clone()), + creates: match t.action { + Action::Create => OptionalValue::Value(contract_address(&t.sender().unwrap(), &t.nonce)), + Action::Call(_) => OptionalValue::Null, + }, } } } @@ -86,7 +93,11 @@ impl From for Transaction { value: t.value, gas_price: t.gas_price, gas: t.gas, - input: Bytes::new(t.data.clone()) + input: Bytes::new(t.data.clone()), + creates: match t.action { + Action::Create => OptionalValue::Value(contract_address(&t.sender().unwrap(), &t.nonce)), + Action::Call(_) => OptionalValue::Null, + }, } } } @@ -100,6 +111,7 @@ mod tests { fn test_transaction_serialize() { let t = Transaction::default(); let serialized = serde_json::to_string(&t).unwrap(); + // TODO: fix create. assert_eq!(serialized, r#"{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x00","blockHash":null,"blockNumber":null,"transactionIndex":null,"from":"0x0000000000000000000000000000000000000000","to":null,"value":"0x00","gasPrice":"0x00","gas":"0x00","input":"0x"}"#); } } From 5766354c191654588ebb3a3e1571aa89278d67de Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Tue, 31 May 2016 12:04:53 +0200 Subject: [PATCH 12/16] Tests for VM tracing. --- ethcore/src/executive.rs | 48 +++++++++++++------------- ethcore/src/trace/mod.rs | 2 +- ethcore/src/types/trace_types/trace.rs | 4 +-- rpc/src/v1/tests/mocked/eth.rs | 2 +- rpc/src/v1/types/block.rs | 2 +- rpc/src/v1/types/transaction.rs | 2 +- 6 files changed, 30 insertions(+), 30 deletions(-) diff --git a/ethcore/src/executive.rs b/ethcore/src/executive.rs index c3ef955de..483e3fa31 100644 --- a/ethcore/src/executive.rs +++ b/ethcore/src/executive.rs @@ -495,7 +495,7 @@ mod tests { use tests::helpers::*; use trace::trace; use trace::{Trace, Tracer, NoopTracer, ExecutiveTracer}; - use trace::{VMTrace, VMOperation, VMTracer, NoopVMTracer, ExecutiveVMTracer}; + use trace::{VMTrace, VMOperation, VMExecutedOperation, MemoryDiff, StorageDiff, VMTracer, NoopVMTracer, ExecutiveVMTracer}; #[test] fn test_contract_address() { @@ -681,28 +681,28 @@ mod tests { parent_step: 0, code: vec![124, 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, 96, 0, 82, 96, 29, 96, 3, 96, 23, 240, 96, 0, 85], operations: vec![ - VMOperation { pc: 1, instruction: 124, gas_cost: x!(3), stack: vecx![] }, - VMOperation { pc: 31, instruction: 96, gas_cost: x!(3), stack: vecx![U256::from_dec_str("2589892687202724018173567190521546555304938078595079151649957320078677").unwrap()] }, - VMOperation { pc: 33, instruction: 82, gas_cost: x!(6), stack: vecx![U256::from_dec_str("2589892687202724018173567190521546555304938078595079151649957320078677").unwrap(), 0] }, - VMOperation { pc: 34, instruction: 96, gas_cost: x!(3), stack: vecx![] }, - VMOperation { pc: 36, instruction: 96, gas_cost: x!(3), stack: vecx![29] }, - VMOperation { pc: 38, instruction: 96, gas_cost: x!(3), stack: vecx![29, 3] }, - VMOperation { pc: 40, instruction: 240, gas_cost: x!(32000), stack: vecx![29, 3, 23] }, - VMOperation { pc: 41, instruction: 96, gas_cost: x!(3), stack: vecx![U256::from_dec_str("1135198453258042933984631383966629874710669425204").unwrap()] }, - VMOperation { pc: 43, instruction: 85, gas_cost: x!(20000), stack: vecx![U256::from_dec_str("1135198453258042933984631383966629874710669425204").unwrap(), 0] } + VMOperation { pc: 0, instruction: 124, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 99997.into(), stack_push: vecx![U256::from_dec_str("2589892687202724018173567190521546555304938078595079151649957320078677").unwrap()], mem_diff: None, store_diff: None }) }, + VMOperation { pc: 30, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 99994.into(), stack_push: vecx![0], mem_diff: None, store_diff: None }) }, + VMOperation { pc: 32, instruction: 82, gas_cost: 6.into(), executed: Some(VMExecutedOperation { gas_used: 99988.into(), stack_push: vecx![], mem_diff: Some(MemoryDiff { offset: 0, data: vec![0, 0, 0, 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] }), store_diff: None }) }, + VMOperation { pc: 33, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 99985.into(), stack_push: vecx![29], mem_diff: None, store_diff: None }) }, + VMOperation { pc: 35, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 99982.into(), stack_push: vecx![3], mem_diff: None, store_diff: None }) }, + VMOperation { pc: 37, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 99979.into(), stack_push: vecx![23], mem_diff: None, store_diff: None }) }, + VMOperation { pc: 39, instruction: 240, gas_cost: 32000.into(), executed: Some(VMExecutedOperation { gas_used: 67979.into(), stack_push: vecx![U256::from_dec_str("1135198453258042933984631383966629874710669425204").unwrap()], mem_diff: None, store_diff: None }) }, + VMOperation { pc: 40, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 64752.into(), stack_push: vecx![0], mem_diff: None, store_diff: None }) }, + VMOperation { pc: 42, instruction: 85, gas_cost: 20000.into(), executed: Some(VMExecutedOperation { gas_used: 44752.into(), stack_push: vecx![], mem_diff: None, store_diff: Some(StorageDiff { location: 0.into(), value: U256::from_dec_str("1135198453258042933984631383966629874710669425204").unwrap() }) }) } ], subs: vec![ VMTrace { parent_step: 7, code: 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], operations: vec![ - VMOperation { pc: 1, instruction: 96, gas_cost: x!(3), stack: vecx![] }, - VMOperation { pc: 3, instruction: 128, gas_cost: x!(3), stack: vecx![16] }, - VMOperation { pc: 4, instruction: 96, gas_cost: x!(3), stack: vecx![16, 16] }, - VMOperation { pc: 6, instruction: 96, gas_cost: x!(3), stack: vecx![16, 16, 12] }, - VMOperation { pc: 8, instruction: 57, gas_cost: x!(9), stack: vecx![16, 16, 12, 0] }, - VMOperation { pc: 9, instruction: 96, gas_cost: x!(3), stack: vecx![16] }, - VMOperation { pc: 11, instruction: 243, gas_cost: x!(0), stack: vecx![16, 0] } + VMOperation { pc: 0, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 67976.into(), stack_push: vecx![16], mem_diff: None, store_diff: None }) }, + VMOperation { pc: 2, instruction: 128, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 67973.into(), stack_push: vecx![16, 16], mem_diff: None, store_diff: None }) }, + VMOperation { pc: 3, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 67970.into(), stack_push: vecx![12], mem_diff: None, store_diff: None }) }, + VMOperation { pc: 5, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 67967.into(), stack_push: vecx![0], mem_diff: None, store_diff: None }) }, + VMOperation { pc: 7, instruction: 57, gas_cost: 9.into(), executed: Some(VMExecutedOperation { gas_used: 67958.into(), stack_push: vecx![], mem_diff: Some(MemoryDiff { offset: 0, data: vec![96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53] }), store_diff: None }) }, + VMOperation { pc: 8, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 67955.into(), stack_push: vecx![0], mem_diff: None, store_diff: None }) }, + VMOperation { pc: 10, instruction: 243, gas_cost: 0.into(), executed: Some(VMExecutedOperation { gas_used: 67955.into(), stack_push: vecx![], mem_diff: None, store_diff: None }) } ], subs: vec![] } @@ -773,13 +773,13 @@ mod tests { parent_step: 0, code: 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], operations: vec![ - VMOperation { pc: 1, instruction: 96, gas_cost: x!(3), stack: vecx![] }, - VMOperation { pc: 3, instruction: 128, gas_cost: x!(3), stack: vecx![16] }, - VMOperation { pc: 4, instruction: 96, gas_cost: x!(3), stack: vecx![16, 16] }, - VMOperation { pc: 6, instruction: 96, gas_cost: x!(3), stack: vecx![16, 16, 12] }, - VMOperation { pc: 8, instruction: 57, gas_cost: x!(9), stack: vecx![16, 16, 12, 0] }, - VMOperation { pc: 9, instruction: 96, gas_cost: x!(3), stack: vecx![16] }, - VMOperation { pc: 11, instruction: 243, gas_cost: x!(0), stack: vecx![16, 0] } + VMOperation { pc: 0, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 99997.into(), stack_push: vecx![16], mem_diff: None, store_diff: None }) }, + VMOperation { pc: 2, instruction: 128, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 99994.into(), stack_push: vecx![16, 16], mem_diff: None, store_diff: None }) }, + VMOperation { pc: 3, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 99991.into(), stack_push: vecx![12], mem_diff: None, store_diff: None }) }, + VMOperation { pc: 5, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 99988.into(), stack_push: vecx![0], mem_diff: None, store_diff: None }) }, + VMOperation { pc: 7, instruction: 57, gas_cost: 9.into(), executed: Some(VMExecutedOperation { gas_used: 99979.into(), stack_push: vecx![], mem_diff: Some(MemoryDiff { offset: 0, data: vec![96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53] }), store_diff: None }) }, + VMOperation { pc: 8, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 99976.into(), stack_push: vecx![0], mem_diff: None, store_diff: None }) }, + VMOperation { pc: 10, instruction: 243, gas_cost: 0.into(), executed: Some(VMExecutedOperation { gas_used: 99976.into(), stack_push: vecx![], mem_diff: None, store_diff: None }) } ], subs: vec![] }; diff --git a/ethcore/src/trace/mod.rs b/ethcore/src/trace/mod.rs index e86f483c4..53c062137 100644 --- a/ethcore/src/trace/mod.rs +++ b/ethcore/src/trace/mod.rs @@ -31,7 +31,7 @@ pub use self::block::BlockTraces; pub use self::config::{Config, Switch}; pub use self::db::TraceDB; pub use self::error::Error; -pub use types::trace_types::trace::{Trace, VMTrace, VMOperation}; +pub use types::trace_types::trace::{Trace, VMTrace, VMOperation, VMExecutedOperation, MemoryDiff, StorageDiff}; pub use self::noop_tracer::{NoopTracer, NoopVMTracer}; pub use self::executive_tracer::{ExecutiveTracer, ExecutiveVMTracer}; pub use types::trace_types::filter::{Filter, AddressesFilter}; diff --git a/ethcore/src/types/trace_types/trace.rs b/ethcore/src/types/trace_types/trace.rs index baa1241d3..243acaf64 100644 --- a/ethcore/src/types/trace_types/trace.rs +++ b/ethcore/src/types/trace_types/trace.rs @@ -410,9 +410,9 @@ pub struct VMExecutedOperation { pub gas_used: U256, /// The stack item placed, if any. pub stack_push: Vec, - /// If altered, the memory delta, given as (offset, new bytes). + /// If altered, the memory delta. pub mem_diff: Option, - /// The altered storage value. + /// The altered storage value, if any. pub store_diff: Option, } diff --git a/rpc/src/v1/tests/mocked/eth.rs b/rpc/src/v1/tests/mocked/eth.rs index 3e9c67f46..b0eae4139 100644 --- a/rpc/src/v1/tests/mocked/eth.rs +++ b/rpc/src/v1/tests/mocked/eth.rs @@ -288,7 +288,7 @@ fn rpc_eth_pending_transaction_by_hash() { tester.miner.pending_transactions.lock().unwrap().insert(H256::zero(), tx); } - let response = r#"{"jsonrpc":"2.0","result":{"blockHash":null,"blockNumber":null,"from":"0x0f65fe9276bc9a24ae7083ae28e2660ef72df99e","gas":"0x5208","gasPrice":"0x01","hash":"0x41df922fd0d4766fcc02e161f8295ec28522f329ae487f14d811e4b64c8d6e31","input":"0x","nonce":"0x00","to":"0x095e7baea6a6c7c4c2dfeb977efac326af552d87","transactionIndex":null,"value":"0x0a"},"id":1}"#; + let response = r#"{"jsonrpc":"2.0","result":{"blockHash":null,"blockNumber":null,"creates":null,"from":"0x0f65fe9276bc9a24ae7083ae28e2660ef72df99e","gas":"0x5208","gasPrice":"0x01","hash":"0x41df922fd0d4766fcc02e161f8295ec28522f329ae487f14d811e4b64c8d6e31","input":"0x","nonce":"0x00","to":"0x095e7baea6a6c7c4c2dfeb977efac326af552d87","transactionIndex":null,"value":"0x0a"},"id":1}"#; let request = r#"{ "jsonrpc": "2.0", "method": "eth_getTransactionByHash", diff --git a/rpc/src/v1/types/block.rs b/rpc/src/v1/types/block.rs index 5810c85e5..b86723357 100644 --- a/rpc/src/v1/types/block.rs +++ b/rpc/src/v1/types/block.rs @@ -103,7 +103,7 @@ mod tests { fn test_serialize_block_transactions() { let t = BlockTransactions::Full(vec![Transaction::default()]); let serialized = serde_json::to_string(&t).unwrap(); - assert_eq!(serialized, r#"[{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x00","blockHash":null,"blockNumber":null,"transactionIndex":null,"from":"0x0000000000000000000000000000000000000000","to":null,"value":"0x00","gasPrice":"0x00","gas":"0x00","input":"0x"}]"#); + assert_eq!(serialized, r#"[{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x00","blockHash":null,"blockNumber":null,"transactionIndex":null,"from":"0x0000000000000000000000000000000000000000","to":null,"value":"0x00","gasPrice":"0x00","gas":"0x00","input":"0x","creates":null}]"#); let t = BlockTransactions::Hashes(vec![H256::default()]); let serialized = serde_json::to_string(&t).unwrap(); diff --git a/rpc/src/v1/types/transaction.rs b/rpc/src/v1/types/transaction.rs index 4193f6d81..e9d4e4cb9 100644 --- a/rpc/src/v1/types/transaction.rs +++ b/rpc/src/v1/types/transaction.rs @@ -112,7 +112,7 @@ mod tests { let t = Transaction::default(); let serialized = serde_json::to_string(&t).unwrap(); // TODO: fix create. - assert_eq!(serialized, r#"{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x00","blockHash":null,"blockNumber":null,"transactionIndex":null,"from":"0x0000000000000000000000000000000000000000","to":null,"value":"0x00","gasPrice":"0x00","gas":"0x00","input":"0x"}"#); + assert_eq!(serialized, r#"{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x00","blockHash":null,"blockNumber":null,"transactionIndex":null,"from":"0x0000000000000000000000000000000000000000","to":null,"value":"0x00","gasPrice":"0x00","gas":"0x00","input":"0x","creates":null}"#); } } From 1fdb1de218e006995c6501b6bab12c929e64df0a Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Tue, 31 May 2016 12:58:10 +0200 Subject: [PATCH 13/16] Don't implement the trait with unimplemented. --- rpc/src/v1/traits/ethcore.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rpc/src/v1/traits/ethcore.rs b/rpc/src/v1/traits/ethcore.rs index 00820db0a..abc87bbb8 100644 --- a/rpc/src/v1/traits/ethcore.rs +++ b/rpc/src/v1/traits/ethcore.rs @@ -73,7 +73,7 @@ pub trait Ethcore: Sized + Send + Sync + 'static { fn default_extra_data(&self, _: Params) -> Result; /// Executes the given call and returns the VM trace for it. - fn vm_trace_call(&self, _: Params) -> Result { rpc_unimplemented!() } + fn vm_trace_call(&self, _: Params) -> Result; /// Should be used to convert object to io delegate. fn to_delegate(self) -> IoDelegate { From 12547ecd37a0e5b159f12c5ad78f85c34ace196b Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Tue, 31 May 2016 12:58:47 +0200 Subject: [PATCH 14/16] Remove invlaid comment. --- rpc/src/v1/types/transaction.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/rpc/src/v1/types/transaction.rs b/rpc/src/v1/types/transaction.rs index e9d4e4cb9..6a9f0e590 100644 --- a/rpc/src/v1/types/transaction.rs +++ b/rpc/src/v1/types/transaction.rs @@ -111,7 +111,6 @@ mod tests { fn test_transaction_serialize() { let t = Transaction::default(); let serialized = serde_json::to_string(&t).unwrap(); - // TODO: fix create. assert_eq!(serialized, r#"{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x00","blockHash":null,"blockNumber":null,"transactionIndex":null,"from":"0x0000000000000000000000000000000000000000","to":null,"value":"0x00","gasPrice":"0x00","gas":"0x00","input":"0x","creates":null}"#); } } From d40a038f370f4a494948bc0305fe0edd7ea917e7 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Thu, 2 Jun 2016 11:49:27 +0200 Subject: [PATCH 15/16] Fix tests. --- ethcore/src/executive.rs | 46 ++++++++++++++++++++-------------------- rpc/src/v1/tests/eth.rs | 3 +-- util/src/common.rs | 7 ++++++ 3 files changed, 31 insertions(+), 25 deletions(-) diff --git a/ethcore/src/executive.rs b/ethcore/src/executive.rs index 0816e81b8..7a48e435e 100644 --- a/ethcore/src/executive.rs +++ b/ethcore/src/executive.rs @@ -681,28 +681,28 @@ mod tests { parent_step: 0, code: vec![124, 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, 96, 0, 82, 96, 29, 96, 3, 96, 23, 240, 96, 0, 85], operations: vec![ - VMOperation { pc: 0, instruction: 124, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 99997.into(), stack_push: vecx![U256::from_dec_str("2589892687202724018173567190521546555304938078595079151649957320078677").unwrap()], mem_diff: None, store_diff: None }) }, - VMOperation { pc: 30, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 99994.into(), stack_push: vecx![0], mem_diff: None, store_diff: None }) }, - VMOperation { pc: 32, instruction: 82, gas_cost: 6.into(), executed: Some(VMExecutedOperation { gas_used: 99988.into(), stack_push: vecx![], mem_diff: Some(MemoryDiff { offset: 0, data: vec![0, 0, 0, 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] }), store_diff: None }) }, - VMOperation { pc: 33, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 99985.into(), stack_push: vecx![29], mem_diff: None, store_diff: None }) }, - VMOperation { pc: 35, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 99982.into(), stack_push: vecx![3], mem_diff: None, store_diff: None }) }, - VMOperation { pc: 37, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 99979.into(), stack_push: vecx![23], mem_diff: None, store_diff: None }) }, - VMOperation { pc: 39, instruction: 240, gas_cost: 32000.into(), executed: Some(VMExecutedOperation { gas_used: 67979.into(), stack_push: vecx![U256::from_dec_str("1135198453258042933984631383966629874710669425204").unwrap()], mem_diff: None, store_diff: None }) }, - VMOperation { pc: 40, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 64752.into(), stack_push: vecx![0], mem_diff: None, store_diff: None }) }, - VMOperation { pc: 42, instruction: 85, gas_cost: 20000.into(), executed: Some(VMExecutedOperation { gas_used: 44752.into(), stack_push: vecx![], mem_diff: None, store_diff: Some(StorageDiff { location: 0.into(), value: U256::from_dec_str("1135198453258042933984631383966629874710669425204").unwrap() }) }) } + VMOperation { pc: 0, instruction: 124, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 99997.into(), stack_push: vec_into![U256::from_dec_str("2589892687202724018173567190521546555304938078595079151649957320078677").unwrap()], mem_diff: None, store_diff: None }) }, + VMOperation { pc: 30, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 99994.into(), stack_push: vec_into![0], mem_diff: None, store_diff: None }) }, + VMOperation { pc: 32, instruction: 82, gas_cost: 6.into(), executed: Some(VMExecutedOperation { gas_used: 99988.into(), stack_push: vec_into![], mem_diff: Some(MemoryDiff { offset: 0, data: vec![0, 0, 0, 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] }), store_diff: None }) }, + VMOperation { pc: 33, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 99985.into(), stack_push: vec_into![29], mem_diff: None, store_diff: None }) }, + VMOperation { pc: 35, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 99982.into(), stack_push: vec_into![3], mem_diff: None, store_diff: None }) }, + VMOperation { pc: 37, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 99979.into(), stack_push: vec_into![23], mem_diff: None, store_diff: None }) }, + VMOperation { pc: 39, instruction: 240, gas_cost: 32000.into(), executed: Some(VMExecutedOperation { gas_used: 67979.into(), stack_push: vec_into![U256::from_dec_str("1135198453258042933984631383966629874710669425204").unwrap()], mem_diff: None, store_diff: None }) }, + VMOperation { pc: 40, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 64752.into(), stack_push: vec_into![0], mem_diff: None, store_diff: None }) }, + VMOperation { pc: 42, instruction: 85, gas_cost: 20000.into(), executed: Some(VMExecutedOperation { gas_used: 44752.into(), stack_push: vec_into![], mem_diff: None, store_diff: Some(StorageDiff { location: 0.into(), value: U256::from_dec_str("1135198453258042933984631383966629874710669425204").unwrap() }) }) } ], subs: vec![ VMTrace { parent_step: 7, code: 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], operations: vec![ - VMOperation { pc: 0, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 67976.into(), stack_push: vecx![16], mem_diff: None, store_diff: None }) }, - VMOperation { pc: 2, instruction: 128, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 67973.into(), stack_push: vecx![16, 16], mem_diff: None, store_diff: None }) }, - VMOperation { pc: 3, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 67970.into(), stack_push: vecx![12], mem_diff: None, store_diff: None }) }, - VMOperation { pc: 5, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 67967.into(), stack_push: vecx![0], mem_diff: None, store_diff: None }) }, - VMOperation { pc: 7, instruction: 57, gas_cost: 9.into(), executed: Some(VMExecutedOperation { gas_used: 67958.into(), stack_push: vecx![], mem_diff: Some(MemoryDiff { offset: 0, data: vec![96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53] }), store_diff: None }) }, - VMOperation { pc: 8, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 67955.into(), stack_push: vecx![0], mem_diff: None, store_diff: None }) }, - VMOperation { pc: 10, instruction: 243, gas_cost: 0.into(), executed: Some(VMExecutedOperation { gas_used: 67955.into(), stack_push: vecx![], mem_diff: None, store_diff: None }) } + VMOperation { pc: 0, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 67976.into(), stack_push: vec_into![16], mem_diff: None, store_diff: None }) }, + VMOperation { pc: 2, instruction: 128, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 67973.into(), stack_push: vec_into![16, 16], mem_diff: None, store_diff: None }) }, + VMOperation { pc: 3, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 67970.into(), stack_push: vec_into![12], mem_diff: None, store_diff: None }) }, + VMOperation { pc: 5, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 67967.into(), stack_push: vec_into![0], mem_diff: None, store_diff: None }) }, + VMOperation { pc: 7, instruction: 57, gas_cost: 9.into(), executed: Some(VMExecutedOperation { gas_used: 67958.into(), stack_push: vec_into![], mem_diff: Some(MemoryDiff { offset: 0, data: vec![96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53] }), store_diff: None }) }, + VMOperation { pc: 8, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 67955.into(), stack_push: vec_into![0], mem_diff: None, store_diff: None }) }, + VMOperation { pc: 10, instruction: 243, gas_cost: 0.into(), executed: Some(VMExecutedOperation { gas_used: 67955.into(), stack_push: vec_into![], mem_diff: None, store_diff: None }) } ], subs: vec![] } @@ -773,13 +773,13 @@ mod tests { parent_step: 0, code: 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], operations: vec![ - VMOperation { pc: 0, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 99997.into(), stack_push: vecx![16], mem_diff: None, store_diff: None }) }, - VMOperation { pc: 2, instruction: 128, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 99994.into(), stack_push: vecx![16, 16], mem_diff: None, store_diff: None }) }, - VMOperation { pc: 3, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 99991.into(), stack_push: vecx![12], mem_diff: None, store_diff: None }) }, - VMOperation { pc: 5, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 99988.into(), stack_push: vecx![0], mem_diff: None, store_diff: None }) }, - VMOperation { pc: 7, instruction: 57, gas_cost: 9.into(), executed: Some(VMExecutedOperation { gas_used: 99979.into(), stack_push: vecx![], mem_diff: Some(MemoryDiff { offset: 0, data: vec![96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53] }), store_diff: None }) }, - VMOperation { pc: 8, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 99976.into(), stack_push: vecx![0], mem_diff: None, store_diff: None }) }, - VMOperation { pc: 10, instruction: 243, gas_cost: 0.into(), executed: Some(VMExecutedOperation { gas_used: 99976.into(), stack_push: vecx![], mem_diff: None, store_diff: None }) } + VMOperation { pc: 0, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 99997.into(), stack_push: vec_into![16], mem_diff: None, store_diff: None }) }, + VMOperation { pc: 2, instruction: 128, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 99994.into(), stack_push: vec_into![16, 16], mem_diff: None, store_diff: None }) }, + VMOperation { pc: 3, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 99991.into(), stack_push: vec_into![12], mem_diff: None, store_diff: None }) }, + VMOperation { pc: 5, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 99988.into(), stack_push: vec_into![0], mem_diff: None, store_diff: None }) }, + VMOperation { pc: 7, instruction: 57, gas_cost: 9.into(), executed: Some(VMExecutedOperation { gas_used: 99979.into(), stack_push: vec_into![], mem_diff: Some(MemoryDiff { offset: 0, data: vec![96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53] }), store_diff: None }) }, + VMOperation { pc: 8, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 99976.into(), stack_push: vec_into![0], mem_diff: None, store_diff: None }) }, + VMOperation { pc: 10, instruction: 243, gas_cost: 0.into(), executed: Some(VMExecutedOperation { gas_used: 99976.into(), stack_push: vec_into![], mem_diff: None, store_diff: None }) } ], subs: vec![] }; diff --git a/rpc/src/v1/tests/eth.rs b/rpc/src/v1/tests/eth.rs index 93887fb63..a7f7920ad 100644 --- a/rpc/src/v1/tests/eth.rs +++ b/rpc/src/v1/tests/eth.rs @@ -19,13 +19,12 @@ use std::collections::HashMap; use std::sync::Arc; use std::str::FromStr; -use ethcore::client::{MiningBlockChainClient, BlockChainClient, Client, ClientConfig}; +use ethcore::client::{BlockChainClient, Client, ClientConfig}; use ethcore::ids::BlockID; use ethcore::spec::{Genesis, Spec}; use ethcore::block::Block; use ethcore::views::BlockView; use ethcore::ethereum; -use ethcore::transaction::{Transaction, Action}; use ethcore::miner::{MinerService, ExternalMiner, Miner}; use devtools::RandomTempPath; use util::Hashable; diff --git a/util/src/common.rs b/util/src/common.rs index 0e0cd7757..941b5b0a6 100644 --- a/util/src/common.rs +++ b/util/src/common.rs @@ -24,6 +24,13 @@ pub use vector::*; pub use numbers::*; pub use sha3::*; +#[macro_export] +macro_rules! vec_into { + ( $( $x:expr ),* ) => { + vec![ $( $x.into() ),* ] + } +} + #[macro_export] macro_rules! hash_map { () => { HashMap::new() }; From b17581d7ded39250602b6d15324ff5bff26f26c1 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Thu, 2 Jun 2016 12:40:31 +0200 Subject: [PATCH 16/16] VM tracing and JSON RPC endpoint for it. (#1169) * Groundwork for basic VM tracing. * RPC endpoint for VM tracing and ser/de types ready. * Create VMTracer trait. * Rearchitected VM tracing to reflect existing tracing. Should more or less work now. * Integrated VM tracing into JSONRPC. * Fix ethcore module tests. * Add tests for VM tracing. * Fix consensus test code. * Fix mock tests. * Added VM trace information for post-execution stuff. * Fix max-value calls and add "creates" field to getTransaction. * Tests for VM tracing. * Don't implement the trait with unimplemented. * Remove invlaid comment. * Fix tests. --- ethcore/res/ethereum/morden.json | 11 +- ethcore/src/client/client.rs | 8 +- ethcore/src/client/mod.rs | 3 +- ethcore/src/client/test_client.rs | 2 +- ethcore/src/evm/ext.rs | 6 + ethcore/src/evm/instructions.rs | 3 +- ethcore/src/evm/interpreter.rs | 63 +++++++- ethcore/src/evm/jit.rs | 1 + ethcore/src/evm/mod.rs | 1 + ethcore/src/executive.rs | 188 +++++++++++++++++----- ethcore/src/externalities.rs | 44 +++-- ethcore/src/json_tests/executive.rs | 36 +++-- ethcore/src/lib.rs | 2 + ethcore/src/miner/miner.rs | 7 +- ethcore/src/miner/mod.rs | 2 +- ethcore/src/state.rs | 8 +- ethcore/src/trace/executive_tracer.rs | 52 +++++- ethcore/src/trace/mod.rs | 27 +++- ethcore/src/trace/noop_tracer.rs | 24 ++- ethcore/src/types/executed.rs | 4 +- ethcore/src/types/trace_types/trace.rs | 164 +++++++++++++++++++ json/src/state/transaction.rs | 2 +- parity/rpc_apis.rs | 2 +- rpc/src/lib.rs | 1 + rpc/src/v1/impls/eth.rs | 8 +- rpc/src/v1/impls/ethcore.rs | 99 +++++++++++- rpc/src/v1/tests/eth.rs | 3 +- rpc/src/v1/tests/helpers/miner_service.rs | 2 +- rpc/src/v1/tests/mocked/eth.rs | 6 +- rpc/src/v1/tests/mocked/ethcore.rs | 61 ++++--- rpc/src/v1/traits/ethcore.rs | 4 + rpc/src/v1/types/block.rs | 2 +- rpc/src/v1/types/transaction.rs | 19 ++- util/src/common.rs | 7 + util/src/rlp/rlpstream.rs | 12 ++ util/src/rlp/untrusted_rlp.rs | 30 ++-- 36 files changed, 742 insertions(+), 172 deletions(-) diff --git a/ethcore/res/ethereum/morden.json b/ethcore/res/ethereum/morden.json index 1e385558d..0cc88ac64 100644 --- a/ethcore/res/ethereum/morden.json +++ b/ethcore/res/ethereum/morden.json @@ -34,17 +34,8 @@ "gasLimit": "0x2fefd8" }, "nodes": [ - "enode://b1217cbaa440e35ed471157123fe468e19e8b5ad5bedb4b1fdbcbdab6fb2f5ed3e95dd9c24a22a79fdb2352204cea207df27d92bfd21bfd41545e8b16f637499@104.44.138.37:30303", - "enode://7ee7195bfac561ec938a72cd84cd1a5d2b334415263feddc325b20b5010446fc6c361297d13decab4039028fa659c1e27cca1396574b87cc7b29eea2985e97fe@108.61.197.28:30303", - "enode://933c5d5470b77537e7d9c1ee686132b5032dd3e2a096d2f64d2004df4ce9fca4ad6da5e358edcc8f81e65f047e40045600181f5fb35066e771025f6cca8e7952@46.101.114.191:30303", - "enode://ad4028ba28783d5bf58f512cb4e24a8ce980d768177c4974e1140b16b925132c947349db9ca3646752891b382dafc839a0c0716c3764c1ed9d424f09d13d01cf@148.251.220.116:30303", - "enode://c54ddaacddc7029683c80edae91015520eb2712176fbe6fdb7a5a074659270638f1266cba1731681c7cb785bceb02ca8d8b23024e3ec736fc5579f2042be97ae@54.175.255.230:30303", - "enode://ceb5c0f85eb994dbe9693bf46d99b03f6b838d17cc74e68d5eb003171ff39e5f120b17f965b267c319303f94d80b9d994b77062fb1486d76ce95d9f3d8fe1cb4@46.101.122.141:30303", "enode://e731347db0521f3476e6bbbb83375dcd7133a1601425ebd15fd10f3835fd4c304fba6282087ca5a0deeafadf0aa0d4fd56c3323331901c1f38bd181c283e3e35@128.199.55.137:30303", - "enode://e941c58fed2709d792f552f408d2162c3d0a5597d22d1da617a9c9e6181f3251056a96adb45ae22eba70119355227298dc7e6dff805b092bae7da2f8564de422@85.25.217.23:30303", - "enode://f4b73c9d11a780293ff0ca7afa12c67797afdc33a4797a7c2ecc5b87e455b32a8b9e9804f2004072bac38350bf82d52521d1a09590d2079705fc8357aef2bf9c@71.202.223.50:56603", - "enode://1173eea53e0cb2b8da92423e44cf4cbafbc8ea16c1558cf06e18dfc5a2fc9b140cc802a4362b4c773fb1442541e6f2a225b200bb4c1f6b347e7510a50fa4873f@104.41.138.167:30300", - "enode://1aad341327808738ad34655611f1b13293c4155dde36c8e3788128829f15cc6db2da9435f29520553d4efc134aadc50115690194ac3af519aac7a388b524811e@109.188.125.2:30303" + "enode://ceb5c0f85eb994dbe9693bf46d99b03f6b838d17cc74e68d5eb003171ff39e5f120b17f965b267c319303f94d80b9d994b77062fb1486d76ce95d9f3d8fe1cb4@46.101.122.141:30303" ], "accounts": { "0000000000000000000000000000000000000001": { "balance": "1", "nonce": "1048576", "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } }, diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index 3aaeb02c3..3ddf7018a 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -436,7 +436,7 @@ impl Client where V: Verifier { } impl BlockChainClient for Client where V: Verifier { - fn call(&self, t: &SignedTransaction) -> Result { + fn call(&self, t: &SignedTransaction, vm_tracing: bool) -> Result { let header = self.block_header(BlockID::Latest).unwrap(); let view = HeaderView::new(&header); let last_hashes = self.build_last_hashes(view.hash()); @@ -456,10 +456,10 @@ impl BlockChainClient for Client where V: Verifier { ExecutionError::TransactionMalformed(message) })); let balance = state.balance(&sender); - // give the sender max balance + // give the sender a decent balance state.sub_balance(&sender, &balance); - state.add_balance(&sender, &U256::max_value()); - let options = TransactOptions { tracing: false, check_nonce: false }; + state.add_balance(&sender, &(U256::from(1) << 200)); + let options = TransactOptions { tracing: false, vm_tracing: vm_tracing, check_nonce: false }; Executive::new(&mut state, &env_info, self.engine.deref().deref(), &self.vm_factory).transact(t, options) } diff --git a/ethcore/src/client/mod.rs b/ethcore/src/client/mod.rs index 3f64cb620..84eea7494 100644 --- a/ethcore/src/client/mod.rs +++ b/ethcore/src/client/mod.rs @@ -157,7 +157,8 @@ pub trait BlockChainClient : Sync + Send { fn logs(&self, filter: Filter) -> Vec; /// Makes a non-persistent transaction call. - fn call(&self, t: &SignedTransaction) -> Result; + // TODO: should be able to accept blockchain location for call. + fn call(&self, t: &SignedTransaction, vm_tracing: bool) -> Result; /// Returns EvmFactory. fn vm_factory(&self) -> &EvmFactory; diff --git a/ethcore/src/client/test_client.rs b/ethcore/src/client/test_client.rs index 83799c78f..f3768d0a6 100644 --- a/ethcore/src/client/test_client.rs +++ b/ethcore/src/client/test_client.rs @@ -251,7 +251,7 @@ impl MiningBlockChainClient for TestBlockChainClient { } impl BlockChainClient for TestBlockChainClient { - fn call(&self, _t: &SignedTransaction) -> Result { + fn call(&self, _t: &SignedTransaction, _vm_tracing: bool) -> Result { Ok(self.execution_result.read().unwrap().clone().unwrap()) } diff --git a/ethcore/src/evm/ext.rs b/ethcore/src/evm/ext.rs index 4986b12c8..1c21e467a 100644 --- a/ethcore/src/evm/ext.rs +++ b/ethcore/src/evm/ext.rs @@ -105,4 +105,10 @@ pub trait Ext { /// Increments sstore refunds count by 1. fn inc_sstore_clears(&mut self); + + /// Prepare to trace an operation. Passthrough for the VM trace. + fn trace_prepare_execute(&mut self, _pc: usize, _instruction: u8, _gas_cost: &U256) -> bool { false } + + /// Trace the finalised execution of a single instruction. + fn trace_executed(&mut self, _gas_used: U256, _stack_push: &[U256], _mem_diff: Option<(usize, &[u8])>, _store_diff: Option<(U256, U256)>) {} } diff --git a/ethcore/src/evm/instructions.rs b/ethcore/src/evm/instructions.rs index 6a1a06ba9..c313726f1 100644 --- a/ethcore/src/evm/instructions.rs +++ b/ethcore/src/evm/instructions.rs @@ -124,6 +124,7 @@ pub struct InstructionInfo { pub side_effects: bool, pub tier: GasPriceTier } + impl InstructionInfo { pub fn new(name: &'static str, additional: usize, args: usize, ret: usize, side_effects: bool, tier: GasPriceTier) -> InstructionInfo { InstructionInfo { @@ -139,7 +140,7 @@ impl InstructionInfo { #[cfg_attr(rustfmt, rustfmt_skip)] /// Return details about specific instruction -pub fn get_info (instruction: Instruction) -> InstructionInfo { +pub fn get_info(instruction: Instruction) -> InstructionInfo { match instruction { STOP => InstructionInfo::new("STOP", 0, 0, 0, true, GasPriceTier::Zero), ADD => InstructionInfo::new("ADD", 0, 2, 1, false, GasPriceTier::VeryLow), diff --git a/ethcore/src/evm/interpreter.rs b/ethcore/src/evm/interpreter.rs index eb29ef257..9cf4d6034 100644 --- a/ethcore/src/evm/interpreter.rs +++ b/ethcore/src/evm/interpreter.rs @@ -17,8 +17,9 @@ ///! Rust VM implementation use common::*; +use trace::VMTracer; use super::instructions as instructions; -use super::instructions::Instruction; +use super::instructions::{Instruction, get_info}; use std::marker::Copy; use evm::{self, MessageCallResult, ContractCreateResult}; @@ -69,6 +70,8 @@ trait Stack { fn push(&mut self, elem: T); /// Get number of elements on Stack fn size(&self) -> usize; + /// Returns all data on stack. + fn peek_top(&mut self, no_of_elems: usize) -> &[T]; } struct VecStack { @@ -131,6 +134,11 @@ impl Stack for VecStack { fn size(&self) -> usize { self.stack.len() } + + fn peek_top(&mut self, no_from_top: usize) -> &[S] { + assert!(self.stack.len() >= no_from_top, "peek_top asked for more items than exist."); + &self.stack[self.stack.len() - no_from_top .. self.stack.len()] + } } trait Memory { @@ -293,10 +301,15 @@ impl evm::Evm for Interpreter { while reader.position < code.len() { let instruction = code[reader.position]; - reader.position += 1; // Calculate gas cost let (gas_cost, mem_size) = try!(self.get_gas_cost_mem(ext, instruction, &mut mem, &stack)); + + // TODO: make compile-time removable if too much of a performance hit. + let trace_executed = ext.trace_prepare_execute(reader.position, instruction, &gas_cost); + + reader.position += 1; + try!(self.verify_gas(¤t_gas, &gas_cost)); mem.expand(mem_size); current_gas = current_gas - gas_cost; //TODO: use operator -= @@ -311,10 +324,19 @@ impl evm::Evm for Interpreter { ); }); + let (mem_written, store_written) = match trace_executed { + true => (Self::mem_written(instruction, &stack), Self::store_written(instruction, &stack)), + false => (None, None), + }; + // Execute instruction let result = try!(self.exec_instruction( current_gas, ¶ms, ext, instruction, &mut reader, &mut mem, &mut stack - )); + )); + + if trace_executed { + ext.trace_executed(current_gas, stack.peek_top(get_info(instruction).ret), mem_written.map(|(o, s)| (o, &(mem[o..(o + s)]))), store_written); + } // Advance match result { @@ -485,6 +507,31 @@ impl Interpreter { } } + fn mem_written( + instruction: Instruction, + stack: &Stack + ) -> Option<(usize, usize)> { + match instruction { + instructions::MSTORE | instructions::MLOAD => Some((stack.peek(0).low_u64() as usize, 32)), + instructions::MSTORE8 => Some((stack.peek(0).low_u64() as usize, 1)), + instructions::CALLDATACOPY | instructions::CODECOPY => Some((stack.peek(0).low_u64() as usize, stack.peek(2).low_u64() as usize)), + instructions::EXTCODECOPY => Some((stack.peek(1).low_u64() as usize, stack.peek(3).low_u64() as usize)), + instructions::CALL | instructions::CALLCODE => Some((stack.peek(5).low_u64() as usize, stack.peek(6).low_u64() as usize)), + instructions::DELEGATECALL => Some((stack.peek(4).low_u64() as usize, stack.peek(5).low_u64() as usize)), + _ => None, + } + } + + fn store_written( + instruction: Instruction, + stack: &Stack + ) -> Option<(U256, U256)> { + match instruction { + instructions::SSTORE => Some((stack.peek(0).clone(), stack.peek(1).clone())), + _ => None, + } + } + fn mem_gas_cost(&self, schedule: &evm::Schedule, current_mem_size: usize, mem_size: &U256) -> Result<(U256, usize), evm::Error> { let gas_for_mem = |mem_size: U256| { let s = mem_size >> 5; @@ -833,10 +880,12 @@ impl Interpreter { } } - fn verify_instructions_requirements(&self, - info: &instructions::InstructionInfo, - stack_limit: usize, - stack: &Stack) -> Result<(), evm::Error> { + fn verify_instructions_requirements( + &self, + info: &instructions::InstructionInfo, + stack_limit: usize, + stack: &Stack + ) -> Result<(), evm::Error> { if !stack.has(info.args) { Err(evm::Error::StackUnderflow { instruction: info.name, diff --git a/ethcore/src/evm/jit.rs b/ethcore/src/evm/jit.rs index 6a22a1306..694c1668a 100644 --- a/ethcore/src/evm/jit.rs +++ b/ethcore/src/evm/jit.rs @@ -16,6 +16,7 @@ //! Just in time compiler execution environment. use common::*; +use trace::VMTracer; use evmjit; use evm; diff --git a/ethcore/src/evm/mod.rs b/ethcore/src/evm/mod.rs index b7816b99c..06ce5e7e8 100644 --- a/ethcore/src/evm/mod.rs +++ b/ethcore/src/evm/mod.rs @@ -33,3 +33,4 @@ pub use self::evm::{Evm, Error, Result}; pub use self::ext::{Ext, ContractCreateResult, MessageCallResult}; pub use self::factory::{Factory, VMType}; pub use self::schedule::Schedule; +pub use self::instructions::get_info; diff --git a/ethcore/src/executive.rs b/ethcore/src/executive.rs index 291b4dba2..7a48e435e 100644 --- a/ethcore/src/executive.rs +++ b/ethcore/src/executive.rs @@ -21,9 +21,8 @@ use engine::*; use evm::{self, Ext, Factory}; use externalities::*; use substate::*; -use trace::{Trace, Tracer, NoopTracer, ExecutiveTracer}; +use trace::{Trace, Tracer, NoopTracer, ExecutiveTracer, VMTrace, VMTracer, ExecutiveVMTracer, NoopVMTracer}; use crossbeam; - pub use types::executed::{Executed, ExecutionResult}; /// Max depth to avoid stack overflow (when it's reached we start a new thread with VM) @@ -43,6 +42,8 @@ pub fn contract_address(address: &Address, nonce: &U256) -> Address { pub struct TransactOptions { /// Enable call tracing. pub tracing: bool, + /// Enable VM tracing. + pub vm_tracing: bool, /// Check transaction nonce before execution. pub check_nonce: bool, } @@ -80,21 +81,40 @@ impl<'a> Executive<'a> { } /// Creates `Externalities` from `Executive`. - pub fn as_externalities<'_, T>(&'_ mut self, origin_info: OriginInfo, substate: &'_ mut Substate, output: OutputPolicy<'_, '_>, tracer: &'_ mut T) -> Externalities<'_, T> where T: Tracer { - Externalities::new(self.state, self.info, self.engine, self.vm_factory, self.depth, origin_info, substate, output, tracer) + pub fn as_externalities<'_, T, V>( + &'_ mut self, + origin_info: OriginInfo, + substate: &'_ mut Substate, + output: OutputPolicy<'_, '_>, + tracer: &'_ mut T, + vm_tracer: &'_ mut V + ) -> Externalities<'_, T, V> where T: Tracer, V: VMTracer { + Externalities::new(self.state, self.info, self.engine, self.vm_factory, self.depth, origin_info, substate, output, tracer, vm_tracer) } /// This function should be used to execute transaction. pub fn transact(&'a mut self, t: &SignedTransaction, options: TransactOptions) -> Result { let check = options.check_nonce; match options.tracing { - true => self.transact_with_tracer(t, check, ExecutiveTracer::default()), - false => self.transact_with_tracer(t, check, NoopTracer), + true => match options.vm_tracing { + true => self.transact_with_tracer(t, check, ExecutiveTracer::default(), ExecutiveVMTracer::default()), + false => self.transact_with_tracer(t, check, ExecutiveTracer::default(), NoopVMTracer), + }, + false => match options.vm_tracing { + true => self.transact_with_tracer(t, check, NoopTracer, ExecutiveVMTracer::default()), + false => self.transact_with_tracer(t, check, NoopTracer, NoopVMTracer), + }, } } /// Execute transaction/call with tracing enabled - pub fn transact_with_tracer(&'a mut self, t: &SignedTransaction, check_nonce: bool, mut tracer: T) -> Result where T: Tracer { + pub fn transact_with_tracer( + &'a mut self, + t: &SignedTransaction, + check_nonce: bool, + mut tracer: T, + mut vm_tracer: V + ) -> Result where T: Tracer, V: VMTracer { let sender = try!(t.sender().map_err(|e| { let message = format!("Transaction malformed: {:?}", e); ExecutionError::TransactionMalformed(message) @@ -154,7 +174,7 @@ impl<'a> Executive<'a> { code: Some(t.data.clone()), data: None, }; - (self.create(params, &mut substate, &mut tracer), vec![]) + (self.create(params, &mut substate, &mut tracer, &mut vm_tracer), vec![]) }, Action::Call(ref address) => { let params = ActionParams { @@ -170,20 +190,26 @@ impl<'a> Executive<'a> { }; // TODO: move output upstream let mut out = vec![]; - (self.call(params, &mut substate, BytesRef::Flexible(&mut out), &mut tracer), out) + (self.call(params, &mut substate, BytesRef::Flexible(&mut out), &mut tracer, &mut vm_tracer), out) } }; // finalize here! - Ok(try!(self.finalize(t, substate, gas_left, output, tracer.traces().pop()))) + Ok(try!(self.finalize(t, substate, gas_left, output, tracer.traces().pop(), vm_tracer.drain()))) } - fn exec_vm(&mut self, params: ActionParams, unconfirmed_substate: &mut Substate, output_policy: OutputPolicy, tracer: &mut T) - -> evm::Result where T: Tracer { + fn exec_vm( + &mut self, + params: ActionParams, + unconfirmed_substate: &mut Substate, + output_policy: OutputPolicy, + tracer: &mut T, + vm_tracer: &mut V + ) -> evm::Result where T: Tracer, V: VMTracer { // Ordinary execution - keep VM in same thread if (self.depth + 1) % MAX_VM_DEPTH_FOR_THREAD != 0 { let vm_factory = self.vm_factory; - let mut ext = self.as_externalities(OriginInfo::from(¶ms), unconfirmed_substate, output_policy, tracer); + let mut ext = self.as_externalities(OriginInfo::from(¶ms), unconfirmed_substate, output_policy, tracer, vm_tracer); trace!(target: "executive", "ext.schedule.have_delegate_call: {}", ext.schedule().have_delegate_call); return vm_factory.create().exec(params, &mut ext); } @@ -193,7 +219,7 @@ impl<'a> Executive<'a> { // https://github.com/aturon/crossbeam/issues/16 crossbeam::scope(|scope| { let vm_factory = self.vm_factory; - let mut ext = self.as_externalities(OriginInfo::from(¶ms), unconfirmed_substate, output_policy, tracer); + let mut ext = self.as_externalities(OriginInfo::from(¶ms), unconfirmed_substate, output_policy, tracer, vm_tracer); scope.spawn(move || { vm_factory.create().exec(params, &mut ext) @@ -205,8 +231,14 @@ impl<'a> Executive<'a> { /// NOTE. It does not finalize the transaction (doesn't do refunds, nor suicides). /// Modifies the substate and the output. /// Returns either gas_left or `evm::Error`. - pub fn call(&mut self, params: ActionParams, substate: &mut Substate, mut output: BytesRef, tracer: &mut T) - -> evm::Result where T: Tracer { + pub fn call( + &mut self, + params: ActionParams, + substate: &mut Substate, + mut output: BytesRef, + tracer: &mut T, + vm_tracer: &mut V + ) -> evm::Result where T: Tracer, V: VMTracer { // backup used in case of running out of gas self.state.snapshot(); @@ -264,16 +296,22 @@ impl<'a> Executive<'a> { let trace_info = tracer.prepare_trace_call(¶ms); let mut trace_output = tracer.prepare_trace_output(); let mut subtracer = tracer.subtracer(); + let gas = params.gas; if params.code.is_some() { // part of substate that may be reverted let mut unconfirmed_substate = Substate::new(); + // TODO: make ActionParams pass by ref then avoid copy altogether. + let mut subvmtracer = vm_tracer.prepare_subtrace(params.code.as_ref().expect("scope is protected by params.code.is_some condition")); + let res = { - self.exec_vm(params, &mut unconfirmed_substate, OutputPolicy::Return(output, trace_output.as_mut()), &mut subtracer) + self.exec_vm(params, &mut unconfirmed_substate, OutputPolicy::Return(output, trace_output.as_mut()), &mut subtracer, &mut subvmtracer) }; + vm_tracer.done_subtrace(subvmtracer); + trace!(target: "executive", "res={:?}", res); let traces = subtracer.traces(); @@ -307,8 +345,13 @@ impl<'a> Executive<'a> { /// Creates contract with given contract params. /// NOTE. It does not finalize the transaction (doesn't do refunds, nor suicides). /// Modifies the substate. - pub fn create(&mut self, params: ActionParams, substate: &mut Substate, tracer: &mut T) -> evm::Result where T: - Tracer { + pub fn create( + &mut self, + params: ActionParams, + substate: &mut Substate, + tracer: &mut T, + vm_tracer: &mut V + ) -> evm::Result where T: Tracer, V: VMTracer { // backup used in case of running out of gas self.state.snapshot(); @@ -330,10 +373,14 @@ impl<'a> Executive<'a> { let gas = params.gas; let created = params.address.clone(); + let mut subvmtracer = vm_tracer.prepare_subtrace(¶ms.code.as_ref().expect("two ways into create (Externalities::create and Executive::transact_with_tracer); both place `Some(...)` `code` in `params`; qed")); + let res = { - self.exec_vm(params, &mut unconfirmed_substate, OutputPolicy::InitContract(trace_output.as_mut()), &mut subtracer) + self.exec_vm(params, &mut unconfirmed_substate, OutputPolicy::InitContract(trace_output.as_mut()), &mut subtracer, &mut subvmtracer) }; + vm_tracer.done_subtrace(subvmtracer); + match res { Ok(gas_left) => tracer.trace_create( trace_info, @@ -351,7 +398,15 @@ impl<'a> Executive<'a> { } /// Finalizes the transaction (does refunds and suicides). - fn finalize(&mut self, t: &SignedTransaction, substate: Substate, result: evm::Result, output: Bytes, trace: Option) -> ExecutionResult { + fn finalize( + &mut self, + t: &SignedTransaction, + substate: Substate, + result: evm::Result, + output: Bytes, + trace: Option, + vm_trace: Option + ) -> ExecutionResult { let schedule = self.engine.schedule(self.info); // refunds from SSTORE nonzero -> zero @@ -394,6 +449,7 @@ impl<'a> Executive<'a> { contracts_created: vec![], output: output, trace: trace, + vm_trace: vm_trace, }) }, _ => { @@ -406,6 +462,7 @@ impl<'a> Executive<'a> { contracts_created: substate.contracts_created, output: output, trace: trace, + vm_trace: vm_trace, }) }, } @@ -438,6 +495,7 @@ mod tests { use tests::helpers::*; use trace::trace; use trace::{Trace, Tracer, NoopTracer, ExecutiveTracer}; + use trace::{VMTrace, VMOperation, VMExecutedOperation, MemoryDiff, StorageDiff, VMTracer, NoopVMTracer, ExecutiveVMTracer}; #[test] fn test_contract_address() { @@ -466,7 +524,7 @@ mod tests { let gas_left = { let mut ex = Executive::new(&mut state, &info, &engine, &factory); - ex.create(params, &mut substate, &mut NoopTracer).unwrap() + ex.create(params, &mut substate, &mut NoopTracer, &mut NoopVMTracer).unwrap() }; assert_eq!(gas_left, U256::from(79_975)); @@ -525,7 +583,7 @@ mod tests { let gas_left = { let mut ex = Executive::new(&mut state, &info, &engine, &factory); - ex.create(params, &mut substate, &mut NoopTracer).unwrap() + ex.create(params, &mut substate, &mut NoopTracer, &mut NoopVMTracer).unwrap() }; assert_eq!(gas_left, U256::from(62_976)); @@ -542,7 +600,7 @@ mod tests { // 52 // 60 1d - push 29 // 60 03 - push 3 - // 60 17 - push 17 + // 60 17 - push 23 // f0 - create // 60 00 - push 0 // 55 sstore @@ -578,13 +636,16 @@ mod tests { let engine = TestEngine::new(5); let mut substate = Substate::new(); let mut tracer = ExecutiveTracer::default(); + let mut vm_tracer = ExecutiveVMTracer::default(); let gas_left = { let mut ex = Executive::new(&mut state, &info, &engine, &factory); let output = BytesRef::Fixed(&mut[0u8;0]); - ex.call(params, &mut substate, output, &mut tracer).unwrap() + ex.call(params, &mut substate, output, &mut tracer, &mut vm_tracer).unwrap() }; + assert_eq!(gas_left, U256::from(44_752)); + let expected_trace = vec![ Trace { depth: 0, action: trace::Action::Call(trace::Call { @@ -615,7 +676,39 @@ mod tests { }] }]; assert_eq!(tracer.traces(), expected_trace); - assert_eq!(gas_left, U256::from(44_752)); + + let expected_vm_trace = VMTrace { + parent_step: 0, + code: vec![124, 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, 96, 0, 82, 96, 29, 96, 3, 96, 23, 240, 96, 0, 85], + operations: vec![ + VMOperation { pc: 0, instruction: 124, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 99997.into(), stack_push: vec_into![U256::from_dec_str("2589892687202724018173567190521546555304938078595079151649957320078677").unwrap()], mem_diff: None, store_diff: None }) }, + VMOperation { pc: 30, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 99994.into(), stack_push: vec_into![0], mem_diff: None, store_diff: None }) }, + VMOperation { pc: 32, instruction: 82, gas_cost: 6.into(), executed: Some(VMExecutedOperation { gas_used: 99988.into(), stack_push: vec_into![], mem_diff: Some(MemoryDiff { offset: 0, data: vec![0, 0, 0, 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] }), store_diff: None }) }, + VMOperation { pc: 33, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 99985.into(), stack_push: vec_into![29], mem_diff: None, store_diff: None }) }, + VMOperation { pc: 35, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 99982.into(), stack_push: vec_into![3], mem_diff: None, store_diff: None }) }, + VMOperation { pc: 37, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 99979.into(), stack_push: vec_into![23], mem_diff: None, store_diff: None }) }, + VMOperation { pc: 39, instruction: 240, gas_cost: 32000.into(), executed: Some(VMExecutedOperation { gas_used: 67979.into(), stack_push: vec_into![U256::from_dec_str("1135198453258042933984631383966629874710669425204").unwrap()], mem_diff: None, store_diff: None }) }, + VMOperation { pc: 40, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 64752.into(), stack_push: vec_into![0], mem_diff: None, store_diff: None }) }, + VMOperation { pc: 42, instruction: 85, gas_cost: 20000.into(), executed: Some(VMExecutedOperation { gas_used: 44752.into(), stack_push: vec_into![], mem_diff: None, store_diff: Some(StorageDiff { location: 0.into(), value: U256::from_dec_str("1135198453258042933984631383966629874710669425204").unwrap() }) }) } + ], + subs: vec![ + VMTrace { + parent_step: 7, + code: 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], + operations: vec![ + VMOperation { pc: 0, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 67976.into(), stack_push: vec_into![16], mem_diff: None, store_diff: None }) }, + VMOperation { pc: 2, instruction: 128, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 67973.into(), stack_push: vec_into![16, 16], mem_diff: None, store_diff: None }) }, + VMOperation { pc: 3, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 67970.into(), stack_push: vec_into![12], mem_diff: None, store_diff: None }) }, + VMOperation { pc: 5, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 67967.into(), stack_push: vec_into![0], mem_diff: None, store_diff: None }) }, + VMOperation { pc: 7, instruction: 57, gas_cost: 9.into(), executed: Some(VMExecutedOperation { gas_used: 67958.into(), stack_push: vec_into![], mem_diff: Some(MemoryDiff { offset: 0, data: vec![96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53] }), store_diff: None }) }, + VMOperation { pc: 8, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 67955.into(), stack_push: vec_into![0], mem_diff: None, store_diff: None }) }, + VMOperation { pc: 10, instruction: 243, gas_cost: 0.into(), executed: Some(VMExecutedOperation { gas_used: 67955.into(), stack_push: vec_into![], mem_diff: None, store_diff: None }) } + ], + subs: vec![] + } + ] + }; + assert_eq!(vm_tracer.drain().unwrap(), expected_vm_trace); } evm_test!{test_create_contract: test_create_contract_jit, test_create_contract_int} @@ -650,12 +743,15 @@ mod tests { let engine = TestEngine::new(5); let mut substate = Substate::new(); let mut tracer = ExecutiveTracer::default(); + let mut vm_tracer = ExecutiveVMTracer::default(); let gas_left = { let mut ex = Executive::new(&mut state, &info, &engine, &factory); - ex.create(params.clone(), &mut substate, &mut tracer).unwrap() + ex.create(params.clone(), &mut substate, &mut tracer, &mut vm_tracer).unwrap() }; + assert_eq!(gas_left, U256::from(96_776)); + let expected_trace = vec![Trace { depth: 0, action: trace::Action::Create(trace::Create { @@ -671,9 +767,23 @@ mod tests { }), subs: vec![] }]; - assert_eq!(tracer.traces(), expected_trace); - assert_eq!(gas_left, U256::from(96_776)); + + let expected_vm_trace = VMTrace { + parent_step: 0, + code: 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], + operations: vec![ + VMOperation { pc: 0, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 99997.into(), stack_push: vec_into![16], mem_diff: None, store_diff: None }) }, + VMOperation { pc: 2, instruction: 128, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 99994.into(), stack_push: vec_into![16, 16], mem_diff: None, store_diff: None }) }, + VMOperation { pc: 3, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 99991.into(), stack_push: vec_into![12], mem_diff: None, store_diff: None }) }, + VMOperation { pc: 5, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 99988.into(), stack_push: vec_into![0], mem_diff: None, store_diff: None }) }, + VMOperation { pc: 7, instruction: 57, gas_cost: 9.into(), executed: Some(VMExecutedOperation { gas_used: 99979.into(), stack_push: vec_into![], mem_diff: Some(MemoryDiff { offset: 0, data: vec![96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53] }), store_diff: None }) }, + VMOperation { pc: 8, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 99976.into(), stack_push: vec_into![0], mem_diff: None, store_diff: None }) }, + VMOperation { pc: 10, instruction: 243, gas_cost: 0.into(), executed: Some(VMExecutedOperation { gas_used: 99976.into(), stack_push: vec_into![], mem_diff: None, store_diff: None }) } + ], + subs: vec![] + }; + assert_eq!(vm_tracer.drain().unwrap(), expected_vm_trace); } evm_test!{test_create_contract_value_too_high: test_create_contract_value_too_high_jit, test_create_contract_value_too_high_int} @@ -722,7 +832,7 @@ mod tests { let gas_left = { let mut ex = Executive::new(&mut state, &info, &engine, &factory); - ex.create(params, &mut substate, &mut NoopTracer).unwrap() + ex.create(params, &mut substate, &mut NoopTracer, &mut NoopVMTracer).unwrap() }; assert_eq!(gas_left, U256::from(62_976)); @@ -774,7 +884,7 @@ mod tests { { let mut ex = Executive::new(&mut state, &info, &engine, &factory); - ex.create(params, &mut substate, &mut NoopTracer).unwrap(); + ex.create(params, &mut substate, &mut NoopTracer, &mut NoopVMTracer).unwrap(); } assert_eq!(substate.contracts_created.len(), 1); @@ -835,7 +945,7 @@ mod tests { let gas_left = { let mut ex = Executive::new(&mut state, &info, &engine, &factory); - ex.call(params, &mut substate, BytesRef::Fixed(&mut []), &mut NoopTracer).unwrap() + ex.call(params, &mut substate, BytesRef::Fixed(&mut []), &mut NoopTracer, &mut NoopVMTracer).unwrap() }; assert_eq!(gas_left, U256::from(73_237)); @@ -880,7 +990,7 @@ mod tests { let gas_left = { let mut ex = Executive::new(&mut state, &info, &engine, &factory); - ex.call(params, &mut substate, BytesRef::Fixed(&mut []), &mut NoopTracer).unwrap() + ex.call(params, &mut substate, BytesRef::Fixed(&mut []), &mut NoopTracer, &mut NoopVMTracer).unwrap() }; assert_eq!(gas_left, U256::from(59_870)); @@ -913,7 +1023,7 @@ mod tests { let executed = { let mut ex = Executive::new(&mut state, &info, &engine, &factory); - let opts = TransactOptions { check_nonce: true, tracing: false }; + let opts = TransactOptions { check_nonce: true, tracing: false, vm_tracing: false }; ex.transact(&t, opts).unwrap() }; @@ -947,7 +1057,7 @@ mod tests { let res = { let mut ex = Executive::new(&mut state, &info, &engine, &factory); - let opts = TransactOptions { check_nonce: true, tracing: false }; + let opts = TransactOptions { check_nonce: true, tracing: false, vm_tracing: false }; ex.transact(&t, opts) }; @@ -979,7 +1089,7 @@ mod tests { let res = { let mut ex = Executive::new(&mut state, &info, &engine, &factory); - let opts = TransactOptions { check_nonce: true, tracing: false }; + let opts = TransactOptions { check_nonce: true, tracing: false, vm_tracing: false }; ex.transact(&t, opts) }; @@ -1013,7 +1123,7 @@ mod tests { let res = { let mut ex = Executive::new(&mut state, &info, &engine, &factory); - let opts = TransactOptions { check_nonce: true, tracing: false }; + let opts = TransactOptions { check_nonce: true, tracing: false, vm_tracing: false }; ex.transact(&t, opts) }; @@ -1047,7 +1157,7 @@ mod tests { let res = { let mut ex = Executive::new(&mut state, &info, &engine, &factory); - let opts = TransactOptions { check_nonce: true, tracing: false }; + let opts = TransactOptions { check_nonce: true, tracing: false, vm_tracing: false }; ex.transact(&t, opts) }; @@ -1082,7 +1192,7 @@ mod tests { let result = { let mut ex = Executive::new(&mut state, &info, &engine, &factory); - ex.create(params, &mut substate, &mut NoopTracer) + ex.create(params, &mut substate, &mut NoopTracer, &mut NoopVMTracer) }; match result { diff --git a/ethcore/src/externalities.rs b/ethcore/src/externalities.rs index 99d2eed72..675d1904b 100644 --- a/ethcore/src/externalities.rs +++ b/ethcore/src/externalities.rs @@ -21,7 +21,7 @@ use engine::*; use executive::*; use evm::{self, Schedule, Ext, ContractCreateResult, MessageCallResult, Factory}; use substate::*; -use trace::Tracer; +use trace::{Tracer, VMTracer}; /// Policy for handling output data on `RETURN` opcode. pub enum OutputPolicy<'a, 'b> { @@ -55,7 +55,7 @@ impl OriginInfo { } /// Implementation of evm Externalities. -pub struct Externalities<'a, T> where T: 'a + Tracer { +pub struct Externalities<'a, T, V> where T: 'a + Tracer, V: 'a + VMTracer { state: &'a mut State, env_info: &'a EnvInfo, engine: &'a Engine, @@ -66,10 +66,10 @@ pub struct Externalities<'a, T> where T: 'a + Tracer { schedule: Schedule, output: OutputPolicy<'a, 'a>, tracer: &'a mut T, + vm_tracer: &'a mut V, } -impl<'a, T> Externalities<'a, T> where T: 'a + Tracer { - +impl<'a, T, V> Externalities<'a, T, V> where T: 'a + Tracer, V: 'a + VMTracer { #[cfg_attr(feature="dev", allow(too_many_arguments))] /// Basic `Externalities` constructor. pub fn new(state: &'a mut State, @@ -81,6 +81,7 @@ impl<'a, T> Externalities<'a, T> where T: 'a + Tracer { substate: &'a mut Substate, output: OutputPolicy<'a, 'a>, tracer: &'a mut T, + vm_tracer: &'a mut V, ) -> Self { Externalities { state: state, @@ -93,11 +94,12 @@ impl<'a, T> Externalities<'a, T> where T: 'a + Tracer { schedule: engine.schedule(env_info), output: output, tracer: tracer, + vm_tracer: vm_tracer, } } } -impl<'a, T> Ext for Externalities<'a, T> where T: 'a + Tracer { +impl<'a, T, V> Ext for Externalities<'a, T, V> where T: 'a + Tracer, V: 'a + VMTracer { fn storage_at(&self, key: &H256) -> H256 { self.state.storage_at(&self.origin_info.address, key) } @@ -152,7 +154,7 @@ impl<'a, T> Ext for Externalities<'a, T> where T: 'a + Tracer { let mut ex = Executive::from_parent(self.state, self.env_info, self.engine, self.vm_factory, self.depth); // TODO: handle internal error separately - match ex.create(params, self.substate, self.tracer) { + match ex.create(params, self.substate, self.tracer, self.vm_tracer) { Ok(gas_left) => { self.substate.contracts_created.push(address.clone()); ContractCreateResult::Created(address, gas_left) @@ -190,7 +192,7 @@ impl<'a, T> Ext for Externalities<'a, T> where T: 'a + Tracer { let mut ex = Executive::from_parent(self.state, self.env_info, self.engine, self.vm_factory, self.depth); - match ex.call(params, self.substate, BytesRef::Fixed(output), self.tracer) { + match ex.call(params, self.substate, BytesRef::Fixed(output), self.tracer, self.vm_tracer) { Ok(gas_left) => MessageCallResult::Success(gas_left), _ => MessageCallResult::Failed } @@ -286,6 +288,14 @@ impl<'a, T> Ext for Externalities<'a, T> where T: 'a + Tracer { fn inc_sstore_clears(&mut self) { self.substate.sstore_clears_count = self.substate.sstore_clears_count + U256::one(); } + + fn trace_prepare_execute(&mut self, pc: usize, instruction: u8, gas_cost: &U256) -> bool { + self.vm_tracer.trace_prepare_execute(pc, instruction, gas_cost) + } + + fn trace_executed(&mut self, gas_used: U256, stack_push: &[U256], mem_diff: Option<(usize, &[u8])>, store_diff: Option<(U256, U256)>) { + self.vm_tracer.trace_executed(gas_used, stack_push, mem_diff, store_diff) + } } #[cfg(test)] @@ -297,7 +307,7 @@ mod tests { use substate::*; use tests::helpers::*; use super::*; - use trace::{NoopTracer}; + use trace::{NoopTracer, NoopVMTracer}; fn get_test_origin() -> OriginInfo { OriginInfo { @@ -349,9 +359,10 @@ mod tests { let mut setup = TestSetup::new(); let state = setup.state.reference_mut(); let mut tracer = NoopTracer; + let mut vm_tracer = NoopVMTracer; let vm_factory = Default::default(); - let ext = Externalities::new(state, &setup.env_info, &*setup.engine, &vm_factory, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None), &mut tracer); + let ext = Externalities::new(state, &setup.env_info, &*setup.engine, &vm_factory, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None), &mut tracer, &mut vm_tracer); assert_eq!(ext.env_info().number, 100); } @@ -361,9 +372,10 @@ mod tests { let mut setup = TestSetup::new(); let state = setup.state.reference_mut(); let mut tracer = NoopTracer; + let mut vm_tracer = NoopVMTracer; let vm_factory = Default::default(); - let ext = Externalities::new(state, &setup.env_info, &*setup.engine, &vm_factory, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None), &mut tracer); + let ext = Externalities::new(state, &setup.env_info, &*setup.engine, &vm_factory, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None), &mut tracer, &mut vm_tracer); let hash = ext.blockhash(&U256::from_str("0000000000000000000000000000000000000000000000000000000000120000").unwrap()); @@ -383,9 +395,10 @@ mod tests { } let state = setup.state.reference_mut(); let mut tracer = NoopTracer; + let mut vm_tracer = NoopVMTracer; let vm_factory = Default::default(); - let ext = Externalities::new(state, &setup.env_info, &*setup.engine, &vm_factory, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None), &mut tracer); + let ext = Externalities::new(state, &setup.env_info, &*setup.engine, &vm_factory, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None), &mut tracer, &mut vm_tracer); let hash = ext.blockhash(&U256::from_str("0000000000000000000000000000000000000000000000000000000000120000").unwrap()); @@ -398,9 +411,10 @@ mod tests { let mut setup = TestSetup::new(); let state = setup.state.reference_mut(); let mut tracer = NoopTracer; + let mut vm_tracer = NoopVMTracer; let vm_factory = Default::default(); - let mut ext = Externalities::new(state, &setup.env_info, &*setup.engine, &vm_factory, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None), &mut tracer); + let mut ext = Externalities::new(state, &setup.env_info, &*setup.engine, &vm_factory, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None), &mut tracer, &mut vm_tracer); let mut output = vec![]; @@ -423,10 +437,11 @@ mod tests { let mut setup = TestSetup::new(); let state = setup.state.reference_mut(); let mut tracer = NoopTracer; + let mut vm_tracer = NoopVMTracer; { let vm_factory = Default::default(); - let mut ext = Externalities::new(state, &setup.env_info, &*setup.engine, &vm_factory, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None), &mut tracer); + let mut ext = Externalities::new(state, &setup.env_info, &*setup.engine, &vm_factory, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None), &mut tracer, &mut vm_tracer); ext.log(log_topics, &log_data); } @@ -440,10 +455,11 @@ mod tests { let mut setup = TestSetup::new(); let state = setup.state.reference_mut(); let mut tracer = NoopTracer; + let mut vm_tracer = NoopVMTracer; { let vm_factory = Default::default(); - let mut ext = Externalities::new(state, &setup.env_info, &*setup.engine, &vm_factory, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None), &mut tracer); + let mut ext = Externalities::new(state, &setup.env_info, &*setup.engine, &vm_factory, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None), &mut tracer, &mut vm_tracer); ext.suicide(&refund_account); } diff --git a/ethcore/src/json_tests/executive.rs b/ethcore/src/json_tests/executive.rs index 9e9620169..c5d781ab2 100644 --- a/ethcore/src/json_tests/executive.rs +++ b/ethcore/src/json_tests/executive.rs @@ -25,6 +25,7 @@ use substate::*; use tests::helpers::*; use ethjson; use trace::{Tracer, NoopTracer}; +use trace::{VMTracer, NoopVMTracer}; #[derive(Debug, PartialEq)] struct CallCreate { @@ -48,32 +49,35 @@ impl From for CallCreate { /// Tiny wrapper around executive externalities. /// Stores callcreates. -struct TestExt<'a, T> where T: 'a + Tracer { - ext: Externalities<'a, T>, +struct TestExt<'a, T, V> where T: 'a + Tracer, V: 'a + VMTracer { + ext: Externalities<'a, T, V>, callcreates: Vec, contract_address: Address } -impl<'a, T> TestExt<'a, T> where T: 'a + Tracer { - fn new(state: &'a mut State, - info: &'a EnvInfo, - engine: &'a Engine, - vm_factory: &'a Factory, - depth: usize, - origin_info: OriginInfo, - substate: &'a mut Substate, - output: OutputPolicy<'a, 'a>, - address: Address, - tracer: &'a mut T) -> Self { +impl<'a, T, V> TestExt<'a, T, V> where T: 'a + Tracer, V: 'a + VMTracer { + fn new( + state: &'a mut State, + info: &'a EnvInfo, + engine: &'a Engine, + vm_factory: &'a Factory, + depth: usize, + origin_info: OriginInfo, + substate: &'a mut Substate, + output: OutputPolicy<'a, 'a>, + address: Address, + tracer: &'a mut T, + vm_tracer: &'a mut V, + ) -> Self { TestExt { contract_address: contract_address(&address, &state.nonce(&address)), - ext: Externalities::new(state, info, engine, vm_factory, depth, origin_info, substate, output, tracer), + ext: Externalities::new(state, info, engine, vm_factory, depth, origin_info, substate, output, tracer, vm_tracer), callcreates: vec![] } } } -impl<'a, T> Ext for TestExt<'a, T> where T: Tracer { +impl<'a, T, V> Ext for TestExt<'a, T, V> where T: Tracer, V: VMTracer { fn storage_at(&self, key: &H256) -> H256 { self.ext.storage_at(key) } @@ -186,6 +190,7 @@ fn do_json_test_for(vm_type: &VMType, json_data: &[u8]) -> Vec { let mut substate = Substate::new(); let mut tracer = NoopTracer; + let mut vm_tracer = NoopVMTracer; let mut output = vec![]; // execute @@ -201,6 +206,7 @@ fn do_json_test_for(vm_type: &VMType, json_data: &[u8]) -> Vec { OutputPolicy::Return(BytesRef::Flexible(&mut output), None), params.address.clone(), &mut tracer, + &mut vm_tracer, ); let evm = vm_factory.create(); let res = evm.exec(params, &mut ex); diff --git a/ethcore/src/lib.rs b/ethcore/src/lib.rs index 5cf9d50a1..cb7942f23 100644 --- a/ethcore/src/lib.rs +++ b/ethcore/src/lib.rs @@ -141,3 +141,5 @@ mod tests; mod json_tests; pub use types::*; +pub use evm::get_info; +pub use executive::contract_address; \ No newline at end of file diff --git a/ethcore/src/miner/miner.rs b/ethcore/src/miner/miner.rs index 66b17cd7c..46b5ae733 100644 --- a/ethcore/src/miner/miner.rs +++ b/ethcore/src/miner/miner.rs @@ -251,7 +251,7 @@ impl MinerService for Miner { } } - fn call(&self, chain: &MiningBlockChainClient, t: &SignedTransaction) -> Result { + fn call(&self, chain: &MiningBlockChainClient, t: &SignedTransaction, vm_tracing: bool) -> Result { let sealing_work = self.sealing_work.lock().unwrap(); match sealing_work.peek_last_ref() { Some(work) => { @@ -277,12 +277,13 @@ impl MinerService for Miner { // give the sender max balance state.sub_balance(&sender, &balance); state.add_balance(&sender, &U256::max_value()); - let options = TransactOptions { tracing: false, check_nonce: false }; + let options = TransactOptions { tracing: false, vm_tracing: vm_tracing, check_nonce: false }; + // TODO: use vm_trace here. Executive::new(&mut state, &env_info, self.engine(), chain.vm_factory()).transact(t, options) }, None => { - chain.call(t) + chain.call(t, vm_tracing) } } } diff --git a/ethcore/src/miner/mod.rs b/ethcore/src/miner/mod.rs index 60b680f7a..8165dfbba 100644 --- a/ethcore/src/miner/mod.rs +++ b/ethcore/src/miner/mod.rs @@ -148,7 +148,7 @@ pub trait MinerService : Send + Sync { fn balance(&self, chain: &MiningBlockChainClient, address: &Address) -> U256; /// Call into contract code using pending state. - fn call(&self, chain: &MiningBlockChainClient, t: &SignedTransaction) -> Result; + fn call(&self, chain: &MiningBlockChainClient, t: &SignedTransaction, vm_tracing: bool) -> Result; /// Get storage value in pending state. fn storage_at(&self, chain: &MiningBlockChainClient, address: &Address, position: &H256) -> H256; diff --git a/ethcore/src/state.rs b/ethcore/src/state.rs index c099b17a5..51162bb8e 100644 --- a/ethcore/src/state.rs +++ b/ethcore/src/state.rs @@ -183,16 +183,14 @@ impl State { /// Add `incr` to the balance of account `a`. pub fn add_balance(&mut self, a: &Address, incr: &U256) { - let old = self.balance(a); + trace!(target: "state", "add_balance({}, {}): {}", a, incr, self.balance(a)); self.require(a, false).add_balance(incr); - trace!("state: add_balance({}, {}): {} -> {}\n", a, incr, old, self.balance(a)); } /// Subtract `decr` from the balance of account `a`. pub fn sub_balance(&mut self, a: &Address, decr: &U256) { - let old = self.balance(a); + trace!(target: "state", "sub_balance({}, {}): {}", a, decr, self.balance(a)); self.require(a, false).sub_balance(decr); - trace!("state: sub_balance({}, {}): {} -> {}\n", a, decr, old, self.balance(a)); } /// Subtracts `by` from the balance of `from` and adds it to that of `to`. @@ -222,7 +220,7 @@ impl State { pub fn apply(&mut self, env_info: &EnvInfo, engine: &Engine, vm_factory: &EvmFactory, t: &SignedTransaction, tracing: bool) -> ApplyResult { // let old = self.to_pod(); - let options = TransactOptions { tracing: tracing, check_nonce: true }; + let options = TransactOptions { tracing: tracing, vm_tracing: false, check_nonce: true }; let e = try!(Executive::new(self, env_info, engine, vm_factory).transact(t, options)); // TODO uncomment once to_pod() works correctly. diff --git a/ethcore/src/trace/executive_tracer.rs b/ethcore/src/trace/executive_tracer.rs index a0bff1c72..a1a13cf43 100644 --- a/ethcore/src/trace/executive_tracer.rs +++ b/ethcore/src/trace/executive_tracer.rs @@ -18,13 +18,13 @@ use util::{Bytes, Address, U256}; use action_params::ActionParams; -use trace::trace::{Trace, Call, Create, Action, Res, CreateResult, CallResult}; -use trace::Tracer; +use trace::trace::{Trace, Call, Create, Action, Res, CreateResult, CallResult, VMTrace, VMOperation, VMExecutedOperation, MemoryDiff, StorageDiff}; +use trace::{Tracer, VMTracer}; /// Simple executive tracer. Traces all calls and creates. Ignores delegatecalls. #[derive(Default)] pub struct ExecutiveTracer { - traces: Vec + traces: Vec, } impl Tracer for ExecutiveTracer { @@ -40,8 +40,7 @@ impl Tracer for ExecutiveTracer { Some(vec![]) } - fn trace_call(&mut self, call: Option, gas_used: U256, output: Option, depth: usize, subs: - Vec, delegate_call: bool) { + fn trace_call(&mut self, call: Option, gas_used: U256, output: Option, depth: usize, subs: Vec, delegate_call: bool) { // don't trace if it's DELEGATECALL or CALLCODE. if delegate_call { return; @@ -106,3 +105,46 @@ impl Tracer for ExecutiveTracer { self.traces } } + +/// Simple VM tracer. Traces all operations. +#[derive(Default)] +pub struct ExecutiveVMTracer { + data: VMTrace, +} + +impl VMTracer for ExecutiveVMTracer { + fn trace_prepare_execute(&mut self, pc: usize, instruction: u8, gas_cost: &U256) -> bool { + self.data.operations.push(VMOperation { + pc: pc, + instruction: instruction, + gas_cost: gas_cost.clone(), + executed: None, + }); + true + } + + fn trace_executed(&mut self, gas_used: U256, stack_push: &[U256], mem_diff: Option<(usize, &[u8])>, store_diff: Option<(U256, U256)>) { + let ex = VMExecutedOperation { + gas_used: gas_used, + stack_push: stack_push.iter().cloned().collect(), + mem_diff: mem_diff.map(|(s, r)| MemoryDiff{ offset: s, data: r.iter().cloned().collect() }), + store_diff: store_diff.map(|(l, v)| StorageDiff{ location: l, value: v }), + }; + self.data.operations.last_mut().expect("trace_executed is always called after a trace_prepare_execute").executed = Some(ex); + } + + fn prepare_subtrace(&self, code: &Bytes) -> Self { + ExecutiveVMTracer { data: VMTrace { + parent_step: self.data.operations.len(), + code: code.clone(), + operations: vec![], + subs: vec![], + }} + } + + fn done_subtrace(&mut self, sub: Self) { + self.data.subs.push(sub.data); + } + + fn drain(mut self) -> Option { self.data.subs.pop() } +} diff --git a/ethcore/src/trace/mod.rs b/ethcore/src/trace/mod.rs index f51cf8ee5..53c062137 100644 --- a/ethcore/src/trace/mod.rs +++ b/ethcore/src/trace/mod.rs @@ -31,9 +31,9 @@ pub use self::block::BlockTraces; pub use self::config::{Config, Switch}; pub use self::db::TraceDB; pub use self::error::Error; -pub use types::trace_types::trace::Trace; -pub use self::noop_tracer::NoopTracer; -pub use self::executive_tracer::ExecutiveTracer; +pub use types::trace_types::trace::{Trace, VMTrace, VMOperation, VMExecutedOperation, MemoryDiff, StorageDiff}; +pub use self::noop_tracer::{NoopTracer, NoopVMTracer}; +pub use self::executive_tracer::{ExecutiveTracer, ExecutiveVMTracer}; pub use types::trace_types::filter::{Filter, AddressesFilter}; pub use self::import::ImportRequest; pub use self::localized::LocalizedTrace; @@ -81,13 +81,32 @@ pub trait Tracer: Send { /// Stores failed create trace. fn trace_failed_create(&mut self, create: Option, depth: usize, subs: Vec); - /// Spawn subracer which will be used to trace deeper levels of execution. + /// Spawn subtracer which will be used to trace deeper levels of execution. fn subtracer(&self) -> Self where Self: Sized; /// Consumes self and returns all traces. fn traces(self) -> Vec; } +/// Used by executive to build VM traces. +pub trait VMTracer: Send { + /// Trace the preparation to execute a single instruction. + /// @returns true if `trace_executed` should be called. + fn trace_prepare_execute(&mut self, _pc: usize, _instruction: u8, _gas_cost: &U256) -> bool { false } + + /// Trace the finalised execution of a single instruction. + fn trace_executed(&mut self, _gas_used: U256, _stack_push: &[U256], _mem_diff: Option<(usize, &[u8])>, _store_diff: Option<(U256, U256)>) {} + + /// Spawn subtracer which will be used to trace deeper levels of execution. + fn prepare_subtrace(&self, code: &Bytes) -> Self where Self: Sized; + + /// Spawn subtracer which will be used to trace deeper levels of execution. + fn done_subtrace(&mut self, sub: Self) where Self: Sized; + + /// Consumes self and returns the VM trace. + fn drain(self) -> Option; +} + /// `DbExtras` provides an interface to query extra data which is not stored in tracesdb, /// but necessary to work correctly. pub trait DatabaseExtras { diff --git a/ethcore/src/trace/noop_tracer.rs b/ethcore/src/trace/noop_tracer.rs index 2581e692b..ed0231b79 100644 --- a/ethcore/src/trace/noop_tracer.rs +++ b/ethcore/src/trace/noop_tracer.rs @@ -18,8 +18,8 @@ use util::{Bytes, Address, U256}; use action_params::ActionParams; -use trace::Tracer; -use trace::trace::{Trace, Call, Create}; +use trace::{Tracer, VMTracer}; +use trace::trace::{Trace, Call, Create, VMTrace}; /// Nonoperative tracer. Does not trace anything. pub struct NoopTracer; @@ -63,3 +63,23 @@ impl Tracer for NoopTracer { vec![] } } + +/// Nonoperative VM tracer. Does not trace anything. +pub struct NoopVMTracer; + +impl VMTracer for NoopVMTracer { + /// Trace the preparation to execute a single instruction. + fn trace_prepare_execute(&mut self, _pc: usize, _instruction: u8, _gas_cost: &U256) -> bool { false } + + /// Trace the finalised execution of a single instruction. + fn trace_executed(&mut self, _gas_used: U256, _stack_push: &[U256], _mem_diff: Option<(usize, &[u8])>, _store_diff: Option<(U256, U256)>) {} + + /// Spawn subtracer which will be used to trace deeper levels of execution. + fn prepare_subtrace(&self, _code: &Bytes) -> Self { NoopVMTracer } + + /// Spawn subtracer which will be used to trace deeper levels of execution. + fn done_subtrace(&mut self, _sub: Self) {} + + /// Consumes self and returns all VM traces. + fn drain(self) -> Option { None } +} diff --git a/ethcore/src/types/executed.rs b/ethcore/src/types/executed.rs index fcde5a392..823c9dda1 100644 --- a/ethcore/src/types/executed.rs +++ b/ethcore/src/types/executed.rs @@ -18,7 +18,7 @@ use util::numbers::*; use util::Bytes; -use trace::Trace; +use trace::{Trace, VMTrace}; use types::log_entry::LogEntry; use ipc::binary::BinaryConvertError; use std::fmt; @@ -59,6 +59,8 @@ pub struct Executed { pub output: Bytes, /// The trace of this transaction. pub trace: Option, + /// The VM trace of this transaction. + pub vm_trace: Option, } /// Result of executing the transaction. diff --git a/ethcore/src/types/trace_types/trace.rs b/ethcore/src/types/trace_types/trace.rs index f8fe07360..243acaf64 100644 --- a/ethcore/src/types/trace_types/trace.rs +++ b/ethcore/src/types/trace_types/trace.rs @@ -349,6 +349,170 @@ impl Trace { } } +#[derive(Debug, Clone, PartialEq, Binary)] +/// A diff of some chunk of memory. +pub struct MemoryDiff { + /// Offset into memory the change begins. + pub offset: usize, + /// The changed data. + pub data: Bytes, +} + +impl Encodable for MemoryDiff { + fn rlp_append(&self, s: &mut RlpStream) { + s.begin_list(2); + s.append(&self.offset); + s.append(&self.data); + } +} + +impl Decodable for MemoryDiff { + fn decode(decoder: &D) -> Result where D: Decoder { + let d = decoder.as_rlp(); + Ok(MemoryDiff { + offset: try!(d.val_at(0)), + data: try!(d.val_at(1)), + }) + } +} + +#[derive(Debug, Clone, PartialEq, Binary)] +/// A diff of some storage value. +pub struct StorageDiff { + /// Which key in storage is changed. + pub location: U256, + /// What the value has been changed to. + pub value: U256, +} + +impl Encodable for StorageDiff { + fn rlp_append(&self, s: &mut RlpStream) { + s.begin_list(2); + s.append(&self.location); + s.append(&self.value); + } +} + +impl Decodable for StorageDiff { + fn decode(decoder: &D) -> Result where D: Decoder { + let d = decoder.as_rlp(); + Ok(StorageDiff { + location: try!(d.val_at(0)), + value: try!(d.val_at(1)), + }) + } +} + +#[derive(Debug, Clone, PartialEq, Binary)] +/// A record of an executed VM operation. +pub struct VMExecutedOperation { + /// The total gas used. + pub gas_used: U256, + /// The stack item placed, if any. + pub stack_push: Vec, + /// If altered, the memory delta. + pub mem_diff: Option, + /// The altered storage value, if any. + pub store_diff: Option, +} + +impl Encodable for VMExecutedOperation { + fn rlp_append(&self, s: &mut RlpStream) { + s.begin_list(4); + s.append(&self.gas_used); + s.append(&self.stack_push); + s.append(&self.mem_diff); + s.append(&self.store_diff); + } +} + +impl Decodable for VMExecutedOperation { + fn decode(decoder: &D) -> Result where D: Decoder { + let d = decoder.as_rlp(); + Ok(VMExecutedOperation { + gas_used: try!(d.val_at(0)), + stack_push: try!(d.val_at(1)), + mem_diff: try!(d.val_at(2)), + store_diff: try!(d.val_at(3)), + }) + } +} + +#[derive(Debug, Clone, PartialEq, Binary)] +/// A record of the execution of a single VM operation. +pub struct VMOperation { + /// The program counter. + pub pc: usize, + /// The instruction executed. + pub instruction: u8, + /// The gas cost for this instruction. + pub gas_cost: U256, + /// Information concerning the execution of the operation. + pub executed: Option, +} + +impl Encodable for VMOperation { + fn rlp_append(&self, s: &mut RlpStream) { + s.begin_list(4); + s.append(&self.pc); + s.append(&self.instruction); + s.append(&self.gas_cost); + s.append(&self.executed); + } +} + +impl Decodable for VMOperation { + fn decode(decoder: &D) -> Result where D: Decoder { + let d = decoder.as_rlp(); + let res = VMOperation { + pc: try!(d.val_at(0)), + instruction: try!(d.val_at(1)), + gas_cost: try!(d.val_at(2)), + executed: try!(d.val_at(3)), + }; + + Ok(res) + } +} + +#[derive(Debug, Clone, PartialEq, Binary, Default)] +/// A record of a full VM trace for a CALL/CREATE. +pub struct VMTrace { + /// The step (i.e. index into operations) at which this trace corresponds. + pub parent_step: usize, + /// The code to be executed. + pub code: Bytes, + /// The operations executed. + pub operations: Vec, + /// The sub traces for each interior action performed as part of this call/create. + /// Thre is a 1:1 correspondance between these and a CALL/CREATE/CALLCODE/DELEGATECALL instruction. + pub subs: Vec, +} + +impl Encodable for VMTrace { + fn rlp_append(&self, s: &mut RlpStream) { + s.begin_list(4); + s.append(&self.parent_step); + s.append(&self.code); + s.append(&self.operations); + s.append(&self.subs); + } +} + +impl Decodable for VMTrace { + fn decode(decoder: &D) -> Result where D: Decoder { + let d = decoder.as_rlp(); + let res = VMTrace { + parent_step: try!(d.val_at(0)), + code: try!(d.val_at(1)), + operations: try!(d.val_at(2)), + subs: try!(d.val_at(3)), + }; + + Ok(res) + } +} + #[cfg(test)] mod tests { use util::{Address, U256, FixedHash}; diff --git a/json/src/state/transaction.rs b/json/src/state/transaction.rs index 15626c224..62b8577d4 100644 --- a/json/src/state/transaction.rs +++ b/json/src/state/transaction.rs @@ -40,7 +40,7 @@ pub struct Transaction { /// To. pub to: MaybeEmpty
, /// Value. - pub value: Uint + pub value: Uint, } #[cfg(test)] diff --git a/parity/rpc_apis.rs b/parity/rpc_apis.rs index d9f0cd4eb..143d6b48f 100644 --- a/parity/rpc_apis.rs +++ b/parity/rpc_apis.rs @@ -153,7 +153,7 @@ pub fn setup_rpc(server: T, deps: Arc, apis: ApiSet } }, Api::Ethcore => { - server.add_delegate(EthcoreClient::new(&deps.miner, deps.logger.clone(), deps.settings.clone()).to_delegate()) + server.add_delegate(EthcoreClient::new(&deps.client, &deps.miner, deps.logger.clone(), deps.settings.clone()).to_delegate()) }, Api::Traces => { server.add_delegate(TracesClient::new(&deps.client).to_delegate()) diff --git a/rpc/src/lib.rs b/rpc/src/lib.rs index ab5069334..b46a13197 100644 --- a/rpc/src/lib.rs +++ b/rpc/src/lib.rs @@ -26,6 +26,7 @@ extern crate serde; extern crate serde_json; extern crate jsonrpc_core; extern crate jsonrpc_http_server; +#[macro_use] extern crate ethcore_util as util; extern crate ethcore; extern crate ethsync; diff --git a/rpc/src/v1/impls/eth.rs b/rpc/src/v1/impls/eth.rs index 83a788599..49cbafe27 100644 --- a/rpc/src/v1/impls/eth.rs +++ b/rpc/src/v1/impls/eth.rs @@ -511,8 +511,8 @@ impl Eth for EthClient where .and_then(|(request, block_number,)| { let signed = try!(self.sign_call(request)); let r = match block_number { - BlockNumber::Pending => take_weak!(self.miner).call(take_weak!(self.client).deref(), &signed), - BlockNumber::Latest => take_weak!(self.client).call(&signed), + BlockNumber::Pending => take_weak!(self.miner).call(take_weak!(self.client).deref(), &signed, false), + BlockNumber::Latest => take_weak!(self.client).call(&signed, false), _ => panic!("{:?}", block_number), }; to_value(&r.map(|e| Bytes(e.output)).unwrap_or(Bytes::new(vec![]))) @@ -524,8 +524,8 @@ impl Eth for EthClient where .and_then(|(request, block_number,)| { let signed = try!(self.sign_call(request)); let r = match block_number { - BlockNumber::Pending => take_weak!(self.miner).call(take_weak!(self.client).deref(), &signed), - BlockNumber::Latest => take_weak!(self.client).call(&signed), + BlockNumber::Pending => take_weak!(self.miner).call(take_weak!(self.client).deref(), &signed, false), + BlockNumber::Latest => take_weak!(self.client).call(&signed, false), _ => return Err(Error::invalid_params()), }; to_value(&r.map(|res| res.gas_used + res.refunded).unwrap_or(From::from(0))) diff --git a/rpc/src/v1/impls/ethcore.rs b/rpc/src/v1/impls/ethcore.rs index e649e37f4..ca14ca021 100644 --- a/rpc/src/v1/impls/ethcore.rs +++ b/rpc/src/v1/impls/ethcore.rs @@ -15,7 +15,7 @@ // along with Parity. If not, see . //! Ethcore-specific rpc implementation. -use util::{U256, Address, RotatingLogger}; +use util::{U256, Address, RotatingLogger, FixedHash, Uint}; use util::network_settings::NetworkSettings; use util::misc::version_data; use std::sync::{Arc, Weak}; @@ -23,29 +23,99 @@ use std::ops::Deref; use std::collections::BTreeMap; use jsonrpc_core::*; use ethcore::miner::MinerService; +use ethcore::transaction::{Transaction as EthTransaction, SignedTransaction, Action}; +use ethcore::client::BlockChainClient; +use ethcore::trace::VMTrace; use v1::traits::Ethcore; -use v1::types::Bytes; +use v1::types::{Bytes, CallRequest}; /// Ethcore implementation. -pub struct EthcoreClient - where M: MinerService { +pub struct EthcoreClient where + C: BlockChainClient, + M: MinerService { + + client: Weak, miner: Weak, logger: Arc, settings: Arc, } -impl EthcoreClient where M: MinerService { +impl EthcoreClient where C: BlockChainClient, M: MinerService { /// Creates new `EthcoreClient`. - pub fn new(miner: &Arc, logger: Arc, settings: Arc) -> Self { + pub fn new(client: &Arc, miner: &Arc, logger: Arc, settings: Arc) -> Self { EthcoreClient { + client: Arc::downgrade(client), miner: Arc::downgrade(miner), logger: logger, settings: settings, } } + + // TODO: share with eth.rs + fn sign_call(&self, request: CallRequest) -> Result { + let client = take_weak!(self.client); + let miner = take_weak!(self.miner); + let from = request.from.unwrap_or(Address::zero()); + Ok(EthTransaction { + nonce: request.nonce.unwrap_or_else(|| client.latest_nonce(&from)), + action: request.to.map_or(Action::Create, Action::Call), + gas: request.gas.unwrap_or(U256::from(50_000_000)), + gas_price: request.gas_price.unwrap_or_else(|| miner.sensible_gas_price()), + value: request.value.unwrap_or_else(U256::zero), + data: request.data.map_or_else(Vec::new, |d| d.to_vec()) + }.fake_sign(from)) + } } -impl Ethcore for EthcoreClient where M: MinerService + 'static { +fn vm_trace_to_object(t: &VMTrace) -> Value { + let mut ret = BTreeMap::new(); + ret.insert("code".to_owned(), to_value(&t.code).unwrap()); + + let mut subs = t.subs.iter(); + let mut next_sub = subs.next(); + + let ops = t.operations + .iter() + .enumerate() + .map(|(i, op)| { + let mut m = map![ + "pc".to_owned() => to_value(&op.pc).unwrap(), + "cost".to_owned() => match op.gas_cost <= U256::from(!0u64) { + true => to_value(&op.gas_cost.low_u64()), + false => to_value(&op.gas_cost), + }.unwrap() + ]; + if let Some(ref ex) = op.executed { + let mut em = map![ + "used".to_owned() => to_value(&ex.gas_used.low_u64()).unwrap(), + "push".to_owned() => to_value(&ex.stack_push).unwrap() + ]; + if let Some(ref md) = ex.mem_diff { + em.insert("mem".to_owned(), Value::Object(map![ + "off".to_owned() => to_value(&md.offset).unwrap(), + "data".to_owned() => to_value(&md.data).unwrap() + ])); + } + if let Some(ref sd) = ex.store_diff { + em.insert("store".to_owned(), Value::Object(map![ + "key".to_owned() => to_value(&sd.location).unwrap(), + "val".to_owned() => to_value(&sd.value).unwrap() + ])); + } + m.insert("ex".to_owned(), Value::Object(em)); + } + if next_sub.is_some() && next_sub.unwrap().parent_step == i { + m.insert("sub".to_owned(), vm_trace_to_object(next_sub.unwrap())); + next_sub = subs.next(); + } + Value::Object(m) + }) + .collect::>(); + ret.insert("ops".to_owned(), Value::Array(ops)); + Value::Object(ret) +} + +impl Ethcore for EthcoreClient where C: BlockChainClient + 'static, M: MinerService + 'static { fn set_min_gas_price(&self, params: Params) -> Result { from_params::<(U256,)>(params).and_then(|(gas_price,)| { @@ -135,4 +205,19 @@ impl Ethcore for EthcoreClient where M: MinerService + 'static { let version = version_data(); to_value(&Bytes::new(version)) } + + fn vm_trace_call(&self, params: Params) -> Result { + trace!(target: "jsonrpc", "vm_trace_call: {:?}", params); + from_params(params) + .and_then(|(request,)| { + let signed = try!(self.sign_call(request)); + let r = take_weak!(self.client).call(&signed, true); + if let Ok(executed) = r { + if let Some(vm_trace) = executed.vm_trace { + return Ok(vm_trace_to_object(&vm_trace)); + } + } + Ok(Value::Null) + }) + } } diff --git a/rpc/src/v1/tests/eth.rs b/rpc/src/v1/tests/eth.rs index 93887fb63..a7f7920ad 100644 --- a/rpc/src/v1/tests/eth.rs +++ b/rpc/src/v1/tests/eth.rs @@ -19,13 +19,12 @@ use std::collections::HashMap; use std::sync::Arc; use std::str::FromStr; -use ethcore::client::{MiningBlockChainClient, BlockChainClient, Client, ClientConfig}; +use ethcore::client::{BlockChainClient, Client, ClientConfig}; use ethcore::ids::BlockID; use ethcore::spec::{Genesis, Spec}; use ethcore::block::Block; use ethcore::views::BlockView; use ethcore::ethereum; -use ethcore::transaction::{Transaction, Action}; use ethcore::miner::{MinerService, ExternalMiner, Miner}; use devtools::RandomTempPath; use util::Hashable; diff --git a/rpc/src/v1/tests/helpers/miner_service.rs b/rpc/src/v1/tests/helpers/miner_service.rs index a53ca3a08..0f9327887 100644 --- a/rpc/src/v1/tests/helpers/miner_service.rs +++ b/rpc/src/v1/tests/helpers/miner_service.rs @@ -202,7 +202,7 @@ impl MinerService for TestMinerService { self.latest_closed_block.lock().unwrap().as_ref().map_or_else(U256::zero, |b| b.block().fields().state.balance(address).clone()) } - fn call(&self, _chain: &MiningBlockChainClient, _t: &SignedTransaction) -> Result { + fn call(&self, _chain: &MiningBlockChainClient, _t: &SignedTransaction, _vm_tracing: bool) -> Result { unimplemented!(); } diff --git a/rpc/src/v1/tests/mocked/eth.rs b/rpc/src/v1/tests/mocked/eth.rs index 63e66f797..4f43be827 100644 --- a/rpc/src/v1/tests/mocked/eth.rs +++ b/rpc/src/v1/tests/mocked/eth.rs @@ -365,7 +365,7 @@ fn rpc_eth_pending_transaction_by_hash() { tester.miner.pending_transactions.lock().unwrap().insert(H256::zero(), tx); } - let response = r#"{"jsonrpc":"2.0","result":{"blockHash":null,"blockNumber":null,"from":"0x0f65fe9276bc9a24ae7083ae28e2660ef72df99e","gas":"0x5208","gasPrice":"0x01","hash":"0x41df922fd0d4766fcc02e161f8295ec28522f329ae487f14d811e4b64c8d6e31","input":"0x","nonce":"0x00","to":"0x095e7baea6a6c7c4c2dfeb977efac326af552d87","transactionIndex":null,"value":"0x0a"},"id":1}"#; + let response = r#"{"jsonrpc":"2.0","result":{"blockHash":null,"blockNumber":null,"creates":null,"from":"0x0f65fe9276bc9a24ae7083ae28e2660ef72df99e","gas":"0x5208","gasPrice":"0x01","hash":"0x41df922fd0d4766fcc02e161f8295ec28522f329ae487f14d811e4b64c8d6e31","input":"0x","nonce":"0x00","to":"0x095e7baea6a6c7c4c2dfeb977efac326af552d87","transactionIndex":null,"value":"0x0a"},"id":1}"#; let request = r#"{ "jsonrpc": "2.0", "method": "eth_getTransactionByHash", @@ -430,6 +430,7 @@ fn rpc_eth_call() { contracts_created: vec![], output: vec![0x12, 0x34, 0xff], trace: None, + vm_trace: None, }); let request = r#"{ @@ -463,6 +464,7 @@ fn rpc_eth_call_default_block() { contracts_created: vec![], output: vec![0x12, 0x34, 0xff], trace: None, + vm_trace: None, }); let request = r#"{ @@ -495,6 +497,7 @@ fn rpc_eth_estimate_gas() { contracts_created: vec![], output: vec![0x12, 0x34, 0xff], trace: None, + vm_trace: None, }); let request = r#"{ @@ -528,6 +531,7 @@ fn rpc_eth_estimate_gas_default_block() { contracts_created: vec![], output: vec![0x12, 0x34, 0xff], trace: None, + vm_trace: None, }); let request = r#"{ diff --git a/rpc/src/v1/tests/mocked/ethcore.rs b/rpc/src/v1/tests/mocked/ethcore.rs index d51545d86..3bed1f0f0 100644 --- a/rpc/src/v1/tests/mocked/ethcore.rs +++ b/rpc/src/v1/tests/mocked/ethcore.rs @@ -18,6 +18,7 @@ use std::sync::Arc; use std::str::FromStr; use jsonrpc_core::IoHandler; use v1::{Ethcore, EthcoreClient}; +use ethcore::client::{TestBlockChainClient}; use ethcore::miner::MinerService; use v1::tests::helpers::TestMinerService; use util::numbers::*; @@ -25,10 +26,15 @@ use rustc_serialize::hex::FromHex; use util::log::RotatingLogger; use util::network_settings::NetworkSettings; +fn blockchain_client() -> Arc { + let client = TestBlockChainClient::new(); + Arc::new(client) +} fn miner_service() -> Arc { Arc::new(TestMinerService::default()) } + fn logger() -> Arc { Arc::new(RotatingLogger::new("rpc=trace".to_owned())) } @@ -45,14 +51,15 @@ fn settings() -> Arc { }) } -fn ethcore_client(miner: &Arc) -> EthcoreClient { - EthcoreClient::new(&miner, logger(), settings()) +fn ethcore_client(client: &Arc, miner: &Arc) -> EthcoreClient { + EthcoreClient::new(&client, &miner, logger(), settings()) } #[test] fn rpc_ethcore_extra_data() { let miner = miner_service(); - let ethcore = ethcore_client(&miner).to_delegate(); + let client = blockchain_client(); + let ethcore = ethcore_client(&client, &miner).to_delegate(); let io = IoHandler::new(); io.add_delegate(ethcore); @@ -68,7 +75,8 @@ fn rpc_ethcore_default_extra_data() { use util::ToPretty; let miner = miner_service(); - let ethcore = ethcore_client(&miner).to_delegate(); + let client = blockchain_client(); + let ethcore = ethcore_client(&client, &miner).to_delegate(); let io = IoHandler::new(); io.add_delegate(ethcore); @@ -81,7 +89,8 @@ fn rpc_ethcore_default_extra_data() { #[test] fn rpc_ethcore_gas_floor_target() { let miner = miner_service(); - let ethcore = ethcore_client(&miner).to_delegate(); + let client = blockchain_client(); + let ethcore = ethcore_client(&client, &miner).to_delegate(); let io = IoHandler::new(); io.add_delegate(ethcore); @@ -94,7 +103,8 @@ fn rpc_ethcore_gas_floor_target() { #[test] fn rpc_ethcore_min_gas_price() { let miner = miner_service(); - let ethcore = ethcore_client(&miner).to_delegate(); + let client = blockchain_client(); + let ethcore = ethcore_client(&client, &miner).to_delegate(); let io = IoHandler::new(); io.add_delegate(ethcore); @@ -107,7 +117,8 @@ fn rpc_ethcore_min_gas_price() { #[test] fn rpc_ethcore_set_min_gas_price() { let miner = miner_service(); - let ethcore = ethcore_client(&miner).to_delegate(); + let client = blockchain_client(); + let ethcore = ethcore_client(&client, &miner).to_delegate(); let io = IoHandler::new(); io.add_delegate(ethcore); @@ -121,7 +132,8 @@ fn rpc_ethcore_set_min_gas_price() { #[test] fn rpc_ethcore_set_gas_floor_target() { let miner = miner_service(); - let ethcore = ethcore_client(&miner).to_delegate(); + let client = blockchain_client(); + let ethcore = ethcore_client(&client, &miner).to_delegate(); let io = IoHandler::new(); io.add_delegate(ethcore); @@ -135,7 +147,8 @@ fn rpc_ethcore_set_gas_floor_target() { #[test] fn rpc_ethcore_set_extra_data() { let miner = miner_service(); - let ethcore = ethcore_client(&miner).to_delegate(); + let client = blockchain_client(); + let ethcore = ethcore_client(&client, &miner).to_delegate(); let io = IoHandler::new(); io.add_delegate(ethcore); @@ -149,7 +162,8 @@ fn rpc_ethcore_set_extra_data() { #[test] fn rpc_ethcore_set_author() { let miner = miner_service(); - let ethcore = ethcore_client(&miner).to_delegate(); + let client = blockchain_client(); + let ethcore = ethcore_client(&client, &miner).to_delegate(); let io = IoHandler::new(); io.add_delegate(ethcore); @@ -163,10 +177,11 @@ fn rpc_ethcore_set_author() { #[test] fn rpc_ethcore_dev_logs() { let miner = miner_service(); + let client = blockchain_client(); let logger = logger(); logger.append("a".to_owned()); logger.append("b".to_owned()); - let ethcore = EthcoreClient::new(&miner, logger.clone(), settings()).to_delegate(); + let ethcore = EthcoreClient::new(&client, &miner, logger.clone(), settings()).to_delegate(); let io = IoHandler::new(); io.add_delegate(ethcore); @@ -179,7 +194,8 @@ fn rpc_ethcore_dev_logs() { #[test] fn rpc_ethcore_dev_logs_levels() { let miner = miner_service(); - let ethcore = ethcore_client(&miner).to_delegate(); + let client = blockchain_client(); + let ethcore = ethcore_client(&client, &miner).to_delegate(); let io = IoHandler::new(); io.add_delegate(ethcore); @@ -191,7 +207,8 @@ fn rpc_ethcore_dev_logs_levels() { #[test] fn rpc_ethcore_set_transactions_limit() { let miner = miner_service(); - let ethcore = ethcore_client(&miner).to_delegate(); + let client = blockchain_client(); + let ethcore = ethcore_client(&client, &miner).to_delegate(); let io = IoHandler::new(); io.add_delegate(ethcore); @@ -205,7 +222,8 @@ fn rpc_ethcore_set_transactions_limit() { #[test] fn rpc_ethcore_transactions_limit() { let miner = miner_service(); - let ethcore = ethcore_client(&miner).to_delegate(); + let client = blockchain_client(); + let ethcore = ethcore_client(&client, &miner).to_delegate(); let io = IoHandler::new(); io.add_delegate(ethcore); @@ -218,7 +236,8 @@ fn rpc_ethcore_transactions_limit() { #[test] fn rpc_ethcore_net_chain() { let miner = miner_service(); - let ethcore = ethcore_client(&miner).to_delegate(); + let client = blockchain_client(); + let ethcore = ethcore_client(&client, &miner).to_delegate(); let io = IoHandler::new(); io.add_delegate(ethcore); @@ -231,7 +250,8 @@ fn rpc_ethcore_net_chain() { #[test] fn rpc_ethcore_net_max_peers() { let miner = miner_service(); - let ethcore = ethcore_client(&miner).to_delegate(); + let client = blockchain_client(); + let ethcore = ethcore_client(&client, &miner).to_delegate(); let io = IoHandler::new(); io.add_delegate(ethcore); @@ -244,7 +264,8 @@ fn rpc_ethcore_net_max_peers() { #[test] fn rpc_ethcore_net_port() { let miner = miner_service(); - let ethcore = ethcore_client(&miner).to_delegate(); + let client = blockchain_client(); + let ethcore = ethcore_client(&client, &miner).to_delegate(); let io = IoHandler::new(); io.add_delegate(ethcore); @@ -257,7 +278,8 @@ fn rpc_ethcore_net_port() { #[test] fn rpc_ethcore_rpc_settings() { let miner = miner_service(); - let ethcore = ethcore_client(&miner).to_delegate(); + let client = blockchain_client(); + let ethcore = ethcore_client(&client, &miner).to_delegate(); let io = IoHandler::new(); io.add_delegate(ethcore); @@ -270,7 +292,8 @@ fn rpc_ethcore_rpc_settings() { #[test] fn rpc_ethcore_node_name() { let miner = miner_service(); - let ethcore = ethcore_client(&miner).to_delegate(); + let client = blockchain_client(); + let ethcore = ethcore_client(&client, &miner).to_delegate(); let io = IoHandler::new(); io.add_delegate(ethcore); diff --git a/rpc/src/v1/traits/ethcore.rs b/rpc/src/v1/traits/ethcore.rs index 3646f6c5a..abc87bbb8 100644 --- a/rpc/src/v1/traits/ethcore.rs +++ b/rpc/src/v1/traits/ethcore.rs @@ -72,6 +72,8 @@ pub trait Ethcore: Sized + Send + Sync + 'static { /// Returns default extra data fn default_extra_data(&self, _: Params) -> Result; + /// Executes the given call and returns the VM trace for it. + fn vm_trace_call(&self, _: Params) -> Result; /// Should be used to convert object to io delegate. fn to_delegate(self) -> IoDelegate { @@ -95,6 +97,8 @@ pub trait Ethcore: Sized + Send + Sync + 'static { delegate.add_method("ethcore_nodeName", Ethcore::node_name); delegate.add_method("ethcore_defaultExtraData", Ethcore::default_extra_data); + delegate.add_method("ethcore_vmTraceCall", Ethcore::vm_trace_call); + delegate } } diff --git a/rpc/src/v1/types/block.rs b/rpc/src/v1/types/block.rs index 5810c85e5..b86723357 100644 --- a/rpc/src/v1/types/block.rs +++ b/rpc/src/v1/types/block.rs @@ -103,7 +103,7 @@ mod tests { fn test_serialize_block_transactions() { let t = BlockTransactions::Full(vec![Transaction::default()]); let serialized = serde_json::to_string(&t).unwrap(); - assert_eq!(serialized, r#"[{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x00","blockHash":null,"blockNumber":null,"transactionIndex":null,"from":"0x0000000000000000000000000000000000000000","to":null,"value":"0x00","gasPrice":"0x00","gas":"0x00","input":"0x"}]"#); + assert_eq!(serialized, r#"[{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x00","blockHash":null,"blockNumber":null,"transactionIndex":null,"from":"0x0000000000000000000000000000000000000000","to":null,"value":"0x00","gasPrice":"0x00","gas":"0x00","input":"0x","creates":null}]"#); let t = BlockTransactions::Hashes(vec![H256::default()]); let serialized = serde_json::to_string(&t).unwrap(); diff --git a/rpc/src/v1/types/transaction.rs b/rpc/src/v1/types/transaction.rs index 1c9a41084..6a9f0e590 100644 --- a/rpc/src/v1/types/transaction.rs +++ b/rpc/src/v1/types/transaction.rs @@ -15,6 +15,7 @@ // along with Parity. If not, see . use util::numbers::*; +use ethcore::contract_address; use ethcore::transaction::{LocalizedTransaction, Action, SignedTransaction}; use v1::types::{Bytes, OptionalValue}; @@ -46,7 +47,9 @@ pub struct Transaction { /// Gas pub gas: U256, /// Data - pub input: Bytes + pub input: Bytes, + /// Creates contract + pub creates: OptionalValue
, } impl From for Transaction { @@ -65,7 +68,11 @@ impl From for Transaction { value: t.value, gas_price: t.gas_price, gas: t.gas, - input: Bytes::new(t.data.clone()) + input: Bytes::new(t.data.clone()), + creates: match t.action { + Action::Create => OptionalValue::Value(contract_address(&t.sender().unwrap(), &t.nonce)), + Action::Call(_) => OptionalValue::Null, + }, } } } @@ -86,7 +93,11 @@ impl From for Transaction { value: t.value, gas_price: t.gas_price, gas: t.gas, - input: Bytes::new(t.data.clone()) + input: Bytes::new(t.data.clone()), + creates: match t.action { + Action::Create => OptionalValue::Value(contract_address(&t.sender().unwrap(), &t.nonce)), + Action::Call(_) => OptionalValue::Null, + }, } } } @@ -100,7 +111,7 @@ mod tests { fn test_transaction_serialize() { let t = Transaction::default(); let serialized = serde_json::to_string(&t).unwrap(); - assert_eq!(serialized, r#"{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x00","blockHash":null,"blockNumber":null,"transactionIndex":null,"from":"0x0000000000000000000000000000000000000000","to":null,"value":"0x00","gasPrice":"0x00","gas":"0x00","input":"0x"}"#); + assert_eq!(serialized, r#"{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x00","blockHash":null,"blockNumber":null,"transactionIndex":null,"from":"0x0000000000000000000000000000000000000000","to":null,"value":"0x00","gasPrice":"0x00","gas":"0x00","input":"0x","creates":null}"#); } } diff --git a/util/src/common.rs b/util/src/common.rs index 0e0cd7757..941b5b0a6 100644 --- a/util/src/common.rs +++ b/util/src/common.rs @@ -24,6 +24,13 @@ pub use vector::*; pub use numbers::*; pub use sha3::*; +#[macro_export] +macro_rules! vec_into { + ( $( $x:expr ),* ) => { + vec![ $( $x.into() ),* ] + } +} + #[macro_export] macro_rules! hash_map { () => { HashMap::new() }; diff --git a/util/src/rlp/rlpstream.rs b/util/src/rlp/rlpstream.rs index c2ff88c41..5da3a3822 100644 --- a/util/src/rlp/rlpstream.rs +++ b/util/src/rlp/rlpstream.rs @@ -338,6 +338,18 @@ impl Encodable for Vec where T: Encodable { } } +impl Encodable for Option where T: Encodable { + fn rlp_append(&self, s: &mut RlpStream) { + match *self { + None => { s.begin_list(0); }, + Some(ref x) => { + s.begin_list(1); + s.append_internal(x); + } + } + } +} + impl RlpEncodable for T where T: Encodable { fn rlp_append(&self, s: &mut RlpStream) { Encodable::rlp_append(self, s) diff --git a/util/src/rlp/untrusted_rlp.rs b/util/src/rlp/untrusted_rlp.rs index 1aa688aba..6109a643b 100644 --- a/util/src/rlp/untrusted_rlp.rs +++ b/util/src/rlp/untrusted_rlp.rs @@ -395,7 +395,7 @@ impl<'a> Decoder for BasicDecoder<'a> { } impl Decodable for T where T: FromBytes { - fn decode(decoder: &D) -> Result where D: Decoder { + fn decode(decoder: &D) -> Result where D: Decoder { decoder.read_value(| bytes | { Ok(try!(T::from_bytes(bytes))) }) @@ -403,13 +403,19 @@ impl Decodable for T where T: FromBytes { } impl Decodable for Vec where T: Decodable { - fn decode(decoder: &D) -> Result where D: Decoder { + fn decode(decoder: &D) -> Result where D: Decoder { decoder.as_rlp().iter().map(|d| T::decode(&BasicDecoder::new(d))).collect() } } +impl Decodable for Option where T: Decodable { + fn decode(decoder: &D) -> Result where D: Decoder { + decoder.as_rlp().iter().map(|d| T::decode(&BasicDecoder::new(d))).collect::, DecoderError>>().map(|mut a| a.pop()) + } +} + impl Decodable for Vec { - fn decode(decoder: &D) -> Result where D: Decoder { + fn decode(decoder: &D) -> Result where D: Decoder { decoder.read_value(| bytes | { let mut res = vec![]; res.extend_from_slice(bytes); @@ -418,22 +424,10 @@ impl Decodable for Vec { } } -impl Decodable for Option where T: Decodable { - fn decode(decoder: &D) -> Result where D: Decoder { - decoder.read_value(| bytes | { - let res = match bytes.len() { - 0 => None, - _ => Some(try!(T::decode(decoder))) - }; - Ok(res) - }) - } -} - macro_rules! impl_array_decodable { ($index_type:ty, $len:expr ) => ( impl Decodable for [T; $len] where T: Decodable { - fn decode(decoder: &D) -> Result where D: Decoder { + fn decode(decoder: &D) -> Result where D: Decoder { let decoders = decoder.as_rlp(); let mut result: [T; $len] = unsafe { ::std::mem::uninitialized() }; @@ -466,7 +460,7 @@ impl_array_decodable_recursive!( ); impl RlpDecodable for T where T: Decodable { - fn decode(decoder: &D) -> Result where D: Decoder { + fn decode(decoder: &D) -> Result where D: Decoder { Decodable::decode(decoder) } } @@ -489,7 +483,7 @@ impl FromBytes for DecodableU8 { } impl RlpDecodable for u8 { - fn decode(decoder: &D) -> Result where D: Decoder { + fn decode(decoder: &D) -> Result where D: Decoder { let u: DecodableU8 = try!(Decodable::decode(decoder)); Ok(u.0) }