Merge remote-tracking branch 'parity/master' into bft

Conflicts:
	ethcore/src/client/client.rs
This commit is contained in:
keorn
2016-10-05 14:57:14 +01:00
89 changed files with 1867 additions and 763 deletions

View File

@@ -121,6 +121,10 @@ impl<'db> HashDB for AccountDB<'db>{
fn remove(&mut self, _key: &H256) {
unimplemented!()
}
fn get_aux(&self, hash: &[u8]) -> Option<Vec<u8>> {
self.db.get_aux(hash)
}
}
/// DB backend wrapper for Account trie
@@ -193,6 +197,18 @@ impl<'db> HashDB for AccountDBMut<'db>{
let key = combine_key(&self.address_hash, key);
self.db.remove(&key)
}
fn insert_aux(&mut self, hash: Vec<u8>, value: Vec<u8>) {
self.db.insert_aux(hash, value);
}
fn get_aux(&self, hash: &[u8]) -> Option<Vec<u8>> {
self.db.get_aux(hash)
}
fn remove_aux(&mut self, hash: &[u8]) {
self.db.remove_aux(hash);
}
}
struct Wrapping<'db>(&'db HashDB);

View File

@@ -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<Bytes>,
pub code: Option<Arc<Bytes>>,
/// Input data.
pub data: Option<Bytes>,
/// 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<ethjson::vm::Transaction> 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(),

View File

@@ -16,14 +16,26 @@
//! Blockchain block.
use common::*;
use std::sync::Arc;
use std::collections::HashSet;
use rlp::{UntrustedRlp, RlpStream, Encodable, Decodable, Decoder, DecoderError, View, Stream};
use util::{Bytes, Address, Uint, FixedHash, Hashable, U256, H256, ordered_trie_root, SHA3_NULL_RLP};
use util::error::{Mismatch, OutOfBounds};
use basic_types::{LogBloom, Seal};
use env_info::{EnvInfo, LastHashes};
use engines::Engine;
use state::*;
use state_db::StateDB;
use verification::PreverifiedBlock;
use trace::FlatTrace;
use error::{Error, BlockError, TransactionError};
use factory::Factories;
use rlp::*;
use header::Header;
use receipt::Receipt;
use state::State;
use state_db::StateDB;
use trace::FlatTrace;
use transaction::SignedTransaction;
use verification::PreverifiedBlock;
use views::BlockView;
/// A block, encoded as it is on the block chain.
#[derive(Default, Debug, Clone, PartialEq)]
@@ -518,25 +530,38 @@ pub fn enact(
b.set_uncles_hash(header.uncles_hash().clone());
b.set_transactions_root(header.transactions_root().clone());
b.set_receipts_root(header.receipts_root().clone());
for t in transactions { try!(b.push_transaction(t.clone(), None)); }
for u in uncles { try!(b.push_uncle(u.clone())); }
try!(push_transactions(&mut b, transactions));
for u in uncles {
try!(b.push_uncle(u.clone()));
}
Ok(b.close_and_lock())
}
/// Enact the block given by `block_bytes` using `engine` on the database `db` with given `parent` block header
#[cfg_attr(feature="dev", allow(too_many_arguments))]
pub fn enact_bytes(
block_bytes: &[u8],
engine: &Engine,
tracing: bool,
db: StateDB,
parent: &Header,
last_hashes: Arc<LastHashes>,
factories: Factories,
) -> Result<LockedBlock, Error> {
let block = BlockView::new(block_bytes);
let header = block.header();
enact(&header, &block.transactions(), &block.uncles(), engine, tracing, db, parent, last_hashes, factories)
#[inline(always)]
#[cfg(not(feature = "slow-blocks"))]
fn push_transactions(block: &mut OpenBlock, transactions: &[SignedTransaction]) -> Result<(), Error> {
for t in transactions {
try!(block.push_transaction(t.clone(), None));
}
Ok(())
}
#[cfg(feature = "slow-blocks")]
fn push_transactions(block: &mut OpenBlock, transactions: &[SignedTransaction]) -> Result<(), Error> {
use std::time;
let slow_tx = option_env!("SLOW_TX_DURATION").and_then(|v| v.parse().ok()).unwrap_or(100);
for t in transactions {
let hash = t.hash();
let start = time::Instant::now();
try!(block.push_transaction(t.clone(), None));
let took = start.elapsed();
if took > time::Duration::from_millis(slow_tx) {
warn!("Heavy transaction in block {:?}: {:?}", block.header().number(), hash);
}
}
Ok(())
}
/// Enact the block given by `block_bytes` using `engine` on the database `db` with given `parent` block header
@@ -554,26 +579,45 @@ pub fn enact_verified(
enact(&block.header, &block.transactions, &view.uncles(), engine, tracing, db, parent, last_hashes, factories)
}
/// Enact the block given by `block_bytes` using `engine` on the database `db` with given `parent` block header. Seal the block aferwards
#[cfg_attr(feature="dev", allow(too_many_arguments))]
pub fn enact_and_seal(
block_bytes: &[u8],
engine: &Engine,
tracing: bool,
db: StateDB,
parent: &Header,
last_hashes: Arc<LastHashes>,
factories: Factories,
) -> Result<SealedBlock, Error> {
let header = BlockView::new(block_bytes).header_view();
Ok(try!(try!(enact_bytes(block_bytes, engine, tracing, db, parent, last_hashes, factories)).seal(engine, header.seal())))
}
#[cfg(test)]
mod tests {
use tests::helpers::*;
use super::*;
use common::*;
use engines::Engine;
use factory::Factories;
use state_db::StateDB;
/// Enact the block given by `block_bytes` using `engine` on the database `db` with given `parent` block header
#[cfg_attr(feature="dev", allow(too_many_arguments))]
fn enact_bytes(
block_bytes: &[u8],
engine: &Engine,
tracing: bool,
db: StateDB,
parent: &Header,
last_hashes: Arc<LastHashes>,
factories: Factories,
) -> Result<LockedBlock, Error> {
let block = BlockView::new(block_bytes);
let header = block.header();
enact(&header, &block.transactions(), &block.uncles(), engine, tracing, db, parent, last_hashes, factories)
}
/// Enact the block given by `block_bytes` using `engine` on the database `db` with given `parent` block header. Seal the block aferwards
#[cfg_attr(feature="dev", allow(too_many_arguments))]
fn enact_and_seal(
block_bytes: &[u8],
engine: &Engine,
tracing: bool,
db: StateDB,
parent: &Header,
last_hashes: Arc<LastHashes>,
factories: Factories,
) -> Result<SealedBlock, Error> {
let header = BlockView::new(block_bytes).header_view();
Ok(try!(try!(enact_bytes(block_bytes, engine, tracing, db, parent, last_hashes, factories)).seal(engine, header.seal())))
}
#[test]
fn open_block() {

View File

@@ -18,7 +18,7 @@ use ipc::IpcConfig;
use util::H256;
/// Represents what has to be handled by actor listening to chain events
#[derive(Ipc)]
#[ipc]
pub trait ChainNotify : Send + Sync {
/// fires when chain has new blocks.
fn new_blocks(&self,

View File

@@ -23,10 +23,10 @@ use time::precise_time_ns;
// util
use util::{Bytes, PerfTimer, Itertools, Mutex, RwLock};
use util::journaldb;
use util::{U256, H256, H520, Address, H2048, Uint};
use util::{journaldb, TrieFactory, Trie};
use util::{U256, H256, H520, Address, H2048, Uint, FixedHash};
use util::sha3::*;
use util::TrieFactory;
use util::trie::TrieSpec;
use util::kvdb::*;
// other
@@ -53,7 +53,7 @@ use blockchain::{BlockChain, BlockProvider, TreeRoute, ImportRoute};
use client::{
BlockID, TransactionID, UncleID, TraceId, ClientConfig, BlockChainClient,
MiningBlockChainClient, TraceFilter, CallAnalytics, BlockImportError, Mode,
ChainNotify
ChainNotify,
};
use client::Error as ClientError;
use env_info::EnvInfo;
@@ -173,6 +173,11 @@ impl Client {
let chain = Arc::new(BlockChain::new(config.blockchain.clone(), &gb, db.clone(), spec.engine.clone()));
let tracedb = RwLock::new(TraceDB::new(config.tracing.clone(), db.clone(), chain.clone()));
let trie_spec = match config.fat_db {
true => TrieSpec::Fat,
false => TrieSpec::Secure,
};
let journal_db = journaldb::new(db.clone(), config.pruning, ::db::COL_STATE);
let mut state_db = StateDB::new(journal_db);
if state_db.journal_db().is_empty() && try!(spec.ensure_db_good(&mut state_db)) {
@@ -195,7 +200,7 @@ impl Client {
let factories = Factories {
vm: EvmFactory::new(config.vm_type.clone()),
trie: TrieFactory::new(config.trie_spec.clone()),
trie: TrieFactory::new(trie_spec),
accountdb: Default::default(),
};
@@ -833,7 +838,7 @@ impl BlockChainClient for Client {
}
fn code(&self, address: &Address, id: BlockID) -> Option<Option<Bytes>> {
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<U256> {
@@ -844,6 +849,38 @@ impl BlockChainClient for Client {
self.state_at(id).map(|s| s.storage_at(address, position))
}
fn list_accounts(&self, id: BlockID) -> Option<Vec<Address>> {
if !self.factories.trie.is_fat() {
trace!(target: "fatdb", "list_accounts: Not a fat DB");
return None;
}
let state = match self.state_at(id) {
Some(state) => state,
_ => return None,
};
let (root, db) = state.drop();
let trie = match self.factories.trie.readonly(db.as_hashdb(), &root) {
Ok(trie) => trie,
_ => {
trace!(target: "fatdb", "list_accounts: Couldn't open the DB");
return None;
}
};
let iter = match trie.iter() {
Ok(iter) => iter,
_ => return None,
};
let accounts = iter.filter_map(|item| {
item.ok().map(|(addr, _)| Address::from_slice(&addr))
}).collect();
Some(accounts)
}
fn transaction(&self, id: TransactionID) -> Option<LocalizedTransaction> {
self.transaction_address(id).and_then(|address| self.chain.read().transaction(&address))
}

View File

@@ -22,7 +22,6 @@ pub use evm::VMType;
use verification::{VerifierType, QueueConfig};
use util::{journaldb, CompactionProfile};
use util::trie::TrieSpec;
/// Client state db compaction profile
#[derive(Debug, PartialEq)]
@@ -91,8 +90,8 @@ pub struct ClientConfig {
pub tracing: TraceConfig,
/// VM type.
pub vm_type: VMType,
/// Trie type.
pub trie_spec: TrieSpec,
/// Fat DB enabled?
pub fat_db: bool,
/// The JournalDB ("pruning") algorithm to use.
pub pruning: journaldb::Algorithm,
/// The name of the client instance.

View File

@@ -25,8 +25,9 @@ use transaction::{Transaction, LocalizedTransaction, SignedTransaction, Action};
use blockchain::TreeRoute;
use client::{
BlockChainClient, MiningBlockChainClient, BlockChainInfo, BlockStatus, BlockID,
TransactionID, UncleID, TraceId, TraceFilter, LastHashes, CallAnalytics, BlockImportError
TransactionID, UncleID, TraceId, TraceFilter, LastHashes, CallAnalytics, BlockImportError,
};
use db::{NUM_COLUMNS, COL_STATE};
use header::{Header as BlockHeader, BlockNumber};
use filter::Filter;
use log_entry::LocalizedLogEntry;
@@ -286,8 +287,8 @@ impl TestBlockChainClient {
pub fn get_temp_state_db() -> GuardedTempResult<StateDB> {
let temp = RandomTempPath::new();
let db = Database::open_default(temp.as_str()).unwrap();
let journal_db = journaldb::new(Arc::new(db), journaldb::Algorithm::EarlyMerge, None);
let db = Database::open(&DatabaseConfig::with_columns(NUM_COLUMNS), temp.as_str()).unwrap();
let journal_db = journaldb::new(Arc::new(db), journaldb::Algorithm::EarlyMerge, COL_STATE);
let state_db = StateDB::new(journal_db);
GuardedTempResult {
_temp: temp,
@@ -384,6 +385,10 @@ impl BlockChainClient for TestBlockChainClient {
}
}
fn list_accounts(&self, _id: BlockID) -> Option<Vec<Address>> {
None
}
fn transaction(&self, _id: TransactionID) -> Option<LocalizedTransaction> {
None // Simple default.
}

View File

@@ -38,7 +38,6 @@ use ipc::IpcConfig;
use types::blockchain_info::BlockChainInfo;
use types::block_status::BlockStatus;
#[derive(Ipc)]
#[ipc(client_ident="RemoteClient")]
/// Blockchain database client. Owns and manages a blockchain and a block queue.
pub trait BlockChainClient : Sync + Send {
@@ -112,6 +111,9 @@ pub trait BlockChainClient : Sync + Send {
Therefore storage_at has returned Some; qed")
}
/// Get a list of all accounts in the block `id`, if fat DB is in operation, otherwise `None`.
fn list_accounts(&self, id: BlockID) -> Option<Vec<Address>>;
/// Get transaction with given hash.
fn transaction(&self, id: TransactionID) -> Option<LocalizedTransaction>;

View File

@@ -34,8 +34,10 @@ pub const COL_BODIES: Option<u32> = Some(2);
pub const COL_EXTRA: Option<u32> = Some(3);
/// Column for Traces
pub const COL_TRACE: Option<u32> = Some(4);
/// Column for Traces
pub const COL_ACCOUNT_BLOOM: Option<u32> = Some(5);
/// Number of columns in DB
pub const NUM_COLUMNS: Option<u32> = Some(5);
pub const NUM_COLUMNS: Option<u32> = Some(6);
/// Modes for updating caches.
#[derive(Clone, Copy)]

View File

@@ -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<Bytes>;
/// Returns code size at given address
fn extcodesize(&self, address: &Address) -> usize;

View File

@@ -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<SharedCache>,
}
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::<usize>::default())
Box::new(super::interpreter::Interpreter::<usize>::new(self.evm_cache.clone()))
} else {
Box::new(super::interpreter::Interpreter::<U256>::default())
Box::new(super::interpreter::Interpreter::<U256>::new(self.evm_cache.clone()))
}
}
}
@@ -108,9 +111,9 @@ impl Factory {
pub fn create(&self, gas: U256) -> Box<Evm> {
match self.evm {
VMType::Interpreter => if Self::can_fit_in_usize(gas) {
Box::new(super::interpreter::Interpreter::<usize>::default())
Box::new(super::interpreter::Interpreter::<usize>::new(self.evm_cache.clone()))
} else {
Box::new(super::interpreter::Interpreter::<U256>::default())
Box::new(super::interpreter::Interpreter::<U256>::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()),
}
}
}

View File

@@ -0,0 +1,164 @@
// 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 <http://www.gnu.org/licenses/>.
pub use self::inner::*;
#[macro_use]
#[cfg(not(feature = "evm-debug"))]
mod inner {
macro_rules! evm_debug {
($x: expr) => {}
}
pub struct EvmInformant;
impl EvmInformant {
pub fn new(_depth: usize) -> Self {
EvmInformant {}
}
pub fn done(&mut self) {}
}
}
#[macro_use]
#[cfg(feature = "evm-debug")]
mod inner {
use std::iter;
use std::collections::HashMap;
use std::time::{Instant, Duration};
use evm::interpreter::stack::Stack;
use evm::instructions::{Instruction, InstructionInfo, INSTRUCTIONS};
use evm::{CostType};
use util::U256;
macro_rules! evm_debug {
($x: expr) => {
$x
}
}
fn print(data: String) {
if cfg!(feature = "evm-debug-tests") {
println!("{}", data);
} else {
debug!(target: "evm", "{}", data);
}
}
pub struct EvmInformant {
spacing: String,
last_instruction: Instant,
stats: HashMap<Instruction, Stats>,
}
impl EvmInformant {
fn color(instruction: Instruction, name: &str) -> String {
let c = instruction as usize % 6;
let colors = [31, 34, 33, 32, 35, 36];
format!("\x1B[1;{}m{}\x1B[0m", colors[c], name)
}
fn as_micro(duration: &Duration) -> u64 {
let mut sec = duration.as_secs();
let subsec = duration.subsec_nanos() as u64;
sec = sec.saturating_mul(1_000_000u64);
sec += subsec / 1_000;
sec
}
pub fn new(depth: usize) -> Self {
EvmInformant {
spacing: iter::repeat(".").take(depth).collect(),
last_instruction: Instant::now(),
stats: HashMap::new(),
}
}
pub fn before_instruction<Cost: CostType>(&mut self, pc: usize, instruction: Instruction, info: &InstructionInfo, current_gas: &Cost, stack: &Stack<U256>) {
let time = self.last_instruction.elapsed();
self.last_instruction = Instant::now();
print(format!("{}[0x{:<3x}][{:>19}(0x{:<2x}) Gas Left: {:6?} (Previous took: {:10}μs)",
&self.spacing,
pc,
Self::color(instruction, info.name),
instruction,
current_gas,
Self::as_micro(&time),
));
if info.args > 0 {
for (idx, item) in stack.peek_top(info.args).iter().enumerate() {
print(format!("{} |{:2}: {:?}", self.spacing, idx, item));
}
}
}
pub fn after_instruction(&mut self, instruction: Instruction) {
let mut stats = self.stats.entry(instruction).or_insert_with(|| Stats::default());
let took = self.last_instruction.elapsed();
stats.note(took);
}
pub fn done(&mut self) {
// Print out stats
let infos = &*INSTRUCTIONS;
let mut stats: Vec<(_,_)> = self.stats.drain().collect();
stats.sort_by(|ref a, ref b| b.1.avg().cmp(&a.1.avg()));
print(format!("\n{}-------OPCODE STATS:", self.spacing));
for (instruction, stats) in stats.into_iter() {
let info = infos[instruction as usize];
print(format!("{}-------{:>19}(0x{:<2x}) count: {:4}, avg: {:10}μs",
self.spacing,
Self::color(instruction, info.name),
instruction,
stats.count,
stats.avg(),
));
}
}
}
struct Stats {
count: u64,
total_duration: Duration,
}
impl Default for Stats {
fn default() -> Self {
Stats {
count: 0,
total_duration: Duration::from_secs(0),
}
}
}
impl Stats {
fn note(&mut self, took: Duration) {
self.count += 1;
self.total_duration += took;
}
fn avg(&self) -> u64 {
EvmInformant::as_micro(&self.total_duration) / self.count
}
}
}

View File

@@ -16,25 +16,17 @@
//! Rust VM implementation
#[cfg(not(feature = "evm-debug"))]
macro_rules! evm_debug {
($x: expr) => {}
}
#[cfg(feature = "evm-debug")]
macro_rules! evm_debug {
($x: expr) => {
$x
}
}
#[macro_use]
mod informant;
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::*;
@@ -43,13 +35,6 @@ use super::instructions::{self, Instruction, InstructionInfo};
use evm::{self, MessageCallResult, ContractCreateResult, GasLeft, CostType};
use bit_set::BitSet;
#[cfg(feature = "evm-debug")]
fn color(instruction: Instruction, name: &'static str) -> String {
let c = instruction as usize % 6;
let colors = [31, 34, 33, 32, 35, 36];
format!("\x1B[1;{}m{}\x1B[0m", colors[c], name)
}
type CodePosition = usize;
type ProgramCounter = usize;
@@ -72,6 +57,15 @@ struct CodeReader<'a> {
#[cfg_attr(feature="dev", allow(len_without_is_empty))]
impl<'a> CodeReader<'a> {
/// Create new code reader - starting at position 0.
fn new(code: &'a Bytes) -> Self {
CodeReader {
position: 0,
code: code,
}
}
/// Get `no_of_bytes` from code and convert to U256. Move PC
fn read(&mut self, no_of_bytes: usize) -> U256 {
let pos = self.position;
@@ -98,9 +92,9 @@ enum InstructionResult<Gas> {
/// Intepreter EVM implementation
#[derive(Default)]
pub struct Interpreter<Cost: CostType> {
mem: Vec<u8>,
cache: Arc<SharedCache>,
_type: PhantomData<Cost>,
}
@@ -108,15 +102,14 @@ impl<Cost: CostType> evm::Evm for Interpreter<Cost> {
fn exec(&mut self, params: ActionParams, ext: &mut evm::Ext) -> evm::Result<GasLeft> {
self.mem.clear();
let mut informant = informant::EvmInformant::new(ext.depth());
let code = &params.code.as_ref().unwrap();
let valid_jump_destinations = self.find_jump_destinations(code);
let valid_jump_destinations = self.cache.jump_destinations(&params.code_hash, code);
let mut gasometer = Gasometer::<Cost>::new(try!(Cost::from_u256(params.gas)));
let mut stack = VecStack::with_capacity(ext.schedule().stack_limit, U256::zero());
let mut reader = CodeReader {
position: 0,
code: code
};
let mut reader = CodeReader::new(code);
let infos = &*instructions::INSTRUCTIONS;
while reader.position < code.len() {
@@ -136,15 +129,7 @@ impl<Cost: CostType> evm::Evm for Interpreter<Cost> {
gasometer.current_mem_gas = mem_gas;
gasometer.current_gas = gasometer.current_gas - gas_cost;
evm_debug!({
println!("[0x{:x}][{}(0x{:x}) Gas: {:?}\n Gas Before: {:?}",
reader.position,
color(instruction, info.name),
instruction,
gas_cost,
gasometer.current_gas + gas_cost
);
});
evm_debug!({ informant.before_instruction(reader.position, instruction, &info, &gasometer.current_gas, &stack) });
let (mem_written, store_written) = match trace_executed {
true => (Self::mem_written(instruction, &stack), Self::store_written(instruction, &stack)),
@@ -156,6 +141,8 @@ impl<Cost: CostType> evm::Evm for Interpreter<Cost> {
gasometer.current_gas, &params, ext, instruction, &mut reader, &mut stack
));
evm_debug!({ informant.after_instruction(instruction) });
if trace_executed {
ext.trace_executed(gasometer.current_gas.as_u256(), stack.peek_top(info.ret), mem_written.map(|(o, s)| (o, &(self.mem[o..(o + s)]))), store_written);
}
@@ -177,17 +164,26 @@ impl<Cost: CostType> evm::Evm for Interpreter<Cost> {
reader.position = pos;
},
InstructionResult::StopExecutionNeedsReturn(gas, off, size) => {
informant.done();
return Ok(GasLeft::NeedsReturn(gas.as_u256(), self.mem.read_slice(off, size)));
},
InstructionResult::StopExecution => break,
}
}
informant.done();
Ok(GasLeft::Known(gasometer.current_gas.as_u256()))
}
}
impl<Cost: CostType> Interpreter<Cost> {
/// Create a new `Interpreter` instance with shared cache.
pub fn new(cache: Arc<SharedCache>) -> Interpreter<Cost> {
Interpreter {
mem: Vec::new(),
cache: cache,
_type: PhantomData::default(),
}
}
fn verify_instruction(&self, ext: &evm::Ext, instruction: Instruction, info: &InstructionInfo, stack: &Stack<U256>) -> evm::Result<()> {
let schedule = ext.schedule();
@@ -486,10 +482,10 @@ impl<Cost: CostType> Interpreter<Cost> {
stack.push(U256::from(len));
},
instructions::CALLDATACOPY => {
self.copy_data_to_memory(stack, &params.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, &params.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 +786,6 @@ impl<Cost: CostType> Interpreter<Cost> {
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 +812,3 @@ fn address_to_u256(value: Address) -> U256 {
U256::from(&*H256::from(value))
}
#[test]
fn test_find_jump_destinations() {
// given
let interpreter = Interpreter::<U256>::default();
let code = "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff5b01600055".from_hex().unwrap();
// when
let valid_jump_destinations = interpreter.find_jump_destinations(&code);
// then
assert!(valid_jump_destinations.contains(66));
}

View File

@@ -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 <http://www.gnu.org/licenses/>.
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<LruCache<H256, Arc<BitSet>>>
}
impl SharedCache {
/// Get jump destincations bitmap for a contract.
pub fn jump_destinations(&self, code_hash: &H256, code: &[u8]) -> Arc<BitSet> {
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<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;
}
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));
}

View File

@@ -34,7 +34,7 @@ pub trait Stack<T> {
/// Get number of elements on Stack
fn size(&self) -> usize;
/// Returns all data on stack.
fn peek_top(&mut self, no_of_elems: usize) -> &[T];
fn peek_top(&self, no_of_elems: usize) -> &[T];
}
pub struct VecStack<S> {
@@ -68,12 +68,7 @@ impl<S : fmt::Display> Stack<S> for VecStack<S> {
fn pop_back(&mut self) -> S {
let val = self.stack.pop();
match val {
Some(x) => {
evm_debug!({
println!(" POP: {}", x)
});
x
},
Some(x) => x,
None => panic!("Tried to pop from empty stack.")
}
}
@@ -88,9 +83,6 @@ impl<S : fmt::Display> Stack<S> for VecStack<S> {
}
fn push(&mut self, elem: S) {
evm_debug!({
println!(" PUSH: {}", elem)
});
self.stack.push(elem);
}
@@ -98,7 +90,7 @@ impl<S : fmt::Display> Stack<S> for VecStack<S> {
self.stack.len()
}
fn peek_top(&mut self, no_from_top: usize) -> &[S] {
fn peek_top(&self, no_from_top: usize) -> &[S] {
assert!(self.stack.len() >= no_from_top, "peek_top asked for more items than exist.");
&self.stack[self.stack.len() - no_from_top .. self.stack.len()]
}

View File

@@ -49,7 +49,7 @@ pub struct FakeExt {
depth: usize,
store: HashMap<H256, H256>,
blockhashes: HashMap<U256, H256>,
codes: HashMap<Address, Bytes>,
codes: HashMap<Address, Arc<Bytes>>,
logs: Vec<FakeLogEntry>,
_suicides: HashSet<Address>,
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<Bytes> {
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<evm::Evm> = Box::new(super::interpreter::Interpreter::<usize>::default());
let mut vm : Box<evm::Evm> = Box::new(super::interpreter::Interpreter::<usize>::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 = {

View File

@@ -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();

View File

@@ -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<Bytes> {
self.state.code(address).unwrap_or_else(|| Arc::new(vec![]))
}
fn extcodesize(&self, address: &Address) -> usize {

View File

@@ -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<Bytes> {
self.ext.extcode(address)
}
@@ -232,7 +232,7 @@ fn do_json_test_for(vm_type: &VMType, json_data: &[u8]) -> Vec<String> {
for (address, account) in vm.post_state.unwrap().into_iter() {
let address = address.into();
let code: Vec<u8> = 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)| {

View File

@@ -99,6 +99,8 @@ extern crate ethcore_devtools as devtools;
extern crate rand;
extern crate bit_set;
extern crate rlp;
extern crate ethcore_bloom_journal as bloom_journal;
extern crate byteorder;
#[macro_use]
extern crate log;

View File

@@ -23,3 +23,6 @@ pub mod extras;
mod v9;
pub use self::v9::ToV9;
pub use self::v9::Extract;
mod v10;
pub use self::v10::ToV10;

View File

@@ -24,6 +24,7 @@ use util::{Address, FixedHash, H256};
use util::kvdb::Database;
use util::migration::{Batch, Config, Error, Migration, SimpleMigration, Progress};
use util::sha3::Hashable;
use std::sync::Arc;
use rlp::{decode, Rlp, RlpStream, Stream, View};
@@ -107,7 +108,7 @@ pub struct OverlayRecentV7 {
impl OverlayRecentV7 {
// walk all journal entries in the database backwards.
// find migrations for any possible inserted keys.
fn walk_journal(&mut self, source: &Database) -> Result<(), Error> {
fn walk_journal(&mut self, source: Arc<Database>) -> Result<(), Error> {
if let Some(val) = try!(source.get(None, V7_LATEST_ERA_KEY).map_err(Error::Custom)) {
let mut era = decode::<u64>(&val);
loop {
@@ -151,7 +152,7 @@ impl OverlayRecentV7 {
// walk all journal entries in the database backwards.
// replace all possible inserted/deleted keys with their migrated counterparts
// and commit the altered entries.
fn migrate_journal(&self, source: &Database, mut batch: Batch, dest: &mut Database) -> Result<(), Error> {
fn migrate_journal(&self, source: Arc<Database>, mut batch: Batch, dest: &mut Database) -> Result<(), Error> {
if let Some(val) = try!(source.get(None, V7_LATEST_ERA_KEY).map_err(Error::Custom)) {
try!(batch.insert(V7_LATEST_ERA_KEY.into(), val.to_owned(), dest));
@@ -228,7 +229,7 @@ impl Migration for OverlayRecentV7 {
// walk all records in the database, attempting to migrate any possible and
// keeping records of those that we do. then migrate the journal using
// this information.
fn migrate(&mut self, source: &Database, config: &Config, dest: &mut Database, col: Option<u32>) -> Result<(), Error> {
fn migrate(&mut self, source: Arc<Database>, config: &Config, dest: &mut Database, col: Option<u32>) -> Result<(), Error> {
let mut batch = Batch::new(config, col);
// check version metadata.
@@ -257,7 +258,7 @@ impl Migration for OverlayRecentV7 {
try!(batch.insert(key, value.into_vec(), dest));
}
try!(self.walk_journal(source));
try!(self.walk_journal(source.clone()));
self.migrate_journal(source, batch, dest)
}
}

View File

@@ -0,0 +1,117 @@
// 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 <http://www.gnu.org/licenses/>.
//! Bloom upgrade
use std::sync::Arc;
use db::{COL_EXTRA, COL_HEADERS, COL_STATE};
use state_db::{ACCOUNT_BLOOM_SPACE, DEFAULT_ACCOUNT_PRESET, StateDB};
use util::trie::TrieDB;
use views::HeaderView;
use bloom_journal::Bloom;
use util::migration::{Error, Migration, Progress, Batch, Config};
use util::journaldb;
use util::{H256, FixedHash, Trie};
use util::{Database, DBTransaction};
/// Account bloom upgrade routine. If bloom already present, does nothing.
/// If database empty (no best block), does nothing.
/// Can be called on upgraded database with no issues (will do nothing).
pub fn generate_bloom(source: Arc<Database>, dest: &mut Database) -> Result<(), Error> {
trace!(target: "migration", "Account bloom upgrade started");
let best_block_hash = match try!(source.get(COL_EXTRA, b"best")) {
// no migration needed
None => {
trace!(target: "migration", "No best block hash, skipping");
return Ok(());
},
Some(hash) => hash,
};
let best_block_header = match try!(source.get(COL_HEADERS, &best_block_hash)) {
// no best block, nothing to do
None => {
trace!(target: "migration", "No best block header, skipping");
return Ok(())
},
Some(x) => x,
};
let state_root = HeaderView::new(&best_block_header).state_root();
trace!("Adding accounts bloom (one-time upgrade)");
let bloom_journal = {
let mut bloom = Bloom::new(ACCOUNT_BLOOM_SPACE, DEFAULT_ACCOUNT_PRESET);
// no difference what algorithm is passed, since there will be no writes
let state_db = journaldb::new(
source.clone(),
journaldb::Algorithm::OverlayRecent,
COL_STATE);
let account_trie = try!(TrieDB::new(state_db.as_hashdb(), &state_root).map_err(|e| Error::Custom(format!("Cannot open trie: {:?}", e))));
for item in try!(account_trie.iter().map_err(|_| Error::MigrationImpossible)) {
let (ref account_key, _) = try!(item.map_err(|_| Error::MigrationImpossible));
let account_key_hash = H256::from_slice(&account_key);
bloom.set(&*account_key_hash);
}
bloom.drain_journal()
};
trace!(target: "migration", "Generated {} bloom updates", bloom_journal.entries.len());
let mut batch = DBTransaction::new(dest);
try!(StateDB::commit_bloom(&mut batch, bloom_journal).map_err(|_| Error::Custom("Failed to commit bloom".to_owned())));
try!(dest.write(batch));
trace!(target: "migration", "Finished bloom update");
Ok(())
}
/// Account bloom migration.
#[derive(Default)]
pub struct ToV10 {
progress: Progress,
}
impl ToV10 {
/// New v10 migration
pub fn new() -> ToV10 { ToV10 { progress: Progress::default() } }
}
impl Migration for ToV10 {
fn version(&self) -> u32 {
10
}
fn pre_columns(&self) -> Option<u32> { Some(5) }
fn columns(&self) -> Option<u32> { Some(6) }
fn migrate(&mut self, source: Arc<Database>, config: &Config, dest: &mut Database, col: Option<u32>) -> Result<(), Error> {
let mut batch = Batch::new(config, col);
for (key, value) in source.iter(col) {
self.progress.tick();
try!(batch.insert(key.to_vec(), value.to_vec(), dest));
}
try!(batch.commit(dest));
if col == COL_STATE {
try!(generate_bloom(source, dest));
}
Ok(())
}
}

View File

@@ -20,6 +20,7 @@
use rlp::{Rlp, RlpStream, View, Stream};
use util::kvdb::Database;
use util::migration::{Batch, Config, Error, Migration, Progress};
use std::sync::Arc;
/// Which part of block to preserve
pub enum Extract {
@@ -55,7 +56,7 @@ impl Migration for ToV9 {
fn version(&self) -> u32 { 9 }
fn migrate(&mut self, source: &Database, config: &Config, dest: &mut Database, col: Option<u32>) -> Result<(), Error> {
fn migrate(&mut self, source: Arc<Database>, config: &Config, dest: &mut Database, col: Option<u32>) -> Result<(), Error> {
let mut batch = Batch::new(config, self.column);
for (key, value) in source.iter(col) {

View File

@@ -587,7 +587,7 @@ impl MinerService for Miner {
fn code(&self, chain: &MiningBlockChainClient, address: &Address) -> Option<Bytes> {
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) {

View File

@@ -43,6 +43,8 @@ use self::account::Account;
use self::block::AbridgedBlock;
use self::io::SnapshotWriter;
use super::state_db::StateDB;
use crossbeam::{scope, ScopedJoinHandle};
use rand::{Rng, OsRng};
@@ -454,6 +456,10 @@ impl StateRebuilder {
self.code_map.insert(code_hash, code);
}
let backing = self.db.backing().clone();
// bloom has to be updated
let mut bloom = StateDB::load_bloom(&backing);
// batch trie writes
{
@@ -464,12 +470,14 @@ impl StateRebuilder {
};
for (hash, thin_rlp) in pairs {
bloom.set(&*hash);
try!(account_trie.insert(&hash, &thin_rlp));
}
}
let backing = self.db.backing().clone();
let bloom_journal = bloom.drain_journal();
let mut batch = backing.transaction();
try!(StateDB::commit_bloom(&mut batch, bloom_journal));
try!(self.db.inject(&mut batch));
try!(backing.write(batch).map_err(::util::UtilError::SimpleString));
trace!(target: "snapshot", "current state root: {:?}", self.state_root);

View File

@@ -22,7 +22,6 @@ use ipc::IpcConfig;
/// This handles:
/// - restoration of snapshots to temporary databases.
/// - responding to queries for snapshot manifests and chunks
#[derive(Ipc)]
#[ipc(client_ident="RemoteSnapshotService")]
pub trait SnapshotService : Sync + Send {
/// Query the most recent manifest data.

View File

@@ -248,6 +248,7 @@ impl Spec {
}
trace!(target: "spec", "ensure_db_good: Populated sec trie; root is {}", root);
for (address, account) in self.genesis_state.get().iter() {
db.note_account_bloom(address);
account.insert_additional(&mut AccountDBMut::new(db.as_hashdb_mut(), address));
}
assert!(db.as_hashdb().contains(&self.state_root()));

View File

@@ -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<H256, H256>,
// Code hash of the account. If None, means that it's a contract whose code has not yet been set.
code_hash: Option<H256>,
// Code hash of the account.
code_hash: H256,
// Size of the accoun code.
code_size: Option<usize>,
// Code cache of the account.
code_cache: Bytes,
// Account is new or has been modified
code_cache: Arc<Bytes>,
// 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<Option<H256>>,
}
@@ -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<Arc<Bytes>> {
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");
}

View File

@@ -300,7 +300,10 @@ impl State {
}
}
}
// check bloom before any requests to trie
if !self.db.check_account_bloom(address) { return H256::zero() }
// account is not found in the global cache, get from the DB and insert into local
let db = self.factories.trie.readonly(self.db.as_hashdb(), &self.root).expect(SEC_TRIE_DB_UNWRAP_STR);
let maybe_acc = match db.get(address) {
@@ -319,9 +322,14 @@ impl State {
}
/// Get accounts' code.
pub fn code(&self, a: &Address) -> Option<Bytes> {
pub fn code(&self, a: &Address) -> Option<Arc<Bytes>> {
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.
@@ -400,6 +408,7 @@ impl State {
for (address, ref mut a) in accounts.iter_mut() {
match a {
&mut&mut AccountEntry::Cached(ref mut account) if account.is_dirty() => {
db.note_account_bloom(&address);
let addr_hash = account.address_hash(address);
let mut account_db = factories.accountdb.create(db.as_hashdb_mut(), addr_hash);
account.commit_storage(&factories.trie, account_db.as_hashdb_mut());
@@ -463,6 +472,7 @@ impl State {
pub fn populate_from(&mut self, accounts: PodState) {
assert!(self.snapshots.borrow().is_empty());
for (add, acc) in accounts.drain().into_iter() {
self.db.note_account_bloom(&add);
self.cache.borrow_mut().insert(add, AccountEntry::Cached(Account::from_pod(acc)));
}
}
@@ -538,6 +548,9 @@ impl State {
match result {
Some(r) => r,
None => {
// first check bloom if it is not in database for sure
if !self.db.check_account_bloom(a) { return f(None); }
// not found in the global cache, get from the DB and insert into local
let db = self.factories.trie.readonly(self.db.as_hashdb(), &self.root).expect(SEC_TRIE_DB_UNWRAP_STR);
let mut maybe_acc = match db.get(a) {
@@ -574,11 +587,17 @@ impl State {
Some(Some(acc)) => self.insert_cache(a, AccountEntry::Cached(acc)),
Some(None) => self.insert_cache(a, AccountEntry::Missing),
None => {
let db = self.factories.trie.readonly(self.db.as_hashdb(), &self.root).expect(SEC_TRIE_DB_UNWRAP_STR);
let maybe_acc = match db.get(a) {
Ok(Some(acc)) => AccountEntry::Cached(Account::from_rlp(acc)),
Ok(None) => AccountEntry::Missing,
Err(e) => panic!("Potential DB corruption encountered: {}", e),
let maybe_acc = if self.db.check_account_bloom(a) {
let db = self.factories.trie.readonly(self.db.as_hashdb(), &self.root).expect(SEC_TRIE_DB_UNWRAP_STR);
let maybe_acc = match db.get(a) {
Ok(Some(acc)) => AccountEntry::Cached(Account::from_rlp(acc)),
Ok(None) => AccountEntry::Missing,
Err(e) => panic!("Potential DB corruption encountered: {}", e),
};
maybe_acc
}
else {
AccountEntry::Missing
};
self.insert_cache(a, maybe_acc);
}
@@ -640,6 +659,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 +1524,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]

View File

@@ -18,11 +18,19 @@ use lru_cache::LruCache;
use util::journaldb::JournalDB;
use util::hash::{H256};
use util::hashdb::HashDB;
use util::{Arc, Address, DBTransaction, UtilError, Mutex};
use state::Account;
use util::{Arc, Address, Database, DBTransaction, UtilError, Mutex, Hashable};
use bloom_journal::{Bloom, BloomJournal};
use db::COL_ACCOUNT_BLOOM;
use byteorder::{LittleEndian, ByteOrder};
const STATE_CACHE_ITEMS: usize = 65536;
pub const ACCOUNT_BLOOM_SPACE: usize = 1048576;
pub const DEFAULT_ACCOUNT_PRESET: usize = 1000000;
pub const ACCOUNT_BLOOM_HASHCOUNT_KEY: &'static [u8] = b"account_hash_count";
struct AccountCache {
/// DB Account cache. `None` indicates that account is known to be missing.
accounts: LruCache<Address, Option<Account>>,
@@ -39,22 +47,83 @@ pub struct StateDB {
account_cache: Arc<Mutex<AccountCache>>,
cache_overlay: Vec<(Address, Option<Account>)>,
is_canon: bool,
account_bloom: Arc<Mutex<Bloom>>,
}
impl StateDB {
/// Create a new instance wrapping `JournalDB`
pub fn new(db: Box<JournalDB>) -> StateDB {
let bloom = Self::load_bloom(db.backing());
StateDB {
db: db,
account_cache: Arc::new(Mutex::new(AccountCache { accounts: LruCache::new(STATE_CACHE_ITEMS) })),
cache_overlay: Vec::new(),
is_canon: false,
account_bloom: Arc::new(Mutex::new(bloom)),
}
}
/// Loads accounts bloom from the database
/// This bloom is used to handle request for the non-existant account fast
pub fn load_bloom(db: &Database) -> Bloom {
let hash_count_entry = db.get(COL_ACCOUNT_BLOOM, ACCOUNT_BLOOM_HASHCOUNT_KEY)
.expect("Low-level database error");
if hash_count_entry.is_none() {
return Bloom::new(ACCOUNT_BLOOM_SPACE, DEFAULT_ACCOUNT_PRESET);
}
let hash_count_bytes = hash_count_entry.unwrap();
assert_eq!(hash_count_bytes.len(), 1);
let hash_count = hash_count_bytes[0];
let mut bloom_parts = vec![0u64; ACCOUNT_BLOOM_SPACE / 8];
let mut key = [0u8; 8];
for i in 0..ACCOUNT_BLOOM_SPACE / 8 {
LittleEndian::write_u64(&mut key, i as u64);
bloom_parts[i] = db.get(COL_ACCOUNT_BLOOM, &key).expect("low-level database error")
.and_then(|val| Some(LittleEndian::read_u64(&val[..])))
.unwrap_or(0u64);
}
let bloom = Bloom::from_parts(&bloom_parts, hash_count as u32);
trace!(target: "account_bloom", "Bloom is {:?} full, hash functions count = {:?}", bloom.saturation(), hash_count);
bloom
}
pub fn check_account_bloom(&self, address: &Address) -> bool {
trace!(target: "account_bloom", "Check account bloom: {:?}", address);
let bloom = self.account_bloom.lock();
bloom.check(&*address.sha3())
}
pub fn note_account_bloom(&self, address: &Address) {
trace!(target: "account_bloom", "Note account bloom: {:?}", address);
let mut bloom = self.account_bloom.lock();
bloom.set(&*address.sha3());
}
pub fn commit_bloom(batch: &mut DBTransaction, journal: BloomJournal) -> Result<(), UtilError> {
assert!(journal.hash_functions <= 255);
batch.put(COL_ACCOUNT_BLOOM, ACCOUNT_BLOOM_HASHCOUNT_KEY, &vec![journal.hash_functions as u8]);
let mut key = [0u8; 8];
let mut val = [0u8; 8];
for (bloom_part_index, bloom_part_value) in journal.entries {
LittleEndian::write_u64(&mut key, bloom_part_index as u64);
LittleEndian::write_u64(&mut val, bloom_part_value);
batch.put(COL_ACCOUNT_BLOOM, &key, &val);
}
Ok(())
}
/// Commit all recent insert operations and canonical historical commits' removals from the
/// old era to the backing database, reverting any non-canonical historical commit's inserts.
pub fn commit(&mut self, batch: &mut DBTransaction, now: u64, id: &H256, end: Option<(u64, H256)>) -> Result<u32, UtilError> {
{
let mut bloom_lock = self.account_bloom.lock();
try!(Self::commit_bloom(batch, bloom_lock.drain_journal()));
}
let records = try!(self.db.commit(batch, now, id, end));
if self.is_canon {
self.commit_cache();
@@ -81,6 +150,7 @@ impl StateDB {
account_cache: self.account_cache.clone(),
cache_overlay: Vec::new(),
is_canon: false,
account_bloom: self.account_bloom.clone(),
}
}
@@ -91,6 +161,7 @@ impl StateDB {
account_cache: self.account_cache.clone(),
cache_overlay: Vec::new(),
is_canon: true,
account_bloom: self.account_bloom.clone(),
}
}

View File

@@ -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]

View File

@@ -29,6 +29,7 @@ use ethereum;
use devtools::*;
use miner::Miner;
use rlp::{self, RlpStream, Stream};
use db::COL_STATE;
#[cfg(feature = "json-tests")]
pub enum ChainEra {
@@ -344,7 +345,7 @@ pub fn get_temp_state() -> GuardedTempResult<State> {
pub fn get_temp_state_db_in(path: &Path) -> StateDB {
let db = new_db(path.to_str().expect("Only valid utf8 paths for tests."));
let journal_db = journaldb::new(db.clone(), journaldb::Algorithm::EarlyMerge, None);
let journal_db = journaldb::new(db.clone(), journaldb::Algorithm::EarlyMerge, COL_STATE);
StateDB::new(journal_db)
}

View File

@@ -181,7 +181,7 @@ impl From<ActionParams> 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()),
}
}
}

View File

@@ -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)
}
}
}
}