diff --git a/Cargo.lock b/Cargo.lock index 8c215ddd12611..3075e1cded697 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1557,6 +1557,15 @@ dependencies = [ "proc-macro-hack 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "historical-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.7.1" @@ -5313,6 +5322,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)", + "historical-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)", @@ -6036,6 +6046,7 @@ name = "substrate-state-db" version = "2.0.0" dependencies = [ "env_logger 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "historical-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 bdc0d8737518c..f077e331360fa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -50,6 +50,7 @@ members = [ "core/transaction-pool/graph", "core/trie", "core/utils/fork-tree", + "core/utils/historical-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..79391e0bbb760 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" } +historical-data = { path = "../../utils/historical-data" } [dev-dependencies] substrate-keyring = { path = "../../keyring" } diff --git a/core/client/db/src/lib.rs b/core/client/db/src/lib.rs index 5902afff0de93..f39004e466cb7 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}; @@ -41,7 +42,7 @@ use std::collections::{HashMap, HashSet}; use client::backend::NewBlockState; use client::blockchain::{well_known_cache_keys, HeaderBackend}; use client::{ForkBlocks, ExecutionStrategies}; -use client::backend::{StorageCollection, ChildStorageCollection}; +use client::backend::{StorageCollection, ChildStorageCollection, FullStorageCollection}; use client::error::{Result as ClientResult, Error as ClientError}; use codec::{Decode, Encode}; use hash_db::{Hasher, Prefix}; @@ -60,19 +61,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 historical_data::tree::Serialized; +use historical_data::PruneResult; +use historical_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 +90,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::StateBackend< + Arc>, + Blake2Hasher, + Arc, +>; /// Re-export the KVDB trait so that one can pass an implementation of it. pub use kvdb; @@ -124,6 +138,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) @@ -137,6 +152,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) } @@ -175,10 +194,29 @@ impl StateBackend for RefTrackingState { self.state.child_storage_root(storage_key, 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_in_memory(&self) -> InMemoryKvBackend { + self.state.kv_in_memory() + } + fn keys(&self, prefix: &[u8]) -> Vec> { self.state.keys(prefix) } @@ -187,8 +225,10 @@ impl StateBackend for RefTrackingState { self.state.child_keys(child_key, prefix) } - fn as_trie_backend(&mut self) -> Option<&state_machine::TrieBackend> { - self.state.as_trie_backend() + fn as_state_backend(&mut self) -> Option< + &state_machine::StateBackend + > { + self.state.as_state_backend() } } @@ -263,6 +303,10 @@ pub(crate) mod columns { pub const AUX: Option = Some(8); /// Offchain workers local storage pub const OFFCHAIN: Option = Some(9); + /// Kv storage main collection. + /// Content here should be organized by prefixing + /// keys to avoid conflicts. + pub const KV: Option = Some(10); } struct PendingBlock { @@ -448,7 +492,7 @@ impl HeaderMetadata for BlockchainDb { /// Database transaction pub struct BlockImportOperation { old_state: CachingState, Block>, - db_updates: PrefixedMemoryDB, + db_updates: (PrefixedMemoryDB, InMemoryKvBackend), storage_updates: StorageCollection, child_storage_updates: ChildStorageCollection, changes_trie_updates: MemoryDB, @@ -501,7 +545,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, InMemoryKvBackend), + ) -> ClientResult<()> { self.db_updates = update; Ok(()) } @@ -528,7 +575,8 @@ 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; @@ -554,11 +602,10 @@ impl client::backend::BlockImportOperation fn update_storage( &mut self, - update: StorageCollection, - child_update: ChildStorageCollection, + update: FullStorageCollection, ) -> ClientResult<()> { - self.storage_updates = update; - self.child_storage_updates = child_update; + self.storage_updates = update.top; + self.child_storage_updates = update.children; Ok(()) } @@ -579,6 +626,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); @@ -596,6 +648,44 @@ 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::KV, 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::KV).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 in_memory(&self) -> InMemoryKvBackend { + self.storage_db.state_db.get_kv_pairs(&self.state, self.storage_db.deref()) + // No deletion on storage db. + .into_iter().map(|(k, v)| (k, Some(v))).collect() + } +} + struct DbGenesisStorage(pub H256); impl DbGenesisStorage { @@ -877,7 +967,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 @@ -888,7 +984,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_in_memory().clone(), + }).unwrap(); inmem.commit_operation(op).unwrap(); } @@ -1065,7 +1164,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.0, &self.storage.db, commit.1).map_err(|err| + client::error::Error::Backend( + format!("Error building commit transaction : {}", err) + ) + )?; }; Ok(()) @@ -1139,17 +1242,32 @@ impl> Backend { let finalized = if operation.commit_state { 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 + // switch to vec for size + .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(); @@ -1189,9 +1307,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)); @@ -1277,15 +1406,22 @@ 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 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.0, &self.storage.db, commit.1) + .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 { @@ -1303,7 +1439,83 @@ 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, change) in commit.kv.iter() { + let (mut ser, new) = if let Some(stored) = db.get(columns::KV, key)? { + (Ser::from_vec(stored.to_vec()), false) + } else { + if change.is_some() { + (Ser::default(), true) + } else { + // Deleting a value on a key that does not exists should + // not happen in theory, but if it where to happen nothing + // will be done here. + debug!( + "A key value is being deleted but does not exists: {:?}", + key + ); + + break; + } + }; + ser.push(last_block, change.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::KV, &key), + PruneResult::Changed + | PruneResult::Unchanged => transaction.put(columns::KV, &key[..], &ser.into_vec()), + } + } else { + transaction.put(columns::KV, &key[..], &ser.into_vec()) + } + } else { + transaction.put(columns::KV, &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() { + let mut ser = if let Some(stored) = db.get(columns::KV, key)? { + Ser::from_vec(stored.to_vec()) + } else { + break; + }; + + match ser.prune(block_prune) { + PruneResult::Cleared => transaction.delete(columns::KV, &key), + PruneResult::Changed => transaction.put(columns::KV, &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); } @@ -1316,6 +1528,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 { @@ -1354,7 +1577,7 @@ impl client::backend::Backend for Backend whe Ok(BlockImportOperation { pending_block: None, old_state, - db_updates: PrefixedMemoryDB::default(), + db_updates: Default::default(), storage_updates: Default::default(), child_storage_updates: Default::default(), changes_trie_updates: MemoryDB::default(), @@ -1445,7 +1668,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)))?; @@ -1485,7 +1709,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)); }, @@ -1499,8 +1724,14 @@ impl client::backend::Backend for Backend whe return Err(client::error::Error::UnknownBlock(format!("State already discarded for {:?}", block))) } 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 { @@ -1617,6 +1848,69 @@ mod tests { header_hash } + fn insert_kvs_and_finalize( + backend: &Backend, + number: u64, + parent_hash: H256, + kvs: Vec<(Vec, Option>)>, + extrinsics_root: H256, + finalize: Option>, + ) -> H256 { + let header = Header { + number, + parent_hash, + state_root: BlakeTwo256::trie_root(Vec::new()), + digest: Default::default(), + extrinsics_root, + }; + let header_hash = header.hash(); + + let block_id = if number == 0 { + BlockId::Hash(Default::default()) + } else { + BlockId::Number(number - 1) + }; + let mut op = backend.begin_operation().unwrap(); + backend.begin_state_operation(&mut op, block_id).unwrap(); + if let Some(finalize) = finalize { + for finalize in finalize { + op.mark_finalized(BlockId::hash(finalize), None).unwrap(); + } + } + let kv_transaction = kvs.into_iter().collect(); + op.update_db_storage((Default::default(), kv_transaction)).unwrap(); + op.set_block_data(header, None, None, NewBlockState::Best).unwrap(); + backend.commit_operation(op).unwrap(); + header_hash + } + + + fn insert_kvs( + backend: &Backend, + number: u64, + parent_hash: H256, + kvs: Vec<(Vec, Option>)>, + extrinsics_root: H256, + ) -> H256 { + insert_kvs_and_finalize( + backend, + number, + parent_hash, + kvs, + extrinsics_root, + None, + ) + } + + fn check_kv (backend: &Backend, block: H256, val: &Vec<(Vec, Option>)>) { + let block = BlockId::Hash(block); + let state = backend.state_at(block).unwrap(); + for (k, v) in val { + let content = state.kv_storage(k).unwrap(); + assert_eq!(v, &content); + } + } + #[test] fn block_hash_inserted_correctly() { let backing = { @@ -1732,7 +2026,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(), + ); op.update_db_storage(overlay).unwrap(); header.state_root = root.into(); @@ -1750,6 +2049,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_in_memory(), 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_in_memory(), vec![].into_iter().collect()); + } } @@ -1781,7 +2086,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![]), @@ -1817,8 +2122,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![]), @@ -1854,7 +2159,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![]), @@ -2132,6 +2437,147 @@ mod tests { assert!(backend.changes_tries_storage.get(&root3, EMPTY_PREFIX).unwrap().is_some()); } + #[test] + fn kv_storage_works() { + let backend = Backend::::new_test(1000, 100); + + let changes0 = vec![(b"key_at_0".to_vec(), Some(b"val_at_0".to_vec()))]; + let changes1 = vec![ + (b"key_at_1".to_vec(), Some(b"val_at_1".to_vec())), + (b"another_key_at_1".to_vec(), Some(b"another_val_at_1".to_vec())), + ]; + let changes2 = vec![(b"key_at_2".to_vec(), Some(b"val_at_2".to_vec()))]; + let changes3 = vec![(b"another_key_at_1".to_vec(), None)]; + + let block0 = insert_kvs(&backend, 0, Default::default(), changes0.clone(), Default::default()); + let block1 = insert_kvs(&backend, 1, block0, changes1.clone(), Default::default()); + let block2 = insert_kvs(&backend, 2, block1, changes2.clone(), Default::default()); + let block3 = insert_kvs(&backend, 3, block2, changes3.clone(), Default::default()); + + check_kv(&backend, block0, &changes0); + check_kv(&backend, block1, &changes0); + check_kv(&backend, block1, &changes1); + check_kv(&backend, block2, &changes1); + check_kv(&backend, block2, &changes2); + check_kv(&backend, block3, &changes3); + } + + #[test] + fn kv_storage_works_with_forks() { + let backend = Backend::::new_test(4, 4); + + let changes0 = vec![(b"k0".to_vec(), Some(b"v0".to_vec()))]; + let changes1 = vec![ + (b"k1".to_vec(), Some(b"v1".to_vec())), + (b"k0".to_vec(), None), + ]; + let changes2 = vec![(b"k2".to_vec(), Some(b"v2".to_vec()))]; + let block0 = insert_kvs(&backend, 0, Default::default(), changes0.clone(), Default::default()); + let block1 = insert_kvs(&backend, 1, block0, changes1.clone(), Default::default()); + let block2 = insert_kvs(&backend, 2, block1, changes2.clone(), Default::default()); + + let changes2_1_0 = vec![(b"k3".to_vec(), Some(b"v3".to_vec()))]; + let changes2_1_1 = vec![(b"k4".to_vec(), Some(b"v4".to_vec()))]; + let block2_1_0 = insert_kvs(&backend, 3, block2, changes2_1_0.clone(), Default::default()); + let block2_1_1 = insert_kvs(&backend, 4, block2_1_0, changes2_1_1.clone(), Default::default()); + + let changes2_2_0 = vec![(b"k5".to_vec(), Some(b"v5".to_vec()))]; + let changes2_2_1 = vec![(b"k6".to_vec(), Some(b"v6".to_vec()))]; + // use different extrinsic root to have different hash than 2_1_0 + let block2_2_0 = insert_kvs(&backend, 3, block2, changes2_2_0.clone(), [1u8; 32].into()); + let block2_2_1 = insert_kvs(&backend, 4, block2_2_0, changes2_2_1.clone(), Default::default()); + + // branch1: when asking for finalized block hash + check_kv(&backend, block0, &changes0); + check_kv(&backend, block1, &vec![]); + check_kv(&backend, block1, &changes1); + check_kv(&backend, block2, &changes2); + check_kv(&backend, block2_1_0, &changes2_1_0); + check_kv(&backend, block2_1_1, &changes2_1_1); + check_kv(&backend, block2_2_0, &changes2_2_0); + check_kv(&backend, block2_2_1, &changes2_2_1); + + // after canonicalize range on 2_2_0 side + let mut block_prev = block2_1_1; + let mut nb_prev = 4; + let mut next = || { + block_prev = insert_kvs(&backend, nb_prev, block_prev, vec![], Default::default()); + nb_prev += 1; + }; + + next(); + next(); + next(); + check_kv(&backend, block0, &changes0); + check_kv(&backend, block1, &changes1); + check_kv(&backend, block2, &changes2); + check_kv(&backend, block2_1_0, &changes2_1_0); + check_kv(&backend, block2_2_0, &changes2_2_0); + let state = backend.state_at(BlockId::Hash(block2_2_0)).unwrap(); + assert!(state.kv_storage(&b"k5"[..]).unwrap().is_some()); + next(); + check_kv(&backend, block2_1_0, &changes2_1_0); + assert!(backend.state_at(BlockId::Hash(block2_2_0)).is_err()); + // pinned state is not garbage collected + assert!(state.kv_storage(&b"k5"[..]).unwrap().is_some()); + + // check pruning is called on storage + check_kv(&backend, block0, &changes0); + assert!(backend.storage.db.get(crate::columns::KV, &b"k0"[..]).unwrap().is_some()); + next(); + assert!(backend.state_at(BlockId::Hash(block0)).is_err()); + assert!(backend.storage.db.get(crate::columns::KV, &b"k0"[..]).unwrap().is_none()); + } + + #[test] + fn kv_storage_works_with_finalize() { + let backend = Backend::::new_test(1000, 100); + + let changes0 = vec![(b"k0".to_vec(), Some(b"v0".to_vec()))]; + let changes1 = vec![(b"k1".to_vec(), Some(b"v1".to_vec()))]; + let changes2 = vec![(b"k2".to_vec(), Some(b"v2".to_vec()))]; + let block0 = insert_kvs(&backend, 0, Default::default(), changes0.clone(), Default::default()); + let block1 = insert_kvs(&backend, 1, block0, changes1.clone(), Default::default()); + let block2 = insert_kvs(&backend, 2, block1, changes2.clone(), Default::default()); + + let changes2_1_0 = vec![(b"k3".to_vec(), Some(b"v3".to_vec()))]; + let changes2_1_1 = vec![(b"k4".to_vec(), Some(b"v4".to_vec()))]; + let block2_1_0 = insert_kvs(&backend, 3, block2, changes2_1_0.clone(), Default::default()); + let _block2_1_1 = insert_kvs(&backend, 4, block2_1_0, changes2_1_1.clone(), Default::default()); + + let changes2_2_0 = vec![(b"k5".to_vec(), Some(b"v5".to_vec()))]; + let changes2_2_1 = vec![(b"k6".to_vec(), Some(b"v6".to_vec()))]; + // use different extrinsic root to have different hash than 2_1_0 + let block2_2_0 = insert_kvs(&backend, 3, block2, changes2_2_0.clone(), [1u8; 32].into()); + let state = backend.state_at(BlockId::Hash(block2_1_0)).unwrap(); + + let block2_2_1 = insert_kvs_and_finalize( + &backend, + 4, + block2_2_0, + changes2_2_1.clone(), + Default::default(), + Some(vec![ + block1, + block2, + block2_2_0, + ]), + ); + + check_kv(&backend, block0, &changes0); + check_kv(&backend, block1, &changes0); + check_kv(&backend, block1, &changes1); + check_kv(&backend, block2, &changes2); + check_kv(&backend, block2_2_0, &changes2_2_0); + check_kv(&backend, block2_2_1, &changes2_2_1); + + // still accessible due to pinned state + assert!(state.kv_storage(&b"k3"[..]).unwrap().is_some()); + assert!(backend.state_at(BlockId::Hash(block2_1_0)).is_err()); + } + + + #[test] fn tree_route_works() { let backend = Backend::::new_test(1000, 100); diff --git a/core/client/db/src/storage_cache.rs b/core/client/db/src/storage_cache.rs index e1ad6f493aa7b..4ab912ad65306 100644 --- a/core/client/db/src/storage_cache.rs +++ b/core/client/db/src/storage_cache.rs @@ -23,7 +23,7 @@ use linked_hash_map::{LinkedHashMap, Entry}; use hash_db::Hasher; use sr_primitives::traits::{Block as BlockT, Header}; use primitives::hexdisplay::HexDisplay; -use state_machine::{backend::Backend as StateBackend, TrieBackend}; +use state_machine::{backend::Backend as StateBackendTrait, StateBackend, InMemoryKvBackend}; use log::trace; use super::{StorageCollection, ChildStorageCollection}; use std::hash::Hash as StdHash; @@ -278,14 +278,14 @@ pub struct CacheChanges { /// For canonical instances local cache is accumulated and applied /// in `sync_cache` along with the change overlay. /// For non-canonical clones local cache and changes are dropped. -pub struct CachingState, B: BlockT> { +pub struct CachingState, B: BlockT> { /// Backing state. state: S, /// Cache data. pub cache: CacheChanges } -impl, B: BlockT> std::fmt::Debug for CachingState { +impl, B: BlockT> std::fmt::Debug for CachingState { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "Block {:?}", self.cache.parent_hash) } @@ -394,7 +394,7 @@ impl CacheChanges { } -impl, B: BlockT> CachingState { +impl, B: BlockT> CachingState { /// Create a new instance wrapping generic State and shared cache. pub fn new(state: S, shared_cache: SharedCache, parent_hash: Option) -> CachingState { CachingState { @@ -462,10 +462,11 @@ impl, B: BlockT> CachingState { } } -impl, B: BlockT> StateBackend for CachingState { +impl, B: BlockT> StateBackendTrait 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(); @@ -526,6 +527,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()) } @@ -566,10 +571,29 @@ impl, B: BlockT> StateBackend for CachingState< self.state.child_storage_root(storage_key, 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_in_memory(&self) -> InMemoryKvBackend { + self.state.kv_in_memory() + } + fn keys(&self, prefix: &[u8]) -> Vec> { self.state.keys(prefix) } @@ -578,8 +602,10 @@ impl, B: BlockT> StateBackend for CachingState< self.state.child_keys(child_key, prefix) } - fn as_trie_backend(&mut self) -> Option<&TrieBackend> { - self.state.as_trie_backend() + fn as_state_backend(&mut self) -> Option< + &StateBackend + > { + self.state.as_state_backend() } } diff --git a/core/client/db/src/utils.rs b/core/client/db/src/utils.rs index 0a6112abe7a6e..bc2cee293952f 100644 --- a/core/client/db/src/utils.rs +++ b/core/client/db/src/utils.rs @@ -37,7 +37,7 @@ use crate::{DatabaseSettings, DatabaseSettingsSrc}; /// 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 dc0f0e5d4c301..4c826f38761e4 100644 --- a/core/client/src/backend.rs +++ b/core/client/src/backend.rs @@ -36,12 +36,24 @@ pub type StorageCollection = Vec<(Vec, Option>)>; /// In memory arrays of storage values for multiple child tries. pub type ChildStorageCollection = Vec<(Vec, StorageCollection)>; +#[derive(Clone)] +/// Collection of all storage element, can manage a single state delta +/// (deletion are included). +pub struct FullStorageCollection { + /// Parent trie changes. + pub top: StorageCollection, + /// Children trie changes. + pub children: ChildStorageCollection, + /// Key value not in trie changes. + pub kv: StorageCollection, +} + pub(crate) struct ImportSummary { pub(crate) hash: Block::Hash, 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, pub(crate) retracted: Vec, } @@ -121,8 +133,7 @@ pub trait BlockImportOperation where /// Set storage changes. fn update_storage( &mut self, - update: StorageCollection, - child_update: ChildStorageCollection, + update: FullStorageCollection, ) -> error::Result<()>; /// Inject changes trie data into the database. diff --git a/core/client/src/call_executor.rs b/core/client/src/call_executor.rs index e634fcf8faa9f..abf7b017dec18 100644 --- a/core/client/src/call_executor.rs +++ b/core/client/src/call_executor.rs @@ -128,7 +128,7 @@ where method: &str, call_data: &[u8] ) -> Result<(Vec, StorageProof), error::Error> { - let trie_state = state.as_trie_backend() + let trie_state = state.as_state_backend() .ok_or_else(|| Box::new(state_machine::ExecutionError::UnableToGenerateProof) as Box @@ -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, + K: state_machine::KvBackend, + >( &self, - trie_state: &state_machine::TrieBackend, + trie_state: &state_machine::StateBackend, overlay: &mut OverlayedChanges, method: &str, call_data: &[u8] @@ -263,7 +266,7 @@ where let result = match recorder { Some(recorder) => { - let trie_state = state.as_trie_backend() + let trie_state = state.as_state_backend() .ok_or_else(|| Box::new(state_machine::ExecutionError::UnableToGenerateProof) as Box @@ -371,9 +374,12 @@ where .map_err(Into::into) } - fn prove_at_trie_state>( + fn prove_at_trie_state< + S: state_machine::TrieBackendStorage, + K: state_machine::KvBackend, + >( &self, - trie_state: &state_machine::TrieBackend, + trie_state: &state_machine::StateBackend, overlay: &mut OverlayedChanges, method: &str, call_data: &[u8] diff --git a/core/client/src/cht.rs b/core/client/src/cht.rs index aff875032d357..2f8ecf8c0d1ae 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::{MemoryDB, TrieBackend, Backend as StateBackend, StorageProof, +use state_machine::backend::InMemoryTransaction; +use state_machine::{MemoryDB, StateBackend, Backend as StateBackendTrait, StorageProof, prove_read_on_trie_backend, read_proof_check, read_proof_check_on_proving_backend}; +use state_machine::InMemoryKvBackend; use crate::error::{Error as ClientError, Result as ClientResult}; @@ -100,11 +102,14 @@ pub fn build_proof( .into_iter() .map(|(k, v)| (None, k, Some(v))) .collect::>(); - let mut storage = InMemoryState::::default().update(transaction); - let trie_storage = storage.as_trie_backend() - .expect("InMemoryState::as_trie_backend always returns Some; qed"); + let mut storage = InMemoryState::::default().update(InMemoryTransaction { + storage: transaction, + kv: Default::default(), + }); + let state_backend = storage.as_state_backend() + .expect("InMemoryState::as_state_backend always returns Some; qed"); prove_read_on_trie_backend( - trie_storage, + state_backend, blocks.into_iter().map(|number| encode_cht_key(number)), ).map_err(ClientError::Execution) } @@ -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: &StateBackend, Hasher, InMemoryKvBackend>, ) -> ClientResult<()> where Header: HeaderT, diff --git a/core/client/src/client.rs b/core/client/src/client.rs index fdd50b0d01e8b..4e936246c1c67 100644 --- a/core/client/src/client.rs +++ b/core/client/src/client.rs @@ -60,7 +60,7 @@ use crate::{ }, backend::{ self, BlockImportOperation, PrunableStateChangesTrieStorage, - ClientImportOperation, Finalizer, ImportSummary, + ClientImportOperation, Finalizer, ImportSummary, FullStorageCollection, }, blockchain::{ self, Info as ChainInfo, Backend as ChainBackend, @@ -960,7 +960,7 @@ impl Client where operation.op.update_db_storage(storage_update)?; } if let Some(storage_changes) = storage_changes.clone() { - operation.op.update_storage(storage_changes.0, storage_changes.1)?; + operation.op.update_storage(storage_changes)?; } if let Some(Some(changes_update)) = changes_update { operation.op.update_changes_trie(changes_update)?; @@ -1033,10 +1033,7 @@ impl Client where ) -> error::Result<( Option>, Option>>, - Option<( - Vec<(Vec, Option>)>, - Vec<(Vec, Vec<(Vec, Option>)>)> - )> + Option, )> where E: CallExecutor + Send + Sync + Clone, @@ -1088,13 +1085,21 @@ 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(FullStorageCollection { + top: top.collect(), + children, + kv: kv.collect(), + }), + )) }, None => Ok((None, None, None)) } @@ -1194,8 +1199,8 @@ impl Client where self.storage_notifications.lock() .trigger( ¬ify_import.hash, - storage_changes.0.into_iter(), - storage_changes.1.into_iter().map(|(sk, v)| (sk, v.into_iter())), + storage_changes.top.into_iter(), + storage_changes.children.into_iter().map(|(sk, v)| (sk, v.into_iter())), ); } diff --git a/core/client/src/in_mem.rs b/core/client/src/in_mem.rs index 5c35400d7743c..92ec39bf89f10 100644 --- a/core/client/src/in_mem.rs +++ b/core/client/src/in_mem.rs @@ -30,7 +30,7 @@ use trie::MemoryDB; use header_metadata::{CachedHeaderMetadata, HeaderMetadata}; use crate::error; -use crate::backend::{self, NewBlockState, StorageCollection, ChildStorageCollection}; +use crate::backend::{self, NewBlockState, FullStorageCollection}; use crate::light; use crate::leaves::LeafSet; use crate::blockchain::{ @@ -511,7 +511,8 @@ where let (root, transaction) = self.old_state.full_storage_root( top.into_iter().map(|(k, v)| (k, Some(v))), - child_delta + child_delta, + None, ); self.new_state = Some(InMemory::from(transaction)); @@ -527,8 +528,7 @@ where fn update_storage( &mut self, - _update: StorageCollection, - _child_update: ChildStorageCollection, + _update: FullStorageCollection, ) -> error::Result<()> { Ok(()) } diff --git a/core/client/src/light/backend.rs b/core/client/src/light/backend.rs index 300d140630d85..908024cfbaa99 100644 --- a/core/client/src/light/backend.rs +++ b/core/client/src/light/backend.rs @@ -22,12 +22,15 @@ use std::sync::Arc; use parking_lot::{RwLock, Mutex}; use sr_primitives::{generic::BlockId, Justification, StorageOverlay, ChildrenStorageOverlay}; -use state_machine::{Backend as StateBackend, TrieBackend, backend::InMemory as InMemoryState, ChangesTrieTransaction}; +use state_machine::{ + Backend as StateBackendTrait, StateBackend, backend::InMemory as InMemoryState, + ChangesTrieTransaction, InMemoryKvBackend, +}; use sr_primitives::traits::{Block as BlockT, NumberFor, Zero, Header}; use crate::in_mem::{self, check_genesis_storage}; use crate::backend::{ AuxStore, Backend as ClientBackend, BlockImportOperation, RemoteBackend, NewBlockState, - StorageCollection, ChildStorageCollection, + FullStorageCollection, }; use crate::blockchain::{HeaderBackend as BlockchainHeaderBackend, well_known_cache_keys}; use crate::error::{Error as ClientError, Result as ClientResult}; @@ -262,7 +265,10 @@ where self.cache = cache; } - fn update_db_storage(&mut self, _update: >::Transaction) -> ClientResult<()> { + fn update_db_storage( + &mut self, + _update: >::Transaction, + ) -> ClientResult<()> { // we're not storing anything locally => ignore changes Ok(()) } @@ -291,7 +297,11 @@ where } let storage_update: InMemoryState = storage.into(); - let (storage_root, _) = storage_update.full_storage_root(::std::iter::empty(), child_delta); + let (storage_root, _) = storage_update.full_storage_root( + ::std::iter::empty(), + child_delta, + ::std::iter::empty(), + ); self.storage_update = Some(storage_update); Ok(storage_root) @@ -306,8 +316,7 @@ where fn update_storage( &mut self, - _update: StorageCollection, - _child_update: ChildStorageCollection, + _update: FullStorageCollection, ) -> ClientResult<()> { // we're not storing anything locally => ignore changes Ok(()) @@ -333,13 +342,14 @@ impl std::fmt::Debug for GenesisOrUnavailableState { } } -impl StateBackend for GenesisOrUnavailableState +impl StateBackendTrait for GenesisOrUnavailableState where H::Out: Ord, { type Error = ClientError; type Transaction = (); type TrieBackendStorage = MemoryDB; + type KvBackend = state_machine::InMemoryKvBackend; fn storage(&self, key: &[u8]) -> ClientResult>> { match *self { @@ -357,6 +367,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), @@ -416,6 +434,13 @@ impl StateBackend for GenesisOrUnavailableState } } + 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 +448,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_in_memory(&self) -> InMemoryKvBackend { + match *self { + GenesisOrUnavailableState::Genesis(ref state) => state.kv_in_memory(), + GenesisOrUnavailableState::Unavailable => Default::default(), + } + } + fn keys(&self, prefix: &[u8]) -> Vec> { match *self { GenesisOrUnavailableState::Genesis(ref state) => state.keys(prefix), @@ -430,9 +476,11 @@ impl StateBackend for GenesisOrUnavailableState } } - fn as_trie_backend(&mut self) -> Option<&TrieBackend> { + fn as_state_backend(&mut self) -> Option< + &StateBackend + > { match self { - GenesisOrUnavailableState::Genesis(ref mut state) => state.as_trie_backend(), + GenesisOrUnavailableState::Genesis(ref mut state) => state.as_state_backend(), GenesisOrUnavailableState::Unavailable => None, } } diff --git a/core/client/src/light/call_executor.rs b/core/client/src/light/call_executor.rs index 7f54004ae6722..38998d54ae0c1 100644 --- a/core/client/src/light/call_executor.rs +++ b/core/client/src/light/call_executor.rs @@ -171,9 +171,12 @@ impl CallExecutor for Err(ClientError::NotAvailableOnLightClient) } - fn prove_at_trie_state>( + fn prove_at_trie_state< + S: state_machine::TrieBackendStorage, + K: state_machine::KvBackend, + >( &self, - _state: &state_machine::TrieBackend, + _state: &state_machine::StateBackend, _changes: &mut OverlayedChanges, _method: &str, _call_data: &[u8] @@ -202,7 +205,7 @@ pub fn prove_execution( S: StateBackend, E: CallExecutor, { - let trie_state = state.as_trie_backend() + let trie_state = state.as_state_backend() .ok_or_else(|| Box::new(state_machine::ExecutionError::UnableToGenerateProof) as Box)?; // prepare execution environment + record preparation proof @@ -370,9 +373,12 @@ mod tests { unreachable!() } - fn prove_at_trie_state>( + fn prove_at_trie_state< + S: state_machine::TrieBackendStorage, + K: state_machine::KvBackend, + >( &self, - _trie_state: &state_machine::TrieBackend, + _trie_state: &state_machine::StateBackend, _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 6ae28b748c527..54e02b48b98da 100644 --- a/core/client/src/light/fetcher.rs +++ b/core/client/src/light/fetcher.rs @@ -30,7 +30,7 @@ use sr_primitives::traits::{ }; use state_machine::{ ChangesTrieRootsStorage, ChangesTrieAnchorBlockId, ChangesTrieConfigurationRange, - TrieBackend, read_proof_check, key_changes_proof_check, create_proof_check_backend_storage, + StateBackend, read_proof_check, key_changes_proof_check, create_proof_check_backend_storage, read_child_proof_check, }; pub use state_machine::StorageProof; @@ -350,8 +350,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 = StateBackend::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/state-db/Cargo.toml b/core/state-db/Cargo.toml index d271a0e179d6d..8625ac4b74e79 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"] } +historical-data = { path = "../../core/utils/historical-data" } [dev-dependencies] env_logger = "0.7.0" +historical-data = { path = "../../core/utils/historical-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..85971aaed2216 --- /dev/null +++ b/core/state-db/src/branch.rs @@ -0,0 +1,530 @@ + +// 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. +//! +//! This allows managing an index over the multiple block forks +//! in the state db. +//! +//! State db use block hash to address blocks in different branches. +//! +//! These utilities aims at managing for trees of fork and indexed +//! way to address blocks in fork. That is the block number and a +//! branch number. +//! Branch number are not allows to change, once set they are kept. +//! This property is important to avoid any data update for on branch +//! modification. +//! Therefore the tree formed by these branch inexing is not balanced, +//! branch are created sequentially to block addition with a very +//! simple rules: if the block can be added to parent branch (number following +//! the last block and there was no block with this numbering before) it +//! uses the same index, otherwhise a new sequential index is used. +//! It should be noticed that parent branch therefore always have a smaller +//! index than children. + +use std::collections::{BTreeMap}; +use historical_data::tree::{BranchesStateTrait, BranchState, BranchRange}; + +#[derive(Clone, Default, Debug)] +/// State needed for queries and updates operations. +/// That is a subset of the full branch ranges set. +/// +/// Values are ordered by branch indexes so from the root +/// to the leaf of the fork tree. +/// Only a single tree branch path is present. +/// +/// This achieve the same purpose as a pointer to the full state +/// and a branch index corresponding to the leaf to query, but +/// with memory aligned state. +/// We prefer using an in memory copy of the full fork path because it +/// fits better the model where we do a big number of queries on a single +/// state (a block processing). +pub struct BranchRanges(Vec); + +impl<'a> BranchesStateTrait for &'a BranchRanges { + type Branch = &'a BranchRange; + 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 over a `BranchRanges`, it iterates over +/// every branches from the leaf to the root. +pub struct BranchRangesIter<'a>(&'a BranchRanges, usize); + +impl<'a> Iterator for BranchRangesIter<'a> { + type Item = (&'a BranchRange, u64); + + fn next(&mut self) -> Option { + if self.1 > 0 { + self.1 -= 1; + Some(( + &(self.0).0[self.1].range, + (self.0).0[self.1].branch_index, + )) + } else { + None + } + } +} + +/// Current full state for the tree(s). +/// It can contain multiple tries as it only store +/// branch elements indexed by their respective branch +/// index. +/// +/// New branches index are sequentially returned by a `last_index` +/// counter. +#[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)] + /// Access to treshold for tests only. + pub fn range_treshold(&self) -> u64 { + self.treshold + } + + /// Iterator over all its branch range sets. + /// The iterator order is reversed so it will return branches from leaf to root. + pub fn reverse_iter_ranges(&self) -> impl Iterator { + self.storage.iter().rev().map(|(k, v)| (&v.state, *k)) + } + + /// For a a given branch, returns its tree path. + /// If block number is undefined, return the path up to the latest block + /// of the last branch. + /// + /// Note that this method is a bit costy and a caching could be use, + /// but it only really need to be call once per block processing. + 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(BranchStateFull { + state, + parent_branch_index, + .. + }) = self.storage.get(&branch_index) { + let range = if state.end > previous_start { + if state.start >= previous_start { + // empty branch stop here + break; + } + BranchRange { + start: state.start, + end: previous_start, + } + } else { state.clone() }; + + previous_start = state.start; + + result.push(BranchState { + range, + branch_index, + }); + + branch_index = *parent_branch_index; + if branch_index < self.treshold { + break; + } + } else { + debug_assert!(false, "inconsistent branch range cache"); + break; + } + } + result.reverse(); + BranchRanges(result) + } + + /// Drop last block from a branch. + /// + /// Returns anchor index for this branch history: + /// - same index as input if branch is not empty + /// - parent index if branch is empty + 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 + } + } + + /// Add a new numbered block into a branch. + /// + /// Return anchor index for this branch history: + /// - same index as input if the branch was modifiable + /// - new index in case of branch range creation + 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 = BranchStateFull::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 range(&self, branch_index: u64) -> Option { + self.storage.get(&branch_index).map(|v| v.range()) + .map(|range| BranchState { + branch_index, + range, + }) + } + + /// 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. + /// + /// This is the same as `add_state` but returning the branch range for the imported + /// block and with a possible branch ranges as parameter. + /// This avoids branch ranges query when possible. + 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.range(anchor_index).expect("added just above")); + (branch_ranges, anchor_index) + } else { + let anchor_index = self.add_state(parent_branch_index, number); + ( + BranchRanges(vec![self.range(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). + /// Clear here means that values for those block gets canonicalized + /// and either written in the backend db or deleted for non canonical + /// blocks. + /// If `full` parameter is not set to true, the operation is + /// runing only a fast branch index based removal. + 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. + /// Will drop from state all branches that are not attached + /// to the canonical block in parameter. + 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: BranchStateFull = state.clone(); + child_anchor.state.start = linear_index; + let old_storage = std::mem::replace(&mut self.storage, BTreeMap::new()); + // insert anchor block + 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 a block for a branch index. + /// Returning new branch index after this removal. + 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.range().end - s.range().start) == size + } else { + false + } + } + +} + +/// Stored state 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)] +struct BranchStateFull { + state: BranchRange, + can_append: bool, + parent_branch_index: u64, +} + +impl Default for BranchStateFull { + // initialize with one element + fn default() -> Self { + Self::new(0, 0) + } +} + +impl BranchStateFull { + fn new(offset: u64, parent_branch_index: u64) -> Self { + let offset = offset as u64; + BranchStateFull { + state: BranchRange { + start: offset, + end: offset + 1, + }, + can_append: true, + parent_branch_index, + } + } + + fn range(&self) -> BranchRange { + self.state.clone() + } + + fn has_deleted_index(&self) -> bool { + !self.can_append + } + + fn add_state(&mut self) -> bool { + if !self.has_deleted_index() { + self.state.end += 1; + true + } else { + false + } + } + + fn drop_state(&mut self) -> Option { + if self.state.end - self.state.start > 0 { + self.state.end -= 1; + Some(self.state.end) + } else { + None + } + } + + 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 e561d9ce9617c..89a95759730c7 100644 --- a/core/state-db/src/lib.rs +++ b/core/state-db/src/lib.rs @@ -31,15 +31,17 @@ 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; const PRUNING_MODE: &[u8] = b"mode"; const PRUNING_MODE_ARCHIVE: &[u8] = b"archive"; @@ -49,6 +51,9 @@ const PRUNING_MODE_CONSTRAINED: &[u8] = b"constrained"; /// 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 {} @@ -70,6 +75,22 @@ pub trait NodeDb { fn get(&self, key: &Self::Key) -> Result, Self::Error>; } +/// Backend database trait. Read-only. +/// +/// All query uses a state parameter which indicates +/// where to query kv storage. +/// It any additional information that is needed to resolve +/// a chain state (depending on the implementation). +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 values at current state. + fn get_kv_pairs(&self, state: &State) -> Vec<(KvKey, DBValue)>; +} + /// Error type. pub enum Error { /// Database backend error. @@ -120,14 +141,31 @@ pub struct ChangeSet { pub deleted: Vec, } +/// A set of key values state changes. +/// +/// This assumes that we only commit block per block (otherwhise +/// we will need to include a block number value). +pub type KvChangeSet = Vec<(H, Option)>; + +/// Info for pruning key values. +/// This is a last prune index (pruning will be done up to this index), +/// and a set keys to prune. +/// Is set to none when not initialized. +pub type KvChangeSetPrune = Option<(u64, HashSet)>; + +/// Commit set on block canonicalization 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>, + /// Key values data changes. + pub kv: KvChangeSet, } /// Pruning constraints. If none are specified pruning is @@ -145,6 +183,7 @@ pub enum PruningMode { /// Maintain a pruning window. Constrained(Constraints), /// No pruning. Canonicalization is a no-op. + /// Except for kv (key value without state) storage. ArchiveAll, /// Canonicalization discards non-canonical nodes. All the canonical nodes are kept in the DB. ArchiveCanonical, @@ -234,7 +273,14 @@ 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> { let mut meta = ChangeSet::default(); if number == 0 { // Save pruning mode when writing first block. @@ -248,10 +294,14 @@ impl StateDbSync { Ok(CommitSet { data: changeset, meta: meta, + // This is only the canonical changeset: archive + // all mode does not keep trace of kv storage for + // fork. This is a big limitation. + kv: kv_changeset, }) }, PruningMode::Constrained(_) | PruningMode::ArchiveCanonical => { - let commit = self.non_canonical.insert(hash, number, parent_hash, changeset); + let commit = self.non_canonical.insert(hash, number, parent_hash, changeset, kv_changeset); commit.map(|mut c| { c.meta.inserted.extend(meta.inserted); c @@ -260,24 +310,28 @@ impl StateDbSync { } } - pub fn canonicalize_block(&mut self, hash: &BlockHash) -> Result, Error> { - let mut commit = CommitSet::default(); + pub fn canonicalize_block( + &mut self, + hash: &BlockHash, + ) -> Result<(CommitSetCanonical, u64), Error> { + let mut commit = (CommitSet::default(), None); if self.mode == PruningMode::ArchiveAll { - return Ok(commit) + return Ok((commit, 0)) } - match self.non_canonical.canonicalize(&hash, &mut commit) { - Ok(()) => { + let block_number = match self.non_canonical.canonicalize(&hash, &mut commit.0) { + Ok(block_number) => { if self.mode == PruningMode::ArchiveCanonical { - commit.data.deleted.clear(); + commit.0.data.deleted.clear(); } - } + block_number + }, 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) + Ok((commit, block_number)) } pub fn best_canonical(&self) -> Option { @@ -297,7 +351,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 { @@ -331,6 +385,12 @@ impl StateDbSync { } } + /// For a a given block return its path in the block tree. + /// Using a block hash and its number. + 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(()), @@ -377,6 +437,44 @@ impl StateDbSync { db.get(key.as_ref()).map_err(|e| Error::Db(e)) } + /// Get a value from non-canonical/pruning overlay or the backing DB. + /// + /// State is both a branch ranges for non canonical storage + /// and a block number for cannonical storage. + 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)) + } + + /// Access current full state for both backend and non cannoical. + /// Very inefficient and costly. + 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 { @@ -414,15 +512,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<(CommitSetCanonical, u64), 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) @@ -440,6 +554,29 @@ impl StateDb { self.db.read().get(key, db) } + /// Get a value from non-canonical/pruning overlay or the backing DB. + /// + /// State is both a branch ranges for non canonical storage + /// and a block number for cannonical storage. + pub fn get_kv>( + &self, + key: &[u8], + state: &(BranchRanges, u64), + db: &D, + ) -> Result, Error> { + self.db.read().get_kv(key, state, db) + } + + /// Access current full state for both backend and non cannoical. + /// Very inefficient and costly. + 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. @@ -473,10 +610,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( @@ -486,6 +624,7 @@ mod tests { 1, &H256::from_low_u64_be(0), make_changeset(&[1], &[91]), + make_kv_changeset(&[1], &[81]), ) .unwrap(), ); @@ -496,6 +635,7 @@ mod tests { 2, &H256::from_low_u64_be(1), make_changeset(&[21], &[921, 1]), + make_kv_changeset(&[21], &[821, 1]), ) .unwrap(), ); @@ -506,6 +646,7 @@ mod tests { 2, &H256::from_low_u64_be(1), make_changeset(&[22], &[922]), + make_kv_changeset(&[22], &[822]), ) .unwrap(), ); @@ -516,11 +657,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().0 + ); state_db.apply_pending(); db.commit( &state_db @@ -529,13 +674,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().0 + ); 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().0 + ); state_db.apply_pending(); (db, state_db) @@ -545,6 +697,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)); } @@ -552,6 +708,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] @@ -561,6 +722,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] @@ -574,6 +740,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] @@ -587,6 +758,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(&[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])); } #[test] @@ -600,6 +776,7 @@ mod tests { 0, &H256::from_low_u64_be(0), make_changeset(&[], &[]), + make_kv_changeset(&[], &[]), ) .unwrap(), ); diff --git a/core/state-db/src/noncanonical.rs b/core/state-db/src/noncanonical.rs index 58715715ccdd2..c5b1ed13f8f34 100644 --- a/core/state-db/src/noncanonical.rs +++ b/core/state-db/src/noncanonical.rs @@ -21,23 +21,104 @@ //! `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 historical_data::tree::History; 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 key value storage garbage collection. + pinned: HashMap, GcIndex)>, + kv_gc: KvPendingGC, +} + +#[derive(Default)] +/// Key value storage garbage collection happen only when all pinned threads +/// that were 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 garbage collection query. + pending_gc_query: Option, + /// Keys to garbage collect. + keys_pending_gc: HashSet, + /// Store keys which are waiting for a gc but for a next call. + /// We use a secondary storage to avoid having threads blocking gc forever. + next_keys_pending_gc: HashSet, +} + +impl KvPendingGC { + fn set_pending_gc(&mut self) { + self.gc_last_index += 1; + if self.pending_gc_query.is_none() { + self.keys_pending_gc.extend(self.next_keys_pending_gc.drain()); + self.pending_gc_query = Some(self.gc_last_index - 1); + } + } + + fn try_gc( + &mut self, + kv_values: &mut HashMap>>, + branches: &RangeSet, + pinned: &HashMap, + ) { + if let Some(pending) = self.pending_gc_query { + if pending < self.min_pinned_index(pinned) { + for key in self.keys_pending_gc.drain() { + match kv_values.get_mut(&key).map(|historical_value| { + historical_value.gc(branches.reverse_iter_ranges()) + }) { + Some(historical_data::PruneResult::Cleared) => { + let _ = kv_values.remove(&key); + }, + _ => (), + } + } + + self.pending_gc_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,19 +129,35 @@ struct JournalRecord { deleted: Vec, } -fn to_journal_key(block: u64, index: u64) -> Vec { +#[derive(Encode, Decode)] +struct KvJournalRecord { + inserted: Vec<(KvKey, DBValue)>, + 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)>) { +fn insert_values( + values: &mut HashMap, + inserted: Vec<(Key, DBValue)>, +) { for (k, v) in inserted { debug_assert!(values.get(&k).map_or(true, |(_, value)| *value == v)); let (ref mut counter, _) = values.entry(k).or_insert_with(|| (0, v)); @@ -68,6 +165,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 +205,44 @@ fn discard_values( } } +fn discard_kv_values( + inserted: Vec, + deleted: Vec, + into: &mut KvPendingGC, +) { + let into = if into.pending_gc_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 +250,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 +260,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 +278,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 +355,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 +407,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 +451,28 @@ 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, + }; + 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 +486,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 +498,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), @@ -284,12 +523,13 @@ impl NonCanonicalOverlay { } /// Select a top-level root and canonicalized it. Discards all sibling subtrees and the root. - /// Returns a set of changes that need to be added to the DB. + /// Complete a set of changes that need to be added to the DB, and return corresponding block + /// number. pub fn canonicalize( &mut self, hash: &BlockHash, commit: &mut CommitSet, - ) -> Result<(), Error> { + ) -> Result> { trace!(target: "state-db", "Canonicalizing {:?}", hash); let level = self.levels.get(self.pending_canonicalizations.len()).ok_or_else(|| Error::InvalidBlock)?; let index = level @@ -309,6 +549,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,17 +558,45 @@ 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 historical 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 historical 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()); - Ok(()) + Ok(block_number) } 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 +616,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(); + // 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 +653,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 +680,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 +728,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 +834,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 +851,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 +868,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 +885,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 +897,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 +930,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 +955,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 +984,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 +1014,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 +1023,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 +1031,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 +1054,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 +1103,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 +1147,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 +1161,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 +1182,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 +1200,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 +1209,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 +1244,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 +1282,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..7d0734948c0ea 100644 --- a/core/state-db/src/pruning.rs +++ b/core/state-db/src/pruning.rs @@ -24,11 +24,13 @@ 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}; 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 +52,10 @@ pub struct RefWindow { struct DeathRow { hash: BlockHash, journal_key: Vec, + kv_journal_key: Vec, deleted: HashSet, + // pruning journal on actual prune). + kv_modified: HashSet, } #[derive(Encode, Decode)] @@ -60,10 +65,19 @@ struct JournalRecord { deleted: Vec, } +#[derive(Encode, Decode)] +struct KvJournalRecord { + modified: 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 +98,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_modified = 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_modified.len(), + record.deleted.len(), + ); + pruning.import( + &record.hash, + journal_key, + kv_journal_key, + record.inserted.into_iter(), + record.deleted, + kv_record_modified.into_iter(), + ); }, None => break, } @@ -97,7 +132,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 +153,14 @@ impl RefWindow { for k in deleted.iter() { self.death_index.insert(k.clone(), imported_block); } + self.death_rows.push_back( DeathRow { hash: hash.clone(), deleted: deleted.into_iter().collect(), - journal_key: journal_key, + kv_modified: kv_modified.into_iter().collect(), + journal_key, + kv_journal_key, } ); } @@ -139,14 +185,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 +219,29 @@ 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, + }; 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 +277,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 +294,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 +301,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 +339,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 +474,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..6c142c82daf2e 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 historical_data::tree::Serialized; +use historical_data::PruneResult; +use historical_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,26 @@ impl MetaDb for TestDb { } } +impl KvDb for TestDb { + type Error = (); + + fn get_kv(&self, key: &[u8], state: &u64) -> Result, ()> { + Ok(self.kv.get(key) + .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)| ( + s.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 +77,65 @@ 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 ser = self.kv.entry(k.clone()) + .or_insert_with(|| Ser::default()); + 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(|ser| { + 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) + }) + .collect(); + } } pub fn make_changeset(inserted: &[u64], deleted: &[u64]) -> ChangeSet { @@ -73,10 +150,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 +199,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 5fbda1aa32f45..e63137d36c4dc 100644 --- a/core/state-machine/src/backend.rs +++ b/core/state-machine/src/backend.rs @@ -19,8 +19,9 @@ use std::{error, fmt, cmp::Ord, collections::HashMap, marker::PhantomData}; use log::warn; use hash_db::Hasher; -use crate::trie_backend::TrieBackend; +use crate::state_backend::StateBackend; 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}, @@ -37,12 +38,18 @@ pub trait Backend: std::fmt::Debug { /// Storage changes to be applied if committing type Transaction: Consolidate + Default; - /// Type of trie backend storage. + /// Type of key value 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))) @@ -100,9 +107,24 @@ pub trait Backend: std::fmt::Debug { 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, and pending deletion + /// if allowed. + fn kv_in_memory(&self) -> crate::InMemoryKvBackend; + /// Get all keys with given prefix fn keys(&self, prefix: &[u8]) -> Vec> { let mut all = Vec::new(); @@ -118,25 +140,30 @@ pub trait Backend: std::fmt::Debug { } /// Try convert into trie backend. - fn as_trie_backend(&mut self) -> Option<&TrieBackend> { + fn as_state_backend(&mut self) -> Option< + &StateBackend + > { 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, + ) -> (H::Out, Self::Transaction) where I1: IntoIterator, Option>)>, I2i: IntoIterator, Option>)>, I2: IntoIterator, I2i)>, + I3: IntoIterator, Option>)>, ::Out: Ord, { - let mut txs: Self::Transaction = Default::default(); + let mut txs: Self::Transaction = self.kv_transaction(kv_deltas); + let mut child_roots: Vec<_> = Default::default(); // child first for (storage_key, child_delta) in child_deltas { @@ -161,6 +188,7 @@ 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 +198,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) } @@ -198,10 +230,29 @@ impl<'a, T: Backend, H: Hasher> Backend for &'a T { (*self).child_storage_root(storage_key, 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_in_memory(&self) -> crate::InMemoryKvBackend { + (*self).kv_in_memory() + } + fn for_key_values_with_prefix(&self, prefix: &[u8], f: F) { (*self).for_key_values_with_prefix(prefix, f); } @@ -219,12 +270,32 @@ 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) @@ -249,23 +320,26 @@ impl error::Error for Void { /// In-memory backend. Fully recomputes tries each time `as_trie_backend` is called but useful for /// tests and proof checking. pub struct InMemory { - inner: HashMap>, HashMap, Vec>>, - // This field is only needed for returning reference in `as_trie_backend`. - trie: Option, H>>, + inner_trie: HashMap>, HashMap, Vec>>, + inner_kv: Option, + // This field is only needed for returning reference in `as_state_backend`. + as_state: Option, H, InMemoryKvBackend>>, _hasher: PhantomData, } impl std::fmt::Debug for InMemory { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "InMemory ({} values)", self.inner.len()) + write!(f, "InMemory ({} trie values, {} key values)", + self.inner_trie.len(), self.inner_kv.as_ref().map(InMemoryKvBackend::len).unwrap_or(0)) } } impl Default for InMemory { fn default() -> Self { InMemory { - inner: Default::default(), - trie: None, + inner_trie: Default::default(), + as_state: None, + inner_kv: Some(Default::default()), _hasher: PhantomData, } } @@ -274,8 +348,9 @@ impl Default for InMemory { impl Clone for InMemory { fn clone(&self) -> Self { InMemory { - inner: self.inner.clone(), - trie: None, + inner_trie: self.inner_trie.clone(), + as_state: None, + inner_kv: self.inner_kv.clone(), _hasher: PhantomData, } } @@ -283,30 +358,67 @@ impl Clone for InMemory { impl PartialEq for InMemory { fn eq(&self, other: &Self) -> bool { - self.inner.eq(&other.inner) + self.inner_trie.eq(&other.inner_trie) + && self.kv().eq(other.kv()) } } impl InMemory { + /// Key value inmemory storage is either used from the + /// state db or its own field. + /// This function access the right field. + fn kv(&self) -> &InMemoryKvBackend { + if let Some(kv) = self.inner_kv.as_ref() { + kv + } else { + self.as_state.as_ref().unwrap().kv_backend() + } + } + + /// Access the current key value db in a mutable way. + fn kv_mut(&mut self) -> &mut InMemoryKvBackend { + if let Some(kv) = self.inner_kv.as_mut() { + kv + } else { + self.as_state.as_mut().unwrap().kv_backend_mut() + } + } + + /// Extract key values from `InMemory`, by emptying it. + fn extract_kv(&mut self) -> InMemoryKvBackend { + if let Some(kv) = self.inner_kv.take() { + kv + } else { + std::mem::replace( + self.as_state.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 inner_trie: HashMap<_, _> = self.inner_trie.clone(); + 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); }, + Some(v) => { inner_trie.entry(storage_key).or_default().insert(key, v); }, + None => { inner_trie.entry(storage_key).or_default().remove(&key); }, } } + kv.extend(changes.kv); - inner.into() + let inner_kv = Some(kv); + InMemory { inner_trie, inner_kv, as_state: None, _hasher: PhantomData } } } impl From>, HashMap, Vec>>> for InMemory { - fn from(inner: HashMap>, HashMap, Vec>>) -> Self { + fn from(inner_trie: HashMap>, HashMap, Vec>>) -> Self { InMemory { - inner: inner, - trie: None, + inner_trie, + as_state: None, + inner_kv: Some(Default::default()), _hasher: PhantomData, } } @@ -316,37 +428,39 @@ impl From<( HashMap, Vec>, HashMap, HashMap, Vec>>, )> for InMemory { - fn from(inners: ( + fn from(inner_tries: ( 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); + let mut inner_trie: HashMap>, HashMap, Vec>> + = inner_tries.1.into_iter().map(|(k, v)| (Some(k), v)).collect(); + inner_trie.insert(None, inner_tries.0); InMemory { - inner: inner, - trie: None, + inner_trie, + as_state: None, + inner_kv: Some(Default::default()), _hasher: PhantomData, } } } impl From, Vec>> for InMemory { - fn from(inner: HashMap, Vec>) -> Self { + fn from(inner_trie: HashMap, Vec>) -> Self { let mut expanded = HashMap::new(); - expanded.insert(None, inner); + expanded.insert(None, inner_trie); InMemory { - inner: expanded, - trie: None, + inner_trie: expanded, + as_state: None, + inner_kv: Some(Default::default()), _hasher: PhantomData, } } } impl From>, Vec, Option>)>> for InMemory { - fn from(inner: Vec<(Option>, Vec, Option>)>) -> Self { + fn from(inner_trie: Vec<(Option>, Vec, Option>)>) -> Self { let mut expanded: HashMap>, HashMap, Vec>> = HashMap::new(); - for (child_key, key, value) in inner { + for (child_key, key, value) in inner_trie { if let Some(value) = value { expanded.entry(child_key).or_default().insert(key, value); } @@ -355,45 +469,78 @@ impl From>, Vec, Option>)>> for InMem } } +impl From for InMemory { + fn from(inner_trie: InMemoryTransaction) -> Self { + let mut expanded: HashMap>, HashMap, Vec>> = HashMap::new(); + for (child_key, key, value) in inner_trie.storage { + if let Some(value) = value { + expanded.entry(child_key).or_default().insert(key, value); + } + } + let mut result: InMemory = expanded.into(); + *result.kv_mut() = inner_trie.kv; + result + } +} + impl InMemory { /// child storage key iterator pub fn child_storage_keys(&self) -> impl Iterator { - self.inner.iter().filter_map(|item| item.0.as_ref().map(|v|&v[..])) + self.inner_trie.iter().filter_map(|item| item.0.as_ref().map(|v|&v[..])) } } +#[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))) + Ok(self.inner_trie.get(&None).and_then(|map| map.get(key).map(Clone::clone))) } fn child_storage(&self, storage_key: &[u8], key: &[u8]) -> Result>, Self::Error> { - Ok(self.inner.get(&Some(storage_key.to_vec())).and_then(|map| map.get(key).map(Clone::clone))) + Ok(self.inner_trie.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)) + Ok(self.inner_trie.get(&None).map(|map| map.get(key).is_some()).unwrap_or(false)) } fn for_keys_with_prefix(&self, prefix: &[u8], f: F) { - self.inner.get(&None).map(|map| map.keys().filter(|key| key.starts_with(prefix)).map(|k| &**k).for_each(f)); + self.inner_trie.get(&None).map(|map| map.keys().filter(|key| key.starts_with(prefix)).map(|k| &**k).for_each(f)); } fn for_key_values_with_prefix(&self, prefix: &[u8], mut f: F) { - self.inner.get(&None).map(|map| map.iter().filter(|(key, _val)| key.starts_with(prefix)) + self.inner_trie.get(&None).map(|map| map.iter().filter(|(key, _val)| key.starts_with(prefix)) .for_each(|(k, v)| f(k, v))); } fn for_keys_in_child_storage(&self, storage_key: &[u8], mut f: F) { - self.inner.get(&Some(storage_key.to_vec())).map(|map| map.keys().for_each(|k| f(&k))); + self.inner_trie.get(&Some(storage_key.to_vec())).map(|map| map.keys().for_each(|k| f(&k))); } fn for_child_keys_with_prefix(&self, storage_key: &[u8], prefix: &[u8], f: F) { - self.inner.get(&Some(storage_key.to_vec())) + self.inner_trie.get(&Some(storage_key.to_vec())) .map(|map| map.keys().filter(|key| key.starts_with(prefix)).map(|k| &**k).for_each(f)); } @@ -402,7 +549,7 @@ impl Backend for InMemory { I: IntoIterator, Option>)>, ::Out: Ord, { - let existing_pairs = self.inner.get(&None) + let existing_pairs = self.inner_trie.get(&None) .into_iter() .flat_map(|map| map.iter().map(|(k, v)| (k.clone(), Some(v.clone())))); @@ -415,7 +562,7 @@ 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) @@ -425,7 +572,7 @@ impl Backend for InMemory { { let storage_key = storage_key.to_vec(); - let existing_pairs = self.inner.get(&Some(storage_key.clone())) + let existing_pairs = self.inner_trie.get(&Some(storage_key.clone())) .into_iter() .flat_map(|map| map.iter().map(|(k, v)| (k.clone(), Some(v.clone())))); @@ -442,35 +589,65 @@ impl Backend for InMemory { let is_default = root == default_child_trie_root::>(&storage_key); - (root, is_default, full_transaction) + ( + root, + is_default, + InMemoryTransaction { storage: full_transaction, kv: Default::default() }, + ) + } + + 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)> { - self.inner.get(&None) + self.inner_trie.get(&None) .into_iter() .flat_map(|map| map.iter().map(|(k, v)| (k.clone(), v.clone()))) .collect() } + fn children_storage_keys(&self) -> Vec> { + self.inner_trie.iter().filter_map(|(child, _)| child.clone()).collect() + } + + fn child_pairs(&self, storage_key: &[u8]) -> Vec<(Vec, Vec)> { + self.inner_trie.get(&Some(storage_key.to_vec())) + .into_iter() + .flat_map(|map| map.iter().map(|(k, v)| (k.clone(), v.clone()))) + .collect() + } + + fn kv_in_memory(&self) -> crate::InMemoryKvBackend { + self.kv().clone() + } + fn keys(&self, prefix: &[u8]) -> Vec> { - self.inner.get(&None) + self.inner_trie.get(&None) .into_iter() .flat_map(|map| map.keys().filter(|k| k.starts_with(prefix)).cloned()) .collect() } fn child_keys(&self, storage_key: &[u8], prefix: &[u8]) -> Vec> { - self.inner.get(&Some(storage_key.to_vec())) + self.inner_trie.get(&Some(storage_key.to_vec())) .into_iter() .flat_map(|map| map.keys().filter(|k| k.starts_with(prefix)).cloned()) .collect() } - fn as_trie_backend(&mut self)-> Option<&TrieBackend> { + fn as_state_backend(&mut self)-> Option< + &StateBackend + > { let mut mdb = MemoryDB::default(); let mut new_child_roots = Vec::new(); let mut root_map = None; - for (storage_key, map) in &self.inner { + for (storage_key, map) in &self.inner_trie { if let Some(storage_key) = storage_key.as_ref() { let ch = insert_into_memory_db::(&mut mdb, map.clone().into_iter())?; new_child_roots.push((storage_key.clone(), ch.as_ref().into())); @@ -488,8 +665,8 @@ impl Backend for InMemory { new_child_roots.into_iter(), )?, }; - self.trie = Some(TrieBackend::new(mdb, root)); - self.trie.as_ref() + self.as_state = Some(StateBackend::new(mdb, root, self.extract_kv())); + self.as_state.as_ref() } } @@ -521,10 +698,11 @@ mod tests { #[test] fn in_memory_with_child_trie_only() { let storage = InMemory::::default(); - let mut storage = storage.update( - vec![(Some(b"1".to_vec()), b"2".to_vec(), Some(b"3".to_vec()))] - ); - let trie_backend = storage.as_trie_backend().unwrap(); + let mut storage = storage.update(InMemoryTransaction { + storage: vec![(Some(b"1".to_vec()), b"2".to_vec(), Some(b"3".to_vec()))], + kv: Default::default(), + }); + let trie_backend = storage.as_state_backend().unwrap(); assert_eq!(trie_backend.child_storage(b"1", b"2").unwrap(), Some(b"3".to_vec())); assert!(trie_backend.storage(b"1").unwrap().is_some()); } 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 0e93302a95a54..45cc4423de752 100644 --- a/core/state-machine/src/ext.rs +++ b/core/state-machine/src/ext.rs @@ -441,7 +441,9 @@ 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); self.storage_transaction = Some((transaction, root)); trace!(target: "state-trace", "{:04x}: Root {}", self.id, diff --git a/core/state-machine/src/kv_backend.rs b/core/state-machine/src/kv_backend.rs new file mode 100644 index 0000000000000..c1a1a4d3044ce --- /dev/null +++ b/core/state-machine/src/kv_backend.rs @@ -0,0 +1,62 @@ +// 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. + /// When use for a storage that implements this trait + /// It should return pending deletion as a `None` value. + /// This contracdicts a bit the backend aspect of this + /// trait but is practical in some cases. + fn in_memory(&self) -> InMemory; +} + +/// In memory storage of content. It is a storage so it +/// can contains deletion of value as a `None` variant. +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 in_memory(&self) -> InMemory { + self.clone() + } +} + +impl KvBackend for Arc { + fn get(&self, key: &[u8]) -> Result>, String> { + KvBackend::get(self.deref(), key) + } + + fn in_memory(&self) -> InMemory { + KvBackend::in_memory(self.deref()) + } +} diff --git a/core/state-machine/src/lib.rs b/core/state-machine/src/lib.rs index 1da9cfb4e7dbe..b44b1ec71cce3 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; +mod kv_backend; +mod state_backend; pub use trie::{trie_types::{Layout, TrieDBMut}, TrieMut, DBValue, MemoryDB}; pub use testing::TestExternalities; @@ -64,6 +68,7 @@ pub use proving_backend::{ }; pub use trie_backend_essence::{TrieBackendStorage, Storage}; pub use trie_backend::TrieBackend; +pub use state_backend::StateBackend; pub use error::{Error, ExecutionError}; type CallResult = Result, E>; @@ -469,7 +474,7 @@ where H: Hasher, Exec: CodeExecutor, { - let trie_backend = backend.as_trie_backend() + let trie_backend = backend.as_state_backend() .ok_or_else(|| Box::new(ExecutionError::UnableToGenerateProof) as Box)?; prove_execution_on_trie_backend(trie_backend, overlay, exec, method, call_data, keystore) } @@ -483,8 +488,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: &StateBackend, overlay: &mut OverlayedChanges, exec: &Exec, method: &str, @@ -495,6 +500,8 @@ where S: trie_backend_essence::TrieBackendStorage, H: Hasher, Exec: CodeExecutor, + H::Out: Ord + 'static, + K: KvBackend, { let proving_backend = proving_backend::ProvingBackend::new(trie_backend); let mut sm = StateMachine::<_, H, _, InMemoryChangesTrieStorage, Exec>::new( @@ -531,7 +538,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: &StateBackend, H, InMemoryKvBackend>, overlay: &mut OverlayedChanges, exec: &Exec, method: &str, @@ -565,7 +572,7 @@ where I: IntoIterator, I::Item: AsRef<[u8]>, { - let trie_backend = backend.as_trie_backend() + let trie_backend = backend.as_state_backend() .ok_or_else( || Box::new(ExecutionError::UnableToGenerateProof) as Box )?; @@ -585,24 +592,25 @@ where I: IntoIterator, I::Item: AsRef<[u8]>, { - let trie_backend = backend.as_trie_backend() + let trie_backend = backend.as_state_backend() .ok_or_else(|| Box::new(ExecutionError::UnableToGenerateProof) as Box)?; prove_child_read_on_trie_backend(trie_backend, storage_key, keys) } /// 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: &StateBackend, keys: I, ) -> Result> where S: trie_backend_essence::TrieBackendStorage, H: Hasher, H::Out: Ord, + K: 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 +620,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: &StateBackend, storage_key: &[u8], keys: I, ) -> Result> @@ -621,10 +629,11 @@ where S: trie_backend_essence::TrieBackendStorage, H: Hasher, H::Out: Ord, + K: 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 +691,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: &StateBackend, H, InMemoryKvBackend>, key: &[u8], ) -> Result>, Box> where @@ -694,7 +703,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: &StateBackend, H, InMemoryKvBackend>, storage_key: &[u8], key: &[u8], ) -> Result>, Box> @@ -813,7 +822,7 @@ mod tests { #[test] fn execute_works() { - let backend = trie_backend::tests::test_trie(); + let backend = state_backend::tests::test_state(); let mut overlayed_changes = Default::default(); let changes_trie_storage = InMemoryChangesTrieStorage::::new(); @@ -842,7 +851,7 @@ mod tests { #[test] fn execute_works_with_native_else_wasm() { - let backend = trie_backend::tests::test_trie(); + let backend = state_backend::tests::test_state(); let mut overlayed_changes = Default::default(); let changes_trie_storage = InMemoryChangesTrieStorage::::new(); @@ -868,7 +877,7 @@ mod tests { #[test] fn dual_execution_strategy_detects_consensus_failure() { let mut consensus_failed = false; - let backend = trie_backend::tests::test_trie(); + let backend = state_backend::tests::test_state(); let mut overlayed_changes = Default::default(); let changes_trie_storage = InMemoryChangesTrieStorage::::new(); @@ -911,7 +920,7 @@ mod tests { }; // fetch execution proof from 'remote' full node - let remote_backend = trie_backend::tests::test_trie(); + let remote_backend = state_backend::tests::test_state(); let remote_root = remote_backend.storage_root(std::iter::empty()).0; let (remote_result, remote_proof) = prove_execution( remote_backend, @@ -947,7 +956,7 @@ mod tests { b"bbb".to_vec() => b"3".to_vec() ]; let mut state = InMemory::::from(initial); - let backend = state.as_trie_backend().unwrap(); + let backend = state.as_state_backend().unwrap(); let mut overlay = OverlayedChanges { committed: map![ b"aba".to_vec() => OverlayedValue::from(Some(b"1312".to_vec())), @@ -989,7 +998,7 @@ mod tests { #[test] fn set_child_storage_works() { let mut state = InMemory::::default(); - let backend = state.as_trie_backend().unwrap(); + let backend = state.as_state_backend().unwrap(); let changes_trie_storage = InMemoryChangesTrieStorage::::new(); let mut overlay = OverlayedChanges::default(); let mut ext = Ext::new( @@ -1026,7 +1035,7 @@ mod tests { #[test] fn prove_read_and_proof_check_works() { // fetch read proof from 'remote' full node - let remote_backend = trie_backend::tests::test_trie(); + let remote_backend = state_backend::tests::test_state(); let remote_root = remote_backend.storage_root(::std::iter::empty()).0; let remote_proof = prove_read(remote_backend, &[b"value2"]).unwrap(); // check proof locally @@ -1047,7 +1056,7 @@ mod tests { ); assert_eq!(local_result2, false); // on child trie - let remote_backend = trie_backend::tests::test_trie(); + let remote_backend = state_backend::tests::test_state(); let remote_root = remote_backend.storage_root(::std::iter::empty()).0; let remote_proof = prove_child_read( remote_backend, @@ -1078,7 +1087,7 @@ mod tests { #[test] fn cannot_change_changes_trie_config() { - let backend = trie_backend::tests::test_trie(); + let backend = state_backend::tests::test_state(); let mut overlayed_changes = Default::default(); let changes_trie_storage = InMemoryChangesTrieStorage::::new(); @@ -1103,7 +1112,7 @@ mod tests { #[test] fn cannot_change_changes_trie_config_with_native_else_wasm() { - let backend = trie_backend::tests::test_trie(); + let backend = state_backend::tests::test_state(); let mut overlayed_changes = Default::default(); let changes_trie_storage = InMemoryChangesTrieStorage::::new(); diff --git a/core/state-machine/src/overlayed_changes.rs b/core/state-machine/src/overlayed_changes.rs index 53a66dc49ee05..453442ee80e1b 100644 --- a/core/state-machine/src/overlayed_changes.rs +++ b/core/state-machine/src/overlayed_changes.rs @@ -57,6 +57,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 +67,7 @@ impl FromIterator<(Vec, OverlayedValue)> for OverlayedChangeSet { Self { top: iter.into_iter().collect(), children: Default::default(), + kv: Default::default(), } } } @@ -72,13 +75,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 +136,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 +174,14 @@ impl OverlayedChanges { } } + #[cfg(test)] + /// 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() { @@ -307,11 +331,13 @@ impl OverlayedChanges { 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 +396,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 14f17a3a48c47..dfbb4700da03e 100644 --- a/core/state-machine/src/proving_backend.rs +++ b/core/state-machine/src/proving_backend.rs @@ -24,11 +24,13 @@ 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::state_backend::StateBackend; use crate::trie_backend_essence::{Ephemeral, TrieBackendEssence, TrieBackendStorage}; use crate::{Error, ExecutionError, Backend}; +use crate::kv_backend::{KvBackend, InMemory as InMemoryKvBackend}; /// A proof that some set of key-value pairs are included in the storage trie. The proof contains /// the storage values so that the partial storage backend can be reconstructed by a verifier that @@ -180,14 +182,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, + K: 'a + KvBackend, +> { + backend: &'a StateBackend, proof_recorder: Rc>>, } -impl<'a, S: 'a + TrieBackendStorage, H: 'a + Hasher> ProvingBackend<'a, S, H> { +impl<'a, + S: 'a + TrieBackendStorage, + H: 'a + Hasher, + K: 'a + KvBackend, +> ProvingBackend<'a, S, H, K> { /// Create new proving backend. - pub fn new(backend: &'a TrieBackend) -> Self { + pub fn new(backend: &'a StateBackend) -> Self { ProvingBackend { backend, proof_recorder: Rc::new(RefCell::new(Recorder::new())), @@ -196,7 +206,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 StateBackend, proof_recorder: Rc>>, ) -> Self { ProvingBackend { @@ -217,25 +227,31 @@ 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, + K: 'a + KvBackend, +> std::fmt::Debug for ProvingBackend<'a, S, H, K> { 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, K> Backend for ProvingBackend<'a, S, H, K> where S: 'a + TrieBackendStorage, H: 'a + Hasher, H::Out: Ord, + K: 'a + KvBackend, { type Error = String; - type Transaction = S::Overlay; + type Transaction = (S::Overlay, HashMap, Option>>); type TrieBackendStorage = PrefixedMemoryDB; + type KvBackend = K; fn storage(&self, key: &[u8]) -> Result>, Self::Error> { ProvingBackendEssence { - backend: self.backend.essence(), + backend: self.backend.trie().essence(), proof_recorder: &mut *self.proof_recorder.try_borrow_mut() .expect("only fails when already borrowed; storage() is non-reentrant; qed"), }.storage(key) @@ -243,12 +259,16 @@ impl<'a, S, H> Backend for ProvingBackend<'a, S, H> fn child_storage(&self, storage_key: &[u8], key: &[u8]) -> Result>, Self::Error> { ProvingBackendEssence { - backend: self.backend.essence(), + backend: self.backend.trie().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) } + 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) { self.backend.for_keys_in_child_storage(storage_key, f) } @@ -269,6 +289,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_in_memory(&self) -> HashMap, Option>> { + self.backend.kv_in_memory() + } + fn keys(&self, prefix: &[u8]) -> Vec> { self.backend.keys(prefix) } @@ -290,20 +322,31 @@ impl<'a, S, H> Backend for ProvingBackend<'a, S, H> { self.backend.child_storage_root(storage_key, 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: StorageProof, -) -> 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(StateBackend::new(db, root, kv)) } else { Err(Box::new(ExecutionError::InvalidProof)) } @@ -325,27 +368,29 @@ where #[cfg(test)] mod tests { - use crate::backend::{InMemory}; - use crate::trie_backend::tests::test_trie; + use crate::backend::{InMemory, InMemoryTransaction}; + use crate::state_backend::tests::test_state; use super::*; use primitives::{Blake2Hasher, storage::ChildStorageKey}; + type KvBackend = InMemoryKvBackend; + fn test_proving<'a>( - trie_backend: &'a TrieBackend,Blake2Hasher>, - ) -> ProvingBackend<'a, PrefixedMemoryDB, Blake2Hasher> { - ProvingBackend::new(trie_backend) + state_backend: &'a StateBackend, Blake2Hasher, KvBackend>, + ) -> ProvingBackend<'a, PrefixedMemoryDB, Blake2Hasher, KvBackend> { + ProvingBackend::new(state_backend) } #[test] fn proof_is_empty_until_value_is_read() { - let trie_backend = test_trie(); - assert!(test_proving(&trie_backend).extract_proof().is_empty()); + let state_backend = test_state(); + assert!(test_proving(&state_backend).extract_proof().is_empty()); } #[test] fn proof_is_non_empty_after_value_is_read() { - let trie_backend = test_trie(); - let backend = test_proving(&trie_backend); + let state_backend = test_state(); + let backend = test_proving(&state_backend); assert_eq!(backend.storage(b"key").unwrap(), Some(b"value".to_vec())); assert!(!backend.extract_proof().is_empty()); } @@ -362,26 +407,31 @@ mod tests { #[test] fn passes_throgh_backend_calls() { - let trie_backend = test_trie(); - let proving_backend = test_proving(&trie_backend); - assert_eq!(trie_backend.storage(b"key").unwrap(), proving_backend.storage(b"key").unwrap()); - assert_eq!(trie_backend.pairs(), proving_backend.pairs()); + let state_backend = test_state(); + let proving_backend = test_proving(&state_backend); + assert_eq!(state_backend.storage(b"key").unwrap(), proving_backend.storage(b"key").unwrap()); + assert_eq!(state_backend.pairs(), proving_backend.pairs()); - let (trie_root, mut trie_mdb) = trie_backend.storage_root(::std::iter::empty()); + let (trie_root, mut trie_mdb) = state_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])); - let trie = in_memory.as_trie_backend().unwrap(); + let trie = in_memory.as_state_backend().unwrap(); let trie_root = trie.storage_root(::std::iter::empty()).0; assert_eq!(in_memory_root, trie_root); (0..64).for_each(|i| assert_eq!(trie.storage(&[i]).unwrap().unwrap(), vec![i])); @@ -406,10 +456,14 @@ 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; (0..64).for_each(|i| assert_eq!( in_memory.storage(&[i]).unwrap().unwrap(), @@ -424,7 +478,7 @@ mod tests { vec![i] )); - let trie = in_memory.as_trie_backend().unwrap(); + let trie = in_memory.as_state_backend().unwrap(); let trie_root = trie.storage_root(::std::iter::empty()).0; assert_eq!(in_memory_root, trie_root); (0..64).for_each(|i| assert_eq!( diff --git a/core/state-machine/src/state_backend.rs b/core/state-machine/src/state_backend.rs new file mode 100644 index 0000000000000..da66432e653cd --- /dev/null +++ b/core/state-machine/src/state_backend.rs @@ -0,0 +1,248 @@ +// 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 . + +//! State machine backend build from a trie and an auxilliary +//! key value backend. + + +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 crate::trie_backend::TrieBackend; +use crate::trie_backend_essence::{TrieBackendStorage, Ephemeral}; +use crate::Backend; +use crate::kv_backend::KvBackend; +use std::collections::HashMap; + +/// Patricia trie-based backend. Transaction type is an overlay of changes to commit. +/// A simple key value backend is also accessible for direct key value storage without +/// proof. +pub struct StateBackend, H: Hasher, K: KvBackend> { + trie: TrieBackend, + kv: K, +} + +impl, K: KvBackend, H: Hasher> StateBackend { + /// Create new state backend. + pub fn new(storage: S, root: H::Out, kv: K) -> Self { + StateBackend { + trie: TrieBackend::new(storage, root), + kv, + } + } + + /// Get backend essence reference. + pub fn trie(&self) -> &TrieBackend { + &self.trie + } + + /// Get backend storage reference. + pub fn backend_storage(&self) -> &S { + self.trie.backend_storage() + } + + /// Get key value storage backend reference. + pub fn kv_backend(&self) -> &K { + &self.kv + } + + /// Get key value storage backend mutable reference. + pub fn kv_backend_mut(&mut self) -> &mut K { + &mut self.kv + } + + /// Consumes self and returns underlying storage. + pub fn into_storage(self) -> S { + self.trie.into_storage() + } + +} + +impl< + S: TrieBackendStorage, + H: Hasher, + K: KvBackend, +> std::fmt::Debug for StateBackend { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "StateBackend") + } +} + +impl< + S: TrieBackendStorage, + H: Hasher, + K: KvBackend, +> Backend for StateBackend where + H::Out: Ord, +{ + type Error = String; + type Transaction = (S::Overlay, HashMap, Option>>); + type TrieBackendStorage = S; + type KvBackend = K; + + fn storage(&self, key: &[u8]) -> Result>, Self::Error> { + self.trie.storage(key) + } + + fn child_storage(&self, storage_key: &[u8], key: &[u8]) -> Result>, Self::Error> { + self.trie.child_storage(storage_key, key) + } + + fn kv_storage(&self, key: &[u8]) -> Result>, Self::Error> { + self.kv.get(key) + } + + fn for_keys_with_prefix(&self, prefix: &[u8], f: F) { + self.trie.for_keys_with_prefix(prefix, f) + } + + fn for_key_values_with_prefix(&self, prefix: &[u8], f: F) { + self.trie.for_key_values_with_prefix(prefix, f) + } + + fn for_keys_in_child_storage(&self, storage_key: &[u8], f: F) { + self.trie.for_keys_in_child_storage(storage_key, f) + } + + fn for_child_keys_with_prefix(&self, storage_key: &[u8], prefix: &[u8], f: F) { + self.trie.for_child_keys_with_prefix(storage_key, prefix, f) + } + + fn pairs(&self) -> Vec<(Vec, Vec)> { + self.trie.pairs() + } + + fn children_storage_keys(&self) -> Vec> { + self.trie.children_storage_keys() + } + + fn child_pairs(&self, storage_key: &[u8]) -> Vec<(Vec, Vec)> { + self.trie.child_pairs(storage_key) + } + + fn kv_in_memory(&self) -> HashMap, Option>> { + self.kv.in_memory() + } + + fn keys(&self, prefix: &[u8]) -> Vec> { + let mut read_overlay = S::Overlay::default(); + let eph = Ephemeral::new(self.trie.backend_storage(), &mut read_overlay); + + let collect_all = || -> Result<_, Box>> { + let trie = TrieDB::::new(&eph, self.trie.root())?; + let mut v = Vec::new(); + for x in trie.iter()? { + let (key, _) = x?; + if key.starts_with(prefix) { + v.push(key.to_vec()); + } + } + + Ok(v) + }; + + collect_all().map_err(|e| debug!(target: "trie", "Error extracting trie keys: {}", e)).unwrap_or_default() + } + + fn storage_root(&self, delta: I) -> (H::Out, Self::Transaction) + where I: IntoIterator, Option>)> + { + let mut write_overlay = S::Overlay::default(); + let mut root = *self.trie.root(); + + { + let mut eph = Ephemeral::new( + self.trie.backend_storage(), + &mut write_overlay, + ); + + match delta_trie_root::, _, _, _, _>(&mut eph, root, delta) { + Ok(ret) => root = ret, + Err(e) => warn!(target: "trie", "Failed to write to trie: {}", e), + } + } + + (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 + { + let default_root = default_child_trie_root::>(storage_key); + + let mut write_overlay = S::Overlay::default(); + let mut root = match self.storage(storage_key) { + Ok(value) => value.unwrap_or(default_root.clone()), + Err(e) => { + warn!(target: "trie", "Failed to read child storage root: {}", e); + default_root.clone() + }, + }; + + { + let mut eph = Ephemeral::new( + self.trie.backend_storage(), + &mut write_overlay, + ); + + match child_delta_trie_root::, _, _, _, _>( + storage_key, + &mut eph, + root.clone(), + delta + ) { + Ok(ret) => root = ret, + Err(e) => warn!(target: "trie", "Failed to write to trie: {}", e), + } + } + + let is_default = root == default_root; + + (root, is_default, (write_overlay, Default::default())) + } + + fn kv_transaction(&self, delta: I) -> Self::Transaction + where + I: IntoIterator, Option>)> + { + let mut result = self.kv.in_memory(); + result.extend(delta.into_iter()); + (Default::default(), result) + } + + fn as_state_backend(&mut self) -> Option< + &StateBackend + > { + Some(self) + } +} + +#[cfg(test)] +pub mod tests { + use crate::trie_backend::tests::{test_db, KvBackend}; + use primitives::Blake2Hasher; + use trie::PrefixedMemoryDB; + use super::*; + + pub(crate) fn test_state( + ) -> StateBackend, Blake2Hasher, KvBackend> { + let (mdb, root, kv) = test_db(); + StateBackend::new(mdb, root, kv) + } +} diff --git a/core/state-machine/src/testing.rs b/core/state-machine/src/testing.rs index 8253f20c8bd01..191952a98e2b8 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::{ InMemoryStorage as ChangesTrieInMemoryStorage, BlockNumber as ChangesTrieBlockNumber, @@ -93,7 +93,18 @@ impl, N: ChangesTrieBlockNumber> TestExternalities { /// 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. @@ -120,7 +131,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. diff --git a/core/state-machine/src/trie_backend.rs b/core/state-machine/src/trie_backend.rs index 432ccf3e75f0e..de4d1c06a3c36 100644 --- a/core/state-machine/src/trie_backend.rs +++ b/core/state-machine/src/trie_backend.rs @@ -21,11 +21,11 @@ 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 crate::trie_backend_essence::{TrieBackendEssence, TrieBackendStorage, Ephemeral}; -use crate::Backend; +use primitives::storage::well_known_keys::CHILD_STORAGE_KEY_PREFIX; /// Patricia trie-based backend. Transaction type is an overlay of changes to commit. pub struct TrieBackend, H: Hasher> { - essence: TrieBackendEssence, + pub(crate) essence: TrieBackendEssence, } impl, H: Hasher> TrieBackend { @@ -55,46 +55,39 @@ impl, H: Hasher> TrieBackend { pub fn into_storage(self) -> S { self.essence.into_storage() } -} - -impl, H: Hasher> 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 - H::Out: Ord, -{ - type Error = String; - type Transaction = S::Overlay; - type TrieBackendStorage = S; - fn storage(&self, key: &[u8]) -> Result>, Self::Error> { + /// `Backend` method inner implementation. + pub fn storage(&self, key: &[u8]) -> Result>, String> { self.essence.storage(key) } - fn child_storage(&self, storage_key: &[u8], key: &[u8]) -> Result>, Self::Error> { + /// `Backend` method inner implementation. + pub fn child_storage(&self, storage_key: &[u8], key: &[u8]) -> Result>, String> { self.essence.child_storage(storage_key, key) } - fn for_keys_with_prefix(&self, prefix: &[u8], f: F) { + /// `Backend` method inner implementation. + pub fn for_keys_with_prefix(&self, prefix: &[u8], f: F) { self.essence.for_keys_with_prefix(prefix, f) } - fn for_key_values_with_prefix(&self, prefix: &[u8], f: F) { + /// `Backend` method inner implementation. + pub fn for_key_values_with_prefix(&self, prefix: &[u8], f: F) { self.essence.for_key_values_with_prefix(prefix, f) } - fn for_keys_in_child_storage(&self, storage_key: &[u8], f: F) { + /// `Backend` method inner implementation. + pub fn for_keys_in_child_storage(&self, storage_key: &[u8], f: F) { self.essence.for_keys_in_child_storage(storage_key, f) } - fn for_child_keys_with_prefix(&self, storage_key: &[u8], prefix: &[u8], f: F) { + /// `Backend` method inner implementation. + pub fn for_child_keys_with_prefix(&self, storage_key: &[u8], prefix: &[u8], f: F) { self.essence.for_child_keys_with_prefix(storage_key, prefix, f) } - fn pairs(&self) -> Vec<(Vec, Vec)> { + /// `Backend` method inner implementation. + pub fn pairs(&self) -> Vec<(Vec, Vec)> { let mut read_overlay = S::Overlay::default(); let eph = Ephemeral::new(self.essence.backend_storage(), &mut read_overlay); @@ -118,7 +111,47 @@ impl, H: Hasher> Backend for TrieBackend where } } - fn keys(&self, prefix: &[u8]) -> Vec> { + /// `Backend` method inner implementation. + pub fn children_storage_keys(&self) -> Vec> { + let mut result = Vec::new(); + self.essence.for_keys_with_prefix(CHILD_STORAGE_KEY_PREFIX, |k| result.push(k.to_vec())); + result + } + + /// `Backend` method inner implementation. + pub 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 collect_all = || -> Result<_, Box>> { + let trie = TrieDB::::new(&eph, &root)?; + let mut v = Vec::new(); + 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() + } + } + } + + /// `Backend` method inner implementation. + pub fn keys(&self, prefix: &[u8]) -> Vec> { let mut read_overlay = S::Overlay::default(); let eph = Ephemeral::new(self.essence.backend_storage(), &mut read_overlay); @@ -138,7 +171,8 @@ 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) + /// `Backend` method inner implementation. + pub fn storage_root(&self, delta: I) -> (H::Out, S::Overlay) where I: IntoIterator, Option>)> { let mut write_overlay = S::Overlay::default(); @@ -159,15 +193,16 @@ impl, H: Hasher> Backend for TrieBackend where (root, write_overlay) } - fn child_storage_root(&self, storage_key: &[u8], delta: I) -> (Vec, bool, Self::Transaction) - where - I: IntoIterator, Option>)>, - H::Out: Ord + /// `Backend` method inner implementation. + pub fn child_storage_root(&self, storage_key: &[u8], delta: I) -> (Vec, bool, S::Overlay) + where + I: IntoIterator, Option>)>, + H::Out: Ord { let default_root = default_child_trie_root::>(storage_key); let mut write_overlay = S::Overlay::default(); - let mut root = match self.storage(storage_key) { + let mut root = match self.essence.storage(storage_key) { Ok(value) => value.unwrap_or(default_root.clone()), Err(e) => { warn!(target: "trie", "Failed to read child storage root: {}", e); @@ -197,9 +232,6 @@ impl, H: Hasher> Backend for TrieBackend where (root, is_default, write_overlay) } - fn as_trie_backend(&mut self) -> Option<&TrieBackend> { - Some(self) - } } #[cfg(test)] @@ -210,7 +242,9 @@ pub mod tests { use trie::{TrieMut, PrefixedMemoryDB, trie_types::TrieDBMut}; use super::*; - fn test_db() -> (PrefixedMemoryDB, H256) { + pub type KvBackend = crate::kv_backend::InMemory; + + pub fn test_db() -> (PrefixedMemoryDB, H256, KvBackend) { let mut root = H256::default(); let mut mdb = PrefixedMemoryDB::::default(); { @@ -232,11 +266,16 @@ pub mod tests { trie.insert(&[i], &[i]).unwrap(); } } - (mdb, root) + // empty history. + let mut kv = crate::kv_backend::InMemory::default(); + kv.insert(b"kv1".to_vec(), Some(b"kv_value1".to_vec())); + kv.insert(b"kv2".to_vec(), Some(b"kv_value2".to_vec())); + (mdb, root, kv) } - pub(crate) fn test_trie() -> TrieBackend, Blake2Hasher> { - let (mdb, root) = test_db(); + pub(crate) fn test_trie( + ) -> TrieBackend, Blake2Hasher> { + let (mdb, root, _kv) = test_db(); TrieBackend::new(mdb, root) } @@ -258,7 +297,7 @@ pub mod tests { #[test] fn pairs_are_empty_on_empty_storage() { assert!(TrieBackend::, Blake2Hasher>::new( - PrefixedMemoryDB::default(), + Default::default(), Default::default(), ).pairs().is_empty()); } @@ -270,7 +309,8 @@ 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.drain().is_empty()); } #[test] diff --git a/core/utils/historical-data/Cargo.toml b/core/utils/historical-data/Cargo.toml new file mode 100644 index 0000000000000..196209b8cd9b0 --- /dev/null +++ b/core/utils/historical-data/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "historical-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/historical-data/README.md b/core/utils/historical-data/README.md new file mode 100644 index 0000000000000..7b2cfcadc1f98 --- /dev/null +++ b/core/utils/historical-data/README.md @@ -0,0 +1,19 @@ +## Historical 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 are done depending on a local history context +and update requires a global history context. + +Internally storage of multiple state is done independantly for each values, as oposed to a trie +where a global state is use to index all content. Each key value manages its own history. + +This crates is `no_std` compatible as long as the `std` feature is not enabled. + +For more information see + +License: GPL-3.0 diff --git a/core/utils/historical-data/src/lib.rs b/core/utils/historical-data/src/lib.rs new file mode 100644 index 0000000000000..93e974fb5d5b4 --- /dev/null +++ b/core/utils/historical-data/src/lib.rs @@ -0,0 +1,53 @@ +// 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 HistoricalValue { + /// The stored value. + pub value: V, + /// The moment in history when the value got set. + pub index: I, +} + +// Utility function for panicking cast (enabling casts similar to `as` cast for number). +fn saturating_into>(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/historical-data/src/linear.rs b/core/utils/historical-data/src/linear.rs new file mode 100644 index 0000000000000..3e65e9f3da9f8 --- /dev/null +++ b/core/utils/historical-data/src/linear.rs @@ -0,0 +1,410 @@ +// 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 historical data. +//! +//! Current encoding is a single encoded succession of values and +//! their state index (version 1 is used for it). +//! The frame for n elements is: +//! +//! `1 byte version ++ (u64 le encoded state index ++ byte value of element) * n +//! ++ (u64 le encoded index of element in the frame) * n - 1 ++ n encoded as le u64` +//! +//! Start index of first element and end of last element are not needed since +//! all other values are of constant size. +//! Latest values and states are pushed a the end, this is ordered +//! by state. +//! Version is optional as can be ommitted for storages of a single kind. +//! This history does not scale with number of version and would need to be split. +//! Other version can be use (a reverse linked list storage with range indexing +//! could be use). +//! Access to latest value in history should be the less costy access. + +#[cfg(not(feature = "std"))] +use rstd::{vec::Vec, vec}; +use rstd::marker::PhantomData; +use rstd::borrow::Cow; +use crate::HistoricalValue; + + +/// 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 InMemory = 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 InMemory = smallvec::SmallVec<[HistoricalValue; 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; + +/// Arraylike buffer with in place byte data. +/// Can be written as is in underlying storage. +/// Could be extended to direct access memory too. +#[derive(Debug, Clone)] +pub struct Serialized<'a, F>(Cow<'a, [u8]>, PhantomData); + +impl<'a, 'b, F> PartialEq> for Serialized<'a, F> { + fn eq(&self, other: &Serialized<'b, F>) -> bool { + self.0.eq(&other.0) + } +} + +impl<'a, F> Eq for Serialized<'a, F> { } + +/// Serialized specific behavior. +pub trait SerializedConfig { + /// Encoded empty slice. + fn empty() -> &'static [u8]; + /// Size needed to encode version. + /// Should be a static value. + 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 version. +pub struct DefaultVersion; + +impl SerializedConfig for NoVersion { + fn empty() -> &'static [u8] { + &NO_VERSION_EMPTY_SERIALIZED + } + fn version_len() -> usize { + 0 + } +} + +impl SerializedConfig for DefaultVersion { + fn empty() -> &'static [u8] { + &DEFAULT_VERSION_EMPTY_SERIALIZED + } + fn version_len() -> usize { + 1 + } +} + +// Length in number of bytes for an encoded size. +// Current value is one of a u64. +// Used both for number of element in history and +// length of elements. +const SIZE_BYTE_LEN: usize = 8; + +// Basis implementation must be on par with InMemory. +// Those method could be move to a 'VecLike' trait. +// +// Those function requires prior index checking. +impl<'a, F: SerializedConfig> Serialized<'a, F> { + + 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) { + // This could be implemented more efficiently + // (useless for test) + self.remove_range(index, self.len()); + } + + // 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(HistoricalValue { 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(HistoricalValue { value, index: state }) + } + + pub(crate) fn push(&mut self, val: HistoricalValue<&[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: HistoricalValue<&[u8], u64>, extra: &[u8]) { + let len = self.len(); + let start_ix = self.index_start(); + let end_ix = self.0.len(); + let new_len = self.0.len() + SIZE_BYTE_LEN + val.value.len() + extra.len(); + self.0.to_mut().resize(new_len, 0); + self.0.to_mut().copy_within( + start_ix .. end_ix, + start_ix + SIZE_BYTE_LEN + val.value.len() + extra.len() + ); + let mut position = start_ix; + self.write_le_u64(position, val.index); + position += SIZE_BYTE_LEN; + self.0.to_mut()[position .. position + val.value.len()].copy_from_slice(val.value); + position += val.value.len(); + self.0.to_mut()[position .. position + extra.len()].copy_from_slice(extra); + 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) -> HistoricalValue<&[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); + HistoricalValue { + value: &self.0[start_ix + SIZE_BYTE_LEN..end_ix], + index: state, + } + } + +} + +const NO_VERSION_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(Cow::Borrowed(F::empty()), PhantomData) + } +} + +impl<'a, F> Into> for &'a[u8] { + fn into(self) -> Serialized<'a, F> { + Serialized(Cow::Borrowed(self), PhantomData) + } +} + +impl Into> for Vec { + fn into(self) -> Serialized<'static, F> { + Serialized(Cow::Owned(self), PhantomData) + } +} + +// Utility function for basis implementation. +impl<'a, F: SerializedConfig> Serialized<'a, F> { + + // Index at the end of the element part, start of internal index table in the buffer. + // (also followed by the encoded size, the last part in the buffer) + 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) + } + + fn slice_copy(&mut self, start_from: usize, start_to: usize, size: usize) { + self.0.to_mut().copy_within(start_from..start_from + size, start_to); + } + + // Usize encoded as le u64 (for historical 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) + } + + // Read 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 + } + + // Write 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[..]); + } + + // Append 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[..]); + } + + // Write u64 encoded as le. + 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::*; + + impl From<(V, I)> for HistoricalValue { + fn from(input: (V, I)) -> HistoricalValue { + HistoricalValue { value: input.0, index: input.1 } + } + } + + 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/historical-data/src/tree.rs b/core/utils/historical-data/src/tree.rs new file mode 100644 index 0000000000000..447116fab08ac --- /dev/null +++ b/core/utils/historical-data/src/tree.rs @@ -0,0 +1,1055 @@ +// 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 state is an acyclic directed graph (tree). +//! +//! General structure for the global state is an array of branch, +//! each branch originating from another branch at designated index. +//! +//! Data structure for data is an indexed collection of linear storages +//! for each of those branches. + +use crate::linear::{ + InMemory as BranchBackend, + Serialized as SerializedInner, + SerializedConfig, +}; +use crate::HistoricalValue; +use crate::PruneResult; +use crate::saturating_into; +use rstd::vec::Vec; +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; +} + +/// This is a simple range, end non inclusive. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct BranchRange { + pub start: u64, + pub end: u64, +} + +impl<'a> BranchStateTrait for &'a BranchRange { + + 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 BranchRange { + /// Return true if the state exists, false otherwhise. + pub fn get_state(&self, index: u64) -> bool { + index < self.end && index >= self.start + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct BranchState { + pub branch_index: u64, + pub range: BranchRange, +} + + +/// 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 = saturating_into(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 = saturating_into(state_branch.last_index()); + let mut history = BranchBackend::::default(); + history.push(HistoricalValue { + 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 = saturating_into(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 = HistoricalValue { + 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; + } + + for (state_branch, state_index) in state.iter() { + let state_index = saturating_into(state_index); + while index > 0 { + let branch_index = saturating_into(self.0[index - 1].branch_index); + if state_index == branch_index { + if let Some(result) = self.branch_get(index - 1, &state_branch) { + return Some(result) + } + break; + } else if state_index > branch_index { + break; + } else { + index -= 1; + } + } + 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 = saturating_into(v.index); + if state.get_node(i) { + return Some(&v.value); + } + } + } + None + } + + /// Gc an historical 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 = saturating_into(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 = saturating_into(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_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 HistoricalValue { value, index: state_index } = self.0.get_state(index); + let state_index = saturating_into(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 = saturating_into(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(HistoricalValue {value, index: target_state_index}, &[0][..]), + None => + self.0.push(HistoricalValue {value: &[], index: target_state_index}), + } + } + + /// Prune value that are before the index if they are + /// not needed afterward. + pub fn prune(&mut self, index: I) -> PruneResult + where + I: Copy + Eq + TryFrom + TryInto, + { + let from = saturating_into(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)] +/// Serialized implementation when transaction support is not +/// needed. +pub struct Serialized<'a, F>(SerializedInner<'a, F>); + +impl<'a, 'b, F> PartialEq> for Serialized<'a, F> { + fn eq(&self, other: &Serialized<'b, F>) -> bool { + self.0.eq(&other.0) + } +} + +impl<'a, F> Eq for Serialized<'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()) + } +} + +impl<'a, F> Into> for &'a [u8] { + 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::*; + + use rstd::collections::btree_map::BTreeMap; + use rstd::rc::Rc; + + /// 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))] + 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, + } + + #[derive(Debug, Clone)] + #[cfg_attr(any(test, feature = "test"), derive(PartialEq, Eq))] + struct BranchStateFull { + // 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: ModifiableBranchRange, + } + + #[derive(Debug, Clone)] + #[cfg_attr(any(test, feature = "test"), derive(PartialEq, Eq))] + struct ModifiableBranchRange { + range: BranchRange, + can_append: bool, + } + + #[derive(Clone)] + /// Reference to state to use for query updates. + /// It is a single brannch path with branches ordered by branch_index. + /// It uses a Rc to be sharable between multiple state, in practice + /// this is not very useful when node get appended. + struct BranchRanges { + /// 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, + } + + impl Default for TestStates { + fn default() -> Self { + TestStates { + branches: Default::default(), + last_branch_index: 0, + valid_treshold: 0, + } + } + } + + impl TestStates { + + /// 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(&self, mut branch_index: u64) -> BranchRanges { + 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_range = branch.state(); + if branch_range.range.end > previous_origin_node_index + 1 { + branch_range.range.end = previous_origin_node_index + 1; + } + previous_origin_node_index = branch.origin_node_index; + // vecdeque would be better suited + result.insert(0, branch_range); + branch_index = branch.origin_branch_index; + } else { + break; + } + } + BranchRanges { 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, BranchStateFull { + 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_range_mut(&mut self, branch_index: u64) -> Option<&mut ModifiableBranchRange> { + 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_range_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, + } + } + } + } + } + + impl BranchStateFull { + pub fn state(&self) -> BranchState { + BranchState { + branch_index: self.branch_index, + range: self.state.range(), + } + } + } + + impl<'a> BranchesStateTrait for &'a BranchRanges { + type Branch = (&'a BranchRange, Option); + 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.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; + } + + BranchRangesIter(self, end, upper_node_index) + } + } + + impl<'a> BranchStateTrait for (&'a BranchRange, 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 Default for ModifiableBranchRange { + // initialize with one element + fn default() -> Self { + Self::new_from(0) + } + } + + impl ModifiableBranchRange { + pub fn new_from(offset: u64) -> Self { + ModifiableBranchRange { + range: BranchRange { + start: offset, + end: offset + 1, + }, + can_append: true, + } + } + + pub fn range(&self) -> BranchRange { + self.range.clone() + } + + pub fn add_state(&mut self) -> bool { + if self.can_append { + self.range.end += 1; + true + } else { + false + } + } + + pub fn drop_state(&mut self) -> Option { + if self.range.end > self.range.start { + self.range.end -= 1; + self.can_append = false; + Some(self.range.end) + } else { + None + } + } + + pub fn get_state(&self, index: u64) -> bool { + self.range.get_state(index) + } + + pub fn latest_ix(&self) -> Option { + if self.range.end > self.range.start { + Some(self.range.end - 1) + } else { + None + } + } + + } + + /// Iterator, contains index of last inner struct. + pub struct BranchRangesIter<'a>(&'a BranchRanges, usize, Option); + + impl<'a> Iterator for BranchRangesIter<'a> { + type Item = ((&'a BranchRange, Option), u64); + + fn next(&mut self) -> Option { + if self.1 > 0 { + self.1 -= 1; + let upper_node_index = self.2.take(); + Some(( + (&self.0.history[self.1].range, upper_node_index), + self.0.history[self.1].branch_index, + )) + } else { + None + } + } + } + + impl BranchRanges { + /// 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; + } + + } + + + 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_range_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_range_mut(1).map(|ls| ls.add_state())); + assert_eq!(Some(Some(2)), states.branch_range_mut(1).map(|ls| ls.drop_state())); + // cannot create when dropped happen on branch + assert_eq!(Some(false), states.branch_range_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_range_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() { + let states = test_states(); + let ref_3 = vec![ + BranchState { + branch_index: 1, + range: BranchRange { start: 0, end: 2 }, + }, + BranchState { + branch_index: 3, + range: BranchRange { start: 0, end: 1 }, + }, + ]; + assert_eq!(*states.state(3).history, ref_3); + + let mut states = states; + + assert_eq!(states.create_branch(1, 1, Some(0)), Some(6)); + let ref_6 = vec![ + BranchState { + branch_index: 1, + range: BranchRange { start: 0, end: 1 }, + }, + BranchState { + branch_index: 6, + range: BranchRange { start: 0, end: 1 }, + }, + ]; + assert_eq!(*states.state(6).history, ref_6); + + states.valid_treshold = 3; + let mut ref_6 = ref_6; + ref_6.remove(0); + assert_eq!(*states.state(6).history, ref_6); + } + + #[test] + fn test_set_get() { + let states = test_states(); + let mut item: History = Default::default(); + let mut ref_1 = states.state(3); + ref_1.limit_branch(1, Some(1)); + item.set(&states.state(1), 4); + assert_eq!(item.get(&states.state(1)), Some(&4)); + assert_eq!(item.get(&states.state(4)), Some(&4)); + + let mut item: History = Default::default(); + + for i in 0..6 { + assert_eq!(item.get(&states.state(i)), None); + } + + // setting value respecting branch build order + for i in 1..6 { + item.set(&states.state(i), i); + } + + for i in 1..6 { + assert_eq!(item.get(&states.state(i)), Some(&i)); + } + + let mut ref_3 = states.state(3); + ref_3.limit_branch(1, None); + assert_eq!(item.get(&ref_3), Some(&1)); + + let mut ref_1 = states.state(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(*i), *i); + } + for i in r { + assert_eq!(item.get(&states.state(*i)), Some(i)); + } + } + + } + + + #[test] + fn test_gc() { + 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(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.range.end -= 1); + let states1: BTreeMap<_, _> = states1.iter().map(|(k,v)| (k, v.state())).collect(); + let mut item1 = item.clone(); + item1.gc(states1.iter().map(|(k, v)| ((&v.range, None), **k)).rev()); + assert_eq!(item1.get(&states.state(1)), None); + for a in action.iter().skip(1) { + if a.1 { + assert_eq!(item1.get(&states.state(a.0)), Some(&a.0)); + } else { + assert_eq!(item1.get(&states.state(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); + + } + +}