From 0dcdaa7a2a51e92bca6977c71871af34330b8864 Mon Sep 17 00:00:00 2001 From: Arkadiy Paronyan Date: Sun, 2 Oct 2016 18:45:36 +0200 Subject: [PATCH] Jumptable cache (#2427) * Jumptable cache * Updated registrar address --- ethcore/src/action_params.rs | 8 +- ethcore/src/client/client.rs | 2 +- ethcore/src/evm/ext.rs | 2 +- ethcore/src/evm/factory.rs | 22 ++-- ethcore/src/evm/interpreter/mod.rs | 47 +++---- ethcore/src/evm/interpreter/shared_cache.rs | 84 +++++++++++++ ethcore/src/evm/tests.rs | 80 ++++++------ ethcore/src/executive.rs | 24 ++-- ethcore/src/externalities.rs | 8 +- ethcore/src/json_tests/executive.rs | 4 +- ethcore/src/miner/miner.rs | 2 +- ethcore/src/state/account.rs | 130 ++++++++++---------- ethcore/src/state/mod.rs | 16 ++- ethcore/src/tests/client.rs | 2 +- ethcore/src/types/trace_types/trace.rs | 2 +- ethcore/src/verification/queue/kind.rs | 3 +- rpc/src/v1/tests/helpers/miner_service.rs | 2 +- util/src/misc.rs | 2 +- util/src/standard.rs | 2 +- 19 files changed, 266 insertions(+), 176 deletions(-) create mode 100644 ethcore/src/evm/interpreter/shared_cache.rs diff --git a/ethcore/src/action_params.rs b/ethcore/src/action_params.rs index 1886c3d36..46c159269 100644 --- a/ethcore/src/action_params.rs +++ b/ethcore/src/action_params.rs @@ -43,6 +43,8 @@ impl ActionValue { pub struct ActionParams { /// Address of currently executed code. pub code_address: Address, + /// Hash of currently executed code. + pub code_hash: H256, /// Receive address. Usually equal to code_address, /// except when called using CALLCODE. pub address: Address, @@ -57,7 +59,7 @@ pub struct ActionParams { /// Transaction value. pub value: ActionValue, /// Code being executed. - pub code: Option, + pub code: Option>, /// Input data. pub data: Option, /// Type of call @@ -70,6 +72,7 @@ impl Default for ActionParams { fn default() -> ActionParams { ActionParams { code_address: Address::new(), + code_hash: SHA3_EMPTY, address: Address::new(), sender: Address::new(), origin: Address::new(), @@ -88,10 +91,11 @@ impl From for ActionParams { let address: Address = t.address.into(); ActionParams { code_address: Address::new(), + code_hash: (&*t.code).sha3(), address: address, sender: t.sender.into(), origin: t.origin.into(), - code: Some(t.code.into()), + code: Some(Arc::new(t.code.into())), data: Some(t.data.into()), gas: t.gas.into(), gas_price: t.gas_price.into(), diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index f8b4259d5..32a363210 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -831,7 +831,7 @@ impl BlockChainClient for Client { } fn code(&self, address: &Address, id: BlockID) -> Option> { - self.state_at(id).map(|s| s.code(address)) + self.state_at(id).map(|s| s.code(address).map(|c| (*c).clone())) } fn balance(&self, address: &Address, id: BlockID) -> Option { diff --git a/ethcore/src/evm/ext.rs b/ethcore/src/evm/ext.rs index 2bbc7035b..6397f067e 100644 --- a/ethcore/src/evm/ext.rs +++ b/ethcore/src/evm/ext.rs @@ -81,7 +81,7 @@ pub trait Ext { ) -> MessageCallResult; /// Returns code at given address - fn extcode(&self, address: &Address) -> Bytes; + fn extcode(&self, address: &Address) -> Arc; /// Returns code size at given address fn extcodesize(&self, address: &Address) -> usize; diff --git a/ethcore/src/evm/factory.rs b/ethcore/src/evm/factory.rs index 94800c7de..629b423da 100644 --- a/ethcore/src/evm/factory.rs +++ b/ethcore/src/evm/factory.rs @@ -18,8 +18,10 @@ //! //! TODO: consider spliting it into two separate files. use std::fmt; +use std::sync::Arc; use evm::Evm; use util::{U256, Uint}; +use super::interpreter::SharedCache; #[derive(Debug, PartialEq, Clone)] /// Type of EVM to use. @@ -82,7 +84,8 @@ impl VMType { /// Evm factory. Creates appropriate Evm. #[derive(Clone)] pub struct Factory { - evm: VMType + evm: VMType, + evm_cache: Arc, } impl Factory { @@ -95,9 +98,9 @@ impl Factory { Box::new(super::jit::JitEvm::default()) }, VMType::Interpreter => if Self::can_fit_in_usize(gas) { - Box::new(super::interpreter::Interpreter::::default()) + Box::new(super::interpreter::Interpreter::::new(self.evm_cache.clone())) } else { - Box::new(super::interpreter::Interpreter::::default()) + Box::new(super::interpreter::Interpreter::::new(self.evm_cache.clone())) } } } @@ -108,9 +111,9 @@ impl Factory { pub fn create(&self, gas: U256) -> Box { match self.evm { VMType::Interpreter => if Self::can_fit_in_usize(gas) { - Box::new(super::interpreter::Interpreter::::default()) + Box::new(super::interpreter::Interpreter::::new(self.evm_cache.clone())) } else { - Box::new(super::interpreter::Interpreter::::default()) + Box::new(super::interpreter::Interpreter::::new(self.evm_cache.clone())) } } } @@ -118,7 +121,8 @@ impl Factory { /// Create new instance of specific `VMType` factory pub fn new(evm: VMType) -> Self { Factory { - evm: evm + evm: evm, + evm_cache: Arc::new(SharedCache::default()), } } @@ -132,7 +136,8 @@ impl Default for Factory { #[cfg(all(feature = "jit", not(test)))] fn default() -> Factory { Factory { - evm: VMType::Jit + evm: VMType::Jit, + evm_cache: Arc::new(SharedCache::default()), } } @@ -140,7 +145,8 @@ impl Default for Factory { #[cfg(any(not(feature = "jit"), test))] fn default() -> Factory { Factory { - evm: VMType::Interpreter + evm: VMType::Interpreter, + evm_cache: Arc::new(SharedCache::default()), } } } diff --git a/ethcore/src/evm/interpreter/mod.rs b/ethcore/src/evm/interpreter/mod.rs index ad2d5cd34..2a6ab8460 100644 --- a/ethcore/src/evm/interpreter/mod.rs +++ b/ethcore/src/evm/interpreter/mod.rs @@ -31,10 +31,12 @@ macro_rules! evm_debug { mod gasometer; mod stack; mod memory; +mod shared_cache; use self::gasometer::Gasometer; use self::stack::{Stack, VecStack}; use self::memory::Memory; +pub use self::shared_cache::SharedCache; use std::marker::PhantomData; use common::*; @@ -98,9 +100,9 @@ enum InstructionResult { /// Intepreter EVM implementation -#[derive(Default)] pub struct Interpreter { mem: Vec, + cache: Arc, _type: PhantomData, } @@ -109,7 +111,7 @@ impl evm::Evm for Interpreter { self.mem.clear(); let code = ¶ms.code.as_ref().unwrap(); - let valid_jump_destinations = self.find_jump_destinations(code); + let valid_jump_destinations = self.cache.jump_destinations(¶ms.code_hash, code); let mut gasometer = Gasometer::::new(try!(Cost::from_u256(params.gas))); let mut stack = VecStack::with_capacity(ext.schedule().stack_limit, U256::zero()); @@ -188,6 +190,14 @@ impl evm::Evm for Interpreter { } impl Interpreter { + /// Create a new `Interpreter` instance with shared cache. + pub fn new(cache: Arc) -> Interpreter { + Interpreter { + mem: Vec::new(), + cache: cache, + _type: PhantomData::default(), + } + } fn verify_instruction(&self, ext: &evm::Ext, instruction: Instruction, info: &InstructionInfo, stack: &Stack) -> evm::Result<()> { let schedule = ext.schedule(); @@ -486,10 +496,10 @@ impl Interpreter { stack.push(U256::from(len)); }, instructions::CALLDATACOPY => { - self.copy_data_to_memory(stack, ¶ms.data.clone().unwrap_or_else(|| vec![])); + self.copy_data_to_memory(stack, params.data.as_ref().map_or_else(|| &[] as &[u8], |d| &*d as &[u8])); }, instructions::CODECOPY => { - self.copy_data_to_memory(stack, ¶ms.code.clone().unwrap_or_else(|| vec![])); + self.copy_data_to_memory(stack, params.code.as_ref().map_or_else(|| &[] as &[u8], |c| &**c as &[u8])); }, instructions::EXTCODECOPY => { let address = u256_to_address(&stack.pop_back()); @@ -790,23 +800,6 @@ impl Interpreter { Ok(()) } - fn find_jump_destinations(&self, code: &[u8]) -> BitSet { - let mut jump_dests = BitSet::with_capacity(code.len()); - let mut position = 0; - - while position < code.len() { - let instruction = code[position]; - - if instruction == instructions::JUMPDEST { - jump_dests.insert(position); - } else if instructions::is_push(instruction) { - position += instructions::get_push_bytes(instruction); - } - position += 1; - } - - jump_dests - } } fn get_and_reset_sign(value: U256) -> (U256, bool) { @@ -833,15 +826,3 @@ fn address_to_u256(value: Address) -> U256 { U256::from(&*H256::from(value)) } -#[test] -fn test_find_jump_destinations() { - // given - let interpreter = Interpreter::::default(); - let code = "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff5b01600055".from_hex().unwrap(); - - // when - let valid_jump_destinations = interpreter.find_jump_destinations(&code); - - // then - assert!(valid_jump_destinations.contains(66)); -} diff --git a/ethcore/src/evm/interpreter/shared_cache.rs b/ethcore/src/evm/interpreter/shared_cache.rs new file mode 100644 index 000000000..76360138b --- /dev/null +++ b/ethcore/src/evm/interpreter/shared_cache.rs @@ -0,0 +1,84 @@ +// Copyright 2015, 2016 Ethcore (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 std::sync::Arc; +use lru_cache::LruCache; +use util::{H256, Mutex}; +use util::sha3::*; +use bit_set::BitSet; +use super::super::instructions; + +const CACHE_CODE_ITEMS: usize = 4096; + +/// GLobal cache for EVM interpreter +pub struct SharedCache { + jump_destinations: Mutex>> +} + +impl SharedCache { + /// Get jump destincations bitmap for a contract. + pub fn jump_destinations(&self, code_hash: &H256, code: &[u8]) -> Arc { + if code_hash == &SHA3_EMPTY { + return Self::find_jump_destinations(code); + } + if let Some(d) = self.jump_destinations.lock().get_mut(code_hash) { + return d.clone(); + } + + let d = Self::find_jump_destinations(code); + self.jump_destinations.lock().insert(code_hash.clone(), d.clone()); + d + } + + fn find_jump_destinations(code: &[u8]) -> Arc { + let mut jump_dests = BitSet::with_capacity(code.len()); + let mut position = 0; + + while position < code.len() { + let instruction = code[position]; + + if instruction == instructions::JUMPDEST { + jump_dests.insert(position); + } else if instructions::is_push(instruction) { + position += instructions::get_push_bytes(instruction); + } + position += 1; + } + Arc::new(jump_dests) + } +} + +impl Default for SharedCache { + fn default() -> SharedCache { + SharedCache { + jump_destinations: Mutex::new(LruCache::new(CACHE_CODE_ITEMS)), + } + } +} + + +#[test] +fn test_find_jump_destinations() { + use util::FromHex; + // given + let code = "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff5b01600055".from_hex().unwrap(); + + // when + let valid_jump_destinations = SharedCache::find_jump_destinations(&code); + + // then + assert!(valid_jump_destinations.contains(66)); +} diff --git a/ethcore/src/evm/tests.rs b/ethcore/src/evm/tests.rs index 89a4c4ba9..eb7d168cf 100644 --- a/ethcore/src/evm/tests.rs +++ b/ethcore/src/evm/tests.rs @@ -49,7 +49,7 @@ pub struct FakeExt { depth: usize, store: HashMap, blockhashes: HashMap, - codes: HashMap, + codes: HashMap>, logs: Vec, _suicides: HashSet
, info: EnvInfo, @@ -136,8 +136,8 @@ impl Ext for FakeExt { MessageCallResult::Success(*gas) } - fn extcode(&self, address: &Address) -> Bytes { - self.codes.get(address).unwrap_or(&Bytes::new()).clone() + fn extcode(&self, address: &Address) -> Arc { + self.codes.get(address).unwrap_or(&Arc::new(Bytes::new())).clone() } fn extcodesize(&self, address: &Address) -> usize { @@ -184,11 +184,11 @@ fn test_stack_underflow() { let mut params = ActionParams::default(); params.address = address.clone(); params.gas = U256::from(100_000); - params.code = Some(code); + params.code = Some(Arc::new(code)); let mut ext = FakeExt::new(); let err = { - let mut vm : Box = Box::new(super::interpreter::Interpreter::::default()); + let mut vm : Box = Box::new(super::interpreter::Interpreter::::new(Arc::new(super::interpreter::SharedCache::default()))); test_finalize(vm.exec(params, &mut ext)).unwrap_err() }; @@ -211,7 +211,7 @@ fn test_add(factory: super::Factory) { let mut params = ActionParams::default(); params.address = address.clone(); params.gas = U256::from(100_000); - params.code = Some(code); + params.code = Some(Arc::new(code)); let mut ext = FakeExt::new(); let gas_left = { @@ -231,7 +231,7 @@ fn test_sha3(factory: super::Factory) { let mut params = ActionParams::default(); params.address = address.clone(); params.gas = U256::from(100_000); - params.code = Some(code); + params.code = Some(Arc::new(code)); let mut ext = FakeExt::new(); let gas_left = { @@ -251,7 +251,7 @@ fn test_address(factory: super::Factory) { let mut params = ActionParams::default(); params.address = address.clone(); params.gas = U256::from(100_000); - params.code = Some(code); + params.code = Some(Arc::new(code)); let mut ext = FakeExt::new(); let gas_left = { @@ -273,7 +273,7 @@ fn test_origin(factory: super::Factory) { params.address = address.clone(); params.origin = origin.clone(); params.gas = U256::from(100_000); - params.code = Some(code); + params.code = Some(Arc::new(code)); let mut ext = FakeExt::new(); let gas_left = { @@ -295,7 +295,7 @@ fn test_sender(factory: super::Factory) { params.address = address.clone(); params.sender = sender.clone(); params.gas = U256::from(100_000); - params.code = Some(code); + params.code = Some(Arc::new(code)); let mut ext = FakeExt::new(); let gas_left = { @@ -329,9 +329,9 @@ fn test_extcodecopy(factory: super::Factory) { params.address = address.clone(); params.sender = sender.clone(); params.gas = U256::from(100_000); - params.code = Some(code); + params.code = Some(Arc::new(code)); let mut ext = FakeExt::new(); - ext.codes.insert(sender, sender_code); + ext.codes.insert(sender, Arc::new(sender_code)); let gas_left = { let mut vm = factory.create(params.gas); @@ -350,7 +350,7 @@ fn test_log_empty(factory: super::Factory) { let mut params = ActionParams::default(); params.address = address.clone(); params.gas = U256::from(100_000); - params.code = Some(code); + params.code = Some(Arc::new(code)); let mut ext = FakeExt::new(); let gas_left = { @@ -382,7 +382,7 @@ fn test_log_sender(factory: super::Factory) { params.address = address.clone(); params.sender = sender.clone(); params.gas = U256::from(100_000); - params.code = Some(code); + params.code = Some(Arc::new(code)); let mut ext = FakeExt::new(); let gas_left = { @@ -406,7 +406,7 @@ fn test_blockhash(factory: super::Factory) { let mut params = ActionParams::default(); params.address = address.clone(); params.gas = U256::from(100_000); - params.code = Some(code); + params.code = Some(Arc::new(code)); let mut ext = FakeExt::new(); ext.blockhashes.insert(U256::zero(), blockhash.clone()); @@ -428,7 +428,7 @@ fn test_calldataload(factory: super::Factory) { let mut params = ActionParams::default(); params.address = address.clone(); params.gas = U256::from(100_000); - params.code = Some(code); + params.code = Some(Arc::new(code)); params.data = Some(data); let mut ext = FakeExt::new(); @@ -449,7 +449,7 @@ fn test_author(factory: super::Factory) { let mut params = ActionParams::default(); params.gas = U256::from(100_000); - params.code = Some(code); + params.code = Some(Arc::new(code)); let mut ext = FakeExt::new(); ext.info.author = author; @@ -469,7 +469,7 @@ fn test_timestamp(factory: super::Factory) { let mut params = ActionParams::default(); params.gas = U256::from(100_000); - params.code = Some(code); + params.code = Some(Arc::new(code)); let mut ext = FakeExt::new(); ext.info.timestamp = timestamp; @@ -489,7 +489,7 @@ fn test_number(factory: super::Factory) { let mut params = ActionParams::default(); params.gas = U256::from(100_000); - params.code = Some(code); + params.code = Some(Arc::new(code)); let mut ext = FakeExt::new(); ext.info.number = number; @@ -509,7 +509,7 @@ fn test_difficulty(factory: super::Factory) { let mut params = ActionParams::default(); params.gas = U256::from(100_000); - params.code = Some(code); + params.code = Some(Arc::new(code)); let mut ext = FakeExt::new(); ext.info.difficulty = difficulty; @@ -529,7 +529,7 @@ fn test_gas_limit(factory: super::Factory) { let mut params = ActionParams::default(); params.gas = U256::from(100_000); - params.code = Some(code); + params.code = Some(Arc::new(code)); let mut ext = FakeExt::new(); ext.info.gas_limit = gas_limit; @@ -548,7 +548,7 @@ fn test_mul(factory: super::Factory) { let mut params = ActionParams::default(); params.gas = U256::from(100_000); - params.code = Some(code); + params.code = Some(Arc::new(code)); let mut ext = FakeExt::new(); let gas_left = { @@ -566,7 +566,7 @@ fn test_sub(factory: super::Factory) { let mut params = ActionParams::default(); params.gas = U256::from(100_000); - params.code = Some(code); + params.code = Some(Arc::new(code)); let mut ext = FakeExt::new(); let gas_left = { @@ -584,7 +584,7 @@ fn test_div(factory: super::Factory) { let mut params = ActionParams::default(); params.gas = U256::from(100_000); - params.code = Some(code); + params.code = Some(Arc::new(code)); let mut ext = FakeExt::new(); let gas_left = { @@ -602,7 +602,7 @@ fn test_div_zero(factory: super::Factory) { let mut params = ActionParams::default(); params.gas = U256::from(100_000); - params.code = Some(code); + params.code = Some(Arc::new(code)); let mut ext = FakeExt::new(); let gas_left = { @@ -620,7 +620,7 @@ fn test_mod(factory: super::Factory) { let mut params = ActionParams::default(); params.gas = U256::from(100_000); - params.code = Some(code); + params.code = Some(Arc::new(code)); let mut ext = FakeExt::new(); let gas_left = { @@ -639,7 +639,7 @@ fn test_smod(factory: super::Factory) { let mut params = ActionParams::default(); params.gas = U256::from(100_000); - params.code = Some(code); + params.code = Some(Arc::new(code)); let mut ext = FakeExt::new(); let gas_left = { @@ -658,7 +658,7 @@ fn test_sdiv(factory: super::Factory) { let mut params = ActionParams::default(); params.gas = U256::from(100_000); - params.code = Some(code); + params.code = Some(Arc::new(code)); let mut ext = FakeExt::new(); let gas_left = { @@ -677,7 +677,7 @@ fn test_exp(factory: super::Factory) { let mut params = ActionParams::default(); params.gas = U256::from(100_000); - params.code = Some(code); + params.code = Some(Arc::new(code)); let mut ext = FakeExt::new(); let gas_left = { @@ -697,7 +697,7 @@ fn test_comparison(factory: super::Factory) { let mut params = ActionParams::default(); params.gas = U256::from(100_000); - params.code = Some(code); + params.code = Some(Arc::new(code)); let mut ext = FakeExt::new(); let gas_left = { @@ -718,7 +718,7 @@ fn test_signed_comparison(factory: super::Factory) { let mut params = ActionParams::default(); params.gas = U256::from(100_000); - params.code = Some(code); + params.code = Some(Arc::new(code)); let mut ext = FakeExt::new(); let gas_left = { @@ -739,7 +739,7 @@ fn test_bitops(factory: super::Factory) { let mut params = ActionParams::default(); params.gas = U256::from(150_000); - params.code = Some(code); + params.code = Some(Arc::new(code)); let mut ext = FakeExt::new(); let gas_left = { @@ -762,7 +762,7 @@ fn test_addmod_mulmod(factory: super::Factory) { let mut params = ActionParams::default(); params.gas = U256::from(100_000); - params.code = Some(code); + params.code = Some(Arc::new(code)); let mut ext = FakeExt::new(); let gas_left = { @@ -783,7 +783,7 @@ fn test_byte(factory: super::Factory) { let mut params = ActionParams::default(); params.gas = U256::from(100_000); - params.code = Some(code); + params.code = Some(Arc::new(code)); let mut ext = FakeExt::new(); let gas_left = { @@ -802,7 +802,7 @@ fn test_signextend(factory: super::Factory) { let mut params = ActionParams::default(); params.gas = U256::from(100_000); - params.code = Some(code); + params.code = Some(Arc::new(code)); let mut ext = FakeExt::new(); let gas_left = { @@ -822,7 +822,7 @@ fn test_badinstruction_int() { let mut params = ActionParams::default(); params.gas = U256::from(100_000); - params.code = Some(code); + params.code = Some(Arc::new(code)); let mut ext = FakeExt::new(); let err = { @@ -842,7 +842,7 @@ fn test_pop(factory: super::Factory) { let mut params = ActionParams::default(); params.gas = U256::from(100_000); - params.code = Some(code); + params.code = Some(Arc::new(code)); let mut ext = FakeExt::new(); let gas_left = { @@ -862,7 +862,7 @@ fn test_extops(factory: super::Factory) { params.gas = U256::from(150_000); params.gas_price = U256::from(0x32); params.value = ActionValue::Transfer(U256::from(0x99)); - params.code = Some(code); + params.code = Some(Arc::new(code)); let mut ext = FakeExt::new(); let gas_left = { @@ -885,7 +885,7 @@ fn test_jumps(factory: super::Factory) { let mut params = ActionParams::default(); params.gas = U256::from(150_000); - params.code = Some(code); + params.code = Some(Arc::new(code)); let mut ext = FakeExt::new(); let gas_left = { @@ -908,7 +908,7 @@ fn test_calls(factory: super::Factory) { let code_address = Address::from(0x998); let mut params = ActionParams::default(); params.gas = U256::from(150_000); - params.code = Some(code); + params.code = Some(Arc::new(code)); params.address = address.clone(); let mut ext = FakeExt::new(); ext.balances = { diff --git a/ethcore/src/executive.rs b/ethcore/src/executive.rs index 6a7e4672a..b0b0b58c8 100644 --- a/ethcore/src/executive.rs +++ b/ethcore/src/executive.rs @@ -168,13 +168,14 @@ impl<'a> Executive<'a> { let new_address = contract_address(&sender, &nonce); let params = ActionParams { code_address: new_address.clone(), + code_hash: t.data.sha3(), address: new_address, sender: sender.clone(), origin: sender.clone(), gas: init_gas, gas_price: t.gas_price, value: ActionValue::Transfer(t.value), - code: Some(t.data.clone()), + code: Some(Arc::new(t.data.clone())), data: None, call_type: CallType::None, }; @@ -190,6 +191,7 @@ impl<'a> Executive<'a> { gas_price: t.gas_price, value: ActionValue::Transfer(t.value), code: self.state.code(address), + code_hash: self.state.code_hash(address), data: Some(t.data.clone()), call_type: CallType::Call, }; @@ -511,7 +513,7 @@ mod tests { params.address = address.clone(); params.sender = sender.clone(); params.gas = U256::from(100_000); - params.code = Some("3331600055".from_hex().unwrap()); + params.code = Some(Arc::new("3331600055".from_hex().unwrap())); params.value = ActionValue::Transfer(U256::from(0x7)); let mut state_result = get_temp_state(); let mut state = state_result.reference_mut(); @@ -570,7 +572,7 @@ mod tests { params.sender = sender.clone(); params.origin = sender.clone(); params.gas = U256::from(100_000); - params.code = Some(code.clone()); + params.code = Some(Arc::new(code)); params.value = ActionValue::Transfer(U256::from(100)); let mut state_result = get_temp_state(); let mut state = state_result.reference_mut(); @@ -628,7 +630,7 @@ mod tests { params.sender = sender.clone(); params.origin = sender.clone(); params.gas = U256::from(100_000); - params.code = Some(code.clone()); + params.code = Some(Arc::new(code)); params.value = ActionValue::Transfer(U256::from(100)); params.call_type = CallType::Call; let mut state_result = get_temp_state(); @@ -740,7 +742,7 @@ mod tests { params.sender = sender.clone(); params.origin = sender.clone(); params.gas = U256::from(100_000); - params.code = Some(code.clone()); + params.code = Some(Arc::new(code)); params.value = ActionValue::Transfer(100.into()); let mut state_result = get_temp_state(); let mut state = state_result.reference_mut(); @@ -828,7 +830,7 @@ mod tests { params.sender = sender.clone(); params.origin = sender.clone(); params.gas = U256::from(100_000); - params.code = Some(code.clone()); + params.code = Some(Arc::new(code)); params.value = ActionValue::Transfer(U256::from(100)); let mut state_result = get_temp_state(); let mut state = state_result.reference_mut(); @@ -880,7 +882,7 @@ mod tests { params.sender = sender.clone(); params.origin = sender.clone(); params.gas = U256::from(100_000); - params.code = Some(code.clone()); + params.code = Some(Arc::new(code)); params.value = ActionValue::Transfer(U256::from(100)); let mut state_result = get_temp_state(); let mut state = state_result.reference_mut(); @@ -937,7 +939,7 @@ mod tests { params.address = address_a.clone(); params.sender = sender.clone(); params.gas = U256::from(100_000); - params.code = Some(code_a.clone()); + params.code = Some(Arc::new(code_a.clone())); params.value = ActionValue::Transfer(U256::from(100_000)); let mut state_result = get_temp_state(); @@ -987,10 +989,10 @@ mod tests { let mut params = ActionParams::default(); params.address = address.clone(); params.gas = U256::from(100_000); - params.code = Some(code.clone()); + params.code = Some(Arc::new(code.clone())); let mut state_result = get_temp_state(); let mut state = state_result.reference_mut(); - state.init_code(&address, code.clone()); + state.init_code(&address, code); let info = EnvInfo::default(); let engine = TestEngine::new(0); let mut substate = Substate::new(); @@ -1188,7 +1190,7 @@ mod tests { params.sender = sender.clone(); params.origin = sender.clone(); params.gas = U256::from(0x0186a0); - params.code = Some(code.clone()); + params.code = Some(Arc::new(code)); params.value = ActionValue::Transfer(U256::from_str("0de0b6b3a7640000").unwrap()); let mut state_result = get_temp_state(); let mut state = state_result.reference_mut(); diff --git a/ethcore/src/externalities.rs b/ethcore/src/externalities.rs index 82b946ffc..67c04aefb 100644 --- a/ethcore/src/externalities.rs +++ b/ethcore/src/externalities.rs @@ -146,7 +146,8 @@ impl<'a, T, V> Ext for Externalities<'a, T, V> where T: 'a + Tracer, V: 'a + VMT gas: *gas, gas_price: self.origin_info.gas_price, value: ActionValue::Transfer(*value), - code: Some(code.to_vec()), + code: Some(Arc::new(code.to_vec())), + code_hash: code.sha3(), data: None, call_type: CallType::None, }; @@ -185,6 +186,7 @@ impl<'a, T, V> Ext for Externalities<'a, T, V> where T: 'a + Tracer, V: 'a + VMT gas: *gas, gas_price: self.origin_info.gas_price, code: self.state.code(code_address), + code_hash: self.state.code_hash(code_address), data: Some(data.to_vec()), call_type: call_type, }; @@ -201,8 +203,8 @@ impl<'a, T, V> Ext for Externalities<'a, T, V> where T: 'a + Tracer, V: 'a + VMT } } - fn extcode(&self, address: &Address) -> Bytes { - self.state.code(address).unwrap_or_else(|| vec![]) + fn extcode(&self, address: &Address) -> Arc { + self.state.code(address).unwrap_or_else(|| Arc::new(vec![])) } fn extcodesize(&self, address: &Address) -> usize { diff --git a/ethcore/src/json_tests/executive.rs b/ethcore/src/json_tests/executive.rs index 1fe98acdb..5576f9ad4 100644 --- a/ethcore/src/json_tests/executive.rs +++ b/ethcore/src/json_tests/executive.rs @@ -127,7 +127,7 @@ impl<'a, T, V> Ext for TestExt<'a, T, V> where T: Tracer, V: VMTracer { MessageCallResult::Success(*gas) } - fn extcode(&self, address: &Address) -> Bytes { + fn extcode(&self, address: &Address) -> Arc { self.ext.extcode(address) } @@ -232,7 +232,7 @@ fn do_json_test_for(vm_type: &VMType, json_data: &[u8]) -> Vec { for (address, account) in vm.post_state.unwrap().into_iter() { let address = address.into(); let code: Vec = account.code.into(); - fail_unless(state.code(&address).unwrap_or_else(Vec::new) == code, "code is incorrect"); + fail_unless(state.code(&address).as_ref().map_or_else(|| code.is_empty(), |c| &**c == &code), "code is incorrect"); fail_unless(state.balance(&address) == account.balance.into(), "balance is incorrect"); fail_unless(state.nonce(&address) == account.nonce.into(), "nonce is incorrect"); account.storage.into_iter().foreach(|(k, v)| { diff --git a/ethcore/src/miner/miner.rs b/ethcore/src/miner/miner.rs index 2b0585db7..77183d452 100644 --- a/ethcore/src/miner/miner.rs +++ b/ethcore/src/miner/miner.rs @@ -587,7 +587,7 @@ impl MinerService for Miner { fn code(&self, chain: &MiningBlockChainClient, address: &Address) -> Option { let sealing_work = self.sealing_work.lock(); - sealing_work.queue.peek_last_ref().map_or_else(|| chain.latest_code(address), |b| b.block().fields().state.code(address)) + sealing_work.queue.peek_last_ref().map_or_else(|| chain.latest_code(address), |b| b.block().fields().state.code(address).map(|c| (*c).clone())) } fn set_author(&self, author: Address) { diff --git a/ethcore/src/state/account.rs b/ethcore/src/state/account.rs index 07478220a..bd7ed810b 100644 --- a/ethcore/src/state/account.rs +++ b/ethcore/src/state/account.rs @@ -40,14 +40,16 @@ pub struct Account { // Modified storage. Accumulates changes to storage made in `set_storage` // Takes precedence over `storage_cache`. storage_changes: HashMap, - // Code hash of the account. If None, means that it's a contract whose code has not yet been set. - code_hash: Option, + // Code hash of the account. + code_hash: H256, // Size of the accoun code. code_size: Option, // Code cache of the account. - code_cache: Bytes, - // Account is new or has been modified + code_cache: Arc, + // Account is new or has been modified. filth: Filth, + // Account code new or has been modified. + code_filth: Filth, // Cached address hash. address_hash: Cell>, } @@ -62,10 +64,11 @@ impl Account { storage_root: SHA3_NULL_RLP, storage_cache: Self::empty_storage_cache(), storage_changes: storage, - code_hash: Some(code.sha3()), + code_hash: code.sha3(), code_size: Some(code.len()), - code_cache: code, + code_cache: Arc::new(code), filth: Filth::Dirty, + code_filth: Filth::Dirty, address_hash: Cell::new(None), } } @@ -82,9 +85,10 @@ impl Account { storage_root: SHA3_NULL_RLP, storage_cache: Self::empty_storage_cache(), storage_changes: pod.storage.into_iter().collect(), - code_hash: pod.code.as_ref().map(|c| c.sha3()), + code_hash: pod.code.as_ref().map_or(SHA3_EMPTY, |c| c.sha3()), + code_filth: Filth::Dirty, code_size: Some(pod.code.as_ref().map_or(0, |c| c.len())), - code_cache: pod.code.map_or_else(|| { warn!("POD account with unknown code is being created! Assuming no code."); vec![] }, |c| c), + code_cache: Arc::new(pod.code.map_or_else(|| { warn!("POD account with unknown code is being created! Assuming no code."); vec![] }, |c| c)), filth: Filth::Dirty, address_hash: Cell::new(None), } @@ -98,10 +102,11 @@ impl Account { storage_root: SHA3_NULL_RLP, storage_cache: Self::empty_storage_cache(), storage_changes: HashMap::new(), - code_hash: Some(SHA3_EMPTY), - code_cache: vec![], + code_hash: SHA3_EMPTY, + code_cache: Arc::new(vec![]), code_size: Some(0), filth: Filth::Dirty, + code_filth: Filth::Clean, address_hash: Cell::new(None), } } @@ -115,10 +120,11 @@ impl Account { storage_root: r.val_at(2), storage_cache: Self::empty_storage_cache(), storage_changes: HashMap::new(), - code_hash: Some(r.val_at(3)), - code_cache: vec![], + code_hash: r.val_at(3), + code_cache: Arc::new(vec![]), code_size: None, filth: Filth::Clean, + code_filth: Filth::Clean, address_hash: Cell::new(None), } } @@ -132,10 +138,11 @@ impl Account { storage_root: SHA3_NULL_RLP, storage_cache: Self::empty_storage_cache(), storage_changes: HashMap::new(), - code_hash: None, - code_cache: vec![], + code_hash: SHA3_EMPTY, + code_cache: Arc::new(vec![]), code_size: None, filth: Filth::Dirty, + code_filth: Filth::Clean, address_hash: Cell::new(None), } } @@ -143,16 +150,15 @@ impl Account { /// Set this account's code to the given code. /// NOTE: Account should have been created with `new_contract()` pub fn init_code(&mut self, code: Bytes) { - assert!(self.code_hash.is_none()); - self.code_cache = code; + self.code_hash = code.sha3(); + self.code_cache = Arc::new(code); self.code_size = Some(self.code_cache.len()); self.filth = Filth::Dirty; + self.code_filth = Filth::Dirty; } /// Reset this account's code to the given code. pub fn reset_code(&mut self, code: Bytes) { - self.code_hash = None; - self.code_size = Some(0); self.init_code(code); } @@ -209,10 +215,9 @@ impl Account { /// return the nonce associated with this account. pub fn nonce(&self) -> &U256 { &self.nonce } - #[cfg(test)] /// return the code hash associated with this account. pub fn code_hash(&self) -> H256 { - self.code_hash.clone().unwrap_or(SHA3_EMPTY) + self.code_hash.clone() } /// return the code hash associated with this account. @@ -227,13 +232,11 @@ impl Account { /// returns the account's code. If `None` then the code cache isn't available - /// get someone who knows to call `note_code`. - pub fn code(&self) -> Option<&[u8]> { - match self.code_hash { - Some(c) if c == SHA3_EMPTY && self.code_cache.is_empty() => Some(&self.code_cache), - Some(_) if !self.code_cache.is_empty() => Some(&self.code_cache), - None => Some(&self.code_cache), - _ => None, + pub fn code(&self) -> Option> { + if self.code_hash != SHA3_EMPTY && self.code_cache.is_empty() { + return None; } + Some(self.code_cache.clone()) } /// returns the account's code size. If `None` then the code cache or code size cache isn't available - @@ -246,24 +249,23 @@ impl Account { /// Provide a byte array which hashes to the `code_hash`. returns the hash as a result. pub fn note_code(&mut self, code: Bytes) -> Result<(), H256> { let h = code.sha3(); - match self.code_hash { - Some(ref i) if h == *i => { - self.code_cache = code; - self.code_size = Some(self.code_cache.len()); - Ok(()) - }, - _ => Err(h) + if self.code_hash == h { + self.code_cache = Arc::new(code); + self.code_size = Some(self.code_cache.len()); + Ok(()) + } else { + Err(h) } } /// Is `code_cache` valid; such that code is going to return Some? pub fn is_cached(&self) -> bool { - !self.code_cache.is_empty() || (self.code_cache.is_empty() && self.code_hash == Some(SHA3_EMPTY)) + !self.code_cache.is_empty() || (self.code_cache.is_empty() && self.code_hash == SHA3_EMPTY) } /// Is this a new or modified account? pub fn is_dirty(&self) -> bool { - self.filth == Filth::Dirty || !self.storage_is_clean() + self.filth == Filth::Dirty || self.code_filth == Filth::Dirty || !self.storage_is_clean() } /// Mark account as clean. @@ -277,20 +279,17 @@ impl Account { // TODO: fill out self.code_cache; trace!("Account::cache_code: ic={}; self.code_hash={:?}, self.code_cache={}", self.is_cached(), self.code_hash, self.code_cache.pretty()); self.is_cached() || - match self.code_hash { - Some(ref h) => match db.get(h) { - Some(x) => { - self.code_cache = x.to_vec(); - self.code_size = Some(x.len()); - true - }, - _ => { - warn!("Failed reverse get of {}", h); - false - }, - }, - _ => false, - } + match db.get(&self.code_hash) { + Some(x) => { + self.code_cache = Arc::new(x.to_vec()); + self.code_size = Some(x.len()); + true + }, + _ => { + warn!("Failed reverse get of {}", self.code_hash); + false + }, + } } /// Provide a database to get `code_size`. Should not be called if it is a contract without code. @@ -298,18 +297,19 @@ impl Account { // TODO: fill out self.code_cache; trace!("Account::cache_code_size: ic={}; self.code_hash={:?}, self.code_cache={}", self.is_cached(), self.code_hash, self.code_cache.pretty()); self.code_size.is_some() || - match self.code_hash { - Some(ref h) if h != &SHA3_EMPTY => match db.get(h) { + if self.code_hash != SHA3_EMPTY { + match db.get(&self.code_hash) { Some(x) => { self.code_size = Some(x.len()); true }, _ => { - warn!("Failed reverse get of {}", h); + warn!("Failed reverse get of {}", self.code_hash); false }, - }, - _ => false, + } + } else { + false } } @@ -370,15 +370,16 @@ impl Account { /// Commit any unsaved code. `code_hash` will always return the hash of the `code_cache` after this. pub fn commit_code(&mut self, db: &mut HashDB) { - trace!("Commiting code of {:?} - {:?}, {:?}", self, self.code_hash.is_none(), self.code_cache.is_empty()); - match (self.code_hash.is_none(), self.code_cache.is_empty()) { + trace!("Commiting code of {:?} - {:?}, {:?}", self, self.code_filth == Filth::Dirty, self.code_cache.is_empty()); + match (self.code_filth == Filth::Dirty, self.code_cache.is_empty()) { (true, true) => { - self.code_hash = Some(SHA3_EMPTY); self.code_size = Some(0); + self.code_filth = Filth::Clean; }, (true, false) => { - self.code_hash = Some(db.insert(&self.code_cache)); + db.emplace(self.code_hash.clone(), (*self.code_cache).clone()); self.code_size = Some(self.code_cache.len()); + self.code_filth = Filth::Clean; }, (false, _) => {}, } @@ -390,7 +391,7 @@ impl Account { stream.append(&self.nonce); stream.append(&self.balance); stream.append(&self.storage_root); - stream.append(self.code_hash.as_ref().unwrap_or(&SHA3_EMPTY)); + stream.append(&self.code_hash); stream.out() } @@ -404,8 +405,9 @@ impl Account { storage_changes: HashMap::new(), code_hash: self.code_hash.clone(), code_size: self.code_size.clone(), - code_cache: Bytes::new(), + code_cache: self.code_cache.clone(), filth: self.filth, + code_filth: self.code_filth, address_hash: self.address_hash.clone(), } } @@ -433,6 +435,7 @@ impl Account { self.nonce = other.nonce; self.storage_root = other.storage_root; self.code_hash = other.code_hash; + self.code_filth = other.code_filth; self.code_cache = other.code_cache; self.code_size = other.code_size; self.address_hash = other.address_hash; @@ -536,7 +539,7 @@ mod tests { let mut db = MemoryDB::new(); let mut db = AccountDBMut::new(&mut db, &Address::new()); a.init_code(vec![0x55, 0x44, 0xffu8]); - assert_eq!(a.code_hash(), SHA3_EMPTY); + assert_eq!(a.code_filth, Filth::Dirty); assert_eq!(a.code_size(), Some(3)); a.commit_code(&mut db); assert_eq!(a.code_hash().hex(), "af231e631776a517ca23125370d542873eca1fb4d613ed9b5d5335a46ae5b7eb"); @@ -548,11 +551,12 @@ mod tests { let mut db = MemoryDB::new(); let mut db = AccountDBMut::new(&mut db, &Address::new()); a.init_code(vec![0x55, 0x44, 0xffu8]); - assert_eq!(a.code_hash(), SHA3_EMPTY); + assert_eq!(a.code_filth, Filth::Dirty); a.commit_code(&mut db); + assert_eq!(a.code_filth, Filth::Clean); assert_eq!(a.code_hash().hex(), "af231e631776a517ca23125370d542873eca1fb4d613ed9b5d5335a46ae5b7eb"); a.reset_code(vec![0x55]); - assert_eq!(a.code_hash(), SHA3_EMPTY); + assert_eq!(a.code_filth, Filth::Dirty); a.commit_code(&mut db); assert_eq!(a.code_hash().hex(), "37bf2238b11b68cdc8382cece82651b59d3c3988873b6e0f33d79694aa45f1be"); } diff --git a/ethcore/src/state/mod.rs b/ethcore/src/state/mod.rs index 79d7cba54..0661420ed 100644 --- a/ethcore/src/state/mod.rs +++ b/ethcore/src/state/mod.rs @@ -319,9 +319,14 @@ impl State { } /// Get accounts' code. - pub fn code(&self, a: &Address) -> Option { + pub fn code(&self, a: &Address) -> Option> { self.ensure_cached(a, RequireCache::Code, - |a| a.as_ref().map_or(None, |a| a.code().map(|x|x.to_vec()))) + |a| a.as_ref().map_or(None, |a| a.code().clone())) + } + + pub fn code_hash(&self, a: &Address) -> H256 { + self.ensure_cached(a, RequireCache::None, + |a| a.as_ref().map_or(SHA3_EMPTY, |a| a.code_hash())) } /// Get accounts' code size. @@ -640,6 +645,7 @@ impl Clone for State { #[cfg(test)] mod tests { +use std::sync::Arc; use std::str::FromStr; use rustc_serialize::hex::FromHex; use super::*; @@ -1504,14 +1510,14 @@ fn code_from_database() { let mut state = get_temp_state_in(temp.as_path()); state.require_or_from(&a, false, ||Account::new_contract(42.into(), 0.into()), |_|{}); state.init_code(&a, vec![1, 2, 3]); - assert_eq!(state.code(&a), Some([1u8, 2, 3].to_vec())); + assert_eq!(state.code(&a), Some(Arc::new([1u8, 2, 3].to_vec()))); state.commit().unwrap(); - assert_eq!(state.code(&a), Some([1u8, 2, 3].to_vec())); + assert_eq!(state.code(&a), Some(Arc::new([1u8, 2, 3].to_vec()))); state.drop() }; let state = State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap(); - assert_eq!(state.code(&a), Some([1u8, 2, 3].to_vec())); + assert_eq!(state.code(&a), Some(Arc::new([1u8, 2, 3].to_vec()))); } #[test] diff --git a/ethcore/src/tests/client.rs b/ethcore/src/tests/client.rs index dc95e8267..59e3699ac 100644 --- a/ethcore/src/tests/client.rs +++ b/ethcore/src/tests/client.rs @@ -57,7 +57,7 @@ fn should_return_registrar() { IoChannel::disconnected(), &db_config ).unwrap(); - assert_eq!(client.additional_params().get("registrar"), Some(&"8e4e9b13d4b45cb0befc93c3061b1408f67316b2".to_owned())); + assert_eq!(client.additional_params().get("registrar"), Some(&"52dff57a8a1532e6afb3dc07e2af58bb9eb05b3d".to_owned())); } #[test] diff --git a/ethcore/src/types/trace_types/trace.rs b/ethcore/src/types/trace_types/trace.rs index 9efeaa001..2571805a6 100644 --- a/ethcore/src/types/trace_types/trace.rs +++ b/ethcore/src/types/trace_types/trace.rs @@ -181,7 +181,7 @@ impl From for Create { from: p.sender, value: p.value.value(), gas: p.gas, - init: p.code.unwrap_or_else(Vec::new), + init: p.code.map_or_else(Vec::new, |c| (*c).clone()), } } } diff --git a/ethcore/src/verification/queue/kind.rs b/ethcore/src/verification/queue/kind.rs index 7585f1e6d..b6b6c5cf6 100644 --- a/ethcore/src/verification/queue/kind.rs +++ b/ethcore/src/verification/queue/kind.rs @@ -164,6 +164,7 @@ pub mod headers { } /// A mode for verifying headers. + #[allow(dead_code)] pub struct Headers; impl Kind for Headers { @@ -179,4 +180,4 @@ pub mod headers { engine.verify_block_unordered(&unverified, None).map(|_| unverified) } } -} \ No newline at end of file +} diff --git a/rpc/src/v1/tests/helpers/miner_service.rs b/rpc/src/v1/tests/helpers/miner_service.rs index 0f36b4f54..ddc0b057b 100644 --- a/rpc/src/v1/tests/helpers/miner_service.rs +++ b/rpc/src/v1/tests/helpers/miner_service.rs @@ -249,7 +249,7 @@ impl MinerService for TestMinerService { } fn code(&self, _chain: &MiningBlockChainClient, address: &Address) -> Option { - self.latest_closed_block.lock().as_ref().map_or(None, |b| b.block().fields().state.code(address).clone()) + self.latest_closed_block.lock().as_ref().map_or(None, |b| b.block().fields().state.code(address).map(|c| (*c).clone())) } } diff --git a/util/src/misc.rs b/util/src/misc.rs index 50b2e7e8d..b0452e85e 100644 --- a/util/src/misc.rs +++ b/util/src/misc.rs @@ -23,7 +23,7 @@ use target_info::Target; include!(concat!(env!("OUT_DIR"), "/version.rs")); include!(concat!(env!("OUT_DIR"), "/rustc_version.rs")); -#[derive(PartialEq,Eq,Clone,Copy)] +#[derive(PartialEq, Eq, Clone, Copy, Debug)] /// Boolean type for clean/dirty status. pub enum Filth { /// Data has not been changed. diff --git a/util/src/standard.rs b/util/src/standard.rs index 3d6c93e1a..0693dcd23 100644 --- a/util/src/standard.rs +++ b/util/src/standard.rs @@ -46,4 +46,4 @@ pub use rustc_serialize::hex::{FromHex, FromHexError}; pub use heapsize::HeapSizeOf; pub use itertools::Itertools; -pub use parking_lot::{Condvar, Mutex, MutexGuard, RwLock, RwLockReadGuard, RwLockWriteGuard}; \ No newline at end of file +pub use parking_lot::{Condvar, Mutex, MutexGuard, RwLock, RwLockReadGuard, RwLockWriteGuard};