// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of OpenEthereum. // OpenEthereum 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. // OpenEthereum 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 OpenEthereum. If not, see . use std::{ collections::{HashMap, HashSet}, sync::Arc, }; use crate::access_list::AccessList; use bytes::Bytes; use error::TrapKind; use ethereum_types::{Address, H256, U256}; use hash::keccak; use CallType; use ContractCreateResult; use CreateContractAddress; use EnvInfo; use Ext; use GasLeft; use MessageCallResult; use Result; use ReturnData; use Schedule; pub struct FakeLogEntry { pub topics: Vec, pub data: Bytes, } #[derive(PartialEq, Eq, Hash, Debug)] pub enum FakeCallType { Call, Create, } #[derive(PartialEq, Eq, Hash, Debug)] pub struct FakeCall { pub call_type: FakeCallType, pub create_scheme: Option, pub gas: U256, pub sender_address: Option
, pub receive_address: Option
, pub value: Option, pub data: Bytes, pub code_address: Option
, } /// Fake externalities test structure. /// /// Can't do recursive calls. #[derive(Default)] pub struct FakeExt { pub store: HashMap, pub suicides: HashSet
, pub calls: HashSet, pub sstore_clears: i128, pub depth: usize, pub blockhashes: HashMap, pub codes: HashMap>, pub logs: Vec, pub info: EnvInfo, pub schedule: Schedule, pub balances: HashMap, pub tracing: bool, pub is_static: bool, pub access_list: AccessList, chain_id: u64, } // similar to the normal `finalize` function, but ignoring NeedsReturn. pub fn test_finalize(res: Result) -> Result { match res { Ok(GasLeft::Known(gas)) => Ok(gas), Ok(GasLeft::NeedsReturn { .. }) => unimplemented!(), // since ret is unimplemented. Err(e) => Err(e), } } impl FakeExt { /// New fake externalities pub fn new() -> Self { FakeExt::default() } /// New fake externalities with byzantium schedule rules pub fn new_byzantium() -> Self { let mut ext = FakeExt::default(); ext.schedule = Schedule::new_byzantium(); ext } /// New fake externalities with constantinople schedule rules pub fn new_constantinople() -> Self { let mut ext = FakeExt::default(); ext.schedule = Schedule::new_constantinople(); ext } /// New fake externalities with Istanbul schedule rules pub fn new_istanbul() -> Self { let mut ext = FakeExt::default(); ext.schedule = Schedule::new_istanbul(); ext } /// New fake externalities with Berlin schedule rules pub fn new_berlin() -> Self { let mut ext = FakeExt::default(); ext.schedule = Schedule::new_berlin(); ext } /// New fake externalities with YoloV2 schedule rules pub fn new_yolo3(from: Address, to: Address, builtins: &[Address]) -> Self { let mut ext = FakeExt::default(); ext.schedule = Schedule::new_yolo3(); ext.access_list.enable(); ext.access_list.insert_address(from); ext.access_list.insert_address(to); for builtin in builtins { ext.access_list.insert_address(*builtin); } ext } /// Alter fake externalities to allow wasm pub fn with_wasm(mut self) -> Self { self.schedule.wasm = Some(Default::default()); self } /// Set chain ID pub fn with_chain_id(mut self, chain_id: u64) -> Self { self.chain_id = chain_id; self } } impl Ext for FakeExt { fn initial_storage_at(&self, _key: &H256) -> Result { Ok(H256::new()) } fn storage_at(&self, key: &H256) -> Result { Ok(self.store.get(key).unwrap_or(&H256::new()).clone()) } fn set_storage(&mut self, key: H256, value: H256) -> Result<()> { self.store.insert(key, value); Ok(()) } fn exists(&self, address: &Address) -> Result { Ok(self.balances.contains_key(address)) } fn exists_and_not_null(&self, address: &Address) -> Result { Ok(self.balances.get(address).map_or(false, |b| !b.is_zero())) } fn origin_balance(&self) -> Result { unimplemented!() } fn balance(&self, address: &Address) -> Result { Ok(self.balances.get(address).cloned().unwrap_or(U256::zero())) } fn blockhash(&mut self, number: &U256) -> H256 { self.blockhashes.get(number).unwrap_or(&H256::new()).clone() } fn create( &mut self, gas: &U256, value: &U256, code: &[u8], address: CreateContractAddress, _trap: bool, ) -> ::std::result::Result { self.calls.insert(FakeCall { call_type: FakeCallType::Create, create_scheme: Some(address), gas: *gas, sender_address: None, receive_address: None, value: Some(*value), data: code.to_vec(), code_address: None, }); // TODO: support traps in testing. Ok(ContractCreateResult::Failed) } fn calc_address(&self, _code: &[u8], _address: CreateContractAddress) -> Option
{ None } fn call( &mut self, gas: &U256, sender_address: &Address, receive_address: &Address, value: Option, data: &[u8], code_address: &Address, _call_type: CallType, _trap: bool, ) -> ::std::result::Result { self.calls.insert(FakeCall { call_type: FakeCallType::Call, create_scheme: None, gas: *gas, sender_address: Some(sender_address.clone()), receive_address: Some(receive_address.clone()), value: value, data: data.to_vec(), code_address: Some(code_address.clone()), }); // TODO: support traps in testing. Ok(MessageCallResult::Success(*gas, ReturnData::empty())) } fn extcode(&self, address: &Address) -> Result>> { Ok(self.codes.get(address).cloned()) } fn extcodesize(&self, address: &Address) -> Result> { Ok(self.codes.get(address).map(|c| c.len())) } fn extcodehash(&self, address: &Address) -> Result> { Ok(self.codes.get(address).map(|c| keccak(c.as_ref()))) } fn log(&mut self, topics: Vec, data: &[u8]) -> Result<()> { self.logs.push(FakeLogEntry { topics, data: data.to_vec(), }); Ok(()) } fn ret(self, _gas: &U256, _data: &ReturnData, _apply_state: bool) -> Result { unimplemented!(); } fn suicide(&mut self, refund_address: &Address) -> Result<()> { self.suicides.insert(refund_address.clone()); Ok(()) } fn schedule(&self) -> &Schedule { &self.schedule } fn env_info(&self) -> &EnvInfo { &self.info } fn chain_id(&self) -> u64 { self.chain_id } fn depth(&self) -> usize { self.depth } fn is_static(&self) -> bool { self.is_static } fn add_sstore_refund(&mut self, value: usize) { self.sstore_clears += value as i128; } fn sub_sstore_refund(&mut self, value: usize) { self.sstore_clears -= value as i128; } fn trace_next_instruction(&mut self, _pc: usize, _instruction: u8, _gas: U256) -> bool { self.tracing } fn al_is_enabled(&self) -> bool { self.access_list.is_enabled() } fn al_contains_storage_key(&self, address: &Address, key: &H256) -> bool { self.access_list.contains_storage_key(address, key) } fn al_insert_storage_key(&mut self, address: Address, key: H256) { self.access_list.insert_storage_key(address, key) } fn al_contains_address(&self, address: &Address) -> bool { self.access_list.contains_address(address) } fn al_insert_address(&mut self, address: Address) { self.access_list.insert_address(address) } }