propagate evmjit errors upstream
This commit is contained in:
parent
dea9ec203b
commit
bbb25fb6ce
@ -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
|
||||
|
@ -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<Address>), 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<u64> {
|
||||
// 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<u64, EvmError> {
|
||||
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<u8> {
|
||||
@ -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);
|
||||
|
@ -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<Address>), 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<u64>;
|
||||
/// 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<u64, EvmError>;
|
||||
|
||||
/// Returns code at given address
|
||||
fn extcode(&self, address: &Address) -> Vec<u8>;
|
||||
|
@ -160,13 +160,15 @@ impl IntoJit<evmjit::RuntimeDataHandle> for RuntimeData {
|
||||
}
|
||||
|
||||
struct ExtAdapter<'a> {
|
||||
ext: &'a mut evm::Ext
|
||||
ext: &'a mut evm::Ext,
|
||||
err: &'a mut Option<evm::EvmError>
|
||||
}
|
||||
|
||||
impl<'a> ExtAdapter<'a> {
|
||||
fn new(ext: &'a mut evm::Ext) -> Self {
|
||||
fn new(ext: &'a mut evm::Ext, err: &'a mut Option<evm::EvmError>) -> 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)),
|
||||
|
Loading…
Reference in New Issue
Block a user