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 ac06ad34b..ec98d341e 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) }