diff --git a/src/evm/ext.rs b/src/evm/ext.rs index 6bbd2bce3..9365c40a0 100644 --- a/src/evm/ext.rs +++ b/src/evm/ext.rs @@ -6,6 +6,20 @@ use util::bytes::*; use evm::{Schedule, Error}; use env_info::*; +pub struct CallResult { + pub gas_left: U256, + pub success: bool +} + +impl CallResult { + pub fn new(gas_left: U256, success: bool) -> Self { + CallResult { + gas_left: gas_left, + success: success + } + } +} + pub trait Ext { /// Returns a value for given key. fn sload(&self, key: &H256) -> H256; @@ -26,8 +40,9 @@ pub trait Ext { /// Message call. /// - /// If call is successfull, returns gas left. - /// otherwise `Error`. + /// Returns None, if we run out of gas. + /// Otherwise returns call_result which contains gas left + /// and true if subcall was successfull. fn call(&mut self, gas: &U256, call_gas: &U256, @@ -35,7 +50,7 @@ pub trait Ext { value: &U256, data: &[u8], code_address: &Address, - output: &mut [u8]) -> Result; + output: &mut [u8]) -> Option; /// Returns code at given address fn extcode(&self, address: &Address) -> Vec; diff --git a/src/evm/jit.rs b/src/evm/jit.rs index da41158d1..4cb2e763e 100644 --- a/src/evm/jit.rs +++ b/src/evm/jit.rs @@ -229,30 +229,21 @@ impl<'a> evmjit::Ext for ExtAdapter<'a> { out_size: u64, code_address: *const evmjit::H256) -> bool { unsafe { - let res = self.ext.call(&U256::from(*io_gas), + let opt = self.ext.call(&U256::from(*io_gas), &U256::from(call_gas), &Address::from_jit(&*receive_address), &U256::from_jit(&*value), slice::from_raw_parts(in_beg, in_size as usize), &Address::from_jit(&*code_address), slice::from_raw_parts_mut(out_beg, out_size as usize)); - - match res { - Ok(gas_left) => { - *io_gas = gas_left.low_u64(); - true - }, - Err(err @ evm::Error::OutOfGas) => { - *self.err = Some(err); - // hack to propagate `OutOfGas` to evmjit and stop - // the execution immediately. - // Works, cause evmjit uses i64, not u64 + match opt { + None => { *io_gas = -1i64 as u64; false }, - Err(err) => { - *self.err = Some(err); - false + Some(res) => { + *io_gas = res.gas_left.low_u64(); + res.success } } } diff --git a/src/evm/mod.rs b/src/evm/mod.rs index ee590c934..d0635a017 100644 --- a/src/evm/mod.rs +++ b/src/evm/mod.rs @@ -11,6 +11,6 @@ mod jit; mod tests; pub use self::evm::{Evm, Error, Result}; -pub use self::ext::Ext; +pub use self::ext::{Ext, CallResult}; pub use self::factory::Factory; pub use self::schedule::Schedule; diff --git a/src/evm/tests.rs b/src/evm/tests.rs index 78a47d7e3..f06d55943 100644 --- a/src/evm/tests.rs +++ b/src/evm/tests.rs @@ -1,6 +1,6 @@ use common::*; use evm; -use evm::{Ext, Schedule, Factory}; +use evm::{Ext, Schedule, Factory, CallResult}; struct FakeLogEntry { topics: Vec, @@ -53,7 +53,7 @@ impl Ext for FakeExt { _value: &U256, _data: &[u8], _code_address: &Address, - _output: &mut [u8]) -> result::Result { + _output: &mut [u8]) -> Option { unimplemented!(); } diff --git a/src/executive.rs b/src/executive.rs index ceeb5f658..e0531fdae 100644 --- a/src/executive.rs +++ b/src/executive.rs @@ -2,7 +2,7 @@ use common::*; use state::*; use engine::*; -use evm::{self, Schedule, Factory, Ext}; +use evm::{self, Schedule, Factory, Ext, CallResult}; /// Returns new address created from address and given nonce. pub fn contract_address(address: &Address, nonce: &U256) -> Address { @@ -38,6 +38,8 @@ impl Substate { contracts_created: vec![] } } + + pub fn out_of_gas(&self) -> bool { self.out_of_gas } } /// Transaction execution receipt. @@ -207,7 +209,8 @@ impl<'a> Executive<'a> { let evm = Factory::create(); evm.exec(¶ms, &mut ext) }; - self.handle_out_of_gas(res, substate, backup) + self.revert_if_needed(&res, substate, backup); + res } else { // otherwise, nothing Ok(params.gas) @@ -232,7 +235,8 @@ impl<'a> Executive<'a> { let evm = Factory::create(); evm.exec(¶ms, &mut ext) }; - self.handle_out_of_gas(res, substate, backup) + self.revert_if_needed(&res, substate, backup); + res } /// Finalizes the transaction (does refunds and suicides). @@ -278,12 +282,11 @@ impl<'a> Executive<'a> { } } - pub fn handle_out_of_gas(&mut self, result: evm::Result, substate: &mut Substate, backup: State) -> evm::Result { - if let &Err(evm::Error::OutOfGas) = &result { + pub fn revert_if_needed(&mut self, result: &evm::Result, substate: &mut Substate, backup: State) { + if let &Err(evm::Error::OutOfGas) = result { substate.out_of_gas = true; self.state.revert(backup); } - result } } @@ -398,7 +401,14 @@ impl<'a> Ext for Externalities<'a> { //ex.create(¶ms, self.substate).map(|gas_left| (gas_left, Some(address))) } - fn call(&mut self, gas: &U256, call_gas: &U256, receive_address: &Address, value: &U256, data: &[u8], code_address: &Address, output: &mut [u8]) -> Result { + fn call(&mut self, + gas: &U256, + call_gas: &U256, + receive_address: &Address, + value: &U256, + data: &[u8], + code_address: &Address, + output: &mut [u8]) -> Option { let mut gas_cost = *call_gas; let mut call_gas = *call_gas; @@ -414,14 +424,17 @@ impl<'a> Ext for Externalities<'a> { } if gas_cost > *gas { - return Err(evm::Error::OutOfGas) + self.substate.out_of_gas = true; + return None; + //return (U256::from(-1i64 as u64), false); } let gas = *gas - gas_cost; // if balance is insufficient or we are to deep, return if self.state.balance(&self.params.address) < *value || self.depth >= self.schedule.max_depth { - return Ok(gas + call_gas) + return Some(CallResult::new(gas + call_gas, true)); + //return (gas + call_gas, true); } let params = ActionParams { @@ -436,7 +449,14 @@ impl<'a> Ext for Externalities<'a> { }; let mut ex = Executive::from_parent(self.state, self.info, self.engine, self.depth); - ex.call(¶ms, self.substate, BytesRef::Fixed(output)).map(|gas_left| gas + gas_left) + match ex.call(¶ms, self.substate, BytesRef::Fixed(output)) { + Ok(gas_left) => Some(CallResult::new(gas + gas_left, true)), + _ => { + self.substate.out_of_gas = true; + Some(CallResult::new(gas, false)) + } + } + //ex.call(¶ms, self.substate, BytesRef::Fixed(output)).map(|gas_left| gas + gas_left) } fn extcode(&self, address: &Address) -> Vec { diff --git a/src/tests/executive.rs b/src/tests/executive.rs index b076c2a31..704ce8259 100644 --- a/src/tests/executive.rs +++ b/src/tests/executive.rs @@ -4,7 +4,7 @@ use executive::*; use spec::*; use engine::*; use evm; -use evm::{Schedule, Ext, Factory}; +use evm::{Schedule, Ext, Factory, CallResult}; use ethereum; struct TestEngine { @@ -110,21 +110,20 @@ impl<'a> Ext for TestExt<'a> { value: &U256, data: &[u8], code_address: &Address, - output: &mut [u8]) -> Result { - let res = self.ext.call(gas, call_gas, receive_address, value, data, code_address, output); + output: &mut [u8]) -> Option { + let opt = self.ext.call(gas, call_gas, receive_address, value, data, code_address, output); let ext = &self.ext; - match res { - Ok(gas_left) if ext.state.balance(&ext.params.address) >= *value => { + if let &Some(_) = &opt { + if ext.state.balance(&ext.params.address) >= *value { self.callcreates.push(CallCreate { data: data.to_vec(), destination: receive_address.clone(), _gas_limit: *call_gas, value: *value }); - Ok(gas_left) - }, - other => other + } } + opt } fn extcode(&self, address: &Address) -> Vec {