diff --git a/Cargo.lock b/Cargo.lock index 6e7dea9d1d8bc..74c000508a798 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1222,6 +1222,15 @@ dependencies = [ "proc-macro-hack 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "historied-data" +version = "2.0.0" +dependencies = [ + "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", + "sr-std 2.0.0", +] + [[package]] name = "hmac" version = "0.4.2" @@ -4746,6 +4755,7 @@ version = "2.0.0" dependencies = [ "env_logger 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "hash-db 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", + "historied-data 2.0.0", "kvdb 0.1.0 (git+https://github.com/paritytech/parity-common?rev=b0317f649ab2c665b7987b8475878fc4d2e1f81d)", "kvdb-memorydb 0.1.0 (git+https://github.com/paritytech/parity-common?rev=b0317f649ab2c665b7987b8475878fc4d2e1f81d)", "kvdb-rocksdb 0.1.4 (git+https://github.com/paritytech/parity-common?rev=b0317f649ab2c665b7987b8475878fc4d2e1f81d)", @@ -5442,6 +5452,7 @@ name = "substrate-state-db" version = "2.0.0" dependencies = [ "env_logger 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "historied-data 2.0.0", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "parity-scale-codec 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/Cargo.toml b/Cargo.toml index 7e34a0bd6ccd0..fa4c433215ce9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -68,6 +68,7 @@ members = [ "core/transaction-pool/graph", "core/trie", "core/utils/fork-tree", + "core/utils/historied-data", "core/utils/wasm-builder", "core/utils/wasm-builder-runner", "core/wasm-interface", diff --git a/core/client/db/Cargo.toml b/core/client/db/Cargo.toml index 7d88c39d7fd7e..98788761d60f6 100644 --- a/core/client/db/Cargo.toml +++ b/core/client/db/Cargo.toml @@ -23,6 +23,7 @@ state_db = { package = "substrate-state-db", path = "../../state-db" } trie = { package = "substrate-trie", path = "../../trie" } consensus_common = { package = "substrate-consensus-common", path = "../../consensus/common" } header_metadata = { package = "substrate-header-metadata", path = "../header-metadata" } +historied-data = { path = "../../utils/historied-data" } [dev-dependencies] substrate-keyring = { path = "../../keyring" } diff --git a/core/client/db/src/lib.rs b/core/client/db/src/lib.rs index 1f7fa604a4092..9426681e145e7 100644 --- a/core/client/db/src/lib.rs +++ b/core/client/db/src/lib.rs @@ -34,6 +34,7 @@ mod storage_cache; mod utils; use std::sync::Arc; +use std::ops::Deref; use std::path::PathBuf; use std::io; use std::collections::{HashMap, HashSet}; @@ -50,6 +51,7 @@ use trie::{MemoryDB, PrefixedMemoryDB, prefixed_key}; use parking_lot::{Mutex, RwLock}; use primitives::{H256, Blake2Hasher, ChangesTrieConfiguration, convert_hash, traits::CodeExecutor}; use primitives::storage::well_known_keys; +use primitives::child_trie::KeySpace; use sr_primitives::{ generic::{BlockId, DigestItem}, Justification, StorageOverlay, ChildrenStorageOverlay, BuildStorage, @@ -60,19 +62,27 @@ use sr_primitives::traits::{ use executor::RuntimeInfo; use state_machine::{ DBValue, ChangesTrieTransaction, ChangesTrieCacheAction, ChangesTrieBuildCache, - backend::Backend as StateBackend, + backend::Backend as StateBackend, InMemoryKvBackend, }; use crate::utils::{Meta, db_err, meta_keys, read_db, read_meta}; use client::leaves::{LeafSet, FinalizationDisplaced}; use client::children; use state_db::StateDb; +use state_db::BranchRanges; use header_metadata::{CachedHeaderMetadata, HeaderMetadata, HeaderMetadataCache}; use crate::storage_cache::{CachingState, SharedCache, new_shared_cache}; use log::{trace, debug, warn}; pub use state_db::PruningMode; +use historied_data::tree::Serialized; +use historied_data::PruneResult; +use historied_data::linear::DefaultVersion; + +type Ser<'a> = Serialized<'a, DefaultVersion>; #[cfg(feature = "test-helpers")] use client::in_mem::Backend as InMemoryBackend; +#[cfg(feature = "test-helpers")] +use state_machine::backend::InMemoryTransaction; const CANONICALIZATION_DELAY: u64 = 4096; const MIN_BLOCKS_TO_KEEP_CHANGES_TRIES_FOR: u32 = 32768; @@ -81,7 +91,12 @@ const MIN_BLOCKS_TO_KEEP_CHANGES_TRIES_FOR: u32 = 32768; const DEFAULT_CHILD_RATIO: (usize, usize) = (1, 10); /// DB-backed patricia trie state, transaction type is an overlay of changes to commit. -pub type DbState = state_machine::TrieBackend>, Blake2Hasher>; +/// A simple key value backend is also accessible for direct key value storage. +pub type DbState = state_machine::TrieBackend< + Arc>, + Blake2Hasher, + Arc, +>; /// A reference tracking state. /// @@ -121,6 +136,7 @@ impl StateBackend for RefTrackingState { type Error = >::Error; type Transaction = >::Transaction; type TrieBackendStorage = >::TrieBackendStorage; + type KvBackend = >::KvBackend; fn storage(&self, key: &[u8]) -> Result>, Self::Error> { self.state.storage(key) @@ -134,6 +150,10 @@ impl StateBackend for RefTrackingState { self.state.child_storage(storage_key, key) } + fn kv_storage(&self, key: &[u8]) -> Result>, Self::Error> { + self.state.kv_storage(key) + } + fn exists_storage(&self, key: &[u8]) -> Result { self.state.exists_storage(key) } @@ -165,17 +185,36 @@ impl StateBackend for RefTrackingState { self.state.storage_root(delta) } - fn child_storage_root(&self, storage_key: &[u8], delta: I) -> (Vec, bool, Self::Transaction) + fn child_storage_root(&self, storage_key: &[u8], keyspace: &KeySpace, delta: I) -> (Vec, bool, Self::Transaction) where I: IntoIterator, Option>)>, { - self.state.child_storage_root(storage_key, delta) + self.state.child_storage_root(storage_key, keyspace, delta) + } + + fn kv_transaction(&self, delta: I) -> Self::Transaction + where + I: IntoIterator, Option>)> + { + self.state.kv_transaction(delta) } fn pairs(&self) -> Vec<(Vec, Vec)> { self.state.pairs() } + fn children_storage_keys(&self) -> Vec> { + self.state.children_storage_keys() + } + + fn child_pairs(&self, child_storage_key: &[u8]) -> Vec<(Vec, Vec)> { + self.state.child_pairs(child_storage_key) + } + + fn kv_pairs(&self) -> HashMap, Option>> { + self.state.kv_pairs() + } + fn keys(&self, prefix: &[u8]) -> Vec> { self.state.keys(prefix) } @@ -184,7 +223,9 @@ impl StateBackend for RefTrackingState { self.state.child_keys(child_key, prefix) } - fn as_trie_backend(&mut self) -> Option<&state_machine::TrieBackend> { + fn as_trie_backend(&mut self) -> Option< + &state_machine::TrieBackend + > { self.state.as_trie_backend() } } @@ -248,6 +289,8 @@ pub(crate) mod columns { pub const AUX: Option = Some(8); /// Offchain workers local storage pub const OFFCHAIN: Option = Some(9); + /// Kv data + pub const OFFSTATE: Option = Some(10); } struct PendingBlock { @@ -433,7 +476,7 @@ impl HeaderMetadata for BlockchainDb { /// Database transaction pub struct BlockImportOperation { old_state: CachingState, Block>, - db_updates: PrefixedMemoryDB, + db_updates: (PrefixedMemoryDB, HashMap, Option>>), storage_updates: StorageCollection, child_storage_updates: ChildStorageCollection, changes_trie_updates: MemoryDB, @@ -485,7 +528,10 @@ impl client::backend::BlockImportOperation // Currently cache isn't implemented on full nodes. } - fn update_db_storage(&mut self, update: PrefixedMemoryDB) -> ClientResult<()> { + fn update_db_storage( + &mut self, + update: (PrefixedMemoryDB, HashMap, Option>>), + ) -> ClientResult<()> { self.db_updates = update; Ok(()) } @@ -512,8 +558,9 @@ impl client::backend::BlockImportOperation let (root, transaction) = self.old_state.full_storage_root( top.into_iter().map(|(k, v)| (k, Some(v))), - child_delta - ); + child_delta, + None, + )?; self.db_updates = transaction; Ok(root) @@ -562,6 +609,11 @@ struct StorageDb { pub state_db: StateDb>, } +struct StorageDbAt { + pub storage_db: Arc>, + pub state: State, +} + impl state_machine::Storage for StorageDb { fn get(&self, key: &H256, prefix: Prefix) -> Result, String> { let key = prefixed_key::(key, prefix); @@ -579,6 +631,45 @@ impl state_db::NodeDb for StorageDb { } } +impl state_db::KvDb for StorageDb { + + type Error = io::Error; + + fn get_kv(&self, key: &[u8], state: &u64) -> Result>, Self::Error> { + Ok(self.db.get(columns::OFFSTATE, key)? + .as_ref() + .map(|s| Ser::from_slice(&s[..])) + .and_then(|s| s.get(*state) + .unwrap_or(None) // flatten + .map(Into::into) + )) + } + + fn get_kv_pairs(&self, state: &u64) -> Vec<(Vec, Vec)> { + self.db.iter(columns::OFFSTATE).filter_map(|(k, v)| + Ser::from_slice(&v[..]).get(*state) + .unwrap_or(None) // flatten + .map(Into::into) + .map(|v| (k.to_vec(), v)) + ).collect() + } +} + +impl state_machine::KvBackend for StorageDbAt { + + fn get(&self, key: &[u8]) -> Result>, String> { + self.storage_db.state_db.get_kv(key, &self.state, self.storage_db.deref()) + .map_err(|e| format!("Database backend error: {:?}", e)) + } + + fn pairs(&self) -> HashMap, Option>> { + self.storage_db.state_db.get_kv_pairs(&self.state, self.storage_db.deref()) + // Not that we do not store deletion. + // Function pairs should not really be call for this backend. + .into_iter().map(|(k, v)| (k, Some(v))).collect() + } +} + struct DbGenesisStorage(pub H256); impl DbGenesisStorage { @@ -879,7 +970,13 @@ impl> Backend { let id = BlockId::Hash(hash); let justification = self.blockchain.justification(id).unwrap(); let body = self.blockchain.body(id).unwrap(); - let state = self.state_at(id).unwrap().pairs(); + let state = self.state_at(id).unwrap(); + let mut storage: Vec<_> = state.pairs().into_iter() + .map(|(k, v)| (None, k, Some(v))).collect(); + for child_key in state.children_storage_keys() { + storage.extend(state.child_pairs(child_key.as_slice()) + .into_iter().map(|(k, v)| (Some(child_key.clone()), k, Some(v)))); + } let new_block_state = if number.is_zero() { NewBlockState::Final @@ -890,7 +987,10 @@ impl> Backend { }; let mut op = inmem.begin_operation().unwrap(); op.set_block_data(header, body, justification, new_block_state).unwrap(); - op.update_db_storage(state.into_iter().map(|(k, v)| (None, k, Some(v))).collect()).unwrap(); + op.update_db_storage(InMemoryTransaction { + storage, + kv: state.kv_pairs().clone(), + }).unwrap(); inmem.commit_operation(op).unwrap(); } @@ -1061,7 +1161,11 @@ impl> Backend { trace!(target: "db", "Canonicalize block #{} ({:?})", new_canonical, hash); let commit = self.storage.state_db.canonicalize_block(&hash) .map_err(|e: state_db::Error| client::error::Error::from(format!("State database error: {:?}", e)))?; - apply_state_commit(transaction, commit); + apply_state_commit_canonical(transaction, commit, &self.storage.db, number_u64).map_err(|err| + client::error::Error::Backend( + format!("Error building commit transaction : {}", err) + ) + )?; }; Ok(()) @@ -1134,17 +1238,30 @@ impl> Backend { } let mut changeset: state_db::ChangeSet> = state_db::ChangeSet::default(); - for (key, (val, rc)) in operation.db_updates.drain() { + for (key, (val, rc)) in operation.db_updates.0.drain() { if rc > 0 { changeset.inserted.push((key, val.to_vec())); } else if rc < 0 { changeset.deleted.push(key); } } + let kv_changeset: state_db::KvChangeSet> = operation.db_updates.1 + // use as vec from hash map + .into_iter().collect(); let number_u64 = number.saturated_into::(); - let commit = self.storage.state_db.insert_block(&hash, number_u64, &pending_block.header.parent_hash(), changeset) - .map_err(|e: state_db::Error| client::error::Error::from(format!("State database error: {:?}", e)))?; - apply_state_commit(&mut transaction, commit); + let commit = self.storage.state_db.insert_block( + &hash, + number_u64, + &pending_block.header.parent_hash(), + changeset, + kv_changeset, + ).map_err(|e: state_db::Error| + client::error::Error::from(format!("State database error: {:?}", e)))?; + apply_state_commit(&mut transaction, commit, &self.storage.db, number_u64).map_err(|err| + client::error::Error::Backend( + format!("Error building commit transaction : {}", err) + ) + )?; // Check if need to finalize. Genesis is always finalized instantly. let finalized = number_u64 == 0 || pending_block.leaf_state.is_final(); @@ -1180,9 +1297,20 @@ impl> Backend { displaced_leaf }; - let mut children = children::read_children(&*self.storage.db, columns::META, meta_keys::CHILDREN_PREFIX, parent_hash)?; + let mut children = children::read_children( + &*self.storage.db, + columns::META, + meta_keys::CHILDREN_PREFIX, + parent_hash, + )?; children.push(hash); - children::write_children(&mut transaction, columns::META, meta_keys::CHILDREN_PREFIX, parent_hash, children); + children::write_children( + &mut transaction, + columns::META, + meta_keys::CHILDREN_PREFIX, + parent_hash, + children, + ); meta_updates.push((hash, number, pending_block.leaf_state.is_best(), finalized)); @@ -1268,15 +1396,24 @@ impl> Backend { { let f_num = f_header.number().clone(); - if self.storage.state_db.best_canonical().map(|c| f_num.saturated_into::() > c).unwrap_or(true) { + if self.storage.state_db.best_canonical() + .map(|c| f_num.saturated_into::() > c) + .unwrap_or(true) { let parent_hash = f_header.parent_hash().clone(); + let number = f_header.number().clone(); + let number_u64 = number.saturated_into::(); let lookup_key = utils::number_and_hash_to_lookup_key(f_num, f_hash.clone())?; transaction.put(columns::META, meta_keys::FINALIZED_BLOCK, &lookup_key); let commit = self.storage.state_db.canonicalize_block(&f_hash) - .map_err(|e: state_db::Error| client::error::Error::from(format!("State database error: {:?}", e)))?; - apply_state_commit(transaction, commit); + .map_err(|e: state_db::Error| + client::error::Error::from(format!("State database error: {:?}", e)) + )?; + apply_state_commit_canonical(transaction, commit, &self.storage.db, number_u64) + .map_err(|err| client::error::Error::Backend( + format!("Error building commit transaction : {}", err) + ))?; let changes_trie_config = self.changes_trie_config(parent_hash)?; if let Some(changes_trie_config) = changes_trie_config { @@ -1294,7 +1431,77 @@ impl> Backend { } } -fn apply_state_commit(transaction: &mut DBTransaction, commit: state_db::CommitSet>) { +fn apply_state_commit( + transaction: &mut DBTransaction, + commit: state_db::CommitSet>, + db: &Arc, + last_block: u64, +) -> Result<(), io::Error> { + apply_state_commit_inner(transaction, commit, db, last_block, None) +} + +fn apply_state_commit_inner( + transaction: &mut DBTransaction, + commit: state_db::CommitSet>, + db: &Arc, + last_block: u64, + mut kv_prune_key: state_db::KvChangeSetPrune, +) -> Result<(), io::Error> { + + for (key, o) in commit.kv.iter() { + let (mut ser, new) = if let Some(stored) = db.get(columns::OFFSTATE, key)? { + (Ser::from_vec(stored.to_vec()), false) + } else { + if o.is_some() { + (Ser::default(), true) + } else { + break; + } + }; + ser.push(last_block, o.as_ref().map(|v| v.as_slice())); + if let Some((block_prune, kv_prune_keys)) = kv_prune_key.as_mut() { + if !new && kv_prune_keys.remove(key) { + match ser.prune(*block_prune) { + PruneResult::Cleared => transaction.delete(columns::OFFSTATE, &key), + PruneResult::Changed + | PruneResult::Unchanged => transaction.put(columns::OFFSTATE, &key[..], &ser.into_vec()), + } + } else { + transaction.put(columns::OFFSTATE, &key[..], &ser.into_vec()) + } + } else { + transaction.put(columns::OFFSTATE, &key[..], &ser.into_vec()) + } + } + + if let Some((block_prune, kv_prune_key)) = kv_prune_key { + // no need to into_iter + for key in kv_prune_key.iter() { + // TODO EMCH should we prune less often (wait on pruning journals for instance) + // or make it out of this context just stored the block prune and do asynch + let mut ser = if let Some(stored) = db.get(columns::OFFSTATE, key)? { + Ser::from_vec(stored.to_vec()) + } else { + break; + }; + + match ser.prune(block_prune) { + PruneResult::Cleared => transaction.delete(columns::OFFSTATE, &key), + PruneResult::Changed => transaction.put(columns::OFFSTATE, &key[..], &ser.into_vec()), + PruneResult::Unchanged => (), + } + } + + } + apply_state_commit_no_kv(transaction, commit); + Ok(()) +} + +fn apply_state_commit_no_kv( + transaction: &mut DBTransaction, + commit: state_db::CommitSet>, + ) { + for (key, val) in commit.data.inserted.into_iter() { transaction.put(columns::STATE, &key[..], &val); } @@ -1307,6 +1514,17 @@ fn apply_state_commit(transaction: &mut DBTransaction, commit: state_db::CommitS for key in commit.meta.deleted.into_iter() { transaction.delete(columns::STATE_META, &key[..]); } + +} + + +fn apply_state_commit_canonical( + transaction: &mut DBTransaction, + commit: state_db::CommitSetCanonical>, + db: &Arc, + last_block: u64, +) -> Result<(), io::Error> { + apply_state_commit_inner(transaction, commit.0, db, last_block, commit.1) } impl client::backend::AuxStore for Backend where Block: BlockT { @@ -1345,7 +1563,7 @@ impl client::backend::Backend for Backend whe Ok(BlockImportOperation { pending_block: None, old_state, - db_updates: PrefixedMemoryDB::default(), + db_updates: (PrefixedMemoryDB::default(), Default::default()), storage_updates: Default::default(), child_storage_updates: Default::default(), changes_trie_updates: MemoryDB::default(), @@ -1417,8 +1635,8 @@ impl client::backend::Backend for Backend whe Some(&self.changes_tries_storage) } - fn offchain_storage(&self) -> Option { - Some(self.offchain_storage.clone()) + fn offchain_storage(&self) -> Option<&Self::OffchainStorage> { + Some(&self.offchain_storage) } fn revert(&self, n: NumberFor) -> ClientResult> { @@ -1434,7 +1652,8 @@ impl client::backend::Backend for Backend whe let mut transaction = DBTransaction::new(); match self.storage.state_db.revert_one() { Some(commit) => { - apply_state_commit(&mut transaction, commit); + debug_assert!(commit.kv.is_empty(), "revert do not change key value store"); + apply_state_commit_no_kv(&mut transaction, commit); let removed = self.blockchain.header(BlockId::Number(best))?.ok_or_else( || client::error::Error::UnknownBlock( format!("Error reverting to {}. Block hash not found.", best)))?; @@ -1474,7 +1693,8 @@ impl client::backend::Backend for Backend whe BlockId::Hash(h) if h == Default::default() => { let genesis_storage = DbGenesisStorage::new(); let root = genesis_storage.0.clone(); - let db_state = DbState::new(Arc::new(genesis_storage), root); + let genesis_kv = InMemoryKvBackend::default(); + let db_state = DbState::new(Arc::new(genesis_storage), root, Arc::new(genesis_kv)); let state = RefTrackingState::new(db_state, self.storage.clone(), None); return Ok(CachingState::new(state, self.shared_cache.clone(), None)); }, @@ -1485,8 +1705,14 @@ impl client::backend::Backend for Backend whe Ok(Some(ref hdr)) => { let hash = hdr.hash(); if let Ok(()) = self.storage.state_db.pin(&hash) { + let block_number = hdr.number().clone().saturated_into::(); + let range = self.storage.state_db.get_branch_range(&hash, block_number); let root = H256::from_slice(hdr.state_root().as_ref()); - let db_state = DbState::new(self.storage.clone(), root); + let kv = StorageDbAt { + storage_db: self.storage.clone(), + state: (range.unwrap_or_else(Default::default), block_number), + }; + let db_state = DbState::new(self.storage.clone(), root, Arc::new(kv)); let state = RefTrackingState::new(db_state, self.storage.clone(), Some(hash.clone())); Ok(CachingState::new(state, self.shared_cache.clone(), Some(hash))) } else { @@ -1698,7 +1924,12 @@ mod tests { (vec![5, 5, 5], Some(vec![4, 5, 6])), ]; - let (root, overlay) = op.old_state.storage_root(storage.iter().cloned()); + let child: Option<(_, Option<_>)> = None; + let (root, overlay) = op.old_state.full_storage_root( + storage.iter().cloned(), + child, + storage.iter().cloned(), + ).unwrap(); op.update_db_storage(overlay).unwrap(); header.state_root = root.into(); @@ -1716,6 +1947,12 @@ mod tests { assert_eq!(state.storage(&[1, 3, 5]).unwrap(), None); assert_eq!(state.storage(&[1, 2, 3]).unwrap(), Some(vec![9, 9, 9])); assert_eq!(state.storage(&[5, 5, 5]).unwrap(), Some(vec![4, 5, 6])); + assert_eq!(state.kv_pairs(), vec![ + (vec![5, 5, 5], Some(vec![4, 5, 6])) + ].into_iter().collect()); + let state = db.state_at(BlockId::Number(0)).unwrap(); + assert_eq!(state.kv_pairs(), vec![].into_iter().collect()); + } } @@ -1747,7 +1984,7 @@ mod tests { op.reset_storage(storage.iter().cloned().collect(), Default::default()).unwrap(); - key = op.db_updates.insert(EMPTY_PREFIX, b"hello"); + key = op.db_updates.0.insert(EMPTY_PREFIX, b"hello"); op.set_block_data( header, Some(vec![]), @@ -1783,8 +2020,8 @@ mod tests { ).0.into(); let hash = header.hash(); - op.db_updates.insert(EMPTY_PREFIX, b"hello"); - op.db_updates.remove(&key, EMPTY_PREFIX); + op.db_updates.0.insert(EMPTY_PREFIX, b"hello"); + op.db_updates.0.remove(&key, EMPTY_PREFIX); op.set_block_data( header, Some(vec![]), @@ -1820,7 +2057,7 @@ mod tests { ).0.into(); let hash = header.hash(); - op.db_updates.remove(&key, EMPTY_PREFIX); + op.db_updates.0.remove(&key, EMPTY_PREFIX); op.set_block_data( header, Some(vec![]), diff --git a/core/client/db/src/storage_cache.rs b/core/client/db/src/storage_cache.rs index af8c9e379c4c1..32b712016896e 100644 --- a/core/client/db/src/storage_cache.rs +++ b/core/client/db/src/storage_cache.rs @@ -22,6 +22,7 @@ use parking_lot::{Mutex, RwLock, RwLockUpgradableReadGuard}; use linked_hash_map::{LinkedHashMap, Entry}; use hash_db::Hasher; use sr_primitives::traits::{Block as BlockT, Header}; +use primitives::child_trie::KeySpace; use state_machine::{backend::Backend as StateBackend, TrieBackend}; use log::trace; use super::{StorageCollection, ChildStorageCollection}; @@ -470,6 +471,7 @@ impl, B: BlockT> StateBackend for CachingState< type Error = S::Error; type Transaction = S::Transaction; type TrieBackendStorage = S::TrieBackendStorage; + type KvBackend = S::KvBackend; fn storage(&self, key: &[u8]) -> Result>, Self::Error> { let local_cache = self.cache.local_cache.upgradable_read(); @@ -530,6 +532,10 @@ impl, B: BlockT> StateBackend for CachingState< Ok(value) } + fn kv_storage(&self, key: &[u8]) -> Result>, Self::Error> { + self.state.kv_storage(key) + } + fn exists_storage(&self, key: &[u8]) -> Result { Ok(self.storage(key)?.is_some()) } @@ -562,18 +568,42 @@ impl, B: BlockT> StateBackend for CachingState< self.state.storage_root(delta) } - fn child_storage_root(&self, storage_key: &[u8], delta: I) -> (Vec, bool, Self::Transaction) + fn child_storage_root( + &self, + storage_key: &[u8], + keyspace: &KeySpace, + delta: I, + ) -> (Vec, bool, Self::Transaction) where I: IntoIterator, Option>)>, H::Out: Ord { - self.state.child_storage_root(storage_key, delta) + self.state.child_storage_root(storage_key, keyspace, delta) + } + + fn kv_transaction(&self, delta: I) -> Self::Transaction + where + I: IntoIterator, Option>)>, + { + self.state.kv_transaction(delta) } fn pairs(&self) -> Vec<(Vec, Vec)> { self.state.pairs() } + fn children_storage_keys(&self) -> Vec> { + self.state.children_storage_keys() + } + + fn child_pairs(&self, storage_key: &[u8]) -> Vec<(Vec, Vec)> { + self.state.child_pairs(storage_key) + } + + fn kv_pairs(&self) -> HashMap, Option>> { + self.state.kv_pairs() + } + fn keys(&self, prefix: &[u8]) -> Vec> { self.state.keys(prefix) } @@ -582,7 +612,9 @@ impl, B: BlockT> StateBackend for CachingState< self.state.child_keys(child_key, prefix) } - fn as_trie_backend(&mut self) -> Option<&TrieBackend> { + fn as_trie_backend(&mut self) -> Option< + &TrieBackend + > { self.state.as_trie_backend() } } diff --git a/core/client/db/src/utils.rs b/core/client/db/src/utils.rs index 70f0ff20588bc..2ad2d82590250 100644 --- a/core/client/db/src/utils.rs +++ b/core/client/db/src/utils.rs @@ -39,7 +39,7 @@ use crate::DatabaseSettings; /// Number of columns in the db. Must be the same for both full && light dbs. /// Otherwise RocksDb will fail to open database && check its type. -pub const NUM_COLUMNS: u32 = 10; +pub const NUM_COLUMNS: u32 = 11; /// Meta column. The set of keys in the column is shared by full && light storages. pub const COLUMN_META: Option = Some(0); diff --git a/core/client/src/backend.rs b/core/client/src/backend.rs index 9b6d9ce58fbfe..a9ab9772dd3bd 100644 --- a/core/client/src/backend.rs +++ b/core/client/src/backend.rs @@ -41,7 +41,11 @@ pub(crate) struct ImportSummary { pub(crate) origin: BlockOrigin, pub(crate) header: Block::Header, pub(crate) is_new_best: bool, - pub(crate) storage_changes: Option<(StorageCollection, ChildStorageCollection)>, + pub(crate) storage_changes: Option<( + StorageCollection, + ChildStorageCollection, + StorageCollection, + )>, pub(crate) retracted: Vec, } @@ -220,7 +224,7 @@ pub trait Backend: AuxStore + Send + Sync where /// Returns reference to changes trie storage. fn changes_trie_storage(&self) -> Option<&Self::ChangesTrieStorage>; /// Returns a handle to offchain storage. - fn offchain_storage(&self) -> Option; + fn offchain_storage(&self) -> Option<&Self::OffchainStorage>; /// Returns true if state for given block is available. fn have_state_at(&self, hash: &Block::Hash, _number: NumberFor) -> bool { self.state_at(BlockId::Hash(hash.clone())).is_ok() diff --git a/core/client/src/call_executor.rs b/core/client/src/call_executor.rs index 05a2a8eba1c3e..3cffc89b3bbd8 100644 --- a/core/client/src/call_executor.rs +++ b/core/client/src/call_executor.rs @@ -139,9 +139,12 @@ where /// Execute a call to a contract on top of given trie state, gathering execution proof. /// /// No changes are made. - fn prove_at_trie_state>( + fn prove_at_trie_state< + S: state_machine::TrieBackendStorage, + O: state_machine::KvBackend, + >( &self, - trie_state: &state_machine::TrieBackend, + trie_state: &state_machine::TrieBackend, overlay: &mut OverlayedChanges, method: &str, call_data: &[u8] @@ -371,9 +374,12 @@ where .map_err(Into::into) } - fn prove_at_trie_state>( + fn prove_at_trie_state< + S: state_machine::TrieBackendStorage, + O: state_machine::KvBackend, + >( &self, - trie_state: &state_machine::TrieBackend, + trie_state: &state_machine::TrieBackend, overlay: &mut OverlayedChanges, method: &str, call_data: &[u8] diff --git a/core/client/src/cht.rs b/core/client/src/cht.rs index 63a5b39c26904..ae432ccbb1eb5 100644 --- a/core/client/src/cht.rs +++ b/core/client/src/cht.rs @@ -30,8 +30,10 @@ use trie; use primitives::{H256, convert_hash}; use sr_primitives::traits::{Header as HeaderT, SimpleArithmetic, Zero, One}; use state_machine::backend::InMemory as InMemoryState; +use state_machine::backend::InMemoryTransaction; use state_machine::{MemoryDB, TrieBackend, Backend as StateBackend, prove_read_on_trie_backend, read_proof_check, read_proof_check_on_proving_backend}; +use state_machine::kv_backend::InMemory as KvBackend; use crate::error::{Error as ClientError, Result as ClientResult}; @@ -100,7 +102,10 @@ pub fn build_proof( .into_iter() .map(|(k, v)| (None, k, Some(v))) .collect::>(); - let mut storage = InMemoryState::::default().update(transaction); + let mut storage = InMemoryState::::default().update(InMemoryTransaction { + storage: transaction, + kv: Default::default(), + }); let trie_storage = storage.as_trie_backend() .expect("InMemoryState::as_trie_backend always returns Some; qed"); prove_read_on_trie_backend( @@ -143,7 +148,7 @@ pub fn check_proof_on_proving_backend( local_root: Header::Hash, local_number: Header::Number, remote_hash: Header::Hash, - proving_backend: &TrieBackend, Hasher>, + proving_backend: &TrieBackend, Hasher, KvBackend>, ) -> ClientResult<()> where Header: HeaderT, diff --git a/core/client/src/client.rs b/core/client/src/client.rs index 34334b1388e34..93506d9036d4a 100644 --- a/core/client/src/client.rs +++ b/core/client/src/client.rs @@ -1029,7 +1029,8 @@ impl Client where Option>>, Option<( Vec<(Vec, Option>)>, - Vec<(Vec, Vec<(Vec, Option>)>)> + Vec<(Vec, Vec<(Vec, Option>)>)>, + Vec<(Vec, Option>)>, )> )> where @@ -1082,13 +1083,17 @@ impl Client where overlay.commit_prospective(); - let (top, children) = overlay.into_committed(); + let (top, children, kv) = overlay.into_committed(); let children = children.map(|(sk, it)| (sk, it.collect())).collect(); if import_headers.post().state_root() != &storage_update.1 { return Err(error::Error::InvalidStateRoot); } - Ok((Some(storage_update.0), Some(changes_update), Some((top.collect(), children)))) + Ok(( + Some(storage_update.0), + Some(changes_update), + Some((top.collect(), children, kv.collect())), + )) }, None => Ok((None, None, None)) } diff --git a/core/client/src/genesis.rs b/core/client/src/genesis.rs index 031b2dc0ad7df..a9172199186d7 100644 --- a/core/client/src/genesis.rs +++ b/core/client/src/genesis.rs @@ -162,7 +162,8 @@ mod tests { ).genesis_map(); let genesis_hash = insert_genesis_block(&mut storage); - let backend = InMemory::from(storage); + // TODO EMCH need to put kv in genesis + let backend = InMemory::from((storage.0, storage.1, Default::default())); let (b1data, _b1hash) = block1(genesis_hash, &backend); let mut overlay = OverlayedChanges::default(); @@ -192,7 +193,8 @@ mod tests { ).genesis_map(); let genesis_hash = insert_genesis_block(&mut storage); - let backend = InMemory::from(storage); + // TODO EMCH need to put kv in genesis + let backend = InMemory::from((storage.0, storage.1, Default::default())); let (b1data, _b1hash) = block1(genesis_hash, &backend); let mut overlay = OverlayedChanges::default(); @@ -222,7 +224,8 @@ mod tests { ).genesis_map(); let genesis_hash = insert_genesis_block(&mut storage); - let backend = InMemory::from(storage); + // TODO EMCH need to put kv in genesis + let backend = InMemory::from((storage.0, storage.1, Default::default())); let (b1data, _b1hash) = block1(genesis_hash, &backend); let mut overlay = OverlayedChanges::default(); diff --git a/core/client/src/in_mem.rs b/core/client/src/in_mem.rs index 5c35400d7743c..d1293d84ecd41 100644 --- a/core/client/src/in_mem.rs +++ b/core/client/src/in_mem.rs @@ -511,8 +511,9 @@ where let (root, transaction) = self.old_state.full_storage_root( top.into_iter().map(|(k, v)| (k, Some(v))), - child_delta - ); + child_delta, + None, + ).map_err(|e| error::Error::from(format!("full storage root: {}", e)))?; self.new_state = Some(InMemory::from(transaction)); Ok(root) @@ -686,7 +687,7 @@ where Some(&self.changes_trie_storage) } - fn offchain_storage(&self) -> Option { + fn offchain_storage(&self) -> Option<&Self::OffchainStorage> { None } diff --git a/core/client/src/light/backend.rs b/core/client/src/light/backend.rs index 300d140630d85..ff464b8a5e008 100644 --- a/core/client/src/light/backend.rs +++ b/core/client/src/light/backend.rs @@ -21,6 +21,7 @@ use std::collections::HashMap; use std::sync::Arc; use parking_lot::{RwLock, Mutex}; +use primitives::child_trie::KeySpace; use sr_primitives::{generic::BlockId, Justification, StorageOverlay, ChildrenStorageOverlay}; use state_machine::{Backend as StateBackend, TrieBackend, backend::InMemory as InMemoryState, ChangesTrieTransaction}; use sr_primitives::traits::{Block as BlockT, NumberFor, Zero, Header}; @@ -185,7 +186,7 @@ impl ClientBackend for Backend where None } - fn offchain_storage(&self) -> Option { + fn offchain_storage(&self) -> Option<&Self::OffchainStorage> { None } @@ -290,8 +291,13 @@ where storage.insert(Some(child_key), child_storage); } - let storage_update: InMemoryState = storage.into(); - let (storage_root, _) = storage_update.full_storage_root(::std::iter::empty(), child_delta); + // TODO EMCH need kv init here + let storage_update: InMemoryState = (storage, Default::default()).into(); + let (storage_root, _) = storage_update.full_storage_root( + ::std::iter::empty(), + child_delta, + ::std::iter::empty(), + ).map_err(|e| ClientError::from(format!("full storage root: {}", e)))?; self.storage_update = Some(storage_update); Ok(storage_root) @@ -340,6 +346,7 @@ impl StateBackend for GenesisOrUnavailableState type Error = ClientError; type Transaction = (); type TrieBackendStorage = MemoryDB; + type KvBackend = state_machine::InMemoryKvBackend; fn storage(&self, key: &[u8]) -> ClientResult>> { match *self { @@ -357,6 +364,14 @@ impl StateBackend for GenesisOrUnavailableState } } + fn kv_storage(&self, key: &[u8]) -> ClientResult>> { + match *self { + GenesisOrUnavailableState::Genesis(ref state) => + Ok(state.kv_storage(key).expect(IN_MEMORY_EXPECT_PROOF)), + GenesisOrUnavailableState::Unavailable => Err(ClientError::NotAvailableOnLightClient), + } + } + fn for_keys_with_prefix(&self, prefix: &[u8], action: A) { match *self { GenesisOrUnavailableState::Genesis(ref state) => state.for_keys_with_prefix(prefix, action), @@ -403,19 +418,26 @@ impl StateBackend for GenesisOrUnavailableState } } - fn child_storage_root(&self, key: &[u8], delta: I) -> (Vec, bool, Self::Transaction) + fn child_storage_root(&self, key: &[u8], keyspace: &KeySpace, delta: I) -> (Vec, bool, Self::Transaction) where I: IntoIterator, Option>)> { match *self { GenesisOrUnavailableState::Genesis(ref state) => { - let (root, is_equal, _) = state.child_storage_root(key, delta); + let (root, is_equal, _) = state.child_storage_root(key, keyspace, delta); (root, is_equal, ()) }, GenesisOrUnavailableState::Unavailable => (H::Out::default().as_ref().to_vec(), true, ()), } } + fn kv_transaction(&self, _delta: I) -> Self::Transaction + where + I: IntoIterator, Option>)> + { + () + } + fn pairs(&self) -> Vec<(Vec, Vec)> { match *self { GenesisOrUnavailableState::Genesis(ref state) => state.pairs(), @@ -423,6 +445,27 @@ impl StateBackend for GenesisOrUnavailableState } } + fn children_storage_keys(&self) -> Vec> { + match *self { + GenesisOrUnavailableState::Genesis(ref state) => state.children_storage_keys(), + GenesisOrUnavailableState::Unavailable => Vec::new(), + } + } + + fn child_pairs(&self, child_storage_key: &[u8]) -> Vec<(Vec, Vec)> { + match *self { + GenesisOrUnavailableState::Genesis(ref state) => state.child_pairs(child_storage_key), + GenesisOrUnavailableState::Unavailable => Vec::new(), + } + } + + fn kv_pairs(&self) -> HashMap, Option>> { + match *self { + GenesisOrUnavailableState::Genesis(ref state) => state.kv_pairs(), + GenesisOrUnavailableState::Unavailable => Default::default(), + } + } + fn keys(&self, prefix: &[u8]) -> Vec> { match *self { GenesisOrUnavailableState::Genesis(ref state) => state.keys(prefix), @@ -430,7 +473,9 @@ impl StateBackend for GenesisOrUnavailableState } } - fn as_trie_backend(&mut self) -> Option<&TrieBackend> { + fn as_trie_backend(&mut self) -> Option< + &TrieBackend + > { match self { GenesisOrUnavailableState::Genesis(ref mut state) => state.as_trie_backend(), GenesisOrUnavailableState::Unavailable => None, diff --git a/core/client/src/light/call_executor.rs b/core/client/src/light/call_executor.rs index 65776bcfe08f3..2595f96357050 100644 --- a/core/client/src/light/call_executor.rs +++ b/core/client/src/light/call_executor.rs @@ -173,9 +173,12 @@ impl CallExecutor for Err(ClientError::NotAvailableOnLightClient) } - fn prove_at_trie_state>( + fn prove_at_trie_state< + S: state_machine::TrieBackendStorage, + O: state_machine::KvBackend, + >( &self, - _state: &state_machine::TrieBackend, + _state: &state_machine::TrieBackend, _changes: &mut OverlayedChanges, _method: &str, _call_data: &[u8] @@ -376,9 +379,12 @@ mod tests { unreachable!() } - fn prove_at_trie_state>( + fn prove_at_trie_state< + S: state_machine::TrieBackendStorage, + O: state_machine::KvBackend, + >( &self, - _trie_state: &state_machine::TrieBackend, + _trie_state: &state_machine::TrieBackend, _overlay: &mut OverlayedChanges, _method: &str, _call_data: &[u8] diff --git a/core/client/src/light/fetcher.rs b/core/client/src/light/fetcher.rs index dbf6d41c38f16..027f492767900 100644 --- a/core/client/src/light/fetcher.rs +++ b/core/client/src/light/fetcher.rs @@ -349,8 +349,13 @@ impl> LightDataChecker { return Err(ClientError::InvalidCHTProof.into()); } + // using empty kv as light do not use kv information + // (things being fetch proved and proof currently do not rely on + // kv). + let kv = state_machine::InMemoryKvBackend::default(); + // check proof for single changes trie root - let proving_backend = TrieBackend::new(storage, cht_root); + let proving_backend = TrieBackend::new(storage, cht_root, kv); let remote_changes_trie_root = remote_roots[&block]; cht::check_proof_on_proving_backend::( local_cht_root, diff --git a/core/executor/src/host_interface.rs b/core/executor/src/host_interface.rs index 7c99415f6c752..6ede6d4762e54 100644 --- a/core/executor/src/host_interface.rs +++ b/core/executor/src/host_interface.rs @@ -1126,4 +1126,3 @@ fn deadline_to_timestamp(deadline: u64) -> Option { Some(offchain::Timestamp::from_unix_millis(deadline)) } } - diff --git a/core/executor/src/wasmi_execution.rs b/core/executor/src/wasmi_execution.rs index 0719ff4d404b4..fc5201d8b118b 100644 --- a/core/executor/src/wasmi_execution.rs +++ b/core/executor/src/wasmi_execution.rs @@ -715,7 +715,7 @@ mod tests { b"input".to_vec() => b"Hello world".to_vec(), b"foo".to_vec() => b"bar".to_vec(), b"baz".to_vec() => b"bar".to_vec() - ], map![])); + ], map![], map![])); assert_eq!(ext, expected); } @@ -738,7 +738,7 @@ mod tests { b"aaa".to_vec() => b"1".to_vec(), b"aab".to_vec() => b"2".to_vec(), b"bbb".to_vec() => b"5".to_vec() - ], map![])); + ], map![], map![])); assert_eq!(expected, ext); } diff --git a/core/primitives/src/child_trie.rs b/core/primitives/src/child_trie.rs new file mode 100644 index 0000000000000..065d5bb3a080d --- /dev/null +++ b/core/primitives/src/child_trie.rs @@ -0,0 +1,118 @@ +// Copyright 2017-2019 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate 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. + +// Substrate 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 Substrate. If not, see . + +//! Child trie related primitives + +use codec::{Encode, Decode, Compact}; +use rstd::prelude::*; +use rstd::ptr; +use crate::storage::well_known_keys::CHILD_STORAGE_KEY_PREFIX; +#[cfg(feature = "std")] +pub use impl_serde::serialize as bytes; +use hash_db::Prefix; + +/// `KeySpace` type contains a unique child trie identifier. +/// It is used to isolate a child trie in its backing database. +/// This is done by prefixing its key with this value. +/// +/// A keyspace byte representation must not be the start of another +/// keyspace byte representation (otherwhise conflict may happen). +/// This guaranty is provided by the fact that keyspace is a scale +/// encoded representation. +/// +/// From a `TrieDB` perspective, accessing a child trie content +/// will requires both the child trie root, but also the `KeySpace`. +/// +/// The `KeySpace` of a child trie must be unique for the canonical chain. +/// This unicity is currently guaranted by build from a simple counter. +/// +/// If a child trie was to be moved between two chains, the underlying +/// key value would be all the keyvaluedb prefixed by this keyspace. +/// Moving those key will still need for every content to change keyspace +/// of the origin chain with a new keyspace of a destination chain. +/// Please notice that usage of keyspace on ALL data makes it possible, +/// +/// The same thing is true for a child trie deletion, there is no need to +/// remove all historic of state child trie keypair from a multiple TrieDB +/// perspective, but simply all keyvaluedb content with a key starting with +/// this keyspace. +pub type KeySpace = Vec; + + +/// Keyspace to use for the parent trie key. +pub const NO_CHILD_KEYSPACE: [u8;1] = [0]; + +/// Prefix for keyspace in key value storage. +/// TODO EMCH consider using its own column +const KEYSPACE_PREFIX: &'static [u8;9] = b"ks_prefix"; + +/// Address of local keyspace counter. For allowing +/// archive node with prunning, this counter should be +/// global (shared between all block evaluation: so +/// a mutex protected value that shall be initialized from +/// aux and updated every time a block is written to db), but +/// with current storage that allows only canonical +/// counter in key value storage works. +/// TODO EMCH consider directly using a global counter. +pub const KEYSPACE_COUNTER: &'static [u8;10] = b"ks_counter"; + +/// Produce a new keyspace from current state counter. +/// TODO EMCH remove if using column +pub fn prefixed_keyspace_kv(key: &[u8]) -> Vec { + let mut resu = Vec::with_capacity(KEYSPACE_PREFIX.len() + key.len()); + resu.extend_from_slice(&KEYSPACE_PREFIX[..]); + resu.extend_from_slice(key); + resu +} + +/// Produce a new keyspace from current state counter. +pub fn produce_keyspace(child_counter: u128) -> Vec { + codec::Encode::encode(&Compact(child_counter)) +} + +/// Decode keyspace to original counter value. +pub fn reverse_keyspace(keyspace: &[u8]) -> Result { + >::decode(&mut &keyspace[..]).map(|c| c.0) +} + +// see FIXME #2741 for removal of this allocation on every operation. +// Simplest would be to put an additional optional field in prefix. +/// Utility function used for merging `KeySpace` data and `prefix` data +/// before calling key value database primitives. +pub fn keyspace_as_prefix_alloc(ks: Option<&KeySpace>, prefix: Prefix) -> (Vec, Option) { + let ks = ks.map(|ks| ks.as_slice()).unwrap_or(&NO_CHILD_KEYSPACE[..]); + let mut result = rstd::vec![0; ks.len() + prefix.0.len()]; + result[..ks.len()].copy_from_slice(ks); + result[ks.len()..].copy_from_slice(prefix.0); + (result, prefix.1) +} + +#[test] +fn encode_empty_prefix() { + let empty = produce_keyspace(0); + + assert_eq!(&NO_CHILD_KEYSPACE[..], &empty[..]); +} + +#[test] +fn keyspace_codec() { + let sample: [u128; 3] = [0, 1, 1_000_000]; + for s in sample.iter() { + let keyspace = produce_keyspace(*s); + let dec_keyspace = reverse_keyspace(keyspace.as_slice()).unwrap(); + assert_eq!(*s, dec_keyspace); + } +} diff --git a/core/primitives/src/lib.rs b/core/primitives/src/lib.rs index b1ce6e2f9758a..c409a28d7f7a8 100644 --- a/core/primitives/src/lib.rs +++ b/core/primitives/src/lib.rs @@ -62,6 +62,7 @@ pub mod offchain; pub mod sandbox; pub mod uint; mod changes_trie; +pub mod child_trie; #[cfg(feature = "std")] pub mod traits; pub mod testing; diff --git a/core/service/src/lib.rs b/core/service/src/lib.rs index fccf04d8a1345..56bcfee8370da 100644 --- a/core/service/src/lib.rs +++ b/core/service/src/lib.rs @@ -215,7 +215,7 @@ macro_rules! new_impl { let offchain_storage = backend.offchain_storage(); let offchain_workers = match ($config.offchain_worker, offchain_storage) { (true, Some(db)) => { - Some(Arc::new(offchain::OffchainWorkers::new(client.clone(), db))) + Some(Arc::new(offchain::OffchainWorkers::new(client.clone(), db.clone()))) }, (true, None) => { log::warn!("Offchain workers disabled, due to lack of offchain storage support in backend."); diff --git a/core/state-db/Cargo.toml b/core/state-db/Cargo.toml index d271a0e179d6d..d096247ddbdf6 100644 --- a/core/state-db/Cargo.toml +++ b/core/state-db/Cargo.toml @@ -9,6 +9,8 @@ parking_lot = "0.9.0" log = "0.4.8" primitives = { package = "substrate-primitives", path = "../../core/primitives" } codec = { package = "parity-scale-codec", version = "1.0.0", features = ["derive"] } +historied-data = { path = "../../core/utils/historied-data" } [dev-dependencies] env_logger = "0.7.0" +historied-data = { path = "../../core/utils/historied-data", features = ["test"] } diff --git a/core/state-db/src/branch.rs b/core/state-db/src/branch.rs new file mode 100644 index 0000000000000..b32b830b36108 --- /dev/null +++ b/core/state-db/src/branch.rs @@ -0,0 +1,494 @@ + +// Copyright 2018-2019 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate 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. + +// Substrate 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 Substrate. If not, see . + +//! Helper for managing the set of indexed available branches. + +use std::collections::{BTreeMap}; +use historied_data::tree::{BranchesStateTrait, BranchStatesRef, BranchStateRef}; + +#[derive(Clone, Default, Debug)] +/// State needed for query updates. +/// That is a subset of the full branch ranges set. +/// +/// Values are ordered by branch_ix, +/// and only a logic branch path should be present. +/// +/// Note that an alternative could be a pointer to the full state +/// a branch index corresponding to the leaf for the fork. +/// Here we use an in memory copy of the path because it seems +/// to fit query at a given state with multiple operations +/// (block processing), that way we iterate on a vec rather than +/// hoping over linked branches. +pub struct BranchRanges(Vec); + +impl<'a> BranchesStateTrait for &'a BranchRanges { + type Branch = &'a BranchStateRef; + type Iter = BranchRangesIter<'a>; + + fn get_branch(self, i: u64) -> Option { + for (b, bi) in self.iter() { + if bi == i { + return Some(b); + } else if bi < i { + break; + } + } + None + } + + fn last_index(self) -> u64 { + let l = self.0.len(); + if l > 0 { + self.0[l - 1].branch_index + } else { + 0 + } + } + + fn iter(self) -> Self::Iter { + BranchRangesIter(self, self.0.len()) + } + +} + +/// Iterator, contains index of last inner struct. +pub struct BranchRangesIter<'a>(&'a BranchRanges, usize); + +impl<'a> Iterator for BranchRangesIter<'a> { + type Item = (&'a BranchStateRef, u64); + + fn next(&mut self) -> Option { + if self.1 > 0 { + Some(( + &(self.0).0[self.1 - 1].state, + (self.0).0[self.1 - 1].branch_index, + )) + } else { + None + } + } +} + +/// current branches range definition, indexed by branch +/// numbers. +/// +/// New branches index are using `last_index`. +/// +/// Also acts as a cache, storage can store +/// unknown db value as `None`. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct RangeSet { + storage: BTreeMap, + last_index: u64, + /// treshold for possible node value, correspond + /// roughly to last cannonical block branch index. + treshold: u64, +} + +const DEFAULT_START_TRESHOLD: u64 = 1; + +impl Default for RangeSet { + fn default() -> Self { + RangeSet { + storage: BTreeMap::new(), + last_index: 0, + treshold: DEFAULT_START_TRESHOLD, + } + } +} + +impl RangeSet { + + #[cfg(test)] + /// test access to treshold. + pub fn range_treshold(&self) -> u64 { + self.treshold + } + + /// Iterator over all its range sets. + pub fn reverse_iter_ranges(&self) -> impl Iterator { + self.storage.iter().rev().map(|(k, v)| (&v.state, *k)) + } + + /// For a a given branch, return its path if the tree of ranges. + /// If block number is undefined, return the path up to the latest block + /// of the branch index. + /// + /// Note that this method is a bit costy and a caching could be use. + /// Similarily in some case using an iterator instead could make sense. + pub fn branch_ranges( + &self, + mut branch_index: u64, + block: Option, + ) -> BranchRanges { + let mut result = Vec::new(); + if branch_index < self.treshold { + return BranchRanges(result); + } + let mut previous_start = block.map(|b| b + 1).unwrap_or(u64::max_value()); + loop { + if let Some(BranchStates{ + state, + parent_branch_index, + .. + }) = self.storage.get(&branch_index) { + // TODO EMCH consider vecdeque ?? + let state = if state.end > previous_start { + if state.start >= previous_start { + // empty branch stop here + break; + } + BranchStateRef { + start: state.start, + end: previous_start, + } + } else { state.clone() }; + + previous_start = state.start; + + result.insert(0, BranchStatesRef { + state, + branch_index, + }); + + branch_index = *parent_branch_index; + if branch_index < self.treshold { + break; + } + } else { + debug_assert!(false, "inconsistent branch range cache"); + break; + } + } + BranchRanges(result) + } + + /// Return anchor index for this branch history: + /// - same index as input if branch is not empty + /// - parent index if branch is empty + pub fn drop_state( + &mut self, + branch_index: u64, + ) -> u64 { + let mut do_remove = None; + match self.storage.get_mut(&branch_index) { + Some(branch_state) => { + if let Some(drop_index) = branch_state.drop_state() { + if drop_index == 0 { + do_remove = Some(branch_state.parent_branch_index); + } else { + branch_state.can_append = false; + } + } else { + // deleted branch, do nothing + } + }, + None => (), + } + + if let Some(parent_index) = do_remove { + self.storage.remove(&branch_index); + parent_index + } else { + branch_index + } + } + + /// Return anchor index for this branch history: + /// - same index as input if the branch was modifiable + /// - new index in case of branch range creation + pub fn add_state( + &mut self, + branch_index: u64, + number: u64, + ) -> u64 { + let mut create_new = false; + if branch_index == 0 || branch_index < self.treshold { + create_new = true; + } else { + let branch_state = self.storage.get_mut(&branch_index) + .expect("Inconsistent state on new block"); + if branch_state.can_append && branch_state.can_add(number) { + branch_state.add_state(); + } else { + create_new = true; + } + } + + if create_new { + self.last_index += 1; + + let state = BranchStates::new(number, branch_index); + self.storage.insert(self.last_index, state); + self.last_index + } else { + branch_index + } + } + + /// Get the branch reference for a given branch index if it exists. + pub fn state_ref(&self, branch_index: u64) -> Option { + self.storage.get(&branch_index).map(|v| v.state_ref()) + .map(|state| BranchStatesRef { + branch_index, + state, + }) + } + + /// Updates the range set on import (defined by its number in branch (block number). + /// Returns the corresponding branch ranges and the branch index for this block. + pub fn import( + &mut self, + number: u64, + parent_branch_index: u64, + parent_branch_range: Option, + ) -> (BranchRanges, u64) { + + if let Some(mut branch_ranges) = parent_branch_range { + let anchor_index = self.add_state(parent_branch_index, number); + if anchor_index == parent_branch_index { + branch_ranges.0.pop(); + } + branch_ranges.0.push(self.state_ref(anchor_index).expect("added just above")); + (branch_ranges, anchor_index) + } else { + let anchor_index = self.add_state(parent_branch_index, number); + ( + BranchRanges(vec![self.state_ref(anchor_index).expect("added just above")]), + anchor_index, + ) + } + } + + /// Apply a post finalize update of treshold: requires + /// that values before new treshold are all clear (since + /// we stop maintaining branches for them). + pub fn update_finalize_treshold( + &mut self, + branch_index: u64, + linear_index: u64, + full: bool, + ) { + + if branch_index < self.treshold { + return; + } + // we do not finalize current branch cause it + // can contains other blocks + self.treshold = branch_index; + if !full { + // remove cached value under treshold only + let new_storage = self.storage.split_off(&(self.treshold)); + self.storage = new_storage; + // set this branch as non appendable (ensure it get gc + // at some point even if there is no branching). + // Also if gc and treshold happen after this call, + // ensure this branch can get remove. + self.storage.get_mut(&branch_index) + .map(|state| state.can_append = false); + } else { + let new_storage = self.storage.split_off(&(self.treshold)); + self.storage = new_storage; + self.finalize_full(branch_index, linear_index); + }; + } + + /// Apply a post finalize without considering treshold. + pub fn finalize_full( + &mut self, + branch_index: u64, + linear_index: u64, + ) { + if let Some(state) = self.storage.remove(&branch_index) { + let mut child_anchor: BranchStates = state.clone(); + child_anchor.state.start = linear_index; + let old_storage = std::mem::replace(&mut self.storage, BTreeMap::new()); + // insert anchor + self.storage.insert(branch_index, child_anchor.clone()); + for (index, state) in old_storage.into_iter() { + // ordered property of branch index allows to skip in depth branch search + if let Some(parent_state) = self.storage.get(&state.parent_branch_index) { + // first state is splitted + // use property that start of a branch - 1 is origin of parent + // this is obvious for parent is first branch (truncated), but also + // true for case where we revert some state. + let linear_index_parent = state.state.start - 1; + if linear_index_parent < parent_state.state.end + && linear_index_parent >= parent_state.state.start { + self.storage.insert(index, state); + } + } + } + // remove anchor block + child_anchor.state.start += 1; + if child_anchor.state.start < child_anchor.state.end { + self.storage.insert(branch_index, child_anchor.clone()); + } else { + self.storage.remove(&branch_index); + } + } + + } + + /// Revert some ranges, without any way to revert. + /// Returning ranges for the parent index. + pub fn revert(&mut self, branch_ix: u64) -> u64 { + if branch_ix != 0 { + self.drop_state(branch_ix) + } else { + 0 + } + } + + #[cfg(test)] + pub fn contains_range(&self, branch_index: u64, size: u64) -> bool { + if let Some(s) = self.storage.get(&branch_index) { + (s.state_ref().end - s.state_ref().start) == size + } else { + false + } + } + +} + +/// Stored states for a branch, it contains branch reference information, +/// structural information (index of parent branch) and fork tree building +/// information (is branch appendable). +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct BranchStates { + state: BranchStateRef, + can_append: bool, + parent_branch_index: u64, +} + +impl Default for BranchStates { + // initialize with one element + fn default() -> Self { + Self::new(0, 0) + } +} + +impl BranchStates { + pub fn new(offset: u64, parent_branch_index: u64) -> Self { + let offset = offset as u64; + BranchStates { + state: BranchStateRef { + start: offset, + end: offset + 1, + }, + can_append: true, + parent_branch_index, + } + } + + pub fn state_ref(&self) -> BranchStateRef { + self.state.clone() + } + + pub fn has_deleted_index(&self) -> bool { + !self.can_append + } + + pub fn add_state(&mut self) -> bool { + if !self.has_deleted_index() { + self.state.end += 1; + true + } else { + false + } + } + + /// return possible dropped state + pub fn drop_state(&mut self) -> Option { + if self.state.end - self.state.start > 0 { + self.state.end -= 1; + Some(self.state.end) + } else { + None + } + } + + /// Return true if you can add this index. + pub fn can_add(&self, index: u64) -> bool { + index == self.state.end + } + +} + +#[cfg(test)] +mod test { + use super::*; + + // set: + // 0 + // |> 1: [10,1] + // |> 2: [10,2] [11,1] + // |> 3: [11,2] [12, 2] + // | > 5: [12,1] + // |> 4: [11,123] [12,1] + // full finalize: 3 + // 0 + // |> 2: [10,2] + // |> 3: [11,2] + fn build_finalize_set() -> (RangeSet, (u64, u64)) { + let mut set = RangeSet::default(); + set.import(10, 0, None); + let (b1, anchor_1) = set.import(10, 0, None); + set.import(11, anchor_1, Some(b1.clone())); + let (bf, finalize) = set.import(11, anchor_1, Some(b1.clone())); + let (b2, anchor_2) = set.import(11, anchor_1, Some(b1)); + set.import(12, anchor_2, Some(b2)); + set.import(12, finalize, Some(bf.clone())); + set.import(12, finalize, Some(bf)); + + (set, (finalize, 11)) + } + + #[test] + fn branch_range_finalize() { + let with_full_finalize = | + full: bool, + ranges_valid: &[(u64, u64)], + not_ranges: &[(u64, u64)], + | { + let (mut ranges, finalize) = build_finalize_set(); + + let _ = ranges.update_finalize_treshold(finalize.0, finalize.1, full); + + assert!(ranges.range_treshold() == 3); + + for (range, size) in ranges_valid { + assert!(ranges.contains_range(*range, *size), "contains {:?}", range); + } + for (range, size) in not_ranges { + assert!(!ranges.contains_range(*range, *size), "contains {:?}", range); + } + + }; + + with_full_finalize(false, + &[(3, 2), (4, 2), (5, 1)][..], + &[(1, 1), (2, 2)][..], + ); + with_full_finalize(true, + // (3, 1) because finalized block is removed + &[(3, 1), (5, 1)][..], + &[(1, 1), (2, 2), (4, 2)][..], + ); + } + +} diff --git a/core/state-db/src/lib.rs b/core/state-db/src/lib.rs index 81772e554bc57..c74b1027432d8 100644 --- a/core/state-db/src/lib.rs +++ b/core/state-db/src/lib.rs @@ -31,19 +31,24 @@ mod noncanonical; mod pruning; +mod branch; #[cfg(test)] mod test; use std::fmt; use parking_lot::RwLock; use codec::Codec; -use std::collections::{HashMap, hash_map::Entry}; +use std::collections::{HashSet, HashMap, hash_map::Entry}; use noncanonical::NonCanonicalOverlay; use pruning::RefWindow; use log::trace; +pub use branch::BranchRanges; /// Database value type. pub type DBValue = Vec; +/// Kv storage key definition. +pub type KvKey = Vec; + /// Basic set of requirements for the Block hash and node key types. pub trait Hash: Send + Sync + Sized + Eq + PartialEq + Clone + Default + fmt::Debug + Codec + std::hash::Hash + 'static {} impl Hash for T {} @@ -65,6 +70,23 @@ pub trait NodeDb { fn get(&self, key: &Self::Key) -> Result, Self::Error>; } +/// Backend database trait. Read-only. +/// +/// State parameter indicate where to query kv storage, +/// this is a Block hash, as branch index are +/// build on load, with a persistence for this +/// data it would be more direct to use a +/// tuple of block number and branchindex. +pub trait KvDb { + type Error: fmt::Debug; + + /// Get state trie node. + fn get_kv(&self, key: &[u8], state: &State) -> Result, Self::Error>; + + /// Get all pairs of key at current state. + fn get_kv_pairs(&self, state: &State) -> Vec<(KvKey, DBValue)>; +} + /// Error type. pub enum Error { /// Database backend error. @@ -112,14 +134,33 @@ pub struct ChangeSet { pub deleted: Vec, } +/// A set of kv state values changes. +/// +/// This assumes that we only commit block per block (otherwhise +/// we need to include block number value here and +/// implement a more efficient batching update). +pub type KvChangeSet = Vec<(H, Option)>; + +/// Info for pruning kv: a last prune index and keys to prune. +/// Is set to none when not initialized. +pub type KvChangeSetPrune = Option<(u64, HashSet)>; + +/// Info for reverting kv of a canonical backend. +pub type KvChangeSetRevert = u64; + +/// Commit set on canonical operation. +pub type CommitSetCanonical = (CommitSet, KvChangeSetPrune); /// A set of changes to the backing database. +/// It only contain a single block change set. #[derive(Default, Debug, Clone)] pub struct CommitSet { /// State node changes. pub data: ChangeSet, /// Metadata changes. pub meta: ChangeSet>, + /// Kv data changes. + pub kv: KvChangeSet, } /// Pruning constraints. If none are specified pruning is @@ -200,7 +241,15 @@ impl StateDbSync { }) } - pub fn insert_block(&mut self, hash: &BlockHash, number: u64, parent_hash: &BlockHash, mut changeset: ChangeSet) -> Result, Error> { + pub fn insert_block( + &mut self, + hash: &BlockHash, + number: u64, + parent_hash: &BlockHash, + mut changeset: ChangeSet, + kv_changeset: KvChangeSet, + ) -> Result, Error> { + match self.mode { PruningMode::ArchiveAll => { changeset.deleted.clear(); @@ -208,29 +257,39 @@ impl StateDbSync { Ok(CommitSet { data: changeset, meta: Default::default(), + // TODO EMCH no current support for archive all, + // it would require managing branch index at + // client level (was implemented in another branch: + // client-db-branch-ix) + // and use and ordered tuple (branchix, blocknumber) + // index to run. + kv: kv_changeset, }) }, PruningMode::Constrained(_) | PruningMode::ArchiveCanonical => { - self.non_canonical.insert(hash, number, parent_hash, changeset) + self.non_canonical.insert(hash, number, parent_hash, changeset, kv_changeset) } } } - pub fn canonicalize_block(&mut self, hash: &BlockHash) -> Result, Error> { - let mut commit = CommitSet::default(); + pub fn canonicalize_block( + &mut self, + hash: &BlockHash, + ) -> Result, Error> { + let mut commit = (CommitSet::default(), None); if self.mode == PruningMode::ArchiveAll { return Ok(commit) } - match self.non_canonical.canonicalize(&hash, &mut commit) { + match self.non_canonical.canonicalize(&hash, &mut commit.0) { Ok(()) => { if self.mode == PruningMode::ArchiveCanonical { - commit.data.deleted.clear(); + commit.0.data.deleted.clear(); } } Err(e) => return Err(e), }; if let Some(ref mut pruning) = self.pruning { - pruning.note_canonical(&hash, &mut commit); + pruning.note_canonical(&hash, &mut commit.0); } self.prune(&mut commit); Ok(commit) @@ -253,7 +312,7 @@ impl StateDbSync { } } - fn prune(&mut self, commit: &mut CommitSet) { + fn prune(&mut self, commit: &mut CommitSetCanonical) { if let (&mut Some(ref mut pruning), &PruningMode::Constrained(ref constraints)) = (&mut self.pruning, &self.mode) { loop { if pruning.window_size() <= constraints.max_blocks.unwrap_or(0) as u64 { @@ -287,6 +346,12 @@ impl StateDbSync { } } + /// For a a given block return its path in the block tree. + /// Note that using `number` is use to skip a query to block number for hash. + pub fn get_branch_range(&self, hash: &BlockHash, number: u64) -> Option { + self.non_canonical.get_branch_range(hash, number) + } + pub fn pin(&mut self, hash: &BlockHash) -> Result<(), PinError> { match self.mode { PruningMode::ArchiveAll => Ok(()), @@ -333,6 +398,38 @@ impl StateDbSync { db.get(key.as_ref()).map_err(|e| Error::Db(e)) } + pub fn get_kv>( + &self, + key: &[u8], + state: &(BranchRanges, u64), + db: &D, + ) -> Result, Error> { + if let Some(value) = self.non_canonical.get_kv(key, &state.0) { + return Ok(value.clone()); + } + db.get_kv(key, &state.1).map_err(|e| Error::Db(e)) + } + + pub fn get_kv_pairs>( + &self, + state: &(BranchRanges, u64), + db: &D, + ) -> Vec<(KvKey, DBValue)> { + let mut result = Vec::new(); + let mut filter = HashSet::new(); + for (k, o) in self.non_canonical.kv_iter(&state.0) { + if let Some(v) = o.as_ref() { + result.push((k.clone(), v.clone())); + } + filter.insert(k.clone()); + } + result.extend( + db.get_kv_pairs(&state.1).into_iter() + .filter(|kv| !filter.contains(&kv.0)) + ); + result + } + pub fn apply_pending(&mut self) { self.non_canonical.apply_pending(); if let Some(pruning) = &mut self.pruning { @@ -370,15 +467,31 @@ impl StateDb { } /// Add a new non-canonical block. - pub fn insert_block(&self, hash: &BlockHash, number: u64, parent_hash: &BlockHash, changeset: ChangeSet) -> Result, Error> { - self.db.write().insert_block(hash, number, parent_hash, changeset) + pub fn insert_block( + &self, + hash: &BlockHash, + number: u64, + parent_hash: &BlockHash, + changeset: ChangeSet, + kv_changeset: KvChangeSet, + ) -> Result, Error> { + self.db.write().insert_block(hash, number, parent_hash, changeset, kv_changeset) } /// Finalize a previously inserted block. - pub fn canonicalize_block(&self, hash: &BlockHash) -> Result, Error> { + pub fn canonicalize_block( + &self, + hash: &BlockHash, + ) -> Result, Error> { self.db.write().canonicalize_block(hash) } + /// For a a given block return its path in the block tree. + /// Note that using `number` is use to skip a query to block number for hash. + pub fn get_branch_range(&self, hash: &BlockHash, number: u64) -> Option { + self.db.read().get_branch_range(hash, number) + } + /// Prevents pruning of specified block and its descendants. pub fn pin(&self, hash: &BlockHash) -> Result<(), PinError> { self.db.write().pin(hash) @@ -396,6 +509,25 @@ impl StateDb { self.db.read().get(key, db) } + /// Get a value from non-canonical/pruning overlay or the backing DB. + pub fn get_kv>( + &self, + key: &[u8], + state: &(BranchRanges, u64), + db: &D, + ) -> Result, Error> { + self.db.read().get_kv(key, state, db) + } + + /// Get pairs values kv. + pub fn get_kv_pairs>( + &self, + state: &(BranchRanges, u64), + db: &D, + ) -> Vec<(KvKey, DBValue)> { + self.db.read().get_kv_pairs(state, db) + } + /// Revert all non-canonical blocks with the best block number. /// Returns a database commit or `None` if not possible. /// For archive an empty commit set is returned. @@ -429,10 +561,11 @@ mod tests { use std::io; use primitives::H256; use crate::{StateDb, PruningMode, Constraints}; - use crate::test::{make_db, make_changeset, TestDb}; + use crate::test::{make_db, make_changeset, make_kv_changeset, TestDb}; fn make_test_db(settings: PruningMode) -> (TestDb, StateDb) { let mut db = make_db(&[91, 921, 922, 93, 94]); + db.initialize_kv(&[81, 821, 822, 83, 84]); let state_db = StateDb::new(settings, &db).unwrap(); db.commit( @@ -442,6 +575,7 @@ mod tests { 1, &H256::from_low_u64_be(0), make_changeset(&[1], &[91]), + make_kv_changeset(&[1], &[81]), ) .unwrap(), ); @@ -452,6 +586,7 @@ mod tests { 2, &H256::from_low_u64_be(1), make_changeset(&[21], &[921, 1]), + make_kv_changeset(&[21], &[821, 1]), ) .unwrap(), ); @@ -462,6 +597,7 @@ mod tests { 2, &H256::from_low_u64_be(1), make_changeset(&[22], &[922]), + make_kv_changeset(&[22], &[822]), ) .unwrap(), ); @@ -472,11 +608,15 @@ mod tests { 3, &H256::from_low_u64_be(21), make_changeset(&[3], &[93]), + make_kv_changeset(&[3], &[83]), ) .unwrap(), ); state_db.apply_pending(); - db.commit(&state_db.canonicalize_block::(&H256::from_low_u64_be(1)).unwrap()); + db.commit_canonical( + &state_db.canonicalize_block::(&H256::from_low_u64_be(1)) + .unwrap() + ); state_db.apply_pending(); db.commit( &state_db @@ -485,13 +625,20 @@ mod tests { 4, &H256::from_low_u64_be(3), make_changeset(&[4], &[94]), + make_kv_changeset(&[4], &[84]), ) .unwrap(), ); state_db.apply_pending(); - db.commit(&state_db.canonicalize_block::(&H256::from_low_u64_be(21)).unwrap()); + db.commit_canonical( + &state_db.canonicalize_block::(&H256::from_low_u64_be(21)) + .unwrap() + ); state_db.apply_pending(); - db.commit(&state_db.canonicalize_block::(&H256::from_low_u64_be(3)).unwrap()); + db.commit_canonical( + &state_db.canonicalize_block::(&H256::from_low_u64_be(3)) + .unwrap() + ); state_db.apply_pending(); (db, state_db) @@ -501,6 +648,10 @@ mod tests { fn full_archive_keeps_everything() { let (db, sdb) = make_test_db(PruningMode::ArchiveAll); assert!(db.data_eq(&make_db(&[1, 21, 22, 3, 4, 91, 921, 922, 93, 94]))); + + // TODO EMCH implement archive all support for test db and complete this + // test for kv store. + assert!(!sdb.is_pruned(&H256::from_low_u64_be(0), 0)); } @@ -508,6 +659,11 @@ mod tests { fn canonical_archive_keeps_canonical() { let (db, _) = make_test_db(PruningMode::ArchiveCanonical); assert!(db.data_eq(&make_db(&[1, 21, 3, 91, 921, 922, 93, 94]))); + assert!(db.kv_eq_at(&[81, 821, 822, 83, 84], Some(0))); + assert!(db.kv_eq_at(&[1, 821, 822, 83, 84], Some(1))); + assert!(db.kv_eq_at(&[21, 822, 83, 84], Some(2))); + assert!(db.kv_eq_at(&[3, 21, 822, 84], Some(3))); + assert!(db.kv_eq(&[3, 21, 822, 84])); } #[test] @@ -517,6 +673,11 @@ mod tests { max_mem: None, })); assert!(db.data_eq(&make_db(&[21, 3, 922, 94]))); + assert!(db.kv_eq_at(&[822, 84], Some(0))); + assert!(db.kv_eq_at(&[822, 84], Some(1))); + assert!(db.kv_eq_at(&[21, 822, 84], Some(2))); + assert!(db.kv_eq_at(&[3, 21, 822, 84], Some(3))); + assert!(db.kv_eq(&[3, 21, 822, 84])); } #[test] @@ -530,6 +691,11 @@ mod tests { assert!(sdb.is_pruned(&H256::from_low_u64_be(21), 2)); assert!(sdb.is_pruned(&H256::from_low_u64_be(22), 2)); assert!(db.data_eq(&make_db(&[21, 3, 922, 93, 94]))); + assert!(db.kv_eq_at(&[822, 83, 84], Some(0))); + assert!(db.kv_eq_at(&[822, 83, 84], Some(1))); + assert!(db.kv_eq_at(&[21, 822, 83, 84], Some(2))); + assert!(db.kv_eq_at(&[3, 21, 822, 84], Some(3))); + assert!(db.kv_eq(&[3, 21, 822, 84])); } #[test] @@ -543,5 +709,10 @@ mod tests { assert!(!sdb.is_pruned(&H256::from_low_u64_be(21), 2)); assert!(sdb.is_pruned(&H256::from_low_u64_be(22), 2)); assert!(db.data_eq(&make_db(&[1, 21, 3, 921, 922, 93, 94]))); + assert!(db.kv_eq_at(&[821, 822, 83, 84], Some(0))); + assert!(db.kv_eq_at(&[1, 821, 822, 83, 84], Some(1))); + assert!(db.kv_eq_at(&[21, 822, 83, 84], Some(2))); + assert!(db.kv_eq_at(&[3, 21, 822, 84], Some(3))); + assert!(db.kv_eq(&[3, 21, 822, 84])); } } diff --git a/core/state-db/src/noncanonical.rs b/core/state-db/src/noncanonical.rs index 58715715ccdd2..bd1b44c922b19 100644 --- a/core/state-db/src/noncanonical.rs +++ b/core/state-db/src/noncanonical.rs @@ -21,23 +21,109 @@ //! `revert_pending` use std::fmt; -use std::collections::{HashMap, VecDeque, hash_map::Entry}; -use super::{Error, DBValue, ChangeSet, CommitSet, MetaDb, Hash, to_meta_key}; +use std::collections::{HashMap, VecDeque, hash_map::Entry, HashSet}; +use super::{ + Error, DBValue, ChangeSet, KvChangeSet, CommitSet, MetaDb, Hash, + to_meta_key, KvKey, +}; use codec::{Encode, Decode}; use log::trace; +use crate::branch::{RangeSet, BranchRanges}; +use historied_data::tree::History; +use primitives::child_trie::KeySpace; const NON_CANONICAL_JOURNAL: &[u8] = b"noncanonical_journal"; +const NON_CANONICAL_OFFSTATE_JOURNAL: &[u8] = b"kv_noncanonical_journal"; const LAST_CANONICAL: &[u8] = b"last_canonical"; +type BranchIndex = u64; + +type BlockNumber = u64; + +type GcIndex = u64; + /// See module documentation. pub struct NonCanonicalOverlay { - last_canonicalized: Option<(BlockHash, u64)>, + last_canonicalized: Option<(BlockHash, BlockNumber)>, levels: VecDeque>>, - parents: HashMap, + parents: HashMap, pending_canonicalizations: Vec, pending_insertions: Vec, values: HashMap, //ref counted - pinned: HashMap>, //would be deleted but kept around because block is pinned + branches: RangeSet, + kv_values: HashMap>>, + /// second value is kv pinned index: used in order to determine if the pinned + /// thread should block garbage collection. + pinned: HashMap, GcIndex)>, + kv_gc: KvPendingGC, +} + +#[derive(Default)] +/// kv gc only happen when all pinned threads that where +/// running at the time of cannonicalisation are finished. +struct KvPendingGC { + /// Each gc call uses a new index. + gc_last_index: GcIndex, + /// Keep trace of last cannonicalization branch index height. + /// All data in state are added after this value (branch is + /// set as non modifiable on canonicalisation). + pending_canonicalisation_query: Option, + /// keys to gc that got their journal removed. + keys_pending_gc: HashSet, + /// store keys while waiting for a gc. + next_keys_pending_gc: HashSet, +} + +impl KvPendingGC { + fn set_pending_gc(&mut self, branch_index: u64) { + self.gc_last_index += 1; + if self.pending_canonicalisation_query.is_some() { + self.keys_pending_gc.extend(self.next_keys_pending_gc.drain()); + } + self.pending_canonicalisation_query = Some(branch_index); + } + + fn try_gc( + &mut self, + kv_values: &mut HashMap>>, + branches: &RangeSet, + pinned: &HashMap, + ) { + if let Some(pending) = self.pending_canonicalisation_query { + if pending < self.min_pinned_index(pinned) { + + // TODO EMCH double get is rather inefficient, see if it is possible + // to reuse the hash of the hashset into the hashmap + for key in self.keys_pending_gc.drain() { + match kv_values.get_mut(&key).map(|historied_value| { + historied_value.gc(branches.reverse_iter_ranges()) + }) { + Some(historied_data::PruneResult::Cleared) => { + let _ = kv_values.remove(&key); + }, + _ => (), + } + } + + self.pending_canonicalisation_query = None; + } + } + } + + fn min_pinned_index( + &self, + pinned: &HashMap, + ) -> u64 { + let mut min = u64::max_value(); + // global min + for (_, (_, index)) in pinned.iter() { + if *index < min { + min = *index; + } + } + min + } + } #[derive(Encode, Decode)] @@ -48,16 +134,31 @@ struct JournalRecord { deleted: Vec, } -fn to_journal_key(block: u64, index: u64) -> Vec { +#[derive(Encode, Decode)] +struct KvJournalRecord { + inserted: Vec<(KvKey, DBValue)>, + deleted: Vec, + // placeholder for deleted keyspace + keyspace_deleted: Vec, +} + +fn to_journal_key(block: BlockNumber, index: u64) -> Vec { to_meta_key(NON_CANONICAL_JOURNAL, &(block, index)) } +fn to_kv_journal_key(block: BlockNumber, index: u64) -> Vec { + to_meta_key(NON_CANONICAL_OFFSTATE_JOURNAL, &(block, index)) +} + #[cfg_attr(test, derive(PartialEq, Debug))] struct BlockOverlay { hash: BlockHash, journal_key: Vec, + kv_journal_key: Vec, inserted: Vec, deleted: Vec, + kv_inserted: Vec, + kv_deleted: Vec, } fn insert_values(values: &mut HashMap, inserted: Vec<(Key, DBValue)>) { @@ -68,6 +169,22 @@ fn insert_values(values: &mut HashMap, inserted: } } +fn insert_kv_values( + state: &BranchRanges, + values: &mut HashMap>>, + inserted: Vec<(Key, DBValue)>, + deleted: Vec, +) { + for (k, v) in inserted { + let entry = values.entry(k).or_insert_with(|| Default::default()); + entry.set(state, Some(v)); + } + for k in deleted { + let entry = values.entry(k).or_insert_with(|| Default::default()); + entry.set(state, None); + } +} + fn discard_values( values: &mut HashMap, inserted: Vec, @@ -92,22 +209,43 @@ fn discard_values( } } +fn discard_kv_values( + inserted: Vec, + deleted: Vec, + into: &mut KvPendingGC, +) { + let into = if into.pending_canonicalisation_query.is_some() { + &mut into.next_keys_pending_gc + } else { + &mut into.keys_pending_gc + }; + into.extend(inserted); + into.extend(deleted); +} + fn discard_descendants( levels: &mut VecDeque>>, mut values: &mut HashMap, index: usize, - parents: &mut HashMap, - pinned: &mut HashMap>, + parents: &mut HashMap, + pinned: &mut HashMap, u64)>, + kv_gc: &mut KvPendingGC, hash: &BlockHash, ) { let mut discarded = Vec::new(); if let Some(level) = levels.get_mut(index) { *level = level.drain(..).filter_map(|overlay| { - let parent = parents.get(&overlay.hash).expect("there is a parent entry for each entry in levels; qed").clone(); + let parent = parents.get(&overlay.hash).expect("there is a parent entry for each entry in levels; qed").0.clone(); if parent == *hash { parents.remove(&overlay.hash); discarded.push(overlay.hash); - discard_values(&mut values, overlay.inserted, pinned.get_mut(hash)); + let mut pinned = pinned.get_mut(hash); + discard_values(&mut values, overlay.inserted, pinned.as_mut().map(|p| &mut p.0)); + discard_kv_values( + overlay.kv_inserted, + overlay.kv_deleted, + kv_gc, + ); None } else { Some(overlay) @@ -115,7 +253,7 @@ fn discard_descendants( }).collect(); } for hash in discarded { - discard_descendants(levels, values, index + 1, parents, pinned, &hash); + discard_descendants(levels, values, index + 1, parents, pinned, kv_gc, &hash); } } @@ -125,12 +263,14 @@ impl NonCanonicalOverlay { let last_canonicalized = db.get_meta(&to_meta_key(LAST_CANONICAL, &())) .map_err(|e| Error::Db(e))?; let last_canonicalized = match last_canonicalized { - Some(buffer) => Some(<(BlockHash, u64)>::decode(&mut buffer.as_slice())?), + Some(buffer) => Some(<(BlockHash, BlockNumber)>::decode(&mut buffer.as_slice())?), None => None, }; let mut levels = VecDeque::new(); let mut parents = HashMap::new(); let mut values = HashMap::new(); + let mut kv_values = HashMap::new(); + let mut branches = RangeSet::default(); if let Some((ref hash, mut block)) = last_canonicalized { // read the journal trace!(target: "state-db", "Reading uncanonicalized journal. Last canonicalized #{} ({:?})", block, hash); @@ -141,20 +281,62 @@ impl NonCanonicalOverlay { let mut level = Vec::new(); loop { let journal_key = to_journal_key(block, index); + let kv_journal_key = to_kv_journal_key(block, index); match db.get_meta(&journal_key).map_err(|e| Error::Db(e))? { Some(record) => { let record: JournalRecord = Decode::decode(&mut record.as_slice())?; + + let parent_branch_index = parents.get(&record.parent_hash) + .map(|(_, i)| *i).unwrap_or(0); + let parent_branch_range = Some( + branches.branch_ranges(parent_branch_index, Some(block - 1)) + ); + let (branch_range, branch_index) = branches.import( + block, + parent_branch_index, + parent_branch_range, + ); + + let (kv_record_inserted, kv_record_deleted) = if let Some(record) = db + .get_meta(&kv_journal_key).map_err(|e| Error::Db(e))? { + let record = KvJournalRecord::decode(&mut record.as_slice())?; + (Some(record.inserted), Some(record.deleted)) + } else { (None, None) }; let inserted = record.inserted.iter().map(|(k, _)| k.clone()).collect(); + let kv_inserted = kv_record_inserted.as_ref() + .map(|inserted| inserted.iter().map(|(k, _)| k.clone()).collect()) + .unwrap_or(Vec::new()); + let kv_deleted = kv_record_deleted.clone().unwrap_or(Vec::new()); let overlay = BlockOverlay { hash: record.hash.clone(), journal_key, + kv_journal_key, inserted: inserted, deleted: record.deleted, + kv_inserted, + kv_deleted, }; insert_values(&mut values, record.inserted); - trace!(target: "state-db", "Uncanonicalized journal entry {}.{} ({} inserted, {} deleted)", block, index, overlay.inserted.len(), overlay.deleted.len()); + if kv_record_inserted.is_some() || kv_record_deleted.is_some() { + insert_kv_values( + &branch_range, + &mut kv_values, + kv_record_inserted.unwrap_or(Vec::new()), + kv_record_deleted.unwrap_or(Vec::new()), + ); + } + trace!( + target: "state-db", + "Uncanonicalized journal entry {}.{} ({} {} inserted, {} {} deleted)", + block, + index, + overlay.inserted.len(), + overlay.kv_inserted.len(), + overlay.deleted.len(), + overlay.kv_deleted.len(), + ); level.push(overlay); - parents.insert(record.hash, record.parent_hash); + parents.insert(record.hash, (record.parent_hash, branch_index)); index += 1; total += 1; }, @@ -176,12 +358,23 @@ impl NonCanonicalOverlay { pending_canonicalizations: Default::default(), pending_insertions: Default::default(), pinned: Default::default(), - values: values, + values, + kv_values, + kv_gc: Default::default(), + branches, }) } - /// Insert a new block into the overlay. If inserted on the second level or lover expects parent to be present in the window. - pub fn insert(&mut self, hash: &BlockHash, number: u64, parent_hash: &BlockHash, changeset: ChangeSet) -> Result, Error> { + /// Insert a new block into the overlay. If inserted on the second level or lower + /// expects parent to be present in the window. + pub fn insert( + &mut self, + hash: &BlockHash, + number: BlockNumber, + parent_hash: &BlockHash, + changeset: ChangeSet, + kv_changeset: KvChangeSet, + ) -> Result, Error> { let mut commit = CommitSet::default(); let front_block_number = self.front_block_number(); if self.levels.is_empty() && self.last_canonicalized.is_none() && number > 0 { @@ -217,16 +410,43 @@ impl NonCanonicalOverlay { let index = level.len() as u64; let journal_key = to_journal_key(number, index); + let kv_journal_key = to_kv_journal_key(number, index); let inserted = changeset.inserted.iter().map(|(k, _)| k.clone()).collect(); + let mut kv_inserted = Vec::new(); + let mut kv_inserted_value = Vec::new(); + let mut kv_deleted = Vec::new(); + for (k, v) in kv_changeset.into_iter() { + if let Some(v) = v { + kv_inserted.push(k.clone()); + kv_inserted_value.push((k, v)); + } else { + kv_deleted.push(k); + } + } let overlay = BlockOverlay { hash: hash.clone(), journal_key: journal_key.clone(), + kv_journal_key: kv_journal_key.clone(), inserted: inserted, deleted: changeset.deleted.clone(), + kv_inserted, + kv_deleted: kv_deleted.clone(), }; level.push(overlay); - self.parents.insert(hash.clone(), parent_hash.clone()); + let parent_branch_index = self.parents.get(&parent_hash).map(|(_, i)| *i).unwrap_or(0); + let parent_branch_range = if number == 0 { + None + } else { + Some(self.branches.branch_ranges(parent_branch_index, Some(number - 1))) + }; + let (branch_range, branch_index) = self.branches.import( + number, + parent_branch_index, + parent_branch_range, + ); + + self.parents.insert(hash.clone(), (parent_hash.clone(), branch_index)); let journal_record = JournalRecord { hash: hash.clone(), parent_hash: parent_hash.clone(), @@ -234,8 +454,29 @@ impl NonCanonicalOverlay { deleted: changeset.deleted, }; commit.meta.inserted.push((journal_key, journal_record.encode())); - trace!(target: "state-db", "Inserted uncanonicalized changeset {}.{} ({} inserted, {} deleted)", number, index, journal_record.inserted.len(), journal_record.deleted.len()); + let kv_journal_record = KvJournalRecord { + inserted: kv_inserted_value, + deleted: kv_deleted, + keyspace_deleted: Default::default(), + }; + commit.meta.inserted.push((kv_journal_key, kv_journal_record.encode())); + + trace!( + target: "state-db", + "Inserted uncanonicalized changeset {}.{} ({} {} inserted, {} deleted)", + number, + index, + journal_record.inserted.len(), + kv_journal_record.inserted.len(), + journal_record.deleted.len(), + ); insert_values(&mut self.values, journal_record.inserted); + insert_kv_values( + &branch_range, + &mut self.kv_values, + kv_journal_record.inserted, + kv_journal_record.deleted, + ); self.pending_insertions.push(hash.clone()); Ok(commit) } @@ -249,9 +490,11 @@ impl NonCanonicalOverlay { ) { if let Some(level) = self.levels.get(level_index) { level.iter().for_each(|overlay| { - let parent = self.parents.get(&overlay.hash).expect("there is a parent entry for each entry in levels; qed").clone(); - if parent == *hash { + let (parent, _branch_index) = self.parents.get(&overlay.hash) + .expect("there is a parent entry for each entry in levels; qed"); + if parent == hash { discarded_journals.push(overlay.journal_key.clone()); + discarded_journals.push(overlay.kv_journal_key.clone()); discarded_blocks.push(overlay.hash.clone()); self.discard_journals(level_index + 1, discarded_journals, discarded_blocks, &overlay.hash); } @@ -259,11 +502,11 @@ impl NonCanonicalOverlay { } } - fn front_block_number(&self) -> u64 { + fn front_block_number(&self) -> BlockNumber { self.last_canonicalized.as_ref().map(|&(_, n)| n + 1).unwrap_or(0) } - pub fn last_canonicalized_block_number(&self) -> Option { + pub fn last_canonicalized_block_number(&self) -> Option { match self.last_canonicalized.as_ref().map(|&(_, n)| n) { Some(n) => Some(n + self.pending_canonicalizations.len() as u64), None if !self.pending_canonicalizations.is_empty() => Some(self.pending_canonicalizations.len() as u64), @@ -309,6 +552,7 @@ impl NonCanonicalOverlay { ); } discarded_journals.push(overlay.journal_key.clone()); + discarded_journals.push(overlay.kv_journal_key.clone()); discarded_blocks.push(overlay.hash.clone()); } @@ -317,9 +561,35 @@ impl NonCanonicalOverlay { commit.data.inserted.extend(overlay.inserted.iter() .map(|k| (k.clone(), self.values.get(k).expect("For each key in overlays there's a value in values").1.clone()))); commit.data.deleted.extend(overlay.deleted.clone()); + let block_number = self.front_block_number() + self.pending_canonicalizations.len() as u64; + if !overlay.kv_inserted.is_empty() || !overlay.kv_deleted.is_empty() { + // canonicalization is not frequent enough that we pass range + // in parameter for now + if let Some(range) = self.get_branch_range(hash, block_number) { + commit.kv.extend( + overlay.kv_inserted.iter() + .map(|k| (k.clone(), self.kv_values.get(k) + .expect("For each key in overlays there's a value in values") + .get(&range) + .expect("For each key in overlays there's a historied entry in values") + .clone()))); + // some deletion can be pruned if in first block so handle is + // a bit less restrictive + commit.kv.extend( + overlay.kv_deleted.iter() + .filter_map(|k| self.kv_values.get(k) + .map(|v| (k.clone(), v.get(&range) + .expect("For each key in overlays there's a historied entry \ + in values, and pruned empty value are cleared") + .clone()) + ) + ) + ); + } + } commit.meta.deleted.append(&mut discarded_journals); - let canonicalized = (hash.clone(), self.front_block_number() + self.pending_canonicalizations.len() as u64); + let canonicalized = (hash.clone(), block_number); commit.meta.inserted.push((to_meta_key(LAST_CANONICAL, &()), canonicalized.encode())); trace!(target: "state-db", "Discarding {} records", commit.meta.deleted.len()); self.pending_canonicalizations.push(hash.clone()); @@ -328,6 +598,8 @@ impl NonCanonicalOverlay { fn apply_canonicalizations(&mut self) { let last = self.pending_canonicalizations.last().cloned(); + let last_index = last.as_ref().and_then(|last| self.parents.get(last)) + .map(|(_, index)| *index); let count = self.pending_canonicalizations.len() as u64; for hash in self.pending_canonicalizations.drain(..) { trace!(target: "state-db", "Post canonicalizing {:?}", hash); @@ -347,16 +619,35 @@ impl NonCanonicalOverlay { 0, &mut self.parents, &mut self.pinned, + &mut self.kv_gc, &overlay.hash, ); } - discard_values(&mut self.values, overlay.inserted, self.pinned.get_mut(&overlay.hash)); + + let mut pinned = self.pinned.get_mut(&overlay.hash); + discard_values(&mut self.values, overlay.inserted, pinned.as_mut().map(|p| &mut p.0)); + discard_kv_values( + overlay.kv_inserted, + overlay.kv_deleted, + &mut self.kv_gc, + ); } } if let Some(hash) = last { - let last_canonicalized = (hash, self.last_canonicalized.as_ref().map(|(_, n)| n + count).unwrap_or(count - 1)); - self.last_canonicalized = Some(last_canonicalized); + let block_number = self.last_canonicalized.as_ref() + .map(|(_, n)| n + count).unwrap_or(count - 1); + self.last_canonicalized = Some((hash, block_number)); + + if let Some(branch_index_cannonicalize) = last_index { + // this needs to be call after parents update + self.branches.update_finalize_treshold(branch_index_cannonicalize, block_number, true); + self.kv_gc.set_pending_gc(branch_index_cannonicalize); + // try to run the garbage collection (can run later if there is + // pinned process). + self.kv_gc.try_gc(&mut self.kv_values, &self.branches, &self.pinned); + } } + } /// Get a value from the node overlay. This searches in every existing changeset. @@ -365,13 +656,21 @@ impl NonCanonicalOverlay { return Some(value.clone()); } for pinned in self.pinned.values() { - if let Some(value) = pinned.get(&key) { + if let Some(value) = pinned.0.get(&key) { return Some(value.clone()); } } None } + /// Get a value from the node overlay. This searches in every existing changeset. + pub fn get_kv(&self, key: &[u8], state: &BranchRanges) -> Option<&Option> { + if let Some(value) = self.kv_values.get(key) { + return value.get(state); + } + None + } + /// Check if the block is in the canonicalization queue. pub fn have_block(&self, hash: &BlockHash) -> bool { (self.parents.contains_key(hash) || self.pending_insertions.contains(hash)) @@ -384,8 +683,16 @@ impl NonCanonicalOverlay { let mut commit = CommitSet::default(); for overlay in level.into_iter() { commit.meta.deleted.push(overlay.journal_key); - self.parents.remove(&overlay.hash); + commit.meta.deleted.push(overlay.kv_journal_key); + if let Some((_, branch_index)) = self.parents.remove(&overlay.hash) { + self.branches.revert(branch_index); + } discard_values(&mut self.values, overlay.inserted, None); + discard_kv_values( + overlay.kv_inserted, + overlay.kv_deleted, + &mut self.kv_gc, + ); } commit }) @@ -424,27 +731,87 @@ impl NonCanonicalOverlay { /// Pin state values in memory pub fn pin(&mut self, hash: &BlockHash) { - self.pinned.insert(hash.clone(), HashMap::default()); + self.pinned.insert(hash.clone(), (Default::default(), self.kv_gc.gc_last_index)); + } + + /// For a a given block return its path in the block tree. + /// Note that using `number` is use to skip a query to block number for hash. + pub fn get_branch_range(&self, hash: &BlockHash, number: BlockNumber) -> Option { + self.parents.get(hash).map(|(_, branch_index)| *branch_index).map(|branch_index| { + self.branches.branch_ranges(branch_index, Some(number)) + }) } /// Discard pinned state pub fn unpin(&mut self, hash: &BlockHash) { self.pinned.remove(hash); + self.kv_gc.try_gc(&mut self.kv_values, &self.branches, &self.pinned); + } + + /// Iterator over values at a given state. Deletion are included in the result as a None value. + pub fn kv_iter<'a>( + &'a self, + state: &'a BranchRanges, + ) -> impl Iterator)> { + let state = state.clone(); + self.kv_values.iter().filter_map(move |(k, v)| { + v.get(&state).map(|v| (k, v)) + }) } } #[cfg(test)] mod tests { use std::io; + use std::collections::BTreeMap; use primitives::H256; use super::{NonCanonicalOverlay, to_journal_key}; - use crate::{ChangeSet, CommitSet}; - use crate::test::{make_db, make_changeset}; + use crate::{ChangeSet, KvChangeSet, CommitSet, KvKey}; + use crate::test::{make_db, make_changeset, make_kv_changeset}; + use crate::branch::BranchRanges; fn contains(overlay: &NonCanonicalOverlay, key: u64) -> bool { overlay.get(&H256::from_low_u64_be(key)) == Some(H256::from_low_u64_be(key).as_bytes().to_vec()) } + fn contains_kv( + overlay: &NonCanonicalOverlay, + key: u64, + state: &H256, + block: u64, + ) -> bool { + overlay.get_branch_range(state, block).and_then(|state| { + overlay.get_kv(&H256::from_low_u64_be(key).as_bytes().to_vec(), &state) + }) == Some(&Some(H256::from_low_u64_be(key).as_bytes().to_vec())) + } + + fn contains_kv2( + overlay: &NonCanonicalOverlay, + key: u64, + state: &BranchRanges, + ) -> bool { + overlay.get_kv(&H256::from_low_u64_be(key).as_bytes().to_vec(), &state) + == Some(&Some(H256::from_low_u64_be(key).as_bytes().to_vec())) + } + + fn contains_both( + overlay: &NonCanonicalOverlay, + key: u64, + state: &H256, + block: u64, + ) -> bool { + contains(overlay, key) && contains_kv(overlay, key, state, block) + } + + fn contains_any( + overlay: &NonCanonicalOverlay, + key: u64, + state: &H256, + block: u64, + ) -> bool { + contains(overlay, key) || contains_kv(overlay, key, state, block) + } + #[test] fn created_from_empty_db() { let db = make_db(&[]); @@ -470,8 +837,14 @@ mod tests { let h1 = H256::random(); let h2 = H256::random(); let mut overlay = NonCanonicalOverlay::::new(&db).unwrap(); - overlay.insert::(&h1, 2, &H256::default(), ChangeSet::default()).unwrap(); - overlay.insert::(&h2, 1, &h1, ChangeSet::default()).unwrap(); + overlay.insert::( + &h1, 2, &H256::default(), + ChangeSet::default(), KvChangeSet::default(), + ).unwrap(); + overlay.insert::( + &h2, 1, &h1, + ChangeSet::default(), KvChangeSet::default(), + ).unwrap(); } #[test] @@ -481,8 +854,14 @@ mod tests { let h2 = H256::random(); let db = make_db(&[]); let mut overlay = NonCanonicalOverlay::::new(&db).unwrap(); - overlay.insert::(&h1, 1, &H256::default(), ChangeSet::default()).unwrap(); - overlay.insert::(&h2, 3, &h1, ChangeSet::default()).unwrap(); + overlay.insert::( + &h1, 1, &H256::default(), + ChangeSet::default(), KvChangeSet::default(), + ).unwrap(); + overlay.insert::( + &h2, 3, &h1, + ChangeSet::default(), KvChangeSet::default(), + ).unwrap(); } #[test] @@ -492,8 +871,14 @@ mod tests { let h1 = H256::random(); let h2 = H256::random(); let mut overlay = NonCanonicalOverlay::::new(&db).unwrap(); - overlay.insert::(&h1, 1, &H256::default(), ChangeSet::default()).unwrap(); - overlay.insert::(&h2, 2, &H256::default(), ChangeSet::default()).unwrap(); + overlay.insert::( + &h1, 1, &H256::default(), + ChangeSet::default(), KvChangeSet::default(), + ).unwrap(); + overlay.insert::( + &h2, 2, &H256::default(), + ChangeSet::default(), KvChangeSet::default(), + ).unwrap(); } #[test] @@ -503,7 +888,10 @@ mod tests { let h2 = H256::random(); let db = make_db(&[]); let mut overlay = NonCanonicalOverlay::::new(&db).unwrap(); - overlay.insert::(&h1, 1, &H256::default(), ChangeSet::default()).unwrap(); + overlay.insert::( + &h1, 1, &H256::default(), + ChangeSet::default(), KvChangeSet::default(), + ).unwrap(); let mut commit = CommitSet::default(); overlay.canonicalize::(&h2, &mut commit).unwrap(); } @@ -512,22 +900,32 @@ mod tests { fn insert_canonicalize_one() { let h1 = H256::random(); let mut db = make_db(&[1, 2]); + db.initialize_kv(&[1, 2]); let mut overlay = NonCanonicalOverlay::::new(&db).unwrap(); let changeset = make_changeset(&[3, 4], &[2]); - let insertion = overlay.insert::(&h1, 1, &H256::default(), changeset.clone()).unwrap(); + let kv_changeset = make_kv_changeset(&[3, 4], &[2]); + let insertion = overlay.insert::( + &h1, 1, &H256::default(), + changeset.clone(), kv_changeset.clone(), + ).unwrap(); assert_eq!(insertion.data.inserted.len(), 0); assert_eq!(insertion.data.deleted.len(), 0); - assert_eq!(insertion.meta.inserted.len(), 2); + assert_eq!(insertion.kv.len(), 0); + // last cannonical, journal_record and kv_journal_record + assert_eq!(insertion.meta.inserted.len(), 3); assert_eq!(insertion.meta.deleted.len(), 0); db.commit(&insertion); let mut finalization = CommitSet::default(); overlay.canonicalize::(&h1, &mut finalization).unwrap(); assert_eq!(finalization.data.inserted.len(), changeset.inserted.len()); assert_eq!(finalization.data.deleted.len(), changeset.deleted.len()); + assert_eq!(finalization.kv.len(), kv_changeset.len()); assert_eq!(finalization.meta.inserted.len(), 1); - assert_eq!(finalization.meta.deleted.len(), 1); + // normal and kv discarded journall + assert_eq!(finalization.meta.deleted.len(), 2); db.commit(&finalization); assert!(db.data_eq(&make_db(&[1, 3, 4]))); + assert!(db.kv_eq(&[1, 3, 4])); } #[test] @@ -535,10 +933,19 @@ mod tests { let h1 = H256::random(); let h2 = H256::random(); let mut db = make_db(&[1, 2]); + db.initialize_kv(&[1, 2]); let mut overlay = NonCanonicalOverlay::::new(&db).unwrap(); - db.commit(&overlay.insert::(&h1, 10, &H256::default(), make_changeset(&[3, 4], &[2])).unwrap()); - db.commit(&overlay.insert::(&h2, 11, &h1, make_changeset(&[5], &[3])).unwrap()); - assert_eq!(db.meta.len(), 3); + db.commit(&overlay.insert::( + &h1, 10, &H256::default(), + make_changeset(&[3, 4], &[2]), + make_kv_changeset(&[3, 4], &[2]), + ).unwrap()); + db.commit(&overlay.insert::( + &h2, 11, &h1, + make_changeset(&[5], &[3]), + make_kv_changeset(&[5], &[3]), + ).unwrap()); + assert_eq!(db.meta.len(), 5); let overlay2 = NonCanonicalOverlay::::new(&db).unwrap(); assert_eq!(overlay.levels, overlay2.levels); @@ -551,9 +958,18 @@ mod tests { let h1 = H256::random(); let h2 = H256::random(); let mut db = make_db(&[1, 2]); + db.initialize_kv(&[1, 2]); let mut overlay = NonCanonicalOverlay::::new(&db).unwrap(); - db.commit(&overlay.insert::(&h1, 10, &H256::default(), make_changeset(&[3, 4], &[2])).unwrap()); - db.commit(&overlay.insert::(&h2, 11, &h1, make_changeset(&[5], &[3])).unwrap()); + db.commit(&overlay.insert::( + &h1, 10, &H256::default(), + make_changeset(&[3, 4], &[2]), + make_kv_changeset(&[3, 4], &[2]), + ).unwrap()); + db.commit(&overlay.insert::( + &h2,11, &h1, + make_changeset(&[5], &[3]), + make_kv_changeset(&[5], &[3]), + ).unwrap()); let mut commit = CommitSet::default(); overlay.canonicalize::(&h1, &mut commit).unwrap(); db.commit(&commit); @@ -571,14 +987,25 @@ mod tests { let h1 = H256::random(); let h2 = H256::random(); let mut db = make_db(&[1, 2, 3, 4]); + db.initialize_kv(&[1, 2, 3, 4]); let mut overlay = NonCanonicalOverlay::::new(&db).unwrap(); let changeset1 = make_changeset(&[5, 6], &[2]); let changeset2 = make_changeset(&[7, 8], &[5, 3]); - db.commit(&overlay.insert::(&h1, 1, &H256::default(), changeset1).unwrap()); - assert!(contains(&overlay, 5)); - db.commit(&overlay.insert::(&h2, 2, &h1, changeset2).unwrap()); - assert!(contains(&overlay, 7)); - assert!(contains(&overlay, 5)); + let kv_changeset1 = make_kv_changeset(&[5, 6], &[2]); + let kv_changeset2 = make_kv_changeset(&[7, 8], &[5, 3]); + db.commit(&overlay.insert::( + &h1, 1, &H256::default(), + changeset1, kv_changeset1, + ).unwrap()); + assert!(contains_both(&overlay, 5, &h1, 1)); + db.commit(&overlay.insert::( + &h2, 2, &h1, + changeset2, kv_changeset2, + ).unwrap()); + assert!(contains_both(&overlay, 7, &h2, 2)); + assert!(!contains_kv(&overlay, 5, &h2, 2)); + assert!(contains_kv(&overlay, 5, &h1, 1)); + assert!(contains_both(&overlay, 5, &h1, 1)); assert_eq!(overlay.levels.len(), 2); assert_eq!(overlay.parents.len(), 2); let mut commit = CommitSet::default(); @@ -590,8 +1017,8 @@ mod tests { overlay.apply_pending(); assert_eq!(overlay.levels.len(), 1); assert_eq!(overlay.parents.len(), 1); - assert!(!contains(&overlay, 5)); - assert!(contains(&overlay, 7)); + assert!(!contains_any(&overlay, 5, &h1, 1)); + assert!(contains_both(&overlay, 7, &h2, 2)); let mut commit = CommitSet::default(); overlay.canonicalize::(&h2, &mut commit).unwrap(); db.commit(&commit); @@ -599,6 +1026,7 @@ mod tests { assert_eq!(overlay.levels.len(), 0); assert_eq!(overlay.parents.len(), 0); assert!(db.data_eq(&make_db(&[1, 4, 6, 7, 8]))); + assert!(db.kv_eq(&[1, 4, 6, 7, 8])); } #[test] @@ -606,17 +1034,19 @@ mod tests { let mut db = make_db(&[]); let (h_1, c_1) = (H256::random(), make_changeset(&[1], &[])); let (h_2, c_2) = (H256::random(), make_changeset(&[1], &[])); + let o_c_1 = make_kv_changeset(&[1], &[]); + let o_c_2 = make_kv_changeset(&[1], &[]); let mut overlay = NonCanonicalOverlay::::new(&db).unwrap(); - db.commit(&overlay.insert::(&h_1, 1, &H256::default(), c_1).unwrap()); - db.commit(&overlay.insert::(&h_2, 1, &H256::default(), c_2).unwrap()); - assert!(contains(&overlay, 1)); + db.commit(&overlay.insert::(&h_1, 1, &H256::default(), c_1, o_c_1).unwrap()); + db.commit(&overlay.insert::(&h_2, 1, &H256::default(), c_2, o_c_2).unwrap()); + assert!(contains_both(&overlay, 1, &h_2, 1)); let mut commit = CommitSet::default(); overlay.canonicalize::(&h_1, &mut commit).unwrap(); db.commit(&commit); - assert!(contains(&overlay, 1)); + assert!(contains_both(&overlay, 1, &h_2, 1)); overlay.apply_pending(); - assert!(!contains(&overlay, 1)); + assert!(!contains_any(&overlay, 1, &h_2, 1)); } #[test] @@ -627,18 +1057,40 @@ mod tests { let mut db = make_db(&[]); let mut overlay = NonCanonicalOverlay::::new(&db).unwrap(); let changeset = make_changeset(&[], &[]); - db.commit(&overlay.insert::(&h1, 1, &H256::default(), changeset.clone()).unwrap()); - db.commit(&overlay.insert::(&h2, 2, &h1, changeset.clone()).unwrap()); + let ochangeset = make_kv_changeset(&[], &[]); + db.commit(&overlay.insert::( + &h1, 1, &H256::default(), + changeset.clone(), ochangeset.clone(), + ).unwrap()); + db.commit(&overlay.insert::( + &h2, 2, &h1, + changeset.clone(), ochangeset.clone(), + ).unwrap()); overlay.apply_pending(); let mut commit = CommitSet::default(); overlay.canonicalize::(&h1, &mut commit).unwrap(); overlay.canonicalize::(&h2, &mut commit).unwrap(); db.commit(&commit); - db.commit(&overlay.insert::(&h3, 3, &h2, changeset.clone()).unwrap()); + db.commit(&overlay.insert::( + &h3, 3, &h2, + changeset.clone(), ochangeset.clone(), + ).unwrap()); overlay.apply_pending(); assert_eq!(overlay.levels.len(), 1); } + fn make_both_changeset(inserted: &[u64], deleted: &[u64]) -> ( + H256, + ChangeSet, + KvChangeSet, + ) { + ( + H256::random(), + make_changeset(inserted, deleted), + make_kv_changeset(inserted, deleted), + ) + } + #[test] fn complex_tree() { use crate::MetaDb; @@ -654,43 +1106,43 @@ mod tests { // // 1_2_2 is the winner - let (h_1, c_1) = (H256::random(), make_changeset(&[1], &[])); - let (h_2, c_2) = (H256::random(), make_changeset(&[2], &[])); + let (h_1, c_1, o_c_1) = make_both_changeset(&[1], &[]); + let (h_2, c_2, o_c_2) = make_both_changeset(&[2], &[]); - let (h_1_1, c_1_1) = (H256::random(), make_changeset(&[11], &[])); - let (h_1_2, c_1_2) = (H256::random(), make_changeset(&[12], &[])); - let (h_2_1, c_2_1) = (H256::random(), make_changeset(&[21], &[])); - let (h_2_2, c_2_2) = (H256::random(), make_changeset(&[22], &[])); + let (h_1_1, c_1_1, o_c_1_1) = make_both_changeset(&[11], &[]); + let (h_1_2, c_1_2, o_c_1_2) = make_both_changeset(&[12], &[]); + let (h_2_1, c_2_1, o_c_2_1) = make_both_changeset(&[21], &[]); + let (h_2_2, c_2_2, o_c_2_2) = make_both_changeset(&[22], &[]); - let (h_1_1_1, c_1_1_1) = (H256::random(), make_changeset(&[111], &[])); - let (h_1_2_1, c_1_2_1) = (H256::random(), make_changeset(&[121], &[])); - let (h_1_2_2, c_1_2_2) = (H256::random(), make_changeset(&[122], &[])); - let (h_1_2_3, c_1_2_3) = (H256::random(), make_changeset(&[123], &[])); - let (h_2_1_1, c_2_1_1) = (H256::random(), make_changeset(&[211], &[])); + let (h_1_1_1, c_1_1_1, o_c_1_1_1) = make_both_changeset(&[111], &[]); + let (h_1_2_1, c_1_2_1, o_c_1_2_1) = make_both_changeset(&[121], &[]); + let (h_1_2_2, c_1_2_2, o_c_1_2_2) = make_both_changeset(&[122], &[]); + let (h_1_2_3, c_1_2_3, o_c_1_2_3) = make_both_changeset(&[123], &[]); + let (h_2_1_1, c_2_1_1, o_c_2_1_1) = make_both_changeset(&[211], &[]); let mut overlay = NonCanonicalOverlay::::new(&db).unwrap(); - db.commit(&overlay.insert::(&h_1, 1, &H256::default(), c_1).unwrap()); + db.commit(&overlay.insert::(&h_1, 1, &H256::default(), c_1, o_c_1).unwrap()); - db.commit(&overlay.insert::(&h_1_1, 2, &h_1, c_1_1).unwrap()); - db.commit(&overlay.insert::(&h_1_2, 2, &h_1, c_1_2).unwrap()); + db.commit(&overlay.insert::(&h_1_1, 2, &h_1, c_1_1, o_c_1_1).unwrap()); + db.commit(&overlay.insert::(&h_1_2, 2, &h_1, c_1_2, o_c_1_2).unwrap()); - db.commit(&overlay.insert::(&h_2, 1, &H256::default(), c_2).unwrap()); + db.commit(&overlay.insert::(&h_2, 1, &H256::default(), c_2, o_c_2).unwrap()); - db.commit(&overlay.insert::(&h_2_1, 2, &h_2, c_2_1).unwrap()); - db.commit(&overlay.insert::(&h_2_2, 2, &h_2, c_2_2).unwrap()); + db.commit(&overlay.insert::(&h_2_1, 2, &h_2, c_2_1, o_c_2_1).unwrap()); + db.commit(&overlay.insert::(&h_2_2, 2, &h_2, c_2_2, o_c_2_2).unwrap()); - db.commit(&overlay.insert::(&h_1_1_1, 3, &h_1_1, c_1_1_1).unwrap()); - db.commit(&overlay.insert::(&h_1_2_1, 3, &h_1_2, c_1_2_1).unwrap()); - db.commit(&overlay.insert::(&h_1_2_2, 3, &h_1_2, c_1_2_2).unwrap()); - db.commit(&overlay.insert::(&h_1_2_3, 3, &h_1_2, c_1_2_3).unwrap()); - db.commit(&overlay.insert::(&h_2_1_1, 3, &h_2_1, c_2_1_1).unwrap()); + db.commit(&overlay.insert::(&h_1_1_1, 3, &h_1_1, c_1_1_1, o_c_1_1_1).unwrap()); + db.commit(&overlay.insert::(&h_1_2_1, 3, &h_1_2, c_1_2_1, o_c_1_2_1).unwrap()); + db.commit(&overlay.insert::(&h_1_2_2, 3, &h_1_2, c_1_2_2, o_c_1_2_2).unwrap()); + db.commit(&overlay.insert::(&h_1_2_3, 3, &h_1_2, c_1_2_3, o_c_1_2_3).unwrap()); + db.commit(&overlay.insert::(&h_2_1_1, 3, &h_2_1, c_2_1_1, o_c_2_1_1).unwrap()); - assert!(contains(&overlay, 2)); - assert!(contains(&overlay, 11)); - assert!(contains(&overlay, 21)); - assert!(contains(&overlay, 111)); - assert!(contains(&overlay, 122)); - assert!(contains(&overlay, 211)); + assert!(contains_both(&overlay, 2, &h_2_1_1, 3)); + assert!(contains_both(&overlay, 11, &h_1_1_1, 3)); + assert!(contains_both(&overlay, 21, &h_2_1_1, 3)); + assert!(contains_both(&overlay, 111, &h_1_1_1, 3)); + assert!(contains_both(&overlay, 122, &h_1_2_2, 3)); + assert!(contains_both(&overlay, 211, &h_2_1_1, 3)); assert_eq!(overlay.levels.len(), 3); assert_eq!(overlay.parents.len(), 11); assert_eq!(overlay.last_canonicalized, Some((H256::default(), 0))); @@ -698,7 +1150,11 @@ mod tests { // check if restoration from journal results in the same tree let overlay2 = NonCanonicalOverlay::::new(&db).unwrap(); assert_eq!(overlay.levels, overlay2.levels); - assert_eq!(overlay.parents, overlay2.parents); + let parents_no_iter: BTreeMap<_, _> = overlay.parents.iter() + .map(|(k, (v, _))| (k.clone(), v.clone())).collect(); + let parents_no_iter2: BTreeMap<_, _> = overlay2.parents.iter() + .map(|(k, (v, _))| (k.clone(), v.clone())).collect(); + assert_eq!(parents_no_iter, parents_no_iter2); assert_eq!(overlay.last_canonicalized, overlay2.last_canonicalized); // canonicalize 1. 2 and all its children should be discarded @@ -708,13 +1164,13 @@ mod tests { overlay.apply_pending(); assert_eq!(overlay.levels.len(), 2); assert_eq!(overlay.parents.len(), 6); - assert!(!contains(&overlay, 1)); - assert!(!contains(&overlay, 2)); - assert!(!contains(&overlay, 21)); - assert!(!contains(&overlay, 22)); - assert!(!contains(&overlay, 211)); - assert!(contains(&overlay, 111)); - assert!(!contains(&overlay, 211)); + assert!(!contains_any(&overlay, 1, &h_1, 1)); + assert!(!contains_any(&overlay, 2, &h_2, 1)); + assert!(!contains_any(&overlay, 21, &h_2_1, 2)); + assert!(!contains_any(&overlay, 22, &h_2_2, 2)); + assert!(!contains_any(&overlay, 211, &h_2_1_1, 3)); + assert!(contains_both(&overlay, 111, &h_1_1_1, 3)); + assert!(contains_both(&overlay, 12, &h_1_2, 2)); // check that journals are deleted assert!(db.get_meta(&to_journal_key(1, 0)).unwrap().is_none()); assert!(db.get_meta(&to_journal_key(1, 1)).unwrap().is_none()); @@ -729,11 +1185,11 @@ mod tests { overlay.apply_pending(); assert_eq!(overlay.levels.len(), 1); assert_eq!(overlay.parents.len(), 3); - assert!(!contains(&overlay, 11)); - assert!(!contains(&overlay, 111)); - assert!(contains(&overlay, 121)); - assert!(contains(&overlay, 122)); - assert!(contains(&overlay, 123)); + assert!(!contains_any(&overlay, 11, &h_1_1, 2)); + assert!(!contains_any(&overlay, 111, &h_1_1_1, 3)); + assert!(contains_both(&overlay, 121, &h_1_2_1, 3)); + assert!(contains_both(&overlay, 122, &h_1_2_2, 3)); + assert!(contains_both(&overlay, 123, &h_1_2_3, 3)); assert!(overlay.have_block(&h_1_2_1)); assert!(!overlay.have_block(&h_1_2)); assert!(!overlay.have_block(&h_1_1)); @@ -747,6 +1203,7 @@ mod tests { assert_eq!(overlay.levels.len(), 0); assert_eq!(overlay.parents.len(), 0); assert!(db.data_eq(&make_db(&[1, 12, 122]))); + assert!(db.kv_eq(&[1, 12, 122])); assert_eq!(overlay.last_canonicalized, Some((h_1_2_2, 3))); } @@ -755,17 +1212,27 @@ mod tests { let h1 = H256::random(); let h2 = H256::random(); let mut db = make_db(&[1, 2, 3, 4]); + db.initialize_kv(&[1, 2, 3, 4]); let mut overlay = NonCanonicalOverlay::::new(&db).unwrap(); assert!(overlay.revert_one().is_none()); let changeset1 = make_changeset(&[5, 6], &[2]); + let ochangeset1 = make_kv_changeset(&[5, 6], &[2]); let changeset2 = make_changeset(&[7, 8], &[5, 3]); - db.commit(&overlay.insert::(&h1, 1, &H256::default(), changeset1).unwrap()); - db.commit(&overlay.insert::(&h2, 2, &h1, changeset2).unwrap()); - assert!(contains(&overlay, 7)); + let ochangeset2 = make_kv_changeset(&[7, 8], &[5, 3]); + db.commit(&overlay.insert::( + &h1, 1, &H256::default(), + changeset1, ochangeset1, + ).unwrap()); + db.commit(&overlay.insert::( + &h2, 2, &h1, + changeset2, ochangeset2, + ).unwrap()); + assert!(contains_both(&overlay, 7, &h2, 2)); db.commit(&overlay.revert_one().unwrap()); assert_eq!(overlay.parents.len(), 1); - assert!(contains(&overlay, 5)); + assert!(contains_both(&overlay, 5, &h1, 1)); assert!(!contains(&overlay, 7)); + assert!(!contains_any(&overlay, 7, &h1, 1)); db.commit(&overlay.revert_one().unwrap()); assert_eq!(overlay.levels.len(), 0); assert_eq!(overlay.parents.len(), 0); @@ -780,19 +1247,33 @@ mod tests { let db = make_db(&[]); let mut overlay = NonCanonicalOverlay::::new(&db).unwrap(); let changeset1 = make_changeset(&[5, 6], &[2]); + let ochangeset1 = make_kv_changeset(&[5, 6], &[2]); let changeset2 = make_changeset(&[7, 8], &[5, 3]); + let ochangeset2 = make_kv_changeset(&[7, 8], &[5, 3]); let changeset3 = make_changeset(&[9], &[]); - overlay.insert::(&h1, 1, &H256::default(), changeset1).unwrap(); + let ochangeset3 = make_kv_changeset(&[9], &[]); + overlay.insert::( + &h1, 1, &H256::default(), + changeset1, ochangeset1, + ).unwrap(); assert!(contains(&overlay, 5)); - overlay.insert::(&h2_1, 2, &h1, changeset2).unwrap(); - overlay.insert::(&h2_2, 2, &h1, changeset3).unwrap(); - assert!(contains(&overlay, 7)); + overlay.insert::( + &h2_1, 2, &h1, + changeset2, ochangeset2, + ).unwrap(); + overlay.insert::( + &h2_2, 2, &h1, + changeset3, ochangeset3, + ).unwrap(); + assert!(contains_kv(&overlay, 5, &h1, 1)); + assert!(contains_both(&overlay, 7, &h2_1, 2)); + assert!(!contains_kv(&overlay, 5, &h2_1, 2)); assert!(contains(&overlay, 5)); - assert!(contains(&overlay, 9)); + assert!(contains_both(&overlay, 9, &h2_2, 2)); assert_eq!(overlay.levels.len(), 2); assert_eq!(overlay.parents.len(), 3); overlay.revert_pending(); - assert!(!contains(&overlay, 5)); + assert!(!contains_any(&overlay, 5, &h1, 1)); assert_eq!(overlay.levels.len(), 0); assert_eq!(overlay.parents.len(), 0); } @@ -804,21 +1285,27 @@ mod tests { // - 1 - 1_1 // \ 1_2 - let (h_1, c_1) = (H256::random(), make_changeset(&[1], &[])); - let (h_2, c_2) = (H256::random(), make_changeset(&[2], &[])); + let (h_1, c_1, o_c_1) = make_both_changeset(&[1], &[]); + let (h_2, c_2, o_c_2) = make_both_changeset(&[2], &[]); let mut overlay = NonCanonicalOverlay::::new(&db).unwrap(); - db.commit(&overlay.insert::(&h_1, 1, &H256::default(), c_1).unwrap()); - db.commit(&overlay.insert::(&h_2, 1, &H256::default(), c_2).unwrap()); + db.commit(&overlay.insert::(&h_1, 1, &H256::default(), c_1, o_c_1).unwrap()); + db.commit(&overlay.insert::(&h_2, 1, &H256::default(), c_2, o_c_2).unwrap()); overlay.pin(&h_1); - + let h1_context = overlay.get_branch_range(&h_1, 1).unwrap(); + let mut commit = CommitSet::default(); overlay.canonicalize::(&h_2, &mut commit).unwrap(); db.commit(&commit); overlay.apply_pending(); assert!(contains(&overlay, 1)); + // we cannot use contains_kv because kv pining is relying on + // asumption that pinned context memoïzed its branch state. + assert!(contains_kv2(&overlay, 1, &h1_context)); + assert!(!contains_kv(&overlay, 1, &h_1, 1)); overlay.unpin(&h_1); assert!(!contains(&overlay, 1)); + assert!(!contains_kv2(&overlay, 1, &h1_context)); } } diff --git a/core/state-db/src/pruning.rs b/core/state-db/src/pruning.rs index 21f472fe69da9..ce1ea839523b3 100644 --- a/core/state-db/src/pruning.rs +++ b/core/state-db/src/pruning.rs @@ -24,11 +24,14 @@ use std::collections::{HashMap, HashSet, VecDeque}; use codec::{Encode, Decode}; -use crate::{CommitSet, Error, MetaDb, to_meta_key, Hash}; +use crate::{CommitSet, CommitSetCanonical, Error, MetaDb, to_meta_key, Hash, + KvKey}; use log::{trace, warn}; +use primitives::child_trie::KeySpace; const LAST_PRUNED: &[u8] = b"last_pruned"; const PRUNING_JOURNAL: &[u8] = b"pruning_journal"; +const OFFSTATE_PRUNING_JOURNAL: &[u8] = b"kv_pruning_journal"; /// See module documentation. pub struct RefWindow { @@ -50,7 +53,12 @@ pub struct RefWindow { struct DeathRow { hash: BlockHash, journal_key: Vec, + kv_journal_key: Vec, deleted: HashSet, + // TODO EMCH for kv there is no need to put + // in memory so we can make it lazy (load from + // pruning journal on actual prune). + kv_modified: HashSet, } #[derive(Encode, Decode)] @@ -60,10 +68,22 @@ struct JournalRecord { deleted: Vec, } +#[derive(Encode, Decode)] +struct KvJournalRecord { + modified: Vec, + // placeholder for trie deletion + // over a full keyspace. + keyspace_deleted: Vec, +} + fn to_journal_key(block: u64) -> Vec { to_meta_key(PRUNING_JOURNAL, &block) } +fn to_kv_journal_key(block: u64) -> Vec { + to_meta_key(OFFSTATE_PRUNING_JOURNAL, &block) +} + impl RefWindow { pub fn new(db: &D) -> Result, Error> { let last_pruned = db.get_meta(&to_meta_key(LAST_PRUNED, &())) @@ -84,11 +104,32 @@ impl RefWindow { trace!(target: "state-db", "Reading pruning journal. Pending #{}", pending_number); loop { let journal_key = to_journal_key(block); + let kv_journal_key = to_kv_journal_key(block); match db.get_meta(&journal_key).map_err(|e| Error::Db(e))? { Some(record) => { let record: JournalRecord = Decode::decode(&mut record.as_slice())?; - trace!(target: "state-db", "Pruning journal entry {} ({} inserted, {} deleted)", block, record.inserted.len(), record.deleted.len()); - pruning.import(&record.hash, journal_key, record.inserted.into_iter(), record.deleted); + let kv_record_inserted = if let Some(record) = db + .get_meta(&kv_journal_key).map_err(|e| Error::Db(e))? { + let record = KvJournalRecord::decode(&mut record.as_slice())?; + record.modified + } else { Vec::new() }; + + trace!( + target: "state-db", + "Pruning journal entry {} ({} {} inserted, {} deleted)", + block, + record.inserted.len(), + kv_record_inserted.len(), + record.deleted.len(), + ); + pruning.import( + &record.hash, + journal_key, + kv_journal_key, + record.inserted.into_iter(), + record.deleted, + kv_record_inserted.into_iter(), + ); }, None => break, } @@ -97,7 +138,15 @@ impl RefWindow { Ok(pruning) } - fn import>(&mut self, hash: &BlockHash, journal_key: Vec, inserted: I, deleted: Vec) { + fn import, I2: IntoIterator>( + &mut self, + hash: &BlockHash, + journal_key: Vec, + kv_journal_key: Vec, + inserted: I, + deleted: Vec, + kv_modified: I2, + ) { // remove all re-inserted keys from death rows for k in inserted { if let Some(block) = self.death_index.remove(&k) { @@ -110,11 +159,15 @@ impl RefWindow { for k in deleted.iter() { self.death_index.insert(k.clone(), imported_block); } + // TODO EMCH is it possible to change type to directly set ?? + let kv_modified = kv_modified.into_iter().collect(); self.death_rows.push_back( DeathRow { hash: hash.clone(), deleted: deleted.into_iter().collect(), - journal_key: journal_key, + kv_modified, + journal_key, + kv_journal_key, } ); } @@ -139,14 +192,30 @@ impl RefWindow { self.death_rows.iter().skip(self.pending_prunings).any(|r| r.hash == *hash) } - /// Prune next block. Expects at least one block in the window. Adds changes to `commit`. - pub fn prune_one(&mut self, commit: &mut CommitSet) { + /// Prune next block. Expects at least one block in the window. + /// Adds changes to `commit`. + /// `kv_prune` to None indicates archive mode. + pub fn prune_one( + &mut self, + commit: &mut CommitSetCanonical, + ) { + let (commit, kv_prune) = commit; if let Some(pruned) = self.death_rows.get(self.pending_prunings) { trace!(target: "state-db", "Pruning {:?} ({} deleted)", pruned.hash, pruned.deleted.len()); let index = self.pending_number + self.pending_prunings as u64; commit.data.deleted.extend(pruned.deleted.iter().cloned()); + if let Some(kv) = kv_prune.as_mut() { + kv.0 = std::cmp::max(kv.0, index); + kv.1.extend(pruned.kv_modified.iter().cloned()); + } else { + *kv_prune = Some(( + index, + pruned.kv_modified.iter().cloned().collect(), + )); + } commit.meta.inserted.push((to_meta_key(LAST_PRUNED, &()), index.encode())); commit.meta.deleted.push(pruned.journal_key.clone()); + commit.meta.deleted.push(pruned.kv_journal_key.clone()); self.pending_prunings += 1; } else { warn!(target: "state-db", "Trying to prune when there's nothing to prune"); @@ -157,16 +226,30 @@ impl RefWindow { pub fn note_canonical(&mut self, hash: &BlockHash, commit: &mut CommitSet) { trace!(target: "state-db", "Adding to pruning window: {:?} ({} inserted, {} deleted)", hash, commit.data.inserted.len(), commit.data.deleted.len()); let inserted = commit.data.inserted.iter().map(|(k, _)| k.clone()).collect(); + let kv_modified = commit.kv.iter().map(|(k, _)| k.clone()).collect(); let deleted = ::std::mem::replace(&mut commit.data.deleted, Vec::new()); let journal_record = JournalRecord { hash: hash.clone(), inserted, deleted, }; + let kv_journal_record = KvJournalRecord { + modified: kv_modified, + keyspace_deleted: Default::default(), + }; let block = self.pending_number + self.death_rows.len() as u64; let journal_key = to_journal_key(block); + let kv_journal_key = to_kv_journal_key(block); commit.meta.inserted.push((journal_key.clone(), journal_record.encode())); - self.import(&journal_record.hash, journal_key, journal_record.inserted.into_iter(), journal_record.deleted); + commit.meta.inserted.push((kv_journal_key.clone(), kv_journal_record.encode())); + self.import( + &journal_record.hash, + journal_key, + kv_journal_key, + journal_record.inserted.into_iter(), + journal_record.deleted, + kv_journal_record.modified.into_iter(), + ); self.pending_canonicalizations += 1; } @@ -202,8 +285,8 @@ impl RefWindow { mod tests { use super::RefWindow; use primitives::H256; - use crate::CommitSet; - use crate::test::{make_db, make_commit, TestDb}; + use crate::CommitSetCanonical; + use crate::test::{make_db, make_commit_both, TestDb, make_commit}; fn check_journal(pruning: &RefWindow, db: &TestDb) { let restored: RefWindow = RefWindow::new(db).unwrap(); @@ -219,17 +302,6 @@ mod tests { assert_eq!(pruning.pending_number, 0); assert!(pruning.death_rows.is_empty()); assert!(pruning.death_index.is_empty()); - } - - #[test] - fn prune_empty() { - let db = make_db(&[]); - let mut pruning: RefWindow = RefWindow::new(&db).unwrap(); - let mut commit = CommitSet::default(); - pruning.prune_one(&mut commit); - assert_eq!(pruning.pending_number, 0); - assert!(pruning.death_rows.is_empty()); - assert!(pruning.death_index.is_empty()); assert!(pruning.pending_prunings == 0); assert!(pruning.pending_canonicalizations == 0); } @@ -237,27 +309,36 @@ mod tests { #[test] fn prune_one() { let mut db = make_db(&[1, 2, 3]); + db.initialize_kv(&[1, 2, 3]); let mut pruning: RefWindow = RefWindow::new(&db).unwrap(); - let mut commit = make_commit(&[4, 5], &[1, 3]); + let mut commit = make_commit_both(&[4, 5], &[1, 3]); + commit.0.initialize_kv(&[4, 5], &[1, 3]); let h = H256::random(); - pruning.note_canonical(&h, &mut commit); - db.commit(&commit); + assert!(!commit.0.data.deleted.is_empty()); + pruning.note_canonical(&h, &mut commit.0); + db.commit_canonical(&commit); assert!(pruning.have_block(&h)); pruning.apply_pending(); assert!(pruning.have_block(&h)); - assert!(commit.data.deleted.is_empty()); + assert!(commit.0.data.deleted.is_empty()); + //assert!(commit.kv.is_empty()); assert_eq!(pruning.death_rows.len(), 1); assert_eq!(pruning.death_index.len(), 2); assert!(db.data_eq(&make_db(&[1, 2, 3, 4, 5]))); + assert!(db.kv_eq_at(&[1, 2, 3], Some(0))); + assert!(db.kv_eq(&[2, 4, 5])); check_journal(&pruning, &db); - let mut commit = CommitSet::default(); + let mut commit = CommitSetCanonical::default(); pruning.prune_one(&mut commit); assert!(!pruning.have_block(&h)); - db.commit(&commit); + db.commit_canonical(&commit); pruning.apply_pending(); assert!(!pruning.have_block(&h)); assert!(db.data_eq(&make_db(&[2, 4, 5]))); + // two remains since it is still valid next + assert!(db.kv_eq_at(&[2], Some(0))); + assert!(db.kv_eq(&[2, 4, 5])); assert!(pruning.death_rows.is_empty()); assert!(pruning.death_index.is_empty()); assert_eq!(pruning.pending_number, 1); @@ -266,83 +347,122 @@ mod tests { #[test] fn prune_two() { let mut db = make_db(&[1, 2, 3]); + db.initialize_kv(&[1, 2, 3]); let mut pruning: RefWindow = RefWindow::new(&db).unwrap(); - let mut commit = make_commit(&[4], &[1]); - pruning.note_canonical(&H256::random(), &mut commit); - db.commit(&commit); - let mut commit = make_commit(&[5], &[2]); - pruning.note_canonical(&H256::random(), &mut commit); - db.commit(&commit); + let mut commit = make_commit_both(&[3, 4], &[1]); + pruning.note_canonical(&H256::random(), &mut commit.0); + db.commit_canonical(&commit); + let mut commit = make_commit_both(&[5], &[2]); + pruning.note_canonical(&H256::random(), &mut commit.0); + db.commit_canonical(&commit); pruning.apply_pending(); assert!(db.data_eq(&make_db(&[1, 2, 3, 4, 5]))); + assert!(db.kv_eq_at(&[1, 2, 3], Some(0))); + assert!(db.kv_eq_at(&[2, 3, 4], Some(1))); + assert!(db.kv_eq(&[3, 4, 5])); check_journal(&pruning, &db); - let mut commit = CommitSet::default(); + let mut commit = CommitSetCanonical::default(); pruning.prune_one(&mut commit); - db.commit(&commit); + db.commit_canonical(&commit); pruning.apply_pending(); assert!(db.data_eq(&make_db(&[2, 3, 4, 5]))); - let mut commit = CommitSet::default(); + // 3 exists at 0 and 1 so 0 removed + assert!(db.kv_eq_at(&[2], Some(0))); + assert!(db.kv_eq_at(&[2, 3, 4], Some(1))); + assert!(db.kv_eq(&[3, 4, 5])); + let mut commit = CommitSetCanonical::default(); pruning.prune_one(&mut commit); - db.commit(&commit); + db.commit_canonical(&commit); pruning.apply_pending(); assert!(db.data_eq(&make_db(&[3, 4, 5]))); + assert!(db.kv_eq_at(&[], Some(0))); + assert!(db.kv_eq_at(&[3, 4], Some(1))); + assert!(db.kv_eq(&[3, 4, 5])); assert_eq!(pruning.pending_number, 2); } #[test] fn prune_two_pending() { let mut db = make_db(&[1, 2, 3]); + db.initialize_kv(&[1, 2, 3]); let mut pruning: RefWindow = RefWindow::new(&db).unwrap(); - let mut commit = make_commit(&[4], &[1]); - pruning.note_canonical(&H256::random(), &mut commit); - db.commit(&commit); - let mut commit = make_commit(&[5], &[2]); - pruning.note_canonical(&H256::random(), &mut commit); - db.commit(&commit); + let mut commit = make_commit_both(&[4], &[1]); + pruning.note_canonical(&H256::random(), &mut commit.0); + db.commit_canonical(&commit); + let mut commit = make_commit_both(&[3, 5], &[2]); + pruning.note_canonical(&H256::random(), &mut commit.0); + db.commit_canonical(&commit); assert!(db.data_eq(&make_db(&[1, 2, 3, 4, 5]))); - let mut commit = CommitSet::default(); + assert!(db.kv_eq_at(&[1, 2, 3], Some(0))); + assert!(db.kv_eq_at(&[2, 3, 4], Some(1))); + assert!(db.kv_eq(&[3, 4, 5])); + let mut commit = CommitSetCanonical::default(); pruning.prune_one(&mut commit); - db.commit(&commit); + db.commit_canonical(&commit); assert!(db.data_eq(&make_db(&[2, 3, 4, 5]))); - let mut commit = CommitSet::default(); + assert!(db.kv_eq_at(&[2, 3], Some(0))); + assert!(db.kv_eq_at(&[2, 3, 4], Some(1))); + assert!(db.kv_eq(&[3, 4, 5])); + let mut commit = CommitSetCanonical::default(); pruning.prune_one(&mut commit); - db.commit(&commit); + db.commit_canonical(&commit); pruning.apply_pending(); assert!(db.data_eq(&make_db(&[3, 4, 5]))); + assert!(db.kv_eq_at(&[], Some(0))); + assert!(db.kv_eq_at(&[4], Some(1))); + assert!(db.kv_eq(&[3, 4, 5])); assert_eq!(pruning.pending_number, 2); } #[test] fn reinserted_survives() { let mut db = make_db(&[1, 2, 3]); + db.initialize_kv(&[1, 2, 3]); let mut pruning: RefWindow = RefWindow::new(&db).unwrap(); - let mut commit = make_commit(&[], &[2]); - pruning.note_canonical(&H256::random(), &mut commit); - db.commit(&commit); - let mut commit = make_commit(&[2], &[]); - pruning.note_canonical(&H256::random(), &mut commit); - db.commit(&commit); - let mut commit = make_commit(&[], &[2]); - pruning.note_canonical(&H256::random(), &mut commit); - db.commit(&commit); + let mut commit = make_commit_both(&[], &[2]); + commit.0.initialize_kv(&[], &[2]); + pruning.note_canonical(&H256::random(), &mut commit.0); + db.commit_canonical(&commit); + let mut commit = make_commit_both(&[2], &[]); + pruning.note_canonical(&H256::random(), &mut commit.0); + db.commit_canonical(&commit); + let mut commit = make_commit_both(&[], &[2]); + pruning.note_canonical(&H256::random(), &mut commit.0); + db.commit_canonical(&commit); assert!(db.data_eq(&make_db(&[1, 2, 3]))); + assert!(db.kv_eq_at(&[1, 2, 3], Some(0))); + assert!(db.kv_eq_at(&[1, 3], Some(1))); + assert!(db.kv_eq_at(&[1, 2, 3], Some(2))); + assert!(db.kv_eq(&[1, 3])); pruning.apply_pending(); check_journal(&pruning, &db); - let mut commit = CommitSet::default(); + let mut commit = CommitSetCanonical::default(); pruning.prune_one(&mut commit); - db.commit(&commit); + db.commit_canonical(&commit); assert!(db.data_eq(&make_db(&[1, 2, 3]))); - let mut commit = CommitSet::default(); + assert!(db.kv_eq_at(&[1, 3], Some(0))); + assert!(db.kv_eq_at(&[1, 3], Some(1))); + assert!(db.kv_eq_at(&[1, 2, 3], Some(2))); + assert!(db.kv_eq(&[1, 3])); + let mut commit = CommitSetCanonical::default(); pruning.prune_one(&mut commit); - db.commit(&commit); + db.commit_canonical(&commit); assert!(db.data_eq(&make_db(&[1, 2, 3]))); + assert!(db.kv_eq_at(&[1, 3], Some(0))); + assert!(db.kv_eq_at(&[1, 3], Some(1))); + assert!(db.kv_eq_at(&[1, 2, 3], Some(2))); + assert!(db.kv_eq(&[1, 3])); pruning.prune_one(&mut commit); - db.commit(&commit); + db.commit_canonical(&commit); assert!(db.data_eq(&make_db(&[1, 3]))); + assert!(db.kv_eq_at(&[1, 3], Some(0))); + assert!(db.kv_eq_at(&[1, 3], Some(1))); + assert!(db.kv_eq_at(&[1, 3], Some(2))); + assert!(db.kv_eq(&[1, 3])); pruning.apply_pending(); assert_eq!(pruning.pending_number, 3); } @@ -362,16 +482,16 @@ mod tests { db.commit(&commit); assert!(db.data_eq(&make_db(&[1, 2, 3]))); - let mut commit = CommitSet::default(); + let mut commit = CommitSetCanonical::default(); pruning.prune_one(&mut commit); - db.commit(&commit); + db.commit_canonical(&commit); assert!(db.data_eq(&make_db(&[1, 2, 3]))); - let mut commit = CommitSet::default(); + let mut commit = CommitSetCanonical::default(); pruning.prune_one(&mut commit); - db.commit(&commit); + db.commit_canonical(&commit); assert!(db.data_eq(&make_db(&[1, 2, 3]))); pruning.prune_one(&mut commit); - db.commit(&commit); + db.commit_canonical(&commit); assert!(db.data_eq(&make_db(&[1, 3]))); pruning.apply_pending(); assert_eq!(pruning.pending_number, 3); diff --git a/core/state-db/src/test.rs b/core/state-db/src/test.rs index d90c36990612e..1ca80c57c5735 100644 --- a/core/state-db/src/test.rs +++ b/core/state-db/src/test.rs @@ -16,14 +16,25 @@ //! Test utils -use std::collections::HashMap; +use std::collections::{HashMap, BTreeMap}; use primitives::H256; -use crate::{DBValue, ChangeSet, CommitSet, MetaDb, NodeDb}; +use crate::{ + DBValue, ChangeSet, KvChangeSet, KvKey, MetaDb, NodeDb, KvDb, + CommitSet, CommitSetCanonical, +}; +use historied_data::tree::Serialized; +use historied_data::PruneResult; +use historied_data::linear::DefaultVersion; + +type Ser<'a> = Serialized<'a, DefaultVersion>; #[derive(Default, Debug, Clone, PartialEq, Eq)] pub struct TestDb { pub data: HashMap, pub meta: HashMap, DBValue>, + pub kv: HashMap, + // Heuristic : increase on commit_canonical. + pub kv_last_block: u64, } impl MetaDb for TestDb { @@ -34,6 +45,28 @@ impl MetaDb for TestDb { } } +impl KvDb for TestDb { + type Error = (); + + fn get_kv(&self, key: &[u8], state: &u64) -> Result, ()> { + Ok(self.kv.get(key) + .map(|s| Ser::from_slice(s.as_slice())) + .and_then(|s| s.get(*state) + .unwrap_or(None) // flatten + .map(Into::into) + )) + } + + fn get_kv_pairs(&self, state: &u64) -> Vec<(KvKey, DBValue)> { + self.kv.iter().filter_map(|(a, s)| ( + Ser::from_slice(s.as_slice()) + .get(*state) + .unwrap_or(None) // flatten + .map(|v| (a.clone(), v.to_vec())) + )).collect() + } +} + impl NodeDb for TestDb { type Error = (); type Key = H256; @@ -46,19 +79,67 @@ impl NodeDb for TestDb { impl TestDb { pub fn commit(&mut self, commit: &CommitSet) { self.data.extend(commit.data.inserted.iter().cloned()); - self.meta.extend(commit.meta.inserted.iter().cloned()); for k in commit.data.deleted.iter() { self.data.remove(k); } + for (k, o) in commit.kv.iter() { + let encoded = self.kv.entry(k.clone()) + .or_insert_with(|| Ser::default().into_vec()); + let mut ser = Ser::from_mut(&mut (*encoded)); + ser.push(self.kv_last_block, o.as_ref().map(|v| v.as_slice())); + } self.meta.extend(commit.meta.inserted.iter().cloned()); for k in commit.meta.deleted.iter() { self.meta.remove(k); } } + pub fn commit_canonical(&mut self, commit: &CommitSetCanonical) { + + self.kv_last_block += 1; + self.commit(&commit.0); + + if let Some((block_prune, kv_prune_key)) = commit.1.as_ref() { + for k in kv_prune_key.iter() { + match self.kv.get_mut(k).map(|v| { + let mut ser = Ser::from_mut(v); + ser.prune(*block_prune) + }) { + Some(PruneResult::Cleared) => { let _ = self.kv.remove(k); }, + Some(PruneResult::Changed) // changed applyied on mutable buffer without copy. + | Some(PruneResult::Unchanged) + | None => (), + } + } + } + } + pub fn data_eq(&self, other: &TestDb) -> bool { self.data == other.data } + + pub fn kv_eq_at(&self, values: &[u64], block: Option) -> bool { + let block = block.unwrap_or(self.kv_last_block); + let data = make_kv_changeset(values, &[]); + let self_kv: BTreeMap<_, _> = self.get_kv_pairs(&block).into_iter().collect(); + self_kv == data.into_iter().filter_map(|(k, v)| v.map(|v| (k,v))).collect() + } + + pub fn kv_eq(&self, values: &[u64]) -> bool { + self.kv_eq_at(values, None) + } + + pub fn initialize_kv(&mut self, inserted: &[u64]) { + let data = make_kv_changeset(inserted, &[]); + self.kv = data.into_iter() + .filter_map(|(k, v)| v.map(|v| (k,v))) + .map(|(k, v)| { + let mut ser = Ser::default(); + ser.push(self.kv_last_block, Some(v.as_slice())); + (k, ser.into_vec()) + }) + .collect(); + } } pub fn make_changeset(inserted: &[u64], deleted: &[u64]) -> ChangeSet { @@ -73,10 +154,43 @@ pub fn make_changeset(inserted: &[u64], deleted: &[u64]) -> ChangeSet { } } +pub fn make_kv_changeset( + inserted: &[u64], + deleted: &[u64], +) -> KvChangeSet { + inserted.iter() + .map(|v| ( + H256::from_low_u64_be(*v).as_bytes().to_vec(), + Some(H256::from_low_u64_be(*v).as_bytes().to_vec()), + )) + .chain( + deleted + .iter() + .map(|v| (H256::from_low_u64_be(*v).as_bytes().to_vec(), None)) + ) + .collect() +} + pub fn make_commit(inserted: &[u64], deleted: &[u64]) -> CommitSet { CommitSet { data: make_changeset(inserted, deleted), meta: ChangeSet::default(), + kv: KvChangeSet::default(), + } +} + +pub fn make_commit_both(inserted: &[u64], deleted: &[u64]) -> CommitSetCanonical { + (CommitSet { + data: make_changeset(inserted, deleted), + meta: ChangeSet::default(), + kv: make_kv_changeset(inserted, deleted), + }, None) +} + +impl CommitSet { + pub fn initialize_kv(&mut self, inserted: &[u64], deleted: &[u64]) { + let data = make_kv_changeset(inserted, deleted); + self.kv = data; } } @@ -89,6 +203,7 @@ pub fn make_db(inserted: &[u64]) -> TestDb { }) .collect(), meta: Default::default(), + kv: Default::default(), + kv_last_block: Default::default(), } } - diff --git a/core/state-machine/src/backend.rs b/core/state-machine/src/backend.rs index e2f398ef7ccae..1eb829d1dda4f 100644 --- a/core/state-machine/src/backend.rs +++ b/core/state-machine/src/backend.rs @@ -21,10 +21,15 @@ use log::warn; use hash_db::Hasher; use crate::trie_backend::TrieBackend; use crate::trie_backend_essence::TrieBackendStorage; +use crate::kv_backend::{KvBackend, InMemory as InMemoryKvBackend}; use trie::{ TrieMut, MemoryDB, child_trie_root, default_child_trie_root, TrieConfiguration, trie_types::{TrieDBMut, Layout}, }; +use primitives::child_trie::{ + KeySpace, NO_CHILD_KEYSPACE, prefixed_keyspace_kv, KEYSPACE_COUNTER, + reverse_keyspace, produce_keyspace, +}; /// A state backend is used to read state data and can have changes committed /// to it. @@ -40,9 +45,15 @@ pub trait Backend: std::fmt::Debug { /// Type of trie backend storage. type TrieBackendStorage: TrieBackendStorage; + /// Type of trie backend storage. + type KvBackend: KvBackend; + /// Get keyed storage or None if there is nothing associated. fn storage(&self, key: &[u8]) -> Result>, Self::Error>; + /// Access a value in the key value storage. + fn kv_storage(&self, key: &[u8]) -> Result>, Self::Error>; + /// Get keyed storage value hash or None if there is nothing associated. fn storage_hash(&self, key: &[u8]) -> Result, Self::Error> { self.storage(key).map(|v| v.map(|v| H::hash(&v))) @@ -56,6 +67,11 @@ pub trait Backend: std::fmt::Debug { self.child_storage(storage_key, key).map(|v| v.map(|v| H::hash(&v))) } + /// Get technical keyspace use for child storage key. + fn get_child_keyspace(&self, storage_key: &[u8]) -> Result, Self::Error> { + self.kv_storage(&prefixed_keyspace_kv(&storage_key)) + } + /// true if a key exists in storage. fn exists_storage(&self, key: &[u8]) -> Result { Ok(self.storage(key)?.is_some()) @@ -95,14 +111,33 @@ pub trait Backend: std::fmt::Debug { /// Calculate the child storage root, with given delta over what is already stored in /// the backend, and produce a "transaction" that can be used to commit. The second argument /// is true if child storage root equals default storage root. - fn child_storage_root(&self, storage_key: &[u8], delta: I) -> (Vec, bool, Self::Transaction) + fn child_storage_root( + &self, + storage_key: &[u8], + keyspace: &KeySpace, + delta: I, + ) -> (Vec, bool, Self::Transaction) where I: IntoIterator, Option>)>, H::Out: Ord; + /// Produce transaction for a given kv information deltas. + fn kv_transaction(&self, delta: I) -> Self::Transaction + where + I: IntoIterator, Option>)>; + /// Get all key/value pairs into a Vec. fn pairs(&self) -> Vec<(Vec, Vec)>; + /// Get all children storage keys + fn children_storage_keys(&self) -> Vec>; + + /// Get all key/value pairs into a Vec for a child storage. + fn child_pairs(&self, child_storage_key: &[u8]) -> Vec<(Vec, Vec)>; + + /// Get all key/value pairs of kv storage. + fn kv_pairs(&self) -> HashMap, Option>>; + /// Get all keys with given prefix fn keys(&self, prefix: &[u8]) -> Vec> { let mut all = Vec::new(); @@ -118,30 +153,70 @@ pub trait Backend: std::fmt::Debug { } /// Try convert into trie backend. - fn as_trie_backend(&mut self) -> Option<&TrieBackend> { + fn as_trie_backend(&mut self) -> Option< + &TrieBackend + > { None } /// Calculate the storage root, with given delta over what is already stored /// in the backend, and produce a "transaction" that can be used to commit. /// Does include child storage updates. - fn full_storage_root( + fn full_storage_root( &self, delta: I1, - child_deltas: I2) - -> (H::Out, Self::Transaction) + child_deltas: I2, + kv_deltas: I3, + ) -> Result<(H::Out, Self::Transaction), Self::Error> where I1: IntoIterator, Option>)>, I2i: IntoIterator, Option>)>, I2: IntoIterator, I2i)>, + I3: IntoIterator, Option>)>, ::Out: Ord, { let mut txs: Self::Transaction = Default::default(); + let mut child_roots: Vec<_> = Default::default(); + + let mut counter_keyspace = None; + let mut new_keyspaces = Vec::new(); // child first for (storage_key, child_delta) in child_deltas { + let keyspace = match self.get_child_keyspace(storage_key.as_slice())? { + Some(keyspace) => keyspace, + None => { + if counter_keyspace.is_none() { + let counter_keyspace_enc = self.kv_storage(KEYSPACE_COUNTER)? + .unwrap_or(produce_keyspace(0)); + let keyspace = reverse_keyspace(counter_keyspace_enc.as_slice()) + .expect("Keyspaces are never added manually so encoding is valid"); + counter_keyspace = Some(keyspace); + } + // increment counter + counter_keyspace.as_mut().map(|c| { + *c += 1; + }); + let enc_counter_keyspace = produce_keyspace( + *counter_keyspace.as_ref().expect("lazy init at start of this block") + ); + new_keyspaces.push(( + prefixed_keyspace_kv(storage_key.as_slice()), + Some(enc_counter_keyspace.clone()), + )); + enc_counter_keyspace + }, + }; + + counter_keyspace.map(|c| { + new_keyspaces.push(( + KEYSPACE_COUNTER.to_vec(), + Some(produce_keyspace(c)), + )); + }); + let (child_root, empty, child_txs) = - self.child_storage_root(&storage_key[..], child_delta); + self.child_storage_root(&storage_key[..], &keyspace, child_delta); txs.consolidate(child_txs); if empty { child_roots.push((storage_key, None)); @@ -153,14 +228,19 @@ pub trait Backend: std::fmt::Debug { delta.into_iter().chain(child_roots.into_iter()) ); txs.consolidate(parent_txs); - (root, txs) + txs.consolidate(self.kv_transaction( + kv_deltas.into_iter().chain(new_keyspaces.into_iter()) + )); + Ok((root, txs)) } + } impl<'a, T: Backend, H: Hasher> Backend for &'a T { type Error = T::Error; type Transaction = T::Transaction; type TrieBackendStorage = T::TrieBackendStorage; + type KvBackend = T::KvBackend; fn storage(&self, key: &[u8]) -> Result>, Self::Error> { (*self).storage(key) @@ -170,6 +250,10 @@ impl<'a, T: Backend, H: Hasher> Backend for &'a T { (*self).child_storage(storage_key, key) } + fn kv_storage(&self, key: &[u8]) -> Result>, Self::Error> { + (*self).kv_storage(key) + } + fn for_keys_in_child_storage(&self, storage_key: &[u8], f: F) { (*self).for_keys_in_child_storage(storage_key, f) } @@ -190,18 +274,43 @@ impl<'a, T: Backend, H: Hasher> Backend for &'a T { (*self).storage_root(delta) } - fn child_storage_root(&self, storage_key: &[u8], delta: I) -> (Vec, bool, Self::Transaction) + fn child_storage_root( + &self, + storage_key: &[u8], + keyspace: &KeySpace, + delta: I, + ) -> (Vec, bool, Self::Transaction) where I: IntoIterator, Option>)>, H::Out: Ord, { - (*self).child_storage_root(storage_key, delta) + (*self).child_storage_root(storage_key, keyspace, delta) + } + + fn kv_transaction(&self, delta: I) -> Self::Transaction + where + I: IntoIterator, Option>)> + { + (*self).kv_transaction(delta) } fn pairs(&self) -> Vec<(Vec, Vec)> { (*self).pairs() } + fn children_storage_keys(&self) -> Vec> { + (*self).children_storage_keys() + } + + fn child_pairs(&self, child_storage_key: &[u8]) -> Vec<(Vec, Vec)> { + (*self).child_pairs(child_storage_key) + } + + fn kv_pairs(&self) -> HashMap, Option>> { + (*self).kv_pairs() + } + + fn for_key_values_with_prefix(&self, prefix: &[u8], f: F) { (*self).for_key_values_with_prefix(prefix, f); } @@ -219,12 +328,33 @@ impl Consolidate for () { } } -impl Consolidate for Vec<(Option>, Vec, Option>)> { +impl Consolidate for Vec { fn consolidate(&mut self, mut other: Self) { self.append(&mut other); } } +impl Consolidate for HashMap { + fn consolidate(&mut self, other: Self) { + self.extend(other); + } +} + +impl Consolidate for (U, V) { + fn consolidate(&mut self, other: Self) { + self.0.consolidate(other.0); + self.1.consolidate(other.1); + } +} + + +impl Consolidate for InMemoryTransaction { + fn consolidate(&mut self, other: Self) { + self.storage.consolidate(other.storage); + self.kv.consolidate(other.kv); + } +} + impl> Consolidate for trie::GenericMemoryDB { fn consolidate(&mut self, other: Self) { trie::GenericMemoryDB::consolidate(self, other) @@ -250,7 +380,8 @@ impl error::Error for Void { /// tests. pub struct InMemory { inner: HashMap>, HashMap, Vec>>, - trie: Option, H>>, + kv: Option, + trie: Option, H, InMemoryKvBackend>>, _hasher: PhantomData, } @@ -265,6 +396,7 @@ impl Default for InMemory { InMemory { inner: Default::default(), trie: None, + kv: Some(Default::default()), _hasher: PhantomData, } } @@ -275,6 +407,7 @@ impl Clone for InMemory { InMemory { inner: self.inner.clone(), trie: None, + kv: self.kv.clone(), _hasher: PhantomData, } } @@ -283,48 +416,85 @@ impl Clone for InMemory { impl PartialEq for InMemory { fn eq(&self, other: &Self) -> bool { self.inner.eq(&other.inner) + && self.kv().eq(other.kv()) } } impl InMemory { + fn kv(&self) -> &InMemoryKvBackend { + if let Some(kv) = self.kv.as_ref() { + kv + } else { + self.trie.as_ref().unwrap().kv_backend() + } + } + + fn kv_mut(&mut self) -> &mut InMemoryKvBackend { + if let Some(kv) = self.kv.as_mut() { + kv + } else { + self.trie.as_mut().unwrap().kv_backend_mut() + } + } + + fn extract_kv(&mut self) -> InMemoryKvBackend { + if let Some(kv) = self.kv.take() { + kv + } else { + std::mem::replace( + self.trie.as_mut().unwrap().kv_backend_mut(), + Default::default(), + ) + } + } + /// Copy the state, with applied updates pub fn update(&self, changes: >::Transaction) -> Self { let mut inner: HashMap<_, _> = self.inner.clone(); - for (storage_key, key, val) in changes { + let mut kv: HashMap<_, _> = self.kv().clone(); + for (storage_key, key, val) in changes.storage { match val { Some(v) => { inner.entry(storage_key).or_default().insert(key, v); }, None => { inner.entry(storage_key).or_default().remove(&key); }, } } + kv.extend(changes.kv); - inner.into() + let kv = Some(kv); + InMemory { inner, kv, trie: None, _hasher: PhantomData } } } -impl From>, HashMap, Vec>>> for InMemory { - fn from(inner: HashMap>, HashMap, Vec>>) -> Self { +type TupleInit = ( + HashMap>, HashMap, Vec>>, + HashMap, Option>, +); + +impl From for InMemory { + fn from(inner: TupleInit) -> Self { InMemory { - inner: inner, + inner: inner.0, trie: None, + kv: Some(inner.1), _hasher: PhantomData, } } } -impl From<( - HashMap, Vec>, - HashMap, HashMap, Vec>>, -)> for InMemory { - fn from(inners: ( - HashMap, Vec>, - HashMap, HashMap, Vec>>, - )) -> Self { - let mut inner: HashMap>, HashMap, Vec>> - = inners.1.into_iter().map(|(k, v)| (Some(k), v)).collect(); - inner.insert(None, inners.0); +type TupleInit2 = ( + HashMap, Vec>, + HashMap, HashMap, Vec>>, + HashMap, Option>, +); + +impl From for InMemory { + fn from(tuple: TupleInit2) -> Self { + let mut inner: HashMap<_, _> = tuple.1.into_iter().map(|(k, v)| (Some(k), v)).collect(); + inner.insert(None, tuple.0); InMemory { inner: inner, trie: None, + kv: Some(tuple.2), _hasher: PhantomData, } } @@ -337,20 +507,39 @@ impl From, Vec>> for InMemory { InMemory { inner: expanded, trie: None, + kv: Some(Default::default()), _hasher: PhantomData, } } } -impl From>, Vec, Option>)>> for InMemory { - fn from(inner: Vec<(Option>, Vec, Option>)>) -> Self { +type TupleTx = ( + Vec<(Option>, Vec, Option>)>, + HashMap, Option>, +); + + +impl From for InMemory { + fn from(inner: TupleTx) -> Self { + let mut expanded: HashMap>, HashMap, Vec>> = HashMap::new(); + for (child_key, key, value) in inner.0 { + if let Some(value) = value { + expanded.entry(child_key).or_default().insert(key, value); + } + } + (expanded, inner.1).into() + } +} + +impl From for InMemory { + fn from(inner: InMemoryTransaction) -> Self { let mut expanded: HashMap>, HashMap, Vec>> = HashMap::new(); - for (child_key, key, value) in inner { + for (child_key, key, value) in inner.storage { if let Some(value) = value { expanded.entry(child_key).or_default().insert(key, value); } } - expanded.into() + (expanded, inner.kv).into() } } @@ -361,10 +550,21 @@ impl InMemory { } } +#[derive(Default)] +/// Transaction produced by the state machine execution for +/// in memory storage. +pub struct InMemoryTransaction { + /// State trie key values changes (both top and child trie). + pub storage: Vec<(Option>, Vec, Option>)>, + /// Changes to non trie key value datas. + pub kv: HashMap, Option>>, +} + impl Backend for InMemory { type Error = Void; - type Transaction = Vec<(Option>, Vec, Option>)>; + type Transaction = InMemoryTransaction; type TrieBackendStorage = MemoryDB; + type KvBackend = InMemoryKvBackend; fn storage(&self, key: &[u8]) -> Result>, Self::Error> { Ok(self.inner.get(&None).and_then(|map| map.get(key).map(Clone::clone))) @@ -374,6 +574,14 @@ impl Backend for InMemory { Ok(self.inner.get(&Some(storage_key.to_vec())).and_then(|map| map.get(key).map(Clone::clone))) } + fn kv_storage(&self, key: &[u8]) -> Result>, Self::Error> { + Ok( + self.kv().get(key) + .map(Clone::clone) + .unwrap_or(None) + ) + } + fn exists_storage(&self, key: &[u8]) -> Result { Ok(self.inner.get(&None).map(|map| map.get(key).is_some()).unwrap_or(false)) } @@ -414,34 +622,55 @@ impl Backend for InMemory { let full_transaction = transaction.into_iter().map(|(k, v)| (None, k, v)).collect(); - (root, full_transaction) + (root, InMemoryTransaction { storage: full_transaction, kv: Default::default() }) } - fn child_storage_root(&self, storage_key: &[u8], delta: I) -> (Vec, bool, Self::Transaction) + fn child_storage_root( + &self, + storage_key: &[u8], + _keyspace: &KeySpace, + delta: I, + ) -> (Vec, bool, Self::Transaction) where I: IntoIterator, Option>)>, H::Out: Ord { - let storage_key = storage_key.to_vec(); + let storage_key = Some(storage_key.to_vec()); - let existing_pairs = self.inner.get(&Some(storage_key.clone())) + let existing_pairs = self.inner.get(&storage_key) .into_iter() .flat_map(|map| map.iter().map(|(k, v)| (k.clone(), Some(v.clone())))); let transaction: Vec<_> = delta.into_iter().collect(); let root = child_trie_root::, _, _, _>( - &storage_key, + storage_key.as_ref().expect("Initialized to some"), existing_pairs.chain(transaction.iter().cloned()) .collect::>() .into_iter() .filter_map(|(k, maybe_val)| maybe_val.map(|val| (k, val))) ); - let full_transaction = transaction.into_iter().map(|(k, v)| (Some(storage_key.clone()), k, v)).collect(); + let full_transaction = transaction.into_iter() + .map(|(k, v)| (storage_key.clone(), k, v)).collect(); - let is_default = root == default_child_trie_root::>(&storage_key); + let is_default = root == default_child_trie_root::>( + storage_key.as_ref().expect("Initialized to some") + ); + + ( + root, + is_default, + InMemoryTransaction { storage: full_transaction, kv: Default::default() }, + ) + } - (root, is_default, full_transaction) + fn kv_transaction(&self, delta: I) -> Self::Transaction + where + I: IntoIterator, Option>)> + { + let mut kv = self.kv().clone(); + kv.extend(delta.into_iter()); + InMemoryTransaction { storage: Default::default(), kv} } fn pairs(&self) -> Vec<(Vec, Vec)> { @@ -451,6 +680,21 @@ impl Backend for InMemory { .collect() } + fn children_storage_keys(&self) -> Vec> { + self.inner.iter().filter_map(|(child, _)| child.clone()).collect() + } + + fn child_pairs(&self, storage_key: &[u8]) -> Vec<(Vec, Vec)> { + self.inner.get(&Some(storage_key.to_vec())) + .into_iter() + .flat_map(|map| map.iter().map(|(k, v)| (k.clone(), v.clone()))) + .collect() + } + + fn kv_pairs(&self) -> HashMap, Option>> { + self.kv().clone() + } + fn keys(&self, prefix: &[u8]) -> Vec> { self.inner.get(&None) .into_iter() @@ -465,7 +709,9 @@ impl Backend for InMemory { .collect() } - fn as_trie_backend(&mut self)-> Option<&TrieBackend> { + fn as_trie_backend(&mut self)-> Option< + &TrieBackend + > { let mut mdb = MemoryDB::default(); let mut root = None; let mut new_child_roots = Vec::new(); @@ -489,7 +735,7 @@ impl Backend for InMemory { Some(root) => root, None => insert_into_memory_db::(&mut mdb, ::std::iter::empty())?, }; - self.trie = Some(TrieBackend::new(mdb, root)); + self.trie = Some(TrieBackend::new(mdb, root, self.extract_kv())); self.trie.as_ref() } } diff --git a/core/state-machine/src/basic.rs b/core/state-machine/src/basic.rs index c2d1a0e3950d0..21e5ea5613441 100644 --- a/core/state-machine/src/basic.rs +++ b/core/state-machine/src/basic.rs @@ -24,6 +24,7 @@ use trie::trie_types::Layout; use primitives::{ storage::{well_known_keys::is_child_storage_key, ChildStorageKey}, traits::Externalities, Blake2Hasher, hash::H256, + child_trie::{KeySpace, NO_CHILD_KEYSPACE}, }; use log::warn; @@ -195,8 +196,9 @@ impl Externalities for BasicExternalities { fn child_storage_root(&mut self, storage_key: ChildStorageKey) -> Vec { if let Some(child) = self.children.get(storage_key.as_ref()) { let delta = child.clone().into_iter().map(|(k, v)| (k, Some(v))); - - InMemory::::default().child_storage_root(storage_key.as_ref(), delta).0 + // no transaction produce, just send a dummy + let keyspace = NO_CHILD_KEYSPACE.to_vec(); + InMemory::::default().child_storage_root(storage_key.as_ref(), &keyspace, delta).0 } else { default_child_trie_root::>(storage_key.as_ref()) } diff --git a/core/state-machine/src/changes_trie/build.rs b/core/state-machine/src/changes_trie/build.rs index 10c38a41e2650..fca828959f441 100644 --- a/core/state-machine/src/changes_trie/build.rs +++ b/core/state-machine/src/changes_trie/build.rs @@ -427,7 +427,8 @@ mod test { extrinsics: Some(vec![0, 2].into_iter().collect()) }) ].into_iter().collect()), - ].into_iter().collect() + ].into_iter().collect(), + kv: vec![].into_iter().collect(), }, committed: OverlayedChangeSet { top: vec![ (EXTRINSIC_INDEX.to_vec(), OverlayedValue { @@ -451,6 +452,7 @@ mod test { }) ].into_iter().collect()), ].into_iter().collect(), + kv: vec![].into_iter().collect(), }, changes_trie_config: Some(config.clone()), }; diff --git a/core/state-machine/src/ext.rs b/core/state-machine/src/ext.rs index 3433aee92cfcf..b7b0cf44f3b08 100644 --- a/core/state-machine/src/ext.rs +++ b/core/state-machine/src/ext.rs @@ -27,6 +27,7 @@ use hash_db::Hasher; use primitives::{ storage::{ChildStorageKey, well_known_keys::is_child_storage_key}, traits::Externalities, hexdisplay::HexDisplay, hash::H256, + child_trie::{KeySpace, NO_CHILD_KEYSPACE, prefixed_keyspace_kv}, }; use trie::{trie_types::Layout, MemoryDB, default_child_trie_root}; use externalities::Extensions; @@ -148,7 +149,6 @@ where } } -#[cfg(test)] impl<'a, H, N, B, T> Ext<'a, H, N, B, T> where H: Hasher, @@ -156,6 +156,7 @@ where T: 'a + ChangesTrieStorage, N: crate::changes_trie::BlockNumber, { + #[cfg(test)] pub fn storage_pairs(&self) -> Vec<(Vec, Vec)> { use std::collections::HashMap; @@ -168,6 +169,16 @@ where .filter_map(|(k, maybe_val)| maybe_val.map(|val| (k, val))) .collect() } + + fn get_child_keyspace(&self, storage_key: &[u8]) -> Option { + match self.overlay.kv_storage(prefixed_keyspace_kv(storage_key).as_slice()) { + Some(Some(keyspace)) => return Some(keyspace.to_vec()), + Some(None) => return None, + None => (), + } + self.backend.get_child_keyspace(storage_key) + .expect(EXT_NOT_ALLOWED_TO_FAIL) + } } impl<'a, H, B, T, N> Externalities for Ext<'a, H, N, B, T> @@ -441,7 +452,10 @@ where let delta = self.overlay.committed.top.iter().map(|(k, v)| (k.clone(), v.value.clone())) .chain(self.overlay.prospective.top.iter().map(|(k, v)| (k.clone(), v.value.clone()))); - let (root, transaction) = self.backend.full_storage_root(delta, child_delta_iter); + let kv_delta = self.overlay.committed.kv.clone().into_iter() + .chain(self.overlay.prospective.kv.clone().into_iter()); + let (root, transaction) = self.backend.full_storage_root(delta, child_delta_iter, kv_delta) + .expect(EXT_NOT_ALLOWED_TO_FAIL); self.storage_transaction = Some((transaction, root)); trace!(target: "state-trace", "{:04x}: Root {}", self.id, @@ -472,9 +486,12 @@ where .flat_map(|map| map.iter().map(|(k, v)| (k.clone(), v.value.clone()))) .chain(self.overlay.prospective.children.get(storage_key) .into_iter() - .flat_map(|map| map.clone().into_iter().map(|(k, v)| (k.clone(), v.value.clone())))); + .flat_map(|map| map.iter().map(|(k, v)| (k.clone(), v.value.clone())))); - let root = self.backend.child_storage_root(storage_key, delta).0; + let keyspace = self.get_child_keyspace(storage_key) + // no need to generate keyspace here (tx are dropped) + .unwrap_or_else(|| NO_CHILD_KEYSPACE.to_vec()); + let root = self.backend.child_storage_root(storage_key, &keyspace, delta).0; self.overlay.set_storage(storage_key.to_vec(), Some(root.to_vec())); diff --git a/core/state-machine/src/kv_backend.rs b/core/state-machine/src/kv_backend.rs new file mode 100644 index 0000000000000..8f3cb30752128 --- /dev/null +++ b/core/state-machine/src/kv_backend.rs @@ -0,0 +1,59 @@ +// Copyright 2017-2019 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate 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. + +// Substrate 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 Substrate. If not, see . + +//! Backend for storing data without a state. + +use std::sync::Arc; +use std::collections::HashMap; +use std::ops::Deref; + +/// This covers kv values access. +/// It target a single history state (state machine +/// only run for a single history state). +pub trait KvBackend: Send + Sync { + /// Retrieve a value from storage under given key. + fn get(&self, key: &[u8]) -> Result>, String>; + + /// Return all values (in memory) for this backend, mainly for + /// tests. This method should only be use for testing or + /// for small kv. + /// It returns a map to be aligned with internal in memory storage + /// types. + fn pairs(&self) -> HashMap, Option>>; +} + +/// need to keep multiple block state. +pub type InMemory = HashMap, Option>>; + +impl KvBackend for InMemory { + fn get(&self, key: &[u8]) -> Result>, String> { + Ok(self.get(key).map(Clone::clone).unwrap_or(None)) + } + + fn pairs(&self) -> HashMap, Option>> { + self.clone() + } +} + +impl KvBackend for Arc { + fn get(&self, key: &[u8]) -> Result>, String> { + KvBackend::get(self.deref(), key) + } + + fn pairs(&self) -> HashMap, Option>> { + KvBackend::pairs(self.deref()) + } +} diff --git a/core/state-machine/src/lib.rs b/core/state-machine/src/lib.rs index c3092367f0646..3e3626e9663e7 100644 --- a/core/state-machine/src/lib.rs +++ b/core/state-machine/src/lib.rs @@ -29,6 +29,8 @@ use primitives::{ use overlayed_changes::OverlayedChangeSet; use externalities::Extensions; +pub use crate::kv_backend::{KvBackend, InMemory as InMemoryKvBackend}; + pub mod backend; mod changes_trie; mod error; @@ -39,6 +41,8 @@ mod overlayed_changes; mod proving_backend; mod trie_backend; mod trie_backend_essence; +// TODO EMCH make it private +pub mod kv_backend; pub use trie::{trie_types::{Layout, TrieDBMut}, TrieMut, DBValue, MemoryDB}; pub use testing::TestExternalities; @@ -483,8 +487,8 @@ where /// /// Note: changes to code will be in place if this call is made again. For running partial /// blocks (e.g. a transaction at a time), ensure a different method is used. -pub fn prove_execution_on_trie_backend( - trie_backend: &TrieBackend, +pub fn prove_execution_on_trie_backend( + trie_backend: &TrieBackend, overlay: &mut OverlayedChanges, exec: &Exec, method: &str, @@ -495,6 +499,8 @@ where S: trie_backend_essence::TrieBackendStorage, H: Hasher, Exec: CodeExecutor, + H::Out: Ord + 'static, + O: KvBackend, { let proving_backend = proving_backend::ProvingBackend::new(trie_backend); let mut sm = StateMachine::<_, H, _, InMemoryChangesTrieStorage, Exec>::new( @@ -531,7 +537,7 @@ where /// Check execution proof on proving backend, generated by `prove_execution` call. pub fn execution_proof_check_on_trie_backend( - trie_backend: &TrieBackend, H>, + trie_backend: &TrieBackend, H, InMemoryKvBackend>, overlay: &mut OverlayedChanges, exec: &Exec, method: &str, @@ -591,18 +597,19 @@ where } /// Generate storage read proof on pre-created trie backend. -pub fn prove_read_on_trie_backend( - trie_backend: &TrieBackend, +pub fn prove_read_on_trie_backend( + trie_backend: &TrieBackend, keys: I, ) -> Result>, Box> where S: trie_backend_essence::TrieBackendStorage, H: Hasher, H::Out: Ord, + O: KvBackend, I: IntoIterator, I::Item: AsRef<[u8]>, { - let proving_backend = proving_backend::ProvingBackend::<_, H>::new(trie_backend); + let proving_backend = proving_backend::ProvingBackend::<_, H, _>::new(trie_backend); for key in keys.into_iter() { proving_backend .storage(key.as_ref()) @@ -612,8 +619,8 @@ where } /// Generate storage read proof on pre-created trie backend. -pub fn prove_child_read_on_trie_backend( - trie_backend: &TrieBackend, +pub fn prove_child_read_on_trie_backend( + trie_backend: &TrieBackend, storage_key: &[u8], keys: I, ) -> Result>, Box> @@ -621,10 +628,11 @@ where S: trie_backend_essence::TrieBackendStorage, H: Hasher, H::Out: Ord, + O: KvBackend, I: IntoIterator, I::Item: AsRef<[u8]>, { - let proving_backend = proving_backend::ProvingBackend::<_, H>::new(trie_backend); + let proving_backend = proving_backend::ProvingBackend::<_, H, _>::new(trie_backend); for key in keys.into_iter() { proving_backend .child_storage(storage_key, key.as_ref()) @@ -682,7 +690,7 @@ where /// Check storage read proof on pre-created proving backend. pub fn read_proof_check_on_proving_backend( - proving_backend: &TrieBackend, H>, + proving_backend: &TrieBackend, H, InMemoryKvBackend>, key: &[u8], ) -> Result>, Box> where @@ -694,7 +702,7 @@ where /// Check child storage read proof on pre-created proving backend. pub fn read_child_proof_check_on_proving_backend( - proving_backend: &TrieBackend, H>, + proving_backend: &TrieBackend, H, InMemoryKvBackend>, storage_key: &[u8], key: &[u8], ) -> Result>, Box> @@ -1101,6 +1109,39 @@ mod tests { assert!(state_machine.execute(ExecutionStrategy::NativeWhenPossible).is_err()); } + #[test] + fn child_storage_keyspace() { + use crate::trie_backend::tests::test_trie; + let backend = test_trie(); + let mut overlay = OverlayedChanges::default(); + + let subtrie1 = ChildStorageKey::from_slice(b":child_storage:default:sub_test1").unwrap(); + let subtrie2 = ChildStorageKey::from_slice(b":child_storage:default:sub_test2").unwrap(); + let mut tr1 = { + let backend = test_trie(); + let changes_trie_storage = InMemoryChangesTrieStorage::::new(); + let mut ext = Ext::new( + &mut overlay, + &backend, + Some(&changes_trie_storage), + None, + ); + ext.set_child_storage(subtrie1, b"abc".to_vec(), b"def".to_vec()); + ext.set_child_storage(subtrie2, b"abc".to_vec(), b"def".to_vec()); + ext.storage_root(); + (ext.transaction().0).0 + }; + let mut duplicate = false; + for (k, (value, rc)) in tr1.0.drain().iter() { + // look for a key inserted twice: transaction rc is 2 + if *rc == 2 { + duplicate = true; + println!("test duplicate for {:?} {:?}", k, value); + } + } + assert!(!duplicate); + } + #[test] fn cannot_change_changes_trie_config_with_native_else_wasm() { let backend = trie_backend::tests::test_trie(); @@ -1125,4 +1166,5 @@ mod tests { assert!(state_machine.execute(ExecutionStrategy::NativeElseWasm).is_err()); } + } diff --git a/core/state-machine/src/overlayed_changes.rs b/core/state-machine/src/overlayed_changes.rs index 53a66dc49ee05..7ad76808b9d7c 100644 --- a/core/state-machine/src/overlayed_changes.rs +++ b/core/state-machine/src/overlayed_changes.rs @@ -22,6 +22,7 @@ use std::collections::{HashMap, BTreeSet}; use codec::Decode; use crate::changes_trie::{NO_EXTRINSIC_INDEX, Configuration as ChangesTrieConfig}; use primitives::storage::well_known_keys::EXTRINSIC_INDEX; +use primitives::child_trie::{KeySpace, prefixed_keyspace_kv}; /// The overlayed changes to state to be queried on top of the backend. /// @@ -57,6 +58,8 @@ pub struct OverlayedChangeSet { pub top: HashMap, OverlayedValue>, /// Child storage changes. pub children: HashMap, HashMap, OverlayedValue>>, + /// Non trie key value storage changes. + pub kv: HashMap, Option>>, } #[cfg(test)] @@ -65,6 +68,7 @@ impl FromIterator<(Vec, OverlayedValue)> for OverlayedChangeSet { Self { top: iter.into_iter().collect(), children: Default::default(), + kv: Default::default(), } } } @@ -72,13 +76,14 @@ impl FromIterator<(Vec, OverlayedValue)> for OverlayedChangeSet { impl OverlayedChangeSet { /// Whether the change set is empty. pub fn is_empty(&self) -> bool { - self.top.is_empty() && self.children.is_empty() + self.top.is_empty() && self.children.is_empty() && self.kv.is_empty() } /// Clear the change set. pub fn clear(&mut self) { self.top.clear(); self.children.clear(); + self.kv.clear(); } } @@ -132,6 +137,15 @@ impl OverlayedChanges { None } + /// Returns a double-Option: None if the key is unknown (i.e. and the query should be refered + /// to the backend); Some(None) if the key has been deleted. Some(Some(...)) for a key whose + /// value has been set. + pub fn kv_storage(&self, key: &[u8]) -> Option> { + self.prospective.kv.get(key) + .or_else(|| self.committed.kv.get(key)) + .map(|x| x.as_ref().map(AsRef::as_ref)) + } + /// Inserts the given key-value pair into the prospective change set. /// /// `None` can be used to delete a value specified by the given key. @@ -161,6 +175,13 @@ impl OverlayedChanges { } } + /// Inserts the given key-value pair as an auxilliary data. + /// + /// `None` can be used to delete a value specified by the given key. + pub(crate) fn set_kv_storage(&mut self, key: Vec, val: Option>) { + self.prospective.kv.insert(key, val); + } + /// Clear child storage of given storage key. /// /// NOTE that this doesn't take place immediately but written into the prospective @@ -285,6 +306,9 @@ impl OverlayedChanges { .extend(prospective_extrinsics); } } + for (key, val) in self.prospective.kv.drain() { + self.committed.kv.insert(key, val); + } for (storage_key, mut map) in self.prospective.children.drain() { let map_dest = self.committed.children.entry(storage_key).or_default(); for (key, val) in map.drain() { @@ -302,16 +326,24 @@ impl OverlayedChanges { /// Consume `OverlayedChanges` and take committed set. /// + /// TODO EMCH this one is use to feed storage cache: but that means + /// we miss the child trie root update in storage cache and the + /// keyspace updates to (good thing is that kv store can be cache from + /// transaction, a cache update should also be send for the root from full + /// storage root). (both are not in the overlay but change on fsr call). + /// /// Panics: /// Will panic if there are any uncommitted prospective changes. pub fn into_committed(self) -> ( impl Iterator, Option>)>, impl Iterator, impl Iterator, Option>)>)>, + impl Iterator, Option>)>, ){ assert!(self.prospective.is_empty()); (self.committed.top.into_iter().map(|(k, v)| (k, v.value)), self.committed.children.into_iter() - .map(|(sk, v)| (sk, v.into_iter().map(|(k, v)| (k, v.value))))) + .map(|(sk, v)| (sk, v.into_iter().map(|(k, v)| (k, v.value)))), + self.committed.kv.into_iter()) } /// Inserts storage entry responsible for current extrinsic index. @@ -370,27 +402,39 @@ mod tests { let mut overlayed = OverlayedChanges::default(); let key = vec![42, 69, 169, 142]; + let kv_key = vec![43, 69, 169, 142]; assert!(overlayed.storage(&key).is_none()); overlayed.set_storage(key.clone(), Some(vec![1, 2, 3])); + overlayed.set_kv_storage(kv_key.clone(), Some(vec![1, 2, 2])); assert_eq!(overlayed.storage(&key).unwrap(), Some(&[1, 2, 3][..])); + assert_eq!(overlayed.kv_storage(&kv_key).unwrap(), Some(&[1, 2, 2][..])); overlayed.commit_prospective(); assert_eq!(overlayed.storage(&key).unwrap(), Some(&[1, 2, 3][..])); + assert_eq!(overlayed.kv_storage(&kv_key).unwrap(), Some(&[1, 2, 2][..])); overlayed.set_storage(key.clone(), Some(vec![])); + overlayed.set_kv_storage(kv_key.clone(), Some(vec![2])); assert_eq!(overlayed.storage(&key).unwrap(), Some(&[][..])); + assert_eq!(overlayed.kv_storage(&kv_key).unwrap(), Some(&[2][..])); overlayed.set_storage(key.clone(), None); + overlayed.set_kv_storage(kv_key.clone(), None); assert!(overlayed.storage(&key).unwrap().is_none()); + assert!(overlayed.kv_storage(&kv_key).unwrap().is_none()); + overlayed.discard_prospective(); assert_eq!(overlayed.storage(&key).unwrap(), Some(&[1, 2, 3][..])); + assert_eq!(overlayed.kv_storage(&kv_key).unwrap(), Some(&[1, 2, 2][..])); overlayed.set_storage(key.clone(), None); + overlayed.set_kv_storage(kv_key.clone(), None); overlayed.commit_prospective(); assert!(overlayed.storage(&key).unwrap().is_none()); + assert!(overlayed.kv_storage(&kv_key).unwrap().is_none()); } #[test] diff --git a/core/state-machine/src/proving_backend.rs b/core/state-machine/src/proving_backend.rs index c6c4ef1ec8c26..f7ffec14e9e00 100644 --- a/core/state-machine/src/proving_backend.rs +++ b/core/state-machine/src/proving_backend.rs @@ -23,11 +23,14 @@ use trie::{ MemoryDB, PrefixedMemoryDB, default_child_trie_root, read_trie_value_with, read_child_trie_value_with, record_all_keys }; +use std::collections::HashMap; pub use trie::Recorder; pub use trie::trie_types::{Layout, TrieError}; use crate::trie_backend::TrieBackend; use crate::trie_backend_essence::{Ephemeral, TrieBackendEssence, TrieBackendStorage}; use crate::{Error, ExecutionError, Backend}; +use crate::kv_backend::{KvBackend, InMemory as InMemoryKvBackend}; +use primitives::child_trie::{KeySpace, NO_CHILD_KEYSPACE}; /// Patricia trie-based backend essence which also tracks all touched storage trie values. /// These can be sent to remote node and used as a proof of execution. @@ -61,10 +64,11 @@ impl<'a, S, H> ProvingBackendEssence<'a, S, H> pub fn child_storage( &mut self, storage_key: &[u8], + keyspace: &KeySpace, key: &[u8] ) -> Result>, String> { let root = self.storage(storage_key)? - .unwrap_or(default_child_trie_root::>(storage_key)); + .unwrap_or_else(|| default_child_trie_root::>(storage_key)); let mut read_overlay = S::Overlay::default(); let eph = Ephemeral::new( @@ -77,6 +81,7 @@ impl<'a, S, H> ProvingBackendEssence<'a, S, H> read_child_trie_value_with::, _, _>( storage_key, &eph, + keyspace, &root, key, &mut *self.proof_recorder @@ -103,14 +108,22 @@ impl<'a, S, H> ProvingBackendEssence<'a, S, H> /// Patricia trie-based backend which also tracks all touched storage trie values. /// These can be sent to remote node and used as a proof of execution. -pub struct ProvingBackend<'a, S: 'a + TrieBackendStorage, H: 'a + Hasher> { - backend: &'a TrieBackend, +pub struct ProvingBackend<'a, + S: 'a + TrieBackendStorage, + H: 'a + Hasher, + O: 'a + KvBackend, +> { + backend: &'a TrieBackend, proof_recorder: Rc>>, } -impl<'a, S: 'a + TrieBackendStorage, H: 'a + Hasher> ProvingBackend<'a, S, H> { +impl<'a, + S: 'a + TrieBackendStorage, + H: 'a + Hasher, + O: 'a + KvBackend, +> ProvingBackend<'a, S, H, O> { /// Create new proving backend. - pub fn new(backend: &'a TrieBackend) -> Self { + pub fn new(backend: &'a TrieBackend) -> Self { ProvingBackend { backend, proof_recorder: Rc::new(RefCell::new(Recorder::new())), @@ -119,7 +132,7 @@ impl<'a, S: 'a + TrieBackendStorage, H: 'a + Hasher> ProvingBackend<'a, S, H> /// Create new proving backend with the given recorder. pub fn new_with_recorder( - backend: &'a TrieBackend, + backend: &'a TrieBackend, proof_recorder: Rc>>, ) -> Self { ProvingBackend { @@ -139,21 +152,27 @@ impl<'a, S: 'a + TrieBackendStorage, H: 'a + Hasher> ProvingBackend<'a, S, H> } } -impl<'a, S: 'a + TrieBackendStorage, H: 'a + Hasher> std::fmt::Debug for ProvingBackend<'a, S, H> { +impl<'a, + S: 'a + TrieBackendStorage, + H: 'a + Hasher, + O: 'a + KvBackend, +> std::fmt::Debug for ProvingBackend<'a, S, H, O> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "ProvingBackend") } } -impl<'a, S, H> Backend for ProvingBackend<'a, S, H> +impl<'a, S, H, O> Backend for ProvingBackend<'a, S, H, O> where S: 'a + TrieBackendStorage, H: 'a + Hasher, H::Out: Ord, + O: 'a + KvBackend, { type Error = String; - type Transaction = S::Overlay; + type Transaction = (S::Overlay, HashMap, Option>>); type TrieBackendStorage = PrefixedMemoryDB; + type KvBackend = O; fn storage(&self, key: &[u8]) -> Result>, Self::Error> { ProvingBackendEssence { @@ -164,11 +183,21 @@ impl<'a, S, H> Backend for ProvingBackend<'a, S, H> } fn child_storage(&self, storage_key: &[u8], key: &[u8]) -> Result>, Self::Error> { + // default to using the main keyspace is not an issue + // here as we are in the case of non existing trie + // and will query default trie root to empty trie. + let keyspace = self.get_child_keyspace(storage_key)? + .unwrap_or_else(|| NO_CHILD_KEYSPACE.to_vec()); + ProvingBackendEssence { backend: self.backend.essence(), proof_recorder: &mut *self.proof_recorder.try_borrow_mut() .expect("only fails when already borrowed; child_storage() is non-reentrant; qed"), - }.child_storage(storage_key, key) + }.child_storage(storage_key, &keyspace, key) + } + + fn kv_storage(&self, key: &[u8]) -> Result>, Self::Error> { + self.backend.kv_storage(key) } fn for_keys_in_child_storage(&self, storage_key: &[u8], f: F) { @@ -191,6 +220,18 @@ impl<'a, S, H> Backend for ProvingBackend<'a, S, H> self.backend.pairs() } + fn children_storage_keys(&self) -> Vec> { + self.backend.children_storage_keys() + } + + fn child_pairs(&self, storage_key: &[u8]) -> Vec<(Vec, Vec)> { + self.backend.child_pairs(storage_key) + } + + fn kv_pairs(&self) -> HashMap, Option>> { + self.backend.kv_pairs() + } + fn keys(&self, prefix: &[u8]) -> Vec> { self.backend.keys(prefix) } @@ -205,27 +246,43 @@ impl<'a, S, H> Backend for ProvingBackend<'a, S, H> self.backend.storage_root(delta) } - fn child_storage_root(&self, storage_key: &[u8], delta: I) -> (Vec, bool, Self::Transaction) + fn child_storage_root( + &self, + storage_key: &[u8], + keyspace: &KeySpace, + delta: I, + ) -> (Vec, bool, Self::Transaction) where I: IntoIterator, Option>)>, H::Out: Ord { - self.backend.child_storage_root(storage_key, delta) + self.backend.child_storage_root(storage_key, keyspace, delta) + } + + fn kv_transaction(&self, delta: I) -> Self::Transaction + where + I: IntoIterator, Option>)> + { + self.backend.kv_transaction(delta) } + } /// Create proof check backend. pub fn create_proof_check_backend( root: H::Out, proof: Vec> -) -> Result, H>, Box> +) -> Result, H, InMemoryKvBackend>, Box> where H: Hasher, { let db = create_proof_check_backend_storage(proof); + // run on empty kv (current proof does not require + // kv). + let kv = InMemoryKvBackend::default(); if db.contains(&root, EMPTY_PREFIX) { - Ok(TrieBackend::new(db, root)) + Ok(TrieBackend::new(db, root, kv)) } else { Err(Box::new(ExecutionError::InvalidProof)) } @@ -247,14 +304,16 @@ where #[cfg(test)] mod tests { - use crate::backend::{InMemory}; + use crate::backend::{InMemory, InMemoryTransaction}; use crate::trie_backend::tests::test_trie; use super::*; use primitives::{Blake2Hasher, storage::ChildStorageKey}; + type KvBackend = InMemoryKvBackend; + fn test_proving<'a>( - trie_backend: &'a TrieBackend,Blake2Hasher>, - ) -> ProvingBackend<'a, PrefixedMemoryDB, Blake2Hasher> { + trie_backend: &'a TrieBackend, Blake2Hasher, KvBackend>, + ) -> ProvingBackend<'a, PrefixedMemoryDB, Blake2Hasher, KvBackend> { ProvingBackend::new(trie_backend) } @@ -288,14 +347,19 @@ mod tests { let (trie_root, mut trie_mdb) = trie_backend.storage_root(::std::iter::empty()); let (proving_root, mut proving_mdb) = proving_backend.storage_root(::std::iter::empty()); assert_eq!(trie_root, proving_root); - assert_eq!(trie_mdb.drain(), proving_mdb.drain()); + assert_eq!(trie_mdb.0.drain(), proving_mdb.0.drain()); + assert_eq!(trie_mdb.1, proving_mdb.1); } #[test] fn proof_recorded_and_checked() { let contents = (0..64).map(|i| (None, vec![i], Some(vec![i]))).collect::>(); let in_memory = InMemory::::default(); - let mut in_memory = in_memory.update(contents); + let mut in_memory = in_memory.update(InMemoryTransaction { + storage: contents, + kv: Default::default(), + }); + let in_memory_root = in_memory.storage_root(::std::iter::empty()).0; (0..64).for_each(|i| assert_eq!(in_memory.storage(&[i]).unwrap().unwrap(), vec![i])); @@ -324,11 +388,15 @@ mod tests { .chain((10..15).map(|i| (Some(own2.clone()), vec![i], Some(vec![i])))) .collect::>(); let in_memory = InMemory::::default(); - let mut in_memory = in_memory.update(contents); - let in_memory_root = in_memory.full_storage_root::<_, Vec<_>, _>( + let mut in_memory = in_memory.update(InMemoryTransaction { + storage: contents, + kv: Default::default(), + }); + let in_memory_root = in_memory.full_storage_root::<_, Vec<_>, _, _>( + ::std::iter::empty(), + in_memory.child_storage_keys().map(|k|(k.to_vec(), Vec::new())), ::std::iter::empty(), - in_memory.child_storage_keys().map(|k|(k.to_vec(), Vec::new())) - ).0; + ).unwrap().0; (0..64).for_each(|i| assert_eq!( in_memory.storage(&[i]).unwrap().unwrap(), vec![i] diff --git a/core/state-machine/src/testing.rs b/core/state-machine/src/testing.rs index ca89921ff054a..6c3cbbefddd22 100644 --- a/core/state-machine/src/testing.rs +++ b/core/state-machine/src/testing.rs @@ -19,7 +19,7 @@ use std::{collections::HashMap, any::{Any, TypeId}}; use hash_db::Hasher; use crate::{ - backend::{InMemory, Backend}, OverlayedChanges, + backend::{InMemory, InMemoryTransaction, Backend}, OverlayedChanges, changes_trie::{ build_changes_trie, InMemoryStorage as ChangesTrieInMemoryStorage, BlockNumber as ChangesTrieBlockNumber, @@ -31,13 +31,18 @@ use primitives::{ well_known_keys::{CHANGES_TRIE_CONFIG, CODE, HEAP_PAGES, is_child_storage_key} }, traits::Externalities, hash::H256, Blake2Hasher, + child_trie::{KeySpace, NO_CHILD_KEYSPACE, prefixed_keyspace_kv}, }; use codec::Encode; use externalities::{Extensions, Extension}; const EXT_NOT_ALLOWED_TO_FAIL: &str = "Externalities not allowed to fail within runtime"; -type StorageTuple = (HashMap, Vec>, HashMap, HashMap, Vec>>); +type StorageTuple = ( + HashMap, Vec>, + HashMap, HashMap, Vec>>, + HashMap, Option>, +); /// Simple HashMap-based Externalities impl. pub struct TestExternalities=Blake2Hasher, N: ChangesTrieBlockNumber=u64> { @@ -48,6 +53,12 @@ pub struct TestExternalities=Blake2Hasher, N: ChangesTrieBlo } impl, N: ChangesTrieBlockNumber> TestExternalities { + // TOOD EMCH delete that when genesis support + /// Create a new instance of `TestExternalities` with storage. + pub fn new_todo(storage: StorageTuple2) -> Self { + Self::new_with_code(&[], (storage.0, storage.1, Default::default())) + } + /// Create a new instance of `TestExternalities` with storage. pub fn new(storage: StorageTuple) -> Self { Self::new_with_code(&[], storage) @@ -70,21 +81,32 @@ impl, N: ChangesTrieBlockNumber> TestExternalities { storage.0.insert(CODE.to_vec(), code.to_vec()); let backend: HashMap<_, _> = storage.1.into_iter() - .map(|(keyspace, map)| (Some(keyspace), map)) + .map(|(storage_key, map)| (Some(storage_key), map)) .chain(Some((None, storage.0)).into_iter()) .collect(); TestExternalities { overlay, changes_trie_storage: ChangesTrieInMemoryStorage::new(), - backend: backend.into(), + backend: (backend, storage.2).into(), extensions: Default::default(), } } /// Insert key/value into backend pub fn insert(&mut self, k: Vec, v: Vec) { - self.backend = self.backend.update(vec![(None, k, Some(v))]); + self.backend = self.backend.update(InMemoryTransaction { + storage: vec![(None, k, Some(v))], + kv: Default::default(), + }); + } + + /// Insert key/value into ofstate information backend + pub fn insert_kv(&mut self, k: Vec, v: Vec) { + self.backend = self.backend.update(InMemoryTransaction { + storage: Default::default(), + kv: Some((k, Some(v))).into_iter().collect(), + }); } /// Registers the given extension for this instance. @@ -97,6 +119,18 @@ impl, N: ChangesTrieBlockNumber> TestExternalities { &mut self.changes_trie_storage } + fn kv_storage(&self, key: &[u8]) -> Result>, ()> { + if let Some(change) = self.overlay.kv_storage(key) { + return Ok(change.map(Into::into)); + } + + self.backend.kv_storage(key).map_err(|_|()) + } + + fn get_child_keyspace(&self, storage_key: &[u8]) -> Result, ()> { + self.kv_storage(&prefixed_keyspace_kv(storage_key)) + } + /// Return a new backend with all pending value. pub fn commit_all(&self) -> InMemory { let top = self.overlay.committed.top.clone().into_iter() @@ -111,7 +145,13 @@ impl, N: ChangesTrieBlockNumber> TestExternalities { .collect::>() }); - self.backend.update(top.chain(children).collect()) + let kv = self.overlay.committed.kv.clone().into_iter() + .chain(self.overlay.prospective.kv.clone().into_iter()); + + self.backend.update(InMemoryTransaction { + storage: top.chain(children).collect(), + kv: kv.collect(), + }) } /// Execute the given closure while `self` is set as externalities. @@ -146,6 +186,15 @@ impl, N: ChangesTrieBlockNumber> From for Test } } +// TODO EMCH this is only needed until genesis builds from kv to. +type StorageTuple2 = (HashMap, Vec>, HashMap, HashMap, Vec>>); + +impl, N: ChangesTrieBlockNumber> From for TestExternalities { + fn from(storage: StorageTuple2) -> Self { + Self::new_todo(storage) + } +} + impl Externalities for TestExternalities where H: Hasher, N: ChangesTrieBlockNumber, @@ -262,7 +311,11 @@ impl Externalities for TestExternalities where // compute and memoize let delta = self.overlay.committed.top.iter().map(|(k, v)| (k.clone(), v.value.clone())) .chain(self.overlay.prospective.top.iter().map(|(k, v)| (k.clone(), v.value.clone()))); - self.backend.full_storage_root(delta, child_delta_iter).0 + + // transaction not used afterward, so not using kv. + self.backend.full_storage_root(delta, child_delta_iter, None) + .expect(EXT_NOT_ALLOWED_TO_FAIL).0 + } fn child_storage_root(&mut self, storage_key: ChildStorageKey) -> Vec { @@ -276,7 +329,12 @@ impl Externalities for TestExternalities where .into_iter() .flat_map(|map| map.clone().into_iter().map(|(k, v)| (k, v.value)))); - self.backend.child_storage_root(storage_key, delta) + let keyspace = match self.get_child_keyspace(storage_key) { + Ok(Some(keyspace)) => keyspace, + // no transaction produced, can default when new trie + _ => NO_CHILD_KEYSPACE.to_vec(), + }; + self.backend.child_storage_root(storage_key, &keyspace, delta) }; if is_empty { self.overlay.set_storage(storage_key.into(), None); diff --git a/core/state-machine/src/trie_backend.rs b/core/state-machine/src/trie_backend.rs index ce5773c0b7956..a11c0cb6d6715 100644 --- a/core/state-machine/src/trie_backend.rs +++ b/core/state-machine/src/trie_backend.rs @@ -20,19 +20,27 @@ use log::{warn, debug}; use hash_db::Hasher; use trie::{Trie, delta_trie_root, default_child_trie_root, child_delta_trie_root}; use trie::trie_types::{TrieDB, TrieError, Layout}; +use trie::KeySpacedDB; use crate::trie_backend_essence::{TrieBackendEssence, TrieBackendStorage, Ephemeral}; use crate::Backend; +use crate::kv_backend::KvBackend; +use primitives::storage::well_known_keys::CHILD_STORAGE_KEY_PREFIX; +use std::collections::HashMap; +use primitives::child_trie::{KeySpace, prefixed_keyspace_kv, NO_CHILD_KEYSPACE}; /// Patricia trie-based backend. Transaction type is an overlay of changes to commit. -pub struct TrieBackend, H: Hasher> { +/// A simple key value backend is also accessible for direct key value storage. +pub struct TrieBackend, H: Hasher, O: KvBackend> { essence: TrieBackendEssence, + kv_storage: O, } -impl, H: Hasher> TrieBackend { +impl, O: KvBackend, H: Hasher> TrieBackend { /// Create new trie-based backend. - pub fn new(storage: S, root: H::Out) -> Self { + pub fn new(storage: S, root: H::Out, kv_storage: O) -> Self { TrieBackend { essence: TrieBackendEssence::new(storage, root), + kv_storage, } } @@ -46,6 +54,16 @@ impl, H: Hasher> TrieBackend { self.essence.backend_storage() } + /// Get key value storage backend reference. + pub fn kv_backend(&self) -> &O { + &self.kv_storage + } + + /// Get key value storage backend mutable reference. + pub fn kv_backend_mut(&mut self) -> &mut O { + &mut self.kv_storage + } + /// Get trie root. pub fn root(&self) -> &H::Out { self.essence.root() @@ -55,27 +73,49 @@ impl, H: Hasher> TrieBackend { pub fn into_storage(self) -> S { self.essence.into_storage() } + } -impl, H: Hasher> std::fmt::Debug for TrieBackend { +impl< + S: TrieBackendStorage, + H: Hasher, + O: KvBackend, +> std::fmt::Debug for TrieBackend { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "TrieBackend") } } -impl, H: Hasher> Backend for TrieBackend where +impl< + S: TrieBackendStorage, + H: Hasher, + O: KvBackend, +> Backend for TrieBackend where H::Out: Ord, { type Error = String; - type Transaction = S::Overlay; + type Transaction = (S::Overlay, HashMap, Option>>); type TrieBackendStorage = S; + type KvBackend = O; fn storage(&self, key: &[u8]) -> Result>, Self::Error> { self.essence.storage(key) } fn child_storage(&self, storage_key: &[u8], key: &[u8]) -> Result>, Self::Error> { - self.essence.child_storage(storage_key, key) + let keyspace = if let Some(keyspace) = self.get_child_keyspace(storage_key)? { + keyspace + } else { + // cannot early exit for case where we do not prefix db + // (proof): can be any dummy value here. + NO_CHILD_KEYSPACE.to_vec() + }; + + self.essence.child_storage(storage_key, &keyspace, key) + } + + fn kv_storage(&self, key: &[u8]) -> Result>, Self::Error> { + self.kv_storage.get(key) } fn for_keys_with_prefix(&self, prefix: &[u8], f: F) { @@ -87,7 +127,16 @@ impl, H: Hasher> Backend for TrieBackend where } fn for_keys_in_child_storage(&self, storage_key: &[u8], f: F) { - self.essence.for_keys_in_child_storage(storage_key, f) + let keyspace = match self.get_child_keyspace(storage_key) { + Ok(Some(keyspace)) => keyspace, + Err(e) => { + warn!(target: "trie", "Failed to query keyspace: {}", e); + return; + }, + Ok(None) => return, + }; + + self.essence.for_keys_in_child_storage(storage_key, &keyspace, f) } fn for_child_keys_with_prefix(&self, storage_key: &[u8], prefix: &[u8], f: F) { @@ -99,6 +148,7 @@ impl, H: Hasher> Backend for TrieBackend where let eph = Ephemeral::new(self.essence.backend_storage(), &mut read_overlay); let collect_all = || -> Result<_, Box>> { + let eph = KeySpacedDB::new(&eph, None); let trie = TrieDB::::new(&eph, self.essence.root())?; let mut v = Vec::new(); for x in trie.iter()? { @@ -118,11 +168,65 @@ impl, H: Hasher> Backend for TrieBackend where } } + fn children_storage_keys(&self) -> Vec> { + let mut result = Vec::new(); + self.for_keys_with_prefix(CHILD_STORAGE_KEY_PREFIX, |k| result.push(k.to_vec())); + result + } + + fn child_pairs(&self, storage_key: &[u8]) -> Vec<(Vec, Vec)> { + + let root_slice = self.essence.storage(storage_key) + .unwrap_or(None) + .unwrap_or(default_child_trie_root::>(storage_key)); + let mut root = H::Out::default(); + root.as_mut().copy_from_slice(&root_slice[..]); + + let mut read_overlay = S::Overlay::default(); + let eph = Ephemeral::new(self.essence.backend_storage(), &mut read_overlay); + + let keyspace = match self.get_child_keyspace(storage_key) { + Ok(ks) => ks, + Err(e) => { + debug!(target: "trie", "Error extracting child trie values: {}", e); + return Vec::new(); + } + }; + + let collect_all = || -> Result<_, Box>> { + let mut v = Vec::new(); + // if no keyspace, the child trie just got created. + if let Some(keyspace) = keyspace { + let eph = KeySpacedDB::new(&eph, Some(&keyspace)); + let trie = TrieDB::::new(&eph, &root)?; + for x in trie.iter()? { + let (key, value) = x?; + v.push((key.to_vec(), value.to_vec())); + } + } + + Ok(v) + }; + + match collect_all() { + Ok(v) => v, + Err(e) => { + debug!(target: "trie", "Error extracting child trie values: {}", e); + Vec::new() + } + } + } + + fn kv_pairs(&self) -> HashMap, Option>> { + self.kv_storage.pairs() + } + fn keys(&self, prefix: &[u8]) -> Vec> { let mut read_overlay = S::Overlay::default(); let eph = Ephemeral::new(self.essence.backend_storage(), &mut read_overlay); let collect_all = || -> Result<_, Box>> { + let eph = KeySpacedDB::new(&eph, None); let trie = TrieDB::::new(&eph, self.essence.root())?; let mut v = Vec::new(); for x in trie.iter()? { @@ -138,7 +242,7 @@ impl, H: Hasher> Backend for TrieBackend where collect_all().map_err(|e| debug!(target: "trie", "Error extracting trie keys: {}", e)).unwrap_or_default() } - fn storage_root(&self, delta: I) -> (H::Out, S::Overlay) + fn storage_root(&self, delta: I) -> (H::Out, Self::Transaction) where I: IntoIterator, Option>)> { let mut write_overlay = S::Overlay::default(); @@ -156,13 +260,18 @@ impl, H: Hasher> Backend for TrieBackend where } } - (root, write_overlay) + (root, (write_overlay, Default::default())) } - fn child_storage_root(&self, storage_key: &[u8], delta: I) -> (Vec, bool, Self::Transaction) - where - I: IntoIterator, Option>)>, - H::Out: Ord + fn child_storage_root( + &self, + storage_key: &[u8], + keyspace: &KeySpace, + delta: I, + ) -> (Vec, bool, Self::Transaction) + where + I: IntoIterator, Option>)>, + H::Out: Ord { let default_root = default_child_trie_root::>(storage_key); @@ -184,6 +293,7 @@ impl, H: Hasher> Backend for TrieBackend where match child_delta_trie_root::, _, _, _, _>( storage_key, &mut eph, + keyspace, root.clone(), delta ) { @@ -194,10 +304,21 @@ impl, H: Hasher> Backend for TrieBackend where let is_default = root == default_root; - (root, is_default, write_overlay) + (root, is_default, (write_overlay, Default::default())) + } + + fn kv_transaction(&self, delta: I) -> Self::Transaction + where + I: IntoIterator, Option>)> + { + let mut result = self.kv_storage.pairs(); + result.extend(delta.into_iter()); + (Default::default(), result) } - fn as_trie_backend(&mut self) -> Option<&TrieBackend> { + fn as_trie_backend(&mut self) -> Option< + &TrieBackend + > { Some(self) } } @@ -206,24 +327,31 @@ impl, H: Hasher> Backend for TrieBackend where pub mod tests { use std::collections::HashSet; use primitives::{Blake2Hasher, H256}; - use codec::Encode; - use trie::{TrieMut, PrefixedMemoryDB, trie_types::TrieDBMut}; + use primitives::child_trie::{produce_keyspace, KEYSPACE_COUNTER, reverse_keyspace}; + use trie::{TrieMut, PrefixedMemoryDB, KeySpacedDBMut, trie_types::TrieDBMut}; use super::*; - fn test_db() -> (PrefixedMemoryDB, H256) { + type KvBackend = crate::kv_backend::InMemory; + + const CHILD_KEY_1: &[u8; 27] = b":child_storage:default:sub1"; + + fn test_db() -> (PrefixedMemoryDB, H256, KvBackend) { let mut root = H256::default(); let mut mdb = PrefixedMemoryDB::::default(); + + let keyspace1 = produce_keyspace(1); + let mut sub_root = H256::default(); { - let mut trie = TrieDBMut::new(&mut mdb, &mut root); + let mut mdb = KeySpacedDBMut::new(&mut mdb, Some(&keyspace1)); + let mut trie = TrieDBMut::new(&mut mdb, &mut sub_root); trie.insert(b"value3", &[142]).expect("insert failed"); trie.insert(b"value4", &[124]).expect("insert failed"); - }; - + } + { - let mut sub_root = Vec::new(); - root.encode_to(&mut sub_root); + let mut mdb = KeySpacedDBMut::new(&mut mdb, None); let mut trie = TrieDBMut::new(&mut mdb, &mut root); - trie.insert(b":child_storage:default:sub1", &sub_root).expect("insert failed"); + trie.insert(CHILD_KEY_1, &sub_root[..]).expect("insert failed"); trie.insert(b"key", b"value").expect("insert failed"); trie.insert(b"value1", &[42]).expect("insert failed"); trie.insert(b"value2", &[24]).expect("insert failed"); @@ -232,12 +360,17 @@ pub mod tests { trie.insert(&[i], &[i]).unwrap(); } } - (mdb, root) + // empty history. + let mut kv = crate::kv_backend::InMemory::default(); + kv.insert(KEYSPACE_COUNTER.to_vec(), Some(keyspace1.clone())); + kv.insert(prefixed_keyspace_kv(&CHILD_KEY_1[..]), Some(keyspace1)); + (mdb, root, kv) } - pub(crate) fn test_trie() -> TrieBackend, Blake2Hasher> { - let (mdb, root) = test_db(); - TrieBackend::new(mdb, root) + pub(crate) fn test_trie( + ) -> TrieBackend, Blake2Hasher, KvBackend> { + let (mdb, root, kv) = test_db(); + TrieBackend::new(mdb, root, kv) } #[test] @@ -245,6 +378,15 @@ pub mod tests { assert_eq!(test_trie().storage(b"key").unwrap(), Some(b"value".to_vec())); } + #[test] + fn read_from_child_storage_returns_some() { + let test_trie = test_trie(); + assert_eq!( + test_trie.child_storage(CHILD_KEY_1, b"value3").unwrap(), + Some(vec![142u8]), + ); + } + #[test] fn read_from_storage_returns_none() { assert_eq!(test_trie().storage(b"non-existing-key").unwrap(), None); @@ -257,8 +399,9 @@ pub mod tests { #[test] fn pairs_are_empty_on_empty_storage() { - assert!(TrieBackend::, Blake2Hasher>::new( - PrefixedMemoryDB::default(), + assert!(TrieBackend::, Blake2Hasher, KvBackend>::new( + Default::default(), + Default::default(), Default::default(), ).pairs().is_empty()); } @@ -270,13 +413,16 @@ pub mod tests { #[test] fn storage_root_transaction_is_empty() { - assert!(test_trie().storage_root(::std::iter::empty()).1.drain().is_empty()); + let mut tx = test_trie().storage_root(::std::iter::empty()).1; + assert!(tx.0.drain().is_empty()); + assert!(tx.1.is_empty()); } #[test] fn storage_root_transaction_is_non_empty() { let (new_root, mut tx) = test_trie().storage_root(vec![(b"new-key".to_vec(), Some(b"new-value".to_vec()))]); - assert!(!tx.drain().is_empty()); + assert!(!tx.0.drain().is_empty()); + assert!(tx.1.is_empty()); assert!(new_root != test_trie().storage_root(::std::iter::empty()).0); } diff --git a/core/state-machine/src/trie_backend_essence.rs b/core/state-machine/src/trie_backend_essence.rs index 5a5431963448c..625ca4864e332 100644 --- a/core/state-machine/src/trie_backend_essence.rs +++ b/core/state-machine/src/trie_backend_essence.rs @@ -23,9 +23,10 @@ use log::{debug, warn}; use hash_db::{self, Hasher, EMPTY_PREFIX, Prefix}; use trie::{Trie, MemoryDB, PrefixedMemoryDB, DBValue, default_child_trie_root, read_trie_value, read_child_trie_value, - for_keys_in_child_trie}; + for_keys_in_child_trie, KeySpacedDB}; use trie::trie_types::{TrieDB, TrieError, Layout}; use crate::backend::Consolidate; +use primitives::child_trie::KeySpace; /// Patricia trie-based storage trait. pub trait Storage: Send + Sync { @@ -77,9 +78,17 @@ impl, H: Hasher> TrieBackendEssence { } /// Get the value of child storage at given key. - pub fn child_storage(&self, storage_key: &[u8], key: &[u8]) -> Result>, String> { - let root = self.storage(storage_key)? - .unwrap_or(default_child_trie_root::>(storage_key)); + pub fn child_storage( + &self, + storage_key: &[u8], + keyspace: &KeySpace, + key: &[u8], + ) -> Result>, String> { + let root = if let Some(root) = self.storage(storage_key)? { + root + } else { + return Ok(None) + }; let mut read_overlay = S::Overlay::default(); let eph = Ephemeral { @@ -89,11 +98,16 @@ impl, H: Hasher> TrieBackendEssence { let map_e = |e| format!("Trie lookup error: {}", e); - read_child_trie_value::, _>(storage_key, &eph, &root, key).map_err(map_e) + read_child_trie_value::, _>(storage_key, &eph, &keyspace, &root, key).map_err(map_e) } /// Retrieve all entries keys of child storage and call `f` for each of those keys. - pub fn for_keys_in_child_storage(&self, storage_key: &[u8], f: F) { + pub fn for_keys_in_child_storage( + &self, + storage_key: &[u8], + keyspace: &KeySpace, + f: F, + ) { let root = match self.storage(storage_key) { Ok(v) => v.unwrap_or(default_child_trie_root::>(storage_key)), Err(e) => { @@ -111,6 +125,7 @@ impl, H: Hasher> TrieBackendEssence { if let Err(e) = for_keys_in_child_trie::, _, Ephemeral>( storage_key, &eph, + keyspace, &root, f, ) { @@ -152,7 +167,8 @@ impl, H: Hasher> TrieBackendEssence { }; let mut iter = move || -> Result<(), Box>> { - let trie = TrieDB::::new(&eph, root)?; + let eph = KeySpacedDB::new(&eph, None); + let trie = TrieDB::::new(&eph, &self.root)?; let mut iter = trie.iter()?; iter.seek(prefix)?; diff --git a/core/test-runtime/src/lib.rs b/core/test-runtime/src/lib.rs index e75cb69149d8d..ff023f222cef4 100644 --- a/core/test-runtime/src/lib.rs +++ b/core/test-runtime/src/lib.rs @@ -36,7 +36,7 @@ use primitives::{ use app_crypto::{ed25519, sr25519, RuntimeAppPublic}; pub use app_crypto; use trie_db::{TrieMut, Trie}; -use substrate_trie::PrefixedMemoryDB; +use substrate_trie::{KeySpacedDBMut, KeySpacedDB, PrefixedMemoryDB}; use substrate_trie::trie_types::{TrieDB, TrieDBMut}; use substrate_client::{ @@ -429,8 +429,9 @@ fn code_using_trie() -> u64 { let mut mdb = PrefixedMemoryDB::default(); let mut root = rstd::default::Default::default(); - let _ = { + { let v = &pairs; + let mut mdb = KeySpacedDBMut::new(&mut mdb, None); let mut t = TrieDBMut::::new(&mut mdb, &mut root); for i in 0..v.len() { let key: &[u8]= &v[i].0; @@ -439,9 +440,9 @@ fn code_using_trie() -> u64 { return 101; } } - t - }; + } + let mdb = KeySpacedDB::new(&mdb, None); if let Ok(trie) = TrieDB::::new(&mdb, &root) { if let Ok(iter) = trie.iter() { let mut iter_pairs = Vec::new(); diff --git a/core/test-runtime/src/system.rs b/core/test-runtime/src/system.rs index f3359aa1baf25..20c40e3fed494 100644 --- a/core/test-runtime/src/system.rs +++ b/core/test-runtime/src/system.rs @@ -353,6 +353,7 @@ mod tests { } ], map![], + map![], ) ) } diff --git a/core/trie/src/lib.rs b/core/trie/src/lib.rs index eab05bab28af0..7bbc58f13db98 100644 --- a/core/trie/src/lib.rs +++ b/core/trie/src/lib.rs @@ -23,9 +23,11 @@ mod node_header; mod node_codec; mod trie_stream; +use primitives::child_trie::{KeySpace, keyspace_as_prefix_alloc}; use rstd::boxed::Box; use rstd::vec::Vec; -use hash_db::Hasher; +use rstd::marker::PhantomData; +use hash_db::{Hasher, Prefix}; /// Our `NodeCodec`-specific error. pub use error::Error; /// The Substrate format implementation of `TrieStream`. @@ -130,7 +132,8 @@ pub fn delta_trie_root( DB: hash_db::HashDB, { { - let mut trie = TrieDBMut::::from_existing(&mut *db, &mut root)?; + let mut db = KeySpacedDBMut::new(db, None); + let mut trie = TrieDBMut::::from_existing(&mut db, &mut root)?; for (key, change) in delta { match change { @@ -149,7 +152,9 @@ pub fn read_trie_value, key: &[u8] ) -> Result>, Box>> { - Ok(TrieDB::::new(&*db, root)?.get(key).map(|x| x.map(|val| val.to_vec()))?) + let db = KeySpacedDB::new(db, None); + let db = TrieDB::::new(&db, root)?; + Ok(db.get(key).map(|x| x.map(|val| val.to_vec()))?) } /// Read a value from the trie with given Query. @@ -163,7 +168,9 @@ pub fn read_trie_value_with< key: &[u8], query: Q ) -> Result>, Box>> { - Ok(TrieDB::::new(&*db, root)?.get_with(key, query).map(|x| x.map(|val| val.to_vec()))?) + let db = KeySpacedDB::new(db, None); + let db = TrieDB::::new(&db, root)?; + Ok(db.get_with(key, query).map(|x| x.map(|val| val.to_vec()))?) } /// Determine the default child trie root. @@ -187,6 +194,7 @@ pub fn child_trie_root(_storage_key: &[u8], input pub fn child_delta_trie_root( _storage_key: &[u8], db: &mut DB, + keyspace: &KeySpace, root_vec: Vec, delta: I ) -> Result, Box>> @@ -197,12 +205,11 @@ pub fn child_delta_trie_root( DB: hash_db::HashDB + hash_db::PlainDB, trie_db::DBValue>, { - let mut root = TrieHash::::default(); - // root is fetched from DB, not writable by runtime, so it's always valid. - root.as_mut().copy_from_slice(&root_vec); - + // keyspaced is needed (db can be init from this operation, this is not only root calculation) + let mut db = KeySpacedDBMut::new(&mut *db, Some(keyspace)); + let mut root = trie_root_as_hash::(root_vec); { - let mut trie = TrieDBMut::::from_existing(&mut *db, &mut root)?; + let mut trie = TrieDBMut::::from_existing(&mut db, &mut root)?; for (key, change) in delta { match change { @@ -211,14 +218,15 @@ pub fn child_delta_trie_root( }; } } - Ok(root.as_ref().to_vec()) + } /// Call `f` for all keys in a child trie. pub fn for_keys_in_child_trie( _storage_key: &[u8], db: &DB, + keyspace: &KeySpace, root_slice: &[u8], mut f: F ) -> Result<(), Box>> @@ -226,11 +234,9 @@ pub fn for_keys_in_child_trie( DB: hash_db::HashDBRef + hash_db::PlainDBRef, trie_db::DBValue>, { - let mut root = TrieHash::::default(); - // root is fetched from DB, not writable by runtime, so it's always valid. - root.as_mut().copy_from_slice(root_slice); - - let trie = TrieDB::::new(&*db, &root)?; + let root = trie_root_as_hash::(root_slice); + let db = KeySpacedDB::new(&*db, Some(keyspace)); + let trie = TrieDB::::new(&db, &root)?; let iter = trie.iter()?; for x in iter { @@ -249,7 +255,8 @@ pub fn record_all_keys( ) -> Result<(), Box>> where DB: hash_db::HashDBRef { - let trie = TrieDB::::new(&*db, root)?; + let db = KeySpacedDB::new(db, None); + let trie = TrieDB::::new(&db, root)?; let iter = trie.iter()?; for x in iter { @@ -268,6 +275,7 @@ pub fn record_all_keys( pub fn read_child_trie_value( _storage_key: &[u8], db: &DB, + keyspace: &KeySpace, root_slice: &[u8], key: &[u8] ) -> Result>, Box>> @@ -275,17 +283,17 @@ pub fn read_child_trie_value( DB: hash_db::HashDBRef + hash_db::PlainDBRef, trie_db::DBValue>, { - let mut root = TrieHash::::default(); - // root is fetched from DB, not writable by runtime, so it's always valid. - root.as_mut().copy_from_slice(root_slice); + let root = trie_root_as_hash::(root_slice); + let db = KeySpacedDB::new(&*db, Some(keyspace)); - Ok(TrieDB::::new(&*db, &root)?.get(key).map(|x| x.map(|val| val.to_vec()))?) + Ok(TrieDB::::new(&db, &root)?.get(key).map(|x| x.map(|val| val.to_vec()))?) } /// Read a value from the child trie with given query. pub fn read_child_trie_value_with, DB>( _storage_key: &[u8], db: &DB, + keyspace: &KeySpace, root_slice: &[u8], key: &[u8], query: Q @@ -294,13 +302,100 @@ pub fn read_child_trie_value_with + hash_db::PlainDBRef, trie_db::DBValue>, { - let mut root = TrieHash::::default(); - // root is fetched from DB, not writable by runtime, so it's always valid. - root.as_mut().copy_from_slice(root_slice); + let root = trie_root_as_hash::(root_slice); + let db = KeySpacedDB::new(&*db, Some(keyspace)); + Ok(TrieDB::::new(&db, &root)?.get_with(key, query).map(|x| x.map(|val| val.to_vec()))?) +} + +/// `HashDB` implementation that append a encoded `KeySpace` (unique id in as bytes) with the +/// prefix of every key value. +pub struct KeySpacedDB<'a, DB, H>(&'a DB, Option<&'a KeySpace>, PhantomData); +/// `HashDBMut` implementation that append a encoded `KeySpace` (unique id in as bytes) with the +/// prefix of every key value. +/// +/// Mutable variant of `KeySpacedDB`, see [`KeySpacedDB`]. +pub struct KeySpacedDBMut<'a, DB, H>(&'a mut DB, Option<&'a KeySpace>, PhantomData); + + +impl<'a, DB, H> KeySpacedDB<'a, DB, H> where + H: Hasher, +{ + /// instantiate new keyspaced db + pub fn new(db: &'a DB, ks: Option<&'a KeySpace>) -> Self { + KeySpacedDB(db, ks, PhantomData) + } +} +impl<'a, DB, H> KeySpacedDBMut<'a, DB, H> where + H: Hasher, +{ + /// instantiate new keyspaced db + pub fn new(db: &'a mut DB, ks: Option<&'a KeySpace>) -> Self { + KeySpacedDBMut(db, ks, PhantomData) + } +} + +impl<'a, DB, H, T> hash_db::HashDBRef for KeySpacedDB<'a, DB, H> where + DB: hash_db::HashDBRef, + H: Hasher, + T: From<&'static [u8]>, +{ + fn get(&self, key: &H::Out, prefix: Prefix) -> Option { + let derived_prefix = keyspace_as_prefix_alloc(self.1, prefix); + self.0.get(key, (&derived_prefix.0, derived_prefix.1)) + } + + fn contains(&self, key: &H::Out, prefix: Prefix) -> bool { + let derived_prefix = keyspace_as_prefix_alloc(self.1, prefix); + self.0.contains(key, (&derived_prefix.0, derived_prefix.1)) + } +} + +impl<'a, DB, H, T> hash_db::HashDB for KeySpacedDBMut<'a, DB, H> where + DB: hash_db::HashDB, + H: Hasher, + T: Default + PartialEq + for<'b> From<&'b [u8]> + Clone + Send + Sync, +{ + fn get(&self, key: &H::Out, prefix: Prefix) -> Option { + let derived_prefix = keyspace_as_prefix_alloc(self.1, prefix); + self.0.get(key, (&derived_prefix.0, derived_prefix.1)) + } + + fn contains(&self, key: &H::Out, prefix: Prefix) -> bool { + let derived_prefix = keyspace_as_prefix_alloc(self.1, prefix); + self.0.contains(key, (&derived_prefix.0, derived_prefix.1)) + } + + fn insert(&mut self, prefix: Prefix, value: &[u8]) -> H::Out { + let derived_prefix = keyspace_as_prefix_alloc(self.1, prefix); + self.0.insert((&derived_prefix.0, derived_prefix.1), value) + } + + fn emplace(&mut self, key: H::Out, prefix: Prefix, value: T) { + let derived_prefix = keyspace_as_prefix_alloc(self.1, prefix); + self.0.emplace(key, (&derived_prefix.0, derived_prefix.1), value) + } + + fn remove(&mut self, key: &H::Out, prefix: Prefix) { + let derived_prefix = keyspace_as_prefix_alloc(self.1, prefix); + self.0.remove(key, (&derived_prefix.0, derived_prefix.1)) + } +} + +impl<'a, DB, H, T> hash_db::AsHashDB for KeySpacedDBMut<'a, DB, H> where + DB: hash_db::HashDB, + H: Hasher, + T: Default + PartialEq + for<'b> From<&'b [u8]> + Clone + Send + Sync, +{ + fn as_hash_db(&self) -> &dyn hash_db::HashDB { &*self } + fn as_hash_db_mut<'b>(&'b mut self) -> &'b mut (dyn hash_db::HashDB + 'b) { + &mut *self + } - Ok(TrieDB::::new(&*db, &root)?.get_with(key, query).map(|x| x.map(|val| val.to_vec()))?) } + +// Utilities (not exported): + /// Constants used into trie simplification codec. mod trie_constants { pub const EMPTY_TRIE: u8 = 0; @@ -310,6 +405,13 @@ mod trie_constants { pub const BRANCH_WITH_MASK: u8 = 0b_11 << 6; } +fn trie_root_as_hash> (trie_root: R) -> TrieHash { + let mut root = >::default(); + // root is fetched from DB, not writable by runtime, so it's always valid. + root.as_mut().copy_from_slice(trie_root.as_ref()); + root +} + #[cfg(test)] mod tests { use super::*; diff --git a/core/utils/historied-data/Cargo.toml b/core/utils/historied-data/Cargo.toml new file mode 100644 index 0000000000000..0a1012a7a2e9b --- /dev/null +++ b/core/utils/historied-data/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "historied-data" +version = "2.0.0" +authors = ["Parity Technologies "] +description = "Data associated with its history" +edition = "2018" + +[dependencies] +rstd = { package = "sr-std", path = "../../sr-std", default-features = false } +smallvec = { version = "0.6", optional = true } +num-traits = { version = "0.2.8", default-features = false } + +[dev-dependencies] + +[features] +default = ["std"] +std = [ + "rstd/std", + "num-traits/std", + "smallvec", +] +test = [] diff --git a/core/utils/historied-data/README.md b/core/utils/historied-data/README.md new file mode 100644 index 0000000000000..8d469fbb00b56 --- /dev/null +++ b/core/utils/historied-data/README.md @@ -0,0 +1,18 @@ +## Historied data + +Crate with methods to manage data that stores its own history. + +This covers: +- linear history driven data, eg. transactional layers for overlay. +- long term storage with multiple branch, eg. offchain storage. + +General design is container where query and update requires global +history context. + +History is serialize as a per item basis. + +This crates is be `no_std` compatible, unless feature `std` is used. + +For more information see + +License: GPL-3.0 diff --git a/core/utils/historied-data/src/lib.rs b/core/utils/historied-data/src/lib.rs new file mode 100644 index 0000000000000..d63216f011b3f --- /dev/null +++ b/core/utils/historied-data/src/lib.rs @@ -0,0 +1,83 @@ +// Copyright 2017-2019 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate 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. + +// Substrate 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 Substrate. If not, see . + +//! History driven data storage. +//! Useful to store information with history +//! on a per item basis. + +#![cfg_attr(not(feature = "std"), no_std)] + +use rstd::convert::TryInto; + +pub mod tree; +pub mod linear; + +/// An entry at a given history index. +#[derive(Debug, Clone)] +#[cfg_attr(any(test, feature = "test"), derive(PartialEq))] +pub struct HistoriedValue { + /// The stored value. + pub value: V, + /// The moment in history when the value got set. + pub index: I, +} + +impl From<(V, I)> for HistoriedValue { + fn from(input: (V, I)) -> HistoriedValue { + HistoriedValue { value: input.0, index: input.1 } + } +} + +impl HistoriedValue { + pub fn as_ref(&self) -> HistoriedValue<&V, I> { + HistoriedValue { + value: &self.value, + index: self.index, + } + } + + pub fn as_mut(&mut self) -> HistoriedValue<&mut V, I> { + HistoriedValue { + value: &mut self.value, + index: self.index, + } + } + + pub fn map R>(self, f: F) -> HistoriedValue { + HistoriedValue { + value: f(self.value), + index: self.index, + } + } + +} + +// utility function for panicking cast (similar to a `as` cast for number). +fn as_u>(i: I) -> U { + match i.try_into() { + Ok(index) => index, + Err(_) => ::max_value(), + } +} + +#[cfg_attr(any(test, feature = "test"), derive(PartialEq, Debug))] +/// Prunning result to be able to proceed +/// with further update if the value needs it. +pub enum PruneResult { + Unchanged, + Changed, + Cleared, +} diff --git a/core/utils/historied-data/src/linear.rs b/core/utils/historied-data/src/linear.rs new file mode 100644 index 0000000000000..5ef55bba9943f --- /dev/null +++ b/core/utils/historied-data/src/linear.rs @@ -0,0 +1,448 @@ +// Copyright 2017-2019 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate 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. + +// Substrate 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 Substrate. If not, see . + +//! Linear historied data. + +#[cfg(not(feature = "std"))] +use rstd::{vec::Vec, vec}; +use rstd::marker::PhantomData; +use rstd::borrow::Cow; +use crate::HistoriedValue; + + +/// Array like buffer for in memory storage. +/// By in memory we expect that this will +/// not required persistence and is not serialized. +#[cfg(not(feature = "std"))] +pub(crate) type MemoryOnly = Vec>; + +/// Array like buffer for in memory storage. +/// By in memory we expect that this will +/// not required persistence and is not serialized. +#[cfg(feature = "std")] +pub(crate) type MemoryOnly = smallvec::SmallVec<[HistoriedValue; ALLOCATED_HISTORY]>; + +/// Size of preallocated history per element. +/// Currently at two for committed and prospective only. +/// It means that using transaction in a module got a direct allocation cost. +#[cfg(feature = "std")] +const ALLOCATED_HISTORY: usize = 2; + +#[derive(Debug, Clone)] +#[cfg_attr(any(test, feature = "test"), derive(PartialEq))] +/// Arraylike buffer with in place byte data. +/// Can be written as is in underlying +/// storage. +/// Could be use for direct access memory to. +pub struct Serialized<'a, F>(SerializedBuff<'a>, PhantomData); + +#[derive(Debug)] +#[cfg_attr(any(test, feature = "test"), derive(PartialEq))] +enum SerializedBuff<'a> { + Cow(Cow<'a, [u8]>), + Mut(&'a mut Vec), +} + +impl<'a> SerializedBuff<'a> { + pub fn to_mut(&mut self) -> &mut Vec { + match self { + SerializedBuff::Cow(c) => c.to_mut(), + SerializedBuff::Mut(m) => m, + } + } + pub fn into_owned(self) -> Vec { + match self { + SerializedBuff::Cow(c) => c.into_owned(), + SerializedBuff::Mut(m) => m.clone(), + } + } +} + +impl<'a> rstd::ops::Deref for SerializedBuff<'a> { + type Target = [u8]; + fn deref(&self) -> &Self::Target { + match self { + SerializedBuff::Cow(c) => c.deref(), + SerializedBuff::Mut(m) => m.deref(), + } + } +} + +impl<'a> rstd::ops::DerefMut for SerializedBuff<'a> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.to_mut()[..] + } +} + +impl<'a> Clone for SerializedBuff<'a> { + fn clone(&self) -> Self { + match self { + SerializedBuff::Cow(c) => SerializedBuff::Cow(c.clone()), + SerializedBuff::Mut(m) => { + let m: Vec = (*m).clone(); + SerializedBuff::Cow(Cow::Owned(m)) + } + } + } +} + +/// Serialized specific behavior. +pub trait SerializedConfig { + /// encoded empty slice + fn empty() -> &'static [u8]; + /// size at start for encoding version. + fn version_len() -> usize; +} + +#[derive(Debug, Clone)] +#[cfg_attr(any(test, feature = "test"), derive(PartialEq))] +/// Serialize without versioning. +pub struct NoVersion; + +#[derive(Debug, Clone)] +#[cfg_attr(any(test, feature = "test"), derive(PartialEq))] +/// Serialize with default verison +pub struct DefaultVersion; + +impl SerializedConfig for NoVersion { + fn empty() -> &'static [u8] { + &EMPTY_SERIALIZED + } + fn version_len() -> usize { + 0 + } +} + +impl SerializedConfig for DefaultVersion { + fn empty() -> &'static [u8] { + &DEFAULT_VERSION_EMPTY_SERIALIZED + } + fn version_len() -> usize { + 1 + } +} + +// encoding size as u64 +const SIZE_BYTE_LEN: usize = 8; + +// Basis implementation to be on par with implementation using +// vec like container. Those method could be move to a trait +// implementation. +// Those function requires checked index. +impl<'a, F: SerializedConfig> Serialized<'a, F> { + + pub fn into_owned(self) -> Serialized<'static, F> { + Serialized(SerializedBuff::Cow(Cow::from(self.0.into_owned())), PhantomData) + } + + pub fn into_vec(self) -> Vec { + self.0.into_owned() + } + + pub(crate) fn len(&self) -> usize { + let len = self.0.len(); + self.read_le_usize(len - SIZE_BYTE_LEN) as usize + } + + pub(crate) fn clear(&mut self) { + self.write_le_usize(F::version_len(), 0); + self.0.to_mut().truncate(F::version_len() + SIZE_BYTE_LEN); + } + + #[cfg(test)] + fn truncate(&mut self, index: usize) { + if index == 0 { + self.clear(); + return; + } + let len = self.len(); + if index >= len { + return; + } + let start_ix = self.index_start(); + let new_start = self.index_element(index) as usize; + let len_ix = index * SIZE_BYTE_LEN; + self.slice_copy(start_ix, new_start, len_ix); + self.write_le_usize(new_start + len_ix - SIZE_BYTE_LEN, index); + self.0.to_mut().truncate(new_start + len_ix); + } + + // index stay in truncated content + pub(crate) fn truncate_until(&mut self, index: usize) { + self.remove_range(0, index); + } + + pub(crate) fn pop(&mut self) -> Option, u64>> { + let len = self.len(); + if len == 0 { + return None; + } + let start_ix = self.index_element(len - 1); + let end_ix = self.index_start(); + let state = self.read_le_u64(start_ix); + let value = self.0[start_ix + SIZE_BYTE_LEN..end_ix].to_vec(); + if len - 1 == 0 { + self.clear(); + return Some(HistoriedValue { value, index: state }) + } else { + self.write_le_usize(self.0.len() - (SIZE_BYTE_LEN * 2), len - 1); + }; + let ix_size = (len * SIZE_BYTE_LEN) - SIZE_BYTE_LEN; + self.slice_copy(end_ix, start_ix, ix_size); + self.0.to_mut().truncate(start_ix + ix_size); + Some(HistoriedValue { value, index: state }) + } + + pub(crate) fn push(&mut self, val: HistoriedValue<&[u8], u64>) { + self.push_extra(val, &[]) + } + + /// variant of push where part of the value is in a second slice. + pub(crate) fn push_extra(&mut self, val: HistoriedValue<&[u8], u64>, extra: &[u8]) { + let len = self.len(); + let start_ix = self.index_start(); + let end_ix = self.0.len(); + // A sized buffer and multiple index to avoid to big copy + // should be use here. + let mut new_ix = self.0[start_ix..end_ix].to_vec(); + // truncate here can be bad + self.0.to_mut().truncate(start_ix + SIZE_BYTE_LEN); + self.write_le_u64(start_ix, val.index); + self.0.to_mut().extend_from_slice(val.value); + self.0.to_mut().extend_from_slice(extra); + self.0.to_mut().append(&mut new_ix); + if len > 0 { + self.write_le_usize(self.0.len() - SIZE_BYTE_LEN, start_ix); + self.append_le_usize(len + 1); + } else { + self.write_le_usize(self.0.len() - SIZE_BYTE_LEN, 1); + } + } + + #[cfg(test)] + fn remove(&mut self, index: usize) { + self.remove_range(index, index + 1); + } + + fn remove_range(&mut self, index: usize, end: usize) { + if end == 0 { + return; + } + let len = self.len(); + if len <= end - index && index == 0 { + self.clear(); + return; + } + // eager removal is costy, running some gc impl + // can be interesting. + let elt_start = self.index_element(index); + let start_ix = self.index_start(); + let elt_end = if end == len { + start_ix + } else { + self.index_element(end) + }; + let delete_size = elt_end - elt_start; + for _ in elt_start..elt_end { + let _ = self.0.to_mut().remove(elt_start); + } + let start_ix = start_ix - delete_size; + + let len = len - (end - index); + for i in index..end { + let pos = i + (end - index); + if pos < len { + let old_value = self.read_le_usize(start_ix + pos * SIZE_BYTE_LEN); + self.write_le_usize(start_ix + i * SIZE_BYTE_LEN, old_value - delete_size); + } + } + let end_index = start_ix + len * SIZE_BYTE_LEN; + self.write_le_usize(end_index - SIZE_BYTE_LEN, len); + self.0.to_mut().truncate(end_index); + + } + + pub(crate) fn get_state(&self, index: usize) -> HistoriedValue<&[u8], u64> { + let start_ix = self.index_element(index); + let len = self.len(); + let end_ix = if index == len - 1 { + self.index_start() + } else { + self.index_element(index + 1) + }; + let state = self.read_le_u64(start_ix); + HistoriedValue { + value: &self.0[start_ix + SIZE_BYTE_LEN..end_ix], + index: state, + } + } + +} + +const EMPTY_SERIALIZED: [u8; SIZE_BYTE_LEN] = [0u8; SIZE_BYTE_LEN]; +const DEFAULT_VERSION: u8 = 1; +const DEFAULT_VERSION_EMPTY_SERIALIZED: [u8; SIZE_BYTE_LEN + 1] = { + let mut buf = [0u8; SIZE_BYTE_LEN + 1]; + buf[0] = DEFAULT_VERSION; + buf +}; + +impl<'a, F: SerializedConfig> Default for Serialized<'a, F> { + fn default() -> Self { + Serialized(SerializedBuff::Cow(Cow::Borrowed(F::empty())), PhantomData) + } +} + +impl<'a, F> Into> for &'a[u8] { + fn into(self) -> Serialized<'a, F> { + Serialized(SerializedBuff::Cow(Cow::Borrowed(self)), PhantomData) + } +} + +impl Into> for Vec { + fn into(self) -> Serialized<'static, F> { + Serialized(SerializedBuff::Cow(Cow::Owned(self)), PhantomData) + } +} + +impl<'a, F> Into> for &'a mut Vec { + fn into(self) -> Serialized<'a, F> { + Serialized(SerializedBuff::Mut(self), PhantomData) + } +} + + +// Utility function for basis implementation. +impl<'a, F: SerializedConfig> Serialized<'a, F> { + + // Index at end, also contains the encoded size + fn index_start(&self) -> usize { + let nb_ix = self.len(); + if nb_ix == 0 { return F::version_len(); } + let end = self.0.len(); + end - (nb_ix * SIZE_BYTE_LEN) + } + + fn index_element(&self, position: usize) -> usize { + if position == 0 { + return F::version_len(); + } + let i = self.index_start() + (position - 1) * SIZE_BYTE_LEN; + self.read_le_usize(i) + } + + // move part of array that can overlap + // This is a memory inefficient implementation. + fn slice_copy(&mut self, start_from: usize, start_to: usize, size: usize) { + let buffer = self.0[start_from..start_from + size].to_vec(); + self.0.to_mut()[start_to..start_to + size].copy_from_slice(&buffer[..]); + } + + // Usize encoded as le u64 (for historied value). + fn read_le_u64(&self, pos: usize) -> u64 { + let mut buffer = [0u8; SIZE_BYTE_LEN]; + buffer.copy_from_slice(&self.0[pos..pos + SIZE_BYTE_LEN]); + u64::from_le_bytes(buffer) + } + + // Usize encoded as le u64 (only for internal indexing). + fn read_le_usize(&self, pos: usize) -> usize { + let mut buffer = [0u8; SIZE_BYTE_LEN]; + buffer.copy_from_slice(&self.0[pos..pos + SIZE_BYTE_LEN]); + u64::from_le_bytes(buffer) as usize + } + + // Usize encoded as le u64. + fn write_le_usize(&mut self, pos: usize, value: usize) { + let buffer = (value as u64).to_le_bytes(); + self.0.to_mut()[pos..pos + SIZE_BYTE_LEN].copy_from_slice(&buffer[..]); + } + + // Usize encoded as le u64. + fn append_le_usize(&mut self, value: usize) { + let buffer = (value as u64).to_le_bytes(); + self.0.to_mut().extend_from_slice(&buffer[..]); + } + + // Usize encoded as le u64. + fn write_le_u64(&mut self, pos: usize, value: u64) { + let buffer = (value as u64).to_le_bytes(); + self.0.to_mut()[pos..pos + SIZE_BYTE_LEN].copy_from_slice(&buffer[..]); + } + +} + +#[cfg(test)] +mod test { + use super::*; + + fn test_serialized_basis(mut ser: Serialized) { + // test basis unsafe function similar to a simple vec + // without index checking. + let v1 = &b"val1"[..]; + let v2 = &b"value_2"[..]; + let v3 = &b"a third value 3"[..]; + + assert_eq!(ser.len(), 0); + assert_eq!(ser.pop(), None); + ser.push((v1, 1).into()); + assert_eq!(ser.get_state(0), (v1, 1).into()); + assert_eq!(ser.pop(), Some((v1.to_vec(), 1).into())); + assert_eq!(ser.len(), 0); + ser.push((v1, 1).into()); + ser.push((v2, 2).into()); + ser.push((v3, 3).into()); + assert_eq!(ser.get_state(0), (v1, 1).into()); + assert_eq!(ser.get_state(1), (v2, 2).into()); + assert_eq!(ser.get_state(2), (v3, 3).into()); + assert_eq!(ser.pop(), Some((v3.to_vec(), 3).into())); + assert_eq!(ser.len(), 2); + ser.push((v3, 3).into()); + assert_eq!(ser.get_state(2), (v3, 3).into()); + ser.remove(0); + assert_eq!(ser.len(), 2); + assert_eq!(ser.get_state(0), (v2, 2).into()); + assert_eq!(ser.get_state(1), (v3, 3).into()); + ser.push((v1, 1).into()); + ser.remove(1); + assert_eq!(ser.len(), 2); + assert_eq!(ser.get_state(0), (v2, 2).into()); + assert_eq!(ser.get_state(1), (v1, 1).into()); + ser.push((v1, 1).into()); + ser.truncate(1); + assert_eq!(ser.len(), 1); + assert_eq!(ser.get_state(0), (v2, 2).into()); + ser.push((v1, 1).into()); + ser.push((v3, 3).into()); + ser.truncate_until(1); + assert_eq!(ser.len(), 2); + assert_eq!(ser.get_state(0), (v1, 1).into()); + assert_eq!(ser.get_state(1), (v3, 3).into()); + ser.push((v2, 2).into()); + ser.truncate_until(2); + assert_eq!(ser.len(), 1); + assert_eq!(ser.get_state(0), (v2, 2).into()); + + } + + #[test] + fn serialized_basis() { + let ser1: Serialized = Default::default(); + let ser2: Serialized = Default::default(); + test_serialized_basis(ser1); + test_serialized_basis(ser2); + } +} diff --git a/core/utils/historied-data/src/tree.rs b/core/utils/historied-data/src/tree.rs new file mode 100644 index 0000000000000..892d71459b55b --- /dev/null +++ b/core/utils/historied-data/src/tree.rs @@ -0,0 +1,1093 @@ +// Copyright 2017-2019 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate 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. + +// Substrate 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 Substrate. If not, see . + +//! Data store acyclic directed graph as tree. +//! +//! General structure is an array of branch, each branch originates +//! from another branch at designated index. +//! +//! No particular state (just present or missing). + +use crate::linear::{ + MemoryOnly as BranchBackend, + Serialized as SerializedInner, + SerializedConfig, +}; +use crate::HistoriedValue; +use crate::PruneResult; +use crate::as_u; +use rstd::rc::Rc; +use rstd::vec::Vec; +use rstd::collections::btree_map::BTreeMap; +use rstd::convert::{TryFrom, TryInto}; +use num_traits::Bounded; + +/// Trait defining a state for querying or modifying a tree. +/// This is a collection of branches index, corresponding +/// to a tree path. +pub trait BranchesStateTrait { + type Branch: BranchStateTrait; + type Iter: Iterator; + + /// Get branch state for node at a given index. + fn get_branch(self, index: I) -> Option; + + /// Get the last index for the state, inclusive. + fn last_index(self) -> I; + + /// Iterator over the branch states. + fn iter(self) -> Self::Iter; +} + +/// Trait defining a state for querying or modifying a branch. +/// This is therefore the representation of a branch state. +pub trait BranchStateTrait { + + /// Get state for node at a given index. + fn get_node(&self, i: I) -> S; + + /// Get the last index for the state, inclusive. + fn last_index(&self) -> I; +} + +impl<'a> BranchesStateTrait for &'a StatesRef { + type Branch = (&'a BranchStateRef, Option); + type Iter = StatesRefIter<'a>; + + fn get_branch(self, i: u64) -> Option { + for (b, bi) in self.iter() { + if bi == i { + return Some(b); + } else if bi < i { + break; + } + } + None + } + + fn last_index(self) -> u64 { + let l = self.history.len(); + let l = if l > 0 { + self.history[l - 1].branch_index + } else { + 0 + }; + self.upper_branch_index.map(|u| rstd::cmp::min(u, l)).unwrap_or(l) + } + + fn iter(self) -> Self::Iter { + let mut end = self.history.len(); + let last_index = self.last_index(); + let upper_node_index = if Some(last_index) == self.upper_branch_index { + self.upper_node_index + } else { None }; + while end > 0 { + if self.history[end - 1].branch_index <= last_index { + break; + } + end -= 1; + } + + StatesRefIter(self, end, upper_node_index) + } +} + +#[derive(Debug, Clone)] +#[cfg_attr(any(test, feature = "test"), derive(PartialEq, Eq))] +pub struct BranchState { + /// Index of first element (only use for indexed access). + /// Element before offset are not in state. + offset: u64, + /// number of elements: all elements equal or bellow + /// this index are valid, over this index they are not. + len: u64, + /// Maximum index before first deletion, it indicates + /// if the state is modifiable (when an element is dropped + /// we cannot append and need to create a new branch). + max_len_ix: u64, +} + +/// This is a simple range, end non inclusive. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct BranchStateRef { + pub start: u64, + pub end: u64, +} + +impl<'a> BranchStateTrait for (&'a BranchStateRef, Option) { + + fn get_node(&self, i: u64) -> bool { + let l = self.0.end; + let upper = self.1.map(|u| rstd::cmp::min(u + 1, l)).unwrap_or(l); + i >= self.0.start && i < upper + } + + fn last_index(&self) -> u64 { + // underflow should not happen as long as branchstateref are not allowed to be empty. + let state_end = self.0.end - 1; + self.1.map(|bound| rstd::cmp::min(state_end, bound)).unwrap_or(state_end) + } + +} + +impl<'a> BranchStateTrait for &'a BranchStateRef { + + fn get_node(&self, i: u64) -> bool { + i >= self.start && i < self.end + } + + fn last_index(&self) -> u64 { + // underflow should not happen as long as branchstateref are not allowed to be empty. + self.end - 1 + } + +} + +/// u64 is use a a state target so it is implemented as +/// a upper bound. +impl<'a> BranchStateTrait for u64 { + + fn get_node(&self, i: u64) -> bool { + &i <= self + } + + fn last_index(&self) -> u64 { + *self + } + +} + +impl Default for BranchState { + // initialize with one element + fn default() -> Self { + Self::new_from(0) + } +} + +impl BranchState { + pub fn new_from(offset: u64) -> Self { + BranchState { + offset, + len: 1, + max_len_ix: offset, + } + } + + pub fn state_ref(&self) -> BranchStateRef { + BranchStateRef { + start: self.offset, + end: self.offset + self.len, + } + } + + pub fn has_deleted_index(&self) -> bool { + self.max_len_ix >= self.offset + self.len + } + + pub fn add_state(&mut self) -> bool { + if !self.has_deleted_index() { + self.len += 1; + self.max_len_ix += 1; + true + } else { + false + } + } + + /// return possible dropped state + pub fn drop_state(&mut self) -> Option { + if self.len > 0 { + self.len -= 1; + Some(self.offset + self.len) + } else { + None + } + } + + /// Return true if state exists. + pub fn get_state(&self, index: u64) -> bool { + if index < self.offset { + return false; + } + self.len > index + self.offset + } + + pub fn latest_ix(&self) -> Option { + if self.len > 0 { + Some(self.offset + self.len - 1) + } else { + None + } + } + +} + +impl BranchStateRef { + /// Return true if the state exists, false otherwhise. + pub fn get_state(&self, index: u64) -> bool { + index < self.end && index >= self.start + } +} + +/// At this point this is only use for testing and as an example +/// implementation. +/// It keeps trace of dropped value, and have some costy recursive +/// deletion. +#[derive(Debug, Clone)] +#[cfg_attr(any(test, feature = "test"), derive(PartialEq))] +pub struct TestStates { + branches: BTreeMap, + last_branch_index: u64, + /// a lower treshold under which every branch are seen + /// as containing only valid values. + /// This can only be updated after a full garbage collection. + valid_treshold: u64, +} + +impl StatesBranch { + pub fn branch_ref(&self) -> BranchStatesRef { + BranchStatesRef { + branch_index: self.branch_index, + state: self.state.state_ref(), + } + } +} + +impl Default for TestStates { + fn default() -> Self { + TestStates { + branches: Default::default(), + last_branch_index: 0, + valid_treshold: 0, + } + } +} + +#[derive(Debug, Clone)] +#[cfg_attr(any(test, feature = "test"), derive(PartialEq, Eq))] +struct StatesBranch { + // this is the key (need to growth unless full gc (can still have + // content pointing to it even if it seems safe to reuse a previously + // use ix). + branch_index: u64, + + origin_branch_index: u64, + origin_node_index: u64, + + state: BranchState, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct BranchStatesRef { + pub branch_index: u64, + pub state: BranchStateRef, +} + +#[derive(Clone)] +/// Reference to state to use for query updates. +/// It is a single brannch path with branches ordered by branch_index. +/// +/// Note that an alternative representation could be a pointer to full +/// tree state with a defined branch index implementing an iterator. +pub struct StatesRef { + /// Oredered by branch index linear branch states. + history: Rc>, + /// Index is included, acts as length of history. + upper_branch_index: Option, + /// Index is included, acts as a branch ref end value. + upper_node_index: Option, +} + +/// Iterator, contains index of last inner struct. +pub struct StatesRefIter<'a>(&'a StatesRef, usize, Option); + +impl<'a> Iterator for StatesRefIter<'a> { + type Item = ((&'a BranchStateRef, Option), u64); + + fn next(&mut self) -> Option { + if self.1 > 0 { + let upper_node_index = self.2.take(); + Some(( + (&self.0.history[self.1 - 1].state, upper_node_index), + self.0.history[self.1 - 1].branch_index, + )) + } else { + None + } + } +} + +impl StatesRef { + /// limit to a given branch (included). + /// Optionally limiting branch to a linear index (included). + pub fn limit_branch(&mut self, branch_index: u64, node_index: Option) { + debug_assert!(branch_index > 0); + self.upper_branch_index = Some(branch_index); + self.upper_node_index = node_index; + } + + /// remove any limit. + pub fn clear_limit(&mut self) { + self.upper_branch_index = None; + self.upper_node_index = None; + } + +} + +impl TestStates { + + /// clear state but keep existing branch values (can be call after a full gc: + /// enforcing no commited containing dropped values). + pub fn unsafe_clear(&mut self) { + self.branches.clear(); + self.last_branch_index = 0; + } + + /// warning it should be the index of the leaf, otherwhise the ref will be incomplete. + /// (which is fine as long as we use this state to query something that refer to this state. + pub fn state_ref(&self, mut branch_index: u64) -> StatesRef { + let mut result = Vec::new(); + let mut previous_origin_node_index = u64::max_value() - 1; + while branch_index > self.valid_treshold { + if let Some(branch) = self.branches.get(&branch_index) { + let mut branch_ref = branch.branch_ref(); + if branch_ref.state.end > previous_origin_node_index + 1 { + branch_ref.state.end = previous_origin_node_index + 1; + } + previous_origin_node_index = branch.origin_node_index; + // vecdeque would be better suited + result.insert(0, branch_ref); + branch_index = branch.origin_branch_index; + } else { + break; + } + } + StatesRef { history: Rc::new(result), upper_branch_index: None, upper_node_index: None } + } + + // create a branches. End current branch. + // Return first created index (next branch are sequential indexed) + // or None if origin branch does not allow branch creation (commited branch or non existing). + pub fn create_branch( + &mut self, + nb_branch: usize, + branch_index: u64, + node_index: Option, + ) -> Option { + if nb_branch == 0 { + return None; + } + + // for 0 is the first branch creation case + let node_index = if branch_index == 0 { + debug_assert!(node_index.is_none()); + 0 + } else { + if let Some(node_index) = self.get_node(branch_index, node_index) { + node_index + } else { + return None; + } + }; + + let result_ix = self.last_branch_index + 1; + for i in result_ix .. result_ix + (nb_branch as u64) { + self.branches.insert(i, StatesBranch { + branch_index: i, + origin_branch_index: branch_index, + origin_node_index: node_index, + state: Default::default(), + }); + } + self.last_branch_index += nb_branch as u64; + + Some(result_ix) + } + + /// check if node is valid for given index. + /// return node_index. + pub fn get_node( + &self, + branch_index: u64, + node_index: Option, + ) -> Option { + if let Some(branch) = self.branches.get(&branch_index) { + if let Some(node_index) = node_index { + if branch.state.get_state(node_index) { + Some(node_index) + } else { + None + } + } else { + branch.state.latest_ix() + } + } else { + None + } + } + + /// Do node exist (return state (being true or false only)). + pub fn get(&self, branch_index: u64, node_index: u64) -> bool { + self.get_node(branch_index, Some(node_index)).is_some() + } + + pub fn branch_state(&self, branch_index: u64) -> Option<&BranchState> { + self.branches.get(&branch_index) + .map(|b| &b.state) + } + + pub fn branch_state_mut(&mut self, branch_index: u64) -> Option<&mut BranchState> { + self.branches.get_mut(&branch_index) + .map(|b| &mut b.state) + } + + /// this function can go into deep recursion with full scan, it indicates + /// that the tree model use here should only be use for small data or + /// tests. + pub fn apply_drop_state(&mut self, branch_index: u64, node_index: u64) { + let mut to_delete = Vec::new(); + for (i, s) in self.branches.iter() { + if s.origin_branch_index == branch_index && s.origin_node_index == node_index { + to_delete.push(*i); + } + } + for i in to_delete.into_iter() { + loop { + match self.branch_state_mut(i).map(|ls| ls.drop_state()) { + Some(Some(li)) => self.apply_drop_state(i, li), + Some(None) => break, // we keep empty branch + None => break, + } + } + } + } +} + +/// First field is the actual history against which we run +/// the state. +/// Second field is an optional value for the no match case. +#[derive(Debug, Clone)] +#[cfg_attr(any(test, feature = "test"), derive(PartialEq))] +pub struct History(Vec>); + +impl Default for History { + fn default() -> Self { + History(Vec::new()) + } +} + +#[derive(Debug, Clone)] +#[cfg_attr(any(test, feature = "test"), derive(PartialEq))] +pub struct HistoryBranch { + branch_index: u64, + history: BranchBackend, +} + +impl History { + + /// Set or update value for a given state. + pub fn set(&mut self, state: S, value: V) + where + S: BranchesStateTrait, + I: Copy + Eq + TryFrom + TryInto, + BI: Copy + Eq + TryFrom + TryInto, + { + if let Some((state_branch, state_index)) = state.iter().next() { + let state_index_u64 = as_u(state_index); + let mut i = self.0.len(); + let (branch_position, new_branch) = loop { + if i == 0 { + break (0, true); + } + let branch_index = self.0[i - 1].branch_index; + if branch_index == state_index_u64 { + break (i - 1, false); + } else if branch_index < state_index_u64 { + break (i, true); + } + i -= 1; + }; + if new_branch { + let index = as_u(state_branch.last_index()); + let mut history = BranchBackend::::default(); + history.push(HistoriedValue { + value, + index, + }); + let h_value = HistoryBranch { + branch_index: state_index_u64, + history, + }; + if branch_position == self.0.len() { + self.0.push(h_value); + } else { + self.0.insert(branch_position, h_value); + } + } else { + self.node_set(branch_position, &state_branch, value) + } + } + } + + fn node_set(&mut self, branch_index: usize, state: &S, value: V) + where + S: BranchStateTrait, + I: Copy + Eq + TryFrom + TryInto, + { + let node_index_u64 = as_u(state.last_index()); + let history = &mut self.0[branch_index]; + let mut index = history.history.len(); + debug_assert!(index > 0); + loop { + if index == 0 || history.history[index - 1].index < node_index_u64 { + let h_value = HistoriedValue { + value, + index: node_index_u64 + }; + if index == history.history.len() { + history.history.push(h_value); + } else { + history.history.insert(index, h_value); + } + break; + } else if history.history[index - 1].index == node_index_u64 { + history.history[index - 1].value = value; + break; + } + index -= 1; + } + } + + /// Access to last valid value (non dropped state in history). + /// When possible please use `get_mut` as it can free some memory. + pub fn get (&self, state: S) -> Option<&V> + where + S: BranchesStateTrait, + I: Copy + Eq + TryFrom + TryInto, + BI: Copy + Eq + TryFrom + TryInto + Bounded, + { + let mut index = self.0.len(); + // note that we expect branch index to be linearily set + // along a branch (no state containing unordered branch_index + // and no history containing unorderd branch_index). + if index == 0 { + return None; + } + + // TODO EMCH switch loops ? probably. + for (state_branch, state_index) in state.iter() { + while index > 0 { + index -= 1; + let branch_index = as_u(self.0[index].branch_index); + let state_index = as_u(state_index); + if state_index == branch_index { + if let Some(result) = self.branch_get(index, &state_branch) { + return Some(result) + } + } + } + if index == 0 { + break; + } + } + None + } + + fn branch_get(&self, index: usize, state: &S) -> Option<&V> + where + S: BranchStateTrait, + I: Copy + Eq + TryFrom + TryInto + Bounded, + { + let history = &self.0[index]; + let mut index = history.history.len(); + while index > 0 { + index -= 1; + if let Some(&v) = history.history.get(index).as_ref() { + let i = as_u(v.index); + if state.get_node(i) { + return Some(&v.value); + } + } + } + None + } + + /// Gc an historied value other its possible values. + /// Iterator need to be reversed ordered by branch index. + pub fn gc(&mut self, mut states: IT) -> PruneResult + where + IT: Iterator, + S: BranchStateTrait, + I: Copy + Eq + TryFrom + TryInto + Bounded, + { + let mut changed = false; + // state is likely bigger than history. + let mut current_state = states.next(); + for branch_index in (0..self.0.len()).rev() { + let history_branch = self.0[branch_index].branch_index; + loop { + if let Some(state) = current_state.as_ref() { + let state_index_u64 = as_u(state.1); + if history_branch < state_index_u64 { + current_state = states.next(); + } else if history_branch == state_index_u64 { + let len = self.0[branch_index].history.len(); + for history_index in (0..len).rev() { + let node_index = as_u(self.0[branch_index].history[history_index].index); + if !state.0.get_node(node_index) { + if history_index == len - 1 { + changed = self.0[branch_index] + .history.pop().is_some() || changed; + } else { + self.0[branch_index] + .history.remove(history_index); + changed = true; + } + } + } + if self.0[branch_index].history.len() == 0 { + self.0.remove(branch_index); + changed = true; + } + break; + } else { + self.0.remove(branch_index); + changed = true; + break; + } + } else { + self.0.remove(branch_index); + changed = true; + break; + } + } + } + if changed { + if self.0.len() == 0 { + PruneResult::Cleared + } else { + PruneResult::Changed + } + } else { + PruneResult::Unchanged + } + } + +} + +impl<'a, F: SerializedConfig> Serialized<'a, F> { + + pub fn into_owned(self) -> Serialized<'static, F> { + Serialized(self.0.into_owned()) + } + + pub fn into_vec(self) -> Vec { + self.0.into_vec() + } + + pub fn get (&self, state: S) -> Option> + where + S: BranchStateTrait, + I: Copy + Eq + TryFrom + TryInto + Bounded, + { + let mut index = self.0.len(); + if index == 0 { + return None; + } + while index > 0 { + index -= 1; + let HistoriedValue { value, index: state_index } = self.0.get_state(index); + let state_index = as_u(state_index); + if state.get_node(state_index) { + // Note this extra byte is note optimal, should be part of index encoding + if value.len() > 0 { + return Some(Some(&value[..value.len() - 1])); + } else { + return Some(None); + } + } + } + None + } + + /// This append the value, and can only be use in an + /// orderly fashion. + pub fn push(&mut self, state: S, value: Option<&[u8]>) + where + S: BranchStateTrait, + I: Copy + Eq + TryFrom + TryInto, + { + let target_state_index = as_u(state.last_index()); + let index = self.0.len(); + if index > 0 { + let last = self.0.get_state(index - 1); + debug_assert!(target_state_index >= last.index); + if target_state_index == last.index { + self.0.pop(); + } + } + match value { + Some(value) => + self.0.push_extra(HistoriedValue {value, index: target_state_index}, &[0][..]), + None => + self.0.push(HistoriedValue {value: &[], index: target_state_index}), + } + } + + /// keep a single with value history before the state. + pub fn prune(&mut self, index: I) -> PruneResult + where + I: Copy + Eq + TryFrom + TryInto, + { + let from = as_u(index); + let len = self.0.len(); + let mut last_index_with_value = None; + let mut index = 0; + while index < len { + let history = self.0.get_state(index); + if history.index == from + 1 { + // new first content + if history.value.len() != 0 { + // start value over a value drop until here + last_index_with_value = Some(index); + break; + } + } else if history.index > from { + if history.value.len() == 0 + && last_index_with_value.is_none() { + // delete on delete, continue + } else { + if last_index_with_value.is_none() { + // first value, use this index + last_index_with_value = Some(index); + } + break; + } + } + if history.value.len() > 0 { + last_index_with_value = Some(index); + } else { + last_index_with_value = None; + } + index += 1; + } + + if let Some(last_index_with_value) = last_index_with_value { + if last_index_with_value > 0 { + self.0.truncate_until(last_index_with_value); + return PruneResult::Changed; + } + } else { + self.0.clear(); + return PruneResult::Cleared; + } + + PruneResult::Unchanged + } + +} + +#[derive(Debug, Clone)] +#[cfg_attr(any(test, feature = "test"), derive(PartialEq))] +/// Serialized implementation when transaction support is not +/// needed. +pub struct Serialized<'a, F>(SerializedInner<'a, F>); + +impl<'a, F> Serialized<'a, F> { + pub fn from_slice(s: &'a [u8]) -> Serialized<'a, F> { + Serialized(s.into()) + } + + pub fn from_vec(s: Vec) -> Serialized<'static, F> { + Serialized(s.into()) + } + + pub fn from_mut(s: &'a mut Vec) -> Serialized<'a, F> { + Serialized(s.into()) + } +} + +impl<'a, F> Into> for &'a [u8] { + fn into(self) -> Serialized<'a, F> { + Serialized(self.into()) + } +} + +impl<'a, F> Into> for &'a mut Vec { + fn into(self) -> Serialized<'a, F> { + Serialized(self.into()) + } +} + +impl Into> for Vec { + fn into(self) -> Serialized<'static, F> { + Serialized(self.into()) + } +} + +impl<'a, F: SerializedConfig> Default for Serialized<'a, F> { + fn default() -> Self { + Serialized(SerializedInner::<'a, F>::default()) + } +} + +#[cfg(test)] +mod test { + use super::*; + + fn test_states() -> TestStates { + let mut states = TestStates::default(); + assert_eq!(states.create_branch(1, 0, None), Some(1)); + // root branching. + assert_eq!(states.create_branch(1, 0, None), Some(2)); + assert_eq!(Some(true), states.branch_state_mut(1).map(|ls| ls.add_state())); + assert_eq!(states.create_branch(2, 1, None), Some(3)); + assert_eq!(states.create_branch(1, 1, Some(0)), Some(5)); + assert_eq!(states.create_branch(1, 1, Some(2)), None); + assert_eq!(Some(true), states.branch_state_mut(1).map(|ls| ls.add_state())); + assert_eq!(Some(Some(2)), states.branch_state_mut(1).map(|ls| ls.drop_state())); + // cannot create when dropped happen on branch + assert_eq!(Some(false), states.branch_state_mut(1).map(|ls| ls.add_state())); + + assert!(states.get(1, 1)); + // 0> 1: _ _ X + // | |> 3: 1 + // | |> 4: 1 + // | |> 5: 1 + // |> 2: _ + + states + } + + #[test] + fn test_remove_attached() { + let mut states = test_states(); + assert_eq!(Some(Some(1)), states.branch_state_mut(1).map(|ls| ls.drop_state())); + assert!(states.get(3, 0)); + assert!(states.get(4, 0)); + states.apply_drop_state(1, 1); + assert!(!states.get(3, 0)); + assert!(!states.get(4, 0)); + } + + #[test] + fn test_state_refs() { + let states = test_states(); + let ref_3 = vec![ + BranchStatesRef { + branch_index: 1, + state: BranchStateRef { start: 0, end: 2 }, + }, + BranchStatesRef { + branch_index: 3, + state: BranchStateRef { start: 0, end: 1 }, + }, + ]; + assert_eq!(*states.state_ref(3).history, ref_3); + + let mut states = states; + + assert_eq!(states.create_branch(1, 1, Some(0)), Some(6)); + let ref_6 = vec![ + BranchStatesRef { + branch_index: 1, + state: BranchStateRef { start: 0, end: 1 }, + }, + BranchStatesRef { + branch_index: 6, + state: BranchStateRef { start: 0, end: 1 }, + }, + ]; + assert_eq!(*states.state_ref(6).history, ref_6); + + states.valid_treshold = 3; + let mut ref_6 = ref_6; + ref_6.remove(0); + assert_eq!(*states.state_ref(6).history, ref_6); + } + + #[test] + fn test_set_get() { + // 0> 1: _ _ X + // | |> 3: 1 + // | |> 4: 1 + // | |> 5: 1 + // |> 2: _ + let states = test_states(); + let mut item: History = Default::default(); + + for i in 0..6 { + assert_eq!(item.get(&states.state_ref(i)), None); + } + + // setting value respecting branch build order + for i in 1..6 { + item.set(&states.state_ref(i), i); + } + + for i in 1..6 { + assert_eq!(item.get(&states.state_ref(i)), Some(&i)); + } + + let mut ref_3 = states.state_ref(3); + ref_3.limit_branch(1, None); + assert_eq!(item.get(&ref_3), Some(&1)); + + let mut ref_1 = states.state_ref(1); + ref_1.limit_branch(1, Some(0)); + assert_eq!(item.get(&ref_1), None); + item.set(&ref_1, 11); + assert_eq!(item.get(&ref_1), Some(&11)); + + item = Default::default(); + + // could rand shuffle if rand get imported later. + let disordered = [ + [1,2,3,5,4], + [2,5,1,3,4], + [5,3,2,4,1], + ]; + for r in disordered.iter() { + for i in r { + item.set(&states.state_ref(*i), *i); + } + for i in r { + assert_eq!(item.get(&states.state_ref(*i)), Some(i)); + } + } + + } + + + #[test] + fn test_gc() { + // 0> 1: _ _ X + // | |> 3: 1 + // | |> 4: 1 + // | |> 5: 1 + // |> 2: _ + let states = test_states(); + let mut item: History = Default::default(); + // setting value respecting branch build order + for i in 1..6 { + item.set(&states.state_ref(i), i); + } + + let mut states1 = states.branches.clone(); + let action = [(1, true), (2, false), (3, false), (4, true), (5, false)]; + for a in action.iter() { + if !a.1 { + states1.remove(&a.0); + } + } + // makes invalid tree (detaches 4) + states1.get_mut(&1).map(|br| br.state.len = 1); + let states1: BTreeMap<_, _> = states1.iter().map(|(k,v)| (k, v.branch_ref())).collect(); + let mut item1 = item.clone(); + item1.gc(states1.iter().map(|(k, v)| ((&v.state, None), **k)).rev()); + assert_eq!(item1.get(&states.state_ref(1)), None); + for a in action.iter().skip(1) { + if a.1 { + assert_eq!(item1.get(&states.state_ref(a.0)), Some(&a.0)); + } else { + assert_eq!(item1.get(&states.state_ref(a.0)), None); + } + } + } + + #[test] + fn test_prune() { + let mut item: Serialized = Default::default(); + // setting value respecting branch build order + for i in 1..6 { + item.push(i, Some(&[i as u8])); + } + + for a in 1..6 { + assert_eq!(item.get(a), Some(Some(&[a as u8][..]))); + } + item.prune(1); + assert_eq!(item.get(1), None); + for a in 2..6 { + assert_eq!(item.get(a), Some(Some(&[a as u8][..]))); + } + + item.prune(4); + for a in 1..5 { + assert_eq!(item.get(a), None); + } + for a in 5..6 { + assert_eq!(item.get(a), Some(Some(&[a as u8][..]))); + } + + item.prune(80); + for a in 1..4 { + assert_eq!(item.get(a), None); + } + // pruning preserve last valid value + for a in 5..11 { + assert_eq!(item.get(a), Some(Some(&[5 as u8][..]))); + } + + // prune skip unrelevant delete + let mut item: Serialized = Default::default(); + item.push(1, Some(&[1 as u8])); + item.push(2, None); + item.push(3, Some(&[3 as u8])); + assert_eq!(item.get(1), Some(Some(&[1][..]))); + assert_eq!(item.get(2), Some(None)); + assert_eq!(item.get(3), Some(Some(&[3][..]))); + assert_eq!(item.0.len(), 3); + item.prune(1); + assert_eq!(item.0.len(), 1); + assert_eq!(item.get(1), None); + assert_eq!(item.get(2), None); + assert_eq!(item.get(3), Some(Some(&[3][..]))); + + // prune skip unrelevant delete + let mut item: Serialized = Default::default(); + item.push(1, Some(&[1 as u8])); + item.push(3, None); + item.push(4, Some(&[4 as u8])); + assert_eq!(item.get(1), Some(Some(&[1][..]))); + assert_eq!(item.get(2), Some(Some(&[1][..]))); + assert_eq!(item.get(3), Some(None)); + assert_eq!(item.get(4), Some(Some(&[4][..]))); + assert_eq!(item.0.len(), 3); + // 1 needed for state two + assert_eq!(PruneResult::Unchanged, item.prune(1)); + // 3 unneeded + item.prune(2); + assert_eq!(item.0.len(), 1); + assert_eq!(item.get(1), None); + assert_eq!(item.get(2), None); + assert_eq!(item.get(3), None); + assert_eq!(item.get(4), Some(Some(&[4][..]))); + + // prune delete at block + let mut item: Serialized = Default::default(); + item.push(0, Some(&[0 as u8])); + item.push(1, None); + assert_eq!(item.get(0), Some(Some(&[0][..]))); + assert_eq!(item.get(1), Some(None)); + item.prune(0); + assert_eq!(item.get(0), None); + assert_eq!(item.get(1), None); + assert_eq!(item.0.len(), 0); + + } + +} diff --git a/node/executor/src/lib.rs b/node/executor/src/lib.rs index 2c2ad479f5c19..e1dac8e680d30 100644 --- a/node/executor/src/lib.rs +++ b/node/executor/src/lib.rs @@ -139,7 +139,7 @@ mod tests { >::hashed_key_for(0) => { vec![0u8; 32] } - ], map![])); + ], map![], map![])); let r = executor().call::<_, NeverNativeValue, fn() -> _>( &mut t, @@ -175,7 +175,7 @@ mod tests { >::hashed_key_for(0) => { vec![0u8; 32] } - ], map![])); + ], map![], map![])); let r = executor().call::<_, NeverNativeValue, fn() -> _>( &mut t, @@ -207,7 +207,7 @@ mod tests { }, >::hashed_key().to_vec() => vec![0u8; 16], >::hashed_key_for(0) => vec![0u8; 32] - ], map![])); + ], map![], map![])); let r = executor().call::<_, NeverNativeValue, fn() -> _>( &mut t, @@ -243,7 +243,7 @@ mod tests { }, >::hashed_key().to_vec() => vec![0u8; 16], >::hashed_key_for(0) => vec![0u8; 32] - ], map![])); + ], map![], map![])); let r = executor().call::<_, NeverNativeValue, fn() -> _>( &mut t, @@ -269,9 +269,11 @@ mod tests { } fn new_test_ext(code: &[u8], support_changes_trie: bool) -> TestExternalities { + let storage = node_testing::genesis::config(support_changes_trie, Some(code)).build_storage().unwrap(); let mut ext = TestExternalities::new_with_code( code, - node_testing::genesis::config(support_changes_trie, Some(code)).build_storage().unwrap(), + // TODO EMCH get genesis config return kv store + (storage.0, storage.1, Default::default()), ); ext.changes_trie_storage().insert(0, GENESIS_HASH.into(), Default::default()); ext @@ -782,7 +784,7 @@ mod tests { }, >::hashed_key().to_vec() => vec![0u8; 16], >::hashed_key_for(0) => vec![0u8; 32] - ], map![])); + ], map![], map![])); let r = executor().call::<_, NeverNativeValue, fn() -> _>( &mut t, @@ -814,7 +816,7 @@ mod tests { }, >::hashed_key().to_vec() => vec![0u8; 16], >::hashed_key_for(0) => vec![0u8; 32] - ], map![])); + ], map![], map![])); let r = executor().call::<_, NeverNativeValue, fn() -> _>( &mut t, @@ -992,7 +994,7 @@ mod tests { }, >::hashed_key().to_vec() => vec![0u8; 16], >::hashed_key_for(0) => vec![0u8; 32] - ], map![])); + ], map![], map![])); let tip = 1_000_000; let xt = sign(CheckedExtrinsic { diff --git a/srml/authority-discovery/src/lib.rs b/srml/authority-discovery/src/lib.rs index 177e11c4c0657..b3273fc35e6d9 100644 --- a/srml/authority-discovery/src/lib.rs +++ b/srml/authority-discovery/src/lib.rs @@ -260,7 +260,7 @@ mod tests { .unwrap(); // Create externalities. - let mut externalities = TestExternalities::new(t); + let mut externalities = TestExternalities::new_todo(t); externalities.register_extension(KeystoreExt(key_store)); externalities.execute_with(|| { @@ -297,7 +297,7 @@ mod tests { .unwrap(); // Create externalities. - let mut externalities = TestExternalities::new(t); + let mut externalities = TestExternalities::new_todo(t); externalities.register_extension(KeystoreExt(key_store)); externalities.execute_with(|| { @@ -334,7 +334,7 @@ mod tests { .unwrap(); // Create externalities. - let mut externalities = TestExternalities::new(t); + let mut externalities = TestExternalities::new_todo(t); externalities.register_extension(KeystoreExt(key_store)); externalities.execute_with(|| { diff --git a/srml/contracts/src/tests.rs b/srml/contracts/src/tests.rs index d6de2ce3bd6f1..b73d32359cfd7 100644 --- a/srml/contracts/src/tests.rs +++ b/srml/contracts/src/tests.rs @@ -288,7 +288,7 @@ impl ExtBuilder { }, gas_price: self.gas_price, }.assimilate_storage(&mut t).unwrap(); - runtime_io::TestExternalities::new(t) + runtime_io::TestExternalities::new_todo(t) } } diff --git a/srml/democracy/src/lib.rs b/srml/democracy/src/lib.rs index 2af301df1ca7f..3ba0951fc5b1f 100644 --- a/srml/democracy/src/lib.rs +++ b/srml/democracy/src/lib.rs @@ -1089,7 +1089,7 @@ mod tests { vesting: vec![], }.assimilate_storage(&mut t).unwrap(); GenesisConfig::default().assimilate_storage(&mut t).unwrap(); - runtime_io::TestExternalities::new(t) + runtime_io::TestExternalities::new_todo(t) } type System = system::Module; diff --git a/srml/executive/src/lib.rs b/srml/executive/src/lib.rs index 23c645f6ef499..f5c8d674defc4 100644 --- a/srml/executive/src/lib.rs +++ b/srml/executive/src/lib.rs @@ -418,7 +418,7 @@ mod tests { }.assimilate_storage(&mut t).unwrap(); let xt = sr_primitives::testing::TestXt(sign_extra(1, 0, 0), Call::Balances(BalancesCall::transfer(2, 69))); let weight = xt.get_dispatch_info().weight as u64; - let mut t = runtime_io::TestExternalities::new(t); + let mut t = runtime_io::TestExternalities::new_todo(t); t.execute_with(|| { Executive::initialize_block(&Header::new( 1, diff --git a/srml/finality-tracker/src/lib.rs b/srml/finality-tracker/src/lib.rs index ba893c42d0b5e..39a77ea6203a0 100644 --- a/srml/finality-tracker/src/lib.rs +++ b/srml/finality-tracker/src/lib.rs @@ -322,7 +322,7 @@ mod tests { #[test] fn median_works() { let t = system::GenesisConfig::default().build_storage::().unwrap(); - TestExternalities::new(t).execute_with(|| { + TestExternalities::new_todo(t).execute_with(|| { FinalityTracker::update_hint(Some(500)); assert_eq!(FinalityTracker::median(), 250); assert!(NOTIFICATIONS.with(|n| n.borrow().is_empty())); @@ -332,7 +332,7 @@ mod tests { #[test] fn notifies_when_stalled() { let t = system::GenesisConfig::default().build_storage::().unwrap(); - TestExternalities::new(t).execute_with(|| { + TestExternalities::new_todo(t).execute_with(|| { let mut parent_hash = System::parent_hash(); for i in 2..106 { System::initialize(&i, &parent_hash, &Default::default(), &Default::default()); @@ -351,7 +351,7 @@ mod tests { #[test] fn recent_notifications_prevent_stalling() { let t = system::GenesisConfig::default().build_storage::().unwrap(); - TestExternalities::new(t).execute_with(|| { + TestExternalities::new_todo(t).execute_with(|| { let mut parent_hash = System::parent_hash(); for i in 2..106 { System::initialize(&i, &parent_hash, &Default::default(), &Default::default()); diff --git a/srml/session/src/historical.rs b/srml/session/src/historical.rs index 08e4a6ce3175f..067183d7ecd59 100644 --- a/srml/session/src/historical.rs +++ b/srml/session/src/historical.rs @@ -329,7 +329,7 @@ mod tests { l.borrow().iter().cloned().map(|i| (i, UintAuthorityId(i).into())).collect() ), }.assimilate_storage(&mut t).unwrap(); - runtime_io::TestExternalities::new(t) + runtime_io::TestExternalities::new_todo(t) } #[test] diff --git a/srml/session/src/lib.rs b/srml/session/src/lib.rs index bb14b0d8f41b2..8779fdedfddca 100644 --- a/srml/session/src/lib.rs +++ b/srml/session/src/lib.rs @@ -695,7 +695,7 @@ mod tests { l.borrow().iter().cloned().map(|i| (i, UintAuthorityId(i).into())).collect() ), }.assimilate_storage(&mut t).unwrap(); - runtime_io::TestExternalities::new(t) + runtime_io::TestExternalities::new_todo(t) } fn initialize_block(block: u64) { diff --git a/srml/system/src/lib.rs b/srml/system/src/lib.rs index 7c90f21240a06..405955787eb7e 100644 --- a/srml/system/src/lib.rs +++ b/srml/system/src/lib.rs @@ -700,7 +700,7 @@ impl Module { >::hashed_key_for(T::BlockNumber::zero()) => [69u8; 32].encode(), >::hashed_key().to_vec() => T::BlockNumber::one().encode(), >::hashed_key().to_vec() => [69u8; 32].encode() - ], map![])) + ], map![], map![])) } /// Set the block number to something in particular. Can be used as an alternative to diff --git a/srml/timestamp/src/lib.rs b/srml/timestamp/src/lib.rs index cdacb8a391316..8511169b9002b 100644 --- a/srml/timestamp/src/lib.rs +++ b/srml/timestamp/src/lib.rs @@ -369,7 +369,7 @@ mod tests { #[test] fn timestamp_works() { let t = system::GenesisConfig::default().build_storage::().unwrap(); - TestExternalities::new(t).execute_with(|| { + TestExternalities::new_todo(t).execute_with(|| { Timestamp::set_timestamp(42); assert_ok!(Timestamp::dispatch(Call::set(69), Origin::NONE)); assert_eq!(Timestamp::now(), 69); @@ -380,7 +380,7 @@ mod tests { #[should_panic(expected = "Timestamp must be updated only once in the block")] fn double_timestamp_should_fail() { let t = system::GenesisConfig::default().build_storage::().unwrap(); - TestExternalities::new(t).execute_with(|| { + TestExternalities::new_todo(t).execute_with(|| { Timestamp::set_timestamp(42); assert_ok!(Timestamp::dispatch(Call::set(69), Origin::NONE)); let _ = Timestamp::dispatch(Call::set(70), Origin::NONE); @@ -391,7 +391,7 @@ mod tests { #[should_panic(expected = "Timestamp must increment by at least between sequential blocks")] fn block_period_minimum_enforced() { let t = system::GenesisConfig::default().build_storage::().unwrap(); - TestExternalities::new(t).execute_with(|| { + TestExternalities::new_todo(t).execute_with(|| { Timestamp::set_timestamp(42); let _ = Timestamp::dispatch(Call::set(46), Origin::NONE); });