From bbb25fb6ce0329bf761b7b3965961704757ec7ab Mon Sep 17 00:00:00 2001 From: debris Date: Mon, 11 Jan 2016 14:08:03 +0100 Subject: [PATCH] propagate evmjit errors upstream --- src/evm/evm.rs | 1 + src/evm/executive.rs | 63 ++++++++++++++++---------------------------- src/evm/ext.rs | 22 +++++++++++----- src/evm/jit.rs | 56 ++++++++++++++++++++++++++++++--------- 4 files changed, 83 insertions(+), 59 deletions(-) diff --git a/src/evm/evm.rs b/src/evm/evm.rs index faf156502..8bc463f75 100644 --- a/src/evm/evm.rs +++ b/src/evm/evm.rs @@ -4,6 +4,7 @@ use util::uint::U256; use evm::{EvmParams, Ext}; /// Evm errors. +#[derive(Debug)] pub enum EvmError { /// `OutOfGas` is returned when transaction execution runs out of gas. /// The state should be reverted to the state from before the diff --git a/src/evm/executive.rs b/src/evm/executive.rs index c7dbd031a..383e79fc5 100644 --- a/src/evm/executive.rs +++ b/src/evm/executive.rs @@ -123,9 +123,9 @@ impl<'a> Executive<'a> { Executive::new_with_depth(state, info, engine, 0) } - /// Populates executive from parent externalities. Increments executive depth. - fn from_parent(e: &'a mut Externalities) -> Self { - Executive::new_with_depth(e.state, e.info, e.engine, e.depth + 1) + /// Populates executive from parent properties. Increments executive depth. + fn from_parent(state: &'a mut State, info: &'a EnvInfo, engine: &'a Engine, depth: usize) -> Self { + Executive::new_with_depth(state, info, engine, depth + 1) } /// Helper constructor. Should be used to create `Executive` with desired depth. @@ -353,10 +353,10 @@ impl<'a> Ext for Externalities<'a> { } } - fn create(&mut self, gas: u64, endowment: &U256, code: &[u8]) -> Option<(u64, Address)> { + fn create(&mut self, gas: u64, value: &U256, code: &[u8]) -> Result<(u64, Option
), EvmError> { // if balance is insufficient or we are to deep, return - if self.state.balance(&self.params.address) < *endowment && self.depth >= 1024 { - return None + if self.state.balance(&self.params.address) < *value && self.depth >= 1024 { + return Ok((gas, None)); } // create new contract address @@ -369,28 +369,17 @@ impl<'a> Ext for Externalities<'a> { origin: self.params.origin.clone(), gas: U256::from(gas), gas_price: self.params.gas_price.clone(), - value: endowment.clone(), + value: value.clone(), code: code.to_vec(), data: vec![], }; - let mut substate = Substate::new(); - { - let mut ex = Executive::from_parent(self); - ex.state.inc_nonce(&address); - let res = ex.create(¶ms, &mut substate); - } - - self.substate.accrue(substate); - Some((gas, address)) + let mut ex = Executive::from_parent(self.state, self.info, self.engine, self.depth); + ex.state.inc_nonce(&self.params.address); + ex.create(¶ms, self.substate).map(|gas_left| (gas_left.low_u64(), Some(address))) } - fn call(&mut self, gas: u64, call_gas: u64, receive_address: &Address, value: &U256, data: &[u8], code_address: &Address, output: &mut [u8]) -> Option { - // TODO: validation of the call - - println!("gas: {:?}", gas); - println!("call_gas: {:?}", call_gas); - + fn call(&mut self, gas: u64, call_gas: u64, receive_address: &Address, value: &U256, data: &[u8], code_address: &Address, output: &mut [u8]) -> Result { let mut gas_cost = call_gas; let mut call_gas = call_gas; @@ -406,13 +395,15 @@ impl<'a> Ext for Externalities<'a> { } if gas_cost > gas { - return None; + return Err(EvmError::OutOfGas) } - // if we are too deep, return - // TODO: replace with >= 1024 - if self.depth == 1 { - return None; + let mut gas = gas - gas_cost; + + //println!("depth: {:?}", self.depth); + // if balance is insufficient or we are to deep, return + if self.state.balance(&self.params.address) < *value && self.depth >= 1024 { + return Ok(gas + call_gas) } let params = EvmParams { @@ -426,18 +417,8 @@ impl<'a> Ext for Externalities<'a> { data: data.to_vec(), }; - println!("params: {:?}", params); - - let mut substate = Substate::new(); - { - let mut ex = Executive::from_parent(self); - // TODO: take output into account - ex.call(¶ms, &mut substate, output); - } - - self.substate.accrue(substate); - // TODO: replace call_gas with what's actually left - Some(gas - gas_cost + call_gas) + let mut ex = Executive::from_parent(self.state, self.info, self.engine, self.depth); + ex.call(¶ms, self.substate, output).map(|gas_left| gas + gas_left.low_u64()) } fn extcode(&self, address: &Address) -> Vec { @@ -556,6 +537,7 @@ mod tests { { let mut ex = Executive::new(&mut state, &info, engine.deref()); let _res = ex.create(¶ms, &mut substate); + println!("res: {:?}", _res); } assert_eq!(state.storage_at(&address, &H256::new()), H256::from(next_address.clone())); @@ -584,7 +566,7 @@ mod tests { // 60 01 - push 1 // 55 - store let sender = Address::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap(); - let code = "600160005401600055600060006000600060003360e05a03f1600155".from_hex().unwrap(); + let code = "600160005401600055600060006000600060003060e05a03f1600155".from_hex().unwrap(); let address = contract_address(&sender, &U256::zero()); let mut params = EvmParams::new(); params.address = address.clone(); @@ -603,6 +585,7 @@ mod tests { { let mut ex = Executive::new(&mut state, &info, engine.deref()); let _res = ex.call(¶ms, &mut substate, &mut []); + println!("res: {:?}", _res); } assert!(false); diff --git a/src/evm/ext.rs b/src/evm/ext.rs index ac9780737..9ffebc605 100644 --- a/src/evm/ext.rs +++ b/src/evm/ext.rs @@ -4,6 +4,7 @@ use util::hash::*; use util::uint::*; use util::bytes::*; use evm_schedule::*; +use evm::EvmError; pub trait Ext { /// Returns a value for given key. @@ -19,15 +20,24 @@ pub trait Ext { fn blockhash(&self, number: &U256) -> H256; /// Creates new contract. - /// If contract creation is successfull, - /// return gas_left and contract address, - /// otherwise `None`. - fn create(&mut self, gas: u64, endowment: &U256, code: &[u8]) -> Option<(u64, Address)>; + /// + /// If contract creation is successfull, return gas_left and contract address, + /// If depth is too big or transfer value exceeds balance, return None + /// Otherwise return appropriate `EvmError`. + fn create(&mut self, gas: u64, value: &U256, code: &[u8]) -> Result<(u64, Option
), EvmError>; /// Message call. + /// /// If call is successfull, returns gas left. - /// otherwise `None`. - fn call(&mut self, gas: u64, call_gas: u64, receive_address: &Address, value: &U256, data: &[u8], code_address: &Address, output: &mut [u8]) -> Option; + /// otherwise `EvmError`. + fn call(&mut self, + gas: u64, + call_gas: u64, + receive_address: &Address, + value: &U256, + data: &[u8], + code_address: &Address, + output: &mut [u8]) -> Result; /// Returns code at given address fn extcode(&self, address: &Address) -> Vec; diff --git a/src/evm/jit.rs b/src/evm/jit.rs index e1c7a23df..6272d87f7 100644 --- a/src/evm/jit.rs +++ b/src/evm/jit.rs @@ -160,13 +160,15 @@ impl IntoJit for RuntimeData { } struct ExtAdapter<'a> { - ext: &'a mut evm::Ext + ext: &'a mut evm::Ext, + err: &'a mut Option } impl<'a> ExtAdapter<'a> { - fn new(ext: &'a mut evm::Ext) -> Self { + fn new(ext: &'a mut evm::Ext, err: &'a mut Option) -> Self { ExtAdapter { - ext: ext + ext: ext, + err: err } } } @@ -210,12 +212,21 @@ impl<'a> evmjit::Ext for ExtAdapter<'a> { address: *mut evmjit::H256) { unsafe { match self.ext.create(*io_gas, &U256::from_jit(&*endowment), slice::from_raw_parts(init_beg, init_size as usize)) { - Some((gas_left, addr)) => { + Ok((gas_left, opt)) => { *io_gas = gas_left; - *address = addr.into_jit(); + if let Some(addr) = opt { + *address = addr.into_jit(); + } }, - None => () - }; + Err(err @ evm::EvmError::OutOfGas) => { + *self.err = Some(err); + // hack to propagate `OutOfGas` to evmjit and stop + // the execution immediately. + // Works, cause evmjit uses i64, not u64 + *io_gas = -1i64 as u64 + }, + Err(err) => *self.err = Some(err) + } } } @@ -230,7 +241,7 @@ impl<'a> evmjit::Ext for ExtAdapter<'a> { out_size: u64, code_address: *const evmjit::H256) -> bool { unsafe { - let opt = self.ext.call(*io_gas, + let res = self.ext.call(*io_gas, call_gas, &Address::from_jit(&*receive_address), &U256::from_jit(&*value), @@ -238,11 +249,22 @@ impl<'a> evmjit::Ext for ExtAdapter<'a> { &Address::from_jit(&*code_address), slice::from_raw_parts_mut(out_beg, out_size as usize)); - match opt { - None => false, - Some(gas_left) => { + match res { + Ok(gas_left) => { *io_gas = gas_left; true + }, + Err(err @ evm::EvmError::OutOfGas) => { + *self.err = Some(err); + // hack to propagate `OutOfGas` to evmjit and stop + // the execution immediately. + // Works, cause evmjit uses i64, not u64 + *io_gas = -1i64 as u64; + false + }, + Err(err) => { + *self.err = Some(err); + false } } } @@ -294,8 +316,9 @@ pub struct JitEvm; impl evm::Evm for JitEvm { fn exec(&self, params: &evm::EvmParams, ext: &mut evm::Ext) -> evm::EvmResult { + let mut optional_err = None; // Dirty hack. This is unsafe, but we interact with ffi, so it's justified. - let ext_adapter: ExtAdapter<'static> = unsafe { ::std::mem::transmute(ExtAdapter::new(ext)) }; + let ext_adapter: ExtAdapter<'static> = unsafe { ::std::mem::transmute(ExtAdapter::new(ext, &mut optional_err)) }; let mut ext_handle = evmjit::ExtHandle::new(ext_adapter); let mut data = RuntimeData::new(); data.gas = params.gas; @@ -315,7 +338,14 @@ impl evm::Evm for JitEvm { data.timestamp = 0; let mut context = unsafe { evmjit::ContextHandle::new(data.into_jit(), &mut ext_handle) }; - match context.exec() { + let res = context.exec(); + + // check in adapter if execution of children contracts failed. + if let Some(err) = optional_err { + return Err(err); + } + + match res { evmjit::ReturnCode::Stop => Ok(U256::from(context.gas_left())), evmjit::ReturnCode::Return => match ext.ret(context.gas_left(), context.output_data()) { Some(gas_left) => Ok(U256::from(gas_left)),