// Copyright 2015, 2016 Ethcore (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // Parity is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with Parity. If not, see . use lru_cache::LruCache; use util::journaldb::JournalDB; use util::hash::{H256}; use util::hashdb::HashDB; 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>, } /// State database abstraction. /// Manages shared global state cache. /// A clone of `StateDB` may be created as canonical or not. /// For canonical clones cache changes are accumulated and applied /// on commit. /// For non-canonical clones cache is cleared on commit. pub struct StateDB { db: Box, account_cache: Arc>, cache_overlay: Vec<(Address, Option)>, is_canon: bool, account_bloom: Arc>, } impl StateDB { /// Create a new instance wrapping `JournalDB` pub fn new(db: Box) -> 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 { { 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(); } else { self.clear_cache(); } Ok(records) } /// 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() } /// Clone the database. pub fn boxed_clone(&self) -> StateDB { StateDB { db: self.db.boxed_clone(), account_cache: self.account_cache.clone(), cache_overlay: Vec::new(), is_canon: false, account_bloom: self.account_bloom.clone(), } } /// Clone the database for a canonical state. pub fn boxed_clone_canon(&self) -> StateDB { StateDB { db: self.db.boxed_clone(), account_cache: self.account_cache.clone(), cache_overlay: Vec::new(), is_canon: true, account_bloom: self.account_bloom.clone(), } } /// Check if pruning is enabled on the database. pub fn is_pruned(&self) -> bool { self.db.is_pruned() } /// Heap size used. pub fn mem_used(&self) -> usize { self.db.mem_used() //TODO: + self.account_cache.lock().heap_size_of_children() } /// Returns underlying `JournalDB`. pub fn journal_db(&self) -> &JournalDB { &*self.db } /// Enqueue cache change. pub fn cache_account(&mut self, addr: Address, data: Option) { self.cache_overlay.push((addr, data)); } /// Apply pending cache changes. fn commit_cache(&mut self) { let mut cache = self.account_cache.lock(); for (address, account) in self.cache_overlay.drain(..) { if let Some(&mut Some(ref mut existing)) = cache.accounts.get_mut(&address) { if let Some(new) = account { existing.merge_with(new); continue; } } cache.accounts.insert(address, account); } } /// Clear the cache. pub fn clear_cache(&mut self) { self.cache_overlay.clear(); let mut cache = self.account_cache.lock(); cache.accounts.clear(); } /// Get basic copy of the cached account. Does not include storage. /// Returns 'None' if the state is non-canonical and cache is disabled /// or if the account is not cached. pub fn get_cached_account(&self, addr: &Address) -> Option> { if !self.is_canon { return None; } let mut cache = self.account_cache.lock(); cache.accounts.get_mut(&addr).map(|a| a.as_ref().map(|a| a.clone_basic())) } /// Get value from a cached account. /// Returns 'None' if the state is non-canonical and cache is disabled /// or if the account is not cached. pub fn get_cached(&self, a: &Address, f: F) -> Option where F: FnOnce(Option<&mut Account>) -> U { if !self.is_canon { return None; } let mut cache = self.account_cache.lock(); cache.accounts.get_mut(a).map(|c| f(c.as_mut())) } }