diff --git a/src/executive.rs b/src/executive.rs index 9f04293a0..c4d3dbc18 100644 --- a/src/executive.rs +++ b/src/executive.rs @@ -14,7 +14,7 @@ pub fn contract_address(address: &Address, nonce: &U256) -> Address { /// State changes which should be applied in finalize, /// after transaction is fully executed. -struct Substate { +pub struct Substate { /// Any accounts that have suicided. suicides: HashSet
, /// Any logs. @@ -27,7 +27,7 @@ struct Substate { impl Substate { /// Creates new substate. - fn new() -> Self { + pub fn new() -> Self { Substate { suicides: HashSet::new(), logs: vec![], @@ -172,7 +172,7 @@ impl<'a> Executive<'a> { /// NOTE. It does not finalize the transaction (doesn't do refunds, nor suicides). /// Modifies the substate and the output. /// Returns either gas_left or `evm::Error`. - fn call(&mut self, params: &ActionParams, substate: &mut Substate, output: &mut [u8]) -> evm::Result { + pub fn call(&mut self, params: &ActionParams, substate: &mut Substate, output: &mut [u8]) -> evm::Result { // at first, transfer value to destination self.state.transfer_balance(¶ms.sender, ¶ms.address, ¶ms.value); @@ -332,12 +332,12 @@ impl<'a> Ext for Externalities<'a> { } fn blockhash(&self, number: &U256) -> H256 { - match *number < U256::from(self.info.number) { - false => H256::from(&U256::zero()), + match *number < U256::from(self.info.number) && number.low_u64() >= cmp::max(256, self.info.number) - 256 { true => { - let index = U256::from(self.info.number) - *number - U256::one(); - self.info.last_hashes[index.low_u32() as usize].clone() - } + let index = self.info.number - number.low_u64() - 1; + self.info.last_hashes[index as usize].clone() + }, + false => H256::from(&U256::zero()), } } @@ -415,7 +415,6 @@ impl<'a> Ext for Externalities<'a> { } fn ret(&mut self, gas: u64, data: &[u8]) -> Result { - println!("ret"); match &mut self.output { &mut OutputPolicy::Return(ref mut slice) => unsafe { let len = cmp::min(slice.len(), data.len()); @@ -472,7 +471,7 @@ mod tests { use engine::*; use spec::*; use evm::Schedule; - use super::Substate; + //use super::Substate; struct TestEngine { spec: Spec, @@ -482,7 +481,7 @@ mod tests { impl TestEngine { fn new(stack_limit: usize) -> TestEngine { TestEngine { - spec: ethereum::new_frontier(), + spec: ethereum::new_frontier_test(), stack_limit: stack_limit } } diff --git a/src/tests/executive.rs b/src/tests/executive.rs new file mode 100644 index 000000000..eb3f1bcfd --- /dev/null +++ b/src/tests/executive.rs @@ -0,0 +1,125 @@ +use super::test_common::*; +use state::*; +use executive::*; +use spec::*; +use engine::*; +use evm::{Schedule}; +use ethereum; + +struct TestEngine { + spec: Spec, + stack_limit: usize +} + +impl TestEngine { + fn new(stack_limit: usize) -> TestEngine { + TestEngine { + spec: ethereum::new_frontier_test(), + stack_limit: stack_limit + } + } +} + +impl Engine for TestEngine { + fn name(&self) -> &str { "TestEngine" } + fn spec(&self) -> &Spec { &self.spec } + fn schedule(&self, _env_info: &EnvInfo) -> Schedule { + let mut schedule = Schedule::new_frontier(); + schedule.stack_limit = self.stack_limit; + schedule + } +} + +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(); + //println!("name: {:?}", name); + 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(name.to_string() + ": "+ s); fail = true }; + + // test env + let mut state = State::new_temp(); + + test.find("pre").map(|pre| for (addr, s) in pre.as_object().unwrap() { + let address = address_from_str(addr); + let balance = u256_from_json(&s["balance"]); + let code = bytes_from_json(&s["code"]); + let nonce = u256_from_json(&s["nonce"]); + + state.new_contract(&address); + state.add_balance(&address, &balance); + state.init_code(&address, code); + + for (k, v) in s["storage"].as_object().unwrap() { + let key = H256::from(&u256_from_str(k)); + let val = H256::from(&u256_from_json(v)); + state.set_storage(&address, key, val); + } + }); + + let mut info = EnvInfo::new(); + + test.find("env").map(|env| { + info.author = address_from_json(&env["currentCoinbase"]); + info.difficulty = u256_from_json(&env["currentDifficulty"]); + info.gas_limit = u256_from_json(&env["currentGasLimit"]); + info.number = u256_from_json(&env["currentNumber"]).low_u64(); + info.timestamp = u256_from_json(&env["currentTimestamp"]).low_u64(); + }); + + let engine = TestEngine::new(0); + + // params + let mut params = ActionParams::new(); + test.find("exec").map(|exec| { + params.address = address_from_json(&exec["address"]); + params.sender = address_from_json(&exec["caller"]); + params.origin = address_from_json(&exec["origin"]); + params.code = bytes_from_json(&exec["code"]); + params.data = bytes_from_json(&exec["data"]); + params.gas = u256_from_json(&exec["gas"]); + params.gas_price = u256_from_json(&exec["gasPrice"]); + params.value = u256_from_json(&exec["value"]); + }); + + let out_of_gas = test.find("callcreates").map(|calls| { + }).is_none(); + + let mut substate = Substate::new(); + + // execute + let res = { + let mut ex = Executive::new(&mut state, &info, &engine); + ex.call(¶ms, &mut substate, &mut []) + }; + + // then validate + match res { + Err(_) => fail_unless(out_of_gas, "didn't expect to run out of gas."), + Ok(gas_left) => { + fail_unless(!out_of_gas, "expected to run out of gas."); + fail_unless(gas_left == u256_from_json(&test["gas"]), "gas_left is incorrect"); + println!("name: {}, gas_left : {:?}, expected: {:?}", name, gas_left, u256_from_json(&test["gas"])); + } + } + } + + + for f in failed.iter() { + println!("FAILED: {:?}", f); + } + + //assert!(false); + failed +} + +declare_test!{ExecutiveTests_vmArithmeticTest, "VMTests/vmArithmeticTest"} +declare_test!{ExecutiveTests_vmSha3Test, "VMTests/vmSha3Test"} +declare_test!{ExecutiveTests_vmBitwiseLogicOperationTest, "VMTests/vmBitwiseLogicOperationTest"} +//declare_test!{ExecutiveTests_vmBlockInfoTest, "VMTests/vmBlockInfoTest"} +declare_test!{ExecutiveTests_vmEnvironmentalInfoTest, "VMTests/vmEnvironmentalInfoTest"} diff --git a/src/tests/mod.rs b/src/tests/mod.rs index b048a1c16..63f77837b 100644 --- a/src/tests/mod.rs +++ b/src/tests/mod.rs @@ -1,4 +1,5 @@ #[macro_use] mod test_common; -mod transaction; \ No newline at end of file +mod transaction; +mod executive; diff --git a/src/tests/test_common.rs b/src/tests/test_common.rs index 8c2f25d8a..1f9b4c891 100644 --- a/src/tests/test_common.rs +++ b/src/tests/test_common.rs @@ -18,7 +18,10 @@ pub fn bytes_from_json(json: &Json) -> Bytes { } pub fn address_from_json(json: &Json) -> Address { - let s = json.as_string().unwrap(); + address_from_str(json.as_string().unwrap()) +} + +pub fn address_from_str<'a>(s: &'a str) -> Address { if s.len() % 2 == 1 { address_from_hex(&("0".to_string() + &(clean(s).to_string()))[..]) } else { @@ -27,7 +30,10 @@ pub fn address_from_json(json: &Json) -> Address { } pub fn u256_from_json(json: &Json) -> U256 { - let s = json.as_string().unwrap(); + u256_from_str(json.as_string().unwrap()) +} + +pub fn u256_from_str<'a>(s: &'a str) -> U256 { if s.len() >= 2 && &s[0..2] == "0x" { // hex U256::from_str(&s[2..]).unwrap() diff --git a/src/tests/transaction.rs b/src/tests/transaction.rs index b6f592628..7ec1f6802 100644 --- a/src/tests/transaction.rs +++ b/src/tests/transaction.rs @@ -8,7 +8,7 @@ fn do_json_test(json_data: &[u8]) -> Vec { let new_schedule = evm::Schedule::new_homestead(); for (name, test) in json.as_object().unwrap() { 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| if !cond && !fail { failed.push(name.to_string()); fail = true }; let schedule = match test.find("blocknumber") .and_then(|j| j.as_string()) .and_then(|s| BlockNumber::from_str(s).ok())