// Copyright 2015-2019 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum 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 Ethereum 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 Ethereum. If not, see . //! 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::collections::{HashMap, HashSet}; use std::sync::Arc; use ethereum_types::{Address, H256}; use hash_db::{AsHashDB, EMPTY_PREFIX, HashDB, Prefix}; use kvdb::DBValue; use memory_db::{HashKey, MemoryDB}; use parking_lot::Mutex; use journaldb::AsKeyedHashDB; use keccak_hasher::KeccakHasher; use crate::account::Account; /// State backend. See module docs for more details. pub trait Backend: Send { /// Treat the backend as a read-only hashdb. fn as_hash_db(&self) -> &dyn HashDB; /// Treat the backend as a writeable hashdb. fn as_hash_db_mut(&mut self) -> &mut dyn HashDB; /// Add an account entry to the cache. fn add_to_account_cache(&mut self, addr: Address, data: Option, 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>); /// 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>; /// 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(&self, a: &Address, f: F) -> Option where F: FnOnce(Option<&mut Account>) -> U; /// Get cached code based on hash. fn get_cached_code(&self, hash: &H256) -> Option>>; /// 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 used to check proofs of execution. /// /// This doesn't delete anything since execution proofs won't have mangled keys /// and we want to avoid collisions. // TODO: when account lookup moved into backends, this won't rely as tenuously on intended // usage. #[derive(Clone, PartialEq)] pub struct ProofCheck(MemoryDB, DBValue>); impl ProofCheck { /// Create a new `ProofCheck` backend from the given state items. pub fn new(proof: &[DBValue]) -> Self { let mut db = journaldb::new_memory_db(); for item in proof { db.insert(EMPTY_PREFIX, item); } ProofCheck(db) } } impl journaldb::KeyedHashDB for ProofCheck { fn keys(&self) -> HashMap { self.0.keys() } } impl HashDB for ProofCheck { fn get(&self, key: &H256, prefix: Prefix) -> Option { self.0.get(key, prefix) } fn contains(&self, key: &H256, prefix: Prefix) -> bool { self.0.contains(key, prefix) } fn insert(&mut self, prefix: Prefix, value: &[u8]) -> H256 { self.0.insert(prefix, value) } fn emplace(&mut self, key: H256, prefix: Prefix, value: DBValue) { self.0.emplace(key, prefix, value) } fn remove(&mut self, _key: &H256, _prefix: Prefix) { } } impl AsHashDB for ProofCheck { fn as_hash_db(&self) -> &dyn HashDB { self } fn as_hash_db_mut(&mut self) -> &mut dyn HashDB { self } } impl Backend for ProofCheck { fn as_hash_db(&self) -> &dyn HashDB { self } fn as_hash_db_mut(&mut self) -> &mut dyn HashDB { self } fn add_to_account_cache(&mut self, _addr: Address, _data: Option, _modified: bool) {} fn cache_code(&self, _hash: H256, _code: Arc>) {} fn get_cached_account(&self, _addr: &Address) -> Option> { None } fn get_cached(&self, _a: &Address, _f: F) -> Option where F: FnOnce(Option<&mut Account>) -> U { None } fn get_cached_code(&self, _hash: &H256) -> Option>> { None } fn note_non_null_account(&self, _address: &Address) {} fn is_known_null(&self, _address: &Address) -> bool { false } } /// Proving state backend. /// This keeps track of all state values loaded during usage of this backend. /// The proof-of-execution can be extracted with `extract_proof`. /// /// This doesn't cache anything or rely on the canonical state caches. pub struct Proving { base: H, // state we're proving values from. changed: MemoryDB, DBValue>, // changed state via insertions. proof: Mutex>, } impl AsKeyedHashDB for Proving { fn as_keyed_hash_db(&self) -> &dyn journaldb::KeyedHashDB { self } } impl + Send + Sync> AsHashDB for Proving { fn as_hash_db(&self) -> &dyn HashDB { self } fn as_hash_db_mut(&mut self) -> &mut dyn HashDB { self } } impl journaldb::KeyedHashDB for Proving { fn keys(&self) -> HashMap { let mut keys = self.base.as_keyed_hash_db().keys(); keys.extend(self.changed.keys()); keys } } impl + Send + Sync> HashDB for Proving { fn get(&self, key: &H256, prefix: Prefix) -> Option { match self.base.as_hash_db().get(key, prefix) { Some(val) => { self.proof.lock().insert(val.clone()); Some(val) } None => self.changed.get(key, prefix) } } fn contains(&self, key: &H256, prefix: Prefix) -> bool { self.get(key, prefix).is_some() } fn insert(&mut self, prefix: Prefix, value: &[u8]) -> H256 { self.changed.insert(prefix, value) } fn emplace(&mut self, key: H256, prefix: Prefix, value: DBValue) { self.changed.emplace(key, prefix, value) } fn remove(&mut self, key: &H256, prefix: Prefix) { // only remove from `changed` if self.changed.contains(key, prefix) { self.changed.remove(key, prefix) } } } impl + Send + Sync> Backend for Proving { fn as_hash_db(&self) -> &dyn HashDB { self } fn as_hash_db_mut(&mut self) -> &mut dyn HashDB { self } fn add_to_account_cache(&mut self, _: Address, _: Option, _: bool) { } fn cache_code(&self, _: H256, _: Arc>) { } fn get_cached_account(&self, _: &Address) -> Option> { None } fn get_cached(&self, _: &Address, _: F) -> Option where F: FnOnce(Option<&mut Account>) -> U { None } fn get_cached_code(&self, _: &H256) -> Option>> { None } fn note_non_null_account(&self, _: &Address) { } fn is_known_null(&self, _: &Address) -> bool { false } } impl> Proving { /// Create a new `Proving` over a base database. /// This will store all values ever fetched from that base. pub fn new(base: H) -> Self { Proving { base, changed: journaldb::new_memory_db(), proof: Mutex::new(HashSet::new()), } } /// Consume the backend, extracting the gathered proof in lexicographical order /// by value. pub fn extract_proof(self) -> Vec { self.proof.into_inner().into_iter().collect() } } impl + Clone> Clone for Proving { fn clone(&self) -> Self { Proving { base: self.base.clone(), changed: self.changed.clone(), proof: Mutex::new(self.proof.lock().clone()), } } } /// A basic backend. Just wraps the given database, directly inserting into and deleting from /// it. Doesn't cache anything. pub struct Basic(pub H); impl + Send + Sync> Backend for Basic { fn as_hash_db(&self) -> &dyn HashDB { self.0.as_hash_db() } fn as_hash_db_mut(&mut self) -> &mut dyn HashDB { self.0.as_hash_db_mut() } fn add_to_account_cache(&mut self, _: Address, _: Option, _: bool) { } fn cache_code(&self, _: H256, _: Arc>) { } fn get_cached_account(&self, _: &Address) -> Option> { None } fn get_cached(&self, _: &Address, _: F) -> Option where F: FnOnce(Option<&mut Account>) -> U { None } fn get_cached_code(&self, _: &H256) -> Option>> { None } fn note_non_null_account(&self, _: &Address) { } fn is_known_null(&self, _: &Address) -> bool { false } }