From 920d8c51db53f776000243fd2a1860ba7cae04c2 Mon Sep 17 00:00:00 2001 From: debris Date: Wed, 13 Jan 2016 13:16:53 +0100 Subject: [PATCH] gas_left is correct in all tests --- src/evm/jit.rs | 9 +-- src/executive.rs | 69 ++++++++++++++++--- src/tests/executive.rs | 147 +++++++++++++++++++++++++++++++++++++++-- 3 files changed, 206 insertions(+), 19 deletions(-) diff --git a/src/evm/jit.rs b/src/evm/jit.rs index fb71f3363..b20b5542f 100644 --- a/src/evm/jit.rs +++ b/src/evm/jit.rs @@ -212,16 +212,17 @@ impl<'a> evmjit::Ext for ExtAdapter<'a> { match self.ext.create(*io_gas, &U256::from_jit(&*endowment), slice::from_raw_parts(init_beg, init_size as usize)) { Ok((gas_left, opt)) => { *io_gas = gas_left; - if let Some(addr) = opt { - *address = addr.into_jit(); - } + *address = match opt { + Some(addr) => addr.into_jit(), + _ => Address::new().into_jit() + }; }, Err(err @ evm::Error::OutOfGas) => { *self.err = Some(err); // hack to propagate `OutOfGas` to evmjit and stop // the execution immediately. // Works, cause evmjit uses i64, not u64 - *io_gas = -1i64 as u64 + *io_gas = -1i64 as u64; }, Err(err) => *self.err = Some(err) } diff --git a/src/executive.rs b/src/executive.rs index c4d3dbc18..9452e51a4 100644 --- a/src/executive.rs +++ b/src/executive.rs @@ -267,7 +267,7 @@ impl<'a> Executive<'a> { } /// Policy for handling output data on `RETURN` opcode. -enum OutputPolicy<'a> { +pub enum OutputPolicy<'a> { /// Return reference to fixed sized output. /// Used for message calls. Return(&'a mut [u8]), @@ -276,12 +276,12 @@ enum OutputPolicy<'a> { } /// Implementation of evm Externalities. -struct Externalities<'a> { - state: &'a mut State, +pub struct Externalities<'a> { + pub state: &'a mut State, info: &'a EnvInfo, engine: &'a Engine, depth: usize, - params: &'a ActionParams, + pub params: &'a ActionParams, substate: &'a mut Substate, schedule: Schedule, output: OutputPolicy<'a> @@ -289,7 +289,7 @@ struct Externalities<'a> { impl<'a> Externalities<'a> { /// Basic `Externalities` constructor. - fn new(state: &'a mut State, + pub fn new(state: &'a mut State, info: &'a EnvInfo, engine: &'a Engine, depth: usize, @@ -584,7 +584,60 @@ mod tests { ex.create(¶ms, &mut substate).unwrap() }; - assert_eq!(gas_left, U256::from(47_976)); + assert_eq!(gas_left, U256::from(62_976)); + // ended with max depth + assert_eq!(substate.contracts_created.len(), 0); + } + + #[test] + fn test_create_contract_value_too_high() { + // code: + // + // 7c 601080600c6000396000f3006000355415600957005b60203560003555 - push 29 bytes? + // 60 00 - push 0 + // 52 + // 60 1d - push 29 + // 60 03 - push 3 + // 60 e6 - push 230 + // f0 - create a contract trying to send 230. + // 60 00 - push 0 + // 55 sstore + // + // other code: + // + // 60 10 - push 16 + // 80 - duplicate first stack item + // 60 0c - push 12 + // 60 00 - push 0 + // 39 - copy current code to memory + // 60 00 - push 0 + // f3 - return + + let code = "7c601080600c6000396000f3006000355415600957005b60203560003555600052601d600360e6f0600055".from_hex().unwrap(); + + let sender = Address::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap(); + let address = contract_address(&sender, &U256::zero()); + // TODO: add tests for 'callcreate' + //let next_address = contract_address(&address, &U256::zero()); + let mut params = ActionParams::new(); + params.address = address.clone(); + params.sender = sender.clone(); + params.origin = sender.clone(); + params.gas = U256::from(100_000); + params.code = code.clone(); + params.value = U256::from(100); + let mut state = State::new_temp(); + state.add_balance(&sender, &U256::from(100)); + let info = EnvInfo::new(); + let engine = TestEngine::new(0); + let mut substate = Substate::new(); + + let gas_left = { + let mut ex = Executive::new(&mut state, &info, &engine); + ex.create(¶ms, &mut substate).unwrap() + }; + + assert_eq!(gas_left, U256::from(62_976)); assert_eq!(substate.contracts_created.len(), 0); } @@ -863,8 +916,8 @@ mod tests { match res { Err(Error::Execution(ExecutionError::NotEnoughCash { required , is })) - if required == U512::zero() && is == U512::one() => (), - _ => assert!(false, "Expected not enough cash error.") + if required == U512::from(100_018) && is == U512::from(100_017) => (), + _ => assert!(false, "Expected not enough cash error. {:?}", res) } } } diff --git a/src/tests/executive.rs b/src/tests/executive.rs index eb3f1bcfd..5784141bc 100644 --- a/src/tests/executive.rs +++ b/src/tests/executive.rs @@ -3,7 +3,8 @@ use state::*; use executive::*; use spec::*; use engine::*; -use evm::{Schedule}; +use evm; +use evm::{Schedule, Ext, Factory}; use ethereum; struct TestEngine { @@ -30,13 +31,134 @@ impl Engine for TestEngine { } } +struct CallCreate { + data: Bytes, + destination: Address, + gas_limit: U256, + value: U256 +} + +/// Tiny wrapper around executive externalities. +/// Stores callcreates. +struct TestExt<'a> { + ext: Externalities<'a>, + callcreates: Vec +} + +impl<'a> TestExt<'a> { + fn new(ext: Externalities<'a>) -> TestExt { + TestExt { + ext: ext, + callcreates: vec![] + } + } +} + +impl<'a> Ext for TestExt<'a> { + fn sload(&self, key: &H256) -> H256 { + self.ext.sload(key) + } + + fn sstore(&mut self, key: H256, value: H256) { + self.ext.sstore(key, value) + } + + fn balance(&self, address: &Address) -> U256 { + self.ext.balance(address) + } + + fn blockhash(&self, number: &U256) -> H256 { + self.ext.blockhash(number) + } + + fn create(&mut self, gas: u64, value: &U256, code: &[u8]) -> Result<(u64, 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 + Ok((gas_left, Some(address))) => { + self.callcreates.push(CallCreate { + data: code.to_vec(), + destination: address.clone(), + gas_limit: U256::from(gas), + value: *value + }); + Ok((gas_left, Some(address))) + }, + // 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(), + // TODO: address is not stored here? + destination: Address::new(), + gas_limit: U256::from(gas), + value: *value + }); + Ok((gas_left, Some(address))) + }, + other => other + } + } + + fn call(&mut self, + gas: u64, + call_gas: u64, + receive_address: &Address, + value: &U256, + data: &[u8], + code_address: &Address, + output: &mut [u8]) -> Result { + let res = self.ext.call(gas, call_gas, receive_address, value, data, code_address, output); + let ext = &self.ext; + match res { + Ok(gas_left) if ext.state.balance(&ext.params.address) >= *value => { + self.callcreates.push(CallCreate { + data: data.to_vec(), + destination: receive_address.clone(), + gas_limit: U256::from(call_gas), + value: *value + }); + Ok(gas_left) + }, + other => other + } + } + + fn extcode(&self, address: &Address) -> Vec { + self.ext.extcode(address) + } + + fn log(&mut self, topics: Vec, data: Bytes) { + self.ext.log(topics, data) + } + + fn ret(&mut self, gas: u64, data: &[u8]) -> Result { + self.ext.ret(gas, data) + } + + fn suicide(&mut self) { + self.ext.suicide() + } + + fn schedule(&self) -> &Schedule { + self.ext.schedule() + } + + fn env_info(&self) -> &EnvInfo { + self.ext.env_info() + } +} + 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() { - ::std::io::stdout().write(&name.as_bytes()); - ::std::io::stdout().write(b"\n"); - ::std::io::stdout().flush(); + //::std::io::stdout().write(&name.as_bytes()); + //::std::io::stdout().write(b"\n"); + //::std::io::stdout().flush(); //println!("name: {:?}", name); let mut fail = false; //let mut fail_unless = |cond: bool| if !cond && !fail { failed.push(name.to_string()); fail = true }; @@ -94,8 +216,10 @@ fn do_json_test(json_data: &[u8]) -> Vec { // execute let res = { - let mut ex = Executive::new(&mut state, &info, &engine); - ex.call(¶ms, &mut substate, &mut []) + let ex = Externalities::new(&mut state, &info, &engine, 0, ¶ms, &mut substate, OutputPolicy::Return(&mut [])); + let mut test_ext = TestExt::new(ex); + let evm = Factory::create(); + evm.exec(¶ms, &mut test_ext) }; // then validate @@ -119,7 +243,16 @@ fn do_json_test(json_data: &[u8]) -> Vec { } declare_test!{ExecutiveTests_vmArithmeticTest, "VMTests/vmArithmeticTest"} -declare_test!{ExecutiveTests_vmSha3Test, "VMTests/vmSha3Test"} declare_test!{ExecutiveTests_vmBitwiseLogicOperationTest, "VMTests/vmBitwiseLogicOperationTest"} +// this one crashes with some vm internal error. Separately they pass. //declare_test!{ExecutiveTests_vmBlockInfoTest, "VMTests/vmBlockInfoTest"} declare_test!{ExecutiveTests_vmEnvironmentalInfoTest, "VMTests/vmEnvironmentalInfoTest"} +declare_test!{ExecutiveTests_vmIOandFlowOperationsTest, "VMTests/vmIOandFlowOperationsTest"} +// this one take way too long. +//declare_test!{ExecutiveTests_vmInputLimits, "VMTests/vmInputLimits"} +declare_test!{ExecutiveTests_vmLogTest, "VMTests/vmLogTest"} +declare_test!{ExecutiveTests_vmPerformanceTest, "VMTests/vmPerformanceTest"} +declare_test!{ExecutiveTests_vmPushDupSwapTest, "VMTests/vmPushDupSwapTest"} +declare_test!{ExecutiveTests_vmSha3Test, "VMTests/vmSha3Test"} +declare_test!{ExecutiveTests_vmSystemOperationsTest, "VMTests/vmSystemOperationsTest"} +declare_test!{ExecutiveTests_vmtests, "VMTests/vmtests"}