diff --git a/src/evm/ext.rs b/src/evm/ext.rs index d74f847d7..b0a93d662 100644 --- a/src/evm/ext.rs +++ b/src/evm/ext.rs @@ -6,12 +6,35 @@ use util::bytes::*; use evm::{Schedule, Error}; use env_info::*; +/// Result of externalities create function. +pub enum ContractCreateResult { + /// Returned when creation was successfull. + /// Contains an address of newly created contract and gas left. + Created(Address, U256), + /// Returned when contract creation failed. + /// VM doesn't have to know the reason. + Failed +} + +/// Result of externalities call function. +pub enum MessageCallResult { + /// Returned when message call was successfull. + /// Contains gas left. + Success(U256), + /// Returned when message call failed. + /// VM doesn't have to know the reason. + Failed +} + pub trait Ext { /// Returns a value for given key. fn storage_at(&self, key: &H256) -> H256; /// Stores a value for given key. - fn set_storage_at(&mut self, key: H256, value: H256); + fn set_storage(&mut self, key: H256, value: H256); + + /// Determine whether an account exists. + fn exists(&self, address: &Address) -> bool; /// Returns address balance. fn balance(&self, address: &Address) -> U256; @@ -22,7 +45,7 @@ pub trait Ext { /// Creates new contract. /// /// Returns gas_left and contract address if contract creation was succesfull. - fn create(&mut self, gas: &U256, value: &U256, code: &[u8]) -> (U256, Option
); + fn create(&mut self, gas: &U256, value: &U256, code: &[u8]) -> ContractCreateResult; /// Message call. /// @@ -31,12 +54,11 @@ pub trait Ext { /// and true if subcall was successfull. fn call(&mut self, gas: &U256, - call_gas: &U256, - receive_address: &Address, + address: &Address, value: &U256, data: &[u8], code_address: &Address, - output: &mut [u8]) -> Result<(U256, bool), Error>; + output: &mut [u8]) -> MessageCallResult; /// Returns code at given address fn extcode(&self, address: &Address) -> Vec; @@ -57,4 +79,13 @@ pub trait Ext { /// Returns environment info. fn env_info(&self) -> &EnvInfo; + + /// Returns current depth of execution. + /// + /// If contract A calls contract B, and contract B calls C, + /// then A depth is 0, B is 1, C is 2 and so on. + fn depth(&self) -> usize; + + /// Increments sstore refunds count by 1. + fn add_sstore_refund(&mut self); } diff --git a/src/evm/jit.rs b/src/evm/jit.rs index 122947d32..c8a3c4b66 100644 --- a/src/evm/jit.rs +++ b/src/evm/jit.rs @@ -159,31 +159,36 @@ impl IntoJit for RuntimeData { /// This adapter 'catches' them and moves upstream. struct ExtAdapter<'a> { ext: &'a mut evm::Ext, - err: &'a mut Option + address: Address } impl<'a> ExtAdapter<'a> { - fn new(ext: &'a mut evm::Ext, err: &'a mut Option) -> Self { + fn new(ext: &'a mut evm::Ext, address: Address) -> Self { ExtAdapter { ext: ext, - err: err + address: address } } } impl<'a> evmjit::Ext for ExtAdapter<'a> { - fn sload(&self, index: *const evmjit::I256, out_value: *mut evmjit::I256) { + fn sload(&self, key: *const evmjit::I256, out_value: *mut evmjit::I256) { unsafe { - let i = H256::from_jit(&*index); + let i = H256::from_jit(&*key); let o = self.ext.storage_at(&i); *out_value = o.into_jit(); } } - fn sstore(&mut self, index: *const evmjit::I256, value: *const evmjit::I256) { - unsafe { - self.ext.set_storage_at(H256::from_jit(&*index), H256::from_jit(&*value)); + fn sstore(&mut self, key: *const evmjit::I256, value: *const evmjit::I256) { + let key = unsafe { H256::from_jit(&*key) }; + let value = unsafe { H256::from_jit(&*value) }; + let old_value = self.ext.storage_at(&key); + // if SSTORE nonzero -> zero, increment refund count + if !old_value.is_zero() && value.is_zero() { + self.ext.add_sstore_refund(); } + self.ext.set_storage(key, value); } fn balance(&self, address: *const evmjit::H256, out_value: *mut evmjit::I256) { @@ -204,17 +209,29 @@ impl<'a> evmjit::Ext for ExtAdapter<'a> { fn create(&mut self, io_gas: *mut u64, - endowment: *const evmjit::I256, + value: *const evmjit::I256, init_beg: *const u8, init_size: u64, address: *mut evmjit::H256) { + + let gas = unsafe { U256::from(*io_gas) }; + let value = unsafe { U256::from_jit(&*value) }; + let code = unsafe { slice::from_raw_parts(init_beg, init_size as usize) }; + + // check if balance is sufficient and we are not too deep + if self.ext.balance(&self.address) >= value && self.ext.depth() < self.ext.schedule().max_depth { + if let evm::ContractCreateResult::Created(new_address, gas_left) = self.ext.create(&gas, &value, code) { + unsafe { + *io_gas = gas_left.low_u64(); + *address = new_address.into_jit(); + return; + } + } + } + unsafe { - 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() - }; + *io_gas = 0; + *address = Address::new().into_jit(); } } @@ -228,31 +245,56 @@ impl<'a> evmjit::Ext for ExtAdapter<'a> { out_beg: *mut u8, out_size: u64, code_address: *const evmjit::H256) -> bool { - unsafe { - let res = 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, ok)) => { - *io_gas = gas_left.low_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 - } + + let mut gas = unsafe { U256::from(*io_gas) }; + let mut call_gas = U256::from(call_gas); + let mut gas_cost = call_gas; + let receive_address = unsafe { Address::from_jit(&*receive_address) }; + let code_address = unsafe { Address::from_jit(&*code_address) }; + let value = unsafe { U256::from_jit(&*value) }; + + // receive address and code address are the same in normal calls + let is_callcode = receive_address != code_address; + if !is_callcode && !self.ext.exists(&code_address) { + gas_cost = gas_cost + U256::from(self.ext.schedule().call_new_account_gas); + } + + if value > U256::zero() { + assert!(self.ext.schedule().call_value_transfer_gas > self.ext.schedule().call_stipend, "overflow possible"); + gas_cost = gas_cost + U256::from(self.ext.schedule().call_value_transfer_gas); + call_gas = call_gas + U256::from(self.ext.schedule().call_stipend); + } + + if gas_cost > gas { + unsafe { + *io_gas = -1i64 as u64; + return false; + } + } + + gas = gas - gas_cost; + + // check if balance is sufficient and we are not too deep + if self.ext.balance(&self.address) < value || self.ext.depth() >= self.ext.schedule().max_depth { + unsafe { + *io_gas = (gas + call_gas).low_u64(); + return false; + } + } + + match self.ext.call(&call_gas, + &receive_address, + &value, + unsafe { slice::from_raw_parts(in_beg, in_size as usize) }, + &code_address, + unsafe { slice::from_raw_parts_mut(out_beg, out_size as usize) }) { + evm::MessageCallResult::Success(gas_left) => unsafe { + *io_gas = (gas + gas_left).low_u64(); + true + }, + evm::MessageCallResult::Failed => unsafe { + *io_gas = gas.low_u64(); + false } } } @@ -303,9 +345,8 @@ pub struct JitEvm; impl evm::Evm for JitEvm { fn exec(&self, params: &ActionParams, ext: &mut evm::Ext) -> evm::Result { - 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, &mut optional_err)) }; + let ext_adapter: ExtAdapter<'static> = unsafe { ::std::mem::transmute(ExtAdapter::new(ext, params.address.clone())) }; let mut ext_handle = evmjit::ExtHandle::new(ext_adapter); let mut data = RuntimeData::new(); data.gas = params.gas; @@ -326,11 +367,6 @@ impl evm::Evm for JitEvm { let mut context = unsafe { evmjit::ContextHandle::new(data.into_jit(), &mut ext_handle) }; 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 => ext.ret(&U256::from(context.gas_left()), context.output_data()), diff --git a/src/evm/mod.rs b/src/evm/mod.rs index e84e133c5..01da1b86c 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, ContractCreateResult, MessageCallResult}; pub use self::factory::Factory; pub use self::schedule::Schedule; diff --git a/src/evm/tests.rs b/src/evm/tests.rs index f43881424..8a0ddfc3f 100644 --- a/src/evm/tests.rs +++ b/src/evm/tests.rs @@ -34,6 +34,10 @@ impl Ext for FakeExt { self.store.insert(key, value); } + fn exists(&self, address: &Address) -> bool { + unimplemented!(); + } + fn balance(&self, _address: &Address) -> U256 { unimplemented!(); } @@ -83,6 +87,10 @@ impl Ext for FakeExt { fn env_info(&self) -> &EnvInfo { &self.info } + + fn depth(&self) -> usize { + unimplemented!(); + } } #[test] diff --git a/src/executive.rs b/src/executive.rs index a7d3c47df..30c5f0f05 100644 --- a/src/executive.rs +++ b/src/executive.rs @@ -203,7 +203,7 @@ impl<'a> Executive<'a> { evm.exec(¶ms, &mut ext) }; - trace!("exec: sstore-clears={}\n", unconfirmed_substate.refunds_count); + trace!("exec: sstore-clears={}\n", unconfirmed_substate.sstore_refunds_count); trace!("exec: substate={:?}; unconfirmed_substate={:?}\n", substate, unconfirmed_substate); self.enact_result(&res, substate, unconfirmed_substate, backup); trace!("exec: new substate={:?}\n", substate); @@ -244,7 +244,7 @@ impl<'a> Executive<'a> { let schedule = self.engine.schedule(self.info); // refunds from SSTORE nonzero -> zero - let sstore_refunds = U256::from(schedule.sstore_refund_gas) * substate.refunds_count; + let sstore_refunds = U256::from(schedule.sstore_refund_gas) * substate.sstore_refunds_count; // refunds from contract suicides let suicide_refunds = U256::from(schedule.suicide_refund_gas) * U256::from(substate.suicides.len()); let refunds_bound = sstore_refunds + suicide_refunds; diff --git a/src/externalities.rs b/src/externalities.rs index 8c75a71f4..725e50e53 100644 --- a/src/externalities.rs +++ b/src/externalities.rs @@ -3,7 +3,7 @@ use common::*; use state::*; use engine::*; use executive::*; -use evm::{self, Schedule, Ext}; +use evm::{self, Schedule, Ext, ContractCreateResult, MessageCallResult}; use substate::*; /// Policy for handling output data on `RETURN` opcode. @@ -61,21 +61,25 @@ impl<'a> Externalities<'a> { impl<'a> Ext for Externalities<'a> { fn storage_at(&self, key: &H256) -> H256 { - trace!("ext: storage_at({}, {}) == {}\n", self.params.address, key, U256::from(self.state.storage_at(&self.params.address, key).as_slice())); + //trace!("ext: storage_at({}, {}) == {}\n", self.params.address, key, U256::from(self.state.storage_at(&self.params.address, key).as_slice())); self.state.storage_at(&self.params.address, key) } - fn set_storage_at(&mut self, key: H256, value: H256) { - let old = self.state.storage_at(&self.params.address, &key); + fn set_storage(&mut self, key: H256, value: H256) { + //let old = self.state.storage_at(&self.params.address, &key); // if SSTORE nonzero -> zero, increment refund count - if value.is_zero() && !old.is_zero() { - trace!("ext: additional refund. {} -> {}\n", self.substate.refunds_count, self.substate.refunds_count + x!(1)); - self.substate.refunds_count = self.substate.refunds_count + U256::one(); - } - trace!("ext: set_storage_at({}, {}): {} -> {}\n", self.params.address, key, U256::from(old.as_slice()), U256::from(value.as_slice())); + //if value.is_zero() && !old.is_zero() { + //trace!("ext: additional refund. {} -> {}\n", self.substate.refunds_count, self.substate.refunds_count + x!(1)); + //self.substate.refunds_count = self.substate.refunds_count + U256::one(); + //} + //trace!("ext: set_storage_at({}, {}): {} -> {}\n", self.params.address, key, U256::from(old.as_slice()), U256::from(value.as_slice())); self.state.set_storage(&self.params.address, key, value) } + fn exists(&self, address: &Address) -> bool { + self.state.exists(address) + } + fn balance(&self, address: &Address) -> U256 { self.state.balance(address) } @@ -95,12 +99,7 @@ impl<'a> Ext for Externalities<'a> { } } - fn create(&mut self, gas: &U256, value: &U256, code: &[u8]) -> (U256, Option
) { - // 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 (*gas, None); - } - + fn create(&mut self, gas: &U256, value: &U256, code: &[u8]) -> ContractCreateResult { // create new contract address let address = contract_address(&self.params.address, &self.state.nonce(&self.params.address)); @@ -119,71 +118,42 @@ impl<'a> Ext for Externalities<'a> { self.state.inc_nonce(&self.params.address); let mut ex = Executive::from_parent(self.state, self.info, self.engine, self.depth); + + // TODO: handle internal error separately match ex.create(¶ms, self.substate) { - Ok(gas_left) => (gas_left, Some(address)), - _ => (U256::zero(), None) + Ok(gas_left) => { + self.substate.contracts_created.push(address.clone()); + ContractCreateResult::Created(address, gas_left) + }, + _ => ContractCreateResult::Failed } } fn call(&mut self, gas: &U256, - call_gas: &U256, - receive_address: &Address, + address: &Address, value: &U256, data: &[u8], code_address: &Address, - output: &mut [u8]) -> Result<(U256, bool), evm::Error> { - - let mut gas_cost = *call_gas; - let mut call_gas = *call_gas; - - let is_call = receive_address == code_address; - if is_call && !self.state.exists(&code_address) { - gas_cost = gas_cost + U256::from(self.schedule.call_new_account_gas); - } - - if *value > U256::zero() { - assert!(self.schedule.call_value_transfer_gas > self.schedule.call_stipend, "overflow possible"); - gas_cost = gas_cost + U256::from(self.schedule.call_value_transfer_gas); - call_gas = call_gas + U256::from(self.schedule.call_stipend); - } - - debug!("Externalities::call(gas={}, call_gas={}, recv={}, value={}, data={}, code={})\n", gas, call_gas, receive_address, value, data.pretty(), code_address); - - if gas_cost > *gas { - debug!("Externalities::call: OutOfGas gas_cost={}, gas={}", gas_cost, gas); - return Err(evm::Error::OutOfGas); - } - - let gas = *gas - gas_cost; - - // if balance is insufficient or we are too deep, return - if self.state.balance(&self.params.address) < *value || self.depth >= self.schedule.max_depth { - debug!("Externalities::call: OutOfCash bal({})={}, value={}", self.params.address, self.state.balance(&self.params.address), value); - return Ok((gas + call_gas, false)); - } + output: &mut [u8]) -> MessageCallResult { let params = ActionParams { code_address: code_address.clone(), - address: receive_address.clone(), + address: address.clone(), sender: self.params.address.clone(), origin: self.params.origin.clone(), - gas: call_gas, + gas: *gas, gas_price: self.params.gas_price.clone(), value: value.clone(), code: self.state.code(code_address), data: Some(data.to_vec()), }; + let mut ex = Executive::from_parent(self.state, self.info, self.engine, self.depth); - trace!("Externalities::call: BEFORE: bal({})={}, bal({})={}\n", params.sender, self.state.balance(¶ms.sender), params.address, self.state.balance(¶ms.address)); - trace!("Externalities::call: CALLING: params={:?}\n", params); - let r = Executive::from_parent(self.state, self.info, self.engine, self.depth).call(¶ms, self.substate, BytesRef::Fixed(output)); - trace!("Externalities::call: AFTER: bal({})={}, bal({})={}\n", params.sender, self.state.balance(¶ms.sender), params.address, self.state.balance(¶ms.address)); - - match r { - Ok(gas_left) => Ok((gas + gas_left, true)), - _ => Ok((gas, false)) + match ex.call(¶ms, self.substate, BytesRef::Fixed(output)) { + Ok(gas_left) => MessageCallResult::Success(gas_left), + _ => MessageCallResult::Failed } } @@ -221,7 +191,6 @@ impl<'a> Ext for Externalities<'a> { } let address = &self.params.address; self.state.init_code(address, code); - self.substate.contracts_created.push(address.clone()); Ok(*gas - return_cost) } } @@ -246,4 +215,12 @@ impl<'a> Ext for Externalities<'a> { fn env_info(&self) -> &EnvInfo { &self.info } + + fn depth(&self) -> usize { + self.depth + } + + fn add_sstore_refund(&mut self) { + self.substate.sstore_refunds_count = self.substate.sstore_refunds_count + U256::one(); + } } diff --git a/src/substate.rs b/src/substate.rs index b227afacc..d3bbc12cc 100644 --- a/src/substate.rs +++ b/src/substate.rs @@ -8,8 +8,8 @@ pub struct Substate { pub suicides: HashSet
, /// Any logs. pub logs: Vec, - /// Refund counter of SSTORE nonzero->zero. - pub refunds_count: U256, + /// Refund counter of SSTORE nonzero -> zero. + pub sstore_refunds_count: U256, /// Created contracts. pub contracts_created: Vec
} @@ -20,7 +20,7 @@ impl Substate { Substate { suicides: HashSet::new(), logs: vec![], - refunds_count: U256::zero(), + sstore_refunds_count: U256::zero(), contracts_created: vec![] } } @@ -28,7 +28,7 @@ impl Substate { pub fn accrue(&mut self, s: Substate) { self.suicides.extend(s.suicides.into_iter()); self.logs.extend(s.logs.into_iter()); - self.refunds_count = self.refunds_count + s.refunds_count; + self.sstore_refunds_count = self.sstore_refunds_count + s.sstore_refunds_count; self.contracts_created.extend(s.contracts_created.into_iter()); } } diff --git a/src/tests/executive.rs b/src/tests/executive.rs index 4581a27cc..bd8817bb9 100644 --- a/src/tests/executive.rs +++ b/src/tests/executive.rs @@ -65,6 +65,10 @@ impl<'a> Ext for TestExt<'a> { self.ext.set_storage_at(key, value) } + fn exists(&self, address: &Address) -> bool { + self.ext.exists(address) + } + fn balance(&self, address: &Address) -> U256 { self.ext.balance(address) } @@ -152,6 +156,10 @@ impl<'a> Ext for TestExt<'a> { fn env_info(&self) -> &EnvInfo { self.ext.env_info() } + + fn depth(&self) -> usize { + self.ext.depth() + } } fn do_json_test(json_data: &[u8]) -> Vec {