Skip to content
This repository has been archived by the owner on Nov 6, 2020. It is now read-only.

Executive revert fix #115

Merged
merged 16 commits into from
Jan 14, 2016
2 changes: 1 addition & 1 deletion src/account.rs
Original file line number Diff line number Diff line change
Expand Up @@ -334,4 +334,4 @@ mod tests {
assert_eq!(a.rlp().to_hex(), "f8448045a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a0c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470");
}

}
}
2 changes: 1 addition & 1 deletion src/env_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ impl FromJson for EnvInfo {
difficulty: xjson!(&json["currentDifficulty"]),
gas_limit: xjson!(&json["currentGasLimit"]),
timestamp: xjson!(&json["currentTimestamp"]),
last_hashes: (1..257).map(|i| format!("{}", current_number - i).as_bytes().sha3()).collect(),
last_hashes: (1..cmp::min(current_number + 1, 257)).map(|i| format!("{}", current_number - i).as_bytes().sha3()).collect(),
gas_used: x!(0),
}
}
Expand Down
13 changes: 6 additions & 7 deletions src/evm/ext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,23 +21,22 @@ pub trait Ext {

/// Creates new contract.
///
/// 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 `Error`.
fn create(&mut self, gas: &U256, value: &U256, code: &[u8]) -> Result<(U256, Option<Address>), Error>;
/// Returns gas_left and contract address if contract creation was succesfull.
fn create(&mut self, gas: &U256, value: &U256, code: &[u8]) -> (U256, Option<Address>);

/// Message call.
///
/// If call is successfull, returns gas left.
/// otherwise `Error`.
/// 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,
gas: &U256,
call_gas: &U256,
receive_address: &Address,
value: &U256,
data: &[u8],
code_address: &Address,
output: &mut [u8]) -> Result<U256, Error>;
output: &mut [u8]) -> Result<(U256, bool), Error>;

/// Returns code at given address
fn extcode(&self, address: &Address) -> Vec<u8>;
Expand Down
40 changes: 14 additions & 26 deletions src/evm/jit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -209,23 +209,12 @@ impl<'a> evmjit::Ext for ExtAdapter<'a> {
init_size: u64,
address: *mut evmjit::H256) {
unsafe {
match self.ext.create(&U256::from(*io_gas), &U256::from_jit(&*endowment), slice::from_raw_parts(init_beg, init_size as usize)) {
Ok((gas_left, opt)) => {
*io_gas = gas_left.low_u64();
*address = match opt {
Some(addr) => addr.into_jit(),
_ => Address::new().into_jit()
};
},
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
*io_gas = -1i64 as u64;
},
Err(err) => *self.err = Some(err)
}
let (gas_left, opt_addr) = self.ext.create(&U256::from(*io_gas), &U256::from_jit(&*endowment), slice::from_raw_parts(init_beg, init_size as usize));
*io_gas = gas_left.low_u64();
*address = match opt_addr {
Some(addr) => addr.into_jit(),
_ => Address::new().into_jit()
};
}
}

Expand All @@ -247,22 +236,21 @@ impl<'a> evmjit::Ext for ExtAdapter<'a> {
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) => {
Ok((gas_left, ok)) => {
*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
ok
}
Err(evm::Error::OutOfGas) => {
// hack to propagate out_of_gas to evmjit.
// must be negative
*io_gas = -1i64 as u64;
false
},
Err(err) => {
// internal error.
*self.err = Some(err);
*io_gas = -1i64 as u64;
false
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/evm/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
pub use self::factory::Factory;
pub use self::schedule::Schedule;
4 changes: 2 additions & 2 deletions src/evm/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ impl Ext for FakeExt {
self.blockhashes.get(number).unwrap_or(&H256::new()).clone()
}

fn create(&mut self, _gas: &U256, _value: &U256, _code: &[u8]) -> result::Result<(U256, Option<Address>), evm::Error> {
fn create(&mut self, _gas: &U256, _value: &U256, _code: &[u8]) -> (U256, Option<Address>) {
unimplemented!();
}

Expand All @@ -53,7 +53,7 @@ impl Ext for FakeExt {
_value: &U256,
_data: &[u8],
_code_address: &Address,
_output: &mut [u8]) -> result::Result<U256, evm::Error> {
_output: &mut [u8]) -> result::Result<(U256, bool), evm::Error> {
unimplemented!();
}

Expand Down
113 changes: 80 additions & 33 deletions src/executive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ pub struct Substate {
logs: Vec<LogEntry>,
/// Refund counter of SSTORE nonzero->zero.
refunds_count: U256,
/// True if transaction, or one of its subcalls runs out of gas.
out_of_gas: bool,
/// Created contracts.
contracts_created: Vec<Address>
}
Expand All @@ -32,9 +34,12 @@ impl Substate {
suicides: HashSet::new(),
logs: vec![],
refunds_count: U256::zero(),
out_of_gas: false,
contracts_created: vec![]
}
}

pub fn out_of_gas(&self) -> bool { self.out_of_gas }
}

/// Transaction execution receipt.
Expand Down Expand Up @@ -135,7 +140,6 @@ impl<'a> Executive<'a> {
self.state.sub_balance(&sender, &U256::from(gas_cost));

let mut substate = Substate::new();
let backup = self.state.clone();

let schedule = self.engine.schedule(self.info);
let init_gas = t.gas - U256::from(t.gas_required(&schedule));
Expand Down Expand Up @@ -172,14 +176,17 @@ impl<'a> Executive<'a> {
};

// finalize here!
Ok(try!(self.finalize(t, substate, backup, res)))
Ok(try!(self.finalize(t, substate, res)))
}

/// 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 `evm::Error`.
pub fn call(&mut self, params: &ActionParams, substate: &mut Substate, mut output: BytesRef) -> evm::Result {
// backup used in case of running out of gas
let backup = self.state.clone();

// at first, transfer value to destination
self.state.transfer_balance(&params.sender, &params.address, &params.value);

Expand All @@ -191,13 +198,19 @@ impl<'a> Executive<'a> {
self.engine.execute_builtin(&params.address, &params.data, &mut output);
Ok(params.gas - cost)
},
false => Err(evm::Error::OutOfGas)
// just drain the whole gas
false => Ok(U256::zero())
}
} else if params.code.len() > 0 {
// if destination is a contract, do normal message call
let mut ext = Externalities::from_executive(self, params, substate, OutputPolicy::Return(output));
let evm = Factory::create();
evm.exec(&params, &mut ext)

let res = {
let mut ext = Externalities::from_executive(self, params, substate, OutputPolicy::Return(output));
let evm = Factory::create();
evm.exec(&params, &mut ext)
};
self.revert_if_needed(&res, substate, backup);
res
} else {
// otherwise, nothing
Ok(params.gas)
Expand All @@ -208,32 +221,28 @@ impl<'a> Executive<'a> {
/// NOTE. It does not finalize the transaction (doesn't do refunds, nor suicides).
/// Modifies the substate.
fn create(&mut self, params: &ActionParams, substate: &mut Substate) -> evm::Result {
// backup used in case of running out of gas
let backup = self.state.clone();

// at first create new contract
self.state.new_contract(&params.address);

// then transfer value to it
self.state.transfer_balance(&params.sender, &params.address, &params.value);

let mut ext = Externalities::from_executive(self, params, substate, OutputPolicy::InitContract);
let evm = Factory::create();
evm.exec(&params, &mut ext)
let res = {
let mut ext = Externalities::from_executive(self, params, substate, OutputPolicy::InitContract);
let evm = Factory::create();
evm.exec(&params, &mut ext)
};
self.revert_if_needed(&res, substate, backup);
res
}

/// Finalizes the transaction (does refunds and suicides).
fn finalize(&mut self, t: &Transaction, substate: Substate, backup: State, result: evm::Result) -> ExecutionResult {
fn finalize(&mut self, t: &Transaction, substate: Substate, result: evm::Result) -> ExecutionResult {
match result {
Err(evm::Error::Internal) => Err(ExecutionError::Internal),
Err(evm::Error::OutOfGas) => {
*self.state = backup;
Ok(Executed {
gas: t.gas,
gas_used: t.gas,
refunded: U256::zero(),
cumulative_gas_used: self.info.gas_used + t.gas,
logs: vec![],
out_of_gas: true,
contracts_created: vec![]
})
},
Ok(gas_left) => {
let schedule = self.engine.schedule(self.info);

Expand Down Expand Up @@ -265,12 +274,37 @@ impl<'a> Executive<'a> {
refunded: refund,
cumulative_gas_used: self.info.gas_used + gas_used,
logs: substate.logs,
out_of_gas: false,
out_of_gas: substate.out_of_gas,
contracts_created: substate.contracts_created
})
},
_err => {
Ok(Executed {
gas: t.gas,
gas_used: t.gas,
refunded: U256::zero(),
cumulative_gas_used: self.info.gas_used + t.gas,
logs: vec![],
out_of_gas: true,
contracts_created: vec![]
})
}
}
}

fn revert_if_needed(&mut self, result: &evm::Result, substate: &mut Substate, backup: State) {
// TODO: handle other evm::Errors same as OutOfGas once they are implemented
match &result {
&Err(evm::Error::OutOfGas) => {
substate.out_of_gas = true;
self.state.revert(backup);
},
&Err(evm::Error::Internal) => (),
&Ok(_) => ()

}
result
}
}

/// Policy for handling output data on `RETURN` opcode.
Expand Down Expand Up @@ -354,10 +388,10 @@ impl<'a> Ext for Externalities<'a> {
}
}

fn create(&mut self, gas: &U256, value: &U256, code: &[u8]) -> Result<(U256, Option<Address>), evm::Error> {
fn create(&mut self, gas: &U256, value: &U256, code: &[u8]) -> (U256, Option<Address>) {
// 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, None));
return (*gas, None);
}

// create new contract address
Expand All @@ -377,10 +411,20 @@ impl<'a> Ext for Externalities<'a> {

let mut ex = Executive::from_parent(self.state, self.info, self.engine, self.depth);
ex.state.inc_nonce(&self.params.address);
ex.create(&params, self.substate).map(|gas_left| (gas_left, Some(address)))
match ex.create(&params, self.substate) {
Ok(gas_left) => (gas_left, Some(address)),
_ => (U256::zero(), None)
}
}

fn call(&mut self, gas: &U256, call_gas: &U256, receive_address: &Address, value: &U256, data: &[u8], code_address: &Address, output: &mut [u8]) -> Result<U256, evm::Error> {
fn call(&mut self,
gas: &U256,
call_gas: &U256,
receive_address: &Address,
value: &U256,
data: &[u8],
code_address: &Address,
output: &mut [u8]) -> Result<(U256, bool), evm::Error> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

consider a less general type than bool

let mut gas_cost = *call_gas;
let mut call_gas = *call_gas;

Expand All @@ -396,14 +440,14 @@ impl<'a> Ext for Externalities<'a> {
}

if gas_cost > *gas {
return Err(evm::Error::OutOfGas)
return Err(evm::Error::OutOfGas);
}

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 Ok((gas + call_gas, true));
}

let params = ActionParams {
Expand All @@ -418,7 +462,10 @@ impl<'a> Ext for Externalities<'a> {
};

let mut ex = Executive::from_parent(self.state, self.info, self.engine, self.depth);
ex.call(&params, self.substate, BytesRef::Fixed(output)).map(|gas_left| gas + gas_left)
match ex.call(&params, self.substate, BytesRef::Fixed(output)) {
Ok(gas_left) => Ok((gas + gas_left, true)), //Some(CallResult::new(gas + gas_left, true)),
_ => Ok((gas, false))
}
}

fn extcode(&self, address: &Address) -> Vec<u8> {
Expand Down Expand Up @@ -832,9 +879,9 @@ mod tests {
};

assert_eq!(executed.gas, U256::from(100_000));
assert_eq!(executed.gas_used, U256::from(20_025));
assert_eq!(executed.refunded, U256::from(79_975));
assert_eq!(executed.cumulative_gas_used, U256::from(20_025));
assert_eq!(executed.gas_used, U256::from(41_301));
assert_eq!(executed.refunded, U256::from(58_699));
assert_eq!(executed.cumulative_gas_used, U256::from(41_301));
assert_eq!(executed.logs.len(), 0);
assert_eq!(executed.out_of_gas, false);
assert_eq!(executed.contracts_created.len(), 0);
Expand Down
7 changes: 6 additions & 1 deletion src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ impl State {

/// Mutate storage of account `a` so that it is `value` for `key`.
pub fn set_storage(&mut self, a: &Address, key: H256, value: H256) {
self.require(a, false).set_storage(key, value);
self.require(a, false).set_storage(key, value)
}

/// Initialise the code of account `a` so that it is `value` for `key`.
Expand All @@ -140,10 +140,15 @@ impl State {
/// This will change the state accordingly.
pub fn apply(&mut self, env_info: &EnvInfo, engine: &Engine, t: &Transaction) -> ApplyResult {
let e = try!(Executive::new(self, env_info, engine).transact(t));
//println!("Executed: {:?}", e);
self.commit();
Ok(Receipt::new(self.root().clone(), e.gas_used, e.logs))
}

pub fn revert(&mut self, backup: State) {
self.cache = backup.cache;
}

/// Convert into a JSON representation.
pub fn as_json(&self) -> String {
unimplemented!();
Expand Down
Loading