// Copyright 2015-2017 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // Parity is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with Parity. If not, see . use super::test_common::*; use action_params::ActionParams; use state::{Backend as StateBackend, State, Substate}; use executive::*; use engines::Engine; use env_info::EnvInfo; use evm; use evm::{Schedule, Ext, Factory, Finalize, VMType, ContractCreateResult, MessageCallResult}; use externalities::*; use types::executed::CallType; use tests::helpers::*; use ethjson; use trace::{Tracer, NoopTracer}; use trace::{VMTracer, NoopVMTracer}; #[derive(Debug, PartialEq, Clone)] struct CallCreate { data: Bytes, destination: Option
, gas_limit: U256, value: U256 } impl From for CallCreate { fn from(c: ethjson::vm::Call) -> Self { let dst: Option = c.destination.into(); CallCreate { data: c.data.into(), destination: dst.map(Into::into), gas_limit: c.gas_limit.into(), value: c.value.into() } } } /// Tiny wrapper around executive externalities. /// Stores callcreates. struct TestExt<'a, T: 'a, V: 'a, B: 'a> where T: Tracer, V: VMTracer, B: StateBackend { ext: Externalities<'a, T, V, B>, callcreates: Vec, contract_address: Address } impl<'a, T: 'a, V: 'a, B: 'a> TestExt<'a, T, V, B> where T: Tracer, V: VMTracer, B: StateBackend { fn new( state: &'a mut State, info: &'a EnvInfo, engine: &'a Engine, vm_factory: &'a Factory, depth: usize, origin_info: OriginInfo, substate: &'a mut Substate, output: OutputPolicy<'a, 'a>, address: Address, tracer: &'a mut T, vm_tracer: &'a mut V, ) -> trie::Result { Ok(TestExt { contract_address: contract_address(&address, &state.nonce(&address)?), ext: Externalities::new(state, info, engine, vm_factory, depth, origin_info, substate, output, tracer, vm_tracer), callcreates: vec![] }) } } impl<'a, T: 'a, V: 'a, B: 'a> Ext for TestExt<'a, T, V, B> where T: Tracer, V: VMTracer, B: StateBackend { fn storage_at(&self, key: &H256) -> trie::Result { self.ext.storage_at(key) } fn set_storage(&mut self, key: H256, value: H256) -> trie::Result<()> { self.ext.set_storage(key, value) } fn exists(&self, address: &Address) -> trie::Result { self.ext.exists(address) } fn exists_and_not_null(&self, address: &Address) -> trie::Result { self.ext.exists_and_not_null(address) } fn balance(&self, address: &Address) -> trie::Result { self.ext.balance(address) } fn origin_balance(&self) -> trie::Result { self.ext.origin_balance() } fn blockhash(&self, number: &U256) -> H256 { self.ext.blockhash(number) } fn create(&mut self, gas: &U256, value: &U256, code: &[u8]) -> ContractCreateResult { self.callcreates.push(CallCreate { data: code.to_vec(), destination: None, gas_limit: *gas, value: *value }); ContractCreateResult::Created(self.contract_address.clone(), *gas) } fn call(&mut self, gas: &U256, _sender_address: &Address, receive_address: &Address, value: Option, data: &[u8], _code_address: &Address, _output: &mut [u8], _call_type: CallType ) -> MessageCallResult { self.callcreates.push(CallCreate { data: data.to_vec(), destination: Some(receive_address.clone()), gas_limit: *gas, value: value.unwrap() }); MessageCallResult::Success(*gas) } fn extcode(&self, address: &Address) -> trie::Result> { self.ext.extcode(address) } fn extcodesize(&self, address: &Address) -> trie::Result { self.ext.extcodesize(address) } fn log(&mut self, topics: Vec, data: &[u8]) { self.ext.log(topics, data) } fn ret(self, gas: &U256, data: &[u8]) -> Result { self.ext.ret(gas, data) } fn suicide(&mut self, refund_address: &Address) -> trie::Result<()> { self.ext.suicide(refund_address) } fn schedule(&self) -> &Schedule { self.ext.schedule() } fn env_info(&self) -> &EnvInfo { self.ext.env_info() } fn depth(&self) -> usize { 0 } fn inc_sstore_clears(&mut self) { self.ext.inc_sstore_clears() } } fn do_json_test(json_data: &[u8]) -> Vec { let vms = VMType::all(); vms .iter() .flat_map(|vm| do_json_test_for(vm, json_data)) .collect() } fn do_json_test_for(vm_type: &VMType, json_data: &[u8]) -> Vec { let tests = ethjson::vm::Test::load(json_data).unwrap(); let mut failed = Vec::new(); for (name, vm) in tests.into_iter() { println!("name: {:?}", name); let mut fail = false; let mut fail_unless = |cond: bool, s: &str | if !cond && !fail { failed.push(format!("[{}] {}: {}", vm_type, name, s)); fail = true }; macro_rules! try_fail { ($e: expr) => { match $e { Ok(x) => x, Err(e) => { let msg = format!("Internal error: {}", e); fail_unless(false, &msg); continue } } } } let out_of_gas = vm.out_of_gas(); let mut state = get_temp_state(); state.populate_from(From::from(vm.pre_state.clone())); let info = From::from(vm.env); let engine = TestEngine::new(1); let vm_factory = Factory::new(vm_type.clone(), 1024 * 32); let params = ActionParams::from(vm.transaction); let mut substate = Substate::new(); let mut tracer = NoopTracer; let mut vm_tracer = NoopVMTracer; let mut output = vec![]; // execute let (res, callcreates) = { let mut ex = try_fail!(TestExt::new( &mut state, &info, &engine, &vm_factory, 0, OriginInfo::from(¶ms), &mut substate, OutputPolicy::Return(BytesRef::Flexible(&mut output), None), params.address.clone(), &mut tracer, &mut vm_tracer, )); let mut evm = vm_factory.create(params.gas); let res = evm.exec(params, &mut ex); // a return in finalize will not alter callcreates let callcreates = ex.callcreates.clone(); (res.finalize(ex), callcreates) }; 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(Some(gas_left) == vm.gas_left.map(Into::into), "gas_left is incorrect"); let vm_output: Option> = vm.output.map(Into::into); fail_unless(Some(output) == vm_output, "output is incorrect"); for (address, account) in vm.post_state.unwrap().into_iter() { let address = address.into(); let code: Vec = account.code.into(); let found_code = try_fail!(state.code(&address)); let found_balance = try_fail!(state.balance(&address)); let found_nonce = try_fail!(state.nonce(&address)); fail_unless(found_code.as_ref().map_or_else(|| code.is_empty(), |c| &**c == &code), "code is incorrect"); fail_unless(found_balance == account.balance.into(), "balance is incorrect"); fail_unless(found_nonce == account.nonce.into(), "nonce is incorrect"); for (k, v) in account.storage { let key: U256 = k.into(); let value: U256 = v.into(); let found_storage = try_fail!(state.storage_at(&address, &From::from(key))); fail_unless(found_storage == From::from(value), "storage is incorrect"); } } let calls: Option> = vm.calls.map(|c| c.into_iter().map(From::from).collect()); fail_unless(Some(callcreates) == calls, "callcreates does not match"); } }; } for f in &failed { println!("FAILED: {:?}", f); } failed } declare_test!{ExecutiveTests_vmArithmeticTest, "VMTests/vmArithmeticTest"} declare_test!{ExecutiveTests_vmBitwiseLogicOperationTest, "VMTests/vmBitwiseLogicOperationTest"} declare_test!{ExecutiveTests_vmBlockInfoTest, "VMTests/vmBlockInfoTest"} // TODO [todr] Fails with Signal 11 when using JIT declare_test!{ExecutiveTests_vmEnvironmentalInfoTest, "VMTests/vmEnvironmentalInfoTest"} declare_test!{ExecutiveTests_vmIOandFlowOperationsTest, "VMTests/vmIOandFlowOperationsTest"} declare_test!{heavy => 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"}