diff --git a/bin/node/bench/src/trie.rs b/bin/node/bench/src/trie.rs index 6f75741fa75c6..477135eac8ba1 100644 --- a/bin/node/bench/src/trie.rs +++ b/bin/node/bench/src/trie.rs @@ -22,6 +22,7 @@ use lazy_static::lazy_static; use rand::Rng; use hash_db::Prefix; use sp_state_machine::Backend as _; +use sp_core::storage::ChildInfo; use node_primitives::Hash; @@ -161,7 +162,7 @@ impl core::BenchmarkDescription for TrieBenchmarkDescription { struct Storage(Arc); impl sp_state_machine::Storage for Storage { - fn get(&self, key: &Hash, prefix: Prefix) -> Result>, String> { + fn get(&self, _trie: &ChildInfo, key: &Hash, prefix: Prefix) -> Result>, String> { let key = sp_trie::prefixed_key::(key, prefix); self.0.get(0, &key).map_err(|e| format!("Database backend error: {:?}", e)) } @@ -171,7 +172,7 @@ impl core::Benchmark for TrieBenchmark { fn run(&mut self, mode: Mode) -> std::time::Duration { let mut db = self.database.clone(); let storage: Arc> = - Arc::new(Storage(db.open())); + Arc::new(Storage(db.open())); let trie_backend = sp_state_machine::TrieBackend::new( storage, @@ -235,4 +236,4 @@ impl SizePool { rng.fill_bytes(&mut key[..]); key.to_vec() } -} \ No newline at end of file +} diff --git a/client/api/src/backend.rs b/client/api/src/backend.rs index 33a370c7cb2c5..33e0dd1ac67ea 100644 --- a/client/api/src/backend.rs +++ b/client/api/src/backend.rs @@ -24,7 +24,7 @@ use sp_runtime::{generic::BlockId, Justification, Storage}; use sp_runtime::traits::{Block as BlockT, NumberFor, HashFor}; use sp_state_machine::{ ChangesTrieState, ChangesTrieStorage as StateChangesTrieStorage, ChangesTrieTransaction, - StorageCollection, ChildStorageCollection, + ChildStorageCollection, StorageCollection, }; use sp_storage::{StorageData, StorageKey, PrefixedStorageKey, ChildInfo}; use crate::{ diff --git a/client/db/src/bench.rs b/client/db/src/bench.rs index 9d6f595498bd0..56c2484001baa 100644 --- a/client/db/src/bench.rs +++ b/client/db/src/bench.rs @@ -21,8 +21,8 @@ use std::cell::{Cell, RefCell}; use std::collections::HashMap; use hash_db::{Prefix, Hasher}; -use sp_trie::{MemoryDB, prefixed_key}; -use sp_core::storage::ChildInfo; +use sp_trie::MemoryDB; +use sp_core::storage::{ChildInfo, ChildType}; use sp_runtime::traits::{Block as BlockT, HashFor}; use sp_runtime::Storage; use sp_state_machine::{DBValue, backend::Backend as StateBackend}; @@ -41,8 +41,13 @@ struct StorageDb { } impl sp_state_machine::Storage> for StorageDb { - fn get(&self, key: &Block::Hash, prefix: Prefix) -> Result, String> { - let key = prefixed_key::>(key, prefix); + fn get( + &self, + child_info: &ChildInfo, + key: &Block::Hash, + prefix: Prefix, + ) -> Result, String> { + let key = crate::keyspace_and_prefixed_key(key.as_ref(), child_info.keyspace(), prefix); self.db.get(0, &key) .map_err(|e| format!("Database backend error: {:?}", e)) } @@ -81,11 +86,24 @@ impl BenchmarkingState { child_content.child_info, child_content.data.into_iter().map(|(k, v)| (k, Some(v))), )); - let (root, transaction): (B::Hash, _) = state.state.borrow_mut().as_mut().unwrap().full_storage_root( + let (root, transaction, _): (B::Hash, _, _) = state.state.borrow_mut().as_mut().unwrap().full_storage_root( genesis.top.into_iter().map(|(k, v)| (k, Some(v))), child_delta, + false, ); - state.genesis = transaction.clone().drain(); + let mut keyspace = crate::Keyspaced::new(&[]); + for (info, mut updates) in transaction.clone().into_iter() { + keyspace.change_keyspace(info.keyspace()); + for (key, rc_val) in updates.drain() { + let key = if info.is_top_trie() { + key + } else { + keyspace.prefix_key(key.as_slice()).to_vec() + }; + + state.genesis.insert(key, rc_val); + } + } state.genesis_root = root.clone(); state.commit(root, transaction)?; state.record.take(); @@ -229,20 +247,37 @@ impl StateBackend> for BenchmarkingState { None } - fn commit(&self, storage_root: as Hasher>::Out, mut transaction: Self::Transaction) + fn commit(&self, storage_root: as Hasher>::Out, transaction: Self::Transaction) -> Result<(), Self::Error> { if let Some(db) = self.db.take() { let mut db_transaction = DBTransaction::new(); - let changes = transaction.drain(); - let mut keys = Vec::with_capacity(changes.len()); - for (key, (val, rc)) in changes { - if rc > 0 { - db_transaction.put(0, &key, &val); - } else if rc < 0 { - db_transaction.delete(0, &key); + let mut keys = Vec::new(); + let mut keyspace = crate::Keyspaced::new(&[]); + for (info, mut updates) in transaction.into_iter() { + // child info with strong unique id are using the same state-db with prefixed key + if info.child_type() != ChildType::ParentKeyId { + // Unhandled child kind + unimplemented!( + "Data for {:?} without a backend implementation", + info.child_type(), + ); + } + keyspace.change_keyspace(info.keyspace()); + for (key, (val, rc)) in updates.drain() { + let key = if info.is_top_trie() { + key + } else { + keyspace.prefix_key(key.as_slice()).to_vec() + }; + + if rc > 0 { + db_transaction.put(0, &key, &val); + } else if rc < 0 { + db_transaction.delete(0, &key); + } + keys.push(key); } - keys.push(key); } self.record.set(keys); db.write(db_transaction).map_err(|_| String::from("Error committing transaction"))?; diff --git a/client/db/src/changes_tries_storage.rs b/client/db/src/changes_tries_storage.rs index 985251f403d62..b2ea6dc80fc21 100644 --- a/client/db/src/changes_tries_storage.rs +++ b/client/db/src/changes_tries_storage.rs @@ -488,7 +488,11 @@ where self.build_cache.read().with_changed_keys(root, functor) } - fn get(&self, key: &Block::Hash, _prefix: Prefix) -> Result>, String> { + fn get( + &self, + key: &Block::Hash, + _prefix: Prefix, + ) -> Result>, String> { Ok(self.db.get(self.changes_tries_column, key.as_ref())) } } @@ -701,7 +705,10 @@ mod tests { .log(DigestItem::as_changes_trie_root) .cloned(); match trie_root { - Some(trie_root) => backend.changes_tries_storage.get(&trie_root, EMPTY_PREFIX).unwrap().is_none(), + Some(trie_root) => backend.changes_tries_storage.get( + &trie_root, + EMPTY_PREFIX, + ).unwrap().is_none(), None => true, } }; diff --git a/client/db/src/lib.rs b/client/db/src/lib.rs index 579ea2db4ad8d..7f47d5dfc051e 100644 --- a/client/db/src/lib.rs +++ b/client/db/src/lib.rs @@ -61,11 +61,11 @@ use sp_blockchain::{ }; use codec::{Decode, Encode}; use hash_db::Prefix; -use sp_trie::{MemoryDB, PrefixedMemoryDB, prefixed_key}; +use sp_trie::{MemoryDB, PrefixedMemoryDB}; use sp_database::Transaction; use parking_lot::RwLock; use sp_core::{ChangesTrieConfiguration, traits::CodeExecutor}; -use sp_core::storage::{well_known_keys, ChildInfo}; +use sp_core::storage::{well_known_keys, ChildInfo, ChildrenMap, ChildType}; use sp_runtime::{ generic::BlockId, Justification, Storage, BuildStorage, @@ -555,7 +555,7 @@ impl HeaderMetadata for BlockchainDb { /// Database transaction pub struct BlockImportOperation { old_state: SyncingCachingState, Block>, - db_updates: PrefixedMemoryDB>, + db_updates: ChildrenMap>>, storage_updates: StorageCollection, child_storage_updates: ChildStorageCollection, changes_trie_updates: MemoryDB>, @@ -610,7 +610,10 @@ impl sc_client_api::backend::BlockImportOperation for Bloc // Currently cache isn't implemented on full nodes. } - fn update_db_storage(&mut self, update: PrefixedMemoryDB>) -> ClientResult<()> { + fn update_db_storage( + &mut self, + update: ChildrenMap>>, + ) -> ClientResult<()> { self.db_updates = update; Ok(()) } @@ -630,7 +633,7 @@ impl sc_client_api::backend::BlockImportOperation for Bloc )); let mut changes_trie_config: Option = None; - let (root, transaction) = self.old_state.full_storage_root( + let (root, transaction, _) = self.old_state.full_storage_root( storage.top.into_iter().map(|(k, v)| { if k == well_known_keys::CHANGES_TRIE_CONFIG { changes_trie_config = Some( @@ -640,7 +643,8 @@ impl sc_client_api::backend::BlockImportOperation for Bloc } (k, Some(v)) }), - child_delta + child_delta, + false, ); self.db_updates = transaction; @@ -697,8 +701,15 @@ struct StorageDb { } impl sp_state_machine::Storage> for StorageDb { - fn get(&self, key: &Block::Hash, prefix: Prefix) -> Result, String> { - let key = prefixed_key::>(key, prefix); + fn get( + &self, + child_info: &ChildInfo, + key: &Block::Hash, + prefix: Prefix, + ) -> Result, String> { + // Default child trie (those with strong unique id) are put + // directly into the same address space at state_db level. + let key = keyspace_and_prefixed_key(key.as_ref(), child_info.keyspace(), prefix); self.state_db.get(&key, self) .map_err(|e| format!("Database backend error: {:?}", e)) } @@ -725,7 +736,12 @@ impl DbGenesisStorage { } impl sp_state_machine::Storage> for DbGenesisStorage { - fn get(&self, _key: &Block::Hash, _prefix: Prefix) -> Result, String> { + fn get( + &self, + _trie: &ChildInfo, + _key: &Block::Hash, + _prefix: Prefix, + ) -> Result, String> { Ok(None) } } @@ -1084,22 +1100,39 @@ impl Backend { } let finalized = if operation.commit_state { - let mut changeset: sc_state_db::ChangeSet> = sc_state_db::ChangeSet::default(); + let mut state_db_changeset: sc_state_db::ChangeSet> = sc_state_db::ChangeSet::default(); let mut ops: u64 = 0; - let mut bytes: u64 = 0; + let mut bytes = 0; let mut removal: u64 = 0; let mut bytes_removal: u64 = 0; - for (key, (val, rc)) in operation.db_updates.drain() { - if rc > 0 { - ops += 1; - bytes += key.len() as u64 + val.len() as u64; - - changeset.inserted.push((key, val.to_vec())); - } else if rc < 0 { - removal += 1; - bytes_removal += key.len() as u64; - - changeset.deleted.push(key); + let mut keyspace = Keyspaced::new(&[]); + for (info, mut updates) in operation.db_updates.into_iter() { + // child info with strong unique id are using the same state-db with prefixed key + if info.child_type() != ChildType::ParentKeyId { + // Unhandled child kind + return Err(ClientError::Backend(format!( + "Data for {:?} without a backend implementation", + info.child_type(), + ))); + } + keyspace.change_keyspace(info.keyspace()); + for (key, (val, rc)) in updates.drain() { + let key = if info.is_top_trie() { + key + } else { + keyspace.prefix_key(key.as_slice()).to_vec() + }; + if rc > 0 { + ops += 1; + bytes += key.len() as u64 + val.len() as u64; + + state_db_changeset.inserted.push((key, val.to_vec())); + } else if rc < 0 { + removal += 1; + bytes_removal += key.len() as u64; + + state_db_changeset.deleted.push(key); + } } } self.state_usage.tally_writes_nodes(ops, bytes); @@ -1108,7 +1141,7 @@ impl Backend { let mut ops: u64 = 0; let mut bytes: u64 = 0; for (key, value) in operation.storage_updates.iter() - .chain(operation.child_storage_updates.iter().flat_map(|(_, s)| s.iter())) { + .chain(operation.child_storage_updates.iter().flat_map(|(_, s, _)| s.iter())) { ops += 1; bytes += key.len() as u64; if let Some(v) = value.as_ref() { @@ -1121,7 +1154,7 @@ impl Backend { &hash, number_u64, &pending_block.header.parent_hash(), - changeset, + state_db_changeset, ).map_err(|e: sc_state_db::Error| sp_blockchain::Error::from(format!("State database error: {:?}", e)) )?; @@ -1357,7 +1390,7 @@ impl sc_client_api::backend::Backend for Backend { 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_config_update: None, @@ -1624,6 +1657,7 @@ impl sc_client_api::backend::Backend for Backend { Ok(Some(header)) => { sp_state_machine::Storage::get( self.storage.as_ref(), + &ChildInfo::top_trie(), &header.state_root(), (&[], None), ).unwrap_or(None).is_some() @@ -1642,6 +1676,47 @@ impl sc_client_api::backend::Backend for Backend { impl sc_client_api::backend::LocalBackend for Backend {} +/// Rules for storing a default child trie with unique id. +struct Keyspaced { + keyspace_len: usize, + buffer: Vec, +} + +impl Keyspaced { + fn new(keyspace: &[u8]) -> Self { + Keyspaced { + keyspace_len: keyspace.len(), + buffer: keyspace.to_vec(), + } + } + + fn change_keyspace(&mut self, new_keyspace: &[u8]) { + self.keyspace_len = new_keyspace.len(); + self.buffer.resize(new_keyspace.len(), 0); + self.buffer[..new_keyspace.len()].copy_from_slice(new_keyspace); + } + + fn prefix_key(&mut self, key: &[u8]) -> &[u8] { + self.buffer.resize(self.keyspace_len + key.len(), 0); + self.buffer[self.keyspace_len..].copy_from_slice(key); + self.buffer.as_slice() + } +} + +// Prefix key and add keyspace with a single vec alloc +// Warning if memory_db `sp_trie::prefixed_key` implementation change, this function +// will need change too. +fn keyspace_and_prefixed_key(key: &[u8], keyspace: &[u8], prefix: Prefix) -> Vec { + let mut prefixed_key = Vec::with_capacity(key.len() + keyspace.len() + prefix.0.len() + 1); + prefixed_key.extend_from_slice(keyspace); + prefixed_key.extend_from_slice(prefix.0); + if let Some(last) = prefix.1 { + prefixed_key.push(last); + } + prefixed_key.extend_from_slice(key); + prefixed_key +} + #[cfg(test)] pub(crate) mod tests { use hash_db::{HashDB, EMPTY_PREFIX}; @@ -1770,6 +1845,9 @@ pub(crate) mod tests { #[test] fn set_state_data() { let db = Backend::::new_test(2, 0); + + let child_info = sp_core::storage::ChildInfo::new_default(b"key1"); + let hash = { let mut op = db.begin_operation().unwrap(); db.begin_state_operation(&mut op, BlockId::Hash(Default::default())).unwrap(); @@ -1786,16 +1864,28 @@ pub(crate) mod tests { (vec![1, 2, 3], vec![9, 9, 9]), ]; - header.state_root = op.old_state.storage_root(storage + let child_storage = vec![ + (vec![2, 3, 5], Some(vec![4, 4, 6])), + (vec![2, 2, 3], Some(vec![7, 9, 9])), + ]; + + header.state_root = op.old_state.full_storage_root(storage .iter() .cloned() - .map(|(x, y)| (x, Some(y))) + .map(|(x, y)| (x, Some(y))), + vec![(child_info.clone(), child_storage.clone())], + false, ).0.into(); let hash = header.hash(); + let mut children_default = HashMap::default(); + children_default.insert(child_info.storage_key().to_vec(), sp_core::storage::StorageChild { + child_info: child_info.clone(), + data: child_storage.iter().map(|(k, v)| (k.clone(), v.clone().unwrap())).collect(), + }); op.reset_storage(Storage { top: storage.iter().cloned().collect(), - children_default: Default::default(), + children_default, }).unwrap(); op.set_block_data( header.clone(), @@ -1811,6 +1901,10 @@ pub(crate) mod tests { assert_eq!(state.storage(&[1, 3, 5]).unwrap(), Some(vec![2, 4, 6])); assert_eq!(state.storage(&[1, 2, 3]).unwrap(), Some(vec![9, 9, 9])); assert_eq!(state.storage(&[5, 5, 5]).unwrap(), None); + assert_eq!( + state.child_storage(&child_info, &[2, 3, 5]).unwrap(), + Some(vec![4, 4, 6]), + ); hash }; @@ -1850,6 +1944,12 @@ pub(crate) 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.child_storage(&child_info, &[2, 3, 5]).unwrap(), + Some(vec![4, 4, 6]), + ); + + } } @@ -1884,7 +1984,9 @@ pub(crate) mod tests { children_default: Default::default(), }).unwrap(); - key = op.db_updates.insert(EMPTY_PREFIX, b"hello"); + key = op.db_updates.entry(ChildInfo::top_trie()) + .or_insert_with(Default::default) + .insert(EMPTY_PREFIX, b"hello"); op.set_block_data( header, Some(vec![]), @@ -1920,8 +2022,14 @@ pub(crate) 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 + .entry(ChildInfo::top_trie()) + .or_insert_with(Default::default) + .insert(EMPTY_PREFIX, b"hello"); + op.db_updates + .entry(ChildInfo::top_trie()) + .or_insert_with(Default::default) + .remove(&key, EMPTY_PREFIX); op.set_block_data( header, Some(vec![]), @@ -1957,7 +2065,10 @@ pub(crate) mod tests { ).0.into(); let hash = header.hash(); - op.db_updates.remove(&key, EMPTY_PREFIX); + op.db_updates + .entry(ChildInfo::top_trie()) + .or_insert_with(Default::default) + .remove(&key, EMPTY_PREFIX); op.set_block_data( header, Some(vec![]), diff --git a/client/db/src/storage_cache.rs b/client/db/src/storage_cache.rs index 66ac74afa4f2a..77fba3d1c78d8 100644 --- a/client/db/src/storage_cache.rs +++ b/client/db/src/storage_cache.rs @@ -381,7 +381,7 @@ impl CacheChanges { } let mut modifications = HashSet::new(); let mut child_modifications = HashSet::new(); - child_changes.into_iter().for_each(|(sk, changes)| + child_changes.into_iter().for_each(|(sk, changes, _ci)| for (k, v) in changes.into_iter() { let k = (sk.clone(), k); if is_best { @@ -1174,6 +1174,7 @@ mod tests { #[test] fn should_track_used_size_correctly() { + let child_info1 = ChildInfo::new_default(b"unique_id_1"); let root_parent = H256::random(); let shared = new_shared_cache::(109, ((109-36), 109)); let h0 = H256::random(); @@ -1201,7 +1202,7 @@ mod tests { &[], &[], vec![], - vec![(s_key.clone(), vec![(key.clone(), Some(vec![1, 2]))])], + vec![(s_key.clone(), vec![(key.clone(), Some(vec![1, 2]))], child_info1)], Some(h0), Some(0), true, diff --git a/client/src/cht.rs b/client/src/cht.rs index de67280632302..535e97533ea22 100644 --- a/client/src/cht.rs +++ b/client/src/cht.rs @@ -23,7 +23,6 @@ //! root has. A correct proof implies that the claimed block is identical to the one //! we discarded. -use hash_db; use codec::Encode; use sp_trie; @@ -86,7 +85,7 @@ pub fn compute_root( ) -> ClientResult where Header: HeaderT, - Hasher: hash_db::Hasher, + Hasher: sp_core::Hasher, Hasher::Out: Ord, I: IntoIterator>>, { @@ -105,7 +104,7 @@ pub fn build_proof( ) -> ClientResult where Header: HeaderT, - Hasher: hash_db::Hasher, + Hasher: sp_core::Hasher, Hasher::Out: Ord + codec::Codec, BlocksI: IntoIterator, HashesI: IntoIterator>>, @@ -132,7 +131,7 @@ pub fn check_proof( ) -> ClientResult<()> where Header: HeaderT, - Hasher: hash_db::Hasher, + Hasher: sp_core::Hasher, Hasher::Out: Ord + codec::Codec, { do_check_proof::( @@ -161,7 +160,7 @@ pub fn check_proof_on_proving_backend( ) -> ClientResult<()> where Header: HeaderT, - Hasher: hash_db::Hasher, + Hasher: sp_core::Hasher, Hasher::Out: Ord + codec::Codec, { do_check_proof::( @@ -185,7 +184,7 @@ fn do_check_proof( ) -> ClientResult<()> where Header: HeaderT, - Hasher: hash_db::Hasher, + Hasher: sp_core::Hasher, Hasher::Out: Ord, F: FnOnce(Hasher::Out, &[u8]) -> ClientResult>>, { diff --git a/client/src/client.rs b/client/src/client.rs index 2a8040febf3ef..3315a9b85f659 100644 --- a/client/src/client.rs +++ b/client/src/client.rs @@ -398,7 +398,11 @@ impl Client where self.storage.with_cached_changed_keys(root, functor) } - fn get(&self, key: &Block::Hash, prefix: Prefix) -> Result, String> { + fn get( + &self, + key: &Block::Hash, + prefix: Prefix, + ) -> Result, String> { self.storage.get(key, prefix) } } @@ -962,7 +966,7 @@ impl Client where .trigger( ¬ify_import.hash, storage_changes.0.into_iter(), - storage_changes.1.into_iter().map(|(sk, v)| (sk, v.into_iter())), + storage_changes.1.into_iter().map(|(sk, v, _ci)| (sk, v.into_iter())), ); } diff --git a/client/src/in_mem.rs b/client/src/in_mem.rs index 20b227e790f6e..c9c65b9aeda08 100644 --- a/client/src/in_mem.rs +++ b/client/src/in_mem.rs @@ -520,9 +520,10 @@ impl backend::BlockImportOperation for BlockImportOperatio .map(|(_storage_key, child_content)| (child_content.child_info, child_content.data.into_iter().map(|(k, v)| (k, Some(v))))); - let (root, transaction) = self.old_state.full_storage_root( + let (root, transaction, _) = self.old_state.full_storage_root( storage.top.into_iter().map(|(k, v)| (k, Some(v))), - child_delta + child_delta, + false, ); self.new_state = Some(InMemoryBackend::from(transaction)); diff --git a/client/src/light/backend.rs b/client/src/light/backend.rs index 01e9854864062..fa7aec5338222 100644 --- a/client/src/light/backend.rs +++ b/client/src/light/backend.rs @@ -46,7 +46,7 @@ use sc_client_api::{ UsageInfo, }; use crate::light::blockchain::Blockchain; -use hash_db::Hasher; +use sp_core::Hasher; const IN_MEMORY_EXPECT_PROOF: &str = "InMemory state backend has Void error type and always succeeds; qed"; @@ -326,7 +326,7 @@ impl BlockImportOperation for ImportOperation } let storage_update = InMemoryBackend::from(storage); - 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, false); self.storage_update = Some(storage_update); Ok(storage_root) diff --git a/client/src/light/call_executor.rs b/client/src/light/call_executor.rs index b439a268d2fe1..7222bd44555da 100644 --- a/client/src/light/call_executor.rs +++ b/client/src/light/call_executor.rs @@ -30,7 +30,7 @@ use sp_state_machine::{ self, Backend as StateBackend, OverlayedChanges, ExecutionStrategy, create_proof_check_backend, execution_proof_check_on_trie_backend, ExecutionManager, StorageProof, CloneableSpawn, }; -use hash_db::Hasher; +use sp_core::Hasher; use sp_api::{ProofRecorder, InitializeBlock, StorageTransactionCache}; diff --git a/client/src/light/fetcher.rs b/client/src/light/fetcher.rs index ef6a062cf3c07..702dd33c6c399 100644 --- a/client/src/light/fetcher.rs +++ b/client/src/light/fetcher.rs @@ -20,7 +20,8 @@ use std::sync::Arc; use std::collections::{BTreeMap, HashMap}; use std::marker::PhantomData; -use hash_db::{HashDB, Hasher, EMPTY_PREFIX}; +use hash_db::{HashDB, EMPTY_PREFIX}; +use sp_core::Hasher; use codec::{Decode, Encode}; use sp_core::{convert_hash, traits::CodeExecutor}; use sp_core::storage::{ChildInfo, ChildType}; diff --git a/frame/contracts/src/account_db.rs b/frame/contracts/src/account_db.rs index aae853d2ff996..14c9ead7e6c22 100644 --- a/frame/contracts/src/account_db.rs +++ b/frame/contracts/src/account_db.rs @@ -17,7 +17,7 @@ //! Auxiliaries to help with managing partial changes to accounts state. use super::{ - AliveContractInfo, BalanceOf, CodeHash, ContractInfo, ContractInfoOf, Trait, TrieId, + AliveContractInfo, BalanceOf, CodeHash, ContractInfo, ContractInfoOf, Trait, TrieIdGenerator, }; use crate::exec::StorageKey; @@ -27,7 +27,7 @@ use sp_std::prelude::*; use sp_io::hashing::blake2_256; use sp_runtime::traits::{Bounded, Zero}; use frame_support::traits::{Currency, Get, Imbalance, SignedImbalance}; -use frame_support::{storage::child, StorageMap}; +use frame_support::{storage::child, StorageMap, storage::child::ChildInfo}; use frame_system; // Note: we don't provide Option because we can't create @@ -108,7 +108,12 @@ pub trait AccountDb { /// /// Trie id is None iff account doesn't have an associated trie id in >. /// Because DirectAccountDb bypass the lookup for this association. - fn get_storage(&self, account: &T::AccountId, trie_id: Option<&TrieId>, location: &StorageKey) -> Option>; + fn get_storage( + &self, + account: &T::AccountId, + trie_id: Option<&ChildInfo>, + location: &StorageKey + ) -> Option>; /// If account has an alive contract then return the code hash associated. fn get_code_hash(&self, account: &T::AccountId) -> Option>; /// If account has an alive contract then return the rent allowance associated. @@ -125,10 +130,10 @@ impl AccountDb for DirectAccountDb { fn get_storage( &self, _account: &T::AccountId, - trie_id: Option<&TrieId>, + trie_id: Option<&ChildInfo>, location: &StorageKey ) -> Option> { - trie_id.and_then(|id| child::get_raw(&crate::child_trie_info(&id[..]), &blake2_256(location))) + trie_id.and_then(|child_info| child::get_raw(child_info, &blake2_256(location))) } fn get_code_hash(&self, account: &T::AccountId) -> Option> { >::get(account).and_then(|i| i.as_alive().map(|i| i.code_hash)) @@ -210,18 +215,19 @@ impl AccountDb for DirectAccountDb { new_info.last_write = Some(>::block_number()); } + let child_info = &new_info.child_trie_info(); for (k, v) in changed.storage.into_iter() { if let Some(value) = child::get_raw( - &new_info.child_trie_info(), + child_info, &blake2_256(&k), ) { new_info.storage_size -= value.len() as u32; } if let Some(value) = v { new_info.storage_size += value.len() as u32; - child::put_raw(&new_info.child_trie_info(), &blake2_256(&k), &value[..]); + child::put_raw(child_info, &blake2_256(&k), &value[..]); } else { - child::kill(&new_info.child_trie_info(), &blake2_256(&k)); + child::kill(child_info, &blake2_256(&k)); } } @@ -326,7 +332,7 @@ impl<'a, T: Trait> AccountDb for OverlayAccountDb<'a, T> { fn get_storage( &self, account: &T::AccountId, - trie_id: Option<&TrieId>, + trie_id: Option<&ChildInfo>, location: &StorageKey ) -> Option> { self.local diff --git a/frame/contracts/src/exec.rs b/frame/contracts/src/exec.rs index 402622331d0ec..d8b42b2f9ecae 100644 --- a/frame/contracts/src/exec.rs +++ b/frame/contracts/src/exec.rs @@ -26,6 +26,7 @@ use frame_support::{ storage::unhashed, dispatch::DispatchError, traits::{WithdrawReason, Currency, Time, Randomness}, }; +use sp_core::storage::ChildInfo; pub type AccountIdOf = ::AccountId; pub type CallOf = ::Call; @@ -291,7 +292,7 @@ pub enum DeferredAction { pub struct ExecutionContext<'a, T: Trait + 'a, V, L> { pub caller: Option<&'a ExecutionContext<'a, T, V, L>>, pub self_account: T::AccountId, - pub self_trie_id: Option, + pub self_trie_info: Option, pub overlay: OverlayAccountDb<'a, T>, pub depth: usize, pub deferred: Vec>, @@ -315,7 +316,7 @@ where pub fn top_level(origin: T::AccountId, cfg: &'a Config, vm: &'a V, loader: &'a L) -> Self { ExecutionContext { caller: None, - self_trie_id: None, + self_trie_info: None, self_account: origin, overlay: OverlayAccountDb::::new(&DirectAccountDb), depth: 0, @@ -328,12 +329,12 @@ where } } - fn nested<'b, 'c: 'b>(&'c self, dest: T::AccountId, trie_id: Option) + fn nested<'b, 'c: 'b>(&'c self, dest: T::AccountId, trie_info: Option) -> ExecutionContext<'b, T, V, L> { ExecutionContext { caller: Some(self), - self_trie_id: trie_id, + self_trie_info: trie_info, self_account: dest, overlay: OverlayAccountDb::new(&self.overlay), depth: self.depth + 1, @@ -575,7 +576,9 @@ where where F: FnOnce(&mut ExecutionContext) -> ExecResult { let (output, change_set, deferred) = { - let mut nested = self.nested(dest, trie_id); + let mut nested = self.nested(dest, trie_id.map(|trie_id| { + crate::child_trie_info(&trie_id) + })); let output = func(&mut nested)?; (output, nested.overlay.into_change_set(), nested.deferred) }; @@ -735,7 +738,12 @@ where type T = T; fn get_storage(&self, key: &StorageKey) -> Option> { - self.ctx.overlay.get_storage(&self.ctx.self_account, self.ctx.self_trie_id.as_ref(), key) + let trie_id = self.ctx.self_trie_info.as_ref(); + self.ctx.overlay.get_storage( + &self.ctx.self_account, + trie_id, + key, + ) } fn set_storage(&mut self, key: StorageKey, value: Option>) -> Result<(), &'static str> { diff --git a/frame/contracts/src/lib.rs b/frame/contracts/src/lib.rs index 2513f2fb618e2..99f42f5752238 100644 --- a/frame/contracts/src/lib.rs +++ b/frame/contracts/src/lib.rs @@ -693,10 +693,11 @@ impl Module { .get_alive() .ok_or(ContractAccessError::IsTombstone)?; + let child_info = child_trie_info(&contract_info.trie_id); let maybe_value = AccountDb::::get_storage( &DirectAccountDb, &address, - Some(&contract_info.trie_id), + Some(&child_info), &key, ); Ok(maybe_value) diff --git a/frame/contracts/src/tests.rs b/frame/contracts/src/tests.rs index 452a4517dbe68..bd1c23b7ff44f 100644 --- a/frame/contracts/src/tests.rs +++ b/frame/contracts/src/tests.rs @@ -312,6 +312,10 @@ fn account_removal_does_not_remove_storage() { ExtBuilder::default().existential_deposit(100).build().execute_with(|| { let trie_id1 = ::TrieIdGenerator::trie_id(&1); let trie_id2 = ::TrieIdGenerator::trie_id(&2); + let child_info1 = crate::child_trie_info(trie_id1.as_ref()); + let child_info2 = crate::child_trie_info(trie_id2.as_ref()); + let child_info1 = Some(&child_info1); + let child_info2 = Some(&child_info2); let key1 = &[1; 32]; let key2 = &[2; 32]; @@ -360,20 +364,20 @@ fn account_removal_does_not_remove_storage() { // Verify that no entries are removed. { assert_eq!( - >::get_storage(&DirectAccountDb, &1, Some(&trie_id1), key1), + >::get_storage(&DirectAccountDb, &1, child_info1, key1), Some(b"1".to_vec()) ); assert_eq!( - >::get_storage(&DirectAccountDb, &1, Some(&trie_id1), key2), + >::get_storage(&DirectAccountDb, &1, child_info1, key2), Some(b"2".to_vec()) ); assert_eq!( - >::get_storage(&DirectAccountDb, &2, Some(&trie_id2), key1), + >::get_storage(&DirectAccountDb, &2, child_info2, key1), Some(b"3".to_vec()) ); assert_eq!( - >::get_storage(&DirectAccountDb, &2, Some(&trie_id2), key2), + >::get_storage(&DirectAccountDb, &2, child_info2, key2), Some(b"4".to_vec()) ); } diff --git a/primitives/api/src/lib.rs b/primitives/api/src/lib.rs index a3fc15ba7e249..271899004d8bc 100644 --- a/primitives/api/src/lib.rs +++ b/primitives/api/src/lib.rs @@ -43,7 +43,7 @@ pub use sp_state_machine::{ pub use sp_core::NativeOrEncoded; #[doc(hidden)] #[cfg(feature = "std")] -pub use hash_db::Hasher; +pub use sp_state_machine::{Hasher, InnerHasher}; #[doc(hidden)] #[cfg(not(feature = "std"))] pub use sp_core::to_substrate_wasm_fn_return_value; diff --git a/primitives/core/src/lib.rs b/primitives/core/src/lib.rs index 8d5ad7daaec83..18d25f1333e48 100644 --- a/primitives/core/src/lib.rs +++ b/primitives/core/src/lib.rs @@ -79,7 +79,8 @@ pub use changes_trie::{ChangesTrieConfiguration, ChangesTrieConfigurationRange}; #[cfg(feature = "full_crypto")] pub use crypto::{DeriveJunction, Pair, Public}; -pub use hash_db::Hasher; +pub use hash_db::Hasher as InnerHasher; +pub use hash_db::{Prefix, EMPTY_PREFIX}; #[cfg(feature = "std")] pub use self::hasher::blake2::Blake2Hasher; @@ -357,3 +358,33 @@ macro_rules! impl_maybe_marker { )+ } } + +/// Technical trait to avoid calculating empty root. +/// This assumes (same wrong asumption as for hashdb trait), +/// an empty node is `[0u8]`. +pub trait Hasher: InnerHasher { + /// Value for an empty root node, this + /// is the hash of `[0u8]` value. + const EMPTY_ROOT: &'static [u8]; +} + +#[cfg(feature = "std")] +impl Hasher for Blake2Hasher { + const EMPTY_ROOT: &'static [u8] = &[ + 3, 23, 10, 46, 117, 151, 183, 183, 227, 216, + 76, 5, 57, 29, 19, 154, 98, 177, 87, 231, + 135, 134, 216, 192, 130, 242, 157, 207, 76, 17, + 19, 20, + ]; +} + +#[cfg(test)] +mod test { + use super::{Blake2Hasher, Hasher, InnerHasher}; + + #[test] + fn empty_root_const() { + let empty = Blake2Hasher::hash(&[0u8]); + assert_eq!(Blake2Hasher::EMPTY_ROOT, empty.as_ref()); + } +} diff --git a/primitives/runtime/src/traits.rs b/primitives/runtime/src/traits.rs index d843bdc478c49..0bc39baad6641 100644 --- a/primitives/runtime/src/traits.rs +++ b/primitives/runtime/src/traits.rs @@ -25,7 +25,7 @@ use std::fmt::Display; use std::str::FromStr; #[cfg(feature = "std")] use serde::{Serialize, Deserialize, de::DeserializeOwned}; -use sp_core::{self, Hasher, TypeId, RuntimeDebug}; +use sp_core::{self, InnerHasher, Hasher, TypeId, RuntimeDebug}; use crate::codec::{Codec, Encode, Decode}; use crate::transaction_validity::{ ValidTransaction, TransactionSource, TransactionValidity, TransactionValidityError, @@ -326,19 +326,20 @@ impl::Output> { +pub trait Hash: 'static + MaybeSerializeDeserialize + Debug + Clone + Eq + + PartialEq + InnerHasher::Output> + Hasher { /// The hash type produced. type Output: Member + MaybeSerializeDeserialize + Debug + sp_std::hash::Hash + AsRef<[u8]> + AsMut<[u8]> + Copy + Default + Encode + Decode; /// Produce the hash of some byte-slice. fn hash(s: &[u8]) -> Self::Output { - ::hash(s) + ::hash(s) } /// Produce the hash of some codec-encodable value. fn hash_of(s: &S) -> Self::Output { - Encode::using_encoded(s, ::hash) + Encode::using_encoded(s, ::hash) } /// The ordered Patricia tree root of the given `input`. @@ -353,7 +354,7 @@ pub trait Hash: 'static + MaybeSerializeDeserialize + Debug + Clone + Eq + Parti #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] pub struct BlakeTwo256; -impl Hasher for BlakeTwo256 { +impl InnerHasher for BlakeTwo256 { type Out = sp_core::H256; type StdHasher = hash256_std_hasher::Hash256StdHasher; const LENGTH: usize = 32; @@ -375,6 +376,15 @@ impl Hash for BlakeTwo256 { } } +impl Hasher for BlakeTwo256 { + const EMPTY_ROOT: &'static [u8] = &[ + 3, 23, 10, 46, 117, 151, 183, 183, 227, 216, + 76, 5, 57, 29, 19, 154, 98, 177, 87, 231, + 135, 134, 216, 192, 130, 242, 157, 207, 76, 17, + 19, 20, + ]; +} + /// Something that can be checked for equality and printed out to a debug channel if bad. pub trait CheckEqual { /// Perform the equality check. @@ -1416,4 +1426,10 @@ mod tests { assert!(signature.verify(msg, &pair.public())); assert!(signature.verify(msg, &pair.public())); } + + #[test] + fn empty_root_const() { + let empty = ::hash(&[0u8]); + assert_eq!(BlakeTwo256::EMPTY_ROOT, empty.as_ref()); + } } diff --git a/primitives/state-machine/src/backend.rs b/primitives/state-machine/src/backend.rs index df8f810ceb7ce..0f3af4466c69e 100644 --- a/primitives/state-machine/src/backend.rs +++ b/primitives/state-machine/src/backend.rs @@ -17,12 +17,12 @@ //! State machine backends. These manage the code and storage of contracts. use log::warn; -use hash_db::Hasher; +use sp_core::{Hasher, InnerHasher}; use codec::{Decode, Encode}; -use sp_core::{traits::RuntimeCode, storage::{ChildInfo, well_known_keys}}; +use sp_core::{traits::RuntimeCode, + storage::{ChildInfo, ChildrenMap, well_known_keys, PrefixedStorageKey}}; use sp_trie::{TrieMut, MemoryDB, trie_types::TrieDBMut}; - use crate::{ trie_backend::TrieBackend, trie_backend_essence::TrieBackendStorage, @@ -170,8 +170,9 @@ pub trait Backend: std::fmt::Debug { fn full_storage_root( &self, delta: I1, - child_deltas: I2) - -> (H::Out, Self::Transaction) + child_deltas: I2, + return_child_roots: bool, + ) -> (H::Out, Self::Transaction, Vec<(PrefixedStorageKey, Option)>) where I1: IntoIterator)>, I2i: IntoIterator)>, @@ -180,6 +181,7 @@ pub trait Backend: std::fmt::Debug { { let mut txs: Self::Transaction = Default::default(); let mut child_roots: Vec<_> = Default::default(); + let mut result_child_roots: Vec<_> = Default::default(); // child first for (child_info, child_delta) in child_deltas { let (child_root, empty, child_txs) = @@ -187,16 +189,24 @@ pub trait Backend: std::fmt::Debug { let prefixed_storage_key = child_info.prefixed_storage_key(); txs.consolidate(child_txs); if empty { + if return_child_roots { + result_child_roots.push((prefixed_storage_key.clone(), None)); + } child_roots.push((prefixed_storage_key.into_inner(), None)); } else { - child_roots.push((prefixed_storage_key.into_inner(), Some(child_root.encode()))); + if return_child_roots { + child_roots.push((prefixed_storage_key.clone().into_inner(), Some(child_root.encode()))); + result_child_roots.push((prefixed_storage_key, Some(child_root))); + } else { + child_roots.push((prefixed_storage_key.into_inner(), Some(child_root.encode()))); + } } } let (root, parent_txs) = self.storage_root( delta.into_iter().chain(child_roots.into_iter()) ); txs.consolidate(parent_txs); - (root, txs) + (root, txs, result_child_roots) } /// Register stats from overlay of state machine. @@ -327,6 +337,24 @@ impl Consolidate for Vec<( } } +impl Consolidate for ChildrenMap { + fn consolidate(&mut self, other: Self) { + self.extend_with(other.into_iter(), Consolidate::consolidate) + } +} + +impl Consolidate for Option { + fn consolidate(&mut self, other: Self) { + if let Some(v) = self.as_mut() { + if let Some(other) = other { + v.consolidate(other); + } + } else { + *self = other; + } + } +} + impl> Consolidate for sp_trie::GenericMemoryDB { fn consolidate(&mut self, other: Self) { sp_trie::GenericMemoryDB::consolidate(self, other) @@ -339,7 +367,7 @@ pub(crate) fn insert_into_memory_db(mdb: &mut MemoryDB, input: I) -> Op H: Hasher, I: IntoIterator, { - let mut root = ::Out::default(); + let mut root = ::Out::default(); { let mut trie = TrieDBMut::::new(mdb, &mut root); for (key, value) in input { diff --git a/primitives/state-machine/src/basic.rs b/primitives/state-machine/src/basic.rs index 7f26085958e97..907676a74f1ff 100644 --- a/primitives/state-machine/src/basic.rs +++ b/primitives/state-machine/src/basic.rs @@ -360,7 +360,7 @@ mod tests { children_default: map![ child_info.storage_key().to_vec() => StorageChild { data: map![ b"doe".to_vec() => b"reindeer".to_vec() ], - child_info: child_info.to_owned(), + child_info: child_info.clone(), } ] }); diff --git a/primitives/state-machine/src/changes_trie/build.rs b/primitives/state-machine/src/changes_trie/build.rs index 45535204e0884..d128d136a6336 100644 --- a/primitives/state-machine/src/changes_trie/build.rs +++ b/primitives/state-machine/src/changes_trie/build.rs @@ -19,7 +19,7 @@ use std::collections::{BTreeMap, BTreeSet}; use std::collections::btree_map::Entry; use codec::{Decode, Encode}; -use hash_db::Hasher; +use sp_core::{Hasher, InnerHasher}; use num_traits::One; use crate::{ StorageKey, @@ -280,6 +280,9 @@ fn prepare_digest_input<'a, H, Number>( return Ok((map, child_map)); } + // change trie content are all stored as top_trie (default child trie with empty keyspace) + let child_info = sp_core::storage::ChildInfo::top_trie(); + let child_info = &child_info; let mut children_roots = BTreeMap::::new(); { let trie_storage = TrieBackendEssence::<_, H>::new( @@ -287,21 +290,21 @@ fn prepare_digest_input<'a, H, Number>( trie_root, ); - trie_storage.for_key_values_with_prefix(&child_prefix, |key, value| + trie_storage.for_key_values_with_prefix(child_info, &child_prefix, |key, value| if let Ok(InputKey::ChildIndex::(trie_key)) = Decode::decode(&mut &key[..]) { if let Ok(value) = >::decode(&mut &value[..]) { - let mut trie_root = ::Out::default(); + let mut trie_root = ::Out::default(); trie_root.as_mut().copy_from_slice(&value[..]); children_roots.insert(trie_key.storage_key, trie_root); } }); - trie_storage.for_keys_with_prefix(&extrinsic_prefix, |key| + trie_storage.for_keys_with_prefix(child_info, &extrinsic_prefix, |key| if let Ok(InputKey::ExtrinsicIndex::(trie_key)) = Decode::decode(&mut &key[..]) { insert_to_map(&mut map, trie_key.key); }); - trie_storage.for_keys_with_prefix(&digest_prefix, |key| + trie_storage.for_keys_with_prefix(child_info, &digest_prefix, |key| if let Ok(InputKey::DigestIndex::(trie_key)) = Decode::decode(&mut &key[..]) { insert_to_map(&mut map, trie_key.key); }); @@ -318,12 +321,12 @@ fn prepare_digest_input<'a, H, Number>( crate::changes_trie::TrieBackendStorageAdapter(storage), trie_root, ); - trie_storage.for_keys_with_prefix(&extrinsic_prefix, |key| + trie_storage.for_keys_with_prefix(child_info, &extrinsic_prefix, |key| if let Ok(InputKey::ExtrinsicIndex::(trie_key)) = Decode::decode(&mut &key[..]) { insert_to_map(&mut map, trie_key.key); }); - trie_storage.for_keys_with_prefix(&digest_prefix, |key| + trie_storage.for_keys_with_prefix(child_info, &digest_prefix, |key| if let Ok(InputKey::DigestIndex::(trie_key)) = Decode::decode(&mut &key[..]) { insert_to_map(&mut map, trie_key.key); }); @@ -434,13 +437,13 @@ mod test { value: Some(vec![200]), extrinsics: Some(vec![0, 2].into_iter().collect()) }) - ].into_iter().collect(), child_info_1.to_owned())), + ].into_iter().collect(), child_info_1.clone())), (child_trie_key2, (vec![ (vec![100], OverlayedValue { value: Some(vec![200]), extrinsics: Some(vec![0, 2].into_iter().collect()) }) - ].into_iter().collect(), child_info_2.to_owned())), + ].into_iter().collect(), child_info_2)), ].into_iter().collect() }, committed: OverlayedChangeSet { top: vec![ @@ -463,7 +466,7 @@ mod test { value: Some(vec![202]), extrinsics: Some(vec![3].into_iter().collect()) }) - ].into_iter().collect(), child_info_1.to_owned())), + ].into_iter().collect(), child_info_1)), ].into_iter().collect(), }, collect_extrinsics: true, diff --git a/primitives/state-machine/src/changes_trie/changes_iterator.rs b/primitives/state-machine/src/changes_trie/changes_iterator.rs index f5a936069ba40..c1e3266b7ca11 100644 --- a/primitives/state-machine/src/changes_trie/changes_iterator.rs +++ b/primitives/state-machine/src/changes_trie/changes_iterator.rs @@ -20,7 +20,8 @@ use std::cell::RefCell; use std::collections::VecDeque; use codec::{Decode, Encode, Codec}; -use hash_db::Hasher; +use sp_core::Hasher; +use sp_core::storage::ChildInfo; use num_traits::Zero; use sp_core::storage::PrefixedStorageKey; use sp_trie::Recorder; @@ -68,6 +69,7 @@ pub fn key_changes<'a, H: Hasher, Number: BlockNumber>( _hasher: ::std::marker::PhantomData::::default(), }, + child_info: ChildInfo::top_trie(), }) } @@ -178,6 +180,7 @@ pub fn key_changes_proof_check_with_db<'a, H: Hasher, Number: BlockNumber>( _hasher: ::std::marker::PhantomData::::default(), }, + child_info: ChildInfo::top_trie(), }.collect() } @@ -315,6 +318,10 @@ pub struct DrilldownIterator<'a, H, Number> H::Out: 'a, { essence: DrilldownIteratorEssence<'a, H, Number>, + /// This is always top trie info, but it cannot be + /// statically instantiated at the time (vec of null + /// size could be in theory). + child_info: ChildInfo, } impl<'a, H: Hasher, Number: BlockNumber> Iterator for DrilldownIterator<'a, H, Number> @@ -323,8 +330,11 @@ impl<'a, H: Hasher, Number: BlockNumber> Iterator for DrilldownIterator<'a, H, N type Item = Result<(Number, u32), String>; fn next(&mut self) -> Option { + let child_info = &self.child_info; self.essence.next(|storage, root, key| - TrieBackendEssence::<_, H>::new(TrieBackendAdapter::new(storage), root).storage(key)) + TrieBackendEssence::<_, H>::new(TrieBackendAdapter::new(storage), root) + .storage(child_info, key) + ) } } diff --git a/primitives/state-machine/src/changes_trie/mod.rs b/primitives/state-machine/src/changes_trie/mod.rs index ee6c6778e0aad..bed84726e5a03 100644 --- a/primitives/state-machine/src/changes_trie/mod.rs +++ b/primitives/state-machine/src/changes_trie/mod.rs @@ -67,7 +67,9 @@ pub use self::prune::prune; use std::collections::{HashMap, HashSet}; use std::convert::TryInto; -use hash_db::{Hasher, Prefix}; +use hash_db::Prefix; +use sp_core::Hasher; +use sp_core::storage::ChildInfo; use num_traits::{One, Zero}; use codec::{Decode, Encode}; use sp_core; @@ -160,16 +162,26 @@ pub trait Storage: RootsStorage { functor: &mut dyn FnMut(&HashMap, HashSet>), ) -> bool; /// Get a trie node. - fn get(&self, key: &H::Out, prefix: Prefix) -> Result, String>; + fn get( + &self, + key: &H::Out, + prefix: Prefix, + ) -> Result, String>; } /// Changes trie storage -> trie backend essence adapter. pub struct TrieBackendStorageAdapter<'a, H: Hasher, Number: BlockNumber>(pub &'a dyn Storage); -impl<'a, H: Hasher, N: BlockNumber> crate::TrieBackendStorage for TrieBackendStorageAdapter<'a, H, N> { +impl<'a, H: Hasher, N: BlockNumber> crate::TrieBackendStorageRef for TrieBackendStorageAdapter<'a, H, N> { type Overlay = sp_trie::MemoryDB; - fn get(&self, key: &H::Out, prefix: Prefix) -> Result, String> { + fn get( + &self, + child_info: &ChildInfo, + key: &H::Out, + prefix: Prefix, + ) -> Result, String> { + debug_assert!(child_info.is_top_trie()); self.0.get(key, prefix) } } diff --git a/primitives/state-machine/src/changes_trie/prune.rs b/primitives/state-machine/src/changes_trie/prune.rs index 05555df305b7c..088773c72cabd 100644 --- a/primitives/state-machine/src/changes_trie/prune.rs +++ b/primitives/state-machine/src/changes_trie/prune.rs @@ -16,7 +16,7 @@ //! Changes trie pruning-related functions. -use hash_db::Hasher; +use sp_core::{Hasher, InnerHasher}; use sp_trie::Recorder; use log::warn; use num_traits::One; @@ -65,10 +65,11 @@ pub fn prune( ); let child_prefix = ChildIndex::key_neutral_prefix(block.clone()); let mut children_roots = Vec::new(); - trie_storage.for_key_values_with_prefix(&child_prefix, |key, value| { + let child_info = sp_core::storage::ChildInfo::top_trie(); + trie_storage.for_key_values_with_prefix(&child_info, &child_prefix, |key, value| { if let Ok(InputKey::ChildIndex::(_trie_key)) = Decode::decode(&mut &key[..]) { if let Ok(value) = >::decode(&mut &value[..]) { - let mut trie_root = ::Out::default(); + let mut trie_root = ::Out::default(); trie_root.as_mut().copy_from_slice(&value[..]); children_roots.push(trie_root); } @@ -100,7 +101,7 @@ fn prune_trie( backend: &TrieBackendEssence::new(TrieBackendAdapter::new(storage), root), proof_recorder: &mut proof_recorder, }; - trie.record_all_keys(); + trie.record_all_top_trie_keys(); } // all nodes of this changes trie should be pruned diff --git a/primitives/state-machine/src/changes_trie/storage.rs b/primitives/state-machine/src/changes_trie/storage.rs index 81651dd2e719b..df731b699eb0f 100644 --- a/primitives/state-machine/src/changes_trie/storage.rs +++ b/primitives/state-machine/src/changes_trie/storage.rs @@ -17,14 +17,16 @@ //! Changes trie storage utilities. use std::collections::{BTreeMap, HashSet, HashMap}; -use hash_db::{Hasher, Prefix, EMPTY_PREFIX}; +use hash_db::{Prefix, EMPTY_PREFIX}; +use sp_core::Hasher; use sp_core::storage::PrefixedStorageKey; +use sp_core::storage::ChildInfo; use sp_trie::DBValue; use sp_trie::MemoryDB; use parking_lot::RwLock; use crate::{ StorageKey, - trie_backend_essence::TrieBackendStorage, + trie_backend_essence::TrieBackendStorageRef, changes_trie::{BuildCache, RootsStorage, Storage, AnchorBlockId, BlockNumber}, }; @@ -188,8 +190,12 @@ impl Storage for InMemoryStorage Result, String> { - MemoryDB::::get(&self.data.read().mdb, key, prefix) + fn get( + &self, + key: &H::Out, + prefix: Prefix, + ) -> Result, String> { + MemoryDB::::get(&self.data.read().mdb, &ChildInfo::top_trie(), key, prefix) } } @@ -199,14 +205,20 @@ impl<'a, H: Hasher, Number: BlockNumber> TrieBackendAdapter<'a, H, Number> { } } -impl<'a, H, Number> TrieBackendStorage for TrieBackendAdapter<'a, H, Number> +impl<'a, H, Number> TrieBackendStorageRef for TrieBackendAdapter<'a, H, Number> where Number: BlockNumber, H: Hasher, { type Overlay = MemoryDB; - fn get(&self, key: &H::Out, prefix: Prefix) -> Result, String> { + fn get( + &self, + child_info: &ChildInfo, + key: &H::Out, + prefix: Prefix, + ) -> Result, String> { + debug_assert!(child_info.is_top_trie()); self.storage.get(key, prefix) } } diff --git a/primitives/state-machine/src/ext.rs b/primitives/state-machine/src/ext.rs index 399bfc69d864f..6d5d248eea683 100644 --- a/primitives/state-machine/src/ext.rs +++ b/primitives/state-machine/src/ext.rs @@ -22,8 +22,8 @@ use crate::{ changes_trie::State as ChangesTrieState, }; -use hash_db::Hasher; use sp_core::{ + Hasher, storage::{well_known_keys::is_child_storage_key, ChildInfo}, traits::Externalities, hexdisplay::HexDisplay, }; @@ -184,6 +184,9 @@ where child_info: &ChildInfo, key: &[u8], ) -> Option { + if child_info.is_top_trie() { + return self.storage(key); + } let _guard = sp_panic_handler::AbortGuard::force_abort(); let result = self.overlay .child_storage(child_info, key) @@ -208,6 +211,9 @@ where child_info: &ChildInfo, key: &[u8], ) -> Option> { + if child_info.is_top_trie() { + return self.storage_hash(key); + } let _guard = sp_panic_handler::AbortGuard::force_abort(); let result = self.overlay .child_storage(child_info, key) @@ -248,6 +254,9 @@ where child_info: &ChildInfo, key: &[u8], ) -> bool { + if child_info.is_top_trie() { + return self.exists_storage(key); + } let _guard = sp_panic_handler::AbortGuard::force_abort(); let result = match self.overlay.child_storage(child_info, key) { @@ -286,6 +295,9 @@ where child_info: &ChildInfo, key: &[u8], ) -> Option { + if child_info.is_top_trie() { + return self.next_storage_key(key); + } let next_backend_key = self.backend .next_child_storage_key(child_info, key) .expect(EXT_NOT_ALLOWED_TO_FAIL); @@ -330,6 +342,9 @@ where key: StorageKey, value: Option, ) { + if child_info.is_top_trie() { + return self.place_storage(key, value); + } trace!(target: "state-trace", "{:04x}: PutChild({}) {}={:?}", self.id, HexDisplay::from(&child_info.storage_key()), @@ -346,6 +361,10 @@ where &mut self, child_info: &ChildInfo, ) { + if child_info.is_top_trie() { + trace!(target: "state-trace", "Ignoring kill_child_storage on top trie"); + return; + } trace!(target: "state-trace", "{:04x}: KillChild({})", self.id, HexDisplay::from(&child_info.storage_key()), @@ -382,6 +401,10 @@ where child_info: &ChildInfo, prefix: &[u8], ) { + if child_info.is_top_trie() { + return self.clear_prefix(prefix); + } + trace!(target: "state-trace", "{:04x}: ClearChildPrefix({}) {}", self.id, HexDisplay::from(&child_info.storage_key()), @@ -423,22 +446,22 @@ where let storage_key = child_info.storage_key(); let prefixed_storage_key = child_info.prefixed_storage_key(); if self.storage_transaction_cache.transaction_storage_root.is_some() { - let root = self - .storage(prefixed_storage_key.as_slice()) - .and_then(|k| Decode::decode(&mut &k[..]).ok()) + let root = self.storage_transaction_cache.transaction_child_storage_root + .get(&prefixed_storage_key) + .map(|root| root.encode()) .unwrap_or( - empty_child_trie_root::>() + empty_child_trie_root::>().encode() ); trace!(target: "state-trace", "{:04x}: ChildRoot({}) (cached) {}", self.id, HexDisplay::from(&storage_key), HexDisplay::from(&root.as_ref()), ); - root.encode() + root } else { if let Some(child_info) = self.overlay.default_child_info(storage_key).cloned() { - let (root, is_empty, _) = { + let (root, _is_empty, _) = { let delta = self.overlay.committed.children_default.get(storage_key) .into_iter() .flat_map(|(map, _)| map.clone().into_iter().map(|(k, v)| (k, v.value))) @@ -452,16 +475,6 @@ where }; let root = root.encode(); - // We store update in the overlay in order to be able to use 'self.storage_transaction' - // cache. This is brittle as it rely on Ext only querying the trie backend for - // storage root. - // A better design would be to manage 'child_storage_transaction' in a - // similar way as 'storage_transaction' but for each child trie. - if is_empty { - self.overlay.set_storage(prefixed_storage_key.into_inner(), None); - } else { - self.overlay.set_storage(prefixed_storage_key.into_inner(), Some(root.clone())); - } trace!(target: "state-trace", "{:04x}: ChildRoot({}) {}", self.id, @@ -711,7 +724,7 @@ mod tests { vec![20] => vec![20], vec![40] => vec![40] ], - child_info: child_info.to_owned(), + child_info: child_info.clone(), } ], }.into(); @@ -741,6 +754,8 @@ mod tests { #[test] fn child_storage_works() { + use sp_core::InnerHasher; + let child_info = ChildInfo::new_default(b"Child1"); let child_info = &child_info; let mut cache = StorageTransactionCache::default(); @@ -756,7 +771,7 @@ mod tests { vec![20] => vec![20], vec![30] => vec![40] ], - child_info: child_info.to_owned(), + child_info: child_info.clone(), } ], }.into(); diff --git a/primitives/state-machine/src/in_memory_backend.rs b/primitives/state-machine/src/in_memory_backend.rs index 83126abbf78e0..66fdada3f4a0d 100644 --- a/primitives/state-machine/src/in_memory_backend.rs +++ b/primitives/state-machine/src/in_memory_backend.rs @@ -23,9 +23,9 @@ use crate::{ stats::UsageInfo, }; use std::{error, fmt, collections::{BTreeMap, HashMap}, marker::PhantomData, ops}; -use hash_db::Hasher; +use sp_core::{Hasher, InnerHasher}; use sp_trie::{ - MemoryDB, child_trie_root, empty_child_trie_root, TrieConfiguration, trie_types::Layout, + MemoryDB, empty_child_trie_root, TrieConfiguration, trie_types::Layout, }; use codec::Codec; use sp_core::storage::{ChildInfo, ChildType, Storage}; @@ -228,7 +228,7 @@ impl Backend for InMemory where H::Out: Codec { fn storage_root(&self, delta: I) -> (H::Out, Self::Transaction) where I: IntoIterator, Option>)>, - ::Out: Ord, + ::Out: Ord, { let existing_pairs = self.inner.get(&None) .into_iter() @@ -263,7 +263,7 @@ impl Backend for InMemory where H::Out: Codec { .flat_map(|map| map.iter().map(|(k, v)| (k.clone(), Some(v.clone())))); let transaction: Vec<_> = delta.into_iter().collect(); - let root = child_trie_root::, _, _, _>( + let root = Layout::::trie_root( existing_pairs.chain(transaction.iter().cloned()) .collect::>() .into_iter() diff --git a/primitives/state-machine/src/lib.rs b/primitives/state-machine/src/lib.rs index 1c0007c5f9108..00a117c836d99 100644 --- a/primitives/state-machine/src/lib.rs +++ b/primitives/state-machine/src/lib.rs @@ -20,7 +20,7 @@ use std::{fmt, result, collections::HashMap, panic::UnwindSafe}; use log::{warn, trace}; -use hash_db::Hasher; +pub use sp_core::{Hasher, InnerHasher}; use codec::{Decode, Encode, Codec}; use sp_core::{ storage::ChildInfo, NativeOrEncoded, NeverNativeValue, hexdisplay::HexDisplay, @@ -69,7 +69,7 @@ pub use overlayed_changes::{ pub use proving_backend::{ create_proof_check_backend, ProofRecorder, ProvingBackend, ProvingBackendRecorder, }; -pub use trie_backend_essence::{TrieBackendStorage, Storage}; +pub use trie_backend_essence::{TrieBackendStorage, TrieBackendStorageRef, Storage}; pub use trie_backend::TrieBackend; pub use error::{Error, ExecutionError}; pub use in_memory_backend::InMemory as InMemoryBackend; @@ -84,7 +84,7 @@ pub type DefaultHandler = fn(CallResult, CallResult) -> CallRe /// Type of changes trie transaction. pub type ChangesTrieTransaction = ( MemoryDB, - ChangesTrieCacheAction<::Out, N>, + ChangesTrieCacheAction<::Out, N>, ); /// Strategy for executing a call into the runtime. @@ -1090,39 +1090,4 @@ mod tests { vec![(b"value2".to_vec(), None)], ); } - - #[test] - fn child_storage_uuid() { - - let child_info_1 = ChildInfo::new_default(b"sub_test1"); - let child_info_2 = ChildInfo::new_default(b"sub_test2"); - - use crate::trie_backend::tests::test_trie; - let mut overlay = OverlayedChanges::default(); - - let mut transaction = { - let backend = test_trie(); - let mut cache = StorageTransactionCache::default(); - let mut ext = Ext::new( - &mut overlay, - &mut cache, - &backend, - changes_trie::disabled_state::<_, u64>(), - None, - ); - ext.set_child_storage(&child_info_1, b"abc".to_vec(), b"def".to_vec()); - ext.set_child_storage(&child_info_2, b"abc".to_vec(), b"def".to_vec()); - ext.storage_root(); - cache.transaction.unwrap() - }; - let mut duplicate = false; - for (k, (value, rc)) in transaction.drain().iter() { - // look for a key inserted twice: transaction rc is 2 - if *rc == 2 { - duplicate = true; - println!("test duplicate for {:?} {:?}", k, value); - } - } - assert!(!duplicate); - } } diff --git a/primitives/state-machine/src/overlayed_changes.rs b/primitives/state-machine/src/overlayed_changes.rs index f57d13ee3ffec..c9b6a6f6defc2 100644 --- a/primitives/state-machine/src/overlayed_changes.rs +++ b/primitives/state-machine/src/overlayed_changes.rs @@ -29,10 +29,10 @@ use crate::{ use std::iter::FromIterator; use std::collections::{HashMap, BTreeMap, BTreeSet}; use codec::{Decode, Encode}; -use sp_core::storage::{well_known_keys::EXTRINSIC_INDEX, ChildInfo}; +use sp_core::storage::{well_known_keys::EXTRINSIC_INDEX, ChildInfo, PrefixedStorageKey}; use std::{mem, ops}; -use hash_db::Hasher; +use sp_core::Hasher; /// Storage key. pub type StorageKey = Vec; @@ -44,7 +44,7 @@ pub type StorageValue = Vec; pub type StorageCollection = Vec<(StorageKey, Option)>; /// In memory arrays of storage values for multiple child tries. -pub type ChildStorageCollection = Vec<(StorageKey, StorageCollection)>; +pub type ChildStorageCollection = Vec<(StorageKey, StorageCollection, ChildInfo)>; /// The overlayed changes to state to be queried on top of the backend. /// @@ -133,6 +133,8 @@ pub struct StorageTransactionCache { pub(crate) transaction: Option, /// The storage root after applying the transaction. pub(crate) transaction_storage_root: Option, + /// The storage child roots after applying the transaction. + pub(crate) transaction_child_storage_root: BTreeMap>, /// Contains the changes trie transaction. pub(crate) changes_trie_transaction: Option>>, /// The storage root after applying the changes trie transaction. @@ -151,6 +153,7 @@ impl Default for StorageTransactionCache Self { transaction: None, transaction_storage_root: None, + transaction_child_storage_root: Default::default(), changes_trie_transaction: None, changes_trie_transaction_storage_root: None, } @@ -507,7 +510,8 @@ impl OverlayedChanges { Ok(StorageChanges { main_storage_changes: main_storage_changes.collect(), - child_storage_changes: child_storage_changes.map(|(sk, it)| (sk, it.0.collect())).collect(), + child_storage_changes: child_storage_changes + .map(|(sk, it)| (sk, it.0.collect(), it.1)).collect(), transaction, transaction_storage_root, changes_trie_transaction, @@ -570,10 +574,11 @@ impl OverlayedChanges { let delta = self.committed.top.iter().map(|(k, v)| (k.clone(), v.value.clone())) .chain(self.prospective.top.iter().map(|(k, v)| (k.clone(), v.value.clone()))); - let (root, transaction) = backend.full_storage_root(delta, child_delta_iter); + let (root, transaction, child_roots) = backend.full_storage_root(delta, child_delta_iter, true); cache.transaction = Some(transaction); cache.transaction_storage_root = Some(root); + cache.transaction_child_storage_root = child_roots.into_iter().collect(); root } diff --git a/primitives/state-machine/src/proving_backend.rs b/primitives/state-machine/src/proving_backend.rs index deafeb902d8bf..7ae36d151745d 100644 --- a/primitives/state-machine/src/proving_backend.rs +++ b/primitives/state-machine/src/proving_backend.rs @@ -20,15 +20,17 @@ use std::sync::Arc; use parking_lot::RwLock; use codec::{Decode, Codec}; use log::debug; -use hash_db::{Hasher, HashDB, EMPTY_PREFIX, Prefix}; +use hash_db::{HashDB, EMPTY_PREFIX, Prefix}; +use sp_core::{Hasher, InnerHasher}; use sp_trie::{ - MemoryDB, empty_child_trie_root, read_trie_value_with, read_child_trie_value_with, + MemoryDB, empty_child_trie_root, read_trie_value_with, record_all_keys, StorageProof, }; pub use sp_trie::Recorder; pub use sp_trie::trie_types::{Layout, TrieError}; use crate::trie_backend::TrieBackend; -use crate::trie_backend_essence::{Ephemeral, TrieBackendEssence, TrieBackendStorage}; +use crate::trie_backend_essence::{BackendStorageDBRef, TrieBackendEssence, + TrieBackendStorage, TrieBackendStorageRef}; use crate::{Error, ExecutionError, Backend}; use std::collections::HashMap; use crate::DBValue; @@ -48,15 +50,15 @@ impl<'a, S, H> ProvingBackendRecorder<'a, S, H> { /// Produce proof for a key query. pub fn storage(&mut self, key: &[u8]) -> Result>, String> { - let mut read_overlay = S::Overlay::default(); - let eph = Ephemeral::new( + let child_info = ChildInfo::top_trie(); + let eph = BackendStorageDBRef::new( self.backend.backend_storage(), - &mut read_overlay, + &child_info, ); let map_e = |e| format!("Trie lookup error: {}", e); - read_trie_value_with::, _, Ephemeral>( + read_trie_value_with::, _, BackendStorageDBRef>( &eph, self.backend.root(), key, @@ -68,36 +70,34 @@ impl<'a, S, H> ProvingBackendRecorder<'a, S, H> pub fn child_storage( &mut self, child_info: &ChildInfo, - key: &[u8] + key: &[u8], ) -> Result>, String> { let storage_key = child_info.storage_key(); let root = self.storage(storage_key)? .and_then(|r| Decode::decode(&mut &r[..]).ok()) .unwrap_or(empty_child_trie_root::>()); - let mut read_overlay = S::Overlay::default(); - let eph = Ephemeral::new( + let eph = BackendStorageDBRef::new( self.backend.backend_storage(), - &mut read_overlay, + child_info, ); let map_e = |e| format!("Trie lookup error: {}", e); - read_child_trie_value_with::, _, _>( - child_info.keyspace(), + read_trie_value_with::, _, _>( &eph, - &root.as_ref(), + &root, key, &mut *self.proof_recorder ).map_err(map_e) } /// Produce proof for the whole backend. - pub fn record_all_keys(&mut self) { - let mut read_overlay = S::Overlay::default(); - let eph = Ephemeral::new( + pub fn record_all_top_trie_keys(&mut self) { + let child_info = ChildInfo::top_trie(); + let eph = BackendStorageDBRef::new( self.backend.backend_storage(), - &mut read_overlay, + &child_info, ); let mut iter = move || -> Result<(), Box>> { @@ -113,7 +113,7 @@ impl<'a, S, H> ProvingBackendRecorder<'a, S, H> /// Global proof recorder, act as a layer over a hash db for recording queried /// data. -pub type ProofRecorder = Arc::Out, Option>>>; +pub type ProofRecorder = Arc::Out, Option>>>; /// 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. @@ -161,16 +161,23 @@ impl<'a, S: 'a + TrieBackendStorage, H: 'a + Hasher> ProvingBackend<'a, S, H> } } -impl<'a, S: 'a + TrieBackendStorage, H: 'a + Hasher> TrieBackendStorage +// proof run on a flatten storage of tries and currently only need implement a single +// trie backend storage api. +impl<'a, S: 'a + TrieBackendStorage, H: 'a + Hasher> TrieBackendStorageRef for ProofRecorderBackend<'a, S, H> { type Overlay = S::Overlay; - fn get(&self, key: &H::Out, prefix: Prefix) -> Result, String> { + fn get( + &self, + child_info: &ChildInfo, + key: &H::Out, + prefix: Prefix, + ) -> Result, String> { if let Some(v) = self.proof_recorder.read().get(key) { return Ok(v.clone()); } - let backend_value = self.backend.get(key, prefix)?; + let backend_value = self.backend.get(child_info, key, prefix)?; self.proof_recorder.write().insert(key.clone(), backend_value.clone()); Ok(backend_value) } @@ -191,7 +198,7 @@ impl<'a, S, H> Backend for ProvingBackend<'a, S, H> H::Out: Ord + Codec, { type Error = String; - type Transaction = S::Overlay; + type Transaction = Option; type TrieBackendStorage = S; fn storage(&self, key: &[u8]) -> Result>, Self::Error> { @@ -262,7 +269,8 @@ impl<'a, S, H> Backend for ProvingBackend<'a, S, H> fn storage_root(&self, delta: I) -> (H::Out, Self::Transaction) where I: IntoIterator, Option>)> { - self.0.storage_root(delta) + let (root, mut tx) = self.0.storage_root(delta); + (root, tx.remove(&ChildInfo::top_trie())) } fn child_storage_root( @@ -274,7 +282,8 @@ impl<'a, S, H> Backend for ProvingBackend<'a, S, H> I: IntoIterator, Option>)>, H::Out: Ord { - self.0.child_storage_root(child_info, delta) + let (root, is_empty, mut tx) = self.0.child_storage_root(child_info, delta); + (root, is_empty, tx.remove(child_info)) } fn register_overlay_stats(&mut self, _stats: &crate::stats::StateMachineStats) { } @@ -349,9 +358,10 @@ mod tests { assert_eq!(trie_backend.pairs(), proving_backend.pairs()); let (trie_root, mut trie_mdb) = trie_backend.storage_root(::std::iter::empty()); - let (proving_root, mut proving_mdb) = proving_backend.storage_root(::std::iter::empty()); + let (proving_root, proving_mdb) = proving_backend.storage_root(::std::iter::empty()); assert_eq!(trie_root, proving_root); - assert_eq!(trie_mdb.drain(), proving_mdb.drain()); + let mut trie_mdb = trie_mdb.remove(&ChildInfo::top_trie()).unwrap(); + assert_eq!(trie_mdb.drain(), proving_mdb.unwrap().drain()); } #[test] @@ -393,7 +403,8 @@ mod tests { let mut in_memory = in_memory.update(contents); let in_memory_root = in_memory.full_storage_root::<_, Vec<_>, _>( ::std::iter::empty(), - in_memory.child_storage_infos().map(|k|(k.to_owned(), Vec::new())) + in_memory.child_storage_infos().map(|k|(k.to_owned(), Vec::new())), + false, ).0; (0..64).for_each(|i| assert_eq!( in_memory.storage(&[i]).unwrap().unwrap(), diff --git a/primitives/state-machine/src/testing.rs b/primitives/state-machine/src/testing.rs index 70a96c623adae..e084d99da000a 100644 --- a/primitives/state-machine/src/testing.rs +++ b/primitives/state-machine/src/testing.rs @@ -18,7 +18,7 @@ use std::any::{Any, TypeId}; use codec::Decode; -use hash_db::Hasher; +use sp_core::Hasher; use crate::{ backend::Backend, OverlayedChanges, StorageTransactionCache, ext::Ext, InMemoryBackend, StorageKey, StorageValue, diff --git a/primitives/state-machine/src/trie_backend.rs b/primitives/state-machine/src/trie_backend.rs index 08eea29360465..8be6be519692d 100644 --- a/primitives/state-machine/src/trie_backend.rs +++ b/primitives/state-machine/src/trie_backend.rs @@ -15,21 +15,24 @@ // along with Substrate. If not, see . //! Trie-based state machine backend. - use log::{warn, debug}; -use hash_db::Hasher; -use sp_trie::{Trie, delta_trie_root, empty_child_trie_root, child_delta_trie_root}; +use sp_core::Hasher; +use sp_trie::{Trie, delta_trie_root, empty_child_trie_root}; use sp_trie::trie_types::{TrieDB, TrieError, Layout}; -use sp_core::storage::{ChildInfo, ChildType}; +use sp_core::storage::{ChildInfo, ChildType, ChildrenMap}; use codec::{Codec, Decode}; use crate::{ StorageKey, StorageValue, Backend, - trie_backend_essence::{TrieBackendEssence, TrieBackendStorage, Ephemeral}, + trie_backend_essence::{TrieBackendEssence, TrieBackendStorage, Ephemeral, BackendStorageDBRef}, }; -/// Patricia trie-based backend. Transaction type is an overlay of changes to commit. +/// Patricia trie-based backend. Transaction type is overlays of changes to commit +/// for this trie and child tries. pub struct TrieBackend, H: Hasher> { essence: TrieBackendEssence, + // storing child_info of top trie even if it is in + // theory a bit useless (no heap alloc on empty vec). + top_trie: ChildInfo, } impl, H: Hasher> TrieBackend where H::Out: Codec { @@ -37,6 +40,7 @@ impl, H: Hasher> TrieBackend where H::Out: Codec pub fn new(storage: S, root: H::Out) -> Self { TrieBackend { essence: TrieBackendEssence::new(storage, root), + top_trie: ChildInfo::top_trie(), } } @@ -71,11 +75,11 @@ impl, H: Hasher> Backend for TrieBackend where H::Out: Ord + Codec, { type Error = String; - type Transaction = S::Overlay; + type Transaction = ChildrenMap; type TrieBackendStorage = S; fn storage(&self, key: &[u8]) -> Result, Self::Error> { - self.essence.storage(key) + self.essence.storage(&self.top_trie, key) } fn child_storage( @@ -83,11 +87,15 @@ impl, H: Hasher> Backend for TrieBackend where child_info: &ChildInfo, key: &[u8], ) -> Result, Self::Error> { - self.essence.child_storage(child_info, key) + if let Some(essence) = self.child_essence(child_info)? { + essence.storage(child_info, key) + } else { + Ok(None) + } } fn next_storage_key(&self, key: &[u8]) -> Result, Self::Error> { - self.essence.next_storage_key(key) + self.essence.next_storage_key(&self.top_trie, key) } fn next_child_storage_key( @@ -95,15 +103,19 @@ impl, H: Hasher> Backend for TrieBackend where child_info: &ChildInfo, key: &[u8], ) -> Result, Self::Error> { - self.essence.next_child_storage_key(child_info, key) + if let Some(essence) = self.child_essence(child_info)? { + essence.next_storage_key(child_info, key) + } else { + Ok(None) + } } fn for_keys_with_prefix(&self, prefix: &[u8], f: F) { - self.essence.for_keys_with_prefix(prefix, f) + self.essence.for_keys_with_prefix(&self.top_trie, prefix, f) } fn for_key_values_with_prefix(&self, prefix: &[u8], f: F) { - self.essence.for_key_values_with_prefix(prefix, f) + self.essence.for_key_values_with_prefix(&self.top_trie, prefix, f) } fn for_keys_in_child_storage( @@ -111,7 +123,9 @@ impl, H: Hasher> Backend for TrieBackend where child_info: &ChildInfo, f: F, ) { - self.essence.for_keys_in_child_storage(child_info, f) + if let Ok(Some(essence)) = self.child_essence(child_info) { + essence.for_keys(child_info, f) + } } fn for_child_keys_with_prefix( @@ -120,12 +134,13 @@ impl, H: Hasher> Backend for TrieBackend where prefix: &[u8], f: F, ) { - self.essence.for_child_keys_with_prefix(child_info, prefix, f) + if let Ok(Some(essence)) = self.child_essence(child_info) { + essence.for_keys_with_prefix(child_info, prefix, f) + } } fn pairs(&self) -> Vec<(StorageKey, StorageValue)> { - let mut read_overlay = S::Overlay::default(); - let eph = Ephemeral::new(self.essence.backend_storage(), &mut read_overlay); + let eph = BackendStorageDBRef::new(self.essence.backend_storage(), &self.top_trie); let collect_all = || -> Result<_, Box>> { let trie = TrieDB::::new(&eph, self.essence.root())?; @@ -148,8 +163,7 @@ impl, H: Hasher> Backend for TrieBackend where } fn keys(&self, prefix: &[u8]) -> Vec { - let mut read_overlay = S::Overlay::default(); - let eph = Ephemeral::new(self.essence.backend_storage(), &mut read_overlay); + let eph = BackendStorageDBRef::new(self.essence.backend_storage(), &self.top_trie); let collect_all = || -> Result<_, Box>> { let trie = TrieDB::::new(&eph, self.essence.root())?; @@ -167,7 +181,7 @@ impl, H: Hasher> Backend for TrieBackend where collect_all().map_err(|e| debug!(target: "trie", "Error extracting trie keys: {}", e)).unwrap_or_default() } - fn storage_root(&self, delta: I) -> (H::Out, S::Overlay) + fn storage_root(&self, delta: I) -> (H::Out, ChildrenMap) where I: IntoIterator)> { let mut write_overlay = S::Overlay::default(); @@ -176,6 +190,7 @@ impl, H: Hasher> Backend for TrieBackend where { let mut eph = Ephemeral::new( self.essence.backend_storage(), + &self.top_trie, &mut write_overlay, ); @@ -184,8 +199,9 @@ impl, H: Hasher> Backend for TrieBackend where Err(e) => warn!(target: "trie", "Failed to write to trie: {}", e), } } - - (root, write_overlay) + let mut tx = ChildrenMap::default(); + tx.insert(self.top_trie.clone(), write_overlay); + (root, tx) } fn child_storage_root( @@ -213,13 +229,15 @@ impl, H: Hasher> Backend for TrieBackend where }; { + let storage = self.essence.backend_storage(); + // Do not write prefix in overlay. let mut eph = Ephemeral::new( - self.essence.backend_storage(), + storage, + child_info, &mut write_overlay, ); - match child_delta_trie_root::, _, _, _, _, _>( - child_info.keyspace(), + match delta_trie_root::, _, _, _, _>( &mut eph, root, delta @@ -231,7 +249,9 @@ impl, H: Hasher> Backend for TrieBackend where let is_default = root == default_root; - (root, is_default, write_overlay) + let mut tx = ChildrenMap::default(); + tx.insert(child_info.clone(), write_overlay); + (root, is_default, tx) } fn as_trie_backend(&mut self) -> Option<&TrieBackend> { @@ -245,12 +265,29 @@ impl, H: Hasher> Backend for TrieBackend where } } +impl, H: Hasher> TrieBackend where + H::Out: Ord + Codec, +{ + fn child_essence<'a>( + &'a self, + child_info: &ChildInfo, + ) -> Result>, >::Error> { + let root: Option = self.storage(&child_info.prefixed_storage_key()[..])? + .and_then(|encoded_root| Decode::decode(&mut &encoded_root[..]).ok()); + Ok(if let Some(root) = root { + Some(TrieBackendEssence::new(self.essence.backend_storage(), root)) + } else { + None + }) + } +} + #[cfg(test)] pub mod tests { use std::collections::HashSet; use sp_core::H256; use codec::Encode; - use sp_trie::{TrieMut, PrefixedMemoryDB, trie_types::TrieDBMut, KeySpacedDBMut}; + use sp_trie::{TrieMut, PrefixedMemoryDB, trie_types::TrieDBMut}; use sp_runtime::traits::BlakeTwo256; use super::*; @@ -261,7 +298,6 @@ pub mod tests { let mut root = H256::default(); let mut mdb = PrefixedMemoryDB::::default(); { - let mut mdb = KeySpacedDBMut::new(&mut mdb, child_info.keyspace()); let mut trie = TrieDBMut::new(&mut mdb, &mut root); trie.insert(b"value3", &[142]).expect("insert failed"); trie.insert(b"value4", &[124]).expect("insert failed"); @@ -328,13 +364,18 @@ pub mod tests { #[test] fn storage_root_transaction_is_empty() { - assert!(test_trie().storage_root(::std::iter::empty()).1.drain().is_empty()); + let tx = test_trie().storage_root(::std::iter::empty()).1; + for (_ct, mut tx) in tx.into_iter() { + assert!(tx.drain().is_empty()); + } } #[test] fn storage_root_transaction_is_non_empty() { - let (new_root, mut tx) = test_trie().storage_root(vec![(b"new-key".to_vec(), Some(b"new-value".to_vec()))]); - assert!(!tx.drain().is_empty()); + let (new_root, tx) = test_trie().storage_root(vec![(b"new-key".to_vec(), Some(b"new-value".to_vec()))]); + for (_ct, mut tx) in tx.into_iter() { + assert!(!tx.drain().is_empty()); + } assert!(new_root != test_trie().storage_root(::std::iter::empty()).0); } diff --git a/primitives/state-machine/src/trie_backend_essence.rs b/primitives/state-machine/src/trie_backend_essence.rs index 28d1c68ca2e40..4c8cde131c440 100644 --- a/primitives/state-machine/src/trie_backend_essence.rs +++ b/primitives/state-machine/src/trie_backend_essence.rs @@ -19,11 +19,13 @@ use std::ops::Deref; use std::sync::Arc; +use std::marker::PhantomData; use log::{debug, warn}; -use hash_db::{self, Hasher, EMPTY_PREFIX, Prefix}; +use sp_core::Hasher; +use hash_db::{self, EMPTY_PREFIX, Prefix}; use sp_trie::{Trie, MemoryDB, PrefixedMemoryDB, DBValue, - empty_child_trie_root, read_trie_value, read_child_trie_value, - for_keys_in_child_trie, KeySpacedDB, TrieDBIterator}; + check_if_empty_root, read_trie_value, + TrieDBIterator, for_keys_in_trie}; use sp_trie::trie_types::{TrieDB, TrieError, Layout}; use crate::{backend::Consolidate, StorageKey, StorageValue}; use sp_core::storage::ChildInfo; @@ -32,16 +34,21 @@ use codec::Encode; /// Patricia trie-based storage trait. pub trait Storage: Send + Sync { /// Get a trie node. - fn get(&self, key: &H::Out, prefix: Prefix) -> Result, String>; + fn get( + &self, + trie: &ChildInfo, + key: &H::Out, + prefix: Prefix, + ) -> Result, String>; } /// Patricia trie-based pairs storage essence. -pub struct TrieBackendEssence, H: Hasher> { +pub struct TrieBackendEssence, H: Hasher> { storage: S, root: H::Out, } -impl, H: Hasher> TrieBackendEssence where H::Out: Encode { +impl, H: Hasher> TrieBackendEssence where H::Out: Encode { /// Create new trie-based backend. pub fn new(storage: S, root: H::Out) -> Self { TrieBackendEssence { @@ -67,60 +74,10 @@ impl, H: Hasher> TrieBackendEssence where H::Out: /// Return the next key in the trie i.e. the minimum key that is strictly superior to `key` in /// lexicographic order. - pub fn next_storage_key(&self, key: &[u8]) -> Result, String> { - self.next_storage_key_from_root(&self.root, None, key) - } - - /// Access the root of the child storage in its parent trie - fn child_root(&self, child_info: &ChildInfo) -> Result, String> { - self.storage(child_info.prefixed_storage_key().as_slice()) - } - - /// Return the next key in the child trie i.e. the minimum key that is strictly superior to - /// `key` in lexicographic order. - pub fn next_child_storage_key( - &self, - child_info: &ChildInfo, - key: &[u8], - ) -> Result, String> { - let child_root = match self.child_root(child_info)? { - Some(child_root) => child_root, - None => return Ok(None), - }; - - let mut hash = H::Out::default(); - - if child_root.len() != hash.as_ref().len() { - return Err(format!("Invalid child storage hash at {:?}", child_info.storage_key())); - } - // note: child_root and hash must be same size, panics otherwise. - hash.as_mut().copy_from_slice(&child_root[..]); - - self.next_storage_key_from_root(&hash, Some(child_info), key) - } - - /// Return next key from main trie or child trie by providing corresponding root. - fn next_storage_key_from_root( - &self, - root: &H::Out, - child_info: Option<&ChildInfo>, - key: &[u8], - ) -> Result, String> { - let mut read_overlay = S::Overlay::default(); - let eph = Ephemeral { - storage: &self.storage, - overlay: &mut read_overlay, - }; - let dyn_eph: &dyn hash_db::HashDBRef<_, _>; - let keyspace_eph; - if let Some(child_info) = child_info.as_ref() { - keyspace_eph = KeySpacedDB::new(&eph, child_info.keyspace()); - dyn_eph = &keyspace_eph; - } else { - dyn_eph = &eph; - } + pub fn next_storage_key(&self, child_info: &ChildInfo, key: &[u8]) -> Result, String> { + let eph = BackendStorageDBRef::new(&self.storage, child_info); - let trie = TrieDB::::new(dyn_eph, root) + let trie = TrieDB::::new(&eph, &self.root) .map_err(|e| format!("TrieDB creation error: {}", e))?; let mut iter = trie.iter() .map_err(|e| format!("TrieDB iteration error: {}", e))?; @@ -150,63 +107,25 @@ impl, H: Hasher> TrieBackendEssence where H::Out: } /// Get the value of storage at given key. - pub fn storage(&self, key: &[u8]) -> Result, String> { - let mut read_overlay = S::Overlay::default(); - let eph = Ephemeral { - storage: &self.storage, - overlay: &mut read_overlay, - }; + pub fn storage(&self, child_info: &ChildInfo, key: &[u8]) -> Result, String> { + let eph = BackendStorageDBRef::new(&self.storage, child_info); let map_e = |e| format!("Trie lookup error: {}", e); read_trie_value::, _>(&eph, &self.root, key).map_err(map_e) } - /// Get the value of child storage at given key. - pub fn child_storage( - &self, - child_info: &ChildInfo, - key: &[u8], - ) -> Result, String> { - let root = self.child_root(child_info)? - .unwrap_or(empty_child_trie_root::>().encode()); - - let mut read_overlay = S::Overlay::default(); - let eph = Ephemeral { - storage: &self.storage, - overlay: &mut read_overlay, - }; - - let map_e = |e| format!("Trie lookup error: {}", e); - - read_child_trie_value::, _>(child_info.keyspace(), &eph, &root, key) - .map_err(map_e) - } - - /// Retrieve all entries keys of child storage and call `f` for each of those keys. - pub fn for_keys_in_child_storage( + /// Retrieve all entries keys of storage and call `f` for each of those keys. + pub fn for_keys( &self, child_info: &ChildInfo, f: F, ) { - let root = match self.child_root(child_info) { - Ok(v) => v.unwrap_or(empty_child_trie_root::>().encode()), - Err(e) => { - debug!(target: "trie", "Error while iterating child storage: {}", e); - return; - } - }; + let eph = BackendStorageDBRef::new(&self.storage, child_info); - let mut read_overlay = S::Overlay::default(); - let eph = Ephemeral { - storage: &self.storage, - overlay: &mut read_overlay, - }; - - if let Err(e) = for_keys_in_child_trie::, _, Ephemeral>( - child_info.keyspace(), + if let Err(e) = for_keys_in_trie::, _, BackendStorageDBRef>( &eph, - &root, + &self.root, f, ) { debug!(target: "trie", "Error while iterating child storage: {}", e); @@ -214,27 +133,8 @@ impl, H: Hasher> TrieBackendEssence where H::Out: } /// Execute given closure for all keys starting with prefix. - pub fn for_child_keys_with_prefix( - &self, - child_info: &ChildInfo, - prefix: &[u8], - mut f: F, - ) { - let root_vec = match self.child_root(child_info) { - Ok(v) => v.unwrap_or(empty_child_trie_root::>().encode()), - Err(e) => { - debug!(target: "trie", "Error while iterating child storage: {}", e); - return; - } - }; - let mut root = H::Out::default(); - root.as_mut().copy_from_slice(&root_vec); - self.keys_values_with_prefix_inner(&root, prefix, |k, _v| f(k), Some(child_info)) - } - - /// Execute given closure for all keys starting with prefix. - pub fn for_keys_with_prefix(&self, prefix: &[u8], mut f: F) { - self.keys_values_with_prefix_inner(&self.root, prefix, |k, _v| f(k), None) + pub fn for_keys_with_prefix(&self, child_info: &ChildInfo, prefix: &[u8], mut f: F) { + self.keys_values_with_prefix_inner(&self.root, prefix, |k, _v| f(k), child_info) } fn keys_values_with_prefix_inner( @@ -242,13 +142,9 @@ impl, H: Hasher> TrieBackendEssence where H::Out: root: &H::Out, prefix: &[u8], mut f: F, - child_info: Option<&ChildInfo>, + child_info: &ChildInfo, ) { - let mut read_overlay = S::Overlay::default(); - let eph = Ephemeral { - storage: &self.storage, - overlay: &mut read_overlay, - }; + let eph = BackendStorageDBRef::new(&self.storage, child_info); let mut iter = move |db| -> Result<(), Box>> { let trie = TrieDB::::new(db, root)?; @@ -264,30 +160,41 @@ impl, H: Hasher> TrieBackendEssence where H::Out: Ok(()) }; - let result = if let Some(child_info) = child_info { - let db = KeySpacedDB::new(&eph, child_info.keyspace()); - iter(&db) - } else { - iter(&eph) - }; - if let Err(e) = result { + if let Err(e) = iter(&eph) { debug!(target: "trie", "Error while iterating by prefix: {}", e); } } /// Execute given closure for all key and values starting with prefix. - pub fn for_key_values_with_prefix(&self, prefix: &[u8], f: F) { - self.keys_values_with_prefix_inner(&self.root, prefix, f, None) + pub fn for_key_values_with_prefix(&self, child_info: &ChildInfo, prefix: &[u8], f: F) { + self.keys_values_with_prefix_inner(&self.root, prefix, f, child_info) } } -pub(crate) struct Ephemeral<'a, S: 'a + TrieBackendStorage, H: 'a + Hasher> { +pub(crate) struct Ephemeral<'a, S, H, O> where + S: 'a + TrieBackendStorageRef, + H: 'a + Hasher, + O: hash_db::HashDB + Default + Consolidate, +{ storage: &'a S, - overlay: &'a mut S::Overlay, + child_info: &'a ChildInfo, + overlay: &'a mut O, + _ph: PhantomData, } -impl<'a, S: 'a + TrieBackendStorage, H: 'a + Hasher> hash_db::AsPlainDB - for Ephemeral<'a, S, H> +pub(crate) struct BackendStorageDBRef<'a, S, H> where + S: 'a + TrieBackendStorageRef, + H: 'a + Hasher, +{ + storage: &'a S, + child_info: &'a ChildInfo, + _ph: PhantomData, +} + +impl<'a, S, H, O> hash_db::AsPlainDB for Ephemeral<'a, S, H, O> where + S: 'a + TrieBackendStorage, + H: 'a + Hasher, + O: hash_db::HashDB + Default + Consolidate, { fn as_plain_db<'b>(&'b self) -> &'b (dyn hash_db::PlainDB + 'b) { self } fn as_plain_db_mut<'b>(&'b mut self) -> &'b mut (dyn hash_db::PlainDB + 'b) { @@ -295,41 +202,54 @@ impl<'a, S: 'a + TrieBackendStorage, H: 'a + Hasher> hash_db::AsPlainDB, H: 'a + Hasher> hash_db::AsHashDB - for Ephemeral<'a, S, H> +impl<'a, S, H, O> hash_db::AsHashDB for Ephemeral<'a, S, H, O> where + S: 'a + TrieBackendStorage, + H: 'a + Hasher, + O: hash_db::HashDB + Default + Consolidate, { fn as_hash_db<'b>(&'b self) -> &'b (dyn hash_db::HashDB + 'b) { self } fn as_hash_db_mut<'b>(&'b mut self) -> &'b mut (dyn hash_db::HashDB + 'b) { self } } -impl<'a, S: TrieBackendStorage, H: Hasher> Ephemeral<'a, S, H> { - pub fn new(storage: &'a S, overlay: &'a mut S::Overlay) -> Self { +impl<'a, S, H, O> Ephemeral<'a, S, H, O> where + S: 'a + TrieBackendStorageRef, + H: 'a + Hasher, + O: hash_db::HashDB + Default + Consolidate, +{ + pub fn new(storage: &'a S, child_info: &'a ChildInfo, overlay: &'a mut O) -> Self { Ephemeral { storage, + child_info, overlay, + _ph: PhantomData, } } } -impl<'a, S: 'a + TrieBackendStorage, H: Hasher> hash_db::PlainDB - for Ephemeral<'a, S, H> +impl<'a, S, H> BackendStorageDBRef<'a, S, H> where + S: 'a + TrieBackendStorageRef, + H: 'a + Hasher, { - fn get(&self, key: &H::Out) -> Option { - if let Some(val) = hash_db::HashDB::get(self.overlay, key, EMPTY_PREFIX) { - Some(val) - } else { - match self.storage.get(&key, EMPTY_PREFIX) { - Ok(x) => x, - Err(e) => { - warn!(target: "trie", "Failed to read from DB: {}", e); - None - }, - } + pub fn new(storage: &'a S, child_info: &'a ChildInfo) -> Self { + BackendStorageDBRef { + storage, + child_info, + _ph: PhantomData, } } +} + +impl<'a, S, H, O> hash_db::PlainDB for Ephemeral<'a, S, H, O> where + S: 'a + TrieBackendStorage, + H: 'a + Hasher, + O: hash_db::HashDB + Default + Consolidate, +{ + fn get(&self, key: &H::Out) -> Option { + hash_db::PlainDBRef::get(self, key) + } fn contains(&self, key: &H::Out) -> bool { - hash_db::HashDB::get(self, key, EMPTY_PREFIX).is_some() + hash_db::PlainDBRef::contains(self, key) } fn emplace(&mut self, key: H::Out, value: DBValue) { @@ -341,21 +261,16 @@ impl<'a, S: 'a + TrieBackendStorage, H: Hasher> hash_db::PlainDB, H: Hasher> hash_db::PlainDBRef - for Ephemeral<'a, S, H> -{ - fn get(&self, key: &H::Out) -> Option { hash_db::PlainDB::get(self, key) } - fn contains(&self, key: &H::Out) -> bool { hash_db::PlainDB::contains(self, key) } -} - -impl<'a, S: 'a + TrieBackendStorage, H: Hasher> hash_db::HashDB - for Ephemeral<'a, S, H> +impl<'a, S, H, O> hash_db::PlainDBRef for Ephemeral<'a, S, H, O> where + S: 'a + TrieBackendStorageRef, + H: 'a + Hasher, + O: hash_db::HashDB + Default + Consolidate, { - fn get(&self, key: &H::Out, prefix: Prefix) -> Option { - if let Some(val) = hash_db::HashDB::get(self.overlay, key, prefix) { + fn get(&self, key: &H::Out) -> Option { + if let Some(val) = hash_db::HashDB::get(self.overlay, key, EMPTY_PREFIX) { Some(val) } else { - match self.storage.get(&key, prefix) { + match self.storage.get(self.child_info, &key, EMPTY_PREFIX) { Ok(x) => x, Err(e) => { warn!(target: "trie", "Failed to read from DB: {}", e); @@ -365,8 +280,47 @@ impl<'a, S: 'a + TrieBackendStorage, H: Hasher> hash_db::HashDB } } + fn contains(&self, key: &H::Out) -> bool { + hash_db::HashDBRef::get(self, key, EMPTY_PREFIX).is_some() + } +} + +impl<'a, S, H> hash_db::PlainDBRef for BackendStorageDBRef<'a, S, H> where + S: 'a + TrieBackendStorageRef, + H: 'a + Hasher, +{ + fn get(&self, key: &H::Out) -> Option { + if check_if_empty_root::(key.as_ref()) { + return Some(vec![0u8]); + } + + match self.storage.get(self.child_info, &key, EMPTY_PREFIX) { + Ok(x) => x, + Err(e) => { + warn!(target: "trie", "Failed to read from DB: {}", e); + None + }, + } + } + + fn contains(&self, key: &H::Out) -> bool { + hash_db::HashDBRef::get(self, key, EMPTY_PREFIX).is_some() + } +} + + +impl<'a, S, H, O> hash_db::HashDB for Ephemeral<'a, S, H, O> where + S: 'a + TrieBackendStorage, + H: 'a + Hasher, + O: hash_db::HashDB + Default + Consolidate, +{ + + fn get(&self, key: &H::Out, prefix: Prefix) -> Option { + hash_db::HashDBRef::get(self, key, prefix) + } + fn contains(&self, key: &H::Out, prefix: Prefix) -> bool { - hash_db::HashDB::get(self, key, prefix).is_some() + hash_db::HashDBRef::contains(self, key, prefix) } fn insert(&mut self, prefix: Prefix, value: &[u8]) -> H::Out { @@ -382,48 +336,122 @@ impl<'a, S: 'a + TrieBackendStorage, H: Hasher> hash_db::HashDB } } -impl<'a, S: 'a + TrieBackendStorage, H: Hasher> hash_db::HashDBRef - for Ephemeral<'a, S, H> +impl<'a, S, H, O> hash_db::HashDBRef for Ephemeral<'a, S, H, O> where + S: 'a + TrieBackendStorageRef, + H: 'a + Hasher, + O: hash_db::HashDB + Default + Consolidate, { fn get(&self, key: &H::Out, prefix: Prefix) -> Option { - hash_db::HashDB::get(self, key, prefix) + if let Some(val) = hash_db::HashDB::get(self.overlay, key, prefix) { + Some(val) + } else { + match self.storage.get(self.child_info, &key, prefix) { + Ok(x) => x, + Err(e) => { + warn!(target: "trie", "Failed to read from DB: {}", e); + None + }, + } + } + } + + fn contains(&self, key: &H::Out, prefix: Prefix) -> bool { + hash_db::HashDBRef::get(self, key, prefix).is_some() + } +} + +impl<'a, S, H> hash_db::HashDBRef for BackendStorageDBRef<'a, S, H> where + S: 'a + TrieBackendStorageRef, + H: 'a + Hasher, +{ + fn get(&self, key: &H::Out, prefix: Prefix) -> Option { + if check_if_empty_root::(key.as_ref()) { + return Some(vec![0u8]); + } + + match self.storage.get(self.child_info, &key, prefix) { + Ok(x) => x, + Err(e) => { + warn!(target: "trie", "Failed to read from DB: {}", e); + None + }, + } } fn contains(&self, key: &H::Out, prefix: Prefix) -> bool { - hash_db::HashDB::contains(self, key, prefix) + hash_db::HashDBRef::get(self, key, prefix).is_some() } } /// Key-value pairs storage that is used by trie backend essence. -pub trait TrieBackendStorage: Send + Sync { +pub trait TrieBackendStorageRef { /// Type of in-memory overlay. type Overlay: hash_db::HashDB + Default + Consolidate; /// Get the value stored at key. - fn get(&self, key: &H::Out, prefix: Prefix) -> Result, String>; + fn get( + &self, + child_info: &ChildInfo, + key: &H::Out, + prefix: Prefix, + ) -> Result, String>; } -// This implementation is used by normal storage trie clients. -impl TrieBackendStorage for Arc> { +/// Key-value pairs storage that is used by trie backend essence. +pub trait TrieBackendStorage: TrieBackendStorageRef + Send + Sync { } + +impl + Send + Sync> TrieBackendStorage for B {} + +impl TrieBackendStorageRef for Arc> { type Overlay = PrefixedMemoryDB; - fn get(&self, key: &H::Out, prefix: Prefix) -> Result, String> { - Storage::::get(self.deref(), key, prefix) + fn get( + &self, + child_info: &ChildInfo, + key: &H::Out, + prefix: Prefix, + ) -> Result, String> { + Storage::::get(self.deref(), child_info, key, prefix) + } +} + +impl> TrieBackendStorageRef for &S { + type Overlay = >::Overlay; + + fn get( + &self, + child_info: &ChildInfo, + key: &H::Out, + prefix: Prefix, + ) -> Result, String> { + >::get(self, child_info, key, prefix) } } // This implementation is used by test storage trie clients. -impl TrieBackendStorage for PrefixedMemoryDB { +impl TrieBackendStorageRef for PrefixedMemoryDB { type Overlay = PrefixedMemoryDB; - fn get(&self, key: &H::Out, prefix: Prefix) -> Result, String> { + fn get( + &self, + _child_info: &ChildInfo, + key: &H::Out, + prefix: Prefix, + ) -> Result, String> { + // No need to use keyspace for in memory db, ignoring child_info parameter. Ok(hash_db::HashDB::get(self, key, prefix)) } } -impl TrieBackendStorage for MemoryDB { +impl TrieBackendStorageRef for MemoryDB { type Overlay = MemoryDB; - fn get(&self, key: &H::Out, prefix: Prefix) -> Result, String> { + fn get( + &self, + _child_info: &ChildInfo, + key: &H::Out, + prefix: Prefix, + ) -> Result, String> { + // No need to use keyspace for in memory db, ignoring child_info parameter. Ok(hash_db::HashDB::get(self, key, prefix)) } } @@ -431,8 +459,10 @@ impl TrieBackendStorage for MemoryDB { #[cfg(test)] mod test { use sp_core::{Blake2Hasher, H256}; - use sp_trie::{TrieMut, PrefixedMemoryDB, trie_types::TrieDBMut, KeySpacedDBMut}; + use sp_trie::{TrieMut, PrefixedMemoryDB, trie_types::TrieDBMut}; use super::*; + use crate::trie_backend::TrieBackend; + use crate::backend::Backend; #[test] fn next_storage_key_and_next_child_storage_key_work() { @@ -450,22 +480,13 @@ mod test { trie.insert(b"4", &[1]).expect("insert failed"); trie.insert(b"6", &[1]).expect("insert failed"); } - { - let mut mdb = KeySpacedDBMut::new(&mut mdb, child_info.keyspace()); - // reuse of root_1 implicitly assert child trie root is same - // as top trie (contents must remain the same). - let mut trie = TrieDBMut::new(&mut mdb, &mut root_1); - trie.insert(b"3", &[1]).expect("insert failed"); - trie.insert(b"4", &[1]).expect("insert failed"); - trie.insert(b"6", &[1]).expect("insert failed"); - } { let mut trie = TrieDBMut::new(&mut mdb, &mut root_2); trie.insert(child_info.prefixed_storage_key().as_slice(), root_1.as_ref()) .expect("insert failed"); }; - let essence_1 = TrieBackendEssence::new(mdb, root_1); + let essence_1 = TrieBackend::new(mdb, root_1); assert_eq!(essence_1.next_storage_key(b"2"), Ok(Some(b"3".to_vec()))); assert_eq!(essence_1.next_storage_key(b"3"), Ok(Some(b"4".to_vec()))); @@ -474,7 +495,7 @@ mod test { assert_eq!(essence_1.next_storage_key(b"6"), Ok(None)); let mdb = essence_1.into_storage(); - let essence_2 = TrieBackendEssence::new(mdb, root_2); + let essence_2 = TrieBackend::new(mdb, root_2); assert_eq!( essence_2.next_child_storage_key(child_info, b"2"), Ok(Some(b"3".to_vec())) diff --git a/primitives/storage/src/lib.rs b/primitives/storage/src/lib.rs index d2c4a73e23d59..abc6d8700bdce 100644 --- a/primitives/storage/src/lib.rs +++ b/primitives/storage/src/lib.rs @@ -20,6 +20,8 @@ #[cfg(feature = "std")] use serde::{Serialize, Deserialize}; +#[cfg(feature = "std")] +use sp_std::collections::btree_map::BTreeMap; use sp_debug_derive::RuntimeDebug; use sp_std::vec::Vec; @@ -90,7 +92,7 @@ pub struct StorageData( /// Map of data to use in a storage, it is a collection of /// byte key and values. #[cfg(feature = "std")] -pub type StorageMap = std::collections::BTreeMap, Vec>; +pub type StorageMap = BTreeMap, Vec>; #[cfg(feature = "std")] #[derive(Debug, PartialEq, Eq, Clone)] @@ -190,6 +192,20 @@ impl ChildInfo { } } + /// Top trie defined as the unique crypto id trie with + /// 0 length unique id. + pub fn top_trie() -> Self { + Self::new_default(&[]) + } + + /// Top trie defined as the unique crypto id trie with + /// 0 length unique id. + pub fn is_top_trie(&self) -> bool { + match self { + ChildInfo::ParentKeyId(ChildTrieParentKeyId { data }) => data.len() == 0, + } + } + /// Returns byte sequence (keyspace) that can be use by underlying db to isolate keys. /// This is a unique id of the child trie. The collision resistance of this value /// depends on the type of child info use. For `ChildInfo::Default` it is and need to be. @@ -331,6 +347,97 @@ impl ChildTrieParentKeyId { } } +#[cfg(feature = "std")] +#[derive(Clone, PartialEq, Eq, Debug)] +/// Type for storing a map of child trie related information. +/// A few utilities methods are defined. +pub struct ChildrenMap(pub BTreeMap); + +/// Type alias for storage of children related content. +pub type ChildrenVec = Vec<(ChildInfo, T)>; + +/// Type alias for storage of children related content. +pub type ChildrenSlice<'a, T> = &'a [(ChildInfo, T)]; + +#[cfg(feature = "std")] +impl sp_std::ops::Deref for ChildrenMap { + type Target = BTreeMap; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +#[cfg(feature = "std")] +impl sp_std::ops::DerefMut for ChildrenMap { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +#[cfg(feature = "std")] +impl sp_std::default::Default for ChildrenMap { + fn default() -> Self { + ChildrenMap(BTreeMap::new()) + } +} + +#[cfg(feature = "std")] +impl ChildrenMap { + /// Extend for `ChildrenMap` is usually about merging entries, + /// this method extends two maps, by applying a merge function + /// on each of its entries. + pub fn extend_with( + &mut self, + other: impl Iterator, + merge: impl Fn(&mut T, T), + ) { + use sp_std::collections::btree_map::Entry; + for (child_info, child_content) in other { + match self.0.entry(child_info) { + Entry::Occupied(mut entry) => { + merge(entry.get_mut(), child_content) + }, + Entry::Vacant(entry) => { + entry.insert(child_content); + }, + } + } + } + + /// Extends two maps, by extending entries with the same key. + pub fn extend_replace( + &mut self, + other: impl Iterator, + ) { + self.0.extend(other) + } + + /// Retains only the elements specified by the predicate. + pub fn retain(&mut self, mut f: impl FnMut(&ChildInfo, &mut T) -> bool) { + let mut to_del = Vec::new(); + for (k, v) in self.0.iter_mut() { + if !f(k, v) { + // this clone can be avoid with unsafe code + to_del.push(k.clone()); + } + } + for k in to_del { + self.0.remove(&k); + } + } +} + +#[cfg(feature = "std")] +impl IntoIterator for ChildrenMap { + type Item = (ChildInfo, T); + type IntoIter = sp_std::collections::btree_map::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } +} + const DEFAULT_CHILD_TYPE_PARENT_PREFIX: &'static [u8] = b":child_storage:default:"; #[test] diff --git a/primitives/trie/src/lib.rs b/primitives/trie/src/lib.rs index 37fe928336337..25b743dc64b5d 100644 --- a/primitives/trie/src/lib.rs +++ b/primitives/trie/src/lib.rs @@ -27,7 +27,7 @@ mod trie_stream; use sp_std::boxed::Box; use sp_std::marker::PhantomData; use sp_std::vec::Vec; -use hash_db::{Hasher, Prefix}; +use sp_core::{Hasher, InnerHasher}; use trie_db::proof::{generate_proof, verify_proof}; pub use trie_db::proof::VerifyError; /// Our `NodeCodec`-specific error. @@ -49,16 +49,16 @@ pub use hash_db::{HashDB as HashDBT, EMPTY_PREFIX}; #[derive(Default)] /// substrate trie layout -pub struct Layout(sp_std::marker::PhantomData); +pub struct Layout(PhantomData); -impl TrieLayout for Layout { +impl TrieLayout for Layout { const USE_EXTENSION: bool = false; type Hash = H; type Codec = NodeCodec; } -impl TrieConfiguration for Layout { - fn trie_root(input: I) -> ::Out where +impl TrieConfiguration for Layout { + fn trie_root(input: I) -> ::Out where I: IntoIterator, A: AsRef<[u8]> + Ord, B: AsRef<[u8]>, @@ -82,8 +82,8 @@ impl TrieConfiguration for Layout { /// TrieDB error over `TrieConfiguration` trait. pub type TrieError = trie_db::TrieError, CError>; /// Reexport from `hash_db`, with genericity set for `Hasher` trait. -pub trait AsHashDB: hash_db::AsHashDB {} -impl> AsHashDB for T {} +pub trait AsHashDB: hash_db::AsHashDB {} +impl> AsHashDB for T {} /// Reexport from `hash_db`, with genericity set for `Hasher` trait. pub type HashDB<'a, H> = dyn hash_db::HashDB + 'a; /// Reexport from `hash_db`, with genericity set for key only. @@ -106,7 +106,7 @@ pub type TrieDBMut<'a, L> = trie_db::TrieDBMut<'a, L>; /// Querying interface, as in `trie_db` but less generic. pub type Lookup<'a, L, Q> = trie_db::Lookup<'a, L, Q>; /// Hash type for a trie layout. -pub type TrieHash = <::Hash as Hasher>::Out; +pub type TrieHash = <::Hash as InnerHasher>::Out; /// This module is for non generic definition of trie type. /// Only the `Hasher` trait is generic in this case. @@ -213,75 +213,28 @@ pub fn read_trie_value_with< /// Determine the empty child trie root. pub fn empty_child_trie_root( -) -> ::Out { +) -> ::Out { L::trie_root::<_, Vec, Vec>(core::iter::empty()) } -/// Determine a child trie root given its ordered contents, closed form. H is the default hasher, -/// but a generic implementation may ignore this type parameter and use other hashers. -pub fn child_trie_root( - input: I, -) -> ::Out - where - I: IntoIterator, - A: AsRef<[u8]> + Ord, - B: AsRef<[u8]>, -{ - L::trie_root(input) -} - -/// Determine a child trie root given a hash DB and delta values. H is the default hasher, -/// but a generic implementation may ignore this type parameter and use other hashers. -pub fn child_delta_trie_root( - keyspace: &[u8], - db: &mut DB, - root_data: RD, - delta: I, -) -> Result<::Out, Box>> - where - I: IntoIterator)>, - A: AsRef<[u8]> + Ord, - B: AsRef<[u8]>, - RD: AsRef<[u8]>, - DB: hash_db::HashDB - + hash_db::PlainDB, trie_db::DBValue>, -{ - let mut root = TrieHash::::default(); - // root is fetched from DB, not writable by runtime, so it's always valid. - root.as_mut().copy_from_slice(root_data.as_ref()); - - { - let mut db = KeySpacedDBMut::new(&mut *db, keyspace); - let mut trie = TrieDBMut::::from_existing(&mut db, &mut root)?; - - for (key, change) in delta { - match change { - Some(val) => trie.insert(key.as_ref(), val.as_ref())?, - None => trie.remove(key.as_ref())?, - }; - } - } - - Ok(root) +/// Test if this is an empty root node. +pub fn check_if_empty_root ( + root: &[u8], +) -> bool { + H::EMPTY_ROOT == root } /// Call `f` for all keys in a child trie. -pub fn for_keys_in_child_trie( - keyspace: &[u8], +pub fn for_keys_in_trie( db: &DB, - root_slice: &[u8], + root: &TrieHash, mut f: F ) -> Result<(), Box>> where DB: hash_db::HashDBRef + hash_db::PlainDBRef, trie_db::DBValue>, { - let mut root = TrieHash::::default(); - // root is fetched from DB, not writable by runtime, so it's always valid. - root.as_mut().copy_from_slice(root_slice); - - let db = KeySpacedDB::new(&*db, keyspace); - let trie = TrieDB::::new(&db, &root)?; + let trie = TrieDB::::new(&*db, &root)?; let iter = trie.iter()?; for x in iter { @@ -315,141 +268,6 @@ pub fn record_all_keys( Ok(()) } -/// Read a value from the child trie. -pub fn read_child_trie_value( - keyspace: &[u8], - db: &DB, - root_slice: &[u8], - key: &[u8] -) -> Result>, Box>> - where - DB: hash_db::HashDBRef - + hash_db::PlainDBRef, trie_db::DBValue>, -{ - let mut root = TrieHash::::default(); - // root is fetched from DB, not writable by runtime, so it's always valid. - root.as_mut().copy_from_slice(root_slice); - - let db = KeySpacedDB::new(&*db, keyspace); - Ok(TrieDB::::new(&db, &root)?.get(key).map(|x| x.map(|val| val.to_vec()))?) -} - -/// Read a value from the child trie with given query. -pub fn read_child_trie_value_with, DB>( - keyspace: &[u8], - db: &DB, - root_slice: &[u8], - key: &[u8], - query: Q -) -> Result>, Box>> - where - DB: hash_db::HashDBRef - + hash_db::PlainDBRef, trie_db::DBValue>, -{ - let mut root = TrieHash::::default(); - // root is fetched from DB, not writable by runtime, so it's always valid. - root.as_mut().copy_from_slice(root_slice); - - let db = KeySpacedDB::new(&*db, keyspace); - Ok(TrieDB::::new(&db, &root)?.get_with(key, query).map(|x| x.map(|val| val.to_vec()))?) -} - -/// `HashDB` implementation that append a encoded prefix (unique id bytes) in addition to the -/// prefix of every key value. -pub struct KeySpacedDB<'a, DB, H>(&'a DB, &'a [u8], PhantomData); - -/// `HashDBMut` implementation that append a encoded prefix (unique id bytes) in addition to the -/// prefix of every key value. -/// -/// Mutable variant of `KeySpacedDB`, see [`KeySpacedDB`]. -pub struct KeySpacedDBMut<'a, DB, H>(&'a mut DB, &'a [u8], PhantomData); - -/// Utility function used to merge some byte data (keyspace) and `prefix` data -/// before calling key value database primitives. -fn keyspace_as_prefix_alloc(ks: &[u8], prefix: Prefix) -> (Vec, Option) { - let mut result = sp_std::vec![0; ks.len() + prefix.0.len()]; - result[..ks.len()].copy_from_slice(ks); - result[ks.len()..].copy_from_slice(prefix.0); - (result, prefix.1) -} - -impl<'a, DB, H> KeySpacedDB<'a, DB, H> where - H: Hasher, -{ - /// instantiate new keyspaced db - pub fn new(db: &'a DB, ks: &'a [u8]) -> Self { - KeySpacedDB(db, ks, PhantomData) - } -} - -impl<'a, DB, H> KeySpacedDBMut<'a, DB, H> where - H: Hasher, -{ - /// instantiate new keyspaced db - pub fn new(db: &'a mut DB, ks: &'a [u8]) -> Self { - KeySpacedDBMut(db, ks, PhantomData) - } -} - -impl<'a, DB, H, T> hash_db::HashDBRef for KeySpacedDB<'a, DB, H> where - DB: hash_db::HashDBRef, - H: Hasher, - T: From<&'static [u8]>, -{ - fn get(&self, key: &H::Out, prefix: Prefix) -> Option { - let derived_prefix = keyspace_as_prefix_alloc(self.1, prefix); - self.0.get(key, (&derived_prefix.0, derived_prefix.1)) - } - - fn contains(&self, key: &H::Out, prefix: Prefix) -> bool { - let derived_prefix = keyspace_as_prefix_alloc(self.1, prefix); - self.0.contains(key, (&derived_prefix.0, derived_prefix.1)) - } -} - -impl<'a, DB, H, T> hash_db::HashDB for KeySpacedDBMut<'a, DB, H> where - DB: hash_db::HashDB, - H: Hasher, - T: Default + PartialEq + for<'b> From<&'b [u8]> + Clone + Send + Sync, -{ - fn get(&self, key: &H::Out, prefix: Prefix) -> Option { - let derived_prefix = keyspace_as_prefix_alloc(self.1, prefix); - self.0.get(key, (&derived_prefix.0, derived_prefix.1)) - } - - fn contains(&self, key: &H::Out, prefix: Prefix) -> bool { - let derived_prefix = keyspace_as_prefix_alloc(self.1, prefix); - self.0.contains(key, (&derived_prefix.0, derived_prefix.1)) - } - - fn insert(&mut self, prefix: Prefix, value: &[u8]) -> H::Out { - let derived_prefix = keyspace_as_prefix_alloc(self.1, prefix); - self.0.insert((&derived_prefix.0, derived_prefix.1), value) - } - - fn emplace(&mut self, key: H::Out, prefix: Prefix, value: T) { - let derived_prefix = keyspace_as_prefix_alloc(self.1, prefix); - self.0.emplace(key, (&derived_prefix.0, derived_prefix.1), value) - } - - fn remove(&mut self, key: &H::Out, prefix: Prefix) { - let derived_prefix = keyspace_as_prefix_alloc(self.1, prefix); - self.0.remove(key, (&derived_prefix.0, derived_prefix.1)) - } -} - -impl<'a, DB, H, T> hash_db::AsHashDB for KeySpacedDBMut<'a, DB, H> where - DB: hash_db::HashDB, - H: Hasher, - T: Default + PartialEq + for<'b> From<&'b [u8]> + Clone + Send + Sync, -{ - fn as_hash_db(&self) -> &dyn hash_db::HashDB { &*self } - - fn as_hash_db_mut<'b>(&'b mut self) -> &'b mut (dyn hash_db::HashDB + 'b) { - &mut *self - } -} - /// Constants used into trie simplification codec. mod trie_constants { pub const EMPTY_TRIE: u8 = 0; @@ -464,7 +282,8 @@ mod tests { use super::*; use codec::{Encode, Compact}; use sp_core::Blake2Hasher; - use hash_db::{HashDB, Hasher}; + use hash_db::HashDB; + use sp_core::InnerHasher; use trie_db::{DBValue, TrieMut, Trie, NodeCodec as NodeCodecT}; use trie_standardmap::{Alphabet, ValueMode, StandardMap}; use hex_literal::hex; @@ -648,7 +467,7 @@ mod tests { #[test] fn random_should_work() { - let mut seed = ::Out::zero(); + let mut seed = ::Out::zero(); for test_i in 0..10000 { if test_i % 50 == 0 { println!("{:?} of 10000 stress tests done", test_i);