From 65d6904e6373d96a240ac1aca943d4d925cdb651 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Fri, 15 Jan 2016 14:22:46 +0100 Subject: [PATCH] Split externalities from executive. --- src/executive.rs | 275 +++-------------------------------------- src/externalities.rs | 228 ++++++++++++++++++++++++++++++++++ src/lib.rs | 4 +- src/substate.rs | 33 +++++ src/tests/executive.rs | 46 +++---- 5 files changed, 303 insertions(+), 283 deletions(-) create mode 100644 src/externalities.rs create mode 100644 src/substate.rs diff --git a/src/executive.rs b/src/executive.rs index 3251763f4..43aea6c70 100644 --- a/src/executive.rs +++ b/src/executive.rs @@ -2,7 +2,9 @@ use common::*; use state::*; use engine::*; -use evm::{self, Schedule, Factory, Ext}; +use evm::{self, Factory, Ext}; +use externalities::*; +use substate::*; /// Returns new address created from address and given nonce. pub fn contract_address(address: &Address, nonce: &U256) -> Address { @@ -12,38 +14,6 @@ pub fn contract_address(address: &Address, nonce: &U256) -> Address { From::from(stream.out().sha3()) } -/// State changes which should be applied in finalize, -/// after transaction is fully executed. -pub struct Substate { - /// Any accounts that have suicided. - suicides: HashSet
, - /// Any logs. - logs: Vec, - /// Refund counter of SSTORE nonzero->zero. - refunds_count: U256, - /// Created contracts. - contracts_created: Vec
-} - -impl Substate { - /// Creates new substate. - pub fn new() -> Self { - Substate { - suicides: HashSet::new(), - logs: vec![], - refunds_count: U256::zero(), - contracts_created: vec![] - } - } - - 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.contracts_created.extend(s.contracts_created.into_iter()); - } -} - /// Transaction execution receipt. #[derive(Debug)] pub struct Executed { @@ -89,7 +59,7 @@ impl<'a> Executive<'a> { } /// Populates executive from parent properties. Increments executive depth. - fn from_parent(state: &'a mut State, info: &'a EnvInfo, engine: &'a Engine, depth: usize) -> Self { + pub 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) } @@ -104,8 +74,13 @@ impl<'a> Executive<'a> { } } + /// Creates `Externalities` from `Executive`. + pub fn to_externalities<'_>(&'_ mut self, params: &'_ ActionParams, substate: &'_ mut Substate, output: OutputPolicy<'_>) -> Externalities { + Externalities::new(self.state, self.info, self.engine, self.depth, params, substate, output) + } + /// This funtion should be used to execute transaction. - pub fn transact(&mut self, t: &Transaction) -> Result { + pub fn transact(&'a mut self, t: &Transaction) -> Result { let sender = try!(t.sender()); let nonce = self.state.nonce(&sender); @@ -215,7 +190,7 @@ impl<'a> Executive<'a> { let mut unconfirmed_substate = Substate::new(); let res = { - let mut ext = Externalities::from_executive(self, params, &mut unconfirmed_substate, OutputPolicy::Return(output)); + let mut ext = self.to_externalities(params, &mut unconfirmed_substate, OutputPolicy::Return(output)); let evm = Factory::create(); evm.exec(¶ms, &mut ext) }; @@ -230,7 +205,7 @@ impl<'a> Executive<'a> { /// Creates contract with given contract params. /// 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 { + pub 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(); @@ -244,7 +219,7 @@ impl<'a> Executive<'a> { self.state.transfer_balance(¶ms.sender, ¶ms.address, ¶ms.value); let res = { - let mut ext = Externalities::from_executive(self, params, &mut unconfirmed_substate, OutputPolicy::InitContract); + let mut ext = self.to_externalities(params, &mut unconfirmed_substate, OutputPolicy::InitContract); let evm = Factory::create(); evm.exec(¶ms, &mut ext) }; @@ -319,228 +294,6 @@ impl<'a> Executive<'a> { } } -/// Policy for handling output data on `RETURN` opcode. -pub enum OutputPolicy<'a> { - /// Return reference to fixed sized output. - /// Used for message calls. - Return(BytesRef<'a>), - /// Init new contract as soon as `RETURN` is called. - InitContract -} - -/// Implementation of evm Externalities. -pub struct Externalities<'a> { - #[cfg(test)] - pub state: &'a mut State, - #[cfg(not(test))] - state: &'a mut State, - info: &'a EnvInfo, - engine: &'a Engine, - depth: usize, - #[cfg(test)] - pub params: &'a ActionParams, - #[cfg(not(test))] - params: &'a ActionParams, - substate: &'a mut Substate, - schedule: Schedule, - output: OutputPolicy<'a> -} - -impl<'a> Externalities<'a> { - /// Basic `Externalities` constructor. - pub fn new(state: &'a mut State, - info: &'a EnvInfo, - engine: &'a Engine, - depth: usize, - params: &'a ActionParams, - substate: &'a mut Substate, - output: OutputPolicy<'a>) -> Self { - Externalities { - state: state, - info: info, - engine: engine, - depth: depth, - params: params, - substate: substate, - schedule: engine.schedule(info), - output: output - } - } - - /// Creates `Externalities` from `Executive`. - fn from_executive(e: &'a mut Executive, params: &'a ActionParams, substate: &'a mut Substate, output: OutputPolicy<'a>) -> Self { - Self::new(e.state, e.info, e.engine, e.depth, params, substate, output) - } -} - -impl<'a> Ext for Externalities<'a> { - fn sload(&self, key: &H256) -> H256 { - self.state.storage_at(&self.params.address, key) - } - - fn sstore(&mut self, key: H256, value: H256) { - // if SSTORE nonzero -> zero, increment refund count - if value == H256::new() && self.state.storage_at(&self.params.address, &key) != H256::new() { - self.substate.refunds_count = self.substate.refunds_count + U256::one(); - } - self.state.set_storage(&self.params.address, key, value) - } - - fn balance(&self, address: &Address) -> U256 { - self.state.balance(address) - } - - fn blockhash(&self, number: &U256) -> H256 { - match *number < U256::from(self.info.number) && number.low_u64() >= cmp::max(256, self.info.number) - 256 { - true => { - let index = self.info.number - number.low_u64() - 1; - self.info.last_hashes[index as usize].clone() - }, - false => H256::from(&U256::zero()), - } - } - - 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); - } - - // create new contract address - let address = contract_address(&self.params.address, &self.state.nonce(&self.params.address)); - - // prepare the params - let params = ActionParams { - address: address.clone(), - sender: self.params.address.clone(), - origin: self.params.origin.clone(), - gas: *gas, - gas_price: self.params.gas_price.clone(), - value: value.clone(), - code: code.to_vec(), - data: vec![], - }; - - let mut ex = Executive::from_parent(self.state, self.info, self.engine, self.depth); - ex.state.inc_nonce(&self.params.address); - match ex.create(¶ms, 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, 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); - } - - if 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 { - return Ok((gas + call_gas, true)); - } - - let params = ActionParams { - address: receive_address.clone(), - sender: self.params.address.clone(), - origin: self.params.origin.clone(), - gas: call_gas, - gas_price: self.params.gas_price.clone(), - value: value.clone(), - code: self.state.code(code_address).unwrap_or(vec![]), - data: data.to_vec(), - }; - - let mut ex = Executive::from_parent(self.state, self.info, self.engine, self.depth); - match ex.call(¶ms, self.substate, BytesRef::Fixed(output)) { - Ok(gas_left) => Ok((gas + gas_left, true)), - _ => Ok((gas, false)) - } - } - - fn extcode(&self, address: &Address) -> Vec { - self.state.code(address).unwrap_or(vec![]) - } - - fn ret(&mut self, gas: &U256, data: &[u8]) -> Result { - match &mut self.output { - &mut OutputPolicy::Return(BytesRef::Fixed(ref mut slice)) => unsafe { - let len = cmp::min(slice.len(), data.len()); - ptr::copy(data.as_ptr(), slice.as_mut_ptr(), len); - Ok(*gas) - }, - &mut OutputPolicy::Return(BytesRef::Flexible(ref mut vec)) => unsafe { - vec.clear(); - vec.reserve(data.len()); - ptr::copy(data.as_ptr(), vec.as_mut_ptr(), data.len()); - vec.set_len(data.len()); - Ok(*gas) - }, - &mut OutputPolicy::InitContract => { - let return_cost = U256::from(data.len()) * U256::from(self.schedule.create_data_gas); - if return_cost > *gas { - return match self.schedule.exceptional_failed_code_deposit { - true => Err(evm::Error::OutOfGas), - false => Ok(*gas) - } - } - let mut code = vec![]; - code.reserve(data.len()); - unsafe { - ptr::copy(data.as_ptr(), code.as_mut_ptr(), data.len()); - code.set_len(data.len()); - } - let address = &self.params.address; - self.state.init_code(address, code); - self.substate.contracts_created.push(address.clone()); - Ok(*gas - return_cost) - } - } - } - - fn log(&mut self, topics: Vec, data: Bytes) { - let address = self.params.address.clone(); - self.substate.logs.push(LogEntry::new(address, topics, data)); - } - - fn suicide(&mut self, refund_address: &Address) { - let address = self.params.address.clone(); - let balance = self.balance(&address); - self.state.transfer_balance(&address, refund_address, &balance); - self.substate.suicides.insert(address); - } - - fn schedule(&self) -> &Schedule { - &self.schedule - } - - fn env_info(&self) -> &EnvInfo { - &self.info - } -} - #[cfg(test)] mod tests { use super::*; @@ -550,6 +303,8 @@ mod tests { use engine::*; use spec::*; use evm::Schedule; + use substate::*; + use externalities::*; struct TestEngine { spec: Spec, diff --git a/src/externalities.rs b/src/externalities.rs new file mode 100644 index 000000000..84131c8e8 --- /dev/null +++ b/src/externalities.rs @@ -0,0 +1,228 @@ +//! Transaction Execution environment. +use common::*; +use state::*; +use engine::*; +use executive::*; +use evm::{self, Schedule, Ext}; +use substate::*; + +/// Policy for handling output data on `RETURN` opcode. +pub enum OutputPolicy<'a> { + /// Return reference to fixed sized output. + /// Used for message calls. + Return(BytesRef<'a>), + /// Init new contract as soon as `RETURN` is called. + InitContract +} + +/// Implementation of evm Externalities. +pub struct Externalities<'a> { + + #[cfg(test)] + pub state: &'a mut State, + #[cfg(not(test))] + state: &'a mut State, + + info: &'a EnvInfo, + engine: &'a Engine, + depth: usize, + + #[cfg(test)] + pub params: &'a ActionParams, + #[cfg(not(test))] + params: &'a ActionParams, + + substate: &'a mut Substate, + schedule: Schedule, + output: OutputPolicy<'a> +} + +impl<'a> Externalities<'a> { + /// Basic `Externalities` constructor. + pub fn new(state: &'a mut State, + info: &'a EnvInfo, + engine: &'a Engine, + depth: usize, + params: &'a ActionParams, + substate: &'a mut Substate, + output: OutputPolicy<'a>) -> Self { + Externalities { + state: state, + info: info, + engine: engine, + depth: depth, + params: params, + substate: substate, + schedule: engine.schedule(info), + output: output + } + } +} + +impl<'a> Ext for Externalities<'a> { + fn sload(&self, key: &H256) -> H256 { + self.state.storage_at(&self.params.address, key) + } + + fn sstore(&mut self, key: H256, value: H256) { + // if SSTORE nonzero -> zero, increment refund count + if value == H256::new() && self.state.storage_at(&self.params.address, &key) != H256::new() { + self.substate.refunds_count = self.substate.refunds_count + U256::one(); + } + self.state.set_storage(&self.params.address, key, value) + } + + fn balance(&self, address: &Address) -> U256 { + self.state.balance(address) + } + + fn blockhash(&self, number: &U256) -> H256 { + match *number < U256::from(self.info.number) && number.low_u64() >= cmp::max(256, self.info.number) - 256 { + true => { + let index = self.info.number - number.low_u64() - 1; + self.info.last_hashes[index as usize].clone() + }, + false => H256::from(&U256::zero()), + } + } + + 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); + } + + // create new contract address + let address = contract_address(&self.params.address, &self.state.nonce(&self.params.address)); + + // prepare the params + let params = ActionParams { + address: address.clone(), + sender: self.params.address.clone(), + origin: self.params.origin.clone(), + gas: *gas, + gas_price: self.params.gas_price.clone(), + value: value.clone(), + code: code.to_vec(), + data: vec![], + }; + + self.state.inc_nonce(&self.params.address); + let mut ex = Executive::from_parent(self.state, self.info, self.engine, self.depth); + match ex.create(¶ms, 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, 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); + } + + if 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 { + return Ok((gas + call_gas, true)); + } + + let params = ActionParams { + address: receive_address.clone(), + sender: self.params.address.clone(), + origin: self.params.origin.clone(), + gas: call_gas, + gas_price: self.params.gas_price.clone(), + value: value.clone(), + code: self.state.code(code_address).unwrap_or(vec![]), + data: data.to_vec(), + }; + + let mut ex = Executive::from_parent(self.state, self.info, self.engine, self.depth); + match ex.call(¶ms, self.substate, BytesRef::Fixed(output)) { + Ok(gas_left) => Ok((gas + gas_left, true)), + _ => Ok((gas, false)) + } + } + + fn extcode(&self, address: &Address) -> Vec { + self.state.code(address).unwrap_or(vec![]) + } + + fn ret(&mut self, gas: &U256, data: &[u8]) -> Result { + match &mut self.output { + &mut OutputPolicy::Return(BytesRef::Fixed(ref mut slice)) => unsafe { + let len = cmp::min(slice.len(), data.len()); + ptr::copy(data.as_ptr(), slice.as_mut_ptr(), len); + Ok(*gas) + }, + &mut OutputPolicy::Return(BytesRef::Flexible(ref mut vec)) => unsafe { + vec.clear(); + vec.reserve(data.len()); + ptr::copy(data.as_ptr(), vec.as_mut_ptr(), data.len()); + vec.set_len(data.len()); + Ok(*gas) + }, + &mut OutputPolicy::InitContract => { + let return_cost = U256::from(data.len()) * U256::from(self.schedule.create_data_gas); + if return_cost > *gas { + return match self.schedule.exceptional_failed_code_deposit { + true => Err(evm::Error::OutOfGas), + false => Ok(*gas) + } + } + let mut code = vec![]; + code.reserve(data.len()); + unsafe { + ptr::copy(data.as_ptr(), code.as_mut_ptr(), data.len()); + code.set_len(data.len()); + } + let address = &self.params.address; + self.state.init_code(address, code); + self.substate.contracts_created.push(address.clone()); + Ok(*gas - return_cost) + } + } + } + + fn log(&mut self, topics: Vec, data: Bytes) { + let address = self.params.address.clone(); + self.substate.logs.push(LogEntry::new(address, topics, data)); + } + + fn suicide(&mut self, refund_address: &Address) { + let address = self.params.address.clone(); + let balance = self.balance(&address); + self.state.transfer_balance(&address, refund_address, &balance); + self.substate.suicides.insert(address); + } + + fn schedule(&self) -> &Schedule { + &self.schedule + } + + fn env_info(&self) -> &EnvInfo { + &self.info + } +} diff --git a/src/lib.rs b/src/lib.rs index 0b81f4fd3..666edebae 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -89,7 +89,6 @@ extern crate ethcore_util as util; pub mod common; pub mod basic_types; -pub mod executive; pub mod error; pub mod log_entry; pub mod env_info; @@ -110,7 +109,10 @@ pub mod spec; pub mod views; pub mod blockchain; pub mod extras; +pub mod substate; pub mod evm; +pub mod executive; +pub mod externalities; #[cfg(test)] mod tests; diff --git a/src/substate.rs b/src/substate.rs new file mode 100644 index 000000000..f85427745 --- /dev/null +++ b/src/substate.rs @@ -0,0 +1,33 @@ +use common::*; + +/// State changes which should be applied in finalize, +/// after transaction is fully executed. +pub struct Substate { + /// Any accounts that have suicided. + pub suicides: HashSet
, + /// Any logs. + pub logs: Vec, + /// Refund counter of SSTORE nonzero->zero. + pub refunds_count: U256, + /// Created contracts. + pub contracts_created: Vec
+} + +impl Substate { + /// Creates new substate. + pub fn new() -> Self { + Substate { + suicides: HashSet::new(), + logs: vec![], + refunds_count: U256::zero(), + contracts_created: vec![] + } + } + + 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.contracts_created.extend(s.contracts_created.into_iter()); + } +} diff --git a/src/tests/executive.rs b/src/tests/executive.rs index 6b59774a5..daa2ccbe3 100644 --- a/src/tests/executive.rs +++ b/src/tests/executive.rs @@ -6,17 +6,19 @@ use engine::*; use evm; use evm::{Schedule, Ext, Factory}; use ethereum; +use externalities::*; +use substate::*; struct TestEngine { spec: Spec, - max_depth: usize + stack_limit: usize } impl TestEngine { - fn new(max_depth: usize) -> TestEngine { + fn new(stack_limit: usize) -> TestEngine { TestEngine { spec: ethereum::new_frontier_test(), - max_depth: max_depth + stack_limit: stack_limit } } } @@ -26,14 +28,14 @@ impl Engine for TestEngine { fn spec(&self) -> &Spec { &self.spec } fn schedule(&self, _env_info: &EnvInfo) -> Schedule { let mut schedule = Schedule::new_frontier(); - schedule.max_depth = self.max_depth; + schedule.stack_limit = self.stack_limit; schedule } } struct CallCreate { data: Bytes, - destination: Option
, + destination: Address, _gas_limit: U256, value: U256 } @@ -71,33 +73,33 @@ impl<'a> Ext for TestExt<'a> { self.ext.blockhash(number) } - fn create(&mut self, gas: &U256, value: &U256, code: &[u8]) -> (U256, Option
) { + fn create(&mut self, gas: &U256, value: &U256, code: &[u8]) -> Result<(U256, Option
), evm::Error> { // in call and create we need to check if we exited with insufficient balance or max limit reached. // in case of reaching max depth, we should store callcreates. Otherwise, ignore. let res = self.ext.create(gas, value, code); let ext = &self.ext; match res { // just record call create - (gas_left, Some(address)) => { + Ok((gas_left, Some(address))) => { self.callcreates.push(CallCreate { data: code.to_vec(), - destination: Some(address.clone()), + destination: address.clone(), _gas_limit: *gas, value: *value }); - (gas_left, Some(address)) + Ok((gas_left, Some(address))) }, - // creation failed only due to reaching max_depth - (gas_left, None) if ext.state.balance(&ext.params.address) >= *value => { + // creation failed only due to reaching stack_limit + Ok((gas_left, None)) if ext.state.balance(&ext.params.address) >= *value => { + let address = contract_address(&ext.params.address, &ext.state.nonce(&ext.params.address)); self.callcreates.push(CallCreate { data: code.to_vec(), - // callcreate test does not need an address - destination: None, + // TODO: address is not stored here? + destination: Address::new(), _gas_limit: *gas, value: *value }); - let address = contract_address(&ext.params.address, &ext.state.nonce(&ext.params.address)); - (gas_left, Some(address)) + Ok((gas_left, Some(address))) }, other => other } @@ -110,20 +112,21 @@ impl<'a> Ext for TestExt<'a> { value: &U256, data: &[u8], code_address: &Address, - output: &mut [u8]) -> Result<(U256, bool), evm::Error> { + output: &mut [u8]) -> Result { let res = self.ext.call(gas, call_gas, receive_address, value, data, code_address, output); let ext = &self.ext; - if let &Ok(_some) = &res { - if ext.state.balance(&ext.params.address) >= *value { + match res { + Ok(gas_left) if ext.state.balance(&ext.params.address) >= *value => { self.callcreates.push(CallCreate { data: data.to_vec(), - destination: Some(receive_address.clone()), + destination: receive_address.clone(), _gas_limit: *call_gas, value: *value }); - } + Ok(gas_left) + }, + other => other } - res } fn extcode(&self, address: &Address) -> Vec { @@ -155,7 +158,6 @@ fn do_json_test(json_data: &[u8]) -> Vec { let json = Json::from_str(::std::str::from_utf8(json_data).unwrap()).expect("Json is invalid"); let mut failed = Vec::new(); for (name, test) in json.as_object().unwrap() { - println!("name: {:?}", name); // sync io is usefull when something crashes in jit //::std::io::stdout().write(&name.as_bytes()); //::std::io::stdout().write(b"\n");