diff --git a/ethcore/src/account.rs b/ethcore/src/account.rs index aa5a0c4bd..c36c35232 100644 --- a/ethcore/src/account.rs +++ b/ethcore/src/account.rs @@ -183,11 +183,11 @@ impl Account { #[cfg(test)] /// Determine whether there are any un-`commit()`-ed storage-setting operations. pub fn storage_is_clean(&self) -> bool { self.storage_overlay.borrow().iter().find(|&(_, &(f, _))| f == Filth::Dirty).is_none() } - + #[cfg(test)] /// return the storage root associated with this account or None if it has been altered via the overlay. pub fn storage_root(&self) -> Option<&H256> { if self.storage_is_clean() {Some(&self.storage_root)} else {None} } - + /// return the storage overlay. pub fn storage_overlay(&self) -> Ref> { self.storage_overlay.borrow() } @@ -198,7 +198,11 @@ impl Account { pub fn add_balance(&mut self, x: &U256) { self.balance = self.balance + *x; } /// Increment the nonce of the account by one. - pub fn sub_balance(&mut self, x: &U256) { self.balance = self.balance - *x; } + /// Panics if balance is less than `x` + pub fn sub_balance(&mut self, x: &U256) { + assert!(self.balance >= *x); + self.balance = self.balance - *x; + } /// Commit the `storage_overlay` to the backing DB and update `storage_root`. pub fn commit_storage(&mut self, db: &mut AccountDBMut) { diff --git a/ethcore/src/executive.rs b/ethcore/src/executive.rs index 812dc3acd..782063cb2 100644 --- a/ethcore/src/executive.rs +++ b/ethcore/src/executive.rs @@ -43,11 +43,11 @@ pub struct Executed { pub gas: U256, /// Gas used during execution of transaction. pub gas_used: U256, - /// Gas refunded after the execution of transaction. + /// Gas refunded after the execution of transaction. /// To get gas that was required up front, add `refunded` and `gas_used`. pub refunded: U256, /// Cumulative gas used in current block so far. - /// + /// /// `cumulative_gas_used = gas_used(t0) + gas_used(t1) + ... gas_used(tn)` /// /// where `tn` is current transaction. @@ -56,9 +56,9 @@ pub struct Executed { pub logs: Vec, /// Addresses of contracts created during execution of transaction. /// Ordered from earliest creation. - /// - /// eg. sender creates contract A and A in constructor creates contract B - /// + /// + /// eg. sender creates contract A and A in constructor creates contract B + /// /// B creation ends first, and it will be the first element of the vector. pub contracts_created: Vec
} @@ -119,13 +119,13 @@ impl<'a> Executive<'a> { if t.nonce != nonce { return Err(From::from(ExecutionError::InvalidNonce { expected: nonce, got: t.nonce })); } - + // validate if transaction fits into given block if self.info.gas_used + t.gas > self.info.gas_limit { - return Err(From::from(ExecutionError::BlockGasLimitReached { - gas_limit: self.info.gas_limit, - gas_used: self.info.gas_used, - gas: t.gas + return Err(From::from(ExecutionError::BlockGasLimitReached { + gas_limit: self.info.gas_limit, + gas_used: self.info.gas_used, + gas: t.gas })); } @@ -220,7 +220,7 @@ impl<'a> Executive<'a> { if self.engine.is_builtin(¶ms.code_address) { // if destination is builtin, try to execute it - + let default = []; let data = if let Some(ref d) = params.data { d as &[u8] } else { &default as &[u8] }; @@ -239,7 +239,7 @@ impl<'a> Executive<'a> { } } else if params.code.is_some() { // if destination is a contract, do normal message call - + // part of substate that may be reverted let mut unconfirmed_substate = Substate::new(); @@ -258,7 +258,7 @@ impl<'a> Executive<'a> { Ok(params.gas) } } - + /// Creates contract with given contract params. /// NOTE. It does not finalize the transaction (doesn't do refunds, nor suicides). /// Modifies the substate. @@ -317,7 +317,7 @@ impl<'a> Executive<'a> { self.state.kill_account(address); } - match result { + match result { Err(evm::Error::Internal) => Err(ExecutionError::Internal), Err(_) => { Ok(Executed { @@ -345,8 +345,8 @@ impl<'a> Executive<'a> { fn enact_result(&mut self, result: &evm::Result, substate: &mut Substate, un_substate: Substate) { match *result { Err(evm::Error::OutOfGas) - | Err(evm::Error::BadJumpDestination {..}) - | Err(evm::Error::BadInstruction {.. }) + | Err(evm::Error::BadJumpDestination {..}) + | Err(evm::Error::BadInstruction {.. }) | Err(evm::Error::StackUnderflow {..}) | Err(evm::Error::OutOfStack {..}) => { self.state.revert_snapshot(); @@ -364,42 +364,10 @@ impl<'a> Executive<'a> { mod tests { use super::*; use common::*; - use ethereum; - use engine::*; - use spec::*; - use evm::{Schedule, Factory, VMType}; + use evm::{Factory, VMType}; use substate::*; use tests::helpers::*; - struct TestEngine { - factory: Factory, - spec: Spec, - max_depth: usize - } - - impl TestEngine { - fn new(max_depth: usize, factory: Factory) -> TestEngine { - TestEngine { - factory: factory, - spec: ethereum::new_frontier_test(), - max_depth: max_depth - } - } - } - - impl Engine for TestEngine { - fn name(&self) -> &str { "TestEngine" } - fn spec(&self) -> &Spec { &self.spec } - fn vm_factory(&self) -> &Factory { - &self.factory - } - fn schedule(&self, _env_info: &EnvInfo) -> Schedule { - let mut schedule = Schedule::new_frontier(); - schedule.max_depth = self.max_depth; - schedule - } - } - #[test] fn test_contract_address() { let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); @@ -488,7 +456,7 @@ mod tests { let mut ex = Executive::new(&mut state, &info, &engine); ex.create(params, &mut substate).unwrap() }; - + assert_eq!(gas_left, U256::from(62_976)); // ended with max depth assert_eq!(substate.contracts_created.len(), 0); @@ -542,7 +510,7 @@ mod tests { let mut ex = Executive::new(&mut state, &info, &engine); ex.create(params, &mut substate).unwrap() }; - + assert_eq!(gas_left, U256::from(62_976)); assert_eq!(substate.contracts_created.len(), 0); } @@ -594,7 +562,7 @@ mod tests { let mut ex = Executive::new(&mut state, &info, &engine); ex.create(params, &mut substate).unwrap(); } - + assert_eq!(substate.contracts_created.len(), 1); assert_eq!(substate.contracts_created[0], next_address); } @@ -666,7 +634,7 @@ mod tests { fn test_recursive_bomb1(factory: Factory) { // 60 01 - push 1 // 60 00 - push 0 - // 54 - sload + // 54 - sload // 01 - add // 60 00 - push 0 // 55 - sstore @@ -766,7 +734,7 @@ mod tests { let mut ex = Executive::new(&mut state, &info, &engine); ex.transact(&t) }; - + match res { Err(Error::Util(UtilError::Crypto(CryptoError::InvalidSignature))) => (), _ => assert!(false, "Expected invalid signature error.") @@ -797,10 +765,10 @@ mod tests { let mut ex = Executive::new(&mut state, &info, &engine); ex.transact(&t) }; - + match res { - Err(Error::Execution(ExecutionError::InvalidNonce { expected, got })) - if expected == U256::zero() && got == U256::one() => (), + Err(Error::Execution(ExecutionError::InvalidNonce { expected, got })) + if expected == U256::zero() && got == U256::one() => (), _ => assert!(false, "Expected invalid nonce error.") } } @@ -832,8 +800,8 @@ mod tests { }; match res { - Err(Error::Execution(ExecutionError::BlockGasLimitReached { gas_limit, gas_used, gas })) - if gas_limit == U256::from(100_000) && gas_used == U256::from(20_000) && gas == U256::from(80_001) => (), + Err(Error::Execution(ExecutionError::BlockGasLimitReached { gas_limit, gas_used, gas })) + if gas_limit == U256::from(100_000) && gas_used == U256::from(20_000) && gas == U256::from(80_001) => (), _ => assert!(false, "Expected block gas limit error.") } } @@ -863,10 +831,10 @@ mod tests { let mut ex = Executive::new(&mut state, &info, &engine); ex.transact(&t) }; - + match res { - Err(Error::Execution(ExecutionError::NotEnoughCash { required , got })) - if required == U512::from(100_018) && got == 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/ethcore/src/externalities.rs b/ethcore/src/externalities.rs index ad2f18f11..558e477c7 100644 --- a/ethcore/src/externalities.rs +++ b/ethcore/src/externalities.rs @@ -68,12 +68,12 @@ pub struct Externalities<'a> { impl<'a> Externalities<'a> { /// Basic `Externalities` constructor. - pub fn new(state: &'a mut State, - env_info: &'a EnvInfo, - engine: &'a Engine, + pub fn new(state: &'a mut State, + env_info: &'a EnvInfo, + engine: &'a Engine, depth: usize, origin_info: OriginInfo, - substate: &'a mut Substate, + substate: &'a mut Substate, output: OutputPolicy<'a>) -> Self { Externalities { state: state, @@ -106,16 +106,18 @@ impl<'a> Ext for Externalities<'a> { } fn blockhash(&self, number: &U256) -> H256 { + // TODO: comment out what this function expects from env_info, since it will produce panics if the latter is inconsistent match *number < U256::from(self.env_info.number) && number.low_u64() >= cmp::max(256, self.env_info.number) - 256 { true => { let index = self.env_info.number - number.low_u64() - 1; + assert!(index < self.env_info.last_hashes.len() as u64, format!("Inconsistent env_info, should contain at least {:?} last hashes", index+1)); let r = self.env_info.last_hashes[index as usize].clone(); trace!("ext: blockhash({}) -> {} self.env_info.number={}\n", number, r, self.env_info.number); r }, false => { trace!("ext: blockhash({}) -> null self.env_info.number={}\n", number, self.env_info.number); - H256::from(&U256::zero()) + H256::zero() }, } } @@ -139,7 +141,7 @@ impl<'a> Ext for Externalities<'a> { self.state.inc_nonce(&self.origin_info.address); let mut ex = Executive::from_parent(self.state, self.env_info, self.engine, self.depth); - + // TODO: handle internal error separately match ex.create(params, self.substate) { Ok(gas_left) => { @@ -150,18 +152,18 @@ impl<'a> Ext for Externalities<'a> { } } - fn call(&mut self, - gas: &U256, - sender_address: &Address, - receive_address: &Address, + fn call(&mut self, + gas: &U256, + sender_address: &Address, + receive_address: &Address, value: Option, - data: &[u8], - code_address: &Address, + data: &[u8], + code_address: &Address, output: &mut [u8]) -> MessageCallResult { let mut params = ActionParams { sender: sender_address.clone(), - address: receive_address.clone(), + address: receive_address.clone(), value: ActionValue::Apparent(self.origin_info.value.clone()), code_address: code_address.clone(), origin: self.origin_info.origin.clone(), @@ -257,3 +259,144 @@ impl<'a> Ext for Externalities<'a> { self.substate.sstore_clears_count = self.substate.sstore_clears_count + U256::one(); } } + +#[cfg(test)] +mod tests { + use common::*; + use state::*; + use engine::*; + use evm::{Ext}; + use substate::*; + use tests::helpers::*; + use super::*; + + fn get_test_origin() -> OriginInfo { + OriginInfo { + address: Address::zero(), + origin: Address::zero(), + gas_price: U256::zero(), + value: U256::zero() + } + } + + fn get_test_env_info() -> EnvInfo { + EnvInfo { + number: 100, + author: x!(0), + timestamp: 0, + difficulty: x!(0), + last_hashes: vec![], + gas_used: x!(0), + gas_limit: x!(0) + } + } + + struct TestSetup { + state: GuardedTempResult, + engine: Box, + sub_state: Substate, + env_info: EnvInfo + } + + impl TestSetup { + fn new() -> TestSetup { + TestSetup { + state: get_temp_state(), + engine: get_test_spec().to_engine().unwrap(), + sub_state: Substate::new(), + env_info: get_test_env_info() + } + } + } + + #[test] + fn can_be_created() { + let mut setup = TestSetup::new(); + let state = setup.state.reference_mut(); + + let ext = Externalities::new(state, &setup.env_info, &*setup.engine, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract); + + assert_eq!(ext.env_info().number, 100); + } + + #[test] + fn can_return_block_hash_no_env() { + let mut setup = TestSetup::new(); + let state = setup.state.reference_mut(); + let ext = Externalities::new(state, &setup.env_info, &*setup.engine, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract); + + let hash = ext.blockhash(&U256::from_str("0000000000000000000000000000000000000000000000000000000000120000").unwrap()); + + assert_eq!(hash, H256::zero()); + } + + #[test] + fn can_return_block_hash() { + let test_hash = H256::from("afafafafafafafafafafafbcbcbcbcbcbcbcbcbcbeeeeeeeeeeeeedddddddddd"); + let test_env_number = 0x120001; + + let mut setup = TestSetup::new(); + { + let env_info = &mut setup.env_info; + env_info.number = test_env_number; + env_info.last_hashes.push(test_hash.clone()); + } + let state = setup.state.reference_mut(); + let ext = Externalities::new(state, &setup.env_info, &*setup.engine, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract); + + let hash = ext.blockhash(&U256::from_str("0000000000000000000000000000000000000000000000000000000000120000").unwrap()); + + assert_eq!(test_hash, hash); + } + + #[test] + #[should_panic] + fn can_call_fail_empty() { + let mut setup = TestSetup::new(); + let state = setup.state.reference_mut(); + let mut ext = Externalities::new(state, &setup.env_info, &*setup.engine, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract); + + let mut output = vec![]; + + // this should panic because we have no balance on any account + ext.call( + &U256::from_str("0000000000000000000000000000000000000000000000000000000000120000").unwrap(), + &Address::new(), + &Address::new(), + Some(U256::from_str("0000000000000000000000000000000000000000000000000000000000150000").unwrap()), + &vec![], + &Address::new(), + &mut output); + } + + #[test] + fn can_log() { + let log_data = vec![120u8, 110u8]; + let log_topics = vec![H256::from("af0fa234a6af46afa23faf23bcbc1c1cb4bcb7bcbe7e7e7ee3ee2edddddddddd")]; + + let mut setup = TestSetup::new(); + let state = setup.state.reference_mut(); + + { + let mut ext = Externalities::new(state, &setup.env_info, &*setup.engine, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract); + ext.log(log_topics, &log_data); + } + + assert_eq!(setup.sub_state.logs.len(), 1); + } + + #[test] + fn can_suicide() { + let refund_account = &Address::new(); + + let mut setup = TestSetup::new(); + let state = setup.state.reference_mut(); + + { + let mut ext = Externalities::new(state, &setup.env_info, &*setup.engine, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract); + ext.suicide(&refund_account); + } + + assert_eq!(setup.sub_state.suicides.len(), 1); + } +} diff --git a/ethcore/src/json_tests/executive.rs b/ethcore/src/json_tests/executive.rs index 7ac60e6b4..b08257a92 100644 --- a/ethcore/src/json_tests/executive.rs +++ b/ethcore/src/json_tests/executive.rs @@ -26,29 +26,29 @@ use externalities::*; use substate::*; use tests::helpers::*; -struct TestEngine { +struct TestEngineFrontier { vm_factory: Factory, spec: Spec, max_depth: usize } -impl TestEngine { - fn new(max_depth: usize, vm_type: VMType) -> TestEngine { - TestEngine { +impl TestEngineFrontier { + fn new(max_depth: usize, vm_type: VMType) -> TestEngineFrontier { + TestEngineFrontier { vm_factory: Factory::new(vm_type), spec: ethereum::new_frontier_test(), - max_depth: max_depth + max_depth: max_depth } } } -impl Engine for TestEngine { +impl Engine for TestEngineFrontier { fn name(&self) -> &str { "TestEngine" } fn spec(&self) -> &Spec { &self.spec } fn vm_factory(&self) -> &Factory { &self.vm_factory } - fn schedule(&self, _env_info: &EnvInfo) -> Schedule { + fn schedule(&self, _env_info: &EnvInfo) -> Schedule { let mut schedule = Schedule::new_frontier(); - schedule.max_depth = self.max_depth; + schedule.max_depth = self.max_depth; schedule } } @@ -69,12 +69,12 @@ struct TestExt<'a> { } impl<'a> TestExt<'a> { - fn new(state: &'a mut State, - info: &'a EnvInfo, - engine: &'a Engine, + fn new(state: &'a mut State, + info: &'a EnvInfo, + engine: &'a Engine, depth: usize, origin_info: OriginInfo, - substate: &'a mut Substate, + substate: &'a mut Substate, output: OutputPolicy<'a>, address: Address) -> Self { TestExt { @@ -116,13 +116,13 @@ impl<'a> Ext for TestExt<'a> { ContractCreateResult::Created(self.contract_address.clone(), *gas) } - fn call(&mut self, - gas: &U256, - _sender_address: &Address, - receive_address: &Address, + fn call(&mut self, + gas: &U256, + _sender_address: &Address, + receive_address: &Address, value: Option, - data: &[u8], - _code_address: &Address, + data: &[u8], + _code_address: &Address, _output: &mut [u8]) -> MessageCallResult { self.callcreates.push(CallCreate { data: data.to_vec(), @@ -136,7 +136,7 @@ impl<'a> Ext for TestExt<'a> { fn extcode(&self, address: &Address) -> Bytes { self.ext.extcode(address) } - + fn log(&mut self, topics: Vec, data: &[u8]) { self.ext.log(topics, data) } @@ -185,11 +185,11 @@ fn do_json_test_for(vm: &VMType, json_data: &[u8]) -> Vec { // ::std::io::stdout().flush(); let mut fail = false; //let mut fail_unless = |cond: bool| if !cond && !fail { failed.push(name.to_string()); fail = true }; - let mut fail_unless = |cond: bool, s: &str | if !cond && !fail { - failed.push(format!("[{}] {}: {}", vm, name, s)); - fail = true + let mut fail_unless = |cond: bool, s: &str | if !cond && !fail { + failed.push(format!("[{}] {}: {}", vm, name, s)); + fail = true }; - + // test env let mut state_result = get_temp_state(); let mut state = state_result.reference_mut(); @@ -209,7 +209,7 @@ fn do_json_test_for(vm: &VMType, json_data: &[u8]) -> Vec { EnvInfo::from_json(env) }).unwrap_or_default(); - let engine = TestEngine::new(1, vm.clone()); + let engine = TestEngineFrontier::new(1, vm.clone()); // params let mut params = ActionParams::default(); @@ -226,18 +226,18 @@ fn do_json_test_for(vm: &VMType, json_data: &[u8]) -> Vec { let out_of_gas = test.find("callcreates").map(|_calls| { }).is_none(); - + let mut substate = Substate::new(); let mut output = vec![]; // execute let (res, callcreates) = { - let mut ex = TestExt::new(&mut state, - &info, - &engine, - 0, - OriginInfo::from(¶ms), - &mut substate, + let mut ex = TestExt::new(&mut state, + &info, + &engine, + 0, + OriginInfo::from(¶ms), + &mut substate, OutputPolicy::Return(BytesRef::Flexible(&mut output)), params.address.clone()); let evm = engine.vm_factory().create(); diff --git a/ethcore/src/substate.rs b/ethcore/src/substate.rs index f42ea38fd..235ce2e97 100644 --- a/ethcore/src/substate.rs +++ b/ethcore/src/substate.rs @@ -56,6 +56,12 @@ mod tests { use super::*; use common::*; + #[test] + fn created() { + let sub_state = Substate::new(); + assert_eq!(sub_state.suicides.len(), 0); + } + #[test] fn accrue() { let mut sub_state = Substate::new(); diff --git a/ethcore/src/tests/helpers.rs b/ethcore/src/tests/helpers.rs index f5815b718..93e3e0a0d 100644 --- a/ethcore/src/tests/helpers.rs +++ b/ethcore/src/tests/helpers.rs @@ -23,7 +23,9 @@ use std::fs::{remove_dir_all}; use blockchain::{BlockChain}; use state::*; use rocksdb::*; - +use evm::{Schedule, Factory}; +use engine::*; +use ethereum; #[cfg(feature = "json-tests")] pub enum ChainEra { @@ -81,6 +83,35 @@ impl GuardedTempResult { } } +pub struct TestEngine { + factory: Factory, + spec: Spec, + max_depth: usize +} + +impl TestEngine { + pub fn new(max_depth: usize, factory: Factory) -> TestEngine { + TestEngine { + factory: factory, + spec: ethereum::new_frontier_test(), + max_depth: max_depth + } + } +} + +impl Engine for TestEngine { + fn name(&self) -> &str { "TestEngine" } + fn spec(&self) -> &Spec { &self.spec } + fn vm_factory(&self) -> &Factory { + &self.factory + } + fn schedule(&self, _env_info: &EnvInfo) -> Schedule { + let mut schedule = Schedule::new_frontier(); + schedule.max_depth = self.max_depth; + schedule + } +} + pub fn get_test_spec() -> Spec { Spec::new_test() }