From 1e9aebbc86c079d23488d0c91b46491f78ae2c8a Mon Sep 17 00:00:00 2001 From: Wei Tang Date: Tue, 2 Oct 2018 22:33:19 +0800 Subject: [PATCH] Resumable EVM and heap-allocated callstack (#9360) * Add new Vm trappable interface * Exec/Resume interface * Basic implementation of CallCreateExecutive * Implement resume_call and resume_create for executive * Move convertion to call/create result to separate function * Implement consume that converts resumable to non-resumable * Use consume for Executive::call/create * Resumable EVM * Implement tracing mode without needing subtracers * Implement vmtracer so it doesn't require extra structs for subtracing * Use the new tracing mode in executive * Fix most of the linting errors for cargo build * Add the concept of stack_depth * Add back crossbeam * Fix some test compile * Fix prefix address test * Fix evm crate tests * Fix wasm crate test compile * Fix wasm runner compile * Fix jsontests compile * Fix evmbin compile * Fix an issue with create nonce and better vm tracing interface * Fix linting * Fix evmbin compile * Fix unconfirmed_substate and static_flag * Fix an issue in create address logic * Fix top-level tracing * Handle builtin tracing * Fix suicide and reward tracing index stack * Fix an issue where trap conflicts with tracing * Fix an issue in parent step vm tracing * Fix revert tracing * Fix evmbin tests * Remove params clone * Fix TODO proofs * Fix jsontests compile * Fix evmbin merge issue * Fix wasm merge issue * Fix wasm test * Fix ethcore merge warnings * Fix evmbin compile * Better expect messages and add some trace::skip_one asserts --- ethcore/evm/src/evm.rs | 2 +- ethcore/evm/src/factory.rs | 4 +- ethcore/evm/src/interpreter/mod.rs | 205 ++++-- ethcore/evm/src/tests.rs | 72 +- ethcore/src/executive.rs | 970 +++++++++++++++++++------- ethcore/src/externalities.rs | 103 +-- ethcore/src/factory.rs | 4 +- ethcore/src/json_tests/executive.rs | 30 +- ethcore/src/trace/db.rs | 5 +- ethcore/src/trace/executive_tracer.rs | 326 +++++---- ethcore/src/trace/mod.rs | 62 +- ethcore/src/trace/noop_tracer.rs | 61 +- ethcore/src/trace/types/flat.rs | 3 +- ethcore/vm/src/error.rs | 25 +- ethcore/vm/src/ext.rs | 25 +- ethcore/vm/src/lib.rs | 18 +- ethcore/vm/src/tests.rs | 37 +- ethcore/wasm/run/src/runner.rs | 12 +- ethcore/wasm/src/lib.rs | 11 +- ethcore/wasm/src/runtime.rs | 5 +- ethcore/wasm/src/tests.rs | 58 +- evmbin/src/display/json.rs | 138 ++-- evmbin/src/display/simple.rs | 4 +- evmbin/src/display/std_json.rs | 89 ++- 24 files changed, 1465 insertions(+), 804 deletions(-) diff --git a/ethcore/evm/src/evm.rs b/ethcore/evm/src/evm.rs index 08b4b0981..29f1b294b 100644 --- a/ethcore/evm/src/evm.rs +++ b/ethcore/evm/src/evm.rs @@ -62,7 +62,7 @@ impl Finalize for Error { } /// Cost calculation type. For low-gas usage we calculate costs using usize instead of U256 -pub trait CostType: Sized + From + Copy +pub trait CostType: Sized + From + Copy + Send + ops::Mul + ops::Div + ops::Add +ops::Sub + ops::Shr + ops::Shl + cmp::Ord + fmt::Debug { diff --git a/ethcore/evm/src/factory.rs b/ethcore/evm/src/factory.rs index 84e01460d..8189c3a29 100644 --- a/ethcore/evm/src/factory.rs +++ b/ethcore/evm/src/factory.rs @@ -17,7 +17,7 @@ //! Evm factory. //! use std::sync::Arc; -use vm::{Vm, Schedule}; +use vm::{Exec, Schedule}; use ethereum_types::U256; use super::vm::ActionParams; use super::interpreter::SharedCache; @@ -33,7 +33,7 @@ pub struct Factory { impl Factory { /// Create fresh instance of VM /// Might choose implementation depending on supplied gas. - pub fn create(&self, params: ActionParams, schedule: &Schedule, depth: usize) -> Box { + pub fn create(&self, params: ActionParams, schedule: &Schedule, depth: usize) -> Box { match self.evm { VMType::Interpreter => if Self::can_fit_in_usize(¶ms.gas) { Box::new(super::interpreter::Interpreter::::new(params, self.evm_cache.clone(), schedule, depth)) diff --git a/ethcore/evm/src/interpreter/mod.rs b/ethcore/evm/src/interpreter/mod.rs index 13784873f..7da3d2a1a 100644 --- a/ethcore/evm/src/interpreter/mod.rs +++ b/ethcore/evm/src/interpreter/mod.rs @@ -32,7 +32,8 @@ use ethereum_types::{U256, U512, H256, Address}; use vm::{ self, ActionParams, ParamsType, ActionValue, CallType, MessageCallResult, - ContractCreateResult, CreateContractAddress, ReturnData, GasLeft, Schedule + ContractCreateResult, CreateContractAddress, ReturnData, GasLeft, Schedule, + TrapKind, TrapError }; use evm::CostType; @@ -103,6 +104,7 @@ enum InstructionResult { apply: bool, }, StopExecution, + Trap(TrapKind), } enum Never {} @@ -161,6 +163,7 @@ pub enum InterpreterResult { Done(vm::Result), /// The VM can continue to run. Continue, + Trap(TrapKind), } impl From for InterpreterResult { @@ -182,22 +185,89 @@ pub struct Interpreter { valid_jump_destinations: Option>, gasometer: Option>, stack: VecStack, + resume_output_range: Option<(U256, U256)>, + resume_result: Option>, + last_stack_ret_len: usize, _type: PhantomData, } -impl vm::Vm for Interpreter { - fn exec(&mut self, ext: &mut vm::Ext) -> vm::Result { +impl vm::Exec for Interpreter { + fn exec(mut self: Box, ext: &mut vm::Ext) -> vm::ExecTrapResult { loop { let result = self.step(ext); match result { InterpreterResult::Continue => {}, - InterpreterResult::Done(value) => return value, + InterpreterResult::Done(value) => return Ok(value), + InterpreterResult::Trap(trap) => match trap { + TrapKind::Call(params) => { + return Err(TrapError::Call(params, self)); + }, + TrapKind::Create(params, address) => { + return Err(TrapError::Create(params, address, self)); + }, + }, InterpreterResult::Stopped => panic!("Attempted to execute an already stopped VM.") } } } } +impl vm::ResumeCall for Interpreter { + fn resume_call(mut self: Box, result: MessageCallResult) -> Box { + { + let this = &mut *self; + let (out_off, out_size) = this.resume_output_range.take().expect("Box is obtained from a call opcode; resume_output_range is always set after those opcodes are executed; qed"); + + match result { + MessageCallResult::Success(gas_left, data) => { + let output = this.mem.writeable_slice(out_off, out_size); + let len = cmp::min(output.len(), data.len()); + (&mut output[..len]).copy_from_slice(&data[..len]); + + this.return_data = data; + this.stack.push(U256::one()); + this.resume_result = Some(InstructionResult::UnusedGas(Cost::from_u256(gas_left).expect("Gas left cannot be greater than current one"))); + }, + MessageCallResult::Reverted(gas_left, data) => { + let output = this.mem.writeable_slice(out_off, out_size); + let len = cmp::min(output.len(), data.len()); + (&mut output[..len]).copy_from_slice(&data[..len]); + + this.return_data = data; + this.stack.push(U256::zero()); + this.resume_result = Some(InstructionResult::UnusedGas(Cost::from_u256(gas_left).expect("Gas left cannot be greater than current one"))); + }, + MessageCallResult::Failed => { + this.stack.push(U256::zero()); + this.resume_result = Some(InstructionResult::Ok); + }, + } + } + self + } +} + +impl vm::ResumeCreate for Interpreter { + fn resume_create(mut self: Box, result: ContractCreateResult) -> Box { + match result { + ContractCreateResult::Created(address, gas_left) => { + self.stack.push(address_to_u256(address)); + self.resume_result = Some(InstructionResult::UnusedGas(Cost::from_u256(gas_left).expect("Gas left cannot be greater."))); + }, + ContractCreateResult::Reverted(gas_left, return_data) => { + self.stack.push(U256::zero()); + self.return_data = return_data; + self.resume_result = Some(InstructionResult::UnusedGas(Cost::from_u256(gas_left).expect("Gas left cannot be greater."))); + }, + ContractCreateResult::Failed => { + self.stack.push(U256::zero()); + self.resume_result = Some(InstructionResult::Ok); + }, + } + self + } +} + impl Interpreter { /// Create a new `Interpreter` instance with shared cache. pub fn new(mut params: ActionParams, cache: Arc, schedule: &Schedule, depth: usize) -> Interpreter { @@ -215,6 +285,9 @@ impl Interpreter { do_trace: true, mem: Vec::new(), return_data: ReturnData::empty(), + last_stack_ret_len: 0, + resume_output_range: None, + resume_result: None, _type: PhantomData, } } @@ -244,51 +317,58 @@ impl Interpreter { /// Inner helper function for step. #[inline(always)] fn step_inner(&mut self, ext: &mut vm::Ext) -> Result { - let opcode = self.reader.code[self.reader.position]; - let instruction = Instruction::from_u8(opcode); - self.reader.position += 1; + let result = match self.resume_result.take() { + Some(result) => result, + None => { + let opcode = self.reader.code[self.reader.position]; + let instruction = Instruction::from_u8(opcode); + self.reader.position += 1; - // TODO: make compile-time removable if too much of a performance hit. - self.do_trace = self.do_trace && ext.trace_next_instruction( - self.reader.position - 1, opcode, self.gasometer.as_mut().expect(GASOMETER_PROOF).current_gas.as_u256(), - ); + // TODO: make compile-time removable if too much of a performance hit. + self.do_trace = self.do_trace && ext.trace_next_instruction( + self.reader.position - 1, opcode, self.gasometer.as_mut().expect(GASOMETER_PROOF).current_gas.as_u256(), + ); - let instruction = match instruction { - Some(i) => i, - None => return Err(InterpreterResult::Done(Err(vm::Error::BadInstruction { - instruction: opcode - }))), + let instruction = match instruction { + Some(i) => i, + None => return Err(InterpreterResult::Done(Err(vm::Error::BadInstruction { + instruction: opcode + }))), + }; + + let info = instruction.info(); + self.last_stack_ret_len = info.ret; + self.verify_instruction(ext, instruction, info)?; + + // Calculate gas cost + let requirements = self.gasometer.as_mut().expect(GASOMETER_PROOF).requirements(ext, instruction, info, &self.stack, self.mem.size())?; + if self.do_trace { + ext.trace_prepare_execute(self.reader.position - 1, opcode, requirements.gas_cost.as_u256(), Self::mem_written(instruction, &self.stack), Self::store_written(instruction, &self.stack)); + } + + self.gasometer.as_mut().expect(GASOMETER_PROOF).verify_gas(&requirements.gas_cost)?; + self.mem.expand(requirements.memory_required_size); + self.gasometer.as_mut().expect(GASOMETER_PROOF).current_mem_gas = requirements.memory_total_gas; + self.gasometer.as_mut().expect(GASOMETER_PROOF).current_gas = self.gasometer.as_mut().expect(GASOMETER_PROOF).current_gas - requirements.gas_cost; + + evm_debug!({ self.informant.before_instruction(self.reader.position, instruction, info, &self.gasometer.as_mut().expect(GASOMETER_PROOF).current_gas, &self.stack) }); + + // Execute instruction + let current_gas = self.gasometer.as_mut().expect(GASOMETER_PROOF).current_gas; + let result = self.exec_instruction( + current_gas, ext, instruction, requirements.provide_gas + )?; + + evm_debug!({ self.informant.after_instruction(instruction) }); + + result + }, }; - let info = instruction.info(); - self.verify_instruction(ext, instruction, info)?; - - // Calculate gas cost - let requirements = self.gasometer.as_mut().expect(GASOMETER_PROOF).requirements(ext, instruction, info, &self.stack, self.mem.size())?; - if self.do_trace { - ext.trace_prepare_execute(self.reader.position - 1, opcode, requirements.gas_cost.as_u256()); + if let InstructionResult::Trap(trap) = result { + return Err(InterpreterResult::Trap(trap)); } - self.gasometer.as_mut().expect(GASOMETER_PROOF).verify_gas(&requirements.gas_cost)?; - self.mem.expand(requirements.memory_required_size); - self.gasometer.as_mut().expect(GASOMETER_PROOF).current_mem_gas = requirements.memory_total_gas; - self.gasometer.as_mut().expect(GASOMETER_PROOF).current_gas = self.gasometer.as_mut().expect(GASOMETER_PROOF).current_gas - requirements.gas_cost; - - evm_debug!({ self.informant.before_instruction(self.reader.position, instruction, info, &self.gasometer.as_mut().expect(GASOMETER_PROOF).current_gas, &self.stack) }); - - let (mem_written, store_written) = match self.do_trace { - true => (Self::mem_written(instruction, &self.stack), Self::store_written(instruction, &self.stack)), - false => (None, None), - }; - - // Execute instruction - let current_gas = self.gasometer.as_mut().expect(GASOMETER_PROOF).current_gas; - let result = self.exec_instruction( - current_gas, ext, instruction, requirements.provide_gas - )?; - - evm_debug!({ self.informant.after_instruction(instruction) }); - if let InstructionResult::UnusedGas(ref gas) = result { self.gasometer.as_mut().expect(GASOMETER_PROOF).current_gas = self.gasometer.as_mut().expect(GASOMETER_PROOF).current_gas + *gas; } @@ -296,9 +376,8 @@ impl Interpreter { if self.do_trace { ext.trace_executed( self.gasometer.as_mut().expect(GASOMETER_PROOF).current_gas.as_u256(), - self.stack.peek_top(info.ret), - mem_written.map(|(o, s)| (o, &(self.mem[o..o+s]))), - store_written, + self.stack.peek_top(self.last_stack_ret_len), + &self.mem, ); } @@ -451,21 +530,24 @@ impl Interpreter { let contract_code = self.mem.read_slice(init_off, init_size); - let create_result = ext.create(&create_gas.as_u256(), &endowment, contract_code, address_scheme); + let create_result = ext.create(&create_gas.as_u256(), &endowment, contract_code, address_scheme, true); return match create_result { - ContractCreateResult::Created(address, gas_left) => { + Ok(ContractCreateResult::Created(address, gas_left)) => { self.stack.push(address_to_u256(address)); Ok(InstructionResult::UnusedGas(Cost::from_u256(gas_left).expect("Gas left cannot be greater."))) }, - ContractCreateResult::Reverted(gas_left, return_data) => { + Ok(ContractCreateResult::Reverted(gas_left, return_data)) => { self.stack.push(U256::zero()); self.return_data = return_data; Ok(InstructionResult::UnusedGas(Cost::from_u256(gas_left).expect("Gas left cannot be greater."))) }, - ContractCreateResult::Failed => { + Ok(ContractCreateResult::Failed) => { self.stack.push(U256::zero()); Ok(InstructionResult::Ok) }, + Err(trap) => { + Ok(InstructionResult::Trap(trap)) + }, }; }, instructions::CALL | instructions::CALLCODE | instructions::DELEGATECALL | instructions::STATICCALL => { @@ -524,13 +606,14 @@ impl Interpreter { let call_result = { let input = self.mem.read_slice(in_off, in_size); - ext.call(&call_gas.as_u256(), sender_address, receive_address, value, input, &code_address, call_type) + ext.call(&call_gas.as_u256(), sender_address, receive_address, value, input, &code_address, call_type, true) }; - let output = self.mem.writeable_slice(out_off, out_size); + self.resume_output_range = Some((out_off, out_size)); return match call_result { - MessageCallResult::Success(gas_left, data) => { + Ok(MessageCallResult::Success(gas_left, data)) => { + let output = self.mem.writeable_slice(out_off, out_size); let len = cmp::min(output.len(), data.len()); (&mut output[..len]).copy_from_slice(&data[..len]); @@ -538,7 +621,8 @@ impl Interpreter { self.return_data = data; Ok(InstructionResult::UnusedGas(Cost::from_u256(gas_left).expect("Gas left cannot be greater than current one"))) }, - MessageCallResult::Reverted(gas_left, data) => { + Ok(MessageCallResult::Reverted(gas_left, data)) => { + let output = self.mem.writeable_slice(out_off, out_size); let len = cmp::min(output.len(), data.len()); (&mut output[..len]).copy_from_slice(&data[..len]); @@ -546,10 +630,13 @@ impl Interpreter { self.return_data = data; Ok(InstructionResult::UnusedGas(Cost::from_u256(gas_left).expect("Gas left cannot be greater than current one"))) }, - MessageCallResult::Failed => { + Ok(MessageCallResult::Failed) => { self.stack.push(U256::zero()); Ok(InstructionResult::Ok) }, + Err(trap) => { + Ok(InstructionResult::Trap(trap)) + }, }; }, instructions::RETURN => { @@ -1095,10 +1182,10 @@ mod tests { use rustc_hex::FromHex; use vmtype::VMType; use factory::Factory; - use vm::{self, Vm, ActionParams, ActionValue}; + use vm::{self, Exec, ActionParams, ActionValue}; use vm::tests::{FakeExt, test_finalize}; - fn interpreter(params: ActionParams, ext: &vm::Ext) -> Box { + fn interpreter(params: ActionParams, ext: &vm::Ext) -> Box { Factory::new(VMType::Interpreter, 1).create(params, ext.schedule(), ext.depth()) } @@ -1118,7 +1205,7 @@ mod tests { let gas_left = { let mut vm = interpreter(params, &ext); - test_finalize(vm.exec(&mut ext)).unwrap() + test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() }; assert_eq!(ext.calls.len(), 1); @@ -1140,7 +1227,7 @@ mod tests { let err = { let mut vm = interpreter(params, &ext); - test_finalize(vm.exec(&mut ext)).err().unwrap() + test_finalize(vm.exec(&mut ext).ok().unwrap()).err().unwrap() }; assert_eq!(err, ::vm::Error::OutOfBounds); diff --git a/ethcore/evm/src/tests.rs b/ethcore/evm/src/tests.rs index 9fd7bcd90..49599f91d 100644 --- a/ethcore/evm/src/tests.rs +++ b/ethcore/evm/src/tests.rs @@ -39,7 +39,7 @@ fn test_add(factory: super::Factory) { let gas_left = { let mut vm = factory.create(params, ext.schedule(), ext.depth()); - test_finalize(vm.exec(&mut ext)).unwrap() + test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() }; assert_eq!(gas_left, U256::from(79_988)); @@ -59,7 +59,7 @@ fn test_sha3(factory: super::Factory) { let gas_left = { let mut vm = factory.create(params, ext.schedule(), ext.depth()); - test_finalize(vm.exec(&mut ext)).unwrap() + test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() }; assert_eq!(gas_left, U256::from(79_961)); @@ -79,7 +79,7 @@ fn test_address(factory: super::Factory) { let gas_left = { let mut vm = factory.create(params, ext.schedule(), ext.depth()); - test_finalize(vm.exec(&mut ext)).unwrap() + test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() }; assert_eq!(gas_left, U256::from(79_995)); @@ -101,7 +101,7 @@ fn test_origin(factory: super::Factory) { let gas_left = { let mut vm = factory.create(params, ext.schedule(), ext.depth()); - test_finalize(vm.exec(&mut ext)).unwrap() + test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() }; assert_eq!(gas_left, U256::from(79_995)); @@ -123,7 +123,7 @@ fn test_sender(factory: super::Factory) { let gas_left = { let mut vm = factory.create(params, ext.schedule(), ext.depth()); - test_finalize(vm.exec(&mut ext)).unwrap() + test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() }; assert_eq!(gas_left, U256::from(79_995)); @@ -158,7 +158,7 @@ fn test_extcodecopy(factory: super::Factory) { let gas_left = { let mut vm = factory.create(params, ext.schedule(), ext.depth()); - test_finalize(vm.exec(&mut ext)).unwrap() + test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() }; assert_eq!(gas_left, U256::from(79_935)); @@ -178,7 +178,7 @@ fn test_log_empty(factory: super::Factory) { let gas_left = { let mut vm = factory.create(params, ext.schedule(), ext.depth()); - test_finalize(vm.exec(&mut ext)).unwrap() + test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() }; assert_eq!(gas_left, U256::from(99_619)); @@ -210,7 +210,7 @@ fn test_log_sender(factory: super::Factory) { let gas_left = { let mut vm = factory.create(params, ext.schedule(), ext.depth()); - test_finalize(vm.exec(&mut ext)).unwrap() + test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() }; assert_eq!(gas_left, U256::from(98_974)); @@ -235,7 +235,7 @@ fn test_blockhash(factory: super::Factory) { let gas_left = { let mut vm = factory.create(params, ext.schedule(), ext.depth()); - test_finalize(vm.exec(&mut ext)).unwrap() + test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() }; assert_eq!(gas_left, U256::from(79_974)); @@ -257,7 +257,7 @@ fn test_calldataload(factory: super::Factory) { let gas_left = { let mut vm = factory.create(params, ext.schedule(), ext.depth()); - test_finalize(vm.exec(&mut ext)).unwrap() + test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() }; assert_eq!(gas_left, U256::from(79_991)); @@ -278,7 +278,7 @@ fn test_author(factory: super::Factory) { let gas_left = { let mut vm = factory.create(params, ext.schedule(), ext.depth()); - test_finalize(vm.exec(&mut ext)).unwrap() + test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() }; assert_eq!(gas_left, U256::from(79_995)); @@ -298,7 +298,7 @@ fn test_timestamp(factory: super::Factory) { let gas_left = { let mut vm = factory.create(params, ext.schedule(), ext.depth()); - test_finalize(vm.exec(&mut ext)).unwrap() + test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() }; assert_eq!(gas_left, U256::from(79_995)); @@ -318,7 +318,7 @@ fn test_number(factory: super::Factory) { let gas_left = { let mut vm = factory.create(params, ext.schedule(), ext.depth()); - test_finalize(vm.exec(&mut ext)).unwrap() + test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() }; assert_eq!(gas_left, U256::from(79_995)); @@ -338,7 +338,7 @@ fn test_difficulty(factory: super::Factory) { let gas_left = { let mut vm = factory.create(params, ext.schedule(), ext.depth()); - test_finalize(vm.exec(&mut ext)).unwrap() + test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() }; assert_eq!(gas_left, U256::from(79_995)); @@ -358,7 +358,7 @@ fn test_gas_limit(factory: super::Factory) { let gas_left = { let mut vm = factory.create(params, ext.schedule(), ext.depth()); - test_finalize(vm.exec(&mut ext)).unwrap() + test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() }; assert_eq!(gas_left, U256::from(79_995)); @@ -376,7 +376,7 @@ fn test_mul(factory: super::Factory) { let gas_left = { let mut vm = factory.create(params, ext.schedule(), ext.depth()); - test_finalize(vm.exec(&mut ext)).unwrap() + test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() }; assert_store(&ext, 0, "000000000000000000000000000000000000000000000000734349397b853383"); @@ -394,7 +394,7 @@ fn test_sub(factory: super::Factory) { let gas_left = { let mut vm = factory.create(params, ext.schedule(), ext.depth()); - test_finalize(vm.exec(&mut ext)).unwrap() + test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() }; assert_store(&ext, 0, "0000000000000000000000000000000000000000000000000000012364ad0302"); @@ -412,7 +412,7 @@ fn test_div(factory: super::Factory) { let gas_left = { let mut vm = factory.create(params, ext.schedule(), ext.depth()); - test_finalize(vm.exec(&mut ext)).unwrap() + test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() }; assert_store(&ext, 0, "000000000000000000000000000000000000000000000000000000000002e0ac"); @@ -430,7 +430,7 @@ fn test_div_zero(factory: super::Factory) { let gas_left = { let mut vm = factory.create(params, ext.schedule(), ext.depth()); - test_finalize(vm.exec(&mut ext)).unwrap() + test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() }; assert_store(&ext, 0, "0000000000000000000000000000000000000000000000000000000000000000"); @@ -448,7 +448,7 @@ fn test_mod(factory: super::Factory) { let gas_left = { let mut vm = factory.create(params, ext.schedule(), ext.depth()); - test_finalize(vm.exec(&mut ext)).unwrap() + test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() }; assert_store(&ext, 0, "0000000000000000000000000000000000000000000000000000000000076b4b"); @@ -467,7 +467,7 @@ fn test_smod(factory: super::Factory) { let gas_left = { let mut vm = factory.create(params, ext.schedule(), ext.depth()); - test_finalize(vm.exec(&mut ext)).unwrap() + test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() }; assert_store(&ext, 0, "0000000000000000000000000000000000000000000000000000000000076b4b"); @@ -486,7 +486,7 @@ fn test_sdiv(factory: super::Factory) { let gas_left = { let mut vm = factory.create(params, ext.schedule(), ext.depth()); - test_finalize(vm.exec(&mut ext)).unwrap() + test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() }; assert_store(&ext, 0, "000000000000000000000000000000000000000000000000000000000002e0ac"); @@ -505,7 +505,7 @@ fn test_exp(factory: super::Factory) { let gas_left = { let mut vm = factory.create(params, ext.schedule(), ext.depth()); - test_finalize(vm.exec(&mut ext)).unwrap() + test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() }; assert_store(&ext, 0, "90fd23767b60204c3d6fc8aec9e70a42a3f127140879c133a20129a597ed0c59"); @@ -525,7 +525,7 @@ fn test_comparison(factory: super::Factory) { let gas_left = { let mut vm = factory.create(params, ext.schedule(), ext.depth()); - test_finalize(vm.exec(&mut ext)).unwrap() + test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() }; assert_store(&ext, 0, "0000000000000000000000000000000000000000000000000000000000000000"); @@ -546,7 +546,7 @@ fn test_signed_comparison(factory: super::Factory) { let gas_left = { let mut vm = factory.create(params, ext.schedule(), ext.depth()); - test_finalize(vm.exec(&mut ext)).unwrap() + test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() }; assert_store(&ext, 0, "0000000000000000000000000000000000000000000000000000000000000000"); @@ -567,7 +567,7 @@ fn test_bitops(factory: super::Factory) { let gas_left = { let mut vm = factory.create(params, ext.schedule(), ext.depth()); - test_finalize(vm.exec(&mut ext)).unwrap() + test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() }; assert_store(&ext, 0, "00000000000000000000000000000000000000000000000000000000000000f0"); @@ -590,7 +590,7 @@ fn test_addmod_mulmod(factory: super::Factory) { let gas_left = { let mut vm = factory.create(params, ext.schedule(), ext.depth()); - test_finalize(vm.exec(&mut ext)).unwrap() + test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() }; assert_store(&ext, 0, "0000000000000000000000000000000000000000000000000000000000000001"); @@ -611,7 +611,7 @@ fn test_byte(factory: super::Factory) { let gas_left = { let mut vm = factory.create(params, ext.schedule(), ext.depth()); - test_finalize(vm.exec(&mut ext)).unwrap() + test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() }; assert_store(&ext, 0, "0000000000000000000000000000000000000000000000000000000000000000"); @@ -630,7 +630,7 @@ fn test_signextend(factory: super::Factory) { let gas_left = { let mut vm = factory.create(params, ext.schedule(), ext.depth()); - test_finalize(vm.exec(&mut ext)).unwrap() + test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() }; assert_store(&ext, 0, "0000000000000000000000000000000000000000000000000000000000000fff"); @@ -650,7 +650,7 @@ fn test_badinstruction_int() { let err = { let mut vm = factory.create(params, ext.schedule(), ext.depth()); - test_finalize(vm.exec(&mut ext)).unwrap_err() + test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap_err() }; match err { @@ -670,7 +670,7 @@ fn test_pop(factory: super::Factory) { let gas_left = { let mut vm = factory.create(params, ext.schedule(), ext.depth()); - test_finalize(vm.exec(&mut ext)).unwrap() + test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() }; assert_store(&ext, 0, "00000000000000000000000000000000000000000000000000000000000000f0"); @@ -690,7 +690,7 @@ fn test_extops(factory: super::Factory) { let gas_left = { let mut vm = factory.create(params, ext.schedule(), ext.depth()); - test_finalize(vm.exec(&mut ext)).unwrap() + test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() }; assert_store(&ext, 0, "0000000000000000000000000000000000000000000000000000000000000004"); // PC / CALLDATASIZE @@ -713,7 +713,7 @@ fn test_jumps(factory: super::Factory) { let gas_left = { let mut vm = factory.create(params, ext.schedule(), ext.depth()); - test_finalize(vm.exec(&mut ext)).unwrap() + test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() }; assert_eq!(ext.sstore_clears, U256::from(ext.schedule().sstore_refund_gas)); @@ -741,7 +741,7 @@ fn test_calls(factory: super::Factory) { let gas_left = { let mut vm = factory.create(params, ext.schedule(), ext.depth()); - test_finalize(vm.exec(&mut ext)).unwrap() + test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() }; assert_set_contains(&ext.calls, &FakeCall { @@ -782,7 +782,7 @@ fn test_create_in_staticcall(factory: super::Factory) { let err = { let mut vm = factory.create(params, ext.schedule(), ext.depth()); - test_finalize(vm.exec(&mut ext)).unwrap_err() + test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap_err() }; assert_eq!(err, vm::Error::MutableCallInStaticContext); @@ -1050,7 +1050,7 @@ fn push_two_pop_one_constantinople_test(factory: &super::Factory, opcode: u8, pu let _ = { let mut vm = factory.create(params, ext.schedule(), ext.depth()); - test_finalize(vm.exec(&mut ext)).unwrap() + test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() }; assert_store(&ext, 0, result); diff --git a/ethcore/src/executive.rs b/ethcore/src/executive.rs index d2cf2f027..cc9ac9a0b 100644 --- a/ethcore/src/executive.rs +++ b/ethcore/src/executive.rs @@ -26,8 +26,9 @@ use machine::EthereumMachine as Machine; use evm::{CallType, Finalize, FinalizationResult}; use vm::{ self, EnvInfo, CreateContractAddress, ReturnData, CleanDustMode, ActionParams, - ActionValue, Schedule, + ActionValue, Schedule, TrapError, ResumeCall, ResumeCreate }; +use factory::VmFactory; use externalities::*; use trace::{self, Tracer, VMTracer}; use transaction::{Action, SignedTransaction}; @@ -80,6 +81,29 @@ pub fn contract_address(address_scheme: CreateContractAddress, sender: &Address, } } +/// Convert a finalization result into a VM message call result. +pub fn into_message_call_result(result: vm::Result) -> vm::MessageCallResult { + match result { + Ok(FinalizationResult { gas_left, return_data, apply_state: true }) => vm::MessageCallResult::Success(gas_left, return_data), + Ok(FinalizationResult { gas_left, return_data, apply_state: false }) => vm::MessageCallResult::Reverted(gas_left, return_data), + _ => vm::MessageCallResult::Failed + } +} + +/// Convert a finalization result into a VM contract create result. +pub fn into_contract_create_result(result: vm::Result, address: &Address, substate: &mut Substate) -> vm::ContractCreateResult { + match result { + Ok(FinalizationResult { gas_left, apply_state: true, .. }) => { + substate.contracts_created.push(address.clone()); + vm::ContractCreateResult::Created(address.clone(), gas_left) + }, + Ok(FinalizationResult { gas_left, apply_state: false, return_data }) => { + vm::ContractCreateResult::Reverted(gas_left, return_data) + }, + _ => vm::ContractCreateResult::Failed, + } +} + /// Transaction execution options. #[derive(Copy, Clone, PartialEq)] pub struct TransactOptions { @@ -165,6 +189,559 @@ impl TransactOptions { } } +/// Trap result returned by executive. +pub type ExecutiveTrapResult<'a, T> = vm::TrapResult, CallCreateExecutive<'a>>; +/// Trap error for executive. +pub type ExecutiveTrapError<'a> = vm::TrapError, CallCreateExecutive<'a>>; + +enum CallCreateExecutiveKind { + Transfer(ActionParams), + CallBuiltin(ActionParams), + ExecCall(ActionParams, Substate), + ExecCreate(ActionParams, Substate), + ResumeCall(OriginInfo, Box, Substate), + ResumeCreate(OriginInfo, Box, Substate), +} + +/// Executive for a raw call/create action. +pub struct CallCreateExecutive<'a> { + info: &'a EnvInfo, + machine: &'a Machine, + schedule: &'a Schedule, + factory: &'a VmFactory, + depth: usize, + stack_depth: usize, + static_flag: bool, + is_create: bool, + gas: U256, + kind: CallCreateExecutiveKind, +} + +impl<'a> CallCreateExecutive<'a> { + /// Create a new call executive using raw data. + pub fn new_call_raw(params: ActionParams, info: &'a EnvInfo, machine: &'a Machine, schedule: &'a Schedule, factory: &'a VmFactory, depth: usize, stack_depth: usize, parent_static_flag: bool) -> Self { + trace!("Executive::call(params={:?}) self.env_info={:?}, parent_static={}", params, info, parent_static_flag); + + let gas = params.gas; + let static_flag = parent_static_flag || params.call_type == CallType::StaticCall; + + // if destination is builtin, try to execute it + let kind = if let Some(builtin) = machine.builtin(¶ms.code_address, info.number) { + // Engines aren't supposed to return builtins until activation, but + // prefer to fail rather than silently break consensus. + if !builtin.is_active(info.number) { + panic!("Consensus failure: engine implementation prematurely enabled built-in at {}", params.code_address); + } + + CallCreateExecutiveKind::CallBuiltin(params) + } else { + if params.code.is_some() { + CallCreateExecutiveKind::ExecCall(params, Substate::new()) + } else { + CallCreateExecutiveKind::Transfer(params) + } + }; + + Self { + info, machine, schedule, factory, depth, stack_depth, static_flag, kind, gas, + is_create: false, + } + } + + /// Create a new create executive using raw data. + pub fn new_create_raw(params: ActionParams, info: &'a EnvInfo, machine: &'a Machine, schedule: &'a Schedule, factory: &'a VmFactory, depth: usize, stack_depth: usize, static_flag: bool) -> Self { + trace!("Executive::create(params={:?}) self.env_info={:?}, static={}", params, info, static_flag); + + let gas = params.gas; + + let kind = CallCreateExecutiveKind::ExecCreate(params, Substate::new()); + + Self { + info, machine, schedule, factory, depth, stack_depth, static_flag, kind, gas, + is_create: true, + } + } + + /// If this executive contains an unconfirmed substate, returns a mutable reference to it. + pub fn unconfirmed_substate(&mut self) -> Option<&mut Substate> { + match self.kind { + CallCreateExecutiveKind::ExecCall(_, ref mut unsub) => Some(unsub), + CallCreateExecutiveKind::ExecCreate(_, ref mut unsub) => Some(unsub), + CallCreateExecutiveKind::ResumeCreate(_, _, ref mut unsub) => Some(unsub), + CallCreateExecutiveKind::ResumeCall(_, _, ref mut unsub) => Some(unsub), + CallCreateExecutiveKind::Transfer(..) | CallCreateExecutiveKind::CallBuiltin(..) => None, + } + } + + fn check_static_flag(params: &ActionParams, static_flag: bool, is_create: bool) -> vm::Result<()> { + if is_create { + if static_flag { + return Err(vm::Error::MutableCallInStaticContext); + } + } else { + if (static_flag && + (params.call_type == CallType::StaticCall || params.call_type == CallType::Call)) && + params.value.value() > U256::zero() + { + return Err(vm::Error::MutableCallInStaticContext); + } + } + + Ok(()) + } + + fn check_eip684(params: &ActionParams, state: &State) -> vm::Result<()> { + if state.exists_and_has_code_or_nonce(¶ms.address)? { + return Err(vm::Error::OutOfGas); + } + + Ok(()) + } + + fn transfer_exec_balance(params: &ActionParams, schedule: &Schedule, state: &mut State, substate: &mut Substate) -> vm::Result<()> { + if let ActionValue::Transfer(val) = params.value { + state.transfer_balance(¶ms.sender, ¶ms.address, &val, substate.to_cleanup_mode(&schedule))?; + } + + Ok(()) + } + + fn transfer_exec_balance_and_init_contract(params: &ActionParams, schedule: &Schedule, state: &mut State, substate: &mut Substate) -> vm::Result<()> { + let nonce_offset = if schedule.no_empty {1} else {0}.into(); + let prev_bal = state.balance(¶ms.address)?; + if let ActionValue::Transfer(val) = params.value { + state.sub_balance(¶ms.sender, &val, &mut substate.to_cleanup_mode(&schedule))?; + state.new_contract(¶ms.address, val + prev_bal, nonce_offset)?; + } else { + state.new_contract(¶ms.address, prev_bal, nonce_offset)?; + } + + Ok(()) + } + + fn enact_result(result: &vm::Result, state: &mut State, substate: &mut Substate, un_substate: Substate) { + match *result { + Err(vm::Error::OutOfGas) + | Err(vm::Error::BadJumpDestination {..}) + | Err(vm::Error::BadInstruction {.. }) + | Err(vm::Error::StackUnderflow {..}) + | Err(vm::Error::BuiltIn {..}) + | Err(vm::Error::Wasm {..}) + | Err(vm::Error::OutOfStack {..}) + | Err(vm::Error::MutableCallInStaticContext) + | Err(vm::Error::OutOfBounds) + | Err(vm::Error::Reverted) + | Ok(FinalizationResult { apply_state: false, .. }) => { + state.revert_to_checkpoint(); + }, + Ok(_) | Err(vm::Error::Internal(_)) => { + state.discard_checkpoint(); + substate.accrue(un_substate); + } + } + } + + /// Creates `Externalities` from `Executive`. + fn as_externalities<'any, B: 'any + StateBackend, T, V>( + state: &'any mut State, + info: &'any EnvInfo, + machine: &'any Machine, + schedule: &'any Schedule, + depth: usize, + stack_depth: usize, + static_flag: bool, + origin_info: &'any OriginInfo, + substate: &'any mut Substate, + output: OutputPolicy, + tracer: &'any mut T, + vm_tracer: &'any mut V, + ) -> Externalities<'any, T, V, B> where T: Tracer, V: VMTracer { + Externalities::new(state, info, machine, schedule, depth, stack_depth, origin_info, substate, output, tracer, vm_tracer, static_flag) + } + + /// Execute the executive. If a sub-call/create action is required, a resume trap error is returned. The caller is + /// then expected to call `resume_call` or `resume_create` to continue the execution. + /// + /// Current-level tracing is expected to be handled by caller. + pub fn exec(mut self, state: &mut State, substate: &mut Substate, tracer: &mut T, vm_tracer: &mut V) -> ExecutiveTrapResult<'a, FinalizationResult> { + match self.kind { + CallCreateExecutiveKind::Transfer(ref params) => { + assert!(!self.is_create); + + let mut inner = || { + Self::check_static_flag(params, self.static_flag, self.is_create)?; + Self::transfer_exec_balance(params, self.schedule, state, substate)?; + + Ok(FinalizationResult { + gas_left: params.gas, + return_data: ReturnData::empty(), + apply_state: true, + }) + }; + + Ok(inner()) + }, + CallCreateExecutiveKind::CallBuiltin(ref params) => { + assert!(!self.is_create); + + let mut inner = || { + let builtin = self.machine.builtin(¶ms.code_address, self.info.number).expect("Builtin is_some is checked when creating this kind in new_call_raw; qed"); + + Self::check_static_flag(¶ms, self.static_flag, self.is_create)?; + state.checkpoint(); + Self::transfer_exec_balance(¶ms, self.schedule, state, substate)?; + + let default = []; + let data = if let Some(ref d) = params.data { d as &[u8] } else { &default as &[u8] }; + + let cost = builtin.cost(data); + if cost <= params.gas { + let mut builtin_out_buffer = Vec::new(); + let result = { + let mut builtin_output = BytesRef::Flexible(&mut builtin_out_buffer); + builtin.execute(data, &mut builtin_output) + }; + if let Err(e) = result { + state.revert_to_checkpoint(); + + Err(e.into()) + } else { + state.discard_checkpoint(); + + let out_len = builtin_out_buffer.len(); + Ok(FinalizationResult { + gas_left: params.gas - cost, + return_data: ReturnData::new(builtin_out_buffer, 0, out_len), + apply_state: true, + }) + } + } else { + // just drain the whole gas + state.revert_to_checkpoint(); + + Err(vm::Error::OutOfGas) + } + }; + + Ok(inner()) + }, + CallCreateExecutiveKind::ExecCall(params, mut unconfirmed_substate) => { + assert!(!self.is_create); + + { + let static_flag = self.static_flag; + let is_create = self.is_create; + let schedule = self.schedule; + + let mut pre_inner = || { + Self::check_static_flag(¶ms, static_flag, is_create)?; + state.checkpoint(); + Self::transfer_exec_balance(¶ms, schedule, state, substate)?; + Ok(()) + }; + + match pre_inner() { + Ok(()) => (), + Err(err) => return Ok(Err(err)), + } + } + + let origin_info = OriginInfo::from(¶ms); + let exec = self.factory.create(params, self.schedule, self.depth); + + let out = { + let mut ext = Self::as_externalities(state, self.info, self.machine, self.schedule, self.depth, self.stack_depth, self.static_flag, &origin_info, &mut unconfirmed_substate, OutputPolicy::Return, tracer, vm_tracer); + match exec.exec(&mut ext) { + Ok(val) => Ok(val.finalize(ext)), + Err(err) => Err(err), + } + }; + + let res = match out { + Ok(val) => val, + Err(TrapError::Call(subparams, resume)) => { + self.kind = CallCreateExecutiveKind::ResumeCall(origin_info, resume, unconfirmed_substate); + return Err(TrapError::Call(subparams, self)); + }, + Err(TrapError::Create(subparams, address, resume)) => { + self.kind = CallCreateExecutiveKind::ResumeCreate(origin_info, resume, unconfirmed_substate); + return Err(TrapError::Create(subparams, address, self)); + }, + }; + + Self::enact_result(&res, state, substate, unconfirmed_substate); + Ok(res) + }, + CallCreateExecutiveKind::ExecCreate(params, mut unconfirmed_substate) => { + assert!(self.is_create); + + { + let static_flag = self.static_flag; + let is_create = self.is_create; + let schedule = self.schedule; + + let mut pre_inner = || { + Self::check_eip684(¶ms, state)?; + Self::check_static_flag(¶ms, static_flag, is_create)?; + state.checkpoint(); + Self::transfer_exec_balance_and_init_contract(¶ms, schedule, state, substate)?; + Ok(()) + }; + + match pre_inner() { + Ok(()) => (), + Err(err) => return Ok(Err(err)), + } + } + + let origin_info = OriginInfo::from(¶ms); + let exec = self.factory.create(params, self.schedule, self.depth); + + let out = { + let mut ext = Self::as_externalities(state, self.info, self.machine, self.schedule, self.depth, self.stack_depth, self.static_flag, &origin_info, &mut unconfirmed_substate, OutputPolicy::InitContract, tracer, vm_tracer); + match exec.exec(&mut ext) { + Ok(val) => Ok(val.finalize(ext)), + Err(err) => Err(err), + } + }; + + let res = match out { + Ok(val) => val, + Err(TrapError::Call(subparams, resume)) => { + self.kind = CallCreateExecutiveKind::ResumeCall(origin_info, resume, unconfirmed_substate); + return Err(TrapError::Call(subparams, self)); + }, + Err(TrapError::Create(subparams, address, resume)) => { + self.kind = CallCreateExecutiveKind::ResumeCreate(origin_info, resume, unconfirmed_substate); + return Err(TrapError::Create(subparams, address, self)); + }, + }; + + Self::enact_result(&res, state, substate, unconfirmed_substate); + Ok(res) + }, + CallCreateExecutiveKind::ResumeCall(..) | CallCreateExecutiveKind::ResumeCreate(..) => panic!("This executive has already been executed once."), + } + } + + /// Resume execution from a call trap previsouly trapped by `exec`. + /// + /// Current-level tracing is expected to be handled by caller. + pub fn resume_call(mut self, result: vm::MessageCallResult, state: &mut State, substate: &mut Substate, tracer: &mut T, vm_tracer: &mut V) -> ExecutiveTrapResult<'a, FinalizationResult> { + match self.kind { + CallCreateExecutiveKind::ResumeCall(origin_info, resume, mut unconfirmed_substate) => { + let out = { + let exec = resume.resume_call(result); + + let mut ext = Self::as_externalities(state, self.info, self.machine, self.schedule, self.depth, self.stack_depth, self.static_flag, &origin_info, &mut unconfirmed_substate, if self.is_create { OutputPolicy::InitContract } else { OutputPolicy::Return }, tracer, vm_tracer); + match exec.exec(&mut ext) { + Ok(val) => Ok(val.finalize(ext)), + Err(err) => Err(err), + } + }; + + let res = match out { + Ok(val) => val, + Err(TrapError::Call(subparams, resume)) => { + self.kind = CallCreateExecutiveKind::ResumeCall(origin_info, resume, unconfirmed_substate); + return Err(TrapError::Call(subparams, self)); + }, + Err(TrapError::Create(subparams, address, resume)) => { + self.kind = CallCreateExecutiveKind::ResumeCreate(origin_info, resume, unconfirmed_substate); + return Err(TrapError::Create(subparams, address, self)); + }, + }; + + Self::enact_result(&res, state, substate, unconfirmed_substate); + Ok(res) + }, + CallCreateExecutiveKind::ResumeCreate(..) => + panic!("Resumable as create, but called resume_call"), + CallCreateExecutiveKind::Transfer(..) | CallCreateExecutiveKind::CallBuiltin(..) | + CallCreateExecutiveKind::ExecCall(..) | CallCreateExecutiveKind::ExecCreate(..) => + panic!("Not resumable"), + } + } + + /// Resume execution from a create trap previsouly trapped by `exec`. + /// + /// Current-level tracing is expected to be handled by caller. + pub fn resume_create(mut self, result: vm::ContractCreateResult, state: &mut State, substate: &mut Substate, tracer: &mut T, vm_tracer: &mut V) -> ExecutiveTrapResult<'a, FinalizationResult> { + match self.kind { + CallCreateExecutiveKind::ResumeCreate(origin_info, resume, mut unconfirmed_substate) => { + let out = { + let exec = resume.resume_create(result); + + let mut ext = Self::as_externalities(state, self.info, self.machine, self.schedule, self.depth, self.stack_depth, self.static_flag, &origin_info, &mut unconfirmed_substate, if self.is_create { OutputPolicy::InitContract } else { OutputPolicy::Return }, tracer, vm_tracer); + match exec.exec(&mut ext) { + Ok(val) => Ok(val.finalize(ext)), + Err(err) => Err(err), + } + }; + + let res = match out { + Ok(val) => val, + Err(TrapError::Call(subparams, resume)) => { + self.kind = CallCreateExecutiveKind::ResumeCall(origin_info, resume, unconfirmed_substate); + return Err(TrapError::Call(subparams, self)); + }, + Err(TrapError::Create(subparams, address, resume)) => { + self.kind = CallCreateExecutiveKind::ResumeCreate(origin_info, resume, unconfirmed_substate); + return Err(TrapError::Create(subparams, address, self)); + }, + }; + + Self::enact_result(&res, state, substate, unconfirmed_substate); + Ok(res) + }, + CallCreateExecutiveKind::ResumeCall(..) => + panic!("Resumable as call, but called resume_create"), + CallCreateExecutiveKind::Transfer(..) | CallCreateExecutiveKind::CallBuiltin(..) | + CallCreateExecutiveKind::ExecCall(..) | CallCreateExecutiveKind::ExecCreate(..) => + panic!("Not resumable"), + } + } + + /// Execute and consume the current executive. This function handles resume traps and sub-level tracing. The caller is expected to handle current-level tracing. + pub fn consume(self, state: &mut State, top_substate: &mut Substate, tracer: &mut T, vm_tracer: &mut V) -> vm::Result { + let mut last_res = Some((false, self.gas, self.exec(state, top_substate, tracer, vm_tracer))); + + let mut callstack: Vec<(Option
, CallCreateExecutive<'a>)> = Vec::new(); + loop { + match last_res { + None => { + match callstack.pop() { + Some((_, exec)) => { + let second_last = callstack.last_mut(); + let parent_substate = match second_last { + Some((_, ref mut second_last)) => second_last.unconfirmed_substate().expect("Current stack value is created from second last item; second last item must be call or create; qed"), + None => top_substate, + }; + + last_res = Some((exec.is_create, exec.gas, exec.exec(state, parent_substate, tracer, vm_tracer))); + }, + None => panic!("When callstack only had one item and it was executed, this function would return; callstack never reaches zero item; qed"), + } + }, + Some((is_create, gas, Ok(val))) => { + let current = callstack.pop(); + + match current { + Some((address, mut exec)) => { + if is_create { + let address = address.expect("If the last executed status was from a create executive, then the destination address was pushed to the callstack; address is_some if it is_create; qed"); + + match val { + Ok(ref val) if val.apply_state => { + tracer.done_trace_create( + gas - val.gas_left, + &val.return_data, + address + ); + }, + Ok(_) => { + tracer.done_trace_failed(&vm::Error::Reverted); + }, + Err(ref err) => { + tracer.done_trace_failed(err); + }, + } + + vm_tracer.done_subtrace(); + + let second_last = callstack.last_mut(); + let parent_substate = match second_last { + Some((_, ref mut second_last)) => second_last.unconfirmed_substate().expect("Current stack value is created from second last item; second last item must be call or create; qed"), + None => top_substate, + }; + + let contract_create_result = into_contract_create_result(val, &address, exec.unconfirmed_substate().expect("Executive is resumed from a create; it has an unconfirmed substate; qed")); + last_res = Some((exec.is_create, exec.gas, exec.resume_create( + contract_create_result, + state, + parent_substate, + tracer, + vm_tracer + ))); + } else { + match val { + Ok(ref val) if val.apply_state => { + tracer.done_trace_call( + gas - val.gas_left, + &val.return_data, + ); + }, + Ok(_) => { + tracer.done_trace_failed(&vm::Error::Reverted); + }, + Err(ref err) => { + tracer.done_trace_failed(err); + }, + } + + vm_tracer.done_subtrace(); + + let second_last = callstack.last_mut(); + let parent_substate = match second_last { + Some((_, ref mut second_last)) => second_last.unconfirmed_substate().expect("Current stack value is created from second last item; second last item must be call or create; qed"), + None => top_substate, + }; + + last_res = Some((exec.is_create, exec.gas, exec.resume_call( + into_message_call_result(val), + state, + parent_substate, + tracer, + vm_tracer + ))); + } + }, + None => return val, + } + }, + Some((_, _, Err(TrapError::Call(subparams, resume)))) => { + tracer.prepare_trace_call(&subparams, resume.depth + 1, resume.machine.builtin(&subparams.address, resume.info.number).is_some()); + vm_tracer.prepare_subtrace(subparams.code.as_ref().map_or_else(|| &[] as &[u8], |d| &*d as &[u8])); + + let sub_exec = CallCreateExecutive::new_call_raw( + subparams, + resume.info, + resume.machine, + resume.schedule, + resume.factory, + resume.depth + 1, + resume.stack_depth, + resume.static_flag, + ); + + callstack.push((None, resume)); + callstack.push((None, sub_exec)); + last_res = None; + }, + Some((_, _, Err(TrapError::Create(subparams, address, resume)))) => { + tracer.prepare_trace_create(&subparams); + vm_tracer.prepare_subtrace(subparams.code.as_ref().map_or_else(|| &[] as &[u8], |d| &*d as &[u8])); + + let sub_exec = CallCreateExecutive::new_create_raw( + subparams, + resume.info, + resume.machine, + resume.schedule, + resume.factory, + resume.depth + 1, + resume.stack_depth, + resume.static_flag + ); + + callstack.push((Some(address), resume)); + callstack.push((None, sub_exec)); + last_res = None; + }, + } + } + } +} + /// Transaction executor. pub struct Executive<'a, B: 'a> { state: &'a mut State, @@ -200,20 +777,6 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> { } } - /// Creates `Externalities` from `Executive`. - pub fn as_externalities<'any, T, V>( - &'any mut self, - origin_info: OriginInfo, - substate: &'any mut Substate, - output: OutputPolicy, - tracer: &'any mut T, - vm_tracer: &'any mut V, - static_call: bool, - ) -> Externalities<'any, T, V, B> where T: Tracer, V: VMTracer { - let is_static = self.static_flag || static_call; - Externalities::new(self.state, self.info, self.machine, self.schedule, self.depth, origin_info, substate, output, tracer, vm_tracer, is_static) - } - /// This function should be used to execute transaction. pub fn transact(&'a mut self, t: &SignedTransaction, options: TransactOptions) -> Result, ExecutionError> where T: Tracer, V: VMTracer, @@ -348,45 +911,79 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> { Ok(self.finalize(t, substate, result, output, tracer.drain(), vm_tracer.drain())?) } - fn exec_vm( + /// Calls contract function with given contract params and stack depth. + /// NOTE. It does not finalize the transaction (doesn't do refunds, nor suicides). + /// Modifies the substate and the output. + /// Returns either gas_left or `vm::Error`. + pub fn call_with_stack_depth( &mut self, params: ActionParams, - unconfirmed_substate: &mut Substate, - output_policy: OutputPolicy, + substate: &mut Substate, + stack_depth: usize, + tracer: &mut T, + vm_tracer: &mut V + ) -> vm::Result where T: Tracer, V: VMTracer { + tracer.prepare_trace_call(¶ms, self.depth, self.machine.builtin(¶ms.address, self.info.number).is_some()); + vm_tracer.prepare_subtrace(params.code.as_ref().map_or_else(|| &[] as &[u8], |d| &*d as &[u8])); + + let gas = params.gas; + + let vm_factory = self.state.vm_factory(); + let result = CallCreateExecutive::new_call_raw( + params, + self.info, + self.machine, + self.schedule, + &vm_factory, + self.depth, + stack_depth, + self.static_flag + ).consume(self.state, substate, tracer, vm_tracer); + + match result { + Ok(ref val) if val.apply_state => { + tracer.done_trace_call( + gas - val.gas_left, + &val.return_data, + ); + }, + Ok(_) => { + tracer.done_trace_failed(&vm::Error::Reverted); + }, + Err(ref err) => { + tracer.done_trace_failed(err); + }, + } + vm_tracer.done_subtrace(); + + result + } + + /// Calls contract function with given contract params, if the stack depth is above a threshold, create a new thread + /// to execute it. + pub fn call_with_crossbeam( + &mut self, + params: ActionParams, + substate: &mut Substate, + stack_depth: usize, tracer: &mut T, vm_tracer: &mut V ) -> vm::Result where T: Tracer, V: VMTracer { let local_stack_size = ::io::LOCAL_STACK_SIZE.with(|sz| sz.get()); let depth_threshold = local_stack_size.saturating_sub(STACK_SIZE_ENTRY_OVERHEAD) / STACK_SIZE_PER_DEPTH; - let static_call = params.call_type == CallType::StaticCall; - // Ordinary execution - keep VM in same thread - if self.depth != depth_threshold { - let vm_factory = self.state.vm_factory(); - let origin_info = OriginInfo::from(¶ms); - trace!(target: "executive", "ext.schedule.have_delegate_call: {}", self.schedule.have_delegate_call); - let mut vm = vm_factory.create(params, self.schedule, self.depth); - let mut ext = self.as_externalities(origin_info, unconfirmed_substate, output_policy, tracer, vm_tracer, static_call); - return vm.exec(&mut ext).finalize(ext); + if stack_depth != depth_threshold { + self.call_with_stack_depth(params, substate, stack_depth, tracer, vm_tracer) + } else { + crossbeam::scope(|scope| { + scope.builder().stack_size(::std::cmp::max(self.schedule.max_depth.saturating_sub(depth_threshold) * STACK_SIZE_PER_DEPTH, local_stack_size)).spawn(move || { + self.call_with_stack_depth(params, substate, stack_depth, tracer, vm_tracer) + }).expect("Sub-thread creation cannot fail; the host might run out of resources; qed") + }).join() } - - // Start in new thread with stack size needed up to max depth - crossbeam::scope(|scope| { - let vm_factory = self.state.vm_factory(); - let origin_info = OriginInfo::from(¶ms); - - scope.builder().stack_size(::std::cmp::max(self.schedule.max_depth.saturating_sub(depth_threshold) * STACK_SIZE_PER_DEPTH, local_stack_size)).spawn(move || { - let mut vm = vm_factory.create(params, self.schedule, self.depth); - let mut ext = self.as_externalities(origin_info, unconfirmed_substate, output_policy, tracer, vm_tracer, static_call); - vm.exec(&mut ext).finalize(ext) - }).expect("Sub-thread creation cannot fail; the host might run out of resources; qed") - }).join() } /// Calls contract function with given contract params. - /// NOTE. It does not finalize the transaction (doesn't do refunds, nor suicides). - /// Modifies the substate and the output. - /// Returns either gas_left or `vm::Error`. pub fn call( &mut self, params: ActionParams, @@ -394,150 +991,83 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> { tracer: &mut T, vm_tracer: &mut V ) -> vm::Result where T: Tracer, V: VMTracer { + self.call_with_stack_depth(params, substate, 0, tracer, vm_tracer) + } - trace!("Executive::call(params={:?}) self.env_info={:?}, static={}", params, self.info, self.static_flag); - if (params.call_type == CallType::StaticCall || - ((params.call_type == CallType::Call) && - self.static_flag)) - && params.value.value() > 0.into() { - return Err(vm::Error::MutableCallInStaticContext); - } + /// Creates contract with given contract params and stack depth. + /// NOTE. It does not finalize the transaction (doesn't do refunds, nor suicides). + /// Modifies the substate. + pub fn create_with_stack_depth( + &mut self, + params: ActionParams, + substate: &mut Substate, + stack_depth: usize, + tracer: &mut T, + vm_tracer: &mut V, + ) -> vm::Result where T: Tracer, V: VMTracer { + tracer.prepare_trace_create(¶ms); + vm_tracer.prepare_subtrace(params.code.as_ref().map_or_else(|| &[] as &[u8], |d| &*d as &[u8])); - // backup used in case of running out of gas - self.state.checkpoint(); + let address = params.address; + let gas = params.gas; - let schedule = self.schedule; + let vm_factory = self.state.vm_factory(); + let result = CallCreateExecutive::new_create_raw( + params, + self.info, + self.machine, + self.schedule, + &vm_factory, + self.depth, + stack_depth, + self.static_flag + ).consume(self.state, substate, tracer, vm_tracer); - // at first, transfer value to destination - if let ActionValue::Transfer(val) = params.value { - self.state.transfer_balance(¶ms.sender, ¶ms.address, &val, substate.to_cleanup_mode(&schedule))?; - } - - // if destination is builtin, try to execute it - if let Some(builtin) = self.machine.builtin(¶ms.code_address, self.info.number) { - // Engines aren't supposed to return builtins until activation, but - // prefer to fail rather than silently break consensus. - if !builtin.is_active(self.info.number) { - panic!("Consensus failure: engine implementation prematurely enabled built-in at {}", params.code_address); - } - - let default = []; - let data = if let Some(ref d) = params.data { d as &[u8] } else { &default as &[u8] }; - - let cost = builtin.cost(data); - if cost <= params.gas { - let mut builtin_out_buffer = Vec::new(); - let result = { - let mut builtin_output = BytesRef::Flexible(&mut builtin_out_buffer); - builtin.execute(data, &mut builtin_output) - }; - if let Err(e) = result { - self.state.revert_to_checkpoint(); - let evm_err: vm::Error = e.into(); - let trace_info = tracer.prepare_trace_call(¶ms); - tracer.trace_failed_call( - trace_info, - vec![], - evm_err.clone().into() - ); - Err(evm_err) - } else { - self.state.discard_checkpoint(); - - // Trace only top level calls and calls with balance transfer to builtins. The reason why we don't - // trace all internal calls to builtin contracts is that memcpy (IDENTITY) is a heavily used - // function. - let is_transferred = match params.value { - ActionValue::Transfer(value) => value != U256::zero(), - ActionValue::Apparent(_) => false, - }; - if self.depth == 0 || is_transferred { - let trace_info = tracer.prepare_trace_call(¶ms); - tracer.trace_call( - trace_info, - cost, - &builtin_out_buffer, - vec![] - ); - } - - let out_len = builtin_out_buffer.len(); - Ok(FinalizationResult { - gas_left: params.gas - cost, - return_data: ReturnData::new(builtin_out_buffer, 0, out_len), - apply_state: true, - }) - } - } else { - // just drain the whole gas - self.state.revert_to_checkpoint(); - - let trace_info = tracer.prepare_trace_call(¶ms); - tracer.trace_failed_call( - trace_info, - vec![], - vm::Error::OutOfGas.into() + match result { + Ok(ref val) if val.apply_state => { + tracer.done_trace_create( + gas - val.gas_left, + &val.return_data, + address, ); + }, + Ok(_) => { + tracer.done_trace_failed(&vm::Error::Reverted); + }, + Err(ref err) => { + tracer.done_trace_failed(err); + }, + } + vm_tracer.done_subtrace(); - Err(vm::Error::OutOfGas) - } + result + } + + /// Creates contract with given contract params, if the stack depth is above a threshold, create a new thread to + /// execute it. + pub fn create_with_crossbeam( + &mut self, + params: ActionParams, + substate: &mut Substate, + stack_depth: usize, + tracer: &mut T, + vm_tracer: &mut V, + ) -> vm::Result where T: Tracer, V: VMTracer { + let local_stack_size = ::io::LOCAL_STACK_SIZE.with(|sz| sz.get()); + let depth_threshold = local_stack_size.saturating_sub(STACK_SIZE_ENTRY_OVERHEAD) / STACK_SIZE_PER_DEPTH; + + if stack_depth != depth_threshold { + self.create_with_stack_depth(params, substate, stack_depth, tracer, vm_tracer) } else { - let trace_info = tracer.prepare_trace_call(¶ms); - 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 conditional on params.code.is_some(); qed")); - - let res = { - self.exec_vm(params, &mut unconfirmed_substate, OutputPolicy::Return, &mut subtracer, &mut subvmtracer) - }; - - vm_tracer.done_subtrace(subvmtracer); - - trace!(target: "executive", "res={:?}", res); - - let traces = subtracer.drain(); - match res { - Ok(ref res) if res.apply_state => { - tracer.trace_call( - trace_info, - gas - res.gas_left, - &res.return_data, - traces - ); - }, - Ok(_) => tracer.trace_failed_call(trace_info, traces, vm::Error::Reverted.into()), - Err(ref e) => tracer.trace_failed_call(trace_info, traces, e.into()), - }; - - trace!(target: "executive", "substate={:?}; unconfirmed_substate={:?}\n", substate, unconfirmed_substate); - - self.enact_result(&res, substate, unconfirmed_substate); - trace!(target: "executive", "enacted: substate={:?}\n", substate); - res - } else { - // otherwise it's just a basic transaction, only do tracing, if necessary. - self.state.discard_checkpoint(); - - tracer.trace_call(trace_info, U256::zero(), &[], vec![]); - Ok(FinalizationResult { - gas_left: params.gas, - return_data: ReturnData::empty(), - apply_state: true, - }) - } + crossbeam::scope(|scope| { + scope.builder().stack_size(::std::cmp::max(self.schedule.max_depth.saturating_sub(depth_threshold) * STACK_SIZE_PER_DEPTH, local_stack_size)).spawn(move || { + self.create_with_stack_depth(params, substate, stack_depth, tracer, vm_tracer) + }).expect("Sub-thread creation cannot fail; the host might run out of resources; qed") + }).join() } } /// 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, @@ -545,73 +1075,7 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> { tracer: &mut T, vm_tracer: &mut V, ) -> vm::Result where T: Tracer, V: VMTracer { - - // EIP-684: If a contract creation is attempted, due to either a creation transaction or the - // CREATE (or future CREATE2) opcode, and the destination address already has either - // nonzero nonce, or nonempty code, then the creation throws immediately, with exactly - // the same behavior as would arise if the first byte in the init code were an invalid - // opcode. This applies retroactively starting from genesis. - if self.state.exists_and_has_code_or_nonce(¶ms.address)? { - return Err(vm::Error::OutOfGas); - } - - trace!("Executive::create(params={:?}) self.env_info={:?}, static={}", params, self.info, self.static_flag); - if params.call_type == CallType::StaticCall || self.static_flag { - let trace_info = tracer.prepare_trace_create(¶ms); - tracer.trace_failed_create(trace_info, vec![], vm::Error::MutableCallInStaticContext.into()); - return Err(vm::Error::MutableCallInStaticContext); - } - - // backup used in case of running out of gas - self.state.checkpoint(); - - // part of substate that may be reverted - let mut unconfirmed_substate = Substate::new(); - - // create contract and transfer value to it if necessary - let schedule = self.schedule; - let nonce_offset = if schedule.no_empty {1} else {0}.into(); - let prev_bal = self.state.balance(¶ms.address)?; - if let ActionValue::Transfer(val) = params.value { - self.state.sub_balance(¶ms.sender, &val, &mut substate.to_cleanup_mode(&schedule))?; - self.state.new_contract(¶ms.address, val + prev_bal, nonce_offset)?; - } else { - self.state.new_contract(¶ms.address, prev_bal, nonce_offset)?; - } - - let trace_info = tracer.prepare_trace_create(¶ms); - let mut subtracer = tracer.subtracer(); - let gas = params.gas; - let created = params.address.clone(); - - let mut subvmtracer = vm_tracer.prepare_subtrace(params.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, - &mut subtracer, - &mut subvmtracer - ); - - vm_tracer.done_subtrace(subvmtracer); - - match res { - Ok(ref res) if res.apply_state => { - tracer.trace_create( - trace_info, - gas - res.gas_left, - &res.return_data, - created, - subtracer.drain() - ); - } - Ok(_) => tracer.trace_failed_create(trace_info, subtracer.drain(), vm::Error::Reverted.into()), - Err(ref e) => tracer.trace_failed_create(trace_info, subtracer.drain(), e.into()) - }; - - self.enact_result(&res, substate, unconfirmed_substate); - res + self.create_with_stack_depth(params, substate, 0, tracer, vm_tracer) } /// Finalizes the transaction (does refunds and suicides). @@ -694,28 +1158,6 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> { }, } } - - fn enact_result(&mut self, result: &vm::Result, substate: &mut Substate, un_substate: Substate) { - match *result { - Err(vm::Error::OutOfGas) - | Err(vm::Error::BadJumpDestination {..}) - | Err(vm::Error::BadInstruction {.. }) - | Err(vm::Error::StackUnderflow {..}) - | Err(vm::Error::BuiltIn {..}) - | Err(vm::Error::Wasm {..}) - | Err(vm::Error::OutOfStack {..}) - | Err(vm::Error::MutableCallInStaticContext) - | Err(vm::Error::OutOfBounds) - | Err(vm::Error::Reverted) - | Ok(FinalizationResult { apply_state: false, .. }) => { - self.state.revert_to_checkpoint(); - }, - Ok(_) | Err(vm::Error::Internal(_)) => { - self.state.discard_checkpoint(); - substate.accrue(un_substate); - } - } - } } #[cfg(test)] diff --git a/ethcore/src/externalities.rs b/ethcore/src/externalities.rs index 5c7c95c66..df8a9c75a 100644 --- a/ethcore/src/externalities.rs +++ b/ethcore/src/externalities.rs @@ -25,9 +25,8 @@ use executive::*; use vm::{ self, ActionParams, ActionValue, EnvInfo, CallType, Schedule, Ext, ContractCreateResult, MessageCallResult, CreateContractAddress, - ReturnData + ReturnData, TrapKind }; -use evm::FinalizationResult; use transaction::UNSIGNED_SENDER; use trace::{Tracer, VMTracer}; @@ -67,7 +66,8 @@ pub struct Externalities<'a, T: 'a, V: 'a, B: 'a> { state: &'a mut State, env_info: &'a EnvInfo, depth: usize, - origin_info: OriginInfo, + stack_depth: usize, + origin_info: &'a OriginInfo, substate: &'a mut Substate, machine: &'a Machine, schedule: &'a Schedule, @@ -87,7 +87,8 @@ impl<'a, T: 'a, V: 'a, B: 'a> Externalities<'a, T, V, B> machine: &'a Machine, schedule: &'a Schedule, depth: usize, - origin_info: OriginInfo, + stack_depth: usize, + origin_info: &'a OriginInfo, substate: &'a mut Substate, output: OutputPolicy, tracer: &'a mut T, @@ -98,6 +99,7 @@ impl<'a, T: 'a, V: 'a, B: 'a> Externalities<'a, T, V, B> state: state, env_info: env_info, depth: depth, + stack_depth: stack_depth, origin_info: origin_info, substate: substate, machine: machine, @@ -176,7 +178,7 @@ impl<'a, T: 'a, V: 'a, B: 'a> Ext for Externalities<'a, T, V, B> }; let mut ex = Executive::new(self.state, self.env_info, self.machine, self.schedule); - let r = ex.call(params, self.substate, self.tracer, self.vm_tracer); + let r = ex.call_with_crossbeam(params, self.substate, self.stack_depth + 1, self.tracer, self.vm_tracer); let output = match &r { Ok(ref r) => H256::from(&r.return_data[..32]), _ => H256::new(), @@ -206,14 +208,15 @@ impl<'a, T: 'a, V: 'a, B: 'a> Ext for Externalities<'a, T, V, B> gas: &U256, value: &U256, code: &[u8], - address_scheme: CreateContractAddress - ) -> ContractCreateResult { + address_scheme: CreateContractAddress, + trap: bool, + ) -> ::std::result::Result { // create new contract address let (address, code_hash) = match self.state.nonce(&self.origin_info.address) { Ok(nonce) => contract_address(address_scheme, &self.origin_info.address, &nonce, &code), Err(e) => { debug!(target: "ext", "Database corruption encountered: {:?}", e); - return ContractCreateResult::Failed + return Ok(ContractCreateResult::Failed) } }; @@ -237,23 +240,19 @@ impl<'a, T: 'a, V: 'a, B: 'a> Ext for Externalities<'a, T, V, B> if !self.schedule.keep_unsigned_nonce || params.sender != UNSIGNED_SENDER { if let Err(e) = self.state.inc_nonce(&self.origin_info.address) { debug!(target: "ext", "Database corruption encountered: {:?}", e); - return ContractCreateResult::Failed + return Ok(ContractCreateResult::Failed) } } } - let mut ex = Executive::from_parent(self.state, self.env_info, self.machine, self.schedule, self.depth, self.static_flag); + + if trap { + return Err(TrapKind::Create(params, address)); + } // TODO: handle internal error separately - match ex.create(params, self.substate, self.tracer, self.vm_tracer) { - Ok(FinalizationResult{ gas_left, apply_state: true, .. }) => { - self.substate.contracts_created.push(address.clone()); - ContractCreateResult::Created(address, gas_left) - }, - Ok(FinalizationResult{ gas_left, apply_state: false, return_data }) => { - ContractCreateResult::Reverted(gas_left, return_data) - }, - _ => ContractCreateResult::Failed, - } + let mut ex = Executive::from_parent(self.state, self.env_info, self.machine, self.schedule, self.depth, self.static_flag); + let out = ex.create_with_crossbeam(params, self.substate, self.stack_depth + 1, self.tracer, self.vm_tracer); + Ok(into_contract_create_result(out, &address, self.substate)) } fn call( @@ -264,8 +263,9 @@ impl<'a, T: 'a, V: 'a, B: 'a> Ext for Externalities<'a, T, V, B> value: Option, data: &[u8], code_address: &Address, - call_type: CallType - ) -> MessageCallResult { + call_type: CallType, + trap: bool, + ) -> ::std::result::Result { trace!(target: "externalities", "call"); let code_res = self.state.code(code_address) @@ -273,7 +273,7 @@ impl<'a, T: 'a, V: 'a, B: 'a> Ext for Externalities<'a, T, V, B> let (code, code_hash) = match code_res { Ok((code, hash)) => (code, hash), - Err(_) => return MessageCallResult::Failed, + Err(_) => return Ok(MessageCallResult::Failed), }; let mut params = ActionParams { @@ -295,13 +295,13 @@ impl<'a, T: 'a, V: 'a, B: 'a> Ext for Externalities<'a, T, V, B> params.value = ActionValue::Transfer(value); } - let mut ex = Executive::from_parent(self.state, self.env_info, self.machine, self.schedule, self.depth, self.static_flag); - - match ex.call(params, self.substate, self.tracer, self.vm_tracer) { - Ok(FinalizationResult{ gas_left, return_data, apply_state: true }) => MessageCallResult::Success(gas_left, return_data), - Ok(FinalizationResult{ gas_left, return_data, apply_state: false }) => MessageCallResult::Reverted(gas_left, return_data), - _ => MessageCallResult::Failed + if trap { + return Err(TrapKind::Call(params)); } + + let mut ex = Executive::from_parent(self.state, self.env_info, self.machine, self.schedule, self.depth, self.static_flag); + let out = ex.call_with_crossbeam(params, self.substate, self.stack_depth + 1, self.tracer, self.vm_tracer); + Ok(into_message_call_result(out)) } fn extcode(&self, address: &Address) -> vm::Result>> { @@ -406,12 +406,12 @@ impl<'a, T: 'a, V: 'a, B: 'a> Ext for Externalities<'a, T, V, B> self.vm_tracer.trace_next_instruction(pc, instruction, current_gas) } - fn trace_prepare_execute(&mut self, pc: usize, instruction: u8, gas_cost: U256) { - self.vm_tracer.trace_prepare_execute(pc, instruction, gas_cost) + fn trace_prepare_execute(&mut self, pc: usize, instruction: u8, gas_cost: U256, mem_written: Option<(usize, usize)>, store_written: Option<(U256, U256)>) { + self.vm_tracer.trace_prepare_execute(pc, instruction, gas_cost, mem_written, store_written) } - 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) + fn trace_executed(&mut self, gas_used: U256, stack_push: &[U256], mem: &[u8]) { + self.vm_tracer.trace_executed(gas_used, stack_push, mem) } } @@ -480,8 +480,9 @@ mod tests { let state = &mut setup.state; let mut tracer = NoopTracer; let mut vm_tracer = NoopVMTracer; + let origin_info = get_test_origin(); - let ext = Externalities::new(state, &setup.env_info, &setup.machine, &setup.schedule, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract, &mut tracer, &mut vm_tracer, false); + let ext = Externalities::new(state, &setup.env_info, &setup.machine, &setup.schedule, 0, 0, &origin_info, &mut setup.sub_state, OutputPolicy::InitContract, &mut tracer, &mut vm_tracer, false); assert_eq!(ext.env_info().number, 100); } @@ -492,8 +493,9 @@ mod tests { let state = &mut setup.state; let mut tracer = NoopTracer; let mut vm_tracer = NoopVMTracer; + let origin_info = get_test_origin(); - let mut ext = Externalities::new(state, &setup.env_info, &setup.machine, &setup.schedule, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract, &mut tracer, &mut vm_tracer, false); + let mut ext = Externalities::new(state, &setup.env_info, &setup.machine, &setup.schedule, 0, 0, &origin_info, &mut setup.sub_state, OutputPolicy::InitContract, &mut tracer, &mut vm_tracer, false); let hash = ext.blockhash(&"0000000000000000000000000000000000000000000000000000000000120000".parse::().unwrap()); @@ -516,8 +518,9 @@ mod tests { let state = &mut setup.state; let mut tracer = NoopTracer; let mut vm_tracer = NoopVMTracer; + let origin_info = get_test_origin(); - let mut ext = Externalities::new(state, &setup.env_info, &setup.machine, &setup.schedule, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract, &mut tracer, &mut vm_tracer, false); + let mut ext = Externalities::new(state, &setup.env_info, &setup.machine, &setup.schedule, 0, 0, &origin_info, &mut setup.sub_state, OutputPolicy::InitContract, &mut tracer, &mut vm_tracer, false); let hash = ext.blockhash(&"0000000000000000000000000000000000000000000000000000000000120000".parse::().unwrap()); @@ -531,8 +534,9 @@ mod tests { let state = &mut setup.state; let mut tracer = NoopTracer; let mut vm_tracer = NoopVMTracer; + let origin_info = get_test_origin(); - let mut ext = Externalities::new(state, &setup.env_info, &setup.machine, &setup.schedule, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract, &mut tracer, &mut vm_tracer, false); + let mut ext = Externalities::new(state, &setup.env_info, &setup.machine, &setup.schedule, 0, 0, &origin_info, &mut setup.sub_state, OutputPolicy::InitContract, &mut tracer, &mut vm_tracer, false); // this should panic because we have no balance on any account ext.call( @@ -542,8 +546,9 @@ mod tests { Some("0000000000000000000000000000000000000000000000000000000000150000".parse::().unwrap()), &[], &Address::new(), - CallType::Call - ); + CallType::Call, + false, + ).ok().unwrap(); } #[test] @@ -555,9 +560,10 @@ mod tests { let state = &mut setup.state; let mut tracer = NoopTracer; let mut vm_tracer = NoopVMTracer; + let origin_info = get_test_origin(); { - let mut ext = Externalities::new(state, &setup.env_info, &setup.machine, &setup.schedule, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract, &mut tracer, &mut vm_tracer, false); + let mut ext = Externalities::new(state, &setup.env_info, &setup.machine, &setup.schedule, 0, 0, &origin_info, &mut setup.sub_state, OutputPolicy::InitContract, &mut tracer, &mut vm_tracer, false); ext.log(log_topics, &log_data).unwrap(); } @@ -572,9 +578,10 @@ mod tests { let state = &mut setup.state; let mut tracer = NoopTracer; let mut vm_tracer = NoopVMTracer; + let origin_info = get_test_origin(); { - let mut ext = Externalities::new(state, &setup.env_info, &setup.machine, &setup.schedule, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract, &mut tracer, &mut vm_tracer, false); + let mut ext = Externalities::new(state, &setup.env_info, &setup.machine, &setup.schedule, 0, 0, &origin_info, &mut setup.sub_state, OutputPolicy::InitContract, &mut tracer, &mut vm_tracer, false); ext.suicide(refund_account).unwrap(); } @@ -589,11 +596,12 @@ mod tests { let state = &mut setup.state; let mut tracer = NoopTracer; let mut vm_tracer = NoopVMTracer; + let origin_info = get_test_origin(); let address = { - let mut ext = Externalities::new(state, &setup.env_info, &setup.machine, &setup.schedule, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract, &mut tracer, &mut vm_tracer, false); - match ext.create(&U256::max_value(), &U256::zero(), &[], CreateContractAddress::FromSenderAndNonce) { - ContractCreateResult::Created(address, _) => address, + let mut ext = Externalities::new(state, &setup.env_info, &setup.machine, &setup.schedule, 0, 0, &origin_info, &mut setup.sub_state, OutputPolicy::InitContract, &mut tracer, &mut vm_tracer, false); + match ext.create(&U256::max_value(), &U256::zero(), &[], CreateContractAddress::FromSenderAndNonce, false) { + Ok(ContractCreateResult::Created(address, _)) => address, _ => panic!("Test create failed; expected Created, got Failed/Reverted."), } }; @@ -609,12 +617,13 @@ mod tests { let state = &mut setup.state; let mut tracer = NoopTracer; let mut vm_tracer = NoopVMTracer; + let origin_info = get_test_origin(); let address = { - let mut ext = Externalities::new(state, &setup.env_info, &setup.machine, &setup.schedule, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract, &mut tracer, &mut vm_tracer, false); + let mut ext = Externalities::new(state, &setup.env_info, &setup.machine, &setup.schedule, 0, 0, &origin_info, &mut setup.sub_state, OutputPolicy::InitContract, &mut tracer, &mut vm_tracer, false); - match ext.create(&U256::max_value(), &U256::zero(), &[], CreateContractAddress::FromSenderSaltAndCodeHash(H256::default())) { - ContractCreateResult::Created(address, _) => address, + match ext.create(&U256::max_value(), &U256::zero(), &[], CreateContractAddress::FromSenderSaltAndCodeHash(H256::default()), false) { + Ok(ContractCreateResult::Created(address, _)) => address, _ => panic!("Test create failed; expected Created, got Failed/Reverted."), } }; diff --git a/ethcore/src/factory.rs b/ethcore/src/factory.rs index dbfdcffc7..9a69e0cf0 100644 --- a/ethcore/src/factory.rs +++ b/ethcore/src/factory.rs @@ -18,7 +18,7 @@ use trie::TrieFactory; use ethtrie::RlpCodec; use account_db::Factory as AccountFactory; use evm::{Factory as EvmFactory, VMType}; -use vm::{Vm, ActionParams, Schedule}; +use vm::{Exec, ActionParams, Schedule}; use wasm::WasmInterpreter; use keccak_hasher::KeccakHasher; @@ -31,7 +31,7 @@ pub struct VmFactory { } impl VmFactory { - pub fn create(&self, params: ActionParams, schedule: &Schedule, depth: usize) -> Box { + pub fn create(&self, params: ActionParams, schedule: &Schedule, depth: usize) -> Box { if schedule.wasm.is_some() && params.code.as_ref().map_or(false, |code| code.len() > 4 && &code[0..4] == WASM_MAGIC_NUMBER) { Box::new(WasmInterpreter::new(params)) } else { diff --git a/ethcore/src/json_tests/executive.rs b/ethcore/src/json_tests/executive.rs index 78ef3e22d..0f8903aed 100644 --- a/ethcore/src/json_tests/executive.rs +++ b/ethcore/src/json_tests/executive.rs @@ -88,7 +88,7 @@ impl<'a, T: 'a, V: 'a, B: 'a> TestExt<'a, T, V, B> machine: &'a Machine, schedule: &'a Schedule, depth: usize, - origin_info: OriginInfo, + origin_info: &'a OriginInfo, substate: &'a mut Substate, output: OutputPolicy, address: Address, @@ -98,7 +98,7 @@ impl<'a, T: 'a, V: 'a, B: 'a> TestExt<'a, T, V, B> let static_call = false; Ok(TestExt { nonce: state.nonce(&address)?, - ext: Externalities::new(state, info, machine, schedule, depth, origin_info, substate, output, tracer, vm_tracer, static_call), + ext: Externalities::new(state, info, machine, schedule, depth, 0, origin_info, substate, output, tracer, vm_tracer, static_call), callcreates: vec![], sender: address, }) @@ -140,7 +140,14 @@ impl<'a, T: 'a, V: 'a, B: 'a> Ext for TestExt<'a, T, V, B> self.ext.blockhash(number) } - fn create(&mut self, gas: &U256, value: &U256, code: &[u8], address: CreateContractAddress) -> ContractCreateResult { + fn create( + &mut self, + gas: &U256, + value: &U256, + code: &[u8], + address: CreateContractAddress, + _trap: bool + ) -> Result { self.callcreates.push(CallCreate { data: code.to_vec(), destination: None, @@ -148,25 +155,27 @@ impl<'a, T: 'a, V: 'a, B: 'a> Ext for TestExt<'a, T, V, B> value: *value }); let contract_address = contract_address(address, &self.sender, &self.nonce, &code).0; - ContractCreateResult::Created(contract_address, *gas) + Ok(ContractCreateResult::Created(contract_address, *gas)) } - fn call(&mut self, + fn call( + &mut self, gas: &U256, _sender_address: &Address, receive_address: &Address, value: Option, data: &[u8], _code_address: &Address, - _call_type: CallType - ) -> MessageCallResult { + _call_type: CallType, + _trap: bool + ) -> Result { self.callcreates.push(CallCreate { data: data.to_vec(), destination: Some(receive_address.clone()), gas_limit: *gas, value: value.unwrap() }); - MessageCallResult::Success(*gas, ReturnData::empty()) + Ok(MessageCallResult::Success(*gas, ReturnData::empty())) } fn extcode(&self, address: &Address) -> vm::Result>> { @@ -270,6 +279,7 @@ fn do_json_test_for(vm_type: &VMType, json_data: &[u8] let mut tracer = NoopTracer; let mut vm_tracer = NoopVMTracer; let vm_factory = state.vm_factory(); + let origin_info = OriginInfo::from(¶ms); // execute let (res, callcreates) = { @@ -280,7 +290,7 @@ fn do_json_test_for(vm_type: &VMType, json_data: &[u8] &machine, &schedule, 0, - OriginInfo::from(¶ms), + &origin_info, &mut substate, OutputPolicy::Return, params.address.clone(), @@ -288,7 +298,7 @@ fn do_json_test_for(vm_type: &VMType, json_data: &[u8] &mut vm_tracer, )); let mut evm = vm_factory.create(params, &schedule, 0); - let res = evm.exec(&mut ex); + let res = evm.exec(&mut ex).ok().expect("TestExt never trap; resume error never happens; qed"); // a return in finalize will not alter callcreates let callcreates = ex.callcreates.clone(); (res.finalize(ex), callcreates) diff --git a/ethcore/src/trace/db.rs b/ethcore/src/trace/db.rs index b74ff7d55..12e628269 100644 --- a/ethcore/src/trace/db.rs +++ b/ethcore/src/trace/db.rs @@ -15,7 +15,7 @@ // along with Parity. If not, see . //! Trace database. -use std::collections::{HashMap, VecDeque}; +use std::collections::HashMap; use std::sync::Arc; use blockchain::{BlockChainDB}; use heapsize::HeapSizeOf; @@ -227,13 +227,12 @@ impl TraceDatabase for TraceDB where T: DatabaseExtras { } fn trace(&self, block_number: BlockNumber, tx_position: usize, trace_position: Vec) -> Option { - let trace_position_deq = VecDeque::from(trace_position); self.extras.block_hash(block_number) .and_then(|block_hash| self.transactions_traces(&block_hash) .and_then(|traces| traces.into_iter().nth(tx_position)) .map(Into::>::into) // this may and should be optimized - .and_then(|traces| traces.into_iter().find(|trace| trace.trace_address == trace_position_deq)) + .and_then(|traces| traces.into_iter().find(|trace| trace.trace_address == trace_position)) .map(|trace| { let tx_hash = self.extras.transaction_hash(block_number, tx_position) .expect("Expected to find transaction hash. Database is probably corrupted"); diff --git a/ethcore/src/trace/executive_tracer.rs b/ethcore/src/trace/executive_tracer.rs index 227b3a39f..789f40bd3 100644 --- a/ethcore/src/trace/executive_tracer.rs +++ b/ethcore/src/trace/executive_tracer.rs @@ -17,159 +17,175 @@ //! Simple executive tracer. use ethereum_types::{U256, Address}; -use vm::ActionParams; +use vm::{Error as VmError, ActionParams}; use trace::trace::{Call, Create, Action, Res, CreateResult, CallResult, VMTrace, VMOperation, VMExecutedOperation, MemoryDiff, StorageDiff, Suicide, Reward, RewardType}; -use trace::{Tracer, VMTracer, FlatTrace, TraceError}; +use trace::{Tracer, VMTracer, FlatTrace}; /// Simple executive tracer. Traces all calls and creates. Ignores delegatecalls. #[derive(Default)] pub struct ExecutiveTracer { traces: Vec, -} - -fn top_level_subtraces(traces: &[FlatTrace]) -> usize { - traces.iter().filter(|t| t.trace_address.is_empty()).count() -} - -fn prefix_subtrace_addresses(mut traces: Vec) -> Vec { - // input traces are expected to be ordered like - // [] - // [0] - // [0, 0] - // [0, 1] - // [] - // [0] - // - // so they can be transformed to - // - // [0] - // [0, 0] - // [0, 0, 0] - // [0, 0, 1] - // [1] - // [1, 0] - let mut current_subtrace_index = 0; - let mut first = true; - for trace in &mut traces { - match (first, trace.trace_address.is_empty()) { - (true, _) => first = false, - (_, true) => current_subtrace_index += 1, - _ => {} - } - trace.trace_address.push_front(current_subtrace_index); - } - traces -} - -#[test] -fn should_prefix_address_properly() { - use super::trace::{Action, Res, Suicide}; - - let f = |v: Vec| FlatTrace { - action: Action::Suicide(Suicide { - address: Default::default(), - balance: Default::default(), - refund_address: Default::default(), - }), - result: Res::None, - subtraces: 0, - trace_address: v.into_iter().collect(), - }; - let t = vec![vec![], vec![0], vec![0, 0], vec![0], vec![], vec![], vec![0], vec![]].into_iter().map(&f).collect(); - let t = prefix_subtrace_addresses(t); - assert_eq!(t, vec![vec![0], vec![0, 0], vec![0, 0, 0], vec![0, 0], vec![1], vec![2], vec![2, 0], vec![3]].into_iter().map(&f).collect::>()); + index_stack: Vec, + vecindex_stack: Vec, + sublen_stack: Vec, + skip_one: bool, } impl Tracer for ExecutiveTracer { type Output = FlatTrace; - fn prepare_trace_call(&self, params: &ActionParams) -> Option { - Some(Call::from(params.clone())) - } + fn prepare_trace_call(&mut self, params: &ActionParams, depth: usize, is_builtin: bool) { + assert!(!self.skip_one, "skip_one is used only for builtin contracts that do not have subsequent calls; in prepare_trace_call it cannot be true; qed"); - fn prepare_trace_create(&self, params: &ActionParams) -> Option { - Some(Create::from(params.clone())) - } + if depth != 0 && is_builtin && params.value.value() == U256::zero() { + self.skip_one = true; + return; + } + + if let Some(parentlen) = self.sublen_stack.last_mut() { + *parentlen += 1; + } - fn trace_call(&mut self, call: Option, gas_used: U256, output: &[u8], subs: Vec) { let trace = FlatTrace { - trace_address: Default::default(), - subtraces: top_level_subtraces(&subs), - action: Action::Call(call.expect("self.prepare_trace_call().is_some(): so we must be tracing: qed")), + trace_address: self.index_stack.clone(), + subtraces: self.sublen_stack.last().cloned().unwrap_or(0), + action: Action::Call(Call::from(params.clone())), result: Res::Call(CallResult { - gas_used: gas_used, - output: output.into() + gas_used: U256::zero(), + output: Vec::new() }), }; - debug!(target: "trace", "Traced call {:?}", trace); + self.vecindex_stack.push(self.traces.len()); self.traces.push(trace); - self.traces.extend(prefix_subtrace_addresses(subs)); + self.index_stack.push(0); + self.sublen_stack.push(0); } - fn trace_create(&mut self, create: Option, gas_used: U256, code: &[u8], address: Address, subs: Vec) { + fn prepare_trace_create(&mut self, params: &ActionParams) { + assert!(!self.skip_one, "skip_one is used only for builtin contracts that do not have subsequent calls; in prepare_trace_create it cannot be true; qed"); + + if let Some(parentlen) = self.sublen_stack.last_mut() { + *parentlen += 1; + } + let trace = FlatTrace { - subtraces: top_level_subtraces(&subs), - action: Action::Create(create.expect("self.prepare_trace_create().is_some(): so we must be tracing: qed")), + trace_address: self.index_stack.clone(), + subtraces: self.sublen_stack.last().cloned().unwrap_or(0), + action: Action::Create(Create::from(params.clone())), result: Res::Create(CreateResult { - gas_used: gas_used, - code: code.into(), - address: address + gas_used: U256::zero(), + code: Vec::new(), + address: Address::default(), }), - trace_address: Default::default(), }; - debug!(target: "trace", "Traced create {:?}", trace); + self.vecindex_stack.push(self.traces.len()); self.traces.push(trace); - self.traces.extend(prefix_subtrace_addresses(subs)); + self.index_stack.push(0); + self.sublen_stack.push(0); } - fn trace_failed_call(&mut self, call: Option, subs: Vec, error: TraceError) { - let trace = FlatTrace { - trace_address: Default::default(), - subtraces: top_level_subtraces(&subs), - action: Action::Call(call.expect("self.prepare_trace_call().is_some(): so we must be tracing: qed")), - result: Res::FailedCall(error), - }; - debug!(target: "trace", "Traced failed call {:?}", trace); - self.traces.push(trace); - self.traces.extend(prefix_subtrace_addresses(subs)); + fn done_trace_call(&mut self, gas_used: U256, output: &[u8]) { + if self.skip_one { + self.skip_one = false; + return; + } + + let vecindex = self.vecindex_stack.pop().expect("Executive invoked prepare_trace_call before this function; vecindex_stack is never empty; qed"); + let sublen = self.sublen_stack.pop().expect("Executive invoked prepare_trace_call before this function; sublen_stack is never empty; qed"); + self.index_stack.pop(); + + self.traces[vecindex].result = Res::Call(CallResult { + gas_used, + output: output.into(), + }); + self.traces[vecindex].subtraces = sublen; + + if let Some(index) = self.index_stack.last_mut() { + *index += 1; + } } - fn trace_failed_create(&mut self, create: Option, subs: Vec, error: TraceError) { - let trace = FlatTrace { - subtraces: top_level_subtraces(&subs), - action: Action::Create(create.expect("self.prepare_trace_create().is_some(): so we must be tracing: qed")), - result: Res::FailedCreate(error), - trace_address: Default::default(), + fn done_trace_create(&mut self, gas_used: U256, code: &[u8], address: Address) { + assert!(!self.skip_one, "skip_one is only set with prepare_trace_call for builtin contracts with no subsequent calls; skip_one cannot be true after the same level prepare_trace_create; qed"); + + let vecindex = self.vecindex_stack.pop().expect("Executive invoked prepare_trace_create before this function; vecindex_stack is never empty; qed"); + let sublen = self.sublen_stack.pop().expect("Executive invoked prepare_trace_create before this function; sublen_stack is never empty; qed"); + self.index_stack.pop(); + + self.traces[vecindex].result = Res::Create(CreateResult { + gas_used, address, + code: code.into(), + }); + self.traces[vecindex].subtraces = sublen; + + if let Some(index) = self.index_stack.last_mut() { + *index += 1; + } + } + + fn done_trace_failed(&mut self, error: &VmError) { + if self.skip_one { + self.skip_one = false; + return; + } + + let vecindex = self.vecindex_stack.pop().expect("Executive invoked prepare_trace_create/call before this function; vecindex_stack is never empty; qed"); + let sublen = self.sublen_stack.pop().expect("Executive invoked prepare_trace_create/call before this function; vecindex_stack is never empty; qed"); + self.index_stack.pop(); + + let is_create = match self.traces[vecindex].action { + Action::Create(_) => true, + _ => false, }; - debug!(target: "trace", "Traced failed create {:?}", trace); - self.traces.push(trace); - self.traces.extend(prefix_subtrace_addresses(subs)); + + if is_create { + self.traces[vecindex].result = Res::FailedCreate(error.into()); + } else { + self.traces[vecindex].result = Res::FailedCall(error.into()); + } + self.traces[vecindex].subtraces = sublen; + + if let Some(index) = self.index_stack.last_mut() { + *index += 1; + } } fn trace_suicide(&mut self, address: Address, balance: U256, refund_address: Address) { + if let Some(parentlen) = self.sublen_stack.last_mut() { + *parentlen += 1; + } + let trace = FlatTrace { subtraces: 0, action: Action::Suicide(Suicide { address, refund_address, balance } ), result: Res::None, - trace_address: Default::default(), + trace_address: self.index_stack.clone(), }; debug!(target: "trace", "Traced suicide {:?}", trace); self.traces.push(trace); + + if let Some(index) = self.index_stack.last_mut() { + *index += 1; + } } fn trace_reward(&mut self, author: Address, value: U256, reward_type: RewardType) { + if let Some(parentlen) = self.sublen_stack.last_mut() { + *parentlen += 1; + } + let trace = FlatTrace { subtraces: 0, action: Action::Reward(Reward { author, value, reward_type } ), result: Res::None, - trace_address: Default::default(), + trace_address: self.index_stack.clone(), }; debug!(target: "trace", "Traced reward {:?}", trace); self.traces.push(trace); - } - fn subtracer(&self) -> Self { - ExecutiveTracer::default() + if let Some(index) = self.index_stack.last_mut() { + *index += 1; + } } fn drain(self) -> Vec { @@ -180,6 +196,9 @@ impl Tracer for ExecutiveTracer { /// Simple VM tracer. Traces all operations. pub struct ExecutiveVMTracer { data: VMTrace, + depth: usize, + last_mem_written: Option<(usize, usize)>, + last_store_written: Option<(U256, U256)>, } impl ExecutiveVMTracer { @@ -191,7 +210,18 @@ impl ExecutiveVMTracer { code: vec![], operations: vec![Default::default()], // prefill with a single entry so that prepare_subtrace can get the parent_step subs: vec![], - } + }, + depth: 0, + last_mem_written: None, + last_store_written: None, + } + } + + fn with_trace_in_depth(trace: &mut VMTrace, depth: usize, f: F) { + if depth == 0 { + f(trace); + } else { + Self::with_trace_in_depth(trace.subs.last_mut().expect("self.depth is incremented with prepare_subtrace; a subtrace is always pushed; self.depth cannot be greater than subtrace stack; qed"), depth - 1, f); } } } @@ -201,37 +231,77 @@ impl VMTracer for ExecutiveVMTracer { fn trace_next_instruction(&mut self, _pc: usize, _instruction: u8, _current_gas: U256) -> bool { true } - fn trace_prepare_execute(&mut self, pc: usize, instruction: u8, gas_cost: U256) { - self.data.operations.push(VMOperation { - pc: pc, - instruction: instruction, - gas_cost: gas_cost, - executed: None, + fn trace_prepare_execute(&mut self, pc: usize, instruction: u8, gas_cost: U256, mem_written: Option<(usize, usize)>, store_written: Option<(U256, U256)>) { + Self::with_trace_in_depth(&mut self.data, self.depth, move |trace| { + trace.operations.push(VMOperation { + pc: pc, + instruction: instruction, + gas_cost: gas_cost, + executed: None, + }); + }); + self.last_mem_written = mem_written; + self.last_store_written = store_written; + } + + fn trace_executed(&mut self, gas_used: U256, stack_push: &[U256], mem: &[u8]) { + let mem_diff = self.last_mem_written.take().map(|(o, s)| (o, &(mem[o..o+s]))); + let store_diff = self.last_store_written.take(); + Self::with_trace_in_depth(&mut self.data, self.depth, move |trace| { + 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 }), + }; + trace.operations.last_mut().expect("trace_executed is always called after a trace_prepare_execute; trace.operations cannot be empty; qed").executed = Some(ex); }); } - 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(&mut self, code: &[u8]) { + Self::with_trace_in_depth(&mut self.data, self.depth, move |trace| { + let parent_step = trace.operations.len() - 1; // won't overflow since we must already have pushed an operation in trace_prepare_execute. + trace.subs.push(VMTrace { + parent_step, + code: code.to_vec(), + operations: vec![], + subs: vec![], + }); + }); + self.depth += 1; } - fn prepare_subtrace(&self, code: &[u8]) -> Self { - ExecutiveVMTracer { data: VMTrace { - parent_step: self.data.operations.len() - 1, // won't overflow since we must already have pushed an operation in trace_prepare_execute. - code: code.to_vec(), - operations: vec![], - subs: vec![], - }} - } - - fn done_subtrace(&mut self, sub: Self) { - self.data.subs.push(sub.data); + fn done_subtrace(&mut self) { + self.depth -= 1; } fn drain(mut self) -> Option { self.data.subs.pop() } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn should_prefix_address_properly() { + let mut tracer = ExecutiveTracer::default(); + + tracer.prepare_trace_call(&ActionParams::default(), 0, false); + tracer.prepare_trace_call(&ActionParams::default(), 1, false); + tracer.prepare_trace_call(&ActionParams::default(), 2, false); + tracer.done_trace_call(U256::zero(), &[]); + tracer.prepare_trace_call(&ActionParams::default(), 2, false); + tracer.done_trace_call(U256::zero(), &[]); + tracer.prepare_trace_call(&ActionParams::default(), 2, false); + tracer.done_trace_call(U256::zero(), &[]); + tracer.done_trace_call(U256::zero(), &[]); + tracer.done_trace_call(U256::zero(), &[]); + + let drained = tracer.drain(); + assert!(drained[0].trace_address.len() == 0); + assert_eq!(&drained[1].trace_address, &[0]); + assert_eq!(&drained[2].trace_address, &[0, 0]); + assert_eq!(&drained[3].trace_address, &[0, 1]); + assert_eq!(&drained[4].trace_address, &[0, 2]); + } +} diff --git a/ethcore/src/trace/mod.rs b/ethcore/src/trace/mod.rs index 670cc755f..1f6a77f2c 100644 --- a/ethcore/src/trace/mod.rs +++ b/ethcore/src/trace/mod.rs @@ -38,8 +38,7 @@ pub use self::types::filter::{Filter, AddressesFilter}; use ethereum_types::{H256, U256, Address}; use kvdb::DBTransaction; -use self::trace::{Call, Create}; -use vm::ActionParams; +use vm::{Error as VmError, ActionParams}; use header::BlockNumber; /// This trait is used by executive to build traces. @@ -47,48 +46,20 @@ pub trait Tracer: Send { /// Data returned when draining the Tracer. type Output; - /// Prepares call trace for given params. Noop tracer should return None. - /// - /// This is called before a call has been executed. - fn prepare_trace_call(&self, params: &ActionParams) -> Option; + /// Prepares call trace for given params. Would panic if prepare/done_trace are not balanced. + fn prepare_trace_call(&mut self, params: &ActionParams, depth: usize, is_builtin: bool); - /// Prepares create trace for given params. Noop tracer should return None. - /// - /// This is called before a create has been executed. - fn prepare_trace_create(&self, params: &ActionParams) -> Option; + /// Prepares create trace for given params. Would panic if prepare/done_trace are not balanced. + fn prepare_trace_create(&mut self, params: &ActionParams); - /// Stores trace call info. - /// - /// This is called after a call has completed successfully. - fn trace_call( - &mut self, - call: Option, - gas_used: U256, - output: &[u8], - subs: Vec, - ); + /// Finishes a successful call trace. Would panic if prepare/done_trace are not balanced. + fn done_trace_call(&mut self, gas_used: U256, output: &[u8]); - /// Stores trace create info. - /// - /// This is called after a create has completed successfully. - fn trace_create( - &mut self, - create: Option, - gas_used: U256, - code: &[u8], - address: Address, - subs: Vec - ); + /// Finishes a successful create trace. Would panic if prepare/done_trace are not balanced. + fn done_trace_create(&mut self, gas_used: U256, code: &[u8], address: Address); - /// Stores failed call trace. - /// - /// This is called after a call has completed erroneously. - fn trace_failed_call(&mut self, call: Option, subs: Vec, error: TraceError); - - /// Stores failed create trace. - /// - /// This is called after a create has completed erroneously. - fn trace_failed_create(&mut self, create: Option, subs: Vec, error: TraceError); + /// Finishes a failed trace. Would panic if prepare/done_trace are not balanced. + fn done_trace_failed(&mut self, error: &VmError); /// Stores suicide info. fn trace_suicide(&mut self, address: Address, balance: U256, refund_address: Address); @@ -96,9 +67,6 @@ pub trait Tracer: Send { /// Stores reward info. fn trace_reward(&mut self, author: Address, value: U256, reward_type: RewardType); - /// 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 drain(self) -> Vec; } @@ -115,16 +83,16 @@ pub trait VMTracer: Send { fn trace_next_instruction(&mut self, _pc: usize, _instruction: u8, _current_gas: U256) -> bool { false } /// Trace the preparation to execute a single valid instruction. - fn trace_prepare_execute(&mut self, _pc: usize, _instruction: u8, _gas_cost: U256) {} + fn trace_prepare_execute(&mut self, _pc: usize, _instruction: u8, _gas_cost: U256, _mem_written: Option<(usize, usize)>, _store_written: Option<(U256, U256)>) {} /// Trace the finalised execution of a single valid instruction. - fn trace_executed(&mut self, _gas_used: U256, _stack_push: &[U256], _mem_diff: Option<(usize, &[u8])>, _store_diff: Option<(U256, U256)>) {} + fn trace_executed(&mut self, _gas_used: U256, _stack_push: &[U256], _mem: &[u8]) {} /// Spawn subtracer which will be used to trace deeper levels of execution. - fn prepare_subtrace(&self, code: &[u8]) -> Self where Self: Sized; + fn prepare_subtrace(&mut self, _code: &[u8]) {} /// Finalize subtracer. - fn done_subtrace(&mut self, sub: Self) where Self: Sized; + fn done_subtrace(&mut self) {} /// Consumes self and returns the VM trace. fn drain(self) -> Option; diff --git a/ethcore/src/trace/noop_tracer.rs b/ethcore/src/trace/noop_tracer.rs index fdde9a6e3..0752f003b 100644 --- a/ethcore/src/trace/noop_tracer.rs +++ b/ethcore/src/trace/noop_tracer.rs @@ -17,9 +17,9 @@ //! Nonoperative tracer. use ethereum_types::{U256, Address}; -use vm::ActionParams; -use trace::{Tracer, VMTracer, FlatTrace, TraceError}; -use trace::trace::{Call, Create, VMTrace, RewardType}; +use vm::{Error as VmError, ActionParams}; +use trace::{Tracer, VMTracer, FlatTrace}; +use trace::trace::{VMTrace, RewardType}; /// Nonoperative tracer. Does not trace anything. pub struct NoopTracer; @@ -27,43 +27,14 @@ pub struct NoopTracer; impl Tracer for NoopTracer { type Output = FlatTrace; - fn prepare_trace_call(&self, _: &ActionParams) -> Option { - None - } - - fn prepare_trace_create(&self, _: &ActionParams) -> Option { - None - } - - fn trace_call(&mut self, call: Option, _: U256, _: &[u8], _: Vec) { - assert!(call.is_none(), "self.prepare_trace_call().is_none(): so we can't be tracing: qed"); - } - - fn trace_create(&mut self, create: Option, _: U256, _: &[u8], _: Address, _: Vec) { - assert!(create.is_none(), "self.prepare_trace_create().is_none(): so we can't be tracing: qed"); - } - - fn trace_failed_call(&mut self, call: Option, _: Vec, _: TraceError) { - assert!(call.is_none(), "self.prepare_trace_call().is_none(): so we can't be tracing: qed"); - } - - fn trace_failed_create(&mut self, create: Option, _: Vec, _: TraceError) { - assert!(create.is_none(), "self.prepare_trace_create().is_none(): so we can't be tracing: qed"); - } - - fn trace_suicide(&mut self, _address: Address, _balance: U256, _refund_address: Address) { - } - - fn trace_reward(&mut self, _: Address, _: U256, _: RewardType) { - } - - fn subtracer(&self) -> Self { - NoopTracer - } - - fn drain(self) -> Vec { - vec![] - } + fn prepare_trace_call(&mut self, _: &ActionParams, _: usize, _: bool) { } + fn prepare_trace_create(&mut self, _: &ActionParams) { } + fn done_trace_call(&mut self, _: U256, _: &[u8]) { } + fn done_trace_create(&mut self, _: U256, _: &[u8], _: Address) { } + fn done_trace_failed(&mut self, _: &VmError) { } + fn trace_suicide(&mut self, _: Address, _: U256, _: Address) { } + fn trace_reward(&mut self, _: Address, _: U256, _: RewardType) { } + fn drain(self) -> Vec { vec![] } } /// Nonoperative VM tracer. Does not trace anything. @@ -72,15 +43,5 @@ pub struct NoopVMTracer; impl VMTracer for NoopVMTracer { type Output = VMTrace; - fn trace_next_instruction(&mut self, _pc: usize, _instruction: u8, _current_gas: U256) -> bool { false } - - fn trace_prepare_execute(&mut self, _pc: usize, _instruction: u8, _gas_cost: U256) {} - - fn trace_executed(&mut self, _gas_used: U256, _stack_push: &[U256], _mem_diff: Option<(usize, &[u8])>, _store_diff: Option<(U256, U256)>) {} - - fn prepare_subtrace(&self, _code: &[u8]) -> Self { NoopVMTracer } - - fn done_subtrace(&mut self, _sub: Self) {} - fn drain(self) -> Option { None } } diff --git a/ethcore/src/trace/types/flat.rs b/ethcore/src/trace/types/flat.rs index 861069220..f4cbba665 100644 --- a/ethcore/src/trace/types/flat.rs +++ b/ethcore/src/trace/types/flat.rs @@ -16,7 +16,6 @@ //! Flat trace module -use std::collections::VecDeque; use rlp::{Rlp, RlpStream, Decodable, Encodable, DecoderError}; use heapsize::HeapSizeOf; use ethereum_types::Bloom; @@ -36,7 +35,7 @@ pub struct FlatTrace { /// Exact location of trace. /// /// [index in root, index in first CALL, index in second CALL, ...] - pub trace_address: VecDeque, + pub trace_address: Vec, } impl FlatTrace { diff --git a/ethcore/vm/src/error.rs b/ethcore/vm/src/error.rs index b5e337a75..fb4444104 100644 --- a/ethcore/vm/src/error.rs +++ b/ethcore/vm/src/error.rs @@ -16,9 +16,23 @@ //! VM errors module +use ::{ResumeCall, ResumeCreate}; +use ethereum_types::Address; +use action_params::ActionParams; use std::fmt; use ethtrie; +#[derive(Debug)] +pub enum TrapKind { + Call(ActionParams), + Create(ActionParams, Address), +} + +pub enum TrapError { + Call(ActionParams, Call), + Create(ActionParams, Address, Create), +} + /// VM errors. #[derive(Debug, Clone, PartialEq)] pub enum Error { @@ -76,18 +90,13 @@ impl From> for Error { Error::Internal(format!("Internal error: {}", err)) } } + impl From for Error { fn from(err: ethtrie::TrieError) -> Self { Error::Internal(format!("Internal error: {}", err)) } } -// impl From for Error { -// fn from(err: wasm::RuntimeError) -> Self { -// Error::Wasm(format!("Runtime error: {:?}", err)) -// } -// } - impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { use self::Error::*; @@ -108,3 +117,7 @@ impl fmt::Display for Error { } pub type Result = ::std::result::Result; +pub type TrapResult = ::std::result::Result, TrapError>; + +pub type ExecTrapResult = TrapResult, Box>; +pub type ExecTrapError = TrapError, Box>; diff --git a/ethcore/vm/src/ext.rs b/ethcore/vm/src/ext.rs index 168640593..aa45066b8 100644 --- a/ethcore/vm/src/ext.rs +++ b/ethcore/vm/src/ext.rs @@ -23,8 +23,9 @@ use call_type::CallType; use env_info::EnvInfo; use schedule::Schedule; use return_data::ReturnData; -use error::Result; +use error::{Result, TrapKind}; +#[derive(Debug)] /// Result of externalities create function. pub enum ContractCreateResult { /// Returned when creation was successfull. @@ -37,6 +38,7 @@ pub enum ContractCreateResult { Reverted(U256, ReturnData), } +#[derive(Debug)] /// Result of externalities call function. pub enum MessageCallResult { /// Returned when message call was successfull. @@ -90,22 +92,31 @@ pub trait Ext { /// Creates new contract. /// /// Returns gas_left and contract address if contract creation was succesfull. - fn create(&mut self, gas: &U256, value: &U256, code: &[u8], address: CreateContractAddress) -> ContractCreateResult; + fn create( + &mut self, + gas: &U256, + value: &U256, + code: &[u8], + address: CreateContractAddress, + trap: bool, + ) -> ::std::result::Result; /// Message call. /// /// Returns Err, if we run out of gas. /// Otherwise returns call_result which contains gas left /// and true if subcall was successfull. - fn call(&mut self, + fn call( + &mut self, gas: &U256, sender_address: &Address, receive_address: &Address, value: Option, data: &[u8], code_address: &Address, - call_type: CallType - ) -> MessageCallResult; + call_type: CallType, + trap: bool + ) -> ::std::result::Result; /// Returns code at given address fn extcode(&self, address: &Address) -> Result>>; @@ -149,10 +160,10 @@ pub trait Ext { fn trace_next_instruction(&mut self, _pc: usize, _instruction: u8, _current_gas: U256) -> bool { false } /// Prepare to trace an operation. Passthrough for the VM trace. - fn trace_prepare_execute(&mut self, _pc: usize, _instruction: u8, _gas_cost: U256) {} + fn trace_prepare_execute(&mut self, _pc: usize, _instruction: u8, _gas_cost: U256, _mem_written: Option<(usize, usize)>, _store_written: Option<(U256, U256)>) {} /// 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)>) {} + fn trace_executed(&mut self, _gas_used: U256, _stack_push: &[U256], _mem: &[u8]) {} /// Check if running in static context. fn is_static(&self) -> bool; diff --git a/ethcore/vm/src/lib.rs b/ethcore/vm/src/lib.rs index 314db030e..b48eb1d84 100644 --- a/ethcore/vm/src/lib.rs +++ b/ethcore/vm/src/lib.rs @@ -41,12 +41,24 @@ pub use env_info::{EnvInfo, LastHashes}; pub use schedule::{Schedule, CleanDustMode, WasmCosts}; pub use ext::{Ext, MessageCallResult, ContractCreateResult, CreateContractAddress}; pub use return_data::{ReturnData, GasLeft}; -pub use error::{Error, Result}; +pub use error::{Error, Result, TrapResult, TrapError, TrapKind, ExecTrapResult, ExecTrapError}; /// Virtual Machine interface -pub trait Vm { +pub trait Exec: Send { /// This function should be used to execute transaction. /// It returns either an error, a known amount of gas left, or parameters to be used /// to compute the final gas left. - fn exec(&mut self, ext: &mut Ext) -> Result; + fn exec(self: Box, ext: &mut Ext) -> ExecTrapResult; +} + +/// Resume call interface +pub trait ResumeCall: Send { + /// Resume an execution for call, returns back the Vm interface. + fn resume_call(self: Box, result: MessageCallResult) -> Box; +} + +/// Resume create interface +pub trait ResumeCreate: Send { + /// Resume an execution from create, returns back the Vm interface. + fn resume_create(self: Box, result: ContractCreateResult) -> Box; } diff --git a/ethcore/vm/src/tests.rs b/ethcore/vm/src/tests.rs index b6e44f000..687fef999 100644 --- a/ethcore/vm/src/tests.rs +++ b/ethcore/vm/src/tests.rs @@ -25,6 +25,7 @@ use { CreateContractAddress, Result, GasLeft, }; use hash::keccak; +use error::TrapKind; pub struct FakeLogEntry { pub topics: Vec, @@ -138,7 +139,14 @@ impl Ext for FakeExt { self.blockhashes.get(number).unwrap_or(&H256::new()).clone() } - fn create(&mut self, gas: &U256, value: &U256, code: &[u8], address: CreateContractAddress) -> ContractCreateResult { + fn create( + &mut self, + gas: &U256, + value: &U256, + code: &[u8], + address: CreateContractAddress, + _trap: bool, + ) -> ::std::result::Result { self.calls.insert(FakeCall { call_type: FakeCallType::Create, create_scheme: Some(address), @@ -149,19 +157,21 @@ impl Ext for FakeExt { data: code.to_vec(), code_address: None }); - ContractCreateResult::Failed + // TODO: support traps in testing. + Ok(ContractCreateResult::Failed) } - fn call(&mut self, - gas: &U256, - sender_address: &Address, - receive_address: &Address, - value: Option, - data: &[u8], - code_address: &Address, - _call_type: CallType - ) -> MessageCallResult { - + fn call( + &mut self, + gas: &U256, + sender_address: &Address, + receive_address: &Address, + value: Option, + data: &[u8], + code_address: &Address, + _call_type: CallType, + _trap: bool, + ) -> ::std::result::Result { self.calls.insert(FakeCall { call_type: FakeCallType::Call, create_scheme: None, @@ -172,7 +182,8 @@ impl Ext for FakeExt { data: data.to_vec(), code_address: Some(code_address.clone()) }); - MessageCallResult::Success(*gas, ReturnData::empty()) + // TODO: support traps in testing. + Ok(MessageCallResult::Success(*gas, ReturnData::empty())) } fn extcode(&self, address: &Address) -> Result>> { diff --git a/ethcore/wasm/run/src/runner.rs b/ethcore/wasm/run/src/runner.rs index a6b7b83a8..b2695fc8e 100644 --- a/ethcore/wasm/run/src/runner.rs +++ b/ethcore/wasm/run/src/runner.rs @@ -16,7 +16,7 @@ use fixture::{Fixture, Assert, CallLocator, Source}; use wasm::WasmInterpreter; -use vm::{self, Vm, GasLeft, ActionParams, ActionValue, ParamsType}; +use vm::{self, Exec, GasLeft, ActionParams, ActionValue, ParamsType}; use vm::tests::FakeExt; use std::io::{self, Read}; use std::{fs, path, fmt}; @@ -31,8 +31,8 @@ fn load_code>(p: P) -> io::Result> { Ok(result) } -fn wasm_interpreter(params: ActionParams) -> WasmInterpreter { - WasmInterpreter::new(params) +fn wasm_interpreter(params: ActionParams) -> Box { + Box::new(WasmInterpreter::new(params)) } #[derive(Debug)] @@ -131,7 +131,7 @@ pub fn construct( params.params_type = ParamsType::Separate; Ok( - match wasm_interpreter(params).exec(ext)? { + match wasm_interpreter(params).exec(ext).ok().expect("Wasm interpreter always calls with trap=false; trap never happens; qed")? { GasLeft::Known(_) => Vec::new(), GasLeft::NeedsReturn { data, .. } => data.to_vec(), } @@ -192,9 +192,9 @@ pub fn run_fixture(fixture: &Fixture) -> Vec { } } - let mut interpreter = wasm_interpreter(params); + let interpreter = wasm_interpreter(params); - let interpreter_return = match interpreter.exec(&mut ext) { + let interpreter_return = match interpreter.exec(&mut ext).ok().expect("Wasm interpreter always calls with trap=false; trap never happens; qed") { Ok(ret) => ret, Err(e) => { return Fail::runtime(e); } }; diff --git a/ethcore/wasm/src/lib.rs b/ethcore/wasm/src/lib.rs index 97758c192..73fe4fff8 100644 --- a/ethcore/wasm/src/lib.rs +++ b/ethcore/wasm/src/lib.rs @@ -91,9 +91,8 @@ enum ExecutionOutcome { NotSpecial, } -impl vm::Vm for WasmInterpreter { - - fn exec(&mut self, ext: &mut vm::Ext) -> vm::Result { +impl WasmInterpreter { + pub fn run(self: Box, ext: &mut vm::Ext) -> vm::Result { let (module, data) = parser::payload(&self.params, ext.schedule().wasm())?; let loaded_module = wasmi::Module::from_parity_wasm_module(module).map_err(Error::Interpreter)?; @@ -190,3 +189,9 @@ impl vm::Vm for WasmInterpreter { } } } + +impl vm::Exec for WasmInterpreter { + fn exec(self: Box, ext: &mut vm::Ext) -> vm::ExecTrapResult { + Ok(self.run(ext)) + } +} diff --git a/ethcore/wasm/src/runtime.rs b/ethcore/wasm/src/runtime.rs index 41c0d950d..a48ac9709 100644 --- a/ethcore/wasm/src/runtime.rs +++ b/ethcore/wasm/src/runtime.rs @@ -450,7 +450,8 @@ impl<'a> Runtime<'a> { &payload, &address, call_type, - ); + false + ).ok().expect("Trap is false; trap error will not happen; qed"); match call_result { vm::MessageCallResult::Success(gas_left, data) => { @@ -528,7 +529,7 @@ impl<'a> Runtime<'a> { * U256::from(self.ext.schedule().wasm().opcodes_mul) / U256::from(self.ext.schedule().wasm().opcodes_div); - match self.ext.create(&gas_left, &endowment, &code, scheme) { + match self.ext.create(&gas_left, &endowment, &code, scheme, false).ok().expect("Trap is false; trap error will not happen; qed") { vm::ContractCreateResult::Created(address, gas_left) => { self.memory.set(result_ptr, &*address)?; self.gas_counter = self.gas_limit - diff --git a/ethcore/wasm/src/tests.rs b/ethcore/wasm/src/tests.rs index b1a773cb4..01f35d6c9 100644 --- a/ethcore/wasm/src/tests.rs +++ b/ethcore/wasm/src/tests.rs @@ -20,7 +20,7 @@ use byteorder::{LittleEndian, ByteOrder}; use ethereum_types::{H256, U256, Address}; use super::WasmInterpreter; -use vm::{self, Vm, GasLeft, ActionParams, ActionValue, CreateContractAddress}; +use vm::{self, Exec, GasLeft, ActionParams, ActionValue, CreateContractAddress}; use vm::tests::{FakeCall, FakeExt, FakeCallType}; macro_rules! load_sample { @@ -48,7 +48,7 @@ macro_rules! reqrep_test { fake_ext.blockhashes = $block_hashes; let mut interpreter = wasm_interpreter(params); - interpreter.exec(&mut fake_ext) + interpreter.exec(&mut fake_ext).ok().unwrap() .map(|result| match result { GasLeft::Known(_) => { panic!("Test is expected to return payload to check"); }, GasLeft::NeedsReturn { gas_left: gas, data: result, apply_state: _apply } => (gas, result.to_vec()), @@ -65,8 +65,8 @@ fn test_finalize(res: Result) -> Result { } } -fn wasm_interpreter(params: ActionParams) -> WasmInterpreter { - WasmInterpreter::new(params) +fn wasm_interpreter(params: ActionParams) -> Box { + Box::new(WasmInterpreter::new(params)) } /// Empty contract does almost nothing except producing 1 (one) local node debug log message @@ -83,7 +83,7 @@ fn empty() { let gas_left = { let mut interpreter = wasm_interpreter(params); - test_finalize(interpreter.exec(&mut ext)).unwrap() + test_finalize(interpreter.exec(&mut ext).ok().unwrap()).unwrap() }; assert_eq!(gas_left, U256::from(96_926)); @@ -112,7 +112,7 @@ fn logger() { let gas_left = { let mut interpreter = wasm_interpreter(params); - test_finalize(interpreter.exec(&mut ext)).unwrap() + test_finalize(interpreter.exec(&mut ext).ok().unwrap()).unwrap() }; let address_val: H256 = address.into(); @@ -161,7 +161,7 @@ fn identity() { let (gas_left, result) = { let mut interpreter = wasm_interpreter(params); - let result = interpreter.exec(&mut ext).expect("Interpreter to execute without any errors"); + let result = interpreter.exec(&mut ext).ok().unwrap().expect("Interpreter to execute without any errors"); match result { GasLeft::Known(_) => { panic!("Identity contract should return payload"); }, GasLeft::NeedsReturn { gas_left: gas, data: result, apply_state: _apply } => (gas, result.to_vec()), @@ -196,7 +196,7 @@ fn dispersion() { let (gas_left, result) = { let mut interpreter = wasm_interpreter(params); - let result = interpreter.exec(&mut ext).expect("Interpreter to execute without any errors"); + let result = interpreter.exec(&mut ext).ok().unwrap().expect("Interpreter to execute without any errors"); match result { GasLeft::Known(_) => { panic!("Dispersion routine should return payload"); }, GasLeft::NeedsReturn { gas_left: gas, data: result, apply_state: _apply } => (gas, result.to_vec()), @@ -224,7 +224,7 @@ fn suicide_not() { let (gas_left, result) = { let mut interpreter = wasm_interpreter(params); - let result = interpreter.exec(&mut ext).expect("Interpreter to execute without any errors"); + let result = interpreter.exec(&mut ext).ok().unwrap().expect("Interpreter to execute without any errors"); match result { GasLeft::Known(_) => { panic!("Suicidal contract should return payload when had not actualy killed himself"); }, GasLeft::NeedsReturn { gas_left: gas, data: result, apply_state: _apply } => (gas, result.to_vec()), @@ -257,7 +257,7 @@ fn suicide() { let gas_left = { let mut interpreter = wasm_interpreter(params); - let result = interpreter.exec(&mut ext).expect("Interpreter to execute without any errors"); + let result = interpreter.exec(&mut ext).ok().unwrap().expect("Interpreter to execute without any errors"); match result { GasLeft::Known(gas) => gas, GasLeft::NeedsReturn { .. } => { @@ -285,7 +285,7 @@ fn create() { let gas_left = { let mut interpreter = wasm_interpreter(params); - let result = interpreter.exec(&mut ext).expect("Interpreter to execute without any errors"); + let result = interpreter.exec(&mut ext).ok().unwrap().expect("Interpreter to execute without any errors"); match result { GasLeft::Known(_) => { panic!("Create contract always return 40 bytes of the creation address, or in the case where it fails, return 40 bytes of zero."); @@ -347,7 +347,7 @@ fn call_msg() { let gas_left = { let mut interpreter = wasm_interpreter(params); - let result = interpreter.exec(&mut ext).expect("Interpreter to execute without any errors"); + let result = interpreter.exec(&mut ext).ok().unwrap().expect("Interpreter to execute without any errors"); match result { GasLeft::Known(gas_left) => gas_left, GasLeft::NeedsReturn { .. } => { panic!("Call test should not return payload"); }, @@ -395,7 +395,7 @@ fn call_msg_gasleft() { let gas_left = { let mut interpreter = wasm_interpreter(params); - let result = interpreter.exec(&mut ext).expect("Interpreter to execute without any errors"); + let result = interpreter.exec(&mut ext).ok().unwrap().expect("Interpreter to execute without any errors"); match result { GasLeft::Known(gas_left) => gas_left, GasLeft::NeedsReturn { .. } => { panic!("Call test should not return payload"); }, @@ -438,7 +438,7 @@ fn call_code() { let (gas_left, result) = { let mut interpreter = wasm_interpreter(params); - let result = interpreter.exec(&mut ext).expect("Interpreter to execute without any errors"); + let result = interpreter.exec(&mut ext).ok().unwrap().expect("Interpreter to execute without any errors"); match result { GasLeft::Known(_) => { panic!("Call test should return payload"); }, GasLeft::NeedsReturn { gas_left: gas, data: result, apply_state: _apply } => (gas, result.to_vec()), @@ -486,7 +486,7 @@ fn call_static() { let (gas_left, result) = { let mut interpreter = wasm_interpreter(params); - let result = interpreter.exec(&mut ext).expect("Interpreter to execute without any errors"); + let result = interpreter.exec(&mut ext).ok().unwrap().expect("Interpreter to execute without any errors"); match result { GasLeft::Known(_) => { panic!("Static call test should return payload"); }, GasLeft::NeedsReturn { gas_left: gas, data: result, apply_state: _apply } => (gas, result.to_vec()), @@ -527,7 +527,7 @@ fn realloc() { let (gas_left, result) = { let mut interpreter = wasm_interpreter(params); - let result = interpreter.exec(&mut ext).expect("Interpreter to execute without any errors"); + let result = interpreter.exec(&mut ext).ok().unwrap().expect("Interpreter to execute without any errors"); match result { GasLeft::Known(_) => { panic!("Realloc should return payload"); }, GasLeft::NeedsReturn { gas_left: gas, data: result, apply_state: _apply } => (gas, result.to_vec()), @@ -549,7 +549,7 @@ fn alloc() { let (gas_left, result) = { let mut interpreter = wasm_interpreter(params); - let result = interpreter.exec(&mut ext).expect("Interpreter to execute without any errors"); + let result = interpreter.exec(&mut ext).ok().unwrap().expect("Interpreter to execute without any errors"); match result { GasLeft::Known(_) => { panic!("alloc test should return payload"); }, GasLeft::NeedsReturn { gas_left: gas, data: result, apply_state: _apply } => (gas, result.to_vec()), @@ -576,7 +576,7 @@ fn storage_read() { let (gas_left, result) = { let mut interpreter = wasm_interpreter(params); - let result = interpreter.exec(&mut ext).expect("Interpreter to execute without any errors"); + let result = interpreter.exec(&mut ext).ok().unwrap().expect("Interpreter to execute without any errors"); match result { GasLeft::Known(_) => { panic!("storage_read should return payload"); }, GasLeft::NeedsReturn { gas_left: gas, data: result, apply_state: _apply } => (gas, result.to_vec()), @@ -602,7 +602,7 @@ fn keccak() { let (gas_left, result) = { let mut interpreter = wasm_interpreter(params); - let result = interpreter.exec(&mut ext).expect("Interpreter to execute without any errors"); + let result = interpreter.exec(&mut ext).ok().unwrap().expect("Interpreter to execute without any errors"); match result { GasLeft::Known(_) => { panic!("keccak should return payload"); }, GasLeft::NeedsReturn { gas_left: gas, data: result, apply_state: _apply } => (gas, result.to_vec()), @@ -749,7 +749,7 @@ fn storage_metering() { let gas_left = { let mut interpreter = wasm_interpreter(params); - test_finalize(interpreter.exec(&mut ext)).unwrap() + test_finalize(interpreter.exec(&mut ext).ok().unwrap()).unwrap() }; // 0 -> not 0 @@ -768,7 +768,7 @@ fn storage_metering() { let gas_left = { let mut interpreter = wasm_interpreter(params); - test_finalize(interpreter.exec(&mut ext)).unwrap() + test_finalize(interpreter.exec(&mut ext).ok().unwrap()).unwrap() }; // not 0 -> not 0 @@ -875,8 +875,8 @@ fn gasleft() { let mut ext = FakeExt::new().with_wasm(); ext.schedule.wasm.as_mut().unwrap().have_gasleft = true; - let mut interpreter = wasm_interpreter(params); - let result = interpreter.exec(&mut ext).expect("Interpreter to execute without any errors"); + let interpreter = wasm_interpreter(params); + let result = interpreter.exec(&mut ext).ok().unwrap().expect("Interpreter to execute without any errors"); match result { GasLeft::Known(_) => {}, GasLeft::NeedsReturn { gas_left, data, .. } => { @@ -897,8 +897,8 @@ fn gasleft_fail() { params.gas = U256::from(100_000); params.code = Some(Arc::new(load_sample!("gasleft.wasm"))); let mut ext = FakeExt::new().with_wasm(); - let mut interpreter = wasm_interpreter(params); - match interpreter.exec(&mut ext) { + let interpreter = wasm_interpreter(params); + match interpreter.exec(&mut ext).ok().unwrap() { Err(_) => {}, Ok(_) => panic!("interpreter.exec should return Err if ext.schedule.wasm.have_gasleft = false") } @@ -919,7 +919,7 @@ fn embedded_keccak() { let (gas_left, result) = { let mut interpreter = wasm_interpreter(params); - let result = interpreter.exec(&mut ext).expect("Interpreter to execute without any errors"); + let result = interpreter.exec(&mut ext).ok().unwrap().expect("Interpreter to execute without any errors"); match result { GasLeft::Known(_) => { panic!("keccak should return payload"); }, GasLeft::NeedsReturn { gas_left: gas, data: result, apply_state: _apply } => (gas, result.to_vec()), @@ -947,7 +947,7 @@ fn events() { let (gas_left, result) = { let mut interpreter = wasm_interpreter(params); - let result = interpreter.exec(&mut ext).expect("Interpreter to execute without any errors"); + let result = interpreter.exec(&mut ext).ok().unwrap().expect("Interpreter to execute without any errors"); match result { GasLeft::Known(_) => { panic!("events should return payload"); }, GasLeft::NeedsReturn { gas_left: gas, data: result, apply_state: _apply } => (gas, result.to_vec()), @@ -986,8 +986,8 @@ fn recursive() { let mut ext = FakeExt::new().with_wasm(); - let mut interpreter = wasm_interpreter(params); - let result = interpreter.exec(&mut ext); + let interpreter = wasm_interpreter(params); + let result = interpreter.exec(&mut ext).ok().unwrap(); // We expect that stack overflow will occur and it should be generated by // deterministic stack metering. Exceeding deterministic stack height limit diff --git a/evmbin/src/display/json.rs b/evmbin/src/display/json.rs index d2a825fdf..8159b07e9 100644 --- a/evmbin/src/display/json.rs +++ b/evmbin/src/display/json.rs @@ -35,14 +35,28 @@ pub struct Informant { instruction: u8, gas_cost: U256, gas_used: U256, + mem_written: Option<(usize, usize)>, + store_written: Option<(U256, U256)>, stack: Vec, memory: Vec, storage: HashMap, traces: Vec, subtraces: Vec, + subinfos: Vec, + subdepth: usize, unmatched: bool, } +impl Informant { + fn with_informant_in_depth(informant: &mut Informant, depth: usize, f: F) { + if depth == 0 { + f(informant); + } else { + Self::with_informant_in_depth(informant.subinfos.last_mut().expect("prepare/done_trace are not balanced"), depth - 1, f); + } + } +} + impl vm::Informant for Informant { fn before_test(&mut self, name: &str, action: &str) { println!("{}", json!({"action": action, "test": name})); @@ -88,72 +102,92 @@ impl trace::VMTracer for Informant { type Output = Vec; fn trace_next_instruction(&mut self, pc: usize, instruction: u8, _current_gas: U256) -> bool { - self.pc = pc; - self.instruction = instruction; - self.unmatched = true; + let subdepth = self.subdepth; + Self::with_informant_in_depth(self, subdepth, |informant: &mut Informant| { + informant.pc = pc; + informant.instruction = instruction; + informant.unmatched = true; + }); true } - fn trace_prepare_execute(&mut self, pc: usize, instruction: u8, gas_cost: U256) { - self.pc = pc; - self.instruction = instruction; - self.gas_cost = gas_cost; - } - - fn trace_executed(&mut self, gas_used: U256, stack_push: &[U256], mem_diff: Option<(usize, &[u8])>, store_diff: Option<(U256, U256)>) { - let info = ::evm::Instruction::from_u8(self.instruction).map(|i| i.info()); - - let trace = json!({ - "pc": self.pc, - "op": self.instruction, - "opName": info.map(|i| i.name).unwrap_or(""), - "gas": format!("{:#x}", gas_used.saturating_add(self.gas_cost)), - "gasCost": format!("{:#x}", self.gas_cost), - "memory": format!("0x{}", self.memory.to_hex()), - "stack": self.stack, - "storage": self.storage, - "depth": self.depth, + fn trace_prepare_execute(&mut self, pc: usize, instruction: u8, gas_cost: U256, mem_written: Option<(usize, usize)>, store_written: Option<(U256, U256)>) { + let subdepth = self.subdepth; + Self::with_informant_in_depth(self, subdepth, |informant: &mut Informant| { + informant.pc = pc; + informant.instruction = instruction; + informant.gas_cost = gas_cost; + informant.mem_written = mem_written; + informant.store_written = store_written; }); + } - self.traces.push(trace.to_string()); + fn trace_executed(&mut self, gas_used: U256, stack_push: &[U256], mem: &[u8]) { + let subdepth = self.subdepth; + Self::with_informant_in_depth(self, subdepth, |informant: &mut Informant| { + let mem_diff = informant.mem_written.clone().map(|(o, s)| (o, &(mem[o..o+s]))); + let store_diff = informant.store_written.clone(); + let info = ::evm::Instruction::from_u8(informant.instruction).map(|i| i.info()); - self.unmatched = false; - self.gas_used = gas_used; + let trace = json!({ + "pc": informant.pc, + "op": informant.instruction, + "opName": info.map(|i| i.name).unwrap_or(""), + "gas": format!("{:#x}", gas_used.saturating_add(informant.gas_cost)), + "gasCost": format!("{:#x}", informant.gas_cost), + "memory": format!("0x{}", informant.memory.to_hex()), + "stack": informant.stack, + "storage": informant.storage, + "depth": informant.depth, + }); + informant.traces.push(trace.to_string()); - let len = self.stack.len(); - let info_args = info.map(|i| i.args).unwrap_or(0); - self.stack.truncate(if len > info_args { len - info_args } else { 0 }); - self.stack.extend_from_slice(stack_push); + informant.unmatched = false; + informant.gas_used = gas_used; - // TODO [ToDr] Align memory? - if let Some((pos, data)) = mem_diff { - if self.memory.len() < (pos + data.len()) { - self.memory.resize(pos + data.len(), 0); + let len = informant.stack.len(); + let info_args = info.map(|i| i.args).unwrap_or(0); + informant.stack.truncate(if len > info_args { len - info_args } else { 0 }); + informant.stack.extend_from_slice(stack_push); + + // TODO [ToDr] Align memory? + if let Some((pos, data)) = mem_diff { + if informant.memory.len() < (pos + data.len()) { + informant.memory.resize(pos + data.len(), 0); + } + informant.memory[pos..pos + data.len()].copy_from_slice(data); } - self.memory[pos..pos + data.len()].copy_from_slice(data); - } - if let Some((pos, val)) = store_diff { - self.storage.insert(pos.into(), val.into()); - } + if let Some((pos, val)) = store_diff { + informant.storage.insert(pos.into(), val.into()); + } - if !self.subtraces.is_empty() { - self.traces.extend(mem::replace(&mut self.subtraces, vec![])); - } + if !informant.subtraces.is_empty() { + informant.traces.extend(mem::replace(&mut informant.subtraces, vec![])); + } + }); } - fn prepare_subtrace(&self, code: &[u8]) -> Self where Self: Sized { - let mut vm = Informant::default(); - vm.depth = self.depth + 1; - vm.code = code.to_vec(); - vm.gas_used = self.gas_used; - vm + fn prepare_subtrace(&mut self, code: &[u8]) { + let subdepth = self.subdepth; + Self::with_informant_in_depth(self, subdepth, |informant: &mut Informant| { + let mut vm = Informant::default(); + vm.depth = informant.depth + 1; + vm.code = code.to_vec(); + vm.gas_used = informant.gas_used; + informant.subinfos.push(vm); + }); + self.subdepth += 1; } - fn done_subtrace(&mut self, sub: Self) { - if let Some(subtraces) = sub.drain() { - self.subtraces.extend(subtraces); - } + fn done_subtrace(&mut self) { + self.subdepth -= 1; + let subdepth = self.subdepth; + Self::with_informant_in_depth(self, subdepth, |informant: &mut Informant| { + if let Some(subtraces) = informant.subinfos.pop().expect("prepare/done_subtrace are not balanced").drain() { + informant.subtraces.extend(subtraces); + } + }); } fn drain(mut self) -> Option { @@ -161,7 +195,7 @@ impl trace::VMTracer for Informant { // print last line with final state: self.gas_cost = 0.into(); let gas_used = self.gas_used; - self.trace_executed(gas_used, &[], None, None); + self.trace_executed(gas_used, &[], &[]); } else if !self.subtraces.is_empty() { self.traces.extend(mem::replace(&mut self.subtraces, vec![])); } diff --git a/evmbin/src/display/simple.rs b/evmbin/src/display/simple.rs index 8ff863cfa..5cb53d4c2 100644 --- a/evmbin/src/display/simple.rs +++ b/evmbin/src/display/simple.rs @@ -49,7 +49,7 @@ impl vm::Informant for Informant { impl trace::VMTracer for Informant { type Output = (); - fn prepare_subtrace(&self, _code: &[u8]) -> Self where Self: Sized { Default::default() } - fn done_subtrace(&mut self, _sub: Self) {} + fn prepare_subtrace(&mut self, _code: &[u8]) { Default::default() } + fn done_subtrace(&mut self) {} fn drain(self) -> Option<()> { None } } diff --git a/evmbin/src/display/std_json.rs b/evmbin/src/display/std_json.rs index f2a0963d8..43b7fede1 100644 --- a/evmbin/src/display/std_json.rs +++ b/evmbin/src/display/std_json.rs @@ -58,6 +58,8 @@ pub struct Informant { depth: usize, stack: Vec, storage: HashMap, + subinfos: Vec>, + subdepth: usize, trace_sink: Trace, out_sink: Out, } @@ -76,9 +78,19 @@ impl Informant { depth: Default::default(), stack: Default::default(), storage: Default::default(), + subinfos: Default::default(), + subdepth: 0, trace_sink, out_sink } } + + fn with_informant_in_depth)>(informant: &mut Informant, depth: usize, f: F) { + if depth == 0 { + f(informant); + } else { + Self::with_informant_in_depth(informant.subinfos.last_mut().expect("prepare/done_trace are not balanced"), depth - 1, f); + } + } } impl vm::Informant for Informant { @@ -128,47 +140,64 @@ impl trace::VMTracer for Informant { type Output = (); fn trace_next_instruction(&mut self, pc: usize, instruction: u8, current_gas: U256) -> bool { - let info = ::evm::Instruction::from_u8(instruction).map(|i| i.info()); - self.instruction = instruction; - let trace_data = json!({ - "pc": pc, - "op": instruction, - "opName": info.map(|i| i.name).unwrap_or(""), - "gas": format!("{:#x}", current_gas), - "stack": self.stack, - "storage": self.storage, - "depth": self.depth, + let subdepth = self.subdepth; + Self::with_informant_in_depth(self, subdepth, |informant: &mut Informant| { + let info = ::evm::Instruction::from_u8(instruction).map(|i| i.info()); + informant.instruction = instruction; + let trace_data = json!({ + "pc": pc, + "op": instruction, + "opName": info.map(|i| i.name).unwrap_or(""), + "gas": format!("{:#x}", current_gas), + "stack": informant.stack, + "storage": informant.storage, + "depth": informant.depth, + }); + + writeln!(&mut informant.trace_sink, "{}", trace_data).expect("The sink must be writeable."); }); - - writeln!(&mut self.trace_sink, "{}", trace_data).expect("The sink must be writeable."); - true } - fn trace_prepare_execute(&mut self, _pc: usize, _instruction: u8, _gas_cost: U256) { + fn trace_prepare_execute(&mut self, _pc: usize, _instruction: u8, _gas_cost: U256, _mem_written: Option<(usize, usize)>, store_written: Option<(U256, U256)>) { + let subdepth = self.subdepth; + Self::with_informant_in_depth(self, subdepth, |informant: &mut Informant| { + if let Some((pos, val)) = store_written { + informant.storage.insert(pos.into(), val.into()); + } + }); } - fn trace_executed(&mut self, _gas_used: U256, stack_push: &[U256], _mem_diff: Option<(usize, &[u8])>, store_diff: Option<(U256, U256)>) { - let info = ::evm::Instruction::from_u8(self.instruction).map(|i| i.info()); + fn trace_executed(&mut self, _gas_used: U256, stack_push: &[U256], _mem: &[u8]) { + let subdepth = self.subdepth; + Self::with_informant_in_depth(self, subdepth, |informant: &mut Informant| { + let info = ::evm::Instruction::from_u8(informant.instruction).map(|i| i.info()); - let len = self.stack.len(); - let info_args = info.map(|i| i.args).unwrap_or(0); - self.stack.truncate(if len > info_args { len - info_args } else { 0 }); - self.stack.extend_from_slice(stack_push); - - if let Some((pos, val)) = store_diff { - self.storage.insert(pos.into(), val.into()); - } + let len = informant.stack.len(); + let info_args = info.map(|i| i.args).unwrap_or(0); + informant.stack.truncate(if len > info_args { len - info_args } else { 0 }); + informant.stack.extend_from_slice(stack_push); + }); } - fn prepare_subtrace(&self, code: &[u8]) -> Self where Self: Sized { - let mut vm = Informant::new(self.trace_sink.clone(), self.out_sink.clone()); - vm.depth = self.depth + 1; - vm.code = code.to_vec(); - vm + fn prepare_subtrace(&mut self, code: &[u8]) { + let subdepth = self.subdepth; + Self::with_informant_in_depth(self, subdepth, |informant: &mut Informant| { + let mut vm = Informant::new(informant.trace_sink.clone(), informant.out_sink.clone()); + vm.depth = informant.depth + 1; + vm.code = code.to_vec(); + informant.subinfos.push(vm); + }); + self.subdepth += 1; } - fn done_subtrace(&mut self, _sub: Self) {} + fn done_subtrace(&mut self) { + self.subdepth -= 1; + let subdepth = self.subdepth; + Self::with_informant_in_depth(self, subdepth, |informant: &mut Informant| { + informant.subinfos.pop(); + }); + } fn drain(self) -> Option { None } }