Merge pull request #4632 from ethcore/state-backend
Generic state backend
This commit is contained in:
commit
eb9ee35d6c
@ -91,7 +91,7 @@ pub struct ExecutedBlock {
|
||||
uncles: Vec<Header>,
|
||||
receipts: Vec<Receipt>,
|
||||
transactions_set: HashSet<H256>,
|
||||
state: State,
|
||||
state: State<StateDB>,
|
||||
traces: Option<Vec<Vec<FlatTrace>>>,
|
||||
}
|
||||
|
||||
@ -106,7 +106,7 @@ pub struct BlockRefMut<'a> {
|
||||
/// Transaction receipts.
|
||||
pub receipts: &'a [Receipt],
|
||||
/// State.
|
||||
pub state: &'a mut State,
|
||||
pub state: &'a mut State<StateDB>,
|
||||
/// Traces.
|
||||
pub traces: &'a Option<Vec<Vec<FlatTrace>>>,
|
||||
}
|
||||
@ -122,14 +122,14 @@ pub struct BlockRef<'a> {
|
||||
/// Transaction receipts.
|
||||
pub receipts: &'a [Receipt],
|
||||
/// State.
|
||||
pub state: &'a State,
|
||||
pub state: &'a State<StateDB>,
|
||||
/// Traces.
|
||||
pub traces: &'a Option<Vec<Vec<FlatTrace>>>,
|
||||
}
|
||||
|
||||
impl ExecutedBlock {
|
||||
/// Create a new block from the given `state`.
|
||||
fn new(state: State, tracing: bool) -> ExecutedBlock {
|
||||
fn new(state: State<StateDB>, tracing: bool) -> ExecutedBlock {
|
||||
ExecutedBlock {
|
||||
header: Default::default(),
|
||||
transactions: Default::default(),
|
||||
@ -184,7 +184,7 @@ pub trait IsBlock {
|
||||
fn header(&self) -> &Header { &self.block().header }
|
||||
|
||||
/// Get the final state associated with this object's block.
|
||||
fn state(&self) -> &State { &self.block().state }
|
||||
fn state(&self) -> &State<StateDB> { &self.block().state }
|
||||
|
||||
/// Get all information on transactions in this block.
|
||||
fn transactions(&self) -> &[SignedTransaction] { &self.block().transactions }
|
||||
@ -228,7 +228,7 @@ pub struct ClosedBlock {
|
||||
block: ExecutedBlock,
|
||||
uncle_bytes: Bytes,
|
||||
last_hashes: Arc<LastHashes>,
|
||||
unclosed_state: State,
|
||||
unclosed_state: State<StateDB>,
|
||||
}
|
||||
|
||||
/// Just like `ClosedBlock` except that we can't reopen it and it's faster.
|
||||
|
@ -656,7 +656,7 @@ impl Client {
|
||||
/// This will not fail if given BlockId::Latest.
|
||||
/// Otherwise, this can fail (but may not) if the DB prunes state or the block
|
||||
/// is unknown.
|
||||
pub fn state_at(&self, id: BlockId) -> Option<State> {
|
||||
pub fn state_at(&self, id: BlockId) -> Option<State<StateDB>> {
|
||||
// fast path for latest state.
|
||||
match id.clone() {
|
||||
BlockId::Pending => return self.miner.pending_state().or_else(|| Some(self.state())),
|
||||
@ -686,7 +686,7 @@ impl Client {
|
||||
///
|
||||
/// This will not fail if given BlockId::Latest.
|
||||
/// Otherwise, this can fail (but may not) if the DB prunes state.
|
||||
pub fn state_at_beginning(&self, id: BlockId) -> Option<State> {
|
||||
pub fn state_at_beginning(&self, id: BlockId) -> Option<State<StateDB>> {
|
||||
// fast path for latest state.
|
||||
match id {
|
||||
BlockId::Pending => self.state_at(BlockId::Latest),
|
||||
@ -698,7 +698,7 @@ impl Client {
|
||||
}
|
||||
|
||||
/// Get a copy of the best block's state.
|
||||
pub fn state(&self) -> State {
|
||||
pub fn state(&self) -> State<StateDB> {
|
||||
let header = self.best_block_header();
|
||||
State::from_existing(
|
||||
self.state_db.lock().boxed_clone_canon(&header.hash()),
|
||||
|
@ -17,7 +17,7 @@
|
||||
//! Transaction Execution environment.
|
||||
use util::*;
|
||||
use action_params::{ActionParams, ActionValue};
|
||||
use state::{State, Substate, CleanupMode};
|
||||
use state::{Backend as StateBackend, State, Substate, CleanupMode};
|
||||
use engines::Engine;
|
||||
use types::executed::CallType;
|
||||
use env_info::EnvInfo;
|
||||
@ -56,17 +56,17 @@ pub struct TransactOptions {
|
||||
}
|
||||
|
||||
/// Transaction executor.
|
||||
pub struct Executive<'a> {
|
||||
state: &'a mut State,
|
||||
pub struct Executive<'a, B: 'a + StateBackend> {
|
||||
state: &'a mut State<B>,
|
||||
info: &'a EnvInfo,
|
||||
engine: &'a Engine,
|
||||
vm_factory: &'a Factory,
|
||||
depth: usize,
|
||||
}
|
||||
|
||||
impl<'a> Executive<'a> {
|
||||
impl<'a, B: 'a + StateBackend> Executive<'a, B> {
|
||||
/// Basic constructor.
|
||||
pub fn new(state: &'a mut State, info: &'a EnvInfo, engine: &'a Engine, vm_factory: &'a Factory) -> Self {
|
||||
pub fn new(state: &'a mut State<B>, info: &'a EnvInfo, engine: &'a Engine, vm_factory: &'a Factory) -> Self {
|
||||
Executive {
|
||||
state: state,
|
||||
info: info,
|
||||
@ -77,7 +77,7 @@ impl<'a> Executive<'a> {
|
||||
}
|
||||
|
||||
/// Populates executive from parent properties. Increments executive depth.
|
||||
pub fn from_parent(state: &'a mut State, info: &'a EnvInfo, engine: &'a Engine, vm_factory: &'a Factory, parent_depth: usize) -> Self {
|
||||
pub fn from_parent(state: &'a mut State<B>, info: &'a EnvInfo, engine: &'a Engine, vm_factory: &'a Factory, parent_depth: usize) -> Self {
|
||||
Executive {
|
||||
state: state,
|
||||
info: info,
|
||||
@ -95,7 +95,7 @@ impl<'a> Executive<'a> {
|
||||
output: OutputPolicy<'any, 'any>,
|
||||
tracer: &'any mut T,
|
||||
vm_tracer: &'any mut V
|
||||
) -> Externalities<'any, T, V> where T: Tracer, V: VMTracer {
|
||||
) -> Externalities<'any, T, V, B> where T: Tracer, V: VMTracer {
|
||||
Externalities::new(self.state, self.info, self.engine, self.vm_factory, self.depth, origin_info, substate, output, tracer, vm_tracer)
|
||||
}
|
||||
|
||||
|
@ -17,7 +17,7 @@
|
||||
//! Transaction Execution environment.
|
||||
use util::*;
|
||||
use action_params::{ActionParams, ActionValue};
|
||||
use state::{State, Substate};
|
||||
use state::{Backend as StateBackend, State, Substate};
|
||||
use engines::Engine;
|
||||
use env_info::EnvInfo;
|
||||
use executive::*;
|
||||
@ -57,8 +57,10 @@ impl OriginInfo {
|
||||
}
|
||||
|
||||
/// Implementation of evm Externalities.
|
||||
pub struct Externalities<'a, T, V> where T: 'a + Tracer, V: 'a + VMTracer {
|
||||
state: &'a mut State,
|
||||
pub struct Externalities<'a, T: 'a, V: 'a, B: 'a>
|
||||
where T: Tracer, V: VMTracer, B: StateBackend
|
||||
{
|
||||
state: &'a mut State<B>,
|
||||
env_info: &'a EnvInfo,
|
||||
engine: &'a Engine,
|
||||
vm_factory: &'a Factory,
|
||||
@ -71,10 +73,12 @@ pub struct Externalities<'a, T, V> where T: 'a + Tracer, V: 'a + VMTracer {
|
||||
vm_tracer: &'a mut V,
|
||||
}
|
||||
|
||||
impl<'a, T, V> Externalities<'a, T, V> where T: 'a + Tracer, V: 'a + VMTracer {
|
||||
#[cfg_attr(feature="dev", allow(too_many_arguments))]
|
||||
impl<'a, T: 'a, V: 'a, B: 'a> Externalities<'a, T, V, B>
|
||||
where T: Tracer, V: VMTracer, B: StateBackend
|
||||
{
|
||||
/// Basic `Externalities` constructor.
|
||||
pub fn new(state: &'a mut State,
|
||||
#[cfg_attr(feature="dev", allow(too_many_arguments))]
|
||||
pub fn new(state: &'a mut State<B>,
|
||||
env_info: &'a EnvInfo,
|
||||
engine: &'a Engine,
|
||||
vm_factory: &'a Factory,
|
||||
@ -101,7 +105,9 @@ impl<'a, T, V> Externalities<'a, T, V> where T: 'a + Tracer, V: 'a + VMTracer {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T, V> Ext for Externalities<'a, T, V> where T: 'a + Tracer, V: 'a + VMTracer {
|
||||
impl<'a, T: 'a, V: 'a, B: 'a> Ext for Externalities<'a, T, V, B>
|
||||
where T: Tracer, V: VMTracer, B: StateBackend
|
||||
{
|
||||
fn storage_at(&self, key: &H256) -> H256 {
|
||||
self.state.storage_at(&self.origin_info.address, key)
|
||||
}
|
||||
@ -346,7 +352,7 @@ mod tests {
|
||||
}
|
||||
|
||||
struct TestSetup {
|
||||
state: GuardedTempResult<State>,
|
||||
state: GuardedTempResult<State<::state_db::StateDB>>,
|
||||
engine: Arc<Engine>,
|
||||
sub_state: Substate,
|
||||
env_info: EnvInfo
|
||||
|
@ -16,7 +16,7 @@
|
||||
|
||||
use super::test_common::*;
|
||||
use action_params::ActionParams;
|
||||
use state::{State, Substate};
|
||||
use state::{Backend as StateBackend, State, Substate};
|
||||
use executive::*;
|
||||
use engines::Engine;
|
||||
use env_info::EnvInfo;
|
||||
@ -51,15 +51,19 @@ impl From<ethjson::vm::Call> for CallCreate {
|
||||
|
||||
/// Tiny wrapper around executive externalities.
|
||||
/// Stores callcreates.
|
||||
struct TestExt<'a, T, V> where T: 'a + Tracer, V: 'a + VMTracer {
|
||||
ext: Externalities<'a, T, V>,
|
||||
struct TestExt<'a, T: 'a, V: 'a, B: 'a>
|
||||
where T: Tracer, V: VMTracer, B: StateBackend
|
||||
{
|
||||
ext: Externalities<'a, T, V, B>,
|
||||
callcreates: Vec<CallCreate>,
|
||||
contract_address: Address
|
||||
}
|
||||
|
||||
impl<'a, T, V> TestExt<'a, T, V> where T: 'a + Tracer, V: 'a + VMTracer {
|
||||
impl<'a, T: 'a, V: 'a, B: 'a> TestExt<'a, T, V, B>
|
||||
where T: Tracer, V: VMTracer, B: StateBackend
|
||||
{
|
||||
fn new(
|
||||
state: &'a mut State,
|
||||
state: &'a mut State<B>,
|
||||
info: &'a EnvInfo,
|
||||
engine: &'a Engine,
|
||||
vm_factory: &'a Factory,
|
||||
@ -79,7 +83,9 @@ impl<'a, T, V> TestExt<'a, T, V> where T: 'a + Tracer, V: 'a + VMTracer {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T, V> Ext for TestExt<'a, T, V> where T: Tracer, V: VMTracer {
|
||||
impl<'a, T: 'a, V: 'a, B: 'a> Ext for TestExt<'a, T, V, B>
|
||||
where T: Tracer, V: VMTracer, B: StateBackend
|
||||
{
|
||||
fn storage_at(&self, key: &H256) -> H256 {
|
||||
self.ext.storage_at(key)
|
||||
}
|
||||
|
@ -140,6 +140,7 @@ pub mod snapshot;
|
||||
pub mod action_params;
|
||||
pub mod db;
|
||||
pub mod verification;
|
||||
pub mod state;
|
||||
#[macro_use] pub mod evm;
|
||||
|
||||
mod cache_manager;
|
||||
@ -147,7 +148,6 @@ mod blooms;
|
||||
mod basic_types;
|
||||
mod env_info;
|
||||
mod pod_account;
|
||||
mod state;
|
||||
mod state_db;
|
||||
mod account_db;
|
||||
mod builtin;
|
||||
|
@ -307,7 +307,7 @@ impl Miner {
|
||||
}
|
||||
|
||||
/// Get `Some` `clone()` of the current pending block's state or `None` if we're not sealing.
|
||||
pub fn pending_state(&self) -> Option<State> {
|
||||
pub fn pending_state(&self) -> Option<State<::state_db::StateDB>> {
|
||||
self.sealing_work.lock().queue.peek_last_ref().map(|b| b.block().fields().state.clone())
|
||||
}
|
||||
|
||||
|
@ -24,7 +24,7 @@ use executive::Executive;
|
||||
use trace::{NoopTracer, NoopVMTracer};
|
||||
use action_params::{ActionValue, ActionParams};
|
||||
use types::executed::CallType;
|
||||
use state::{State, Substate};
|
||||
use state::{Backend, State, Substate};
|
||||
use env_info::EnvInfo;
|
||||
use pod_state::*;
|
||||
use account_db::*;
|
||||
|
93
ethcore/src/state/backend.rs
Normal file
93
ethcore/src/state/backend.rs
Normal file
@ -0,0 +1,93 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! A minimal "state backend" trait: an abstraction over the sources of data
|
||||
//! a blockchain state may draw upon.
|
||||
//!
|
||||
//! Currently assumes a very specific DB + cache structure, but
|
||||
//! should become general over time to the point where not even a
|
||||
//! merkle trie is strictly necessary.
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use state::Account;
|
||||
use util::{Address, AsHashDB, HashDB, H256};
|
||||
|
||||
/// State backend. See module docs for more details.
|
||||
pub trait Backend: Send {
|
||||
/// Treat the backend as a read-only hashdb.
|
||||
fn as_hashdb(&self) -> &HashDB;
|
||||
|
||||
/// Treat the backend as a writeable hashdb.
|
||||
fn as_hashdb_mut(&mut self) -> &mut HashDB;
|
||||
|
||||
/// Add an account entry to the cache.
|
||||
fn add_to_account_cache(&mut self, addr: Address, data: Option<Account>, modified: bool);
|
||||
|
||||
/// Add a global code cache entry. This doesn't need to worry about canonicality because
|
||||
/// it simply maps hashes to raw code and will always be correct in the absence of
|
||||
/// hash collisions.
|
||||
fn cache_code(&self, hash: H256, code: Arc<Vec<u8>>);
|
||||
|
||||
/// Get basic copy of the cached account. Not required to include storage.
|
||||
/// Returns 'None' if cache is disabled or if the account is not cached.
|
||||
fn get_cached_account(&self, addr: &Address) -> Option<Option<Account>>;
|
||||
|
||||
/// Get value from a cached account.
|
||||
/// `None` is passed to the closure if the account entry cached
|
||||
/// is known not to exist.
|
||||
/// `None` is returned if the entry is not cached.
|
||||
fn get_cached<F, U>(&self, a: &Address, f: F) -> Option<U>
|
||||
where F: FnOnce(Option<&mut Account>) -> U;
|
||||
|
||||
/// Get cached code based on hash.
|
||||
fn get_cached_code(&self, hash: &H256) -> Option<Arc<Vec<u8>>>;
|
||||
|
||||
/// Note that an account with the given address is non-null.
|
||||
fn note_non_null_account(&self, address: &Address);
|
||||
|
||||
/// Check whether an account is known to be empty. Returns true if known to be
|
||||
/// empty, false otherwise.
|
||||
fn is_known_null(&self, address: &Address) -> bool;
|
||||
}
|
||||
|
||||
/// A raw backend which simply wraps a hashdb and does no caching.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct NoCache<T>(T);
|
||||
|
||||
impl<T> NoCache<T> {
|
||||
/// Create a new `NoCache` backend.
|
||||
pub fn new(inner: T) -> Self { NoCache(inner) }
|
||||
|
||||
/// Consume the backend, yielding the inner database.
|
||||
pub fn into_inner(self) -> T { self.0 }
|
||||
}
|
||||
|
||||
impl<T: AsHashDB + Send> Backend for NoCache<T> {
|
||||
fn as_hashdb(&self) -> &HashDB { self.0.as_hashdb() }
|
||||
fn as_hashdb_mut(&mut self) -> &mut HashDB { self.0.as_hashdb_mut() }
|
||||
fn add_to_account_cache(&mut self, _addr: Address, _data: Option<Account>, _modified: bool) {}
|
||||
fn cache_code(&self, _hash: H256, _code: Arc<Vec<u8>>) {}
|
||||
fn get_cached_account(&self, _addr: &Address) -> Option<Option<Account>> { None }
|
||||
fn get_cached<F, U>(&self, _a: &Address, _f: F) -> Option<U>
|
||||
where F: FnOnce(Option<&mut Account>) -> U
|
||||
{
|
||||
None
|
||||
}
|
||||
fn get_cached_code(&self, _hash: &H256) -> Option<Arc<Vec<u8>>> { None }
|
||||
fn note_non_null_account(&self, _address: &Address) {}
|
||||
fn is_known_null(&self, _address: &Address) -> bool { false }
|
||||
}
|
@ -14,6 +14,11 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! A mutable state representation suitable to execute transactions.
|
||||
//! Generic over a `Backend`. Deals with `Account`s.
|
||||
//! Unconfirmed sub-states are managed with `checkpoint`s which may be canonicalized
|
||||
//! or rolled back.
|
||||
|
||||
use std::cell::{RefCell, RefMut};
|
||||
use std::collections::hash_map::Entry;
|
||||
|
||||
@ -37,7 +42,10 @@ use util::trie::recorder::Recorder;
|
||||
mod account;
|
||||
mod substate;
|
||||
|
||||
pub mod backend;
|
||||
|
||||
pub use self::account::Account;
|
||||
pub use self::backend::Backend;
|
||||
pub use self::substate::Substate;
|
||||
|
||||
/// Used to return information about an `State::apply` operation.
|
||||
@ -186,8 +194,8 @@ impl AccountEntry {
|
||||
/// checkpoint can be discateded with `discard_checkpoint`. All of the orignal
|
||||
/// backed-up values are moved into a parent checkpoint (if any).
|
||||
///
|
||||
pub struct State {
|
||||
db: StateDB,
|
||||
pub struct State<B: Backend> {
|
||||
db: B,
|
||||
root: H256,
|
||||
cache: RefCell<HashMap<Address, AccountEntry>>,
|
||||
// The original account is preserved in
|
||||
@ -203,20 +211,24 @@ enum RequireCache {
|
||||
Code,
|
||||
}
|
||||
|
||||
/// Mode of dealing with null accounts.
|
||||
#[derive(PartialEq)]
|
||||
pub enum CleanupMode<'a> {
|
||||
/// Create accounts which would be null.
|
||||
ForceCreate,
|
||||
/// Don't delete null accounts upon touching, but also don't create them.
|
||||
NoEmpty,
|
||||
/// Add encountered null accounts to the provided kill-set, to be deleted later.
|
||||
KillEmpty(&'a mut HashSet<Address>),
|
||||
}
|
||||
|
||||
const SEC_TRIE_DB_UNWRAP_STR: &'static str = "A state can only be created with valid root. Creating a SecTrieDB with a valid root will not fail. \
|
||||
Therefore creating a SecTrieDB with this state's root will not fail.";
|
||||
|
||||
impl State {
|
||||
impl<B: Backend> State<B> {
|
||||
/// Creates new state with empty state root
|
||||
#[cfg(test)]
|
||||
pub fn new(mut db: StateDB, account_start_nonce: U256, factories: Factories) -> State {
|
||||
pub fn new(mut db: B, account_start_nonce: U256, factories: Factories) -> State<B> {
|
||||
let mut root = H256::new();
|
||||
{
|
||||
// init trie and reset root too null
|
||||
@ -234,7 +246,7 @@ impl State {
|
||||
}
|
||||
|
||||
/// Creates new state with existing state root
|
||||
pub fn from_existing(db: StateDB, root: H256, account_start_nonce: U256, factories: Factories) -> Result<State, TrieError> {
|
||||
pub fn from_existing(db: B, root: H256, account_start_nonce: U256, factories: Factories) -> Result<State<B>, TrieError> {
|
||||
if !db.as_hashdb().contains(&root) {
|
||||
return Err(TrieError::InvalidStateRoot(root));
|
||||
}
|
||||
@ -328,7 +340,7 @@ impl State {
|
||||
}
|
||||
|
||||
/// Destroy the current object and return root and database.
|
||||
pub fn drop(mut self) -> (H256, StateDB) {
|
||||
pub fn drop(mut self) -> (H256, B) {
|
||||
self.propagate_to_global_cache();
|
||||
(self.root, self.db)
|
||||
}
|
||||
@ -420,8 +432,8 @@ impl State {
|
||||
}
|
||||
}
|
||||
|
||||
// check bloom before any requests to trie
|
||||
if !self.db.check_non_null_bloom(address) { return H256::zero() }
|
||||
// check if the account could exist before any requests to trie
|
||||
if self.db.is_known_null(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);
|
||||
@ -443,6 +455,7 @@ impl State {
|
||||
|a| a.as_ref().map_or(None, |a| a.code().clone()))
|
||||
}
|
||||
|
||||
/// Get an account's code hash.
|
||||
pub fn code_hash(&self, a: &Address) -> H256 {
|
||||
self.ensure_cached(a, RequireCache::None, true,
|
||||
|a| a.as_ref().map_or(SHA3_EMPTY, |a| a.code_hash()))
|
||||
@ -536,7 +549,7 @@ impl State {
|
||||
#[cfg_attr(feature="dev", allow(needless_borrow))]
|
||||
fn commit_into(
|
||||
factories: &Factories,
|
||||
db: &mut StateDB,
|
||||
db: &mut B,
|
||||
root: &mut H256,
|
||||
accounts: &mut HashMap<Address, AccountEntry>
|
||||
) -> Result<(), Error> {
|
||||
@ -630,7 +643,7 @@ impl State {
|
||||
|
||||
/// Returns a `StateDiff` describing the difference from `orig` to `self`.
|
||||
/// Consumes self.
|
||||
pub fn diff_from(&self, orig: State) -> StateDiff {
|
||||
pub fn diff_from<X: Backend>(&self, orig: State<X>) -> StateDiff {
|
||||
let pod_state_post = self.to_pod();
|
||||
let mut state_pre = orig;
|
||||
state_pre.query_pod(&pod_state_post);
|
||||
@ -638,7 +651,7 @@ impl State {
|
||||
}
|
||||
|
||||
// load required account data from the databases.
|
||||
fn update_account_cache(require: RequireCache, account: &mut Account, state_db: &StateDB, db: &HashDB) {
|
||||
fn update_account_cache(require: RequireCache, account: &mut Account, state_db: &B, db: &HashDB) {
|
||||
match (account.is_cached(), require) {
|
||||
(true, _) | (false, RequireCache::None) => {}
|
||||
(false, require) => {
|
||||
@ -668,7 +681,7 @@ impl State {
|
||||
/// Check caches for required data
|
||||
/// First searches for account in the local, then the shared cache.
|
||||
/// Populates local cache if nothing found.
|
||||
fn ensure_cached<F, U>(&self, a: &Address, require: RequireCache, check_bloom: bool, f: F) -> U
|
||||
fn ensure_cached<F, U>(&self, a: &Address, require: RequireCache, check_null: bool, f: F) -> U
|
||||
where F: Fn(Option<&Account>) -> U {
|
||||
// check local cache first
|
||||
if let Some(ref mut maybe_acc) = self.cache.borrow_mut().get_mut(a) {
|
||||
@ -690,8 +703,8 @@ impl State {
|
||||
match result {
|
||||
Some(r) => r,
|
||||
None => {
|
||||
// first check bloom if it is not in database for sure
|
||||
if check_bloom && !self.db.check_non_null_bloom(a) { return f(None); }
|
||||
// first check if it is not in database for sure
|
||||
if check_null && self.db.is_known_null(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);
|
||||
@ -725,7 +738,7 @@ impl State {
|
||||
match self.db.get_cached_account(a) {
|
||||
Some(acc) => self.insert_cache(a, AccountEntry::new_clean_cached(acc)),
|
||||
None => {
|
||||
let maybe_acc = if self.db.check_non_null_bloom(a) {
|
||||
let maybe_acc = if !self.db.is_known_null(a) {
|
||||
let db = self.factories.trie.readonly(self.db.as_hashdb(), &self.root).expect(SEC_TRIE_DB_UNWRAP_STR);
|
||||
match db.get_with(a, Account::from_rlp) {
|
||||
Ok(acc) => AccountEntry::new_clean(acc),
|
||||
@ -767,7 +780,7 @@ impl State {
|
||||
}
|
||||
|
||||
// LES state proof implementations.
|
||||
impl State {
|
||||
impl<B: Backend> State<B> {
|
||||
/// Prove an account's existence or nonexistence in the state trie.
|
||||
/// Returns a merkle proof of the account's trie node with all nodes before `from_level`
|
||||
/// omitted or an encountered trie error.
|
||||
@ -813,14 +826,16 @@ impl State {
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for State {
|
||||
impl<B: Backend> fmt::Debug for State<B> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{:?}", self.cache.borrow())
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for State {
|
||||
fn clone(&self) -> State {
|
||||
// TODO: cloning for `State` shouldn't be possible in general; Remove this and use
|
||||
// checkpoints where possible.
|
||||
impl Clone for State<StateDB> {
|
||||
fn clone(&self) -> State<StateDB> {
|
||||
let cache = {
|
||||
let mut cache: HashMap<Address, AccountEntry> = HashMap::new();
|
||||
for (key, val) in self.cache.borrow().iter() {
|
||||
|
@ -21,7 +21,7 @@ use util::journaldb::JournalDB;
|
||||
use util::kvdb::KeyValueDB;
|
||||
use util::hash::{H256};
|
||||
use util::hashdb::HashDB;
|
||||
use state::Account;
|
||||
use state::{self, Account};
|
||||
use header::BlockNumber;
|
||||
use util::{Arc, Address, DBTransaction, UtilError, Mutex, Hashable};
|
||||
use bloom_journal::{Bloom, BloomJournal};
|
||||
@ -166,18 +166,6 @@ impl StateDB {
|
||||
bloom
|
||||
}
|
||||
|
||||
pub fn check_non_null_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_non_null_account(&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, &[journal.hash_functions as u8]);
|
||||
@ -306,12 +294,10 @@ impl StateDB {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns an interface to HashDB.
|
||||
pub fn as_hashdb(&self) -> &HashDB {
|
||||
self.db.as_hashdb()
|
||||
}
|
||||
|
||||
/// Returns an interface to mutable HashDB.
|
||||
pub fn as_hashdb_mut(&mut self) -> &mut HashDB {
|
||||
self.db.as_hashdb_mut()
|
||||
}
|
||||
@ -366,56 +352,6 @@ impl StateDB {
|
||||
&*self.db
|
||||
}
|
||||
|
||||
/// Add a local cache entry.
|
||||
/// The entry will be propagated to the global cache in `sync_cache`.
|
||||
/// `modified` indicates that the entry was changed since being read from disk or global cache.
|
||||
/// `data` can be set to an existing (`Some`), or non-existing account (`None`).
|
||||
pub fn add_to_account_cache(&mut self, addr: Address, data: Option<Account>, modified: bool) {
|
||||
self.local_cache.push(CacheQueueItem {
|
||||
address: addr,
|
||||
account: data,
|
||||
modified: modified,
|
||||
})
|
||||
}
|
||||
|
||||
/// Add a global code cache entry. This doesn't need to worry about canonicality because
|
||||
/// it simply maps hashes to raw code and will always be correct in the absence of
|
||||
/// hash collisions.
|
||||
pub fn cache_code(&self, hash: H256, code: Arc<Vec<u8>>) {
|
||||
let mut cache = self.code_cache.lock();
|
||||
|
||||
cache.insert(hash, code);
|
||||
}
|
||||
|
||||
/// Get basic copy of the cached account. Does not include storage.
|
||||
/// Returns 'None' if cache is disabled or if the account is not cached.
|
||||
pub fn get_cached_account(&self, addr: &Address) -> Option<Option<Account>> {
|
||||
let mut cache = self.account_cache.lock();
|
||||
if !Self::is_allowed(addr, &self.parent_hash, &cache.modifications) {
|
||||
return None;
|
||||
}
|
||||
cache.accounts.get_mut(addr).map(|a| a.as_ref().map(|a| a.clone_basic()))
|
||||
}
|
||||
|
||||
/// Get cached code based on hash.
|
||||
#[cfg_attr(feature="dev", allow(map_clone))]
|
||||
pub fn get_cached_code(&self, hash: &H256) -> Option<Arc<Vec<u8>>> {
|
||||
let mut cache = self.code_cache.lock();
|
||||
|
||||
cache.get_mut(hash).map(|code| code.clone())
|
||||
}
|
||||
|
||||
/// Get value from a cached account.
|
||||
/// Returns 'None' if cache is disabled or if the account is not cached.
|
||||
pub fn get_cached<F, U>(&self, a: &Address, f: F) -> Option<U>
|
||||
where F: FnOnce(Option<&mut Account>) -> U {
|
||||
let mut cache = self.account_cache.lock();
|
||||
if !Self::is_allowed(a, &self.parent_hash, &cache.modifications) {
|
||||
return None;
|
||||
}
|
||||
cache.accounts.get_mut(a).map(|c| f(c.as_mut()))
|
||||
}
|
||||
|
||||
/// Query how much memory is set aside for the accounts cache (in bytes).
|
||||
pub fn cache_size(&self) -> usize {
|
||||
self.cache_size
|
||||
@ -456,11 +392,71 @@ impl StateDB {
|
||||
}
|
||||
}
|
||||
|
||||
impl state::Backend for StateDB {
|
||||
fn as_hashdb(&self) -> &HashDB {
|
||||
self.db.as_hashdb()
|
||||
}
|
||||
|
||||
fn as_hashdb_mut(&mut self) -> &mut HashDB {
|
||||
self.db.as_hashdb_mut()
|
||||
}
|
||||
|
||||
fn add_to_account_cache(&mut self, addr: Address, data: Option<Account>, modified: bool) {
|
||||
self.local_cache.push(CacheQueueItem {
|
||||
address: addr,
|
||||
account: data,
|
||||
modified: modified,
|
||||
})
|
||||
}
|
||||
|
||||
fn cache_code(&self, hash: H256, code: Arc<Vec<u8>>) {
|
||||
let mut cache = self.code_cache.lock();
|
||||
|
||||
cache.insert(hash, code);
|
||||
}
|
||||
|
||||
fn get_cached_account(&self, addr: &Address) -> Option<Option<Account>> {
|
||||
let mut cache = self.account_cache.lock();
|
||||
if !Self::is_allowed(addr, &self.parent_hash, &cache.modifications) {
|
||||
return None;
|
||||
}
|
||||
cache.accounts.get_mut(addr).map(|a| a.as_ref().map(|a| a.clone_basic()))
|
||||
}
|
||||
|
||||
#[cfg_attr(feature="dev", allow(map_clone))]
|
||||
fn get_cached_code(&self, hash: &H256) -> Option<Arc<Vec<u8>>> {
|
||||
let mut cache = self.code_cache.lock();
|
||||
|
||||
cache.get_mut(hash).map(|code| code.clone())
|
||||
}
|
||||
|
||||
fn get_cached<F, U>(&self, a: &Address, f: F) -> Option<U>
|
||||
where F: FnOnce(Option<&mut Account>) -> U {
|
||||
let mut cache = self.account_cache.lock();
|
||||
if !Self::is_allowed(a, &self.parent_hash, &cache.modifications) {
|
||||
return None;
|
||||
}
|
||||
cache.accounts.get_mut(a).map(|c| f(c.as_mut()))
|
||||
}
|
||||
|
||||
fn note_non_null_account(&self, address: &Address) {
|
||||
trace!(target: "account_bloom", "Note account bloom: {:?}", address);
|
||||
let mut bloom = self.account_bloom.lock();
|
||||
bloom.set(&*address.sha3());
|
||||
}
|
||||
|
||||
fn is_known_null(&self, address: &Address) -> bool {
|
||||
trace!(target: "account_bloom", "Check account bloom: {:?}", address);
|
||||
let bloom = self.account_bloom.lock();
|
||||
!bloom.check(&*address.sha3())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use util::{U256, H256, FixedHash, Address, DBTransaction};
|
||||
use tests::helpers::*;
|
||||
use state::Account;
|
||||
use state::{Account, Backend};
|
||||
use util::log::init_log;
|
||||
|
||||
#[test]
|
||||
|
@ -349,7 +349,7 @@ pub fn get_temp_state_db() -> GuardedTempResult<StateDB> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_temp_state() -> GuardedTempResult<State> {
|
||||
pub fn get_temp_state() -> GuardedTempResult<State<::state_db::StateDB>> {
|
||||
let temp = RandomTempPath::new();
|
||||
let journal_db = get_temp_state_db_in(temp.as_path());
|
||||
|
||||
@ -365,7 +365,7 @@ pub fn get_temp_state_db_in(path: &Path) -> StateDB {
|
||||
StateDB::new(journal_db, 5 * 1024 * 1024)
|
||||
}
|
||||
|
||||
pub fn get_temp_state_in(path: &Path) -> State {
|
||||
pub fn get_temp_state_in(path: &Path) -> State<::state_db::StateDB> {
|
||||
let journal_db = get_temp_state_db_in(path);
|
||||
State::new(journal_db, U256::from(0), Default::default())
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user