diff --git a/Cargo.toml b/Cargo.toml index bffab3ac3..d9092de53 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,7 @@ env_logger = "0.3" ethcore-util = { path = "../ethcore-util" } rustc-serialize = "0.3" flate2 = "0.2" -rocksdb = "0.2" +rocksdb = "0.3" heapsize = "0.2.0" rust-crypto = "0.2.34" time = "0.1" diff --git a/src/error.rs b/src/error.rs index 01618c66c..e3d880103 100644 --- a/src/error.rs +++ b/src/error.rs @@ -19,16 +19,19 @@ pub struct OutOfBounds { /// Result of executing the transaction. #[derive(PartialEq, Debug)] pub enum ExecutionError { + /// Returned when there gas paid for transaction execution is + /// lower than base gas required. + NotEnoughBaseGas { required: U256, got: U256 }, /// Returned when block (gas_used + gas) > gas_limit. /// /// If gas =< gas_limit, upstream may try to execute the transaction /// in next block. BlockGasLimitReached { gas_limit: U256, gas_used: U256, gas: U256 }, /// Returned when transaction nonce does not match state nonce. - InvalidNonce { expected: U256, is: U256 }, + InvalidNonce { expected: U256, got: U256 }, /// Returned when cost of transaction (value + gas_price * gas) exceeds /// current sender balance. - NotEnoughCash { required: U512, is: U512 }, + NotEnoughCash { required: U512, got: U512 }, /// Returned when internal evm error occurs. Internal } diff --git a/src/ethereum/ethash.rs b/src/ethereum/ethash.rs index 8a0450e72..2dc25a16c 100644 --- a/src/ethereum/ethash.rs +++ b/src/ethereum/ethash.rs @@ -20,6 +20,8 @@ impl Ethash { factory: Factory::default() }) } + + fn u256_param(&self, name: &str) -> U256 { self.spec().engine_params.get(name).map(|a| decode(&a)).unwrap_or(U256::from(0u64)) } } impl Engine for Ethash { @@ -39,6 +41,20 @@ impl Engine for Ethash { &self.factory } + fn populate_from_parent(&self, header: &mut Header, parent: &Header) { + header.difficulty = self.calculate_difficuty(header, parent); + header.gas_limit = { + let gas_floor_target: U256 = x!(3141562); + let gas_limit = parent.gas_limit; + let bound_divisor = self.u256_param("gasLimitBoundDivisor"); + if gas_limit < gas_floor_target { + min(gas_floor_target, gas_limit + gas_limit / bound_divisor - x!(1)) + } else { + max(gas_floor_target, gas_limit - gas_limit / bound_divisor + x!(1) + (header.gas_used * x!(6) / x!(5)) / bound_divisor) + } + }; + } + /// Apply the block reward on finalisation of the block. /// This assumes that all uncles are valid uncles (i.e. of at least one generation before the current). fn on_close_block(&self, block: &mut Block) { @@ -55,7 +71,6 @@ impl Engine for Ethash { } } - fn verify_block_basic(&self, header: &Header, _block: Option<&[u8]>) -> result::Result<(), Error> { let min_difficulty = decode(self.spec().engine_params.get("minimumDifficulty").unwrap()); if header.difficulty < min_difficulty { diff --git a/src/executive.rs b/src/executive.rs index f15ee2915..d3dd5aab2 100644 --- a/src/executive.rs +++ b/src/executive.rs @@ -2,7 +2,9 @@ use common::*; use state::*; use engine::*; -use evm::{self, Schedule, 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,44 +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, - /// True if transaction, or one of its subcalls runs out of gas. - excepted: bool, - /// Created contracts. - contracts_created: Vec
-} - -impl Substate { - /// Creates new substate. - pub fn new() -> Self { - Substate { - suicides: HashSet::new(), - logs: vec![], - refunds_count: U256::zero(), - excepted: false, - 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.excepted |= s.excepted; - self.contracts_created.extend(s.contracts_created.into_iter()); - } - - pub fn excepted(&self) -> bool { self.excepted } -} - /// Transaction execution receipt. #[derive(Debug)] pub struct Executed { @@ -68,8 +32,6 @@ pub struct Executed { pub cumulative_gas_used: U256, /// Vector of logs generated by transaction. pub logs: Vec, - /// Execution ended running out of gas. - pub excepted: bool, /// Addresses of contracts created during execution of transaction. /// Ordered from earliest creation. /// @@ -97,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) } @@ -112,16 +74,28 @@ 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); - // TODO: error on base gas required + let schedule = self.engine.schedule(self.info); + let base_gas_required = U256::from(t.gas_required(&schedule)); + + if t.gas < base_gas_required { + return Err(From::from(ExecutionError::NotEnoughBaseGas { required: base_gas_required, got: t.gas })); + } + + let init_gas = t.gas - base_gas_required; // validate transaction nonce if t.nonce != nonce { - return Err(From::from(ExecutionError::InvalidNonce { expected: nonce, is: t.nonce })); + return Err(From::from(ExecutionError::InvalidNonce { expected: nonce, got: t.nonce })); } // validate if transaction fits into given block @@ -140,7 +114,7 @@ impl<'a> Executive<'a> { // avoid unaffordable transactions if U512::from(balance) < total_cost { - return Err(From::from(ExecutionError::NotEnoughCash { required: total_cost, is: U512::from(balance) })); + return Err(From::from(ExecutionError::NotEnoughCash { required: total_cost, got: U512::from(balance) })); } // NOTE: there can be no invalid transactions from this point. @@ -149,9 +123,6 @@ impl<'a> Executive<'a> { let mut substate = Substate::new(); - let schedule = self.engine.schedule(self.info); - let init_gas = t.gas - U256::from(t.gas_required(&schedule)); - let res = match t.action() { &Action::Create => { let params = ActionParams { @@ -207,7 +178,10 @@ impl<'a> Executive<'a> { Ok(params.gas - cost) }, // just drain the whole gas - false => Ok(U256::zero()) + false => { + self.state.revert(backup); + Err(evm::Error::OutOfGas) + } } } else if params.code.len() > 0 { // if destination is a contract, do normal message call @@ -216,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)); self.engine.vm_factory().create().exec(¶ms, &mut ext) }; self.enact_result(&res, substate, unconfirmed_substate, backup); @@ -230,7 +204,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 +218,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); self.engine.vm_factory().create().exec(¶ms, &mut ext) }; self.enact_result(&res, substate, unconfirmed_substate, backup); @@ -264,7 +238,7 @@ impl<'a> Executive<'a> { let gas_left = match &result { &Ok(x) => x, _ => x!(0) }; let refund = cmp::min(sstore_refunds + suicide_refunds, (t.gas - gas_left) / U256::from(2)) + gas_left; let refund_value = refund * t.gas_price; - trace!("Refunding sender: gas_left: {}, refund: {}, refund_value: {}, sender: {}", gas_left, refund, refund_value, t.sender().unwrap()); + trace!("Refunding sender: sstore0s: {}, suicides: {}, gas_left: {}, refund: {}, refund_value: {}, sender: {}", sstore_refunds, suicide_refunds, gas_left, refund, refund_value, t.sender().unwrap()); self.state.add_balance(&t.sender().unwrap(), &refund_value); // fees earned by author @@ -307,9 +281,18 @@ impl<'a> Executive<'a> { refunded: refund, cumulative_gas_used: self.info.gas_used + gas_used, logs: substate.logs, - excepted: substate.excepted, 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![], + contracts_created: vec![] + }) } } } @@ -322,7 +305,6 @@ impl<'a> Executive<'a> { | &Err(evm::Error::BadInstruction { instruction: _ }) | &Err(evm::Error::StackUnderflow {instruction: _, wanted: _, on_stack: _}) | &Err(evm::Error::OutOfStack {instruction: _, wanted: _, limit: _}) => { - substate.excepted = true; self.state.revert(backup); }, &Ok(_) | &Err(evm::Error::Internal) => substate.accrue(un_substate) @@ -330,228 +312,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 to 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: &[u8]) { - let address = self.params.address.clone(); - self.substate.logs.push(LogEntry::new(address, topics, data.to_vec())); - } - - 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::*; @@ -561,8 +321,7 @@ mod tests { use engine::*; use spec::*; use evm::Schedule; - use evm::Factory; - use evm::factory::VMType; + use substate::*; struct TestEngine { factory: Factory, @@ -913,7 +672,6 @@ mod tests { 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.excepted, false); assert_eq!(executed.contracts_created.len(), 0); assert_eq!(state.balance(&sender), U256::from(1)); assert_eq!(state.balance(&contract), U256::from(17)); @@ -960,8 +718,8 @@ mod tests { }; match res { - Err(Error::Execution(ExecutionError::InvalidNonce { expected, is })) - if expected == U256::zero() && is == U256::one() => (), + Err(Error::Execution(ExecutionError::InvalidNonce { expected, got })) + if expected == U256::zero() && got == U256::one() => (), _ => assert!(false, "Expected invalid nonce error.") } } @@ -1011,8 +769,8 @@ mod tests { }; match res { - Err(Error::Execution(ExecutionError::NotEnoughCash { required , is })) - if required == U512::from(100_018) && is == U512::from(100_017) => (), + Err(Error::Execution(ExecutionError::NotEnoughCash { required , got })) + if required == U512::from(100_018) && got == U512::from(100_017) => (), _ => assert!(false, "Expected not enough cash error. {:?}", res) } } 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 ac0ce3d6a..5c5ac1638 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -92,7 +92,6 @@ pub mod common; pub mod basic_types; #[macro_use] pub mod evm; -pub mod executive; pub mod error; pub mod log_entry; pub mod env_info; @@ -113,6 +112,9 @@ pub mod spec; pub mod views; pub mod blockchain; pub mod extras; +pub mod substate; +pub mod executive; +pub mod externalities; #[cfg(test)] mod tests; diff --git a/src/pod_account.rs b/src/pod_account.rs index a39bf1fa3..29b43c0bf 100644 --- a/src/pod_account.rs +++ b/src/pod_account.rs @@ -31,8 +31,7 @@ impl PodAccount { let mut stream = RlpStream::new_list(4); stream.append(&self.nonce); stream.append(&self.balance); - // TODO. - stream.append(&SHA3_NULL_RLP); + stream.append(&sec_trie_root(self.storage.iter().map(|(k, v)| (k.to_vec(), encode(&U256::from(v.as_slice())))).collect())); stream.append(&self.code.sha3()); stream.out() } diff --git a/src/state.rs b/src/state.rs index fdf71a496..648caa20a 100644 --- a/src/state.rs +++ b/src/state.rs @@ -142,7 +142,7 @@ impl State { 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)) + Ok(Receipt::new(self.root().clone(), e.cumulative_gas_used, e.logs)) } pub fn revert(&mut self, backup: State) { @@ -156,7 +156,7 @@ impl State { /// Commit accounts to SecTrieDBMut. This is similar to cpp-ethereum's dev::eth::commit. /// `accounts` is mutable because we may need to commit the code or storage and record that. - pub fn commit_into(db: &mut HashDB, mut root: H256, accounts: &mut HashMap>) -> H256 { + pub fn commit_into(db: &mut HashDB, root: &mut H256, accounts: &mut HashMap>) { // first, commit the sub trees. // TODO: is this necessary or can we dispense with the `ref mut a` for just `a`? for (_, ref mut a) in accounts.iter_mut() { @@ -170,7 +170,7 @@ impl State { } { - let mut trie = SecTrieDBMut::from_existing(db, &mut root); + let mut trie = SecTrieDBMut::from_existing(db, root); for (address, ref a) in accounts.iter() { match a { &&Some(ref account) => trie.insert(address, &account.rlp()), @@ -178,13 +178,11 @@ impl State { } } } - root } /// Commits our cached account changes into the trie. pub fn commit(&mut self) { - let r = self.root.clone(); // would prefer not to do this, really. - self.root = Self::commit_into(&mut self.db, r, self.cache.borrow_mut().deref_mut()); + Self::commit_into(&mut self.db, &mut self.root, self.cache.borrow_mut().deref_mut()); } /// Populate the state from `accounts`. 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 39a8142cd..8b87681a5 100644 --- a/src/tests/executive.rs +++ b/src/tests/executive.rs @@ -6,6 +6,8 @@ use engine::*; use evm; use evm::{Schedule, Ext, Factory, VMType}; use ethereum; +use externalities::*; +use substate::*; struct TestEngine { vm_factory: Factory, diff --git a/src/tests/state.rs b/src/tests/state.rs index ce89003e1..d4a921add 100644 --- a/src/tests/state.rs +++ b/src/tests/state.rs @@ -10,47 +10,61 @@ fn do_json_test(json_data: &[u8]) -> Vec { let engine = ethereum::new_frontier_test().to_engine().unwrap(); + flush(format!("\n")); + for (name, test) in json.as_object().unwrap() { - println!("name: {:?}", name); let mut fail = false; - let mut fail_unless = |cond: bool| if !cond && !fail { failed.push(name.to_string()); fail = true; true } else {false}; - - let t = Transaction::from_json(&test["transaction"]); - let env = EnvInfo::from_json(&test["env"]); - let _out = Bytes::from_json(&test["out"]); - let post_state_root = xjson!(&test["postStateRoot"]); - let pre = PodState::from_json(&test["pre"]); - let post = PodState::from_json(&test["post"]); - let logs: Vec<_> = test["logs"].as_array().unwrap().iter().map(&LogEntry::from_json).collect(); - - //println!("Transaction: {:?}", t); - //println!("Env: {:?}", env); - { - let mut s = State::new_temp(); - s.populate_from(post.clone()); - s.commit(); - assert_eq!(&post_state_root, s.root()); + let mut fail_unless = |cond: bool| if !cond && !fail { + failed.push(name.to_string()); + flush(format!("FAIL\n")); + fail = true; + true + } else {false}; + + flush(format!(" - {}...", name)); + + let t = Transaction::from_json(&test["transaction"]); + let env = EnvInfo::from_json(&test["env"]); + let _out = Bytes::from_json(&test["out"]); + let post_state_root = xjson!(&test["postStateRoot"]); + let pre = PodState::from_json(&test["pre"]); + let post = PodState::from_json(&test["post"]); + let logs: Vec<_> = test["logs"].as_array().unwrap().iter().map(&LogEntry::from_json).collect(); + + //println!("Transaction: {:?}", t); + //println!("Env: {:?}", env); + let calc_post = sec_trie_root(post.get().iter().map(|(k, v)| (k.to_vec(), v.rlp())).collect()); + + if fail_unless(post_state_root == calc_post) { + println!("!!! {}: Trie root mismatch (got: {}, expect: {}):", name, calc_post, post_state_root); + println!("!!! Post:\n{}", post); + } else { + let mut s = State::new_temp(); + s.populate_from(pre); + s.commit(); + let res = s.apply(&env, engine.deref(), &t); + + if fail_unless(s.root() == &post_state_root) { + println!("!!! {}: State mismatch (got: {}, expect: {}):", name, s.root(), post_state_root); + let our_post = s.to_pod(); + println!("Got:\n{}", our_post); + println!("Expect:\n{}", post); + println!("Diff ---expect -> +++got:\n{}", StateDiff::diff_pod(&post, &our_post)); + } + + if let Ok(r) = res { + if fail_unless(logs == r.logs) { + println!("!!! {}: Logs mismatch:", name); + println!("Got:\n{:?}", r.logs); + println!("Expect:\n{:?}", logs); + } + } + } } - - let mut s = State::new_temp(); - s.populate_from(pre); - let r = s.apply(&env, engine.deref(), &t).unwrap(); - - if fail_unless(&r.state_root == &post_state_root) { - println!("!!! {}: State mismatch (got: {}, expect: {}):", name, r.state_root, post_state_root); - let our_post = s.to_pod(); - println!("Got:\n{}", our_post); - println!("Expect:\n{}", post); - println!("Diff ---expect -> +++got:\n{}", StateDiff::diff_pod(&post, &our_post)); + if !fail { + flush(format!("ok\n")); } - - if fail_unless(logs == r.logs) { - println!("!!! {}: Logs mismatch:", name); - println!("Got:\n{:?}", r.logs); - println!("Expect:\n{:?}", logs); - } - // TODO: Add extra APIs for output //if fail_unless(out == r.) } @@ -58,8 +72,22 @@ fn do_json_test(json_data: &[u8]) -> Vec { failed } -declare_test!{StateTests_stExample, "StateTests/stExample"} declare_test!{StateTests_stBlockHashTest, "StateTests/stBlockHashTest"} -declare_test!{StateTests_stLogTests, "StateTests/stLogTests"} declare_test!{StateTests_stCallCodes, "StateTests/stCallCodes"} -declare_test_ignore!{StateTests_stCallCreateCallCodeTest, "StateTests/stCallCreateCallCodeTest"} +declare_test_ignore!{StateTests_stCallCreateCallCodeTest, "StateTests/stCallCreateCallCodeTest"} //<< Out of stack +declare_test!{StateTests_stDelegatecallTest, "StateTests/stDelegatecallTest"} //<< FAIL - gas too high +declare_test!{StateTests_stExample, "StateTests/stExample"} +declare_test!{StateTests_stInitCodeTest, "StateTests/stInitCodeTest"} +declare_test!{StateTests_stLogTests, "StateTests/stLogTests"} +declare_test!{StateTests_stMemoryStressTest, "StateTests/stMemoryStressTest"} +declare_test!{StateTests_stMemoryTest, "StateTests/stMemoryTest"} +declare_test!{StateTests_stPreCompiledContracts, "StateTests/stPreCompiledContracts"} +declare_test_ignore!{StateTests_stQuadraticComplexityTest, "StateTests/stQuadraticComplexityTest"} //<< Too long +declare_test_ignore!{StateTests_stRecursiveCreate, "StateTests/stRecursiveCreate"} //<< Out of stack +declare_test!{StateTests_stRefundTest, "StateTests/stRefundTest"} +declare_test!{StateTests_stSolidityTest, "StateTests/stSolidityTest"} +declare_test_ignore!{StateTests_stSpecialTest, "StateTests/stSpecialTest"} //<< Signal 11 +declare_test_ignore!{StateTests_stSystemOperationsTest, "StateTests/stSystemOperationsTest"} //<< Signal 11 +declare_test!{StateTests_stTransactionTest, "StateTests/stTransactionTest"} +declare_test!{StateTests_stTransitionTest, "StateTests/stTransitionTest"} +declare_test!{StateTests_stWalletTest, "StateTests/stWalletTest"}