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)),