diff --git a/core/client/db/src/lib.rs b/core/client/db/src/lib.rs index f5ece21fbb710..5da9b940c5f21 100644 --- a/core/client/db/src/lib.rs +++ b/core/client/db/src/lib.rs @@ -47,14 +47,15 @@ use hash_db::{Hasher, Prefix}; use kvdb::{KeyValueDB, DBTransaction}; use trie::{MemoryDB, PrefixedMemoryDB, prefixed_key}; use parking_lot::{Mutex, RwLock}; -use primitives::{H256, Blake2Hasher, ChangesTrieConfiguration, convert_hash}; +use primitives::{H256, Blake2Hasher, ChangesTrieConfiguration, convert_hash, + child_trie::{ChildTrie, ChildTrieReadRef}}; use primitives::storage::well_known_keys; use sr_primitives::{ generic::{BlockId, DigestItem}, Justification, StorageOverlay, ChildrenStorageOverlay, BuildStorage }; use sr_primitives::traits::{ - Block as BlockT, Header as HeaderT, NumberFor, Zero, One, SaturatedConversion + Block as BlockT, Header as HeaderT, NumberFor, Zero, One, SaturatedConversion, }; use state_machine::backend::Backend as StateBackend; use executor::RuntimeInfo; @@ -109,7 +110,7 @@ impl Drop for RefTrackingState { } impl StateBackend for RefTrackingState { - type Error = >::Error; + type Error = >::Error; type Transaction = >::Transaction; type TrieBackendStorage = >::TrieBackendStorage; @@ -121,16 +122,20 @@ impl StateBackend for RefTrackingState { self.state.storage_hash(key) } - fn child_storage(&self, storage_key: &[u8], key: &[u8]) -> Result>, Self::Error> { - self.state.child_storage(storage_key, key) + fn child_trie(&self, storage_key: &[u8]) -> Result, Self::Error> { + self.state.child_trie(storage_key) + } + + fn child_storage(&self, child_trie: ChildTrieReadRef, key: &[u8]) -> Result>, Self::Error> { + self.state.child_storage(child_trie, key) } fn exists_storage(&self, key: &[u8]) -> Result { self.state.exists_storage(key) } - fn exists_child_storage(&self, storage_key: &[u8], key: &[u8]) -> Result { - self.state.exists_child_storage(storage_key, key) + fn exists_child_storage(&self, child_trie: ChildTrieReadRef, key: &[u8]) -> Result { + self.state.exists_child_storage(child_trie, key) } fn for_keys_with_prefix(&self, prefix: &[u8], f: F) { @@ -141,12 +146,17 @@ impl StateBackend for RefTrackingState { self.state.for_key_values_with_prefix(prefix, f) } - fn for_keys_in_child_storage(&self, storage_key: &[u8], f: F) { - self.state.for_keys_in_child_storage(storage_key, f) + fn for_keys_in_child_storage(&self, child_trie: ChildTrieReadRef, f: F) { + self.state.for_keys_in_child_storage(child_trie, f) } - fn for_child_keys_with_prefix(&self, storage_key: &[u8], prefix: &[u8], f: F) { - self.state.for_child_keys_with_prefix(storage_key, prefix, f) + fn for_child_keys_with_prefix( + &self, + child_trie: ChildTrieReadRef, + prefix: &[u8], + f: F, + ) { + self.state.for_child_keys_with_prefix(child_trie, prefix, f) } fn storage_root(&self, delta: I) -> (H256, Self::Transaction) @@ -156,11 +166,11 @@ impl StateBackend for RefTrackingState { self.state.storage_root(delta) } - fn child_storage_root(&self, storage_key: &[u8], delta: I) -> (Vec, bool, Self::Transaction) + fn child_storage_root(&self, child_trie: &ChildTrie, delta: I) -> (Vec, bool, Self::Transaction) where I: IntoIterator, Option>)>, { - self.state.child_storage_root(storage_key, delta) + self.state.child_storage_root(child_trie, delta) } fn pairs(&self) -> Vec<(Vec, Vec)> { @@ -171,8 +181,8 @@ impl StateBackend for RefTrackingState { self.state.keys(prefix) } - fn child_keys(&self, child_key: &[u8], prefix: &[u8]) -> Vec> { - self.state.child_keys(child_key, prefix) + fn child_keys(&self, child_trie: ChildTrieReadRef, prefix: &[u8]) -> Vec> { + self.state.child_keys(child_trie, prefix) } fn as_trie_backend(&mut self) -> Option<&state_machine::TrieBackend> { @@ -469,15 +479,10 @@ where Block: BlockT, return Err(client::error::Error::GenesisInvalid.into()); } - for child_key in children.keys() { - if !well_known_keys::is_child_storage_key(&child_key) { - return Err(client::error::Error::GenesisInvalid.into()); - } - } let child_delta = children.into_iter() - .map(|(storage_key, child_overlay)| - (storage_key, child_overlay.into_iter().map(|(k, v)| (k, Some(v))))); + .map(|(_storage_key, (child_overlay, subtrie))| + (subtrie, child_overlay.into_iter().map(|(k, v)| (k, Some(v))))); let (root, transaction) = self.old_state.full_storage_root( top.into_iter().map(|(k, v)| (k, Some(v))), @@ -553,8 +558,10 @@ struct DbGenesisStorage(pub H256); impl DbGenesisStorage { pub fn new() -> Self { let mut root = H256::default(); - let mut mdb = MemoryDB::::default(); - state_machine::TrieDBMut::::new(&mut mdb, &mut root); + { + let mut mdb = MemoryDB::::default(); + state_machine::TrieDBMut::::new(&mut mdb, &mut root); + } DbGenesisStorage(root) } } @@ -1713,6 +1720,7 @@ mod tests { ).unwrap(); backend.commit_operation(op).unwrap(); + assert_eq!(backend.storage.db.get( columns::STATE, &trie::prefixed_key::(&key, EMPTY_PREFIX) @@ -1787,7 +1795,6 @@ mod tests { backend.commit_operation(op).unwrap(); - assert!(backend.storage.db.get( columns::STATE, &trie::prefixed_key::(&key, EMPTY_PREFIX) diff --git a/core/client/db/src/storage_cache.rs b/core/client/db/src/storage_cache.rs index 09c0f71fdd56c..0098c37ca948f 100644 --- a/core/client/db/src/storage_cache.rs +++ b/core/client/db/src/storage_cache.rs @@ -24,6 +24,9 @@ use hash_db::Hasher; use sr_primitives::traits::{Block as BlockT, Header}; use state_machine::{backend::Backend as StateBackend, TrieBackend}; use log::trace; +use primitives::child_trie::ChildTrie; +use primitives::child_trie::ChildTrieReadRef; + use super::{StorageCollection, ChildStorageCollection}; use std::hash::Hash as StdHash; const STATE_CACHE_BLOCKS: usize = 12; @@ -39,6 +42,7 @@ pub struct Cache { /// Storage hashes cache. `None` indicates that key is known to be missing. lru_hashes: LRUMap>, /// Storage cache for child trie. `None` indicates that key is known to be missing. + /// Key is a pair of child trie `KeySpace` and `Key` for value in the child trie. lru_child_storage: LRUMap>, /// Information on the modifications in recently committed blocks; specifically which keys /// changed in which block. Ordered by block number. @@ -149,7 +153,7 @@ impl Cache { pub fn used_storage_cache_size(&self) -> usize { self.lru_storage.used_size() + self.lru_child_storage.used_size() - // ignore small hashes storage and self.lru_hashes.used_size() + // ignore small hashes storage and self.lru_hashes.used_size() } /// Synchronize the shared cache with the best block state. @@ -504,8 +508,12 @@ impl, B: BlockT> StateBackend for CachingState< Ok(hash) } - fn child_storage(&self, storage_key: &[u8], key: &[u8]) -> Result>, Self::Error> { - let key = (storage_key.to_vec(), key.to_vec()); + fn child_storage( + &self, + child_trie: ChildTrieReadRef, + key: &[u8], + ) -> Result>, Self::Error> { + let key = (child_trie.keyspace().to_vec(), key.to_vec()); let local_cache = self.cache.local_cache.upgradable_read(); if let Some(entry) = local_cache.child_storage.get(&key).cloned() { trace!("Found in local cache: {:?}", key); @@ -519,7 +527,7 @@ impl, B: BlockT> StateBackend for CachingState< } } trace!("Cache miss: {:?}", key); - let value = self.state.child_storage(storage_key, &key.1[..])?; + let value = self.state.child_storage(child_trie, &key.1[..])?; RwLockUpgradableReadGuard::upgrade(local_cache).child_storage.insert(key, value.clone()); Ok(value) } @@ -528,8 +536,8 @@ impl, B: BlockT> StateBackend for CachingState< Ok(self.storage(key)?.is_some()) } - fn exists_child_storage(&self, storage_key: &[u8], key: &[u8]) -> Result { - self.state.exists_child_storage(storage_key, key) + fn exists_child_storage(&self, child_trie: ChildTrieReadRef, key: &[u8]) -> Result { + self.state.exists_child_storage(child_trie, key) } fn for_keys_with_prefix(&self, prefix: &[u8], f: F) { @@ -540,12 +548,12 @@ impl, B: BlockT> StateBackend for CachingState< self.state.for_key_values_with_prefix(prefix, f) } - fn for_keys_in_child_storage(&self, storage_key: &[u8], f: F) { - self.state.for_keys_in_child_storage(storage_key, f) + fn for_keys_in_child_storage(&self, child_trie: ChildTrieReadRef, f: F) { + self.state.for_keys_in_child_storage(child_trie, f) } - fn for_child_keys_with_prefix(&self, storage_key: &[u8], prefix: &[u8], f: F) { - self.state.for_child_keys_with_prefix(storage_key, prefix, f) + fn for_child_keys_with_prefix(&self, child_trie: ChildTrieReadRef, prefix: &[u8], f: F) { + self.state.for_child_keys_with_prefix(child_trie, prefix, f) } fn storage_root(&self, delta: I) -> (H::Out, Self::Transaction) @@ -556,12 +564,12 @@ impl, B: BlockT> StateBackend for CachingState< self.state.storage_root(delta) } - fn child_storage_root(&self, storage_key: &[u8], delta: I) -> (Vec, bool, Self::Transaction) + fn child_storage_root(&self, child_trie: &ChildTrie, delta: I) -> (Vec, bool, Self::Transaction) where I: IntoIterator, Option>)>, H::Out: Ord { - self.state.child_storage_root(storage_key, delta) + self.state.child_storage_root(child_trie, delta) } fn pairs(&self) -> Vec<(Vec, Vec)> { @@ -572,8 +580,8 @@ impl, B: BlockT> StateBackend for CachingState< self.state.keys(prefix) } - fn child_keys(&self, child_key: &[u8], prefix: &[u8]) -> Vec> { - self.state.child_keys(child_key, prefix) + fn child_keys(&self, child_trie: ChildTrieReadRef, prefix: &[u8]) -> Vec> { + self.state.child_keys(child_trie, prefix) } fn as_trie_backend(&mut self) -> Option<&TrieBackend> { diff --git a/core/client/src/client.rs b/core/client/src/client.rs index 50e962c8b505c..ec8951a1ae835 100644 --- a/core/client/src/client.rs +++ b/core/client/src/client.rs @@ -30,6 +30,8 @@ use primitives::{ NeverNativeValue, ExecutionContext, NativeOrEncoded, storage::{StorageKey, StorageData, well_known_keys}, offchain, + child_trie::{ChildTrie, ChildTrieReadRef}, + storage::StorageKeySpace, }; use substrate_telemetry::{telemetry, SUBSTRATE_INFO}; use sr_primitives::{ @@ -145,7 +147,7 @@ pub trait BlockchainEvents { fn storage_changes_notification_stream( &self, filter_keys: Option<&[StorageKey]>, - child_filter_keys: Option<&[(StorageKey, Option>)]>, + child_filter_keys: Option<&[(StorageKeySpace, Option>)]>, ) -> error::Result>; } @@ -308,10 +310,10 @@ impl Client where execution_strategies: ExecutionStrategies ) -> error::Result { if backend.blockchain().header(BlockId::Number(Zero::zero()))?.is_none() { - let (genesis_storage, children_genesis_storage) = build_genesis_storage.build_storage()?; + let genesis_storage = build_genesis_storage.build_storage()?; let mut op = backend.begin_operation()?; backend.begin_state_operation(&mut op, BlockId::Hash(Default::default()))?; - let state_root = op.reset_storage(genesis_storage, children_genesis_storage)?; + let state_root = op.reset_storage(genesis_storage.top, genesis_storage.children)?; let genesis_block = genesis::construct_genesis_block::(state_root.into()); info!("Initializing Genesis block/state (state: {}, header-hash: {})", genesis_block.header().state_root(), @@ -371,6 +373,18 @@ impl Client where ) } + /// Given a `BlockId` and a child storage key, + /// return the matching child storage keys. + pub fn child_trie( + &self, + id: &BlockId, + child_key: &StorageKey + ) -> error::Result> { + self.state_at(id)? + .child_trie(&child_key.0[..]) + .map_err(|e| error::Error::from_state(Box::new(e))) + } + /// Given a `BlockId` and a key, return the value under the hash in that block. pub fn storage_hash(&self, id: &BlockId, key: &StorageKey) -> error::Result> { @@ -383,26 +397,27 @@ impl Client where pub fn child_storage_keys( &self, id: &BlockId, - child_storage_key: &StorageKey, + child_trie: ChildTrieReadRef, key_prefix: &StorageKey ) -> error::Result> { let keys = self.state_at(id)? - .child_keys(&child_storage_key.0, &key_prefix.0) + .child_keys(child_trie, &key_prefix.0) .into_iter() .map(StorageKey) .collect(); Ok(keys) } - /// Given a `BlockId`, a key and a child storage key, return the value under the key in that block. + /// Given a `BlockId`, a key and a child trie reference, + /// return the value under the key in that block. pub fn child_storage( &self, id: &BlockId, - child_storage_key: &StorageKey, + child_trie: ChildTrieReadRef, key: &StorageKey ) -> error::Result> { Ok(self.state_at(id)? - .child_storage(&child_storage_key.0, &key.0).map_err(|e| error::Error::from_state(Box::new(e)))? + .child_storage(child_trie, &key.0).map_err(|e| error::Error::from_state(Box::new(e)))? .map(StorageData)) } @@ -410,11 +425,11 @@ impl Client where pub fn child_storage_hash( &self, id: &BlockId, - child_storage_key: &StorageKey, + child_trie: ChildTrieReadRef, key: &StorageKey ) -> error::Result> { Ok(self.state_at(id)? - .child_storage_hash(&child_storage_key.0, &key.0).map_err(|e| error::Error::from_state(Box::new(e)))? + .child_storage_hash(child_trie, &key.0).map_err(|e| error::Error::from_state(Box::new(e)))? ) } @@ -443,16 +458,16 @@ impl Client where .map_err(Into::into)) } - /// Reads child storage value at a given block + storage_key + key, returning + /// Reads child storage value at a given block + child trie reference + key, returning /// read proof. pub fn read_child_proof( &self, id: &BlockId, - storage_key: &[u8], + child_trie: ChildTrieReadRef, key: &[u8] ) -> error::Result>> { self.state_at(id) - .and_then(|state| prove_child_read(state, storage_key, key) + .and_then(|state| prove_child_read(state, child_trie, key) .map(|(_, proof)| proof) .map_err(Into::into)) } @@ -1058,7 +1073,7 @@ impl Client where overlay.commit_prospective(); let (top, children) = overlay.into_committed(); - let children = children.map(|(sk, it)| (sk, it.collect())).collect(); + let children = children.map(|(ks, it)| (ks, it.collect())).collect(); if import_headers.post().state_root() != &storage_update.1 { return Err(error::Error::InvalidStateRoot); } @@ -1607,7 +1622,7 @@ where fn storage_changes_notification_stream( &self, filter_keys: Option<&[StorageKey]>, - child_filter_keys: Option<&[(StorageKey, Option>)]>, + child_filter_keys: Option<&[(StorageKeySpace, Option>)]>, ) -> error::Result> { Ok(self.storage_notifications.lock().listen(filter_keys, child_filter_keys)) } diff --git a/core/client/src/in_mem.rs b/core/client/src/in_mem.rs index 3c6a1e18c01d6..13a816821e42b 100644 --- a/core/client/src/in_mem.rs +++ b/core/client/src/in_mem.rs @@ -493,8 +493,8 @@ where check_genesis_storage(&top, &children)?; let child_delta = children.into_iter() - .map(|(storage_key, child_overlay)| - (storage_key, child_overlay.into_iter().map(|(k, v)| (k, Some(v))))); + .map(|(_keyspace, (child_overlay, subtrie))| + (subtrie, child_overlay.into_iter().map(|(k, v)| (k, Some(v))))); let (root, transaction) = self.old_state.full_storage_root( top.into_iter().map(|(k, v)| (k, Some(v))), @@ -785,9 +785,8 @@ pub fn check_genesis_storage(top: &StorageOverlay, children: &ChildrenStorageOve return Err(error::Error::GenesisInvalid.into()); } - if children.keys().any(|child_key| !well_known_keys::is_child_storage_key(&child_key)) { - return Err(error::Error::GenesisInvalid.into()); - } + debug_assert!(!children.iter() + .any(|(_, (_, subtrie))| !well_known_keys::is_child_storage_key(&subtrie.parent_trie()[..]))); Ok(()) } diff --git a/core/client/src/lib.rs b/core/client/src/lib.rs index 1313b8b80eb5d..55d42f6c56230 100644 --- a/core/client/src/lib.rs +++ b/core/client/src/lib.rs @@ -48,7 +48,7 @@ //! use std::sync::Arc; //! use substrate_client::{Client, in_mem::Backend, LocalCallExecutor}; //! use primitives::Blake2Hasher; -//! use sr_primitives::{StorageOverlay, ChildrenStorageOverlay}; +//! use sr_primitives::{StorageContent}; //! use executor::NativeExecutor; //! //! // In this example, we're using the `Block` and `RuntimeApi` types from the @@ -66,7 +66,7 @@ //! None, //! ), //! // This parameter provides the storage for the chain genesis. -//! <(StorageOverlay, ChildrenStorageOverlay)>::default(), +//! StorageContent::default(), //! Default::default() //! ); //! ``` diff --git a/core/client/src/light/backend.rs b/core/client/src/light/backend.rs index 6b2f2f5c0af4c..91ffb3d7ebb17 100644 --- a/core/client/src/light/backend.rs +++ b/core/client/src/light/backend.rs @@ -22,7 +22,10 @@ use std::sync::{Arc, Weak}; use parking_lot::{RwLock, Mutex}; use sr_primitives::{generic::BlockId, Justification, StorageOverlay, ChildrenStorageOverlay}; -use state_machine::{Backend as StateBackend, TrieBackend, backend::InMemory as InMemoryState, ChangesTrieTransaction}; +use primitives::child_trie::ChildTrie; +use primitives::child_trie::ChildTrieReadRef; +use state_machine::{Backend as StateBackend, TrieBackend, ChangesTrieTransaction}; +use state_machine::backend::{InMemory as InMemoryState, StorageContent}; use sr_primitives::traits::{Block as BlockT, NumberFor, Zero, Header}; use crate::in_mem::{self, check_genesis_storage}; use crate::backend::{ @@ -293,18 +296,15 @@ where check_genesis_storage(&top, &children)?; // this is only called when genesis block is imported => shouldn't be performance bottleneck - let mut storage: HashMap>, StorageOverlay> = HashMap::new(); - storage.insert(None, top); - + let mut storage = StorageContent { top, children: Default::default() }; // create a list of children keys to re-compute roots for - let child_delta = children.keys() - .cloned() - .map(|storage_key| (storage_key, None)) + let child_delta : Vec<(ChildTrie, _)> = children.iter() + .map(|(_, (_, child_trie))| (child_trie.clone(), None)) .collect::>(); // make sure to persist the child storage - for (child_key, child_storage) in children { - storage.insert(Some(child_key), child_storage); + for (child_key, (child_storage, child_trie)) in children { + storage.children.insert(child_key, (child_storage, child_trie)); } let storage_update: InMemoryState = storage.into(); @@ -373,7 +373,7 @@ where ) } - fn child_storage(&self, _storage_key: &[u8], _key: &[u8]) -> ClientResult>> { + fn child_storage(&self, _child_trie: ChildTrieReadRef, _key: &[u8]) -> ClientResult>> { Err(ClientError::NotAvailableOnLightClient.into()) } @@ -385,13 +385,13 @@ where // whole state is not available on light node } - fn for_keys_in_child_storage(&self, _storage_key: &[u8], _action: A) { + fn for_keys_in_child_storage(&self, _child_trie: ChildTrieReadRef, _action: A) { // whole state is not available on light node } fn for_child_keys_with_prefix( &self, - _storage_key: &[u8], + _child_trie: ChildTrieReadRef, _prefix: &[u8], _action: A, ) { @@ -405,7 +405,7 @@ where (H::Out::default(), ()) } - fn child_storage_root(&self, _key: &[u8], _delta: I) -> (Vec, bool, Self::Transaction) + fn child_storage_root(&self, _child_trie: &ChildTrie, _delta: I) -> (Vec, bool, Self::Transaction) where I: IntoIterator, Option>)> { @@ -448,12 +448,12 @@ where } } - fn child_storage(&self, storage_key: &[u8], key: &[u8]) -> ClientResult>> { + fn child_storage(&self, child_trie: ChildTrieReadRef, key: &[u8]) -> ClientResult>> { match *self { OnDemandOrGenesisState::OnDemand(ref state) => - StateBackend::::child_storage(state, storage_key, key), + StateBackend::::child_storage(state, child_trie, key), OnDemandOrGenesisState::Genesis(ref state) => - Ok(state.child_storage(storage_key, key).expect(IN_MEMORY_EXPECT_PROOF)), + Ok(state.child_storage(child_trie, key).expect(IN_MEMORY_EXPECT_PROOF)), } } @@ -473,26 +473,25 @@ where } } - - fn for_keys_in_child_storage(&self, storage_key: &[u8], action: A) { + fn for_keys_in_child_storage(&self, child_trie: ChildTrieReadRef, action: A) { match *self { OnDemandOrGenesisState::OnDemand(ref state) => - StateBackend::::for_keys_in_child_storage(state, storage_key, action), - OnDemandOrGenesisState::Genesis(ref state) => state.for_keys_in_child_storage(storage_key, action), + StateBackend::::for_keys_in_child_storage(state, child_trie, action), + OnDemandOrGenesisState::Genesis(ref state) => state.for_keys_in_child_storage(child_trie, action), } } fn for_child_keys_with_prefix( &self, - storage_key: &[u8], + child_trie: ChildTrieReadRef, prefix: &[u8], action: A, ) { match *self { OnDemandOrGenesisState::OnDemand(ref state) => - StateBackend::::for_child_keys_with_prefix(state, storage_key, prefix, action), + StateBackend::::for_child_keys_with_prefix(state, child_trie, prefix, action), OnDemandOrGenesisState::Genesis(ref state) => - state.for_child_keys_with_prefix(storage_key, prefix, action), + state.for_child_keys_with_prefix(child_trie, prefix, action), } } @@ -510,15 +509,15 @@ where } } - fn child_storage_root(&self, key: &[u8], delta: I) -> (Vec, bool, Self::Transaction) + fn child_storage_root(&self, child_trie: &ChildTrie, delta: I) -> (Vec, bool, Self::Transaction) where I: IntoIterator, Option>)> { match *self { OnDemandOrGenesisState::OnDemand(ref state) => - StateBackend::::child_storage_root(state, key, delta), + StateBackend::::child_storage_root(state, child_trie, delta), OnDemandOrGenesisState::Genesis(ref state) => { - let (root, is_equal, _) = state.child_storage_root(key, delta); + let (root, is_equal, _) = state.child_storage_root(child_trie, delta); (root, is_equal, ()) }, } diff --git a/core/client/src/light/fetcher.rs b/core/client/src/light/fetcher.rs index 14a3c72b05c45..157b5b23691a7 100644 --- a/core/client/src/light/fetcher.rs +++ b/core/client/src/light/fetcher.rs @@ -24,6 +24,7 @@ use std::future::Future; use hash_db::{HashDB, Hasher, EMPTY_PREFIX}; use codec::{Decode, Encode}; use primitives::{ChangesTrieConfiguration, convert_hash}; +use primitives::child_trie::ChildTrieRead; use sr_primitives::traits::{ Block as BlockT, Header as HeaderT, Hash, HashFor, NumberFor, SimpleArithmetic, CheckedConversion, Zero, @@ -86,8 +87,8 @@ pub struct RemoteReadChildRequest { pub block: Header::Hash, /// Header of block at which read is performed. pub header: Header, - /// Storage key for child. - pub storage_key: Vec, + /// Child trie to query. + pub child_trie: ChildTrieRead, /// Child storage key to read. pub key: Vec, /// Number of times to retry request. None means that default RETRY_COUNT is used. @@ -409,11 +410,10 @@ impl FetchChecker for LightDataChecker> ) -> ClientResult>> { read_child_proof_check::( - convert_hash(request.header.state_root()), remote_proof, - &request.storage_key, - &request.key) - .map_err(Into::into) + request.child_trie.node_ref(), + &request.key[..] + ).map_err(Into::into) } fn check_execution_proof( @@ -514,6 +514,7 @@ pub mod tests { use crate::light::blockchain::tests::{DummyStorage, DummyBlockchain}; use primitives::{blake2_256, Blake2Hasher, H256}; use primitives::storage::{well_known_keys, StorageKey}; + use primitives::child_trie::ChildTrie; use sr_primitives::generic::BlockId; use state_machine::Backend; use super::*; @@ -596,14 +597,21 @@ pub mod tests { (local_checker, remote_block_header, remote_read_proof, heap_pages) } - fn prepare_for_read_child_proof_check() -> (TestChecker, Header, Vec>, Vec) { + fn prepare_for_read_child_proof_check() -> (TestChecker, Header, Vec>, Vec, ChildTrieRead) { use test_client::DefaultTestClientBuilderExt; use test_client::TestClientBuilderExt; + let child_trie = ChildTrie::fetch_or_new(|_| None, |_| (), b"default:child1", || 1u128); // prepare remote client let remote_client = test_client::TestClientBuilder::new() - .add_extra_child_storage(b":child_storage:default:child1".to_vec(), b"key1".to_vec(), b"value1".to_vec()) + .add_extra_child_storage(&child_trie, b"key1".to_vec(), b"value1".to_vec()) .build(); let remote_block_id = BlockId::Number(0); + let storage_key = StorageKey(child_trie.parent_slice().to_vec()); + // update child trie to contain a root + let child_trie = remote_client.child_trie(&remote_block_id, &storage_key) + .expect("just added").expect("just added"); + let child_trie_read = child_trie.child_trie_read().expect("accessed from client"); + let remote_block_hash = remote_client.block_hash(0).unwrap().unwrap(); let mut remote_block_header = remote_client.header(&remote_block_id).unwrap().unwrap(); remote_block_header.state_root = remote_client.state_at(&remote_block_id).unwrap() @@ -612,13 +620,13 @@ pub mod tests { // 'fetch' child read proof from remote node let child_value = remote_client.child_storage( &remote_block_id, - &StorageKey(b":child_storage:default:child1".to_vec()), + child_trie.node_ref(), &StorageKey(b"key1".to_vec()), ).unwrap().unwrap().0; assert_eq!(b"value1"[..], child_value[..]); let remote_read_proof = remote_client.read_child_proof( &remote_block_id, - b":child_storage:default:child1", + child_trie.node_ref(), b"key1", ).unwrap(); @@ -633,7 +641,7 @@ pub mod tests { ).unwrap(); let local_executor = NativeExecutor::::new(None); let local_checker = LightDataChecker::new(Arc::new(DummyBlockchain::new(DummyStorage::new())), local_executor); - (local_checker, remote_block_header, remote_read_proof, child_value) + (local_checker, remote_block_header, remote_read_proof, child_value, child_trie_read) } fn prepare_for_header_proof_check(insert_cht: bool) -> (TestChecker, Hash, Header, Vec>) { @@ -689,12 +697,13 @@ pub mod tests { remote_block_header, remote_read_proof, result, + child_trie, ) = prepare_for_read_child_proof_check(); assert_eq!((&local_checker as &dyn FetchChecker).check_read_child_proof( &RemoteReadChildRequest::
{ block: remote_block_header.hash(), header: remote_block_header, - storage_key: b":child_storage:default:child1".to_vec(), + child_trie, key: b"key1".to_vec(), retry_count: None, }, diff --git a/core/client/src/notifications.rs b/core/client/src/notifications.rs index 37f90dcc0ba64..53590265f106c 100644 --- a/core/client/src/notifications.rs +++ b/core/client/src/notifications.rs @@ -23,22 +23,22 @@ use std::{ use fnv::{FnvHashSet, FnvHashMap}; use futures03::channel::mpsc; -use primitives::storage::{StorageKey, StorageData}; +use primitives::storage::{StorageKey, StorageData, StorageKeySpace}; use sr_primitives::traits::Block as BlockT; /// Storage change set #[derive(Debug)] pub struct StorageChangeSet { changes: Arc)>>, - child_changes: Arc)>)>>, + child_changes: Arc)>)>>, filter: Option>, - child_filters: Option>>>, + child_filters: Option>>>, } impl StorageChangeSet { /// Convert the change set into iterator over storage items. pub fn iter<'a>(&'a self) - -> impl Iterator, &'a StorageKey, Option<&'a StorageData>)> + 'a { + -> impl Iterator, &'a StorageKey, Option<&'a StorageData>)> + 'a { let top = self.changes .iter() .filter(move |&(key, _)| match self.filter { @@ -48,16 +48,16 @@ impl StorageChangeSet { .map(move |(k,v)| (None, k, v.as_ref())); let children = self.child_changes .iter() - .filter_map(move |(sk, changes)| { + .filter_map(move |(keyspace, changes)| { if let Some(cf) = self.child_filters.as_ref() { - if let Some(filter) = cf.get(sk) { + if let Some(filter) = cf.get(keyspace) { Some(changes .iter() .filter(move |&(key, _)| match filter { Some(ref filter) => filter.contains(key), None => true, }) - .map(move |(k,v)| (Some(sk), k, v.as_ref()))) + .map(move |(k,v)| (Some(keyspace), k, v.as_ref()))) } else { None } } else { None } }) @@ -77,14 +77,14 @@ pub struct StorageNotifications { next_id: SubscriberId, wildcard_listeners: FnvHashSet, listeners: HashMap>, - child_listeners: HashMap>, FnvHashSet )>, sinks: FnvHashMap, Option>, - Option>>>, + Option>>>, )>, } @@ -137,9 +137,9 @@ impl StorageNotifications { changes.push((k, v.map(StorageData))); } } - for (sk, changeset) in child_changeset { - let sk = StorageKey(sk); - if let Some((cl, cw)) = self.child_listeners.get(&sk) { + for (keyspace, changeset) in child_changeset { + let keyspace = StorageKeySpace(keyspace); + if let Some((cl, cw)) = self.child_listeners.get(&keyspace) { let mut changes = Vec::new(); for (k, v) in changeset { let k = StorageKey(k); @@ -156,7 +156,7 @@ impl StorageNotifications { } } if !changes.is_empty() { - child_changes.push((sk, changes)); + child_changes.push((keyspace, changes)); } } } @@ -270,7 +270,7 @@ impl StorageNotifications { pub fn listen( &mut self, filter_keys: Option<&[StorageKey]>, - filter_child_keys: Option<&[(StorageKey, Option>)]>, + filter_child_keys: Option<&[(StorageKeySpace, Option>)]>, ) -> StorageEventStream { self.next_id += 1; let current_id = self.next_id; @@ -313,7 +313,7 @@ mod tests { type TestChangeSet = ( Vec<(StorageKey, Option)>, - Vec<(StorageKey, Vec<(StorageKey, Option)>)>, + Vec<(StorageKeySpace, Vec<(StorageKey, Option)>)>, ); #[cfg(test)] @@ -321,8 +321,8 @@ mod tests { fn from(changes: TestChangeSet) -> Self { // warning hardcoded child trie wildcard to test upon let child_filters = Some([ - (StorageKey(vec![4]), None), - (StorageKey(vec![5]), None), + (StorageKeySpace(vec![4]), None), + (StorageKeySpace(vec![5]), None), ].into_iter().cloned().collect()); StorageChangeSet { changes: Arc::new(changes.0), @@ -346,7 +346,7 @@ mod tests { fn triggering_change_should_notify_wildcard_listeners() { // given let mut notifications = StorageNotifications::::default(); - let child_filter = [(StorageKey(vec![4]), None)]; + let child_filter = [(StorageKeySpace(vec![4]), None)]; let mut recv = futures03::executor::block_on_stream( notifications.listen(None, Some(&child_filter[..])) ); @@ -371,7 +371,7 @@ mod tests { assert_eq!(recv.next().unwrap(), (Hash::from_low_u64_be(1), (vec![ (StorageKey(vec![2]), Some(StorageData(vec![3]))), (StorageKey(vec![3]), None), - ], vec![(StorageKey(vec![4]), vec![ + ], vec![(StorageKeySpace(vec![4]), vec![ (StorageKey(vec![5]), Some(StorageData(vec![4]))), (StorageKey(vec![6]), None), ])]).into())); @@ -381,7 +381,7 @@ mod tests { fn should_only_notify_interested_listeners() { // given let mut notifications = StorageNotifications::::default(); - let child_filter = [(StorageKey(vec![4]), Some(vec![StorageKey(vec![5])]))]; + let child_filter = [(StorageKeySpace(vec![4]), Some(vec![StorageKey(vec![5])]))]; let mut recv1 = futures03::executor::block_on_stream( notifications.listen(Some(&[StorageKey(vec![1])]), None) ); @@ -418,7 +418,7 @@ mod tests { ], vec![]).into())); assert_eq!(recv3.next().unwrap(), (Hash::from_low_u64_be(1), (vec![], vec![ - (StorageKey(vec![4]), vec![(StorageKey(vec![5]), Some(StorageData(vec![4])))]), + (StorageKeySpace(vec![4]), vec![(StorageKey(vec![5]), Some(StorageData(vec![4])))]), ]).into())); } @@ -428,7 +428,7 @@ mod tests { // given let mut notifications = StorageNotifications::::default(); { - let child_filter = [(StorageKey(vec![4]), Some(vec![StorageKey(vec![5])]))]; + let child_filter = [(StorageKeySpace(vec![4]), Some(vec![StorageKey(vec![5])]))]; let _recv1 = futures03::executor::block_on_stream( notifications.listen(Some(&[StorageKey(vec![1])]), None) ); @@ -452,7 +452,11 @@ mod tests { (vec![1], None), ]; let c_changeset = empty::<(_, Empty<_>)>(); - notifications.trigger(&Hash::from_low_u64_be(1), changeset.into_iter(), c_changeset); + notifications.trigger( + &Hash::from_low_u64_be(1), + changeset.into_iter(), + c_changeset, + ); // then assert_eq!(notifications.listeners.len(), 0); @@ -470,7 +474,11 @@ mod tests { // when let changeset = vec![]; let c_changeset = empty::<(_, Empty<_>)>(); - notifications.trigger(&Hash::from_low_u64_be(1), changeset.into_iter(), c_changeset); + notifications.trigger( + &Hash::from_low_u64_be(1), + changeset.into_iter(), + c_changeset, + ); recv }; diff --git a/core/executor/src/wasm_executor.rs b/core/executor/src/wasm_executor.rs index b22fe3d363af3..fe7080be58fd0 100644 --- a/core/executor/src/wasm_executor.rs +++ b/core/executor/src/wasm_executor.rs @@ -24,12 +24,13 @@ use wasmi::{ Module, ModuleInstance, MemoryInstance, MemoryRef, TableRef, ImportsBuilder, ModuleRef, memory_units::Pages, RuntimeValue::{I32, I64, self}, }; -use state_machine::{Externalities, ChildStorageKey}; +use state_machine::Externalities; use crate::error::{Error, Result}; use codec::Encode; use primitives::{ blake2_128, blake2_256, twox_64, twox_128, twox_256, ed25519, sr25519, Pair, crypto::KeyTypeId, offchain, hexdisplay::HexDisplay, sandbox as sandbox_primitives, H256, Blake2Hasher, + child_trie::{ChildTrie, ChildTrieReadRef}, }; use trie::{TrieConfiguration, trie_types::Layout}; use crate::sandbox; @@ -65,6 +66,16 @@ impl<'e, E: Externalities> FunctionExecutor<'e, E> { hash_lookup: HashMap::new(), }) } + + // see FIXME #2739 (api using `storage_key` at this level creates unwanted requests). + fn with_child_trie( + &mut self, + storage_key: &[u8], + f: impl FnOnce(&mut Self, ChildTrie) -> R + ) -> Result { + self.ext.child_trie(storage_key).map(|s| f(self,s)) + .ok_or("No child trie at given address.".into()) + } } impl<'e, E: Externalities> sandbox::SandboxCapabilities for FunctionExecutor<'e, E> { @@ -150,6 +161,74 @@ impl_function_executor!(this: FunctionExecutor<'e, E>, debug_trace!(target: "sr-io", "free {}", addr); Ok(()) }, + ext_child_trie( + storage_key_data: *const u8, + storage_key_length: u32, + key: *mut *mut u8, + key_length: *mut u32, + root: *mut *mut u8, + root_length: *mut u32, + parent: *mut *mut u8, + parent_length: *mut u32, + extension: *mut *mut u8, + extension_length: *mut u32 + ) -> u32 => { + let storage_key = this.memory.get(storage_key_data, storage_key_length as usize) + .map_err(|_| "Invalid attempt to determine storage_key in ext_get_child_trie")?; + + Ok(if let Some(ct) = this.ext.child_trie(&storage_key) { + + let mut alloc_vec = + |value: Option<&[u8]>, dest_vec, dest_len| -> Result<()> { + if let Some(value) = value { + let offset = this.heap.allocate(value.len() as u32)? as u32; + this.memory.set(offset, &value) + .map_err(|_| "Invalid attempt to set memory in ext_child_trie")?; + this.memory.write_primitive(dest_len, value.len() as u32) + .map_err(|_| "Invalid attempt to write length in ext_child_trie")?; + this.memory.write_primitive(dest_vec, offset) + .map_err(|_| "Invalid attempt to write vec ptr in ext_child_trie")?; + } else { + this.memory.write_primitive(dest_len, u32::max_value()) + .map_err(|_| "Invalid attempt to write failed length in ext_child_trie")?; + } + Ok(()) + }; + let fields = ct.to_ptr_vec(); + alloc_vec(Some(fields.0), key, key_length)?; + alloc_vec(fields.1, root, root_length)?; + alloc_vec(Some(fields.2), parent, parent_length)?; + alloc_vec(Some(fields.3), extension, extension_length)?; + 1 + } else { + 0 + }) + }, + ext_set_child_trie( + keyspace: *const u8, + keyspace_len: u32, + root: *const u8, + root_len: u32, + parent: *const u8, + parent_len: u32, + extension: *const u8, + extension_len: u32 + ) -> u32 => { + let keyspace = this.memory.get(keyspace, keyspace_len as usize) + .map_err(|_| "Invalid attempt to determine keyspace in ext_set_child_trie")?; + let root = if root_len == u32::max_value() { + None + } else { + Some(this.memory.get(root, root_len as usize) + .map_err(|_| "Invalid attempt to determine root in ext_set_child_trie")?) + }; + let parent = this.memory.get(parent, parent_len as usize) + .map_err(|_| "Invalid attempt to determine parent in ext_set_child_trie")?; + let extension = this.memory.get(extension, extension_len as usize) + .map_err(|_| "Invalid attempt to determine extension in ext_set_child_trie")?; + let ct = ChildTrie::unsafe_from_ptr_vecs(keyspace, root, parent, extension); + Ok(if this.ext.set_child_trie(ct) { 1 } else { 0 }) + }, ext_set_storage(key_data: *const u8, key_len: u32, value_data: *const u8, value_len: u32) => { let key = this.memory.get(key_data, key_len as usize) .map_err(|_| "Invalid attempt to determine key in ext_set_storage")?; @@ -206,9 +285,9 @@ impl_function_executor!(this: FunctionExecutor<'e, E>, HexDisplay::from(&key) ); } - let storage_key = ChildStorageKey::from_vec(storage_key) - .ok_or_else(|| "ext_set_child_storage: child storage key is invalid")?; - this.ext.set_child_storage(storage_key, key, value); + this.with_child_trie(&storage_key[..], move |this, child_trie| + this.ext.set_child_storage(&child_trie, key, value) + )?; Ok(()) }, ext_clear_child_storage( @@ -229,14 +308,12 @@ impl_function_executor!(this: FunctionExecutor<'e, E>, if let Some(_preimage) = this.hash_lookup.get(&key) { format!("%{}", primitives::hexdisplay::ascii_format(&_preimage)) } else { - format!(" {}", primitives::hexdisplay::ascii_format(&key)) - }, - HexDisplay::from(&key) - ); - let storage_key = ChildStorageKey::from_vec(storage_key) - .ok_or_else(|| "ext_clear_child_storage: child storage key is not valid")?; + format!(" {}", ::primitives::hexdisplay::ascii_format(&key)) + }, HexDisplay::from(&key)); + this.with_child_trie(&storage_key[..], |this, child_trie| + this.ext.clear_child_storage(&child_trie, &key) + )?; - this.ext.clear_child_storage(storage_key, &key); Ok(()) }, ext_clear_storage(key_data: *const u8, key_len: u32) => { @@ -260,20 +337,31 @@ impl_function_executor!(this: FunctionExecutor<'e, E>, Ok(if this.ext.exists_storage(&key) { 1 } else { 0 }) }, ext_exists_child_storage( - storage_key_data: *const u8, - storage_key_len: u32, + keyspace_data: *const u8, + keyspace_len: u32, + root_data: *const u8, + root_len: u32, key_data: *const u8, key_len: u32 ) -> u32 => { - let storage_key = this.memory.get( - storage_key_data, - storage_key_len as usize + let keyspace = &this.memory.get( + keyspace_data, + keyspace_len as usize, ).map_err(|_| "Invalid attempt to determine storage_key in ext_exists_child_storage")?; let key = this.memory.get(key_data, key_len as usize) .map_err(|_| "Invalid attempt to determine key in ext_exists_child_storage")?; - let storage_key = ChildStorageKey::from_vec(storage_key) - .ok_or_else(|| "ext_exists_child_storage: child storage key is not valid")?; - Ok(if this.ext.exists_child_storage(storage_key, &key) { 1 } else { 0 }) + let root; + let root = if root_len > 0 { + root = this.memory.get(root_data, root_len as usize) + .map_err(|_| "Invalid attempt to determine storage_key in ext_set_child_storage")?; + Some(&root[..]) + } else { None }; + let child_trie = if let Some(root) = root { + ChildTrieReadRef::Existing(root, keyspace) + } else { + ChildTrieReadRef::New(keyspace) + }; + Ok(if this.ext.exists_child_storage(child_trie, &key) { 1 } else { 0 }) }, ext_clear_prefix(prefix_data: *const u8, prefix_len: u32) => { let prefix = this.memory.get(prefix_data, prefix_len as usize) @@ -291,29 +379,24 @@ impl_function_executor!(this: FunctionExecutor<'e, E>, storage_key_data, storage_key_len as usize ).map_err(|_| "Invalid attempt to determine storage_key in ext_clear_child_prefix")?; - let storage_key = ChildStorageKey::from_vec(storage_key) - .ok_or_else(|| "ext_clear_child_prefix: child storage key is not valid")?; let prefix = this.memory.get(prefix_data, prefix_len as usize) .map_err(|_| "Invalid attempt to determine prefix in ext_clear_child_prefix")?; - this.ext.clear_child_prefix(storage_key, &prefix); + this.with_child_trie( + &storage_key[..], + |this, child_trie| this.ext.clear_child_prefix(&child_trie, &prefix), + )?; Ok(()) }, ext_kill_child_storage(storage_key_data: *const u8, storage_key_len: u32) => { - let storage_key = this.memory.get( - storage_key_data, - storage_key_len as usize - ).map_err(|_| "Invalid attempt to determine storage_key in ext_kill_child_storage")?; - let storage_key = ChildStorageKey::from_vec(storage_key) - .ok_or_else(|| "ext_exists_child_storage: child storage key is not valid")?; - this.ext.kill_child_storage(storage_key); + let storage_key = this.memory.get(storage_key_data, storage_key_len as usize) + .map_err(|_| "Invalid attempt to determine storage_key in ext_kill_child_storage")?; + this.with_child_trie(&storage_key[..], |this, child_trie| this.ext.kill_child_storage(&child_trie))?; Ok(()) }, // return 0 and place u32::max_value() into written_out if no value exists for the key. ext_get_allocated_storage(key_data: *const u8, key_len: u32, written_out: *mut u32) -> *mut u8 => { - let key = this.memory.get( - key_data, - key_len as usize - ).map_err(|_| "Invalid attempt to determine key in ext_get_allocated_storage")?; + let key = this.memory.get(key_data, key_len as usize) + .map_err(|_| "Invalid attempt to determine key in ext_get_allocated_storage")?; let maybe_value = this.ext.storage(&key); debug_trace!( @@ -346,26 +429,34 @@ impl_function_executor!(this: FunctionExecutor<'e, E>, }, // return 0 and place u32::max_value() into written_out if no value exists for the key. ext_get_allocated_child_storage( - storage_key_data: *const u8, - storage_key_len: u32, + keyspace_data: *const u8, + keyspace_len: u32, + root_data: *const u8, + root_len: u32, key_data: *const u8, key_len: u32, written_out: *mut u32 ) -> *mut u8 => { - let storage_key = this.memory.get( - storage_key_data, - storage_key_len as usize + let keyspace = &this.memory.get( + keyspace_data, + keyspace_len as usize, ).map_err(|_| "Invalid attempt to determine storage_key in ext_get_allocated_child_storage")?; let key = this.memory.get( key_data, - key_len as usize + key_len as usize, ).map_err(|_| "Invalid attempt to determine key in ext_get_allocated_child_storage")?; - - let maybe_value = { - let storage_key = ChildStorageKey::from_slice(&storage_key) - .ok_or_else(|| "ext_get_allocated_child_storage: child storage key is not valid")?; - this.ext.child_storage(storage_key, &key) + let root; + let root = if root_len > 0 { + root = this.memory.get(root_data, root_len as usize) + .map_err(|_| "Invalid attempt to determine storage_key in ext_set_child_storage")?; + Some(&root[..]) + } else { None }; + let child_trie = if let Some(root) = root { + ChildTrieReadRef::Existing(root, keyspace) + } else { + ChildTrieReadRef::New(keyspace) }; + let maybe_value = this.ext.child_storage(child_trie, &key); debug_trace!( target: "wasm-trace", "*** Getting child storage: {} -> {} == {} [k={}]", @@ -434,31 +525,37 @@ impl_function_executor!(this: FunctionExecutor<'e, E>, }, // return u32::max_value() if no value exists for the key. ext_get_child_storage_into( - storage_key_data: *const u8, - storage_key_len: u32, + keyspace_data: *const u8, + keyspace_len: u32, + root_data: *const u8, + root_len: u32, key_data: *const u8, key_len: u32, value_data: *mut u8, value_len: u32, value_offset: u32 ) -> u32 => { - let storage_key = this.memory.get( - storage_key_data, - storage_key_len as usize - ).map_err(|_| "Invalid attempt to determine storage_key in ext_get_child_storage_into")?; - let key = this.memory.get( - key_data, - key_len as usize - ).map_err(|_| "Invalid attempt to get key in ext_get_child_storage_into")?; - - let maybe_value = { - let storage_key = ChildStorageKey::from_slice(&*storage_key) - .ok_or_else(|| "ext_get_child_storage_into: child storage key is not valid")?; - this.ext.child_storage(storage_key, &key) + let keyspace = &this.memory.get( + keyspace_data, + keyspace_len as usize, + ).map_err(|_| "Invalid attempt to determine storage_key in ext_get_allocated_child_storage")?; + let key = this.memory.get(key_data, key_len as usize) + .map_err(|_| "Invalid attempt to determine key in ext_get_allocated_child_storage")?; + let root; + let root = if root_len > 0 { + root = this.memory.get(root_data, root_len as usize) + .map_err(|_| "Invalid attempt to determine storage_key in ext_set_child_storage")?; + Some(&root[..]) + } else { None }; + let child_trie = if let Some(root) = root { + ChildTrieReadRef::Existing(root, keyspace) + } else { + ChildTrieReadRef::New(keyspace) }; - debug_trace!( - target: "wasm-trace", "*** Getting storage: {} -> {} == {} [k={}]", - primitives::hexdisplay::ascii_format(&storage_key), + + let maybe_value = this.ext.child_storage(child_trie, &key); + debug_trace!(target: "wasm-trace", "*** Getting storage: {} -> {} == {} [k={}]", + ::primitives::hexdisplay::ascii_format(&storage_key), if let Some(_preimage) = this.hash_lookup.get(&key) { format!("%{}", ::primitives::hexdisplay::ascii_format(&_preimage)) } else { @@ -495,9 +592,10 @@ impl_function_executor!(this: FunctionExecutor<'e, E>, ) -> *mut u8 => { let storage_key = this.memory.get(storage_key_data, storage_key_len as usize) .map_err(|_| "Invalid attempt to determine storage_key in ext_child_storage_root")?; - let storage_key = ChildStorageKey::from_slice(&*storage_key) - .ok_or_else(|| "ext_child_storage_root: child storage key is not valid")?; - let value = this.ext.child_storage_root(storage_key); + let value = this.with_child_trie( + &storage_key[..], + |this, child_trie| this.ext.child_storage_root(&child_trie), + )?; let offset = this.heap.allocate(value.len() as u32)? as u32; this.memory.set(offset, &value) @@ -1516,6 +1614,7 @@ mod tests { use hex_literal::hex; use primitives::map; use runtime_test::WASM_BINARY; + use runtime_io::StorageContent; use substrate_offchain::testing; type TestExternalities = CoreTestExternalities; @@ -1554,11 +1653,11 @@ mod tests { assert_eq!(output, b"all ok!".to_vec()); - let expected = TestExternalities::new((map![ + let expected = TestExternalities::new(StorageContent { top: map![ b"input".to_vec() => b"Hello world".to_vec(), b"foo".to_vec() => b"bar".to_vec(), b"baz".to_vec() => b"bar".to_vec() - ], map![])); + ], children: map![]}); assert_eq!(ext, expected); } @@ -1577,11 +1676,11 @@ mod tests { assert_eq!(output, b"all ok!".to_vec()); - let expected = TestExternalities::new((map![ + let expected = TestExternalities::new(StorageContent { top: map![ b"aaa".to_vec() => b"1".to_vec(), b"aab".to_vec() => b"2".to_vec(), b"bbb".to_vec() => b"5".to_vec() - ], map![])); + ], children: map![]}); assert_eq!(expected, ext); } diff --git a/core/network/src/chain.rs b/core/network/src/chain.rs index a4767caf13d28..ca7df22195cb1 100644 --- a/core/network/src/chain.rs +++ b/core/network/src/chain.rs @@ -24,6 +24,7 @@ use sr_primitives::traits::{Block as BlockT, Header as HeaderT}; use sr_primitives::generic::{BlockId}; use sr_primitives::Justification; use primitives::{H256, Blake2Hasher, storage::StorageKey}; +use primitives::child_trie::ChildTrieReadRef; /// Local client abstraction for the network. pub trait Client: Send + Sync { @@ -52,7 +53,7 @@ pub trait Client: Send + Sync { fn read_proof(&self, block: &Block::Hash, key: &[u8]) -> Result>, Error>; /// Get child storage read execution proof. - fn read_child_proof(&self, block: &Block::Hash, storage_key: &[u8], key: &[u8]) -> Result>, Error>; + fn read_child_proof(&self, block: &Block::Hash, child_trie: ChildTrieReadRef, key: &[u8]) -> Result>, Error>; /// Get method execution proof. fn execution_proof(&self, block: &Block::Hash, method: &str, data: &[u8]) -> Result<(Vec, Vec>), Error>; @@ -120,11 +121,11 @@ impl Client for SubstrateClient where fn read_child_proof( &self, block: &Block::Hash, - storage_key: &[u8], + child_trie: ChildTrieReadRef, key: &[u8] ) -> Result>, Error> { (self as &SubstrateClient) - .read_child_proof(&BlockId::Hash(block.clone()), storage_key, key) + .read_child_proof(&BlockId::Hash(block.clone()), child_trie, key) } fn execution_proof(&self, block: &Block::Hash, method: &str, data: &[u8]) -> Result<(Vec, Vec>), Error> { diff --git a/core/network/src/protocol.rs b/core/network/src/protocol.rs index ee2849e1dde09..9367ea9220600 100644 --- a/core/network/src/protocol.rs +++ b/core/network/src/protocol.rs @@ -23,6 +23,7 @@ use libp2p::core::{ConnectedPoint, nodes::Substream, muxing::StreamMuxerBox}; use libp2p::swarm::{ProtocolsHandler, IntoProtocolsHandler}; use libp2p::swarm::{NetworkBehaviour, NetworkBehaviourAction, PollParameters}; use primitives::storage::StorageKey; +use primitives::child_trie::ChildTrieRead; use consensus::{import_queue::IncomingBlock, import_queue::Origin, BlockOrigin}; use sr_primitives::{generic::BlockId, ConsensusEngineId, Justification}; use sr_primitives::traits::{ @@ -187,13 +188,13 @@ impl<'a, B: BlockT> LightDispatchNetwork for LightDispatchIn<'a, B> { who: &PeerId, id: RequestId, block: ::Hash, - storage_key: Vec, + child_trie: ChildTrieRead, key: Vec ) { let message = message::generic::Message::RemoteReadChildRequest(message::RemoteReadChildRequest { id, block, - storage_key, + child_trie, key, }); @@ -1302,14 +1303,23 @@ impl, H: ExHashT> Protocol { request: message::RemoteReadChildRequest, ) { trace!(target: "sync", "Remote read child request {} from {} ({} {} at {})", - request.id, who, request.storage_key.to_hex::(), request.key.to_hex::(), request.block); - let proof = match self.context_data.chain.read_child_proof(&request.block, &request.storage_key, &request.key) { + request.id, + who, + request.child_trie.keyspace.to_hex::(), + request.key.to_hex::(), + request.block, + ); + let proof = match self.context_data.chain.read_child_proof( + &request.block, + request.child_trie.node_ref(), + &request.key, + ) { Ok(proof) => proof, Err(error) => { trace!(target: "sync", "Remote read child request {} from {} ({} {} at {}) failed with: {}", request.id, who, - request.storage_key.to_hex::(), + request.child_trie.keyspace.to_hex::(), request.key.to_hex::(), request.block, error diff --git a/core/network/src/protocol/light_dispatch.rs b/core/network/src/protocol/light_dispatch.rs index db7a55d742bef..db053bed88ad0 100644 --- a/core/network/src/protocol/light_dispatch.rs +++ b/core/network/src/protocol/light_dispatch.rs @@ -33,6 +33,7 @@ use crate::message::{self, BlockAttributes, Direction, FromBlock, RequestId}; use libp2p::PeerId; use crate::config::Roles; use sr_primitives::traits::{Block as BlockT, Header as HeaderT, NumberFor}; +use primitives::child_trie::ChildTrieRead; /// Remote request timeout. const REQUEST_TIMEOUT: Duration = Duration::from_secs(15); @@ -61,7 +62,7 @@ pub trait LightDispatchNetwork { who: &PeerId, id: RequestId, block: ::Hash, - storage_key: Vec, + child_trie: ChildTrieRead, key: Vec ); @@ -611,7 +612,7 @@ impl Request { peer, self.id, data.block, - data.storage_key.clone(), + data.child_trie.clone(), data.key.clone(), ), RequestData::RemoteCall(ref data, _) => @@ -667,6 +668,7 @@ pub mod tests { use std::sync::Arc; use std::time::Instant; use futures::{Future, sync::oneshot}; + use primitives::child_trie::ChildTrieRead; use sr_primitives::traits::{Block as BlockT, NumberFor, Header as HeaderT}; use client::{error::{Error as ClientError, Result as ClientResult}}; use client::light::fetcher::{FetchChecker, RemoteHeaderRequest, @@ -783,7 +785,7 @@ pub mod tests { } fn send_header_request(&mut self, _: &PeerId, _: RequestId, _: <::Header as HeaderT>::Number) {} fn send_read_request(&mut self, _: &PeerId, _: RequestId, _: ::Hash, _: Vec) {} - fn send_read_child_request(&mut self, _: &PeerId, _: RequestId, _: ::Hash, _: Vec, + fn send_read_child_request(&mut self, _: &PeerId, _: RequestId, _: ::Hash, _: ChildTrieRead, _: Vec) {} fn send_call_request(&mut self, _: &PeerId, _: RequestId, _: ::Hash, _: String, _: Vec) {} fn send_changes_request(&mut self, _: &PeerId, _: RequestId, _: ::Hash, _: ::Hash, @@ -1006,7 +1008,10 @@ pub mod tests { light_dispatch.add_request(&mut network_interface, RequestData::RemoteReadChild(RemoteReadChildRequest { header: dummy_header(), block: Default::default(), - storage_key: b":child_storage:sub".to_vec(), + child_trie: ChildTrieRead { + keyspace: b"keyspace".to_vec(), + root: b"root".to_vec(), + }, key: b":key".to_vec(), retry_count: None, }, tx)); diff --git a/core/network/src/protocol/message.rs b/core/network/src/protocol/message.rs index 39bb94eb330ac..332e9bfce1ad2 100644 --- a/core/network/src/protocol/message.rs +++ b/core/network/src/protocol/message.rs @@ -18,6 +18,7 @@ use bitflags::bitflags; use sr_primitives::{ConsensusEngineId, traits::{Block as BlockT, Header as HeaderT}}; +use primitives::child_trie::ChildTrieRead; use codec::{Encode, Decode, Input, Output, Error}; pub use self::generic::{ BlockAnnounce, RemoteCallRequest, RemoteReadRequest, @@ -131,7 +132,7 @@ pub mod generic { use sr_primitives::Justification; use crate::config::Roles; use super::{ - RemoteReadResponse, Transactions, Direction, + RemoteReadResponse, Transactions, Direction, ChildTrieRead, RequestId, BlockAttributes, RemoteCallResponse, ConsensusEngineId, }; /// Consensus is mostly opaque to us @@ -294,9 +295,9 @@ pub mod generic { pub id: RequestId, /// Block at which to perform call. pub block: H, - /// Child Storage key. - pub storage_key: Vec, - /// Storage key. + /// Child trie to query. + pub child_trie: ChildTrieRead, + /// Storage key inside child trie. pub key: Vec, } diff --git a/core/primitives/src/child_trie.rs b/core/primitives/src/child_trie.rs new file mode 100644 index 0000000000000..31e5a98347d82 --- /dev/null +++ b/core/primitives/src/child_trie.rs @@ -0,0 +1,419 @@ +// Copyright 2017-2019 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! Child trie related struct + +use codec::{Encode, Decode, Compact}; +use rstd::prelude::*; +use rstd::ptr; +use crate::storage::well_known_keys::CHILD_STORAGE_KEY_PREFIX; +#[cfg(feature = "std")] +pub use impl_serde::serialize as bytes; +use hash_db::Prefix; + +/// `KeySpace` type contains a unique child trie identifier. +/// It is used to isolate a child trie in its backing database. +/// This is done by prefixing its key with this value. +/// +/// A keyspace byte representation must not be the start of another +/// keyspace byte representation (otherwhise conflict may happen). +/// This guaranty is provided by the fact that keyspace is a scale +/// encoded representation. +/// +/// From a `TrieDB` perspective, accessing a child trie content +/// will requires both the child trie root, but also the `KeySpace`. +/// +/// The `KeySpace` of a child trie must be unique for the canonical chain. +/// This unicity is currently guaranted by build from a simple counter. +/// +/// If a child trie was to be moved between two chains, the underlying +/// key value would be all the keyvaluedb prefixed by this keyspace. +/// Moving those key will still need for every content to change keyspace +/// of the origin chain with a new keyspace of a destination chain. +/// Please notice that usage of keyspace on ALL data makes it possible, +/// +/// The same thing is true for a child trie deletion, there is no need to +/// remove all historic of state child trie keypair from a multiple TrieDB +/// perspective, but simply all keyvaluedb content with a key starting with +/// this keyspace. +pub type KeySpace = Vec; + + +/// Keyspace to use for the parent trie key. +pub const NO_CHILD_KEYSPACE: [u8;1] = [0]; + + +/// Produce a new keyspace from current state counter. +pub fn produce_keyspace(child_counter: u128) -> Vec { + codec::Encode::encode(&Compact(child_counter)) +} + +/// Decode keyspace to original counter value. +pub fn reverse_keyspace(keyspace: &[u8]) -> Result { + >::decode(&mut &keyspace[..]).map(|c| c.0) +} + +// see FIXME #2741 for removal of this allocation on every operation. +// Simplest would be to put an additional optional field in prefix. +/// Utility function used for merging `KeySpace` data and `prefix` data +/// before calling key value database primitives. +pub fn keyspace_as_prefix_alloc(ks: Option<&KeySpace>, prefix: Prefix) -> (Vec, Option) { + let ks = ks.map(|ks| ks.as_slice()).unwrap_or(&NO_CHILD_KEYSPACE[..]); + let mut result = rstd::vec![0; ks.len() + prefix.0.len()]; + result[..ks.len()].copy_from_slice(ks); + result[ks.len()..].copy_from_slice(prefix.0); + (result, prefix.1) +} + +/// Parent trie origin. This type contains all information +/// needed to access a child trie from a root state. +/// Currently only a single depth is supported for child trie, +/// so it only contains the top trie key to the child trie. +/// Internally this contains a full path key (including +/// `well_known_keys::CHILD_STORAGE_KEY_PREFIX`). +pub type ParentTrie = Vec; + +/// `ChildTrieReadRef` contains a reference to information +/// needed to access a child trie content. +/// Generally this should not be build directly but accessed +/// through a `node_ref` function call. +/// The struct can be build directly with invalid data, so +/// its usage is limited to read only querying. +#[derive(Clone)] +pub enum ChildTrieReadRef<'a> { + /// Subtrie path for new trie, see [`ParentTrie`] for details. + New(&'a KeySpace), + /// Subtrie root hash and keyspace for existing child trie. + Existing(&'a [u8], &'a KeySpace), +} + +impl<'a> ChildTrieReadRef<'a> { + /// Keyspace accessor for the enum. + pub fn keyspace(&self) -> &KeySpace { + match self { + ChildTrieReadRef::New(k) => k, + ChildTrieReadRef::Existing(_, k) => k, + } + } +} +/// Current codec version of a child trie definition. +const LAST_SUBTRIE_CODEC_VERSION: u16 = 1u16; + +/// `ChildTrieNode` encoder internal implementation, +/// it shall not be public. +#[derive(Encode, Clone)] +struct ChildTrieReadEncode<'a> { + /// Current codec version + #[codec(compact)] + version: u16, + /// Child trie unique keyspace + keyspace: &'a KeySpace, + /// Child trie root hash + root: &'a [u8], +} + +#[derive(Decode)] +struct ChildTrieReadDecode { + #[codec(compact)] + version: u16, + keyspace: KeySpace, + root: Vec, +} + +#[derive(PartialEq, Eq, Clone, Encode, Decode)] +#[cfg_attr(feature = "std", derive(Debug, Hash, PartialOrd, Ord))] +/// This struct contains information needed to access a child trie. +/// Mainly use for doing remote queries (after accessing a child trie +/// content). +pub struct ChildTrieRead { + /// Child trie parent key. + pub keyspace: KeySpace, + /// Child trie root hash. + pub root: Vec, +} + +impl ChildTrieRead { + /// Use `ChildTrieRead` as a `ChildTrieReadRef`, + /// forcing root field to an existing value. + pub fn node_ref<'a>(&'a self) -> ChildTrieReadRef<'a> { + debug_assert!(self.root.len() > 0); + ChildTrieReadRef::Existing(&self.root[..], &self.keyspace) + } +} + +/// Information related to a child trie. +/// This contains information needed to access a child trie +/// content but also information needed to manage child trie +/// from its parent trie (removal, move, update of root). +/// +#[derive(PartialEq, Eq, Clone)] +#[cfg_attr(feature = "std", derive(Debug, Hash, PartialOrd, Ord))] +pub struct ChildTrie { + /// `KeySpace` for this child trie, see [`KeySpace`] for details. + keyspace: KeySpace, + /// Child trie current root, in case of modification + /// this is not updated. + root: Option>, + /// Current position of this child trie, see [`ParentTrie`] for details. + parent: ParentTrie, + /// Extension contains additional encoded data related to this child trie + /// node. A trait over the content could be use, + /// but for simplicity the encoded value is use here. + /// Direct operation on the child trie node are doable without + /// having to know the type of the extension content. + extension: Vec, +} + +impl ChildTrie { + /// Primitive to build a `ParentTrie` from its + /// key. + pub fn prefix_parent_key(parent: &[u8]) -> ParentTrie { + let mut key_full = CHILD_STORAGE_KEY_PREFIX.to_vec(); + key_full.extend_from_slice(parent); + key_full + } + + /// Function to access the current key to a child trie. + /// This does not include `well_known_keys::CHILD_STORAGE_KEY_PREFIX`. + pub fn parent_key_slice(p: &ParentTrie) -> &[u8] { + &p[CHILD_STORAGE_KEY_PREFIX.len()..] + } + + /// Method for fetching or initiating a new child trie. + /// + /// Note that call back could do nothing, which will allow unspecified behavior, + /// but can be useful in case we create a child trie at a known unused location, + /// or for performance purpose (later write). + /// + /// We also provide an encodable value specific to the creation state (block number). + pub fn fetch_or_new( + parent_fetcher: impl FnOnce(&[u8]) -> Option, + child_trie_update: impl FnOnce(ChildTrie), + parent: &[u8], + child_trie_next_counter: impl FnOnce() -> u128, + ) -> Self { + parent_fetcher(parent) + .unwrap_or_else(|| { + let parent = Self::prefix_parent_key(parent); + let ct = ChildTrie { + keyspace: produce_keyspace(child_trie_next_counter()), + root: Default::default(), + parent, + extension: Default::default(), + }; + child_trie_update(ct.clone()); + ct + }) + } + + /// Get a reference to the child trie information + /// needed for a read only query. + pub fn node_ref(&self) -> ChildTrieReadRef { + if let Some(root) = self.root.as_ref() { + ChildTrieReadRef::Existing(&root[..], &self.keyspace) + } else { + ChildTrieReadRef::New(&self.keyspace) + } + } + + /// Instantiate child trie from its encoded value and location. + /// Please do not use this function with encoded content + /// which is not fetch from an existing child trie. + pub fn decode_node_with_parent( + encoded_node: &[u8], + parent: ParentTrie, + ) -> Option { + let input = &mut &encoded_node[..]; + ChildTrieReadDecode::decode(input).ok().map(|ChildTrieReadDecode { version, keyspace, root }| { + debug_assert!(version == LAST_SUBTRIE_CODEC_VERSION); + ChildTrie { + keyspace, + root: Some(root), + parent, + extension: (*input).to_vec(), + }}) + } + + /// Return true when the child trie is new and does not contain a root. + pub fn is_new(&self) -> bool { + self.root.is_none() + } + + /// See [`parent_key_slice`]. + pub fn parent_slice(&self) -> &[u8] { + Self::parent_key_slice(&self.parent) + } + + /// Function to access the full key buffer pointing to + /// a child trie. This contains technical information + /// and should only be used for backend implementation. + pub fn parent_trie(&self) -> &ParentTrie { + &self.parent + } + + /// Getter function to the original root value of this + /// child trie. + pub fn root_initial_value(&self) -> &Option> { + &self.root + } + + /// Getter function for the `KeySpace` of this child trie. + pub fn keyspace(&self) -> &KeySpace { + &self.keyspace + } + + /// Getter function for extension content of child trie. + pub fn extension(&self) -> &[u8] { + &self.extension[..] + } + + /// Is it possible to overwrite an existing chid trie with + /// a new one. + pub fn is_updatable_with(&self, old_ct: &ChildTrie) -> bool { + old_ct.root_initial_value() == self.root_initial_value() + && old_ct.keyspace() == self.keyspace() + && old_ct.parent_slice() == self.parent_slice() + } + + /// Encoder for the child trie, with a new root value. + /// The child trie current root value is not updated (if + /// content is commited the child trie will need to be fetch + /// again for update). + pub fn encoded_with_root(&self, new_root: &[u8]) -> Vec { + let mut enc = codec::Encode::encode(&ChildTrieReadEncode{ + version: LAST_SUBTRIE_CODEC_VERSION, + keyspace: &self.keyspace, + root: new_root, + }); + enc.extend_from_slice(&self.extension[..]); + enc + } + + /// Convenience conversion function. + pub fn child_trie_read(&self) -> Option { + self.root.as_ref().map(|root| + ChildTrieRead { + root: root.clone(), + keyspace: self.keyspace.clone(), + } + ) + } + + + /// Function accessing all child trie fields and returning + /// tuple of pointer and size from them. + pub fn ptr_child_trie(&self) -> PtrChildTrie { + ( + self.keyspace.as_ptr(), + self.keyspace.len() as u32, + self.root.as_ref().map(|r| r.as_ptr()).unwrap_or(ptr::null()), + self.root.as_ref().map(|r| r.len() as u32).unwrap_or(u32::max_value()), + self.parent.as_ptr(), + self.parent.len() as u32, + self.extension.as_ptr(), + self.extension.len() as u32, + ) + } + + /// Function to access all child trie fields. + pub fn to_ptr_vec(&self) -> (&[u8], Option<&[u8]>, &[u8], &[u8]) { + ( + self.keyspace.as_ref(), + self.root.as_ref().map(|r| r.as_ref()), + self.parent.as_ref(), + self.extension.as_ref(), + ) + } + + /// Function to rebuild child trie accessed from. + /// This is unsafe to use because it allows to build invalid + /// child trie object: duplicate keyspace or invalid root. + pub fn unsafe_from_ptr_child_trie( + keyspace: *mut u8, + keyspace_length: u32, + root: *mut u8, + root_length: u32, + parent: *mut u8, + parent_length: u32, + extension: *mut u8, + extension_length: u32, + ) -> Self { + unsafe { + let keyspace = from_raw_parts(keyspace, keyspace_length).expect("non optional; qed"); + let root = from_raw_parts(root, root_length); + let parent = from_raw_parts(parent, parent_length).expect("non optional; qed"); + let extension = from_raw_parts(extension, extension_length).expect("non optional; qed"); + ChildTrie { keyspace, root, parent, extension } + } + } + + /// Function to rebuild child trie accessed from mem copied field. + /// This is unsafe to use because it allows to build invalid + /// child trie object: duplicate keyspace or invalid root. + pub fn unsafe_from_ptr_vecs( + keyspace: Vec, + root: Option>, + parent: Vec, + extension: Vec, + ) -> Self { + ChildTrie { keyspace, root, parent, extension } + } + +} + +unsafe fn from_raw_parts(ptr: *mut u8, len: u32) -> Option> { + if len == u32::max_value() { + None + } else { + Some(>::from_raw_parts(ptr, len as usize, len as usize)) + } +} + +/// Pointers repersentation of ChildTrie +type PtrChildTrie = ( + *const u8, + u32, + *const u8, + u32, + *const u8, + u32, + *const u8, + u32, +); + +impl AsRef for ChildTrie { + fn as_ref(&self) -> &ChildTrie { + self + } +} + +#[test] +fn encode_empty_prefix() { + let empty = produce_keyspace(0); + + assert_eq!(&NO_CHILD_KEYSPACE[..], &empty[..]); +} + +#[test] +fn keyspace_codec() { + let sample: [u128; 3] = [0, 1, 1_000_000]; + for s in sample.iter() { + let keyspace = produce_keyspace(*s); + let dec_keyspace = reverse_keyspace(keyspace.as_slice()).unwrap(); + assert_eq!(*s, dec_keyspace); + } +} + diff --git a/core/primitives/src/lib.rs b/core/primitives/src/lib.rs index 5c918e4964e37..6ab4da5de1145 100644 --- a/core/primitives/src/lib.rs +++ b/core/primitives/src/lib.rs @@ -63,6 +63,7 @@ pub mod sandbox; pub mod storage; pub mod uint; mod changes_trie; +pub mod child_trie; pub mod traits; pub mod testing; diff --git a/core/primitives/src/storage.rs b/core/primitives/src/storage.rs index 8fdb7bdcc40c0..8b8f1f3a91e7f 100644 --- a/core/primitives/src/storage.rs +++ b/core/primitives/src/storage.rs @@ -32,7 +32,12 @@ pub struct StorageKey(#[cfg_attr(feature = "std", serde(with="bytes"))] pub Vec< #[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug, Hash, PartialOrd, Ord, Clone))] pub struct StorageData(#[cfg_attr(feature = "std", serde(with="bytes"))] pub Vec); -/// Storage change set +/// Contract storage keyspace data. +#[derive(PartialEq, Eq)] +#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug, Hash, PartialOrd, Ord, Clone))] +pub struct StorageKeySpace(#[cfg_attr(feature = "std", serde(with="bytes"))] pub Vec); + +/// Storage change set. #[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug, PartialEq, Eq))] #[cfg_attr(feature = "std", serde(rename_all = "camelCase"))] pub struct StorageChangeSet { @@ -43,6 +48,15 @@ pub struct StorageChangeSet { StorageKey, Option, )>, + /// A list of children changes. + pub child_changes: Vec<( + StorageKeySpace, + Vec<( + StorageKey, + Option + )>, + )>, + } /// List of all well known keys and prefixes in storage. @@ -67,6 +81,9 @@ pub mod well_known_keys { /// Prefix of child storage keys. pub const CHILD_STORAGE_KEY_PREFIX: &'static [u8] = b":child_storage:"; + /// Counter for child trie keyspace. + pub const CHILD_STORAGE_KEYSPACE_COUNTER: &'static [u8] = b":child_keyspace_counter:"; + /// Whether a key is a child storage key. /// /// This is convenience function which basically checks if the given `key` starts @@ -75,4 +92,5 @@ pub mod well_known_keys { // Other code might depend on this, so be careful changing this. key.starts_with(CHILD_STORAGE_KEY_PREFIX) } + } diff --git a/core/rpc/src/state/mod.rs b/core/rpc/src/state/mod.rs index 390f95ab41dff..805e3e0f955fd 100644 --- a/core/rpc/src/state/mod.rs +++ b/core/rpc/src/state/mod.rs @@ -248,7 +248,10 @@ pub trait StateBackend: Send + Sync + 'static .unwrap_or_else(|_| (key, None)) ) .collect(); - vec![Ok(Ok(StorageChangeSet { block, changes }))] + vec![Ok(Ok(StorageChangeSet { + block, changes, + child_changes: Default::default(), + }))] }).unwrap_or_default()); self.subscriptions().add(subscriber, |sink| { @@ -259,6 +262,7 @@ pub trait StateBackend: Send + Sync + 'static .filter_map(|(o_sk, k, v)| if o_sk.is_none() { Some((k.clone(),v.cloned())) } else { None }).collect(), + child_changes: Default::default(), }))) .compat(); diff --git a/core/rpc/src/state/state_full.rs b/core/rpc/src/state/state_full.rs index 3115a525a8f29..f8bf6b776df9e 100644 --- a/core/rpc/src/state/state_full.rs +++ b/core/rpc/src/state/state_full.rs @@ -147,11 +147,16 @@ impl FullState ) -> Result<()> { for block in range.unfiltered_range.start..range.unfiltered_range.end { let block_hash = range.hashes[block].clone(); - let mut block_changes = StorageChangeSet { block: block_hash.clone(), changes: Vec::new() }; + let mut block_changes = StorageChangeSet { + block: block_hash.clone(), + changes: Vec::new(), + child_changes: Vec::new(), + }; let id = BlockId::hash(block_hash); for key in keys { let (has_changed, data) = { - let curr_data = self.client.storage(&id, key).map_err(client_err)?; + let curr_data = self.client.storage(&id, key) + .map_err(client_err)?; match last_values.get(key) { Some(prev_data) => (curr_data != *prev_data, curr_data), None => (true, curr_data), @@ -202,7 +207,11 @@ impl FullState } changes_map.entry(block) - .or_insert_with(|| StorageChangeSet { block: block_hash, changes: Vec::new() }) + .or_insert_with(|| StorageChangeSet { + block: block_hash, + changes: Vec::new(), + child_changes: Vec::new(), + }) .changes.push((key.clone(), value_at_block.clone())); last_block = Some(block); last_value = value_at_block; @@ -294,7 +303,14 @@ impl StateBackend for FullState FutureResult> { Box::new(result( self.block_or_best(block) - .and_then(|block| self.client.child_storage_keys(&BlockId::Hash(block), &child_storage_key, &prefix)) + .and_then(|block| { + let block = BlockId::Hash(block); + if let Some(subtrie) = self.client.child_trie(&block, &child_storage_key)? { + self.client.child_storage_keys(&block, subtrie.node_ref(), &prefix) + } else { + Ok(Vec::new()) + } + }) .map_err(client_err))) } @@ -306,7 +322,14 @@ impl StateBackend for FullState FutureResult> { Box::new(result( self.block_or_best(block) - .and_then(|block| self.client.child_storage(&BlockId::Hash(block), &child_storage_key, &key)) + .and_then(|block| { + let block = BlockId::Hash(block); + if let Some(subtrie) = self.client.child_trie(&block, &child_storage_key)? { + self.client.child_storage(&block, subtrie.node_ref(), &key) + } else { + Ok(None) + } + }) .map_err(client_err))) } @@ -318,7 +341,14 @@ impl StateBackend for FullState FutureResult> { Box::new(result( self.block_or_best(block) - .and_then(|block| self.client.child_storage_hash(&BlockId::Hash(block), &child_storage_key, &key)) + .and_then(|block| { + let block = BlockId::Hash(block); + if let Some(subtrie) = self.client.child_trie(&block, &child_storage_key)? { + self.client.child_storage_hash(&block, subtrie.node_ref(), &key) + } else { + Ok(None) + } + }) .map_err(client_err))) } diff --git a/core/rpc/src/state/state_light.rs b/core/rpc/src/state/state_light.rs index eb14b3fe7bd0b..41b54d0930a9b 100644 --- a/core/rpc/src/state/state_light.rs +++ b/core/rpc/src/state/state_light.rs @@ -190,20 +190,27 @@ impl StateBackend for LightState FutureResult> { - let fetcher = self.fetcher.clone(); - let child_storage = self.resolve_header(block) - .then(move |result| match result { - Ok(header) => Either::Left(fetcher.remote_read_child(RemoteReadChildRequest { - block: header.hash(), - header, - storage_key: child_storage_key.0, - key: key.0, - retry_count: Default::default(), - }).then(|result| ready(result.map(|data| data.map(StorageData)).map_err(client_err)))), - Err(error) => Either::Right(ready(Err(error))), - }); - - Box::new(child_storage.boxed().compat()) + let block_id = BlockId::Hash(self.block_or_best(block)); + // using client to benefit from cache (not chaining query) + if let Ok(Some(child_trie)) = self.client.child_trie(&block_id, &child_storage_key) { + if let Some(child_trie) = child_trie.child_trie_read() { + let fetcher = self.fetcher.clone(); + let child_storage = self.resolve_header(block) + .then(move |result| match result { + Ok(header) => Either::Left(fetcher.remote_read_child(RemoteReadChildRequest { + block: header.hash(), + header, + child_trie, + key: key.0, + retry_count: Default::default(), + }).then(|result| ready(result.map(|data| data.map(StorageData)).map_err(client_err)))), + Err(error) => Either::Right(ready(Err(error))), + }); + + return Box::new(child_storage.boxed().compat()) + } + } + Box::new(result(Ok(None))) } fn child_storage_hash( diff --git a/core/rpc/src/state/tests.rs b/core/rpc/src/state/tests.rs index ba1aac4cc3eb4..ea0edea6f797a 100644 --- a/core/rpc/src/state/tests.rs +++ b/core/rpc/src/state/tests.rs @@ -31,15 +31,17 @@ use test_client::{ #[test] fn should_return_storage() { + use primitives::child_trie::ChildTrie; const KEY: &[u8] = b":mock"; const VALUE: &[u8] = b"hello world"; - const STORAGE_KEY: &[u8] = b":child_storage:default:child"; + const STORAGE_KEY: &[u8] = b":default:child"; const CHILD_VALUE: &[u8] = b"hello world !"; + let child_trie = ChildTrie::fetch_or_new(|_| None, |_| (), STORAGE_KEY, || 1u128); let mut core = tokio::runtime::Runtime::new().unwrap(); let client = TestClientBuilder::new() .add_extra_storage(KEY.to_vec(), VALUE.to_vec()) - .add_extra_child_storage(STORAGE_KEY.to_vec(), KEY.to_vec(), CHILD_VALUE.to_vec()) + .add_extra_child_storage(&child_trie, KEY.to_vec(), CHILD_VALUE.to_vec()) .build(); let genesis_hash = client.genesis_hash(); let client = new_full(Arc::new(client), Subscriptions::new(Arc::new(core.executor()))); @@ -218,6 +220,7 @@ fn should_query_storage() { (StorageKey(vec![4]), None), (StorageKey(vec![5]), None), ], + child_changes: vec![], }, StorageChangeSet { block: block1_hash, @@ -226,6 +229,7 @@ fn should_query_storage() { (StorageKey(vec![3]), Some(StorageData(vec![3]))), (StorageKey(vec![5]), Some(StorageData(vec![0]))), ], + child_changes: vec![], }, ]; @@ -253,6 +257,7 @@ fn should_query_storage() { (StorageKey(vec![4]), Some(StorageData(vec![4]))), (StorageKey(vec![5]), Some(StorageData(vec![1]))), ], + child_changes: vec![], }); assert_eq!(result.wait().unwrap(), expected); } diff --git a/core/service/src/chain_spec.rs b/core/service/src/chain_spec.rs index 8b35b0bac9581..9489192634b7e 100644 --- a/core/service/src/chain_spec.rs +++ b/core/service/src/chain_spec.rs @@ -22,7 +22,8 @@ use std::fs::File; use std::path::PathBuf; use serde::{Serialize, Deserialize}; use primitives::storage::{StorageKey, StorageData}; -use sr_primitives::{BuildStorage, StorageOverlay, ChildrenStorageOverlay}; +use primitives::child_trie::{ChildTrie, produce_keyspace, reverse_keyspace}; +use sr_primitives::{BuildStorage, StorageContent}; use serde_json as json; use crate::RuntimeGenesis; use network::Multiaddr; @@ -69,20 +70,32 @@ impl GenesisSource { } impl<'a, G: RuntimeGenesis> BuildStorage for &'a ChainSpec { - fn build_storage(self) -> Result<(StorageOverlay, ChildrenStorageOverlay), String> { + fn build_storage(self) -> Result { match self.genesis.resolve()? { Genesis::Runtime(gc) => gc.build_storage(), - Genesis::Raw(map, children_map) => Ok(( - map.into_iter().map(|(k, v)| (k.0, v.0)).collect(), - children_map.into_iter().map(|(sk, map)| ( - sk.0, - map.into_iter().map(|(k, v)| (k.0, v.0)).collect(), + Genesis::Raw(map, children_map) => Ok(StorageContent { + top: map.into_iter().map(|(k, v)| (k.0, v.0)).collect(), + children: children_map.into_iter().map(|((sk, ks), map)| ( + produce_keyspace(ks), + ( + map.into_iter().map(|(k, v)| (k.0, v.0)).collect(), + // no fetch or update on genesis building (use it as + // a dump build. + // child trie counter as a simple usize should be correctly + // define and the counter for it actually stored properly + // in top (with value of the max child trie). + ChildTrie::fetch_or_new( + |_| None, + |_| (), + sk.0.as_slice(), + || ks, + ) + ) )).collect(), - )), + }), } } - - fn assimilate_storage(self, _: &mut (StorageOverlay, ChildrenStorageOverlay)) -> Result<(), String> { + fn assimilate_storage(self, _: &mut StorageContent) -> Result<(), String> { Err("`assimilate_storage` not implemented for `ChainSpec`.".into()) } } @@ -94,7 +107,7 @@ enum Genesis { Runtime(G), Raw( HashMap, - HashMap>, + HashMap<(StorageKey, u128), HashMap>, ), } @@ -231,17 +244,21 @@ impl ChainSpec { let genesis = match (raw, self.genesis.resolve()?) { (true, Genesis::Runtime(g)) => { let storage = g.build_storage()?; - let top = storage.0.into_iter() + let top = storage.top.into_iter() .map(|(k, v)| (StorageKey(k), StorageData(v))) .collect(); - let children = storage.1.into_iter() - .map(|(sk, child)| ( - StorageKey(sk), - child.into_iter() - .map(|(k, v)| (StorageKey(k), StorageData(v))) - .collect(), - )) - .collect(); + let mut children = HashMap::new(); + for (_ks, (child_map, child)) in storage.children.into_iter() { + children.insert( + ( + StorageKey(child.parent_slice().to_vec()), + reverse_keyspace(child.keyspace()).map_err(|_| "u128 uncode error".to_string())?, + ), + child_map.into_iter() + .map(|(k, v)| (StorageKey(k), StorageData(v))) + .collect(), + ); + } Genesis::Raw(top, children) }, diff --git a/core/sr-io/src/lib.rs b/core/sr-io/src/lib.rs index de5c48f5fedf9..649aefc5511d9 100644 --- a/core/sr-io/src/lib.rs +++ b/core/sr-io/src/lib.rs @@ -33,6 +33,7 @@ use rstd::vec::Vec; pub use codec; pub use primitives::Blake2Hasher; +pub use primitives::child_trie::{ChildTrie, ChildTrieReadRef}; use primitives::{ crypto::KeyTypeId, ed25519, sr25519, offchain::{ @@ -95,7 +96,7 @@ macro_rules! export_api { $( where $( $w_name : $w_ty ),+ )? { #[allow(deprecated)] - <()>:: $name $(::< $( $g_name ),+ > )? ( $( $arg ),* ) + <()>:: $name $(::< $( $g_name ),+ > )? ( $( $arg ),* ) } )* } @@ -107,7 +108,7 @@ export_api! { fn storage(key: &[u8]) -> Option>; /// Get `key` from child storage and return a `Vec`, empty if there's a problem. - fn child_storage(storage_key: &[u8], key: &[u8]) -> Option>; + fn child_storage(child_trie: ChildTrieReadRef, key: &[u8]) -> Option>; /// Get `key` from storage, placing the value into `value_out` and return the number of /// bytes that the entry in storage has beyond the offset or `None` if the storage entry @@ -116,45 +117,62 @@ export_api! { /// are copied into `value_out`. fn read_storage(key: &[u8], value_out: &mut [u8], value_offset: usize) -> Option; + /// Get child trie for a given `storage_key` location, or `None` if undefined. + fn child_trie(storage_key: &[u8]) -> Option; + + /// Update or create an existing child trie. + /// Return false if it could not be updated (eg direct change + /// of root is not allowed). + /// This is accessible to runtime module, but is not safe for + /// direct access, (eg contract). + /// Problematic use case is direct construction of child trie with existing root or keyspace. + /// `ChildTrie` input need to be fetch or instantiate from a valid `Keyspace` generator. + fn set_child_trie(ct: ChildTrie) -> bool; + /// Get `key` from child storage, placing the value into `value_out` and return the number /// of bytes that the entry in storage has beyond the offset or `None` if the storage entry /// doesn't exist at all. /// If `value_out` length is smaller than the returned length, only `value_out` length bytes /// are copied into `value_out`. - fn read_child_storage(storage_key: &[u8], key: &[u8], value_out: &mut [u8], value_offset: usize) -> Option; + fn read_child_storage( + child_trie: ChildTrieReadRef, + key: &[u8], + value_out: &mut [u8], + value_offset: usize + ) -> Option; /// Set the storage of some particular key to Some value. fn set_storage(key: &[u8], value: &[u8]); /// Set the child storage of some particular key to Some value. - fn set_child_storage(storage_key: &[u8], key: &[u8], value: &[u8]); + fn set_child_storage(child_trie: &ChildTrie, key: &[u8], value: &[u8]); /// Clear the storage of a key. fn clear_storage(key: &[u8]); /// Clear the storage of a key. - fn clear_child_storage(storage_key: &[u8], key: &[u8]); + fn clear_child_storage(child_trie: &ChildTrie, key: &[u8]); /// Clear an entire child storage. - fn kill_child_storage(storage_key: &[u8]); + fn kill_child_storage(child_trie: &ChildTrie); /// Check whether a given `key` exists in storage. fn exists_storage(key: &[u8]) -> bool; /// Check whether a given `key` exists in storage. - fn exists_child_storage(storage_key: &[u8], key: &[u8]) -> bool; + fn exists_child_storage(child_trie: ChildTrieReadRef, key: &[u8]) -> bool; /// Clear the storage entries with a key that starts with the given prefix. fn clear_prefix(prefix: &[u8]); /// Clear the child storage entries with a key that starts with the given prefix. - fn clear_child_prefix(storage_key: &[u8], prefix: &[u8]); + fn clear_child_prefix(child_trie: &ChildTrie, prefix: &[u8]); /// "Commit" all existing operations and compute the resultant storage root. fn storage_root() -> [u8; 32]; /// "Commit" all existing operations and compute the resultant child storage root. - fn child_storage_root(storage_key: &[u8]) -> Vec; + fn child_storage_root(child_trie: &ChildTrie) -> Vec; /// "Commit" all existing operations and get the resultant storage change root. fn storage_changes_root(parent_hash: [u8; 32]) -> Option<[u8; 32]>; @@ -408,7 +426,7 @@ mod imp { #[cfg(feature = "std")] pub use self::imp::{ StorageOverlay, ChildrenStorageOverlay, with_storage, - with_externalities + with_externalities, StorageContent, }; #[cfg(not(feature = "std"))] pub use self::imp::ext::*; diff --git a/core/sr-io/with_std.rs b/core/sr-io/with_std.rs index acbeb8ce0e9bd..a4d057d889cf0 100644 --- a/core/sr-io/with_std.rs +++ b/core/sr-io/with_std.rs @@ -15,19 +15,21 @@ // along with Substrate. If not, see . use primitives::{ - blake2_128, blake2_256, twox_128, twox_256, twox_64, ed25519, Blake2Hasher, sr25519, Pair, + blake2_128, blake2_256, twox_128, twox_256, twox_64, ed25519, Blake2Hasher, + sr25519, Pair, child_trie::{ChildTrie, ChildTrieReadRef}, }; // Switch to this after PoC-3 // pub use primitives::BlakeHasher; pub use substrate_state_machine::{ - Externalities, BasicExternalities, TestExternalities, ChildStorageKey, + Externalities, BasicExternalities, TestExternalities, + backend::{StorageContent, StorageOverlay, ChildrenStorageOverlay}, }; use environmental::environmental; use primitives::{offchain, hexdisplay::HexDisplay, H256}; use trie::{TrieConfiguration, trie_types::Layout}; -use std::{collections::HashMap, convert::TryFrom}; +use std::convert::TryFrom; environmental!(ext: trait Externalities); @@ -35,18 +37,6 @@ environmental!(ext: trait Externalities); pub trait HasherBounds {} impl HasherBounds for T {} -/// Returns a `ChildStorageKey` if the given `storage_key` slice is a valid storage -/// key or panics otherwise. -/// -/// Panicking here is aligned with what the `without_std` environment would do -/// in the case of an invalid child storage key. -fn child_storage_key_or_panic(storage_key: &[u8]) -> ChildStorageKey { - match ChildStorageKey::from_slice(storage_key) { - Some(storage_key) => storage_key, - None => panic!("child storage key is invalid"), - } -} - impl StorageApi for () { fn storage(key: &[u8]) -> Option> { ext::with(|ext| ext.storage(key).map(|s| s.to_vec())) @@ -62,10 +52,19 @@ impl StorageApi for () { })).expect("read_storage cannot be called outside of an Externalities-provided environment.") } - fn child_storage(storage_key: &[u8], key: &[u8]) -> Option> { + fn child_trie(storage_key: &[u8]) -> Option { + ext::with(|ext| ext.child_trie(storage_key)) + .expect("storage cannot be called outside of an Externalities-provided environment.") + } + + fn set_child_trie(ct: ChildTrie) -> bool { + ext::with(|ext| ext.set_child_trie(ct)) + .expect("storage cannot be called outside of an Externalities-provided environment.") + } + + fn child_storage(child_trie: ChildTrieReadRef, key: &[u8]) -> Option> { ext::with(|ext| { - let storage_key = child_storage_key_or_panic(storage_key); - ext.child_storage(storage_key, key).map(|s| s.to_vec()) + ext.child_storage(child_trie, key).map(|s| s.to_vec()) }) .expect("storage cannot be called outside of an Externalities-provided environment.") } @@ -77,14 +76,13 @@ impl StorageApi for () { } fn read_child_storage( - storage_key: &[u8], + child_trie: ChildTrieReadRef, key: &[u8], value_out: &mut [u8], value_offset: usize, ) -> Option { ext::with(|ext| { - let storage_key = child_storage_key_or_panic(storage_key); - ext.child_storage(storage_key, key) + ext.child_storage(child_trie, key) .map(|value| { let value = &value[value_offset..]; let written = std::cmp::min(value.len(), value_out.len()); @@ -95,10 +93,9 @@ impl StorageApi for () { .expect("read_child_storage cannot be called outside of an Externalities-provided environment.") } - fn set_child_storage(storage_key: &[u8], key: &[u8], value: &[u8]) { + fn set_child_storage(child_trie: &ChildTrie, key: &[u8], value: &[u8]) { ext::with(|ext| { - let storage_key = child_storage_key_or_panic(storage_key); - ext.set_child_storage(storage_key, key.to_vec(), value.to_vec()) + ext.set_child_storage(child_trie, key.to_vec(), value.to_vec()) }); } @@ -108,17 +105,15 @@ impl StorageApi for () { ); } - fn clear_child_storage(storage_key: &[u8], key: &[u8]) { + fn clear_child_storage(child_trie: &ChildTrie, key: &[u8]) { ext::with(|ext| { - let storage_key = child_storage_key_or_panic(storage_key); - ext.clear_child_storage(storage_key, key) + ext.clear_child_storage(child_trie, key) }); } - fn kill_child_storage(storage_key: &[u8]) { + fn kill_child_storage(child_trie: &ChildTrie) { ext::with(|ext| { - let storage_key = child_storage_key_or_panic(storage_key); - ext.kill_child_storage(storage_key) + ext.kill_child_storage(child_trie) }); } @@ -128,10 +123,9 @@ impl StorageApi for () { ).unwrap_or(false) } - fn exists_child_storage(storage_key: &[u8], key: &[u8]) -> bool { + fn exists_child_storage(child_trie: ChildTrieReadRef, key: &[u8]) -> bool { ext::with(|ext| { - let storage_key = child_storage_key_or_panic(storage_key); - ext.exists_child_storage(storage_key, key) + ext.exists_child_storage(child_trie, key) }).unwrap_or(false) } @@ -141,10 +135,9 @@ impl StorageApi for () { ); } - fn clear_child_prefix(storage_key: &[u8], prefix: &[u8]) { + fn clear_child_prefix(child_trie: &ChildTrie, prefix: &[u8]) { ext::with(|ext| { - let storage_key = child_storage_key_or_panic(storage_key); - ext.clear_child_prefix(storage_key, prefix) + ext.clear_child_prefix(child_trie, prefix) }); } @@ -154,10 +147,9 @@ impl StorageApi for () { ).unwrap_or(H256::zero()).into() } - fn child_storage_root(storage_key: &[u8]) -> Vec { + fn child_storage_root(child_trie: &ChildTrie) -> Vec { ext::with(|ext| { - let storage_key = child_storage_key_or_panic(storage_key); - ext.child_storage_root(storage_key) + ext.child_storage_root(child_trie) }).expect("child_storage_root cannot be called outside of an Externalities-provided environment.") } @@ -454,23 +446,17 @@ pub fn with_externalities R>(ext: &mut dyn Externalities, Vec>; - -/// A set of key value pairs for children storage; -pub type ChildrenStorageOverlay = HashMap, StorageOverlay>; - /// Execute the given closure with global functions available whose functionality routes into -/// externalities that draw from and populate `storage` and `children_storage`. +/// externalities that draw from and populate `storage`. /// Forwards the value that the closure returns. pub fn with_storage R>( - storage: &mut (StorageOverlay, ChildrenStorageOverlay), + storage: &mut StorageContent, f: F ) -> R { let mut alt_storage = Default::default(); rstd::mem::swap(&mut alt_storage, storage); - let mut ext = BasicExternalities::new(alt_storage.0, alt_storage.1); + let mut ext = BasicExternalities::new(alt_storage); let r = ext::using(&mut ext, f); *storage = ext.into_storages(); @@ -513,7 +499,10 @@ mod std_tests { true })); - t = BasicExternalities::new(map![b"foo".to_vec() => b"bar".to_vec()], map![]); + t = BasicExternalities::new(StorageContent { + top: map![b"foo".to_vec() => b"bar".to_vec()], + children: map![], + }); assert!(!with_externalities(&mut t, || { assert_eq!(storage(b"hello"), None); @@ -524,9 +513,9 @@ mod std_tests { #[test] fn read_storage_works() { - let mut t = BasicExternalities::new(map![ + let mut t = BasicExternalities::new(StorageContent { top: map![ b":test".to_vec() => b"\x0b\0\0\0Hello world".to_vec() - ], map![]); + ], children: map![]}); with_externalities(&mut t, || { let mut v = [0u8; 4]; @@ -540,12 +529,12 @@ mod std_tests { #[test] fn clear_prefix_works() { - let mut t = BasicExternalities::new(map![ + let mut t = BasicExternalities::new(StorageContent { top: map![ b":a".to_vec() => b"\x0b\0\0\0Hello world".to_vec(), b":abcd".to_vec() => b"\x0b\0\0\0Hello world".to_vec(), b":abc".to_vec() => b"\x0b\0\0\0Hello world".to_vec(), b":abdd".to_vec() => b"\x0b\0\0\0Hello world".to_vec() - ], map![]); + ], children: map![]}); with_externalities(&mut t, || { clear_prefix(b":abc"); diff --git a/core/sr-io/without_std.rs b/core/sr-io/without_std.rs index 58aff2444e368..a54d26a5a37af 100644 --- a/core/sr-io/without_std.rs +++ b/core/sr-io/without_std.rs @@ -19,8 +19,11 @@ pub use rstd; pub use rstd::{mem, slice}; use core::{intrinsics, panic::PanicInfo}; -use rstd::{vec::Vec, cell::Cell, convert::TryInto}; -use primitives::{offchain, Blake2Hasher}; +use rstd::{vec::Vec, cell::Cell, convert::TryInto, ptr}; +use primitives::{ + offchain, Blake2Hasher, + child_trie::{ChildTrie, ChildTrieReadRef}, +}; use codec::Decode; #[cfg(not(feature = "no_panic_handler"))] @@ -91,7 +94,7 @@ pub mod ext { /// # Returns /// /// Returns the original implementation wrapped in [`RestoreImplementation`]. - pub fn replace_implementation(&'static self, new_impl: T) -> RestoreImplementation { + pub fn replace_implementation(&'static self, new_impl: T) -> RestoreImplementation { if let ExchangeableFunctionState::Replaced = self.0.get().1 { panic!("Trying to replace an already replaced implementation!") } @@ -212,6 +215,39 @@ pub mod ext { /// Set value for key in storage. fn ext_set_storage(key_data: *const u8, key_len: u32, value_data: *const u8, value_len: u32); + /// Get child trie at a storage location. + /// + /// # Returns + /// + /// - `1` if child trie found and content written. + /// - `0` otherwhise. + fn ext_child_trie( + storage_key_data: *const u8, + storage_key_len: u32, + keyspace: *mut *mut u8, + keyspace_len: *mut u32, + root: *mut *mut u8, + root_len: *mut u32, + parent: *mut *mut u8, + parent_len: *mut u32, + extension: *mut *mut u8, + extension_len: *mut u32 + ) -> u32; + /// Set child trie return false if there is an attempt to change non empty root. + /// # Returns + /// + /// - `1` if set successfull + /// - `0` if not + fn ext_set_child_trie( + keyspace: *const u8, + keyspace_len: u32, + root: *const u8, + root_len: u32, + parent: *const u8, + parent_len: u32, + extension: *const u8, + extension_len: u32 + ) -> u32; /// Remove key and value from storage. fn ext_clear_storage(key_data: *const u8, key_len: u32); /// Checks if the given key exists in the storage. @@ -298,8 +334,10 @@ pub mod ext { /// /// A child storage is used e.g. by a contract. fn ext_exists_child_storage( - storage_key_data: *const u8, - storage_key_len: u32, + keyspace_data: *const u8, + keyspace_len: u32, + root_data: *const u8, + root_len: u32, key_data: *const u8, key_len: u32 ) -> u32; @@ -308,15 +346,17 @@ pub mod ext { /// See [`ext_kill_storage`] for details. /// /// A child storage is used e.g. by a contract. - fn ext_kill_child_storage(storage_key_data: *const u8, storage_key_len: u32); + fn ext_kill_child_storage(prefix_storage_key_data: *const u8, storage_key_len: u32); /// A child storage function. /// /// See [`ext_get_allocated_storage`] for details. /// /// A child storage is used e.g. by a contract. fn ext_get_allocated_child_storage( - storage_key_data: *const u8, - storage_key_len: u32, + keyspace_data: *const u8, + keyspace_len: u32, + root_data: *const u8, + root_len: u32, key_data: *const u8, key_len: u32, written_out: *mut u32 @@ -327,8 +367,10 @@ pub mod ext { /// /// A child storage is used e.g. by a contract. fn ext_get_child_storage_into( - storage_key_data: *const u8, - storage_key_len: u32, + keyspace_data: *const u8, + keyspace_len: u32, + root_data: *const u8, + root_len: u32, key_data: *const u8, key_len: u32, value_data: *mut u8, @@ -621,20 +663,6 @@ impl StorageApi for () { } } - fn child_storage(storage_key: &[u8], key: &[u8]) -> Option> { - let mut length: u32 = 0; - unsafe { - let ptr = ext_get_allocated_child_storage.get()( - storage_key.as_ptr(), - storage_key.len() as u32, - key.as_ptr(), - key.len() as u32, - &mut length - ); - from_raw_parts(ptr, length) - } - } - fn read_storage(key: &[u8], value_out: &mut [u8], value_offset: usize) -> Option { unsafe { match ext_get_storage_into.get()( @@ -650,10 +678,92 @@ impl StorageApi for () { } } - fn read_child_storage(storage_key: &[u8], key: &[u8], value_out: &mut [u8], value_offset: usize) -> Option { + /// Get child trie at storage key location. + fn child_trie(storage_key: &[u8]) -> Option { + let mut key = ptr::null_mut(); + let mut key_length = 0u32; + let mut root = ptr::null_mut(); + let mut root_length = 0u32; + let mut parent = ptr::null_mut(); + let mut parent_length = 0u32; + let mut extension = ptr::null_mut(); + let mut extension_length = 0u32; + unsafe { + if ext_child_trie.get()( + storage_key.as_ptr(), + storage_key.len() as u32, + &mut key as *mut *mut u8, + &mut key_length, + &mut root as *mut *mut u8, + &mut root_length, + &mut parent as *mut *mut u8, + &mut parent_length, + &mut extension as *mut *mut u8, + &mut extension_length, + ) == 1 { + Some(ChildTrie::unsafe_from_ptr_child_trie( + key, + key_length, + root, + root_length, + parent, + parent_length, + extension, + extension_length, + )) + } else { + None + } + } + } + + /// Set child trie. Can fail and return false (eg change of root). + fn set_child_trie(child_trie: ChildTrie) -> bool { + unsafe { + let p = child_trie.ptr_child_trie(); + ext_set_child_trie.get()(p.0, p.1, p.2, p.3, p.4, p.5, p.6, p.7) == 1 + } + } + + + fn child_storage(child_trie: ChildTrieReadRef, key: &[u8]) -> Option> { + let mut length: u32 = 0; + let empty_byte: [u8;0] = []; + let (keyspace, root) = match child_trie { + ChildTrieReadRef::New(keyspace) => (keyspace, &empty_byte[..]), + ChildTrieReadRef::Existing (root, keyspace) => (keyspace, root), + }; + unsafe { + let ptr = ext_get_allocated_child_storage.get()( + keyspace.as_ptr(), + keyspace.len() as u32, + root.as_ptr(), + root.len() as u32, + key.as_ptr(), + key.len() as u32, + &mut length + ); + from_raw_parts(ptr, length) + } + } + + fn read_child_storage( + child_trie: ChildTrieReadRef, + key: &[u8], + value_out: &mut [u8], + value_offset: usize + ) -> Option { + let empty_byte: [u8;0] = []; + let (keyspace, root) = match child_trie { + ChildTrieReadRef::New(keyspace) => (keyspace, &empty_byte[..]), + ChildTrieReadRef::Existing (root, keyspace) => (keyspace, root), + }; unsafe { match ext_get_child_storage_into.get()( - storage_key.as_ptr(), storage_key.len() as u32, + keyspace.as_ptr(), + keyspace.len() as u32, + root.as_ptr(), + root.len() as u32, key.as_ptr(), key.len() as u32, value_out.as_mut_ptr(), value_out.len() as u32, value_offset as u32 @@ -673,7 +783,8 @@ impl StorageApi for () { } } - fn set_child_storage(storage_key: &[u8], key: &[u8], value: &[u8]) { + fn set_child_storage(child_trie: &ChildTrie, key: &[u8], value: &[u8]) { + let storage_key = child_trie.parent_slice(); unsafe { ext_set_child_storage.get()( storage_key.as_ptr(), storage_key.len() as u32, @@ -691,7 +802,8 @@ impl StorageApi for () { } } - fn clear_child_storage(storage_key: &[u8], key: &[u8]) { + fn clear_child_storage(child_trie: &ChildTrie, key: &[u8]) { + let storage_key = child_trie.parent_slice(); unsafe { ext_clear_child_storage.get()( storage_key.as_ptr(), storage_key.len() as u32, @@ -708,10 +820,17 @@ impl StorageApi for () { } } - fn exists_child_storage(storage_key: &[u8], key: &[u8]) -> bool { + fn exists_child_storage(child_trie: ChildTrieReadRef, key: &[u8]) -> bool { + let (keyspace, root) = match child_trie { + ChildTrieReadRef::New(keyspace) => (keyspace, &[][..]), + ChildTrieReadRef::Existing (root, keyspace) => (keyspace, root), + }; unsafe { ext_exists_child_storage.get()( - storage_key.as_ptr(), storage_key.len() as u32, + keyspace.as_ptr(), + keyspace.len() as u32, + root.as_ptr(), + root.len() as u32, key.as_ptr(), key.len() as u32 ) != 0 } @@ -726,16 +845,19 @@ impl StorageApi for () { } } - fn clear_child_prefix(storage_key: &[u8], prefix: &[u8]) { + fn clear_child_prefix(child_trie: &ChildTrie, prefix: &[u8]) { + let storage_key = child_trie.parent_slice(); unsafe { ext_clear_child_prefix.get()( - storage_key.as_ptr(), storage_key.len() as u32, + storage_key.as_ptr(), + storage_key.len() as u32, prefix.as_ptr(), prefix.len() as u32 ); } } - fn kill_child_storage(storage_key: &[u8]) { + fn kill_child_storage(child_trie: &ChildTrie) { + let storage_key = child_trie.parent_slice(); unsafe { ext_kill_child_storage.get()( storage_key.as_ptr(), @@ -752,7 +874,8 @@ impl StorageApi for () { result } - fn child_storage_root(storage_key: &[u8]) -> Vec { + fn child_storage_root(child_trie: &ChildTrie) -> Vec { + let storage_key = child_trie.parent_slice(); let mut length: u32 = 0; unsafe { let ptr = ext_child_storage_root.get()( diff --git a/core/sr-primitives/src/lib.rs b/core/sr-primitives/src/lib.rs index bcd54c660b8a7..8c7df65eec79f 100644 --- a/core/sr-primitives/src/lib.rs +++ b/core/sr-primitives/src/lib.rs @@ -35,7 +35,7 @@ pub use paste; pub use app_crypto; #[cfg(feature = "std")] -pub use runtime_io::{StorageOverlay, ChildrenStorageOverlay}; +pub use runtime_io::{StorageOverlay, ChildrenStorageOverlay, StorageContent}; use rstd::{prelude::*, ops, convert::{TryInto, TryFrom}}; use primitives::{crypto, ed25519, sr25519, hash::{H256, H512}}; @@ -99,15 +99,15 @@ pub use serde::{Serialize, Deserialize, de::DeserializeOwned}; #[cfg(feature = "std")] pub trait BuildStorage: Sized { /// Build the storage out of this builder. - fn build_storage(self) -> Result<(StorageOverlay, ChildrenStorageOverlay), String> { - let mut storage = (Default::default(), Default::default()); + fn build_storage(self) -> Result { + let mut storage = Default::default(); self.assimilate_storage(&mut storage)?; Ok(storage) } /// Assimilate the storage for this module into pre-existing overlays. fn assimilate_storage( self, - storage: &mut (StorageOverlay, ChildrenStorageOverlay), + storage: &mut StorageContent, ) -> Result<(), String>; } @@ -117,25 +117,27 @@ pub trait BuildModuleGenesisStorage: Sized { /// Create the module genesis storage into the given `storage` and `child_storage`. fn build_module_genesis_storage( self, - storage: &mut (StorageOverlay, ChildrenStorageOverlay), + storage: &mut StorageContent, ) -> Result<(), String>; } #[cfg(feature = "std")] -impl BuildStorage for (StorageOverlay, ChildrenStorageOverlay) { - fn build_storage(self) -> Result<(StorageOverlay, ChildrenStorageOverlay), String> { +impl BuildStorage for StorageContent { + fn build_storage(self) -> Result { Ok(self) } fn assimilate_storage( self, - storage: &mut (StorageOverlay, ChildrenStorageOverlay), + storage: &mut StorageContent, )-> Result<(), String> { - storage.0.extend(self.0); - for (k, other_map) in self.1.into_iter() { - if let Some(map) = storage.1.get_mut(&k) { - map.extend(other_map); + storage.top.extend(self.top); + for (k, other_map) in self.children.into_iter() { + if let Some(map) = storage.children.get_mut(&k) { + map.0.extend(other_map.0); + // Unchecked child trie replacement + map.1 = other_map.1; } else { - storage.1.insert(k, other_map); + storage.children.insert(k, other_map); } } Ok(()) @@ -817,7 +819,7 @@ macro_rules! impl_outer_config { impl $crate::BuildStorage for $main { fn assimilate_storage( self, - storage: &mut ($crate::StorageOverlay, $crate::ChildrenStorageOverlay), + storage: &mut $crate::StorageContent, ) -> std::result::Result<(), String> { $( if let Some(extra) = self.[< $snake $(_ $instance )? >] { diff --git a/core/state-machine/src/backend.rs b/core/state-machine/src/backend.rs index 28ab61a5edf9c..4bec16fd06ea4 100644 --- a/core/state-machine/src/backend.rs +++ b/core/state-machine/src/backend.rs @@ -18,14 +18,61 @@ use std::{error, fmt}; use std::cmp::Ord; -use std::collections::HashMap; +use std::collections::{HashMap}; use std::marker::PhantomData; use log::warn; use hash_db::Hasher; use crate::trie_backend::TrieBackend; use crate::trie_backend_essence::TrieBackendStorage; -use trie::{TrieMut, MemoryDB, child_trie_root, default_child_trie_root, TrieConfiguration}; +use trie::{TrieMut, MemoryDB, child_trie_root, default_child_trie_root, KeySpacedDBMut, + TrieConfiguration}; use trie::trie_types::{TrieDBMut, Layout}; +use primitives::child_trie::{KeySpace, ChildTrie, ChildTrieReadRef}; + +/// A set of key value pairs for storage. +pub type StorageOverlay = HashMap, Vec>; + +/// A set of key value pairs for children storage; +pub type ChildrenStorageOverlay = HashMap; + +#[derive(Default, Clone, PartialEq)] +// see FIXME #2740 related to performance. +/// StorageContent, it can contain transactional change +/// for storage to. +pub struct StorageContent { + /// Key value for top trie. + pub top: StorageOverlay, + /// Key value and child trie update (stored by `KeySpace`). + pub children: ChildrenStorageOverlay, +} + +impl StorageContent { + /// Assimilate this map transaction into other storage. + /// This is pretty close to a reverse `extend` + pub fn assimilate_storage( + self, + storage: &mut StorageOverlay, + child_storage: &mut ChildrenStorageOverlay + ) { + storage.extend(self.top); + for (k, (other_map, other_child_trie)) in self.children.into_iter() { + if let Some((map, child_trie)) = child_storage.get_mut(&k) { + map.extend(other_map); + // warning this can result in loss of info if both map are not + // build other same backend. + *child_trie = other_child_trie; + } else { + child_storage.insert(k, (other_map, other_child_trie)); + } + } + } +} + +// see FIXME #2740 related to performance. +/// Type alias over a list of memory change cache, with support for child trie. +/// This only need to contain an iterable set of values. +pub type VecTransaction = Vec<(Option, Vec, Option>)>; + /// A state backend is used to read state data and can have changes committed /// to it. @@ -35,7 +82,7 @@ pub trait Backend { /// An error type when fetching data is not possible. type Error: super::Error; - /// Storage changes to be applied if committing + /// Storage changes to be applied if committing. type Transaction: Consolidate + Default; /// Type of trie backend storage. @@ -49,12 +96,19 @@ pub trait Backend { self.storage(key).map(|v| v.map(|v| H::hash(&v))) } + /// Get ChildTrie information or `None` if no child trie is defined for this key. + fn child_trie(&self, storage_key: &[u8]) -> Result, Self::Error> { + let prefixed_key = ChildTrie::prefix_parent_key(storage_key); + Ok(self.storage(&prefixed_key[..])? + .and_then(|n| ChildTrie::decode_node_with_parent(&n[..], prefixed_key))) + } + /// Get keyed child storage or None if there is nothing associated. - fn child_storage(&self, storage_key: &[u8], key: &[u8]) -> Result>, Self::Error>; + fn child_storage(&self, child_trie: ChildTrieReadRef, key: &[u8]) -> Result>, Self::Error>; /// Get child keyed storage value hash or None if there is nothing associated. - fn child_storage_hash(&self, storage_key: &[u8], key: &[u8]) -> Result, Self::Error> { - self.child_storage(storage_key, key).map(|v| v.map(|v| H::hash(&v))) + fn child_storage_hash(&self, child_trie: ChildTrieReadRef, key: &[u8]) -> Result, Self::Error> { + self.child_storage(child_trie, key).map(|v| v.map(|v| H::hash(&v))) } /// true if a key exists in storage. @@ -63,12 +117,12 @@ pub trait Backend { } /// true if a key exists in child storage. - fn exists_child_storage(&self, storage_key: &[u8], key: &[u8]) -> Result { - Ok(self.child_storage(storage_key, key)?.is_some()) + fn exists_child_storage(&self, child_trie: ChildTrieReadRef, key: &[u8]) -> Result { + Ok(self.child_storage(child_trie, key)?.is_some()) } /// Retrieve all entries keys of child storage and call `f` for each of those keys. - fn for_keys_in_child_storage(&self, storage_key: &[u8], f: F); + fn for_keys_in_child_storage(&self, child_trie: ChildTrieReadRef, f: F); /// Retrieve all entries keys which start with the given prefix and /// call `f` for each of those keys. @@ -83,7 +137,12 @@ pub trait Backend { /// Retrieve all child entries keys which start with the given prefix and /// call `f` for each of those keys. - fn for_child_keys_with_prefix(&self, storage_key: &[u8], prefix: &[u8], f: F); + fn for_child_keys_with_prefix( + &self, + child_trie: ChildTrieReadRef, + prefix: &[u8], + f: F, + ); /// Calculate the storage root, with given delta over what is already stored in /// the backend, and produce a "transaction" that can be used to commit. @@ -96,7 +155,7 @@ pub trait Backend { /// Calculate the child storage root, with given delta over what is already stored in /// the backend, and produce a "transaction" that can be used to commit. The second argument /// is true if child storage root equals default storage root. - fn child_storage_root(&self, storage_key: &[u8], delta: I) -> (Vec, bool, Self::Transaction) + fn child_storage_root(&self, child_trie: &ChildTrie, delta: I) -> (Vec, bool, Self::Transaction) where I: IntoIterator, Option>)>, H::Out: Ord; @@ -112,9 +171,9 @@ pub trait Backend { } /// Get all keys of child storage with given prefix - fn child_keys(&self, child_storage_key: &[u8], prefix: &[u8]) -> Vec> { + fn child_keys(&self, child_trie: ChildTrieReadRef, prefix: &[u8]) -> Vec> { let mut all = Vec::new(); - self.for_child_keys_with_prefix(child_storage_key, prefix, |k| all.push(k.to_vec())); + self.for_child_keys_with_prefix(child_trie, prefix, |k| all.push(k.to_vec())); all } @@ -124,7 +183,7 @@ pub trait Backend { /// Calculate the storage root, with given delta over what is already stored /// in the backend, and produce a "transaction" that can be used to commit. /// Does include child storage updates. - fn full_storage_root( + fn full_storage_root( &self, delta: I1, child_deltas: I2) @@ -132,20 +191,24 @@ pub trait Backend { where I1: IntoIterator, Option>)>, I2i: IntoIterator, Option>)>, - I2: IntoIterator, I2i)>, + TR: AsRef, + I2: IntoIterator, ::Out: Ord, { let mut txs: Self::Transaction = Default::default(); let mut child_roots: Vec<_> = Default::default(); // child first - for (storage_key, child_delta) in child_deltas { + for (child_trie, child_delta) in child_deltas { let (child_root, empty, child_txs) = - self.child_storage_root(&storage_key[..], child_delta); + self.child_storage_root(child_trie.as_ref(), child_delta); txs.consolidate(child_txs); if empty { - child_roots.push((storage_key, None)); + child_roots.push((child_trie.as_ref().parent_trie().to_vec(), None)); } else { - child_roots.push((storage_key, Some(child_root))); + child_roots.push( + (child_trie.as_ref().parent_trie().to_vec(), + Some(child_trie.as_ref().encoded_with_root(&child_root[..]))) + ); } } let (root, parent_txs) = self.storage_root( @@ -169,7 +232,7 @@ impl Consolidate for () { } } -impl Consolidate for Vec<(Option>, Vec, Option>)> { +impl Consolidate for VecTransaction { fn consolidate(&mut self, mut other: Self) { self.append(&mut other); } @@ -199,7 +262,7 @@ impl error::Error for Void { /// In-memory backend. Fully recomputes tries on each commit but useful for /// tests. pub struct InMemory { - inner: HashMap>, HashMap, Vec>>, + inner: StorageContent, trie: Option, H>>, _hasher: PhantomData, } @@ -233,39 +296,39 @@ impl PartialEq for InMemory { impl InMemory { /// Copy the state, with applied updates pub fn update(&self, changes: >::Transaction) -> Self { - let mut inner: HashMap<_, _> = self.inner.clone(); - for (storage_key, key, val) in changes { - match val { - Some(v) => { inner.entry(storage_key).or_default().insert(key, v); }, - None => { inner.entry(storage_key).or_default().remove(&key); }, + // costy clone + let mut inner = self.inner.clone(); + + for (child_trie, key, val) in changes { + if let Some(child_trie) = child_trie { + let mut entry = inner.children.entry(child_trie.keyspace().clone()) + .or_insert_with(|| (Default::default(), child_trie.clone())); + match val { + Some(v) => { + entry.0.insert(key, v); + // very costy clone here for update of value: could be change + entry.1 = child_trie; + }, + None => { + entry.0.remove(&key); + }, + } + } else { + match val { + Some(v) => { let _ = inner.top.insert(key, v); }, + None => { let _ = inner.top.remove(&key); }, + } } + } inner.into() } -} -impl From>, HashMap, Vec>>> for InMemory { - fn from(inner: HashMap>, HashMap, Vec>>) -> Self { - InMemory { - inner: inner, - trie: None, - _hasher: PhantomData, - } - } } -impl From<( - HashMap, Vec>, - HashMap, HashMap, Vec>>, -)> for InMemory { - fn from(inners: ( - HashMap, Vec>, - HashMap, HashMap, Vec>>, - )) -> Self { - let mut inner: HashMap>, HashMap, Vec>> - = inners.1.into_iter().map(|(k, v)| (Some(k), v)).collect(); - inner.insert(None, inners.0); +impl From for InMemory { + fn from(inner: StorageContent) -> Self { InMemory { inner: inner, trie: None, @@ -276,22 +339,27 @@ impl From<( impl From, Vec>> for InMemory { fn from(inner: HashMap, Vec>) -> Self { - let mut expanded = HashMap::new(); - expanded.insert(None, inner); InMemory { - inner: expanded, + inner: StorageContent { top: inner, children: Default::default() }, trie: None, _hasher: PhantomData, } } } -impl From>, Vec, Option>)>> for InMemory { - fn from(inner: Vec<(Option>, Vec, Option>)>) -> Self { - let mut expanded: HashMap>, HashMap, Vec>> = HashMap::new(); +impl From for InMemory { + fn from(inner: VecTransaction) -> Self { + let mut expanded: StorageContent = Default::default(); for (child_key, key, value) in inner { if let Some(value) = value { - expanded.entry(child_key).or_default().insert(key, value); + if let Some(child_trie) = child_key { + let mut entry = expanded.children.entry(child_trie.keyspace().clone()) + .or_insert_with(|| (Default::default(), child_trie.clone())); + entry.0.insert(key, value); + entry.1 = child_trie; + } else { + expanded.top.insert(key, value); + } } } expanded.into() @@ -300,46 +368,52 @@ impl From>, Vec, Option>)>> for InMem impl super::Error for Void {} +#[cfg(test)] impl InMemory { - /// child storage key iterator - pub fn child_storage_keys(&self) -> impl Iterator { - self.inner.iter().filter_map(|item| item.0.as_ref().map(|v|&v[..])) + /// Child trie in memory content iterator. + pub fn child_storage_child_trie(&self) -> impl Iterator { + self.inner.children.iter().map(|item| &(item.1).1) } } impl Backend for InMemory { type Error = Void; - type Transaction = Vec<(Option>, Vec, Option>)>; + type Transaction = VecTransaction; type TrieBackendStorage = MemoryDB; fn storage(&self, key: &[u8]) -> Result>, Self::Error> { - Ok(self.inner.get(&None).and_then(|map| map.get(key).map(Clone::clone))) + Ok(self.inner.top.get(key).map(Clone::clone)) } - fn child_storage(&self, storage_key: &[u8], key: &[u8]) -> Result>, Self::Error> { - Ok(self.inner.get(&Some(storage_key.to_vec())).and_then(|map| map.get(key).map(Clone::clone))) + fn child_storage(&self, child_trie: ChildTrieReadRef, key: &[u8]) -> Result>, Self::Error> { + Ok(self.inner.children.get(child_trie.keyspace()).and_then(|map| map.0.get(key).map(Clone::clone))) } fn exists_storage(&self, key: &[u8]) -> Result { - Ok(self.inner.get(&None).map(|map| map.get(key).is_some()).unwrap_or(false)) + Ok(self.storage(key)?.is_some()) } fn for_keys_with_prefix(&self, prefix: &[u8], f: F) { - self.inner.get(&None).map(|map| map.keys().filter(|key| key.starts_with(prefix)).map(|k| &**k).for_each(f)); + self.inner.top.keys().filter(|key| key.starts_with(prefix)).map(|k| &**k).for_each(f); } fn for_key_values_with_prefix(&self, prefix: &[u8], mut f: F) { - self.inner.get(&None).map(|map| map.iter().filter(|(key, _val)| key.starts_with(prefix)) - .for_each(|(k, v)| f(k, v))); + self.inner.top.iter().filter(|(key, _val)| key.starts_with(prefix)) + .for_each(|(k, v)| f(k, v)); } - fn for_keys_in_child_storage(&self, storage_key: &[u8], mut f: F) { - self.inner.get(&Some(storage_key.to_vec())).map(|map| map.keys().for_each(|k| f(&k))); + fn for_keys_in_child_storage(&self, child_trie: ChildTrieReadRef, mut f: F) { + self.inner.children.get(child_trie.keyspace()).map(|m| m.0.keys().for_each(|k| f(&k))); } - fn for_child_keys_with_prefix(&self, storage_key: &[u8], prefix: &[u8], f: F) { - self.inner.get(&Some(storage_key.to_vec())) - .map(|map| map.keys().filter(|key| key.starts_with(prefix)).map(|k| &**k).for_each(f)); + fn for_child_keys_with_prefix( + &self, + child_trie: ChildTrieReadRef, + prefix: &[u8], + f: F, + ) { + self.inner.children.get(child_trie.keyspace()) + .map(|(map, _ct)| map.keys().filter(|key| key.starts_with(prefix)).map(|k| &**k).for_each(f)); } fn storage_root(&self, delta: I) -> (H::Out, Self::Transaction) @@ -347,13 +421,13 @@ impl Backend for InMemory { I: IntoIterator, Option>)>, ::Out: Ord, { - let existing_pairs = self.inner.get(&None) - .into_iter() - .flat_map(|map| map.iter().map(|(k, v)| (k.clone(), Some(v.clone())))); + let existing_pairs = self.inner.top.iter().map(|(k, v)| (k.clone(), Some(v.clone()))); let transaction: Vec<_> = delta.into_iter().collect(); - let root = Layout::::trie_root(existing_pairs.chain(transaction.iter().cloned()) - .collect::>() + let map_input = existing_pairs.chain(transaction.iter().cloned()) + .collect::>(); + + let root = Layout::::trie_root(map_input .into_iter() .filter_map(|(k, maybe_val)| maybe_val.map(|val| (k, val))) ); @@ -363,92 +437,86 @@ impl Backend for InMemory { (root, full_transaction) } - fn child_storage_root(&self, storage_key: &[u8], delta: I) -> (Vec, bool, Self::Transaction) + fn child_storage_root(&self, child_trie: &ChildTrie, delta: I) -> (Vec, bool, Self::Transaction) where I: IntoIterator, Option>)>, H::Out: Ord { - let storage_key = storage_key.to_vec(); - - let existing_pairs = self.inner.get(&Some(storage_key.clone())) - .into_iter() - .flat_map(|map| map.iter().map(|(k, v)| (k.clone(), Some(v.clone())))); - let transaction: Vec<_> = delta.into_iter().collect(); + // all content is clone with this method, avoiding it would require changing + // the prototype (having a function for calculating root on reference and + // one for getting transaction while dropping value. + let existing_pairs = self.inner.children.get( + child_trie.keyspace() + ).into_iter().flat_map(|map| map.0.iter().map(|(k, v)| (k.clone(), Some(v.clone())))); let root = child_trie_root::, _, _, _>( - &storage_key, existing_pairs.chain(transaction.iter().cloned()) .collect::>() .into_iter() .filter_map(|(k, maybe_val)| maybe_val.map(|val| (k, val))) ); - let full_transaction = transaction.into_iter().map(|(k, v)| (Some(storage_key.clone()), k, v)).collect(); + // note that the child trie is from parameter, this is allowed because update of child + // trie should be done by the same child trie instance; can prove problematic? + let full_transaction = transaction.into_iter().map(|(k, v)| (Some(child_trie.clone()), k, v)).collect(); - let is_default = root == default_child_trie_root::>(&storage_key); + let is_default = root == default_child_trie_root::>(); - (root, is_default, full_transaction) + (root, is_default && child_trie.extension().is_empty(), full_transaction) } fn pairs(&self) -> Vec<(Vec, Vec)> { - self.inner.get(&None) - .into_iter() - .flat_map(|map| map.iter().map(|(k, v)| (k.clone(), v.clone()))) - .collect() + self.inner.top.iter().map(|(k, v)| (k.clone(), v.clone())).collect() } fn keys(&self, prefix: &[u8]) -> Vec> { - self.inner.get(&None) - .into_iter() - .flat_map(|map| map.keys().filter(|k| k.starts_with(prefix)).cloned()) - .collect() - } - - fn child_keys(&self, storage_key: &[u8], prefix: &[u8]) -> Vec> { - self.inner.get(&Some(storage_key.to_vec())) - .into_iter() - .flat_map(|map| map.keys().filter(|k| k.starts_with(prefix)).cloned()) - .collect() + self.inner.top.keys().filter(|k| k.starts_with(prefix)).cloned().collect() } fn as_trie_backend(&mut self)-> Option<&TrieBackend> { + // Warning tihs do not use keyspace for child trie, that is not an issue unless + // the memorydb gets incorectly use to fead keyvalue database. let mut mdb = MemoryDB::default(); - let mut root = None; let mut new_child_roots = Vec::new(); - let mut root_map = None; - for (storage_key, map) in &self.inner { - if let Some(storage_key) = storage_key.as_ref() { - let ch = insert_into_memory_db::(&mut mdb, map.clone().into_iter())?; - new_child_roots.push((storage_key.clone(), ch.as_ref().into())); - } else { - root_map = Some(map); - } - } - // root handling - if let Some(map) = root_map.take() { - root = Some(insert_into_memory_db::( + let StorageContent { top: root_map, children: child_map } = + std::mem::replace(&mut self.inner, Default::default()); + + for (_k, (map, child_trie)) in child_map.into_iter() { + let ch = insert_into_memory_db::( &mut mdb, - map.clone().into_iter().chain(new_child_roots.into_iter()) - )?); + map.into_iter(), + Some(child_trie.node_ref()), + )?; + new_child_roots.push(( + child_trie.parent_trie().to_vec(), + child_trie.encoded_with_root(ch.as_ref()))); } - let root = match root { - Some(root) => root, - None => insert_into_memory_db::(&mut mdb, ::std::iter::empty())?, - }; + // root handling + let root = insert_into_memory_db::( + &mut mdb, + root_map.into_iter().chain(new_child_roots.into_iter()), + None, + )?; self.trie = Some(TrieBackend::new(mdb, root)); self.trie.as_ref() } } /// Insert input pairs into memory db. -pub(crate) fn insert_into_memory_db(mdb: &mut MemoryDB, input: I) -> Option +pub(crate) fn insert_into_memory_db( + mdb: &mut MemoryDB, + input: I, + child_trie: Option +) -> Option where H: Hasher, I: IntoIterator, Vec)>, { + let keyspace = child_trie.as_ref().map(|child| child.keyspace()); + let mut mdb = KeySpacedDBMut::new(&mut *mdb, keyspace); let mut root = ::Out::default(); { - let mut trie = TrieDBMut::::new(mdb, &mut root); + let mut trie = TrieDBMut::::new(&mut mdb, &mut root); for (key, value) in input { if let Err(e) = trie.insert(&key, &value) { warn!(target: "trie", "Failed to write to trie: {}", e); diff --git a/core/state-machine/src/basic.rs b/core/state-machine/src/basic.rs index 1d36a0ddad51d..7b6c07b77598c 100644 --- a/core/state-machine/src/basic.rs +++ b/core/state-machine/src/basic.rs @@ -18,32 +18,38 @@ use std::collections::HashMap; use std::iter::FromIterator; -use crate::backend::{Backend, InMemory}; +use crate::backend::{Backend, InMemory, StorageContent}; use hash_db::Hasher; use trie::{TrieConfiguration, default_child_trie_root}; use trie::trie_types::Layout; use primitives::offchain; use primitives::storage::well_known_keys::is_child_storage_key; -use super::{ChildStorageKey, Externalities}; +use primitives::child_trie::{ChildTrie, ChildTrieReadRef, KeySpace}; +use super::Externalities; use log::warn; /// Simple HashMap-based Externalities impl. #[derive(Debug)] pub struct BasicExternalities { top: HashMap, Vec>, - children: HashMap, HashMap, Vec>>, + children: HashMap, Vec>, ChildTrie)>, + pending_child: HashMap, Option>, } impl BasicExternalities { /// Create a new instance of `BasicExternalities` - pub fn new( - top: HashMap, Vec>, - children: HashMap, HashMap, Vec>>, - ) -> Self { + pub fn new(map: StorageContent) -> Self { + let pending_child = map.children.values() + .map(|(_, child_trie)| ( + child_trie.parent_slice().to_vec(), + Some(child_trie.keyspace().clone()) + )).collect(); + BasicExternalities { - top, - children, + top: map.top, + children: map.children, + pending_child, } } @@ -53,11 +59,8 @@ impl BasicExternalities { } /// Consume self and returns inner storages - pub fn into_storages(self) -> ( - HashMap, Vec>, - HashMap, HashMap, Vec>>, - ) { - (self.top, self.children) + pub fn into_storages(self) -> StorageContent { + StorageContent {top: self.top, children: self.children} } } @@ -76,7 +79,7 @@ impl FromIterator<(Vec, Vec)> for BasicExternalities { } impl Default for BasicExternalities { - fn default() -> Self { Self::new(Default::default(), Default::default()) } + fn default() -> Self { Self::new(Default::default()) } } impl From, Vec>> for BasicExternalities { @@ -84,6 +87,7 @@ impl From, Vec>> for BasicExternalities { BasicExternalities { top: hashmap, children: Default::default(), + pending_child: Default::default(), } } } @@ -97,12 +101,25 @@ impl Externalities for BasicExternalities where H::Out: Ord { Externalities::::storage(self, key) } - fn child_storage(&self, storage_key: ChildStorageKey, key: &[u8]) -> Option> { - self.children.get(storage_key.as_ref()).and_then(|child| child.get(key)).cloned() + fn child_storage(&self, child_trie: ChildTrieReadRef, key: &[u8]) -> Option> { + let keyspace = child_trie.keyspace(); + self.children.get(keyspace).and_then(|child| child.0.get(key)).cloned() + } + + fn child_trie(&self, storage_key: &[u8]) -> Option { + match self.pending_child.get(storage_key) { + Some(Some(keyspace)) => { + let map = self.children.get(keyspace) + .expect("pending entry always have a children association; qed"); + return Some(map.1.clone()); + }, + Some(None) => None, + None => None, + } } - fn original_child_storage(&self, storage_key: ChildStorageKey, key: &[u8]) -> Option> { - Externalities::::child_storage(self, storage_key, key) + fn original_child_storage(&self, child_trie: ChildTrieReadRef, key: &[u8]) -> Option> { + Externalities::::child_storage(self, child_trie, key) } fn place_storage(&mut self, key: Vec, maybe_value: Option>) { @@ -119,22 +136,48 @@ impl Externalities for BasicExternalities where H::Out: Ord { fn place_child_storage( &mut self, - storage_key: ChildStorageKey, + child_trie: &ChildTrie, key: Vec, value: Option> ) { - let child_map = self.children.entry(storage_key.into_owned()).or_default(); + let p = &mut self.children; + let pc = &mut self.pending_child; + let child_map = p.entry(child_trie.keyspace().clone()) + .or_insert_with(|| { + let parent = child_trie.parent_slice().to_vec(); + pc.insert(parent, Some(child_trie.keyspace().clone())); + (Default::default(), child_trie.clone()) + }); + if let Some(value) = value { - child_map.insert(key, value); + child_map.0.insert(key, value); } else { - child_map.remove(&key); + child_map.0.remove(&key); } } - fn kill_child_storage(&mut self, storage_key: ChildStorageKey) { - self.children.remove(storage_key.as_ref()); + fn kill_child_storage(&mut self, child_trie: &ChildTrie) { + let keyspace = child_trie.keyspace(); + if let Some((map_val, _ct)) = self.children.get_mut(keyspace) { + map_val.clear(); + } } + fn set_child_trie(&mut self, child_trie: ChildTrie) -> bool { + let keyspace = child_trie.keyspace(); + if let Some((_, old_ct)) = self.children.get_mut(keyspace) { + if old_ct.is_updatable_with(&child_trie) { + *old_ct = child_trie; + } else { + return false; + } + } else { + self.pending_child.insert(child_trie.parent_slice().to_vec(), Some(child_trie.keyspace().clone())); + self.children.insert(keyspace.to_vec(), (Default::default(), child_trie)); + } + true + } + fn clear_prefix(&mut self, prefix: &[u8]) { if is_child_storage_key(prefix) { warn!( @@ -147,42 +190,42 @@ impl Externalities for BasicExternalities where H::Out: Ord { self.top.retain(|key, _| !key.starts_with(prefix)); } - fn clear_child_prefix(&mut self, storage_key: ChildStorageKey, prefix: &[u8]) { - if let Some(child) = self.children.get_mut(storage_key.as_ref()) { - child.retain(|key, _| !key.starts_with(prefix)); + fn clear_child_prefix(&mut self, child_trie: &ChildTrie, prefix: &[u8]) { + let keyspace = child_trie.keyspace(); + if let Some(child) = self.children.get_mut(keyspace) { + child.0.retain(|key, _| !key.starts_with(prefix)); } } fn chain_id(&self) -> u64 { 42 } fn storage_root(&mut self) -> H::Out { + let mut top = self.top.clone(); - let keys: Vec<_> = self.children.keys().map(|k| k.to_vec()).collect(); + let child_tries: Vec<_> = self.children.values().map(|v| v.1.clone()).collect(); // Single child trie implementation currently allows using the same child // empty root for all child trie. Using null storage key until multiple // type of child trie support. - let empty_hash = default_child_trie_root::>(&[]); - for storage_key in keys { - let child_root = self.child_storage_root( - ChildStorageKey::::from_slice(storage_key.as_slice()) - .expect("Map only feed by valid keys; qed") - ); - if &empty_hash[..] == &child_root[..] { - top.remove(&storage_key); + let empty_hash = default_child_trie_root::>(); + for child_trie in child_tries { + let child_root = >::child_storage_root(self, &child_trie); + if &empty_hash[..] == &child_root[..] && child_trie.extension().len() > 0 { + top.remove(child_trie.parent_trie()); } else { - top.insert(storage_key, child_root); + top.insert(child_trie.parent_trie().to_vec(), child_trie.encoded_with_root(&child_root)); } } Layout::::trie_root(self.top.clone()) } - fn child_storage_root(&mut self, storage_key: ChildStorageKey) -> Vec { - if let Some(child) = self.children.get(storage_key.as_ref()) { - let delta = child.clone().into_iter().map(|(k, v)| (k, Some(v))); + fn child_storage_root(&mut self, child_trie: &ChildTrie) -> Vec { + let keyspace = child_trie.keyspace(); + if let Some(child) = self.children.get(keyspace) { + let delta = child.0.clone().into_iter().map(|(k, v)| (k, Some(v))); - InMemory::::default().child_storage_root(storage_key.as_ref(), delta).0 + InMemory::::default().child_storage_root(&child.1, delta).0 } else { - default_child_trie_root::>(storage_key.as_ref()) + default_child_trie_root::>() } } @@ -233,41 +276,38 @@ mod tests { #[test] fn children_works() { - let child_storage = b":child_storage:default:test".to_vec(); - - let mut ext = BasicExternalities::new( - Default::default(), - map![ - child_storage.clone() => map![ + let child_storage = b":child_storage:default:test"; + + // use a dummy child trie (keyspace and undefined trie). + let child_trie = ChildTrie::fetch_or_new(|_| None, |_| (), child_storage, || 1u128); + let mut ext = BasicExternalities::new(StorageContent { + top: Default::default(), + children: map![ + child_trie.keyspace().to_vec() => (map![ b"doe".to_vec() => b"reindeer".to_vec() - ] - ] - ); + ], child_trie.clone()) + ], + }); let ext = &mut ext as &mut dyn Externalities; - let child = || ChildStorageKey::from_vec(child_storage.clone()).unwrap(); - - assert_eq!(ext.child_storage(child(), b"doe"), Some(b"reindeer".to_vec())); + assert_eq!(ext.child_storage(child_trie.node_ref(), b"doe"), Some(b"reindeer".to_vec())); - ext.set_child_storage(child(), b"dog".to_vec(), b"puppy".to_vec()); - assert_eq!(ext.child_storage(child(), b"dog"), Some(b"puppy".to_vec())); + ext.set_child_storage(&child_trie, b"dog".to_vec(), b"puppy".to_vec()); + assert_eq!(ext.child_storage(child_trie.node_ref(), b"dog"), Some(b"puppy".to_vec())); - ext.clear_child_storage(child(), b"dog"); - assert_eq!(ext.child_storage(child(), b"dog"), None); + ext.clear_child_storage(&child_trie, b"dog"); + assert_eq!(ext.child_storage(child_trie.node_ref(), b"dog"), None); - ext.kill_child_storage(child()); - assert_eq!(ext.child_storage(child(), b"doe"), None); + ext.kill_child_storage(&child_trie); + assert_eq!(ext.child_storage(child_trie.node_ref(), b"doe"), None); } #[test] fn basic_externalities_is_empty() { // Make sure no values are set by default in `BasicExternalities`. - let (storage, child_storage) = BasicExternalities::new( - Default::default(), - Default::default(), - ).into_storages(); - assert!(storage.is_empty()); - assert!(child_storage.is_empty()); + let storages = BasicExternalities::new(Default::default()).into_storages(); + assert!(storages.top.is_empty()); + assert!(storages.children.is_empty()); } } diff --git a/core/state-machine/src/changes_trie/build.rs b/core/state-machine/src/changes_trie/build.rs index 824bdd3542d58..e080cca26741f 100644 --- a/core/state-machine/src/changes_trie/build.rs +++ b/core/state-machine/src/changes_trie/build.rs @@ -16,13 +16,14 @@ //! Structures and functions required to build changes trie for given block. +use primitives::child_trie::ChildTrie; use std::collections::{BTreeMap, BTreeSet}; use std::collections::btree_map::Entry; use codec::Decode; use hash_db::Hasher; use num_traits::One; use crate::backend::Backend; -use crate::overlayed_changes::OverlayedChanges; +use crate::overlayed_changes::{OverlayedChanges, OverlayedValueResult}; use crate::trie_backend_essence::TrieBackendEssence; use crate::changes_trie::build_iterator::digest_build_iterator; use crate::changes_trie::input::{InputKey, InputPair, DigestIndex, ExtrinsicIndex}; @@ -100,19 +101,19 @@ fn prepare_extrinsics_input<'a, B, H, Number>( Number: BlockNumber, { - let mut children_keys = BTreeSet::>::new(); + let mut children_tries = BTreeSet::::new(); let mut children_result = BTreeMap::new(); - for (storage_key, _) in changes.prospective.children.iter() + for (_ks, v) in changes.prospective.children.iter() .chain(changes.committed.children.iter()) { - children_keys.insert(storage_key.clone()); + children_tries.insert(v.child_trie.clone()); } - for storage_key in children_keys { + for child_trie in children_tries { let child_index = ChildIndex:: { block: block.clone(), - storage_key: storage_key.clone(), + storage_key: child_trie.parent_slice().to_vec(), }; - let iter = prepare_extrinsics_input_inner(backend, block, changes, Some(storage_key))?; + let iter = prepare_extrinsics_input_inner(backend, block, changes, Some(child_trie))?; children_result.insert(child_index, iter); } @@ -125,15 +126,20 @@ fn prepare_extrinsics_input_inner<'a, B, H, Number>( backend: &'a B, block: &Number, changes: &'a OverlayedChanges, - storage_key: Option>, + child_trie: Option, ) -> Result> + 'a, String> where B: Backend, H: Hasher, Number: BlockNumber, { - let (committed, prospective) = if let Some(sk) = storage_key.as_ref() { - (changes.committed.children.get(sk), changes.prospective.children.get(sk)) + let (committed, prospective) = if let Some(ct) = child_trie.as_ref() { + ( + changes.committed.children.get(ct.keyspace()) + .map(|cs| &cs.values), + changes.prospective.children.get(ct.keyspace()) + .map(|cs| &cs.values), + ) } else { (Some(&changes.committed.top), Some(&changes.prospective.top)) }; @@ -145,17 +151,26 @@ fn prepare_extrinsics_input_inner<'a, B, H, Number>( Entry::Vacant(entry) => { // ignore temporary values (values that have null value at the end of operation // AND are not in storage at the beginning of operation - if let Some(sk) = storage_key.as_ref() { - if !changes.child_storage(sk, k).map(|v| v.is_some()).unwrap_or_default() { - if !backend.exists_child_storage(sk, k).map_err(|e| format!("{}", e))? { - return Ok(map); - } + + if let Some(ct) = child_trie.as_ref() { + match changes.child_storage(ct.node_ref(), k) { + OverlayedValueResult::NotFound + | OverlayedValueResult::Deleted => { + if !backend.exists_storage(k).map_err(|e| format!("{}", e))? { + return Ok(map) + } + }, + OverlayedValueResult::Modified(_val) => (), } } else { - if !changes.storage(k).map(|v| v.is_some()).unwrap_or_default() { - if !backend.exists_storage(k).map_err(|e| format!("{}", e))? { - return Ok(map); - } + match changes.storage(k) { + OverlayedValueResult::NotFound + | OverlayedValueResult::Deleted => { + if !backend.exists_storage(k).map_err(|e| format!("{}", e))? { + return Ok(map) + } + }, + OverlayedValueResult::Modified(_val) => (), } }; @@ -329,12 +344,12 @@ fn prepare_digest_input<'a, H, Number>( #[cfg(test)] mod test { use codec::Encode; - use primitives::Blake2Hasher; + use primitives::{Blake2Hasher, map}; use primitives::storage::well_known_keys::{EXTRINSIC_INDEX}; use crate::backend::InMemory; use crate::changes_trie::{RootsStorage, Configuration, storage::InMemoryStorage}; use crate::changes_trie::build_cache::{IncompleteCacheAction, IncompleteCachedBuildData}; - use crate::overlayed_changes::{OverlayedValue, OverlayedChangeSet}; + use crate::overlayed_changes::{OverlayedValue, OverlayedChangeSet, ChildOverlayChangeSet}; use super::*; fn prepare_for_build(zero: u64) -> ( @@ -352,8 +367,12 @@ mod test { (vec![104], vec![255]), (vec![105], vec![255]), ].into_iter().collect::<::std::collections::HashMap<_, _>>().into(); - let child_trie_key1 = b"1".to_vec(); - let child_trie_key2 = b"2".to_vec(); + let child_trie_key1 = b":child_storage:default:1".to_vec(); + let child_trie_key2 = b":child_storage:default:2".to_vec(); + let child_trie1 = ChildTrie::fetch_or_new(|_| None, |_| (), &b"1"[..], || 3u128); + let child_trie2 = ChildTrie::fetch_or_new(|_| None, |_| (), &b"2"[..], || 4u128); + let keyspace1 = child_trie1.keyspace().to_vec(); + let keyspace2 = child_trie2.keyspace().to_vec(); let storage = InMemoryStorage::with_inputs(vec![ (zero + 1, vec![ InputPair::ExtrinsicIndex(ExtrinsicIndex { block: zero + 1, key: vec![100] }, vec![1, 3]), @@ -387,7 +406,7 @@ mod test { ]), (zero + 9, Vec::new()), (zero + 10, Vec::new()), (zero + 11, Vec::new()), (zero + 12, Vec::new()), (zero + 13, Vec::new()), (zero + 14, Vec::new()), (zero + 15, Vec::new()), - ], vec![(child_trie_key1.clone(), vec![ + ], vec![(child_trie1.clone(), vec![ (zero + 1, vec![ InputPair::ExtrinsicIndex(ExtrinsicIndex { block: zero + 1, key: vec![100] }, vec![1, 3]), InputPair::ExtrinsicIndex(ExtrinsicIndex { block: zero + 1, key: vec![101] }, vec![0, 2]), @@ -405,52 +424,68 @@ mod test { ]); let changes = OverlayedChanges { prospective: OverlayedChangeSet { top: vec![ - (vec![100], OverlayedValue { - value: Some(vec![200]), - extrinsics: Some(vec![0, 2].into_iter().collect()) - }), - (vec![103], OverlayedValue { - value: None, - extrinsics: Some(vec![0, 1].into_iter().collect()) - }), - ].into_iter().collect(), + (vec![100], OverlayedValue { + value: Some(vec![200]), + extrinsics: Some(vec![0, 2].into_iter().collect()) + }), + (vec![103], OverlayedValue { + value: None, + extrinsics: Some(vec![0, 1].into_iter().collect()) + }), + ].into_iter().collect(), children: vec![ - (child_trie_key1.clone(), vec![ - (vec![100], OverlayedValue { - value: Some(vec![200]), - extrinsics: Some(vec![0, 2].into_iter().collect()) - }) - ].into_iter().collect()), - (child_trie_key2, vec![ - (vec![100], OverlayedValue { - value: Some(vec![200]), - extrinsics: Some(vec![0, 2].into_iter().collect()) - }) - ].into_iter().collect()), - ].into_iter().collect() + (keyspace1.clone(), ChildOverlayChangeSet { + child_trie: child_trie1.clone(), + values: vec![ + (vec![100], OverlayedValue { + value: Some(vec![200]), + extrinsics: Some(vec![0, 2].into_iter().collect()) + }) + ].into_iter().collect() + }), + (keyspace2.clone(), ChildOverlayChangeSet { + child_trie: child_trie2, + values: vec![ + (vec![100], OverlayedValue { + value: Some(vec![200]), + extrinsics: Some(vec![0, 2].into_iter().collect()) + }) + ].into_iter().collect() + }), + ].into_iter().collect(), + pending_child: map![ + child_trie_key1.clone() => Some(keyspace1.clone()), + child_trie_key2 => Some(keyspace2) + ], }, committed: OverlayedChangeSet { top: vec![ - (EXTRINSIC_INDEX.to_vec(), OverlayedValue { - value: Some(3u32.encode()), - extrinsics: None, - }), - (vec![100], OverlayedValue { - value: Some(vec![202]), - extrinsics: Some(vec![3].into_iter().collect()) - }), - (vec![101], OverlayedValue { - value: Some(vec![203]), - extrinsics: Some(vec![1].into_iter().collect()) - }), - ].into_iter().collect(), + (EXTRINSIC_INDEX.to_vec(), OverlayedValue { + value: Some(3u32.encode()), + extrinsics: None, + }), + (vec![100], OverlayedValue { + value: Some(vec![202]), + extrinsics: Some(vec![3].into_iter().collect()) + }), + (vec![101], OverlayedValue { + value: Some(vec![203]), + extrinsics: Some(vec![1].into_iter().collect()) + }), + ].into_iter().collect(), children: vec![ - (child_trie_key1, vec![ - (vec![100], OverlayedValue { - value: Some(vec![202]), - extrinsics: Some(vec![3].into_iter().collect()) - }) - ].into_iter().collect()), + (keyspace1.clone(), ChildOverlayChangeSet { + child_trie: child_trie1, + values: vec![ + (vec![100], OverlayedValue { + value: Some(vec![202]), + extrinsics: Some(vec![3].into_iter().collect()) + }) + ].into_iter().collect() + }), ].into_iter().collect(), + pending_child: map![ + child_trie_key1 => Some(keyspace1) + ], }, changes_trie_config: Some(config.clone()), }; diff --git a/core/state-machine/src/changes_trie/changes_iterator.rs b/core/state-machine/src/changes_trie/changes_iterator.rs index e8730a1bddedb..5ce72c83bb852 100644 --- a/core/state-machine/src/changes_trie/changes_iterator.rs +++ b/core/state-machine/src/changes_trie/changes_iterator.rs @@ -376,13 +376,15 @@ impl<'a, H, Number> Iterator for ProvingDrilldownIterator<'a, H, Number> #[cfg(test)] mod tests { use std::iter::FromIterator; - use primitives::Blake2Hasher; + use primitives::{Blake2Hasher, child_trie::ChildTrie}; use crate::changes_trie::Configuration; use crate::changes_trie::input::InputPair; use crate::changes_trie::storage::InMemoryStorage; use super::*; fn prepare_for_drilldown() -> (Configuration, InMemoryStorage) { + let child_trie = ChildTrie::fetch_or_new(|_| None, |_| (), &b"1"[..], || 1u128); + let config = Configuration { digest_interval: 4, digest_levels: 2 }; let backend = InMemoryStorage::with_inputs(vec![ // digest: 1..4 => [(3, 0)] @@ -418,7 +420,7 @@ mod tests { (16, vec![ InputPair::DigestIndex(DigestIndex { block: 16, key: vec![42] }, vec![4, 8]), ]), - ], vec![(b"1".to_vec(), vec![ + ], vec![(child_trie, vec![ (1, vec![ InputPair::ExtrinsicIndex(ExtrinsicIndex { block: 1, key: vec![42] }, vec![0]), ]), diff --git a/core/state-machine/src/changes_trie/prune.rs b/core/state-machine/src/changes_trie/prune.rs index 1dc7c3e6c07b2..1efd8bef66c0e 100644 --- a/core/state-machine/src/changes_trie/prune.rs +++ b/core/state-machine/src/changes_trie/prune.rs @@ -201,7 +201,7 @@ fn max_digest_intervals_to_keep( mod tests { use std::collections::HashSet; use trie::MemoryDB; - use primitives::Blake2Hasher; + use primitives::{Blake2Hasher, child_trie::ChildTrie}; use crate::backend::insert_into_memory_db; use crate::changes_trie::storage::InMemoryStorage; use codec::Encode; @@ -229,21 +229,38 @@ mod tests { #[test] fn prune_works() { fn prepare_storage() -> InMemoryStorage { - + let child_trie = ChildTrie::fetch_or_new(|_| None, |_| (), &b"1"[..], || 1u128); + let child_key = ChildIndex { block: 67u64, storage_key: b"1".to_vec() }.encode(); let mut mdb1 = MemoryDB::::default(); - let root1 = insert_into_memory_db::(&mut mdb1, vec![(vec![10], vec![20])]).unwrap(); + let root1 = insert_into_memory_db::( + &mut mdb1, + vec![(vec![10], vec![20])], + None + ).unwrap(); let mut mdb2 = MemoryDB::::default(); - let root2 = insert_into_memory_db::(&mut mdb2, vec![(vec![11], vec![21]), (vec![12], vec![22])]).unwrap(); + let root2 = insert_into_memory_db::( + &mut mdb2, + vec![(vec![11], vec![21]), (vec![12], vec![22])], + None + ).unwrap(); let mut mdb3 = MemoryDB::::default(); - let ch_root3 = insert_into_memory_db::(&mut mdb3, vec![(vec![110], vec![120])]).unwrap(); + let ch_root3 = insert_into_memory_db::( + &mut mdb3, + vec![(vec![110], vec![120])], + Some(child_trie.node_ref()), + ).unwrap(); let root3 = insert_into_memory_db::(&mut mdb3, vec![ (vec![13], vec![23]), (vec![14], vec![24]), (child_key, ch_root3.as_ref().encode()), - ]).unwrap(); + ], None).unwrap(); let mut mdb4 = MemoryDB::::default(); - let root4 = insert_into_memory_db::(&mut mdb4, vec![(vec![15], vec![25])]).unwrap(); + let root4 = insert_into_memory_db::( + &mut mdb4, + vec![(vec![15], vec![25])], + None + ).unwrap(); let storage = InMemoryStorage::new(); storage.insert(65, root1, mdb1); storage.insert(66, root2, mdb2); diff --git a/core/state-machine/src/changes_trie/storage.rs b/core/state-machine/src/changes_trie/storage.rs index f8c556a46c61b..611faaf9cde7f 100644 --- a/core/state-machine/src/changes_trie/storage.rs +++ b/core/state-machine/src/changes_trie/storage.rs @@ -24,6 +24,9 @@ use parking_lot::RwLock; use crate::changes_trie::{BuildCache, RootsStorage, Storage, AnchorBlockId, BlockNumber}; use crate::trie_backend_essence::TrieBackendStorage; +#[cfg(test)] +use primitives::child_trie::ChildTrie; + #[cfg(test)] use crate::backend::insert_into_memory_db; #[cfg(test)] @@ -93,13 +96,17 @@ impl InMemoryStorage { #[cfg(test)] pub fn with_inputs( mut top_inputs: Vec<(Number, Vec>)>, - children_inputs: Vec<(Vec, Vec<(Number, Vec>)>)>, + children_inputs: Vec<(ChildTrie, Vec<(Number, Vec>)>)>, ) -> Self { let mut mdb = MemoryDB::default(); let mut roots = BTreeMap::new(); - for (storage_key, child_input) in children_inputs { + for (child_trie, child_input) in children_inputs { for (block, pairs) in child_input { - let root = insert_into_memory_db::(&mut mdb, pairs.into_iter().map(Into::into)); + let root = insert_into_memory_db::( + &mut mdb, + pairs.into_iter().map(Into::into), + Some(child_trie.node_ref()), + ); if let Some(root) = root { let ix = if let Some(ix) = top_inputs.iter().position(|v| v.0 == block) { @@ -109,7 +116,7 @@ impl InMemoryStorage { top_inputs.len() - 1 }; top_inputs[ix].1.push(InputPair::ChildIndex( - ChildIndex { block: block.clone(), storage_key: storage_key.clone() }, + ChildIndex { block: block.clone(), storage_key: child_trie.parent_slice().to_vec() }, root.as_ref().to_vec(), )); } @@ -117,7 +124,7 @@ impl InMemoryStorage { } for (block, pairs) in top_inputs { - let root = insert_into_memory_db::(&mut mdb, pairs.into_iter().map(Into::into)); + let root = insert_into_memory_db::(&mut mdb, pairs.into_iter().map(Into::into), None); if let Some(root) = root { roots.insert(block, root); } diff --git a/core/state-machine/src/ext.rs b/core/state-machine/src/ext.rs index fde3ba2b01953..610b7d29c9159 100644 --- a/core/state-machine/src/ext.rs +++ b/core/state-machine/src/ext.rs @@ -23,9 +23,14 @@ use crate::changes_trie::{ Storage as ChangesTrieStorage, CacheAction as ChangesTrieCacheAction, build_changes_trie, }; -use crate::{Externalities, OverlayedChanges, ChildStorageKey}; +use crate::{Externalities, OverlayedChanges, OverlayedValueResult}; use hash_db::Hasher; -use primitives::{offchain, storage::well_known_keys::is_child_storage_key, traits::BareCryptoStorePtr}; +use primitives::{ + offchain, + storage::well_known_keys::is_child_storage_key, + traits::BareCryptoStorePtr, + child_trie::{ChildTrie, ChildTrieReadRef} +}; use trie::{MemoryDB, default_child_trie_root}; use trie::trie_types::Layout; @@ -181,14 +186,22 @@ where { fn storage(&self, key: &[u8]) -> Option> { let _guard = panic_handler::AbortGuard::force_abort(); - self.overlay.storage(key).map(|x| x.map(|x| x.to_vec())).unwrap_or_else(|| - self.backend.storage(key).expect(EXT_NOT_ALLOWED_TO_FAIL)) + match self.overlay.storage(key) { + OverlayedValueResult::NotFound => + self.backend.storage(key).expect(EXT_NOT_ALLOWED_TO_FAIL), + OverlayedValueResult::Deleted => None, + OverlayedValueResult::Modified(value) => Some(value.to_vec()), + } } fn storage_hash(&self, key: &[u8]) -> Option { let _guard = panic_handler::AbortGuard::force_abort(); - self.overlay.storage(key).map(|x| x.map(|x| H::hash(x))).unwrap_or_else(|| - self.backend.storage_hash(key).expect(EXT_NOT_ALLOWED_TO_FAIL)) + match self.overlay.storage(key) { + OverlayedValueResult::NotFound => + self.backend.storage_hash(key).expect(EXT_NOT_ALLOWED_TO_FAIL), + OverlayedValueResult::Deleted => None, + OverlayedValueResult::Modified(value) => Some( H::hash(value)), + } } fn original_storage(&self, key: &[u8]) -> Option> { @@ -201,42 +214,61 @@ where self.backend.storage_hash(key).expect(EXT_NOT_ALLOWED_TO_FAIL) } - fn child_storage(&self, storage_key: ChildStorageKey, key: &[u8]) -> Option> { + fn child_trie(&self, storage_key: &[u8]) -> Option { let _guard = panic_handler::AbortGuard::force_abort(); - self.overlay.child_storage(storage_key.as_ref(), key).map(|x| x.map(|x| x.to_vec())).unwrap_or_else(|| - self.backend.child_storage(storage_key.as_ref(), key).expect(EXT_NOT_ALLOWED_TO_FAIL)) + self.overlay.child_trie(storage_key).unwrap_or_else(|| + self.backend.child_trie(storage_key).expect(EXT_NOT_ALLOWED_TO_FAIL)) } - fn child_storage_hash(&self, storage_key: ChildStorageKey, key: &[u8]) -> Option { + fn child_storage(&self, child_trie: ChildTrieReadRef, key: &[u8]) -> Option> { let _guard = panic_handler::AbortGuard::force_abort(); - self.overlay.child_storage(storage_key.as_ref(), key).map(|x| x.map(|x| H::hash(x))).unwrap_or_else(|| - self.backend.storage_hash(key).expect(EXT_NOT_ALLOWED_TO_FAIL)) + match self.overlay.child_storage(child_trie.clone(), key) { + OverlayedValueResult::NotFound => + self.backend.child_storage(child_trie, key).expect(EXT_NOT_ALLOWED_TO_FAIL), + OverlayedValueResult::Deleted => None, + OverlayedValueResult::Modified(value) => Some(value.to_vec()), + } } - fn original_child_storage(&self, storage_key: ChildStorageKey, key: &[u8]) -> Option> { + fn child_storage_hash(&self, child_trie: ChildTrieReadRef, key: &[u8]) -> Option { let _guard = panic_handler::AbortGuard::force_abort(); - self.backend.child_storage(storage_key.as_ref(), key).expect(EXT_NOT_ALLOWED_TO_FAIL) + match self.overlay.child_storage(child_trie.clone(), key) { + OverlayedValueResult::NotFound => + self.backend.child_storage(child_trie, key) + .map(|x| x.map(|x| H::hash(x.as_slice()))) + .expect(EXT_NOT_ALLOWED_TO_FAIL), + OverlayedValueResult::Deleted => None, + OverlayedValueResult::Modified(value) => Some(H::hash(value)), + } } - fn original_child_storage_hash(&self, storage_key: ChildStorageKey, key: &[u8]) -> Option { + fn original_child_storage(&self, child_trie: ChildTrieReadRef, key: &[u8]) -> Option> { let _guard = panic_handler::AbortGuard::force_abort(); - self.backend.child_storage_hash(storage_key.as_ref(), key).expect(EXT_NOT_ALLOWED_TO_FAIL) + self.backend.child_storage(child_trie, key).expect(EXT_NOT_ALLOWED_TO_FAIL) + } + + fn original_child_storage_hash(&self, child_trie: ChildTrieReadRef, key: &[u8]) -> Option { + let _guard = panic_handler::AbortGuard::force_abort(); + self.backend.child_storage_hash(child_trie, key).expect(EXT_NOT_ALLOWED_TO_FAIL) } fn exists_storage(&self, key: &[u8]) -> bool { let _guard = panic_handler::AbortGuard::force_abort(); match self.overlay.storage(key) { - Some(x) => x.is_some(), - _ => self.backend.exists_storage(key).expect(EXT_NOT_ALLOWED_TO_FAIL), + OverlayedValueResult::NotFound => + self.backend.exists_storage(key).expect(EXT_NOT_ALLOWED_TO_FAIL), + OverlayedValueResult::Deleted => false, + OverlayedValueResult::Modified(_value) => true, } } - fn exists_child_storage(&self, storage_key: ChildStorageKey, key: &[u8]) -> bool { + fn exists_child_storage(&self, child_trie: ChildTrieReadRef, key: &[u8]) -> bool { let _guard = panic_handler::AbortGuard::force_abort(); - - match self.overlay.child_storage(storage_key.as_ref(), key) { - Some(x) => x.is_some(), - _ => self.backend.exists_child_storage(storage_key.as_ref(), key).expect(EXT_NOT_ALLOWED_TO_FAIL), + match self.overlay.child_storage(child_trie.clone(), key) { + OverlayedValueResult::NotFound => + self.backend.exists_child_storage(child_trie, key).expect(EXT_NOT_ALLOWED_TO_FAIL), + OverlayedValueResult::Deleted => false, + OverlayedValueResult::Modified(_value) => true, } } @@ -251,20 +283,41 @@ where self.overlay.set_storage(key, value); } - fn place_child_storage(&mut self, storage_key: ChildStorageKey, key: Vec, value: Option>) { + fn place_child_storage(&mut self, child_trie: &ChildTrie, key: Vec, value: Option>) { let _guard = panic_handler::AbortGuard::force_abort(); self.mark_dirty(); - self.overlay.set_child_storage(storage_key.into_owned(), key, value); + self.overlay.set_child_storage(child_trie, key, value); } - fn kill_child_storage(&mut self, storage_key: ChildStorageKey) { + fn set_child_trie(&mut self, ct: ChildTrie) -> bool { + let _guard = panic_handler::AbortGuard::force_abort(); + // do check for backend, theorically this could be skip + // (`ChildTrie` being initiated from backend, this is + // still here for safety but removal can be considered + // in the future). + let ct = match self.child_trie(ct.parent_slice()) { + Some(ct_old) => if ct_old.is_updatable_with(&ct) { + ct + } else { + return false; + }, + None => if ct.is_new() { + ct + } else { + return false; + }, + }; + self.overlay.set_child_trie(ct) + } + + fn kill_child_storage(&mut self, child_trie: &ChildTrie) { let _guard = panic_handler::AbortGuard::force_abort(); self.mark_dirty(); - self.overlay.clear_child_storage(storage_key.as_ref()); - self.backend.for_keys_in_child_storage(storage_key.as_ref(), |key| { - self.overlay.set_child_storage(storage_key.as_ref().to_vec(), key.to_vec(), None); + self.overlay.clear_child_storage(child_trie); + self.backend.for_keys_in_child_storage(child_trie.node_ref(), |key| { + self.overlay.set_child_storage(child_trie, key.to_vec(), None); }); } @@ -282,13 +335,13 @@ where }); } - fn clear_child_prefix(&mut self, storage_key: ChildStorageKey, prefix: &[u8]) { + fn clear_child_prefix(&mut self, child_trie: &ChildTrie, prefix: &[u8]) { let _guard = panic_handler::AbortGuard::force_abort(); self.mark_dirty(); - self.overlay.clear_child_prefix(storage_key.as_ref(), prefix); - self.backend.for_child_keys_with_prefix(storage_key.as_ref(), prefix, |key| { - self.overlay.set_child_storage(storage_key.as_ref().to_vec(), key.to_vec(), None); + self.overlay.clear_child_prefix(child_trie, prefix); + self.backend.for_child_keys_with_prefix(child_trie.node_ref(), prefix, |key| { + self.overlay.set_child_storage(child_trie, key.to_vec(), None); }); } @@ -302,17 +355,22 @@ where return root.clone(); } - let child_storage_keys = - self.overlay.prospective.children.keys() - .chain(self.overlay.committed.children.keys()); - let child_delta_iter = child_storage_keys.map(|storage_key| - (storage_key.clone(), self.overlay.committed.children.get(storage_key) - .into_iter() - .flat_map(|map| map.iter().map(|(k, v)| (k.clone(), v.value.clone()))) - .chain(self.overlay.prospective.children.get(storage_key) - .into_iter() - .flat_map(|map| map.iter().map(|(k, v)| (k.clone(), v.value.clone())))))); - + let child_storage_tries = + self.overlay.prospective.children.values() + .chain(self.overlay.committed.children.values()) + .map(|v|&v.child_trie); + + let child_delta_iter = child_storage_tries.map(|child_trie| { + let keyspace = child_trie.keyspace(); + let committed_iter = self.overlay.committed.children + .get(keyspace).into_iter() + .flat_map(|map| map.values.iter().map(|(k, v)| (k.clone(), v.value.clone()))); + let prospective_iter = self.overlay.prospective.children + .get(keyspace).into_iter() + .flat_map(|map| map.values.iter().map(|(k, v)| (k.clone(), v.value.clone()))); + + (child_trie, committed_iter.chain(prospective_iter)) + }); // compute and memoize let delta = self.overlay.committed.top.iter().map(|(k, v)| (k.clone(), v.value.clone())) @@ -323,30 +381,34 @@ where root } - fn child_storage_root(&mut self, storage_key: ChildStorageKey) -> Vec { + fn child_storage_root(&mut self, child_trie: &ChildTrie) -> Vec { let _guard = panic_handler::AbortGuard::force_abort(); + if self.storage_transaction.is_some() { - self - .storage(storage_key.as_ref()) - .unwrap_or( - default_child_trie_root::>(storage_key.as_ref()) - ) + self.child_trie(child_trie.parent_slice()) + .and_then(|child_trie| child_trie.root_initial_value().clone()) + .unwrap_or(default_child_trie_root::>()) } else { - let storage_key = storage_key.as_ref(); - - let delta = self.overlay.committed.children.get(storage_key) + let keyspace = child_trie.keyspace(); + let delta = self.overlay.committed.children.get(keyspace) .into_iter() - .flat_map(|map| map.iter().map(|(k, v)| (k.clone(), v.value.clone()))) - .chain(self.overlay.prospective.children.get(storage_key) - .into_iter() - .flat_map(|map| map.clone().into_iter().map(|(k, v)| (k.clone(), v.value.clone())))); - - let root = self.backend.child_storage_root(storage_key, delta).0; - - self.overlay.set_storage(storage_key.to_vec(), Some(root.to_vec())); + .flat_map(|map| map.values.iter().map(|(k, v)| (k.clone(), v.value.clone()))) + .chain(self.overlay.prospective.children.get(keyspace) + .into_iter() + .flat_map(|map| { + map.values.clone().into_iter().map(|(k, v)| (k.clone(), v.value.clone())) + })); + let (root, is_empty, _) = self.backend.child_storage_root(child_trie, delta); + if is_empty { + self.overlay.set_storage(child_trie.parent_trie().clone(), None); + } else { + self.overlay.set_storage( + child_trie.parent_trie().clone(), + Some(child_trie.encoded_with_root(&root[..])), + ); + } root - } } diff --git a/core/state-machine/src/lib.rs b/core/state-machine/src/lib.rs index 70a7f3b1462b0..36b73d696f186 100644 --- a/core/state-machine/src/lib.rs +++ b/core/state-machine/src/lib.rs @@ -19,12 +19,12 @@ #![warn(missing_docs)] use std::{fmt, panic::UnwindSafe, result, marker::PhantomData}; -use std::borrow::Cow; use log::warn; use hash_db::Hasher; use codec::{Decode, Encode}; use primitives::{ storage::well_known_keys, NativeOrEncoded, NeverNativeValue, offchain, + child_trie::{ChildTrie, ChildTrieReadRef}, traits::BareCryptoStorePtr, }; @@ -38,8 +38,8 @@ mod proving_backend; mod trie_backend; mod trie_backend_essence; -use overlayed_changes::OverlayedChangeSet; -pub use trie::{TrieMut, DBValue, MemoryDB}; +use overlayed_changes::{OverlayedChangeSet, OverlayedValueResult}; +pub use trie::{TrieMut, DBValue, MemoryDB, Recorder as ProofRecorder}; pub use trie::trie_types::{Layout, TrieDBMut}; pub use testing::TestExternalities; pub use basic::BasicExternalities; @@ -60,7 +60,7 @@ pub use changes_trie::{ pub use overlayed_changes::OverlayedChanges; pub use proving_backend::{ create_proof_check_backend, create_proof_check_backend_storage, - Recorder as ProofRecorder, ProvingBackend, + ProvingBackend, }; pub use trie_backend_essence::{TrieBackendStorage, Storage}; pub use trie_backend::TrieBackend; @@ -71,58 +71,6 @@ pub type ChangesTrieTransaction = ( ChangesTrieCacheAction<::Out, N>, ); -/// A wrapper around a child storage key. -/// -/// This wrapper ensures that the child storage key is correct and properly used. It is -/// impossible to create an instance of this struct without providing a correct `storage_key`. -pub struct ChildStorageKey<'a, H: Hasher> { - storage_key: Cow<'a, [u8]>, - _hasher: PhantomData, -} - -impl<'a, H: Hasher> ChildStorageKey<'a, H> { - fn new(storage_key: Cow<'a, [u8]>) -> Option { - if !trie::is_child_trie_key_valid::>(&storage_key) { - return None; - } - - Some(ChildStorageKey { - storage_key, - _hasher: PhantomData, - }) - } - - /// Create a new `ChildStorageKey` from a vector. - /// - /// `storage_key` has should start with `:child_storage:default:` - /// See `is_child_trie_key_valid` for more details. - pub fn from_vec(key: Vec) -> Option { - Self::new(Cow::Owned(key)) - } - - /// Create a new `ChildStorageKey` from a slice. - /// - /// `storage_key` has should start with `:child_storage:default:` - /// See `is_child_trie_key_valid` for more details. - pub fn from_slice(key: &'a [u8]) -> Option { - Self::new(Cow::Borrowed(key)) - } - - /// Get access to the byte representation of the storage key. - /// - /// This key is guaranteed to be correct. - pub fn as_ref(&self) -> &[u8] { - &*self.storage_key - } - - /// Destruct this instance into an owned vector that represents the storage key. - /// - /// This key is guaranteed to be correct. - pub fn into_owned(self) -> Vec { - self.storage_key.into_owned() - } -} - /// State Machine Error bound. /// /// This should reflect WASM error type bound for future compatibility. @@ -164,15 +112,15 @@ pub trait Externalities { } /// Get child storage value hash. This may be optimized for large values. - fn child_storage_hash(&self, storage_key: ChildStorageKey, key: &[u8]) -> Option { - self.child_storage(storage_key, key).map(|v| H::hash(&v)) + fn child_storage_hash(&self, child_trie: ChildTrieReadRef, key: &[u8]) -> Option { + self.child_storage(child_trie, key).map(|v| H::hash(&v)) } /// Read original runtime storage, ignoring any overlayed changes. fn original_storage(&self, key: &[u8]) -> Option>; /// Read original runtime child storage, ignoring any overlayed changes. - fn original_child_storage(&self, storage_key: ChildStorageKey, key: &[u8]) -> Option>; + fn original_child_storage(&self, child_trie: ChildTrieReadRef, key: &[u8]) -> Option>; /// Get original storage value hash, ignoring any overlayed changes. /// This may be optimized for large values. @@ -184,23 +132,30 @@ pub trait Externalities { /// This may be optimized for large values. fn original_child_storage_hash( &self, - storage_key: ChildStorageKey, + child_trie: ChildTrieReadRef, key: &[u8], ) -> Option { - self.original_child_storage(storage_key, key).map(|v| H::hash(&v)) + self.original_child_storage(child_trie, key).map(|v| H::hash(&v)) } /// Read child runtime storage. - fn child_storage(&self, storage_key: ChildStorageKey, key: &[u8]) -> Option>; + fn child_storage(&self, child_trie: ChildTrieReadRef, key: &[u8]) -> Option>; + + /// Get child trie infos at 'storage_key'. + fn child_trie(&self, storage_key: &[u8]) -> Option; /// Set storage entry `key` of current contract being called (effective immediately). fn set_storage(&mut self, key: Vec, value: Vec) { self.place_storage(key, Some(value)); } + /// Set child trie infos, can fail if there is an attempt to change a + /// non empty child root directly. + fn set_child_trie(&mut self, ct: ChildTrie) -> bool; + /// Set child storage entry `key` of current contract being called (effective immediately). - fn set_child_storage(&mut self, storage_key: ChildStorageKey, key: Vec, value: Vec) { - self.place_child_storage(storage_key, key, Some(value)) + fn set_child_storage(&mut self, child_trie: &ChildTrie, key: Vec, value: Vec) { + self.place_child_storage(child_trie, key, Some(value)) } /// Clear a storage entry (`key`) of current contract being called (effective immediately). @@ -209,8 +164,8 @@ pub trait Externalities { } /// Clear a child storage entry (`key`) of current contract being called (effective immediately). - fn clear_child_storage(&mut self, storage_key: ChildStorageKey, key: &[u8]) { - self.place_child_storage(storage_key, key.to_vec(), None) + fn clear_child_storage(&mut self, child_trie: &ChildTrie, key: &[u8]) { + self.place_child_storage(child_trie, key.to_vec(), None) } /// Whether a storage entry exists. @@ -219,24 +174,24 @@ pub trait Externalities { } /// Whether a child storage entry exists. - fn exists_child_storage(&self, storage_key: ChildStorageKey, key: &[u8]) -> bool { - self.child_storage(storage_key, key).is_some() + fn exists_child_storage(&self, child_trie: ChildTrieReadRef, key: &[u8]) -> bool { + self.child_storage(child_trie, key).is_some() } - /// Clear an entire child storage. - fn kill_child_storage(&mut self, storage_key: ChildStorageKey); + /// Clear an entire child storage and update parent. + fn kill_child_storage(&mut self, child_trie: &ChildTrie); /// Clear storage entries which keys are start with the given prefix. fn clear_prefix(&mut self, prefix: &[u8]); /// Clear child storage entries which keys are start with the given prefix. - fn clear_child_prefix(&mut self, storage_key: ChildStorageKey, prefix: &[u8]); + fn clear_child_prefix(&mut self, child_trie: &ChildTrie, prefix: &[u8]); /// Set or clear a storage entry (`key`) of current contract being called (effective immediately). fn place_storage(&mut self, key: Vec, value: Option>); /// Set or clear a child storage entry. Return whether the operation succeeds. - fn place_child_storage(&mut self, storage_key: ChildStorageKey, key: Vec, value: Option>); + fn place_child_storage(&mut self, child_trie: &ChildTrie, key: Vec, value: Option>); /// Get the identity of the chain. fn chain_id(&self) -> u64; @@ -248,7 +203,7 @@ pub trait Externalities { /// storage keys in the top-level storage map. /// If the storage root equals the default hash as defined by the trie, the key in the top-level /// storage map will be removed. - fn child_storage_root(&mut self, storage_key: ChildStorageKey) -> Vec; + fn child_storage_root(&mut self, child_trie: &ChildTrie) -> Vec; /// Get the change trie root of the current storage overlay at a block with given parent. fn storage_changes_root(&mut self, parent: H::Out) -> Result, ()> where H::Out: Ord; @@ -830,8 +785,8 @@ where /// Generate child storage read proof. pub fn prove_child_read( mut backend: B, - storage_key: &[u8], - key: &[u8], + child_trie: ChildTrieReadRef, + key: &[u8] ) -> Result<(Option>, Vec>), Box> where B: Backend, @@ -840,7 +795,7 @@ where { let trie_backend = backend.as_trie_backend() .ok_or_else(|| Box::new(ExecutionError::UnableToGenerateProof) as Box)?; - prove_child_read_on_trie_backend(trie_backend, storage_key, key) + prove_child_read_on_trie_backend(&trie_backend, child_trie, key) } @@ -859,10 +814,10 @@ where Ok((result, proving_backend.extract_proof())) } -/// Generate storage read proof on pre-created trie backend. +/// Generate child storage read proof on pre-created trie backend. pub fn prove_child_read_on_trie_backend( trie_backend: &TrieBackend, - storage_key: &[u8], + child_trie: ChildTrieReadRef, key: &[u8] ) -> Result<(Option>, Vec>), Box> where @@ -871,7 +826,8 @@ where H::Out: Ord { let proving_backend = proving_backend::ProvingBackend::<_, H>::new(trie_backend); - let result = proving_backend.child_storage(storage_key, key).map_err(|e| Box::new(e) as Box)?; + let result = proving_backend.child_storage(child_trie, key) + .map_err(|e| Box::new(e) as Box)?; Ok((result, proving_backend.extract_proof())) } @@ -889,22 +845,30 @@ where read_proof_check_on_proving_backend(&proving_backend, key) } -/// Check child storage read proof, generated by `prove_child_read` call. +/// Check child storage read proof, generated by `prove_read` call. pub fn read_child_proof_check( - root: H::Out, proof: Vec>, - storage_key: &[u8], + child_trie: ChildTrieReadRef, key: &[u8], ) -> Result>, Box> where H: Hasher, H::Out: Ord { - let proving_backend = create_proof_check_backend::(root, proof)?; - read_child_proof_check_on_proving_backend(&proving_backend, storage_key, key) + // no need to use keyspace with proof + if let ChildTrieReadRef::Existing(trie_root, _keyspace) = child_trie { + let mut root = H::Out::default(); + // root is fetched from DB, not writable by runtime, so it's always valid. + root.as_mut().copy_from_slice(trie_root.as_ref()); + + let root = root; + let proving_backend = proving_backend::create_proof_check_backend::(root, proof)?; + read_child_proof_check_on_proving_backend(&proving_backend, child_trie, key) + } else { + Ok(None) + } } - /// Check storage read proof on pre-created proving backend. pub fn read_proof_check_on_proving_backend( proving_backend: &TrieBackend, H>, @@ -920,14 +884,14 @@ where /// Check child storage read proof on pre-created proving backend. pub fn read_child_proof_check_on_proving_backend( proving_backend: &TrieBackend, H>, - storage_key: &[u8], + child_trie: ChildTrieReadRef, key: &[u8], ) -> Result>, Box> where H: Hasher, H::Out: Ord { - proving_backend.child_storage(storage_key, key).map_err(|e| Box::new(e) as Box) + proving_backend.child_storage(child_trie, key).map_err(|e| Box::new(e) as Box) } /// Sets overlayed changes' changes trie configuration. Returns error if configuration @@ -962,18 +926,18 @@ where H: Hasher, B: Backend, { - match overlay.storage(key).map(|x| x.map(|x| x.to_vec())) { - Some(value) => Ok(value), - None => backend - .storage(key) + match overlay.storage(key) { + OverlayedValueResult::NotFound => backend.storage(key) .map_err(|err| Box::new(ExecutionError::Backend(format!("{}", err))) as Box), + OverlayedValueResult::Deleted => Ok(None), + OverlayedValueResult::Modified(value) => Ok(Some(value.to_vec())), } } #[cfg(test)] mod tests { use std::collections::HashMap; - use codec::Encode; + use codec::{Encode, Decode}; use overlayed_changes::OverlayedValue; use super::*; use super::backend::InMemory; @@ -1142,6 +1106,25 @@ mod tests { // check that both results are correct assert_eq!(remote_result, vec![66]); assert_eq!(remote_result, local_result); + + // on child trie + let remote_backend = trie_backend::tests::test_trie(); + // Note that proof of get_child_trie should use standard child proof + let child_trie1 = remote_backend.child_trie(b"sub1").unwrap().unwrap(); + let _remote_root = remote_backend.storage_root(::std::iter::empty()).0; + let (_v, remote_proof) = prove_child_read(remote_backend, child_trie1.node_ref(), b"value3").unwrap(); + let local_result1 = read_child_proof_check::( + remote_proof.clone(), + child_trie1.node_ref(), + b"value3" + ).unwrap(); + let local_result2 = read_child_proof_check::( + remote_proof.clone(), + child_trie1.node_ref(), + b"value2" + ).unwrap(); + assert_eq!(local_result1, Some(vec![142])); + assert_eq!(local_result2, None); } #[test] @@ -1207,28 +1190,17 @@ mod tests { None, ); - ext.set_child_storage( - ChildStorageKey::from_slice(b":child_storage:default:testchild").unwrap(), - b"abc".to_vec(), - b"def".to_vec() - ); - assert_eq!( - ext.child_storage( - ChildStorageKey::from_slice(b":child_storage:default:testchild").unwrap(), - b"abc" - ), - Some(b"def".to_vec()) - ); - ext.kill_child_storage( - ChildStorageKey::from_slice(b":child_storage:default:testchild").unwrap() - ); - assert_eq!( - ext.child_storage( - ChildStorageKey::from_slice(b":child_storage:default:testchild").unwrap(), - b"abc" - ), - None + assert_eq!(ext.child_trie(&b"testchild"[..]), None); + let child_trie = ChildTrie::fetch_or_new( + |_| None, + |_| (), + b"testchild", + || 1u128, // child trie counter ); + ext.set_child_storage(&child_trie, b"abc".to_vec(), b"def".to_vec()); + assert_eq!(ext.child_storage(child_trie.node_ref(), b"abc"), Some(b"def".to_vec())); + ext.kill_child_storage(&child_trie); + assert_eq!(ext.child_storage(child_trie.node_ref(), b"abc"), None); } #[test] @@ -1248,26 +1220,43 @@ mod tests { remote_proof.clone(), &[0xff] ).is_ok(); - // check that results are correct + // check that results are correct assert_eq!(local_result1, Some(vec![24])); assert_eq!(local_result2, false); - // on child trie + let remote_backend = trie_backend::tests::test_trie(); let remote_root = remote_backend.storage_root(::std::iter::empty()).0; + let pr_sub1 = ChildTrie::prefix_parent_key(b"sub1"); + let remote_proof = prove_read( + remote_backend, + &pr_sub1, + ).unwrap().1; + let local_result1 = read_proof_check::( + remote_root, + remote_proof.clone(), + &pr_sub1, + ).unwrap(); + + let child_trie1: ChildTrie = ChildTrie::decode_node_with_parent( + &local_result1.unwrap()[..], + b"value2".to_vec(), + ).unwrap(); + + // on child trie + let remote_backend = trie_backend::tests::test_trie(); let remote_proof = prove_child_read( remote_backend, - b":child_storage:default:sub1", + child_trie1.node_ref(), b"value3" ).unwrap().1; let local_result1 = read_child_proof_check::( - remote_root, remote_proof.clone(), - b":child_storage:default:sub1",b"value3" + child_trie1.node_ref(), + b"value3" ).unwrap(); let local_result2 = read_child_proof_check::( - remote_root, remote_proof.clone(), - b":child_storage:default:sub1", + child_trie1.node_ref(), b"value2" ).unwrap(); assert_eq!(local_result1, Some(vec![142])); @@ -1319,4 +1308,133 @@ mod tests { .is_err() ); } + + #[test] + fn child_storage_keyspace() { + use crate::trie_backend::tests::test_trie; + use std::collections::HashSet; + + let child_trie1 = ChildTrie::fetch_or_new(|_| None, |_| (), &[0x01], || 1u128); + let child_trie2 = ChildTrie::fetch_or_new(|_| None, |_| (), &[0x23], || 2u128); + let mut tr1 = { + let mut ttrie = test_trie(); + let backend = ttrie.as_trie_backend().unwrap(); + let changes_trie_storage = InMemoryChangesTrieStorage::<_, u64>::new(); + let mut overlay = OverlayedChanges::default(); + let mut ext = Ext::new( + &mut overlay, + backend, + Some(&changes_trie_storage), + NeverOffchainExt::new(), + None, + ); + ext.set_child_storage(&child_trie1, b"abc".to_vec(), b"def".to_vec()); + ext.storage_root(); + ext.transaction().0 + }; + let mut tr2 = { + let mut ttrie = test_trie(); + let backend = ttrie.as_trie_backend().unwrap(); + let changes_trie_storage = InMemoryChangesTrieStorage::<_, u64>::new(); + let mut overlay = OverlayedChanges::default(); + let mut ext = Ext::new( + &mut overlay, + backend, + Some(&changes_trie_storage), + NeverOffchainExt::new(), + None, + ); + ext.set_child_storage(&child_trie2, b"abc".to_vec(), b"def".to_vec()); + ext.storage_root(); + ext.transaction().0 + }; + + let mut set1 = HashSet::new(); + tr1.0.drain().into_iter().for_each(|(i, (_,rc))| if rc == -1i32 { + set1.remove(&i); + } else { + set1.insert(i); + }); + let mut set2 = HashSet::new(); + tr2.0.drain().into_iter().for_each(|(i, (_,rc))| if rc == -1i32 { + set2.remove(&i); + } else { + set2.insert(i); + }); + assert!(set1.len() == set2.len()); + assert!(set1.len() != 0); + let mut nb_id = 0; + for k in set1.iter() { + if set2.contains(k) { + nb_id += 1; + } + } + // this is the test_trie existing child trie being switch + // from a leaf node to a shorter leaf node due to addition + // of a branch at child trie prefix. If a child trie is added + // to the test this is likely to need to switch to 0 (can depend + // on child ix) + assert_eq!(nb_id, 1); + } + + #[test] + fn storage_same_branch_keyspace() { + use crate::trie_backend::tests::test_trie; + use std::collections::HashSet; + let mut tr1 = { + let mut ttrie = test_trie(); + let backend = ttrie.as_trie_backend().unwrap(); + let changes_trie_storage = InMemoryChangesTrieStorage::<_, u64>::new(); + let mut overlay = OverlayedChanges::default(); + let mut ext = Ext::new( + &mut overlay, + backend, + Some(&changes_trie_storage), + NeverOffchainExt::new(), + None, + ); + ext.set_storage(b"branch".to_vec(), [40;42].to_vec()); + ext.set_storage(b"branch1".to_vec(), [42;42].to_vec()); + ext.storage_root(); + ext.transaction().0 + }; + let mut tr2 = { + let mut ttrie = test_trie(); + let backend = ttrie.as_trie_backend().unwrap(); + let changes_trie_storage = InMemoryChangesTrieStorage::<_, u64>::new(); + let mut overlay = OverlayedChanges::default(); + let mut ext = Ext::new( + &mut overlay, + backend, + Some(&changes_trie_storage), + NeverOffchainExt::new(), + None, + ); + ext.set_storage(b"Branch".to_vec(), [40;42].to_vec()); + ext.set_storage(b"Branch1".to_vec(), [42;42].to_vec()); + ext.storage_root(); + ext.transaction().0 + }; + let mut set1 = HashSet::new(); + tr1.0.drain().into_iter().for_each(|(i, (_,rc))| if rc == -1i32 { + set1.remove(&i); + } else { + set1.insert(i); + }); + let mut set2 = HashSet::new(); + tr2.0.drain().into_iter().for_each(|(i, (_,rc))| if rc == -1i32 { + set2.remove(&i); + } else { + set2.insert(i); + }); + assert!(set1.len() != 0); + assert!(set2.len() != 0); + + // Assert there is no duplicated new key (removal is fine). + for k in set1.iter() { + assert!(!set2.contains(k)); + } + } + + } diff --git a/core/state-machine/src/overlayed_changes.rs b/core/state-machine/src/overlayed_changes.rs index 1dbf2a4352e1e..f0c134d31a1fb 100644 --- a/core/state-machine/src/overlayed_changes.rs +++ b/core/state-machine/src/overlayed_changes.rs @@ -21,6 +21,7 @@ use std::collections::{HashMap, BTreeSet}; use codec::Decode; use crate::changes_trie::{NO_EXTRINSIC_INDEX, Configuration as ChangesTrieConfig}; use primitives::storage::well_known_keys::EXTRINSIC_INDEX; +use primitives::child_trie::{KeySpace, ChildTrie, ChildTrieReadRef}; /// The overlayed changes to state to be queried on top of the backend. /// @@ -48,6 +49,16 @@ pub struct OverlayedValue { pub extrinsics: Option>, } +/// All changes related to a child trie. +#[derive(Debug, Clone)] +#[cfg_attr(test, derive(PartialEq))] +pub struct ChildOverlayChangeSet { + /// Mapping of key with optional value, if value is `None` that is a removal. + pub values: HashMap, OverlayedValue>, + /// Child trie value. + pub child_trie: ChildTrie, +} + /// Prospective or committed overlayed change set. #[derive(Debug, Default, Clone)] #[cfg_attr(test, derive(PartialEq))] @@ -55,7 +66,10 @@ pub struct OverlayedChangeSet { /// Top level storage changes. pub top: HashMap, OverlayedValue>, /// Child storage changes. - pub children: HashMap, HashMap, OverlayedValue>>, + pub children: HashMap, + /// Association from parent storage location to keyspace. + /// If value is none the child is moved or deleted. + pub pending_child: HashMap, Option>, } #[cfg(test)] @@ -64,6 +78,7 @@ impl FromIterator<(Vec, OverlayedValue)> for OverlayedChangeSet { Self { top: iter.into_iter().collect(), children: Default::default(), + pending_child: Default::default(), } } } @@ -78,9 +93,24 @@ impl OverlayedChangeSet { pub fn clear(&mut self) { self.top.clear(); self.children.clear(); + self.pending_child.clear(); } } +#[derive(Debug, Clone)] +#[cfg_attr(test, derive(PartialEq))] +/// Possible result from value +/// query in the overlay. +pub enum OverlayedValueResult<'a> { + /// The key is unknown (i.e. and the query should be refered + /// to the backend) + NotFound, + /// The key has been deleted. + Deleted, + /// Current value set in the overlay. + Modified(&'a[u8]), +} + impl OverlayedChanges { /// Whether the overlayed changes are empty. pub fn is_empty(&self) -> bool { @@ -103,34 +133,66 @@ impl OverlayedChanges { true } - /// Returns a double-Option: None if the key is unknown (i.e. and the query should be refered - /// to the backend); Some(None) if the key has been deleted. Some(Some(...)) for a key whose - /// value has been set. - pub fn storage(&self, key: &[u8]) -> Option> { - self.prospective.top.get(key) - .or_else(|| self.committed.top.get(key)) - .map(|x| x.value.as_ref().map(AsRef::as_ref)) + /// Get the `OverlayedValueResult` for a given key. + pub fn storage(&self, key: &[u8]) -> OverlayedValueResult { + match self.prospective.top.get(key).or_else(|| self.committed.top.get(key)) { + Some(OverlayedValue { value: Some(val), .. }) => OverlayedValueResult::Modified(val.as_ref()), + Some(OverlayedValue { value: None, .. }) => OverlayedValueResult::Deleted, + None => OverlayedValueResult::NotFound, + } } - /// Returns a double-Option: None if the key is unknown (i.e. and the query should be refered - /// to the backend); Some(None) if the key has been deleted. Some(Some(...)) for a key whose - /// value has been set. - pub fn child_storage(&self, storage_key: &[u8], key: &[u8]) -> Option> { - if let Some(map) = self.prospective.children.get(storage_key) { - if let Some(val) = map.get(key) { - return Some(val.value.as_ref().map(AsRef::as_ref)); - } + /// Get the `OverlayedValueResult` for a given child key. + pub fn child_storage(&self, child_trie: ChildTrieReadRef, key: &[u8]) -> OverlayedValueResult { + let keyspace = child_trie.keyspace(); + match self.prospective.children.get(keyspace) + .and_then(|map| map.values.get(key).map(|v| &v.value)) { + Some(Some(val)) => + return OverlayedValueResult::Modified(val.as_ref()), + Some(None) => + return OverlayedValueResult::Deleted, + None => (), } - if let Some(map) = self.committed.children.get(storage_key) { - if let Some(val) = map.get(key) { - return Some(val.value.as_ref().map(AsRef::as_ref)); - } + match self.committed.children.get(keyspace) + .and_then(|map| map.values.get(key).map(|v| &v.value)) { + Some(Some(val)) => + return OverlayedValueResult::Modified(val.as_ref()), + Some(None) => + return OverlayedValueResult::Deleted, + None => OverlayedValueResult::NotFound, + } + + } + + /// returns a child trie if present + pub fn child_trie(&self, storage_key: &[u8]) -> Option> { + + match self.prospective.pending_child.get(storage_key) { + Some(Some(keyspace)) => { + let map = self.prospective.children.get(keyspace) + .expect("pending entry always have a children association; qed"); + return Some(Some(map.child_trie.clone())); + }, + Some(None) => return Some(None), + None => (), + } + + match self.committed.pending_child.get(storage_key) { + Some(Some(keyspace)) => { + let map = self.committed.children.get(keyspace) + .expect("pending entry always have a children association; qed"); + return Some(Some(map.child_trie.clone())); + }, + Some(None) => return Some(None), + None => (), } None } + + /// Inserts the given key-value pair into the prospective change set. /// /// `None` can be used to delete a value specified by the given key. @@ -148,10 +210,20 @@ impl OverlayedChanges { /// Inserts the given key-value pair into the prospective child change set. /// /// `None` can be used to delete a value specified by the given key. - pub(crate) fn set_child_storage(&mut self, storage_key: Vec, key: Vec, val: Option>) { + pub(crate) fn set_child_storage(&mut self, child_trie: &ChildTrie, key: Vec, val: Option>) { let extrinsic_index = self.extrinsic_index(); - let map_entry = self.prospective.children.entry(storage_key).or_default(); - let entry = map_entry.entry(key).or_default(); + let p = &mut self.prospective.children; + let pc = &mut self.prospective.pending_child; + let map_entry = p.entry(child_trie.keyspace().clone()) + .or_insert_with(|| { + let parent = child_trie.parent_slice().to_vec(); + pc.insert(parent, Some(child_trie.keyspace().clone())); + ChildOverlayChangeSet { + values: Default::default(), + child_trie: child_trie.clone(), + } + }); + let entry = map_entry.values.entry(key).or_default(); entry.value = val; if let Some(extrinsic) = extrinsic_index { @@ -160,29 +232,72 @@ impl OverlayedChanges { } } + /// Try to update child trie. Some changes are not allowed: + /// - keyspace + /// - root + /// - parent path + /// TODO EMCH merge removed the extrinsic local to a child trie + /// it should update the storage_key value extrinsics (parent). + pub(crate) fn set_child_trie(&mut self, child_trie: ChildTrie) -> bool { + if let Some(Some(old_ct)) = self.prospective.pending_child + .get(child_trie.parent_slice()) { + let old_ct = self.prospective.children.get_mut(old_ct) + .expect("pending entry always have a children association; qed"); + let old_ct = &mut old_ct.child_trie; + if old_ct.is_updatable_with(&child_trie) { + *old_ct = child_trie; + } else { + return false; + } + } else { + if let Some(old_ct) = self.committed.pending_child + .get(child_trie.parent_slice()).and_then(|k| + k.as_ref().and_then(|k| self.committed.children.get(k))) { + if old_ct.child_trie.root_initial_value() != child_trie.root_initial_value() + || old_ct.child_trie.keyspace() != child_trie.keyspace() + || old_ct.child_trie.parent_slice() != child_trie.parent_slice() { + return false; + } + } + self.prospective.pending_child + .insert(child_trie.parent_slice().to_vec(), Some(child_trie.keyspace().clone())); + self.prospective.children.insert( + child_trie.keyspace().to_vec(), + ChildOverlayChangeSet { + values: Default::default(), + child_trie: child_trie.clone(), + } + ); + } + true + } + /// Clear child storage of given storage key. /// /// NOTE that this doesn't take place immediately but written into the prospective /// change set, and still can be reverted by [`discard_prospective`]. /// /// [`discard_prospective`]: #method.discard_prospective - pub(crate) fn clear_child_storage(&mut self, storage_key: &[u8]) { + pub(crate) fn clear_child_storage(&mut self, child_trie: &ChildTrie) { let extrinsic_index = self.extrinsic_index(); - let map_entry = self.prospective.children.entry(storage_key.to_vec()).or_default(); + let map_entry = self.prospective.children.entry(child_trie.keyspace().clone()) + .or_insert_with(|| ChildOverlayChangeSet { + values: Default::default(), + child_trie: child_trie.clone(), + }); - map_entry.values_mut().for_each(|e| { + map_entry.values.values_mut().for_each(|e| { if let Some(extrinsic) = extrinsic_index { e.extrinsics.get_or_insert_with(Default::default) .insert(extrinsic); } - e.value = None; }); - if let Some(committed_map) = self.committed.children.get(storage_key) { - for (key, value) in committed_map.iter() { - if !map_entry.contains_key(key) { - map_entry.insert(key.clone(), OverlayedValue { + if let Some(child_set) = self.committed.children.get(child_trie.keyspace()) { + for (key, value) in child_set.values.iter() { + if !map_entry.values.contains_key(key) { + map_entry.values.insert(key.clone(), OverlayedValue { value: None, extrinsics: extrinsic_index.map(|i| { let mut e = value.extrinsics.clone() @@ -233,11 +348,16 @@ impl OverlayedChanges { } } - pub(crate) fn clear_child_prefix(&mut self, storage_key: &[u8], prefix: &[u8]) { + pub(crate) fn clear_child_prefix(&mut self, child_trie: &ChildTrie, prefix: &[u8]) { let extrinsic_index = self.extrinsic_index(); - let map_entry = self.prospective.children.entry(storage_key.to_vec()).or_default(); + let map_entry = self.prospective.children.entry(child_trie.keyspace().to_vec()) + .or_insert_with(|| ChildOverlayChangeSet { + values: Default::default(), + child_trie: child_trie.clone(), + }); + - for (key, entry) in map_entry.iter_mut() { + for (key, entry) in map_entry.values.iter_mut() { if key.starts_with(prefix) { entry.value = None; @@ -248,12 +368,12 @@ impl OverlayedChanges { } } - if let Some(child_committed) = self.committed.children.get(storage_key) { + if let Some(child_committed) = self.committed.children.get(child_trie.keyspace()) { // Then do the same with keys from commited changes. // NOTE that we are making changes in the prospective change set. - for key in child_committed.keys() { + for key in child_committed.values.keys() { if key.starts_with(prefix) { - let entry = map_entry.entry(key.clone()).or_default(); + let entry = map_entry.values.entry(key.clone()).or_default(); entry.value = None; if let Some(extrinsic) = extrinsic_index { @@ -284,10 +404,14 @@ impl OverlayedChanges { .extend(prospective_extrinsics); } } - for (storage_key, mut map) in self.prospective.children.drain() { - let map_dest = self.committed.children.entry(storage_key).or_default(); - for (key, val) in map.drain() { - let entry = map_dest.entry(key).or_default(); + for (keyspace, ChildOverlayChangeSet {mut values, child_trie}) in self.prospective.children.drain() { + let child_set = self.committed.children.entry(keyspace) + .or_insert_with(|| ChildOverlayChangeSet { + values: Default::default(), + child_trie: child_trie.clone(), + }); + for (key, val) in values.drain() { + let entry = child_set.values.entry(key).or_default(); entry.value = val.value; if let Some(prospective_extrinsics) = val.extrinsics { @@ -296,6 +420,7 @@ impl OverlayedChanges { } } } + self.committed.pending_child.extend(self.prospective.pending_child.drain()); } } @@ -310,7 +435,7 @@ impl OverlayedChanges { assert!(self.prospective.is_empty()); (self.committed.top.into_iter().map(|(k, v)| (k, v.value)), self.committed.children.into_iter() - .map(|(sk, v)| (sk, v.into_iter().map(|(k, v)| (k, v.value))))) + .map(|(ks, v)| (ks, v.values.into_iter().map(|(k, v)| (k, v.value))))) } /// Inserts storage entry responsible for current extrinsic index. @@ -332,9 +457,13 @@ impl OverlayedChanges { fn extrinsic_index(&self) -> Option { match self.changes_trie_config.is_some() { true => Some( - self.storage(EXTRINSIC_INDEX) - .and_then(|idx| idx.and_then(|idx| Decode::decode(&mut &*idx).ok())) - .unwrap_or(NO_EXTRINSIC_INDEX)), + match self.storage(EXTRINSIC_INDEX) { + OverlayedValueResult::Modified(idx) => Decode::decode(&mut &*idx) + .unwrap_or(NO_EXTRINSIC_INDEX), + OverlayedValueResult::Deleted + | OverlayedValueResult::NotFound => NO_EXTRINSIC_INDEX, + } + ), false => None, } } @@ -370,26 +499,26 @@ mod tests { let key = vec![42, 69, 169, 142]; - assert!(overlayed.storage(&key).is_none()); + assert_eq!(overlayed.storage(&key), OverlayedValueResult::NotFound); overlayed.set_storage(key.clone(), Some(vec![1, 2, 3])); - assert_eq!(overlayed.storage(&key).unwrap(), Some(&[1, 2, 3][..])); + assert_eq!(overlayed.storage(&key), OverlayedValueResult::Modified(&[1, 2, 3][..])); overlayed.commit_prospective(); - assert_eq!(overlayed.storage(&key).unwrap(), Some(&[1, 2, 3][..])); + assert_eq!(overlayed.storage(&key), OverlayedValueResult::Modified(&[1, 2, 3][..])); overlayed.set_storage(key.clone(), Some(vec![])); - assert_eq!(overlayed.storage(&key).unwrap(), Some(&[][..])); + assert_eq!(overlayed.storage(&key), OverlayedValueResult::Modified(&[][..])); overlayed.set_storage(key.clone(), None); - assert!(overlayed.storage(&key).unwrap().is_none()); + assert_eq!(overlayed.storage(&key), OverlayedValueResult::Deleted); overlayed.discard_prospective(); - assert_eq!(overlayed.storage(&key).unwrap(), Some(&[1, 2, 3][..])); + assert_eq!(overlayed.storage(&key), OverlayedValueResult::Modified(&[1, 2, 3][..])); overlayed.set_storage(key.clone(), None); overlayed.commit_prospective(); - assert!(overlayed.storage(&key).unwrap().is_none()); + assert_eq!(overlayed.storage(&key), OverlayedValueResult::Deleted); } #[test] diff --git a/core/state-machine/src/proving_backend.rs b/core/state-machine/src/proving_backend.rs index 2b44aae778db4..627f60b180c29 100644 --- a/core/state-machine/src/proving_backend.rs +++ b/core/state-machine/src/proving_backend.rs @@ -20,14 +20,14 @@ use std::{cell::RefCell, rc::Rc}; use log::debug; use hash_db::{Hasher, HashDB, EMPTY_PREFIX}; use trie::{ - MemoryDB, PrefixedMemoryDB, default_child_trie_root, + MemoryDB, PrefixedMemoryDB, Recorder, read_trie_value_with, read_child_trie_value_with, record_all_keys }; -pub use trie::Recorder; pub use trie::trie_types::{Layout, TrieError}; use crate::trie_backend::TrieBackend; use crate::trie_backend_essence::{Ephemeral, TrieBackendEssence, TrieBackendStorage}; use crate::{Error, ExecutionError, Backend}; +use primitives::child_trie::{ChildTrie, ChildTrieReadRef}; /// Patricia trie-based backend essence which also tracks all touched storage trie values. /// These can be sent to remote node and used as a proof of execution. @@ -60,11 +60,9 @@ impl<'a, S, H> ProvingBackendEssence<'a, S, H> pub fn child_storage( &mut self, - storage_key: &[u8], + child_trie: ChildTrieReadRef, key: &[u8] ) -> Result>, String> { - let root = self.storage(storage_key)? - .unwrap_or(default_child_trie_root::>(storage_key)); let mut read_overlay = S::Overlay::default(); let eph = Ephemeral::new( @@ -75,9 +73,8 @@ impl<'a, S, H> ProvingBackendEssence<'a, S, H> let map_e = |e| format!("Trie lookup error: {}", e); read_child_trie_value_with::, _, _>( - storage_key, + child_trie, &eph, - &root, key, &mut *self.proof_recorder ).map_err(map_e) @@ -158,16 +155,16 @@ impl<'a, S, H> Backend for ProvingBackend<'a, S, H> }.storage(key) } - fn child_storage(&self, storage_key: &[u8], key: &[u8]) -> Result>, Self::Error> { + fn child_storage(&self, child_trie: ChildTrieReadRef, key: &[u8]) -> Result>, Self::Error> { ProvingBackendEssence { backend: self.backend.essence(), proof_recorder: &mut *self.proof_recorder.try_borrow_mut() .expect("only fails when already borrowed; child_storage() is non-reentrant; qed"), - }.child_storage(storage_key, key) + }.child_storage(child_trie, key) } - fn for_keys_in_child_storage(&self, storage_key: &[u8], f: F) { - self.backend.for_keys_in_child_storage(storage_key, f) + fn for_keys_in_child_storage(&self, child_trie: ChildTrieReadRef, f: F) { + self.backend.for_keys_in_child_storage(child_trie, f) } fn for_keys_with_prefix(&self, prefix: &[u8], f: F) { @@ -178,8 +175,13 @@ impl<'a, S, H> Backend for ProvingBackend<'a, S, H> self.backend.for_key_values_with_prefix(prefix, f) } - fn for_child_keys_with_prefix(&self, storage_key: &[u8], prefix: &[u8], f: F) { - self.backend.for_child_keys_with_prefix(storage_key, prefix, f) + fn for_child_keys_with_prefix( + &self, + child_trie: ChildTrieReadRef, + prefix: &[u8], + f: F, + ) { + self.backend.for_child_keys_with_prefix(child_trie, prefix, f) } fn pairs(&self) -> Vec<(Vec, Vec)> { @@ -190,8 +192,8 @@ impl<'a, S, H> Backend for ProvingBackend<'a, S, H> self.backend.keys(prefix) } - fn child_keys(&self, child_storage_key: &[u8], prefix: &[u8]) -> Vec> { - self.backend.child_keys(child_storage_key, prefix) + fn child_keys(&self, child_trie: ChildTrieReadRef, prefix: &[u8]) -> Vec> { + self.backend.child_keys(child_trie, prefix) } fn storage_root(&self, delta: I) -> (H::Out, Self::Transaction) @@ -200,12 +202,12 @@ impl<'a, S, H> Backend for ProvingBackend<'a, S, H> self.backend.storage_root(delta) } - fn child_storage_root(&self, storage_key: &[u8], delta: I) -> (Vec, bool, Self::Transaction) + fn child_storage_root(&self, child_trie: &ChildTrie, delta: I) -> (Vec, bool, Self::Transaction) where I: IntoIterator, Option>)>, H::Out: Ord { - self.backend.child_storage_root(storage_key, delta) + self.backend.child_storage_root(child_trie, delta) } fn as_trie_backend(&mut self) -> Option<&TrieBackend> { @@ -250,7 +252,6 @@ mod tests { use crate::trie_backend::tests::test_trie; use super::*; use primitives::{Blake2Hasher}; - use crate::ChildStorageKey; fn test_proving<'a>( trie_backend: &'a TrieBackend,Blake2Hasher>, @@ -315,34 +316,28 @@ mod tests { #[test] fn proof_recorded_and_checked_with_child() { - let subtrie1 = ChildStorageKey::::from_slice( - b":child_storage:default:sub1" - ).unwrap(); - let subtrie2 = ChildStorageKey::::from_slice( - b":child_storage:default:sub2" - ).unwrap(); - let own1 = subtrie1.into_owned(); - let own2 = subtrie2.into_owned(); + let child_trie1 = ChildTrie::fetch_or_new(|_| None, |_| (), b"sub1", || 1u128); + let child_trie2 = ChildTrie::fetch_or_new(|_| None, |_| (), b"sub2", || 2u128); let contents = (0..64).map(|i| (None, vec![i], Some(vec![i]))) - .chain((28..65).map(|i| (Some(own1.clone()), vec![i], Some(vec![i])))) - .chain((10..15).map(|i| (Some(own2.clone()), vec![i], Some(vec![i])))) + .chain((28..65).map(|i| (Some(child_trie1.clone()), vec![i], Some(vec![i])))) + .chain((10..15).map(|i| (Some(child_trie2.clone()), vec![i], Some(vec![i])))) .collect::>(); let in_memory = InMemory::::default(); let mut in_memory = in_memory.update(contents); - let in_memory_root = in_memory.full_storage_root::<_, Vec<_>, _>( + let in_memory_root = in_memory.full_storage_root::<_, Vec<_>, _, _>( ::std::iter::empty(), - in_memory.child_storage_keys().map(|k|(k.to_vec(), Vec::new())) + in_memory.child_storage_child_trie().map(|k| (k, Vec::new())) ).0; (0..64).for_each(|i| assert_eq!( in_memory.storage(&[i]).unwrap().unwrap(), vec![i] )); (28..65).for_each(|i| assert_eq!( - in_memory.child_storage(&own1[..], &[i]).unwrap().unwrap(), + in_memory.child_storage(child_trie1.node_ref(), &[i]).unwrap().unwrap(), vec![i] )); (10..15).for_each(|i| assert_eq!( - in_memory.child_storage(&own2[..], &[i]).unwrap().unwrap(), + in_memory.child_storage(child_trie2.node_ref(), &[i]).unwrap().unwrap(), vec![i] )); @@ -369,16 +364,18 @@ mod tests { assert_eq!(proof_check.storage(&[41]).unwrap().unwrap(), vec![41]); assert_eq!(proof_check.storage(&[64]).unwrap(), None); - let proving = ProvingBackend::new(trie); - assert_eq!(proving.child_storage(&own1[..], &[64]), Ok(Some(vec![64]))); + let proving = ProvingBackend::new(&trie); + let child_trie1 = proving.child_trie(b"sub1").unwrap().unwrap(); + assert_eq!(proving.child_storage(child_trie1.node_ref(), &[64]), Ok(Some(vec![64]))); let proof = proving.extract_proof(); let proof_check = create_proof_check_backend::( in_memory_root.into(), proof ).unwrap(); + let child_trie1 = proof_check.child_trie(b"sub1").unwrap().unwrap(); assert_eq!( - proof_check.child_storage(&own1[..], &[64]).unwrap().unwrap(), + proof_check.child_storage(child_trie1.node_ref(), &[64]).unwrap().unwrap(), vec![64] ); } diff --git a/core/state-machine/src/testing.rs b/core/state-machine/src/testing.rs index a40026b271287..80c737d695678 100644 --- a/core/state-machine/src/testing.rs +++ b/core/state-machine/src/testing.rs @@ -16,25 +16,24 @@ //! Test implementation for Externalities. -use std::collections::{HashMap}; use hash_db::Hasher; -use crate::backend::{InMemory, Backend}; +use crate::backend::{InMemory, Backend, StorageContent}; use primitives::storage::well_known_keys::is_child_storage_key; use crate::changes_trie::{ build_changes_trie, InMemoryStorage as ChangesTrieInMemoryStorage, BlockNumber as ChangesTrieBlockNumber, }; use primitives::{ - storage::well_known_keys::{CHANGES_TRIE_CONFIG, CODE, HEAP_PAGES}, traits::BareCryptoStorePtr, offchain + storage::well_known_keys::{CHANGES_TRIE_CONFIG, CODE, HEAP_PAGES}, + traits::BareCryptoStorePtr, offchain, + child_trie::{ChildTrie, ChildTrieReadRef}, }; use codec::Encode; -use super::{ChildStorageKey, Externalities, OverlayedChanges}; +use super::{Externalities, OverlayedChanges, OverlayedValueResult}; const EXT_NOT_ALLOWED_TO_FAIL: &str = "Externalities not allowed to fail within runtime"; -type StorageTuple = (HashMap, Vec>, HashMap, HashMap, Vec>>); - -/// Simple HashMap-based Externalities impl. +/// Simple Externalities impl. pub struct TestExternalities { overlay: OverlayedChanges, backend: InMemory, @@ -45,35 +44,30 @@ pub struct TestExternalities { impl TestExternalities { /// Create a new instance of `TestExternalities` with storage. - pub fn new(storage: StorageTuple) -> Self { + pub fn new(storage: StorageContent) -> Self { Self::new_with_code(&[], storage) } /// Create a new instance of `TestExternalities` with code and storage. - pub fn new_with_code(code: &[u8], mut storage: StorageTuple) -> Self { + pub fn new_with_code(code: &[u8], mut storage: StorageContent) -> Self { let mut overlay = OverlayedChanges::default(); - assert!(storage.0.keys().all(|key| !is_child_storage_key(key))); - assert!(storage.1.keys().all(|key| is_child_storage_key(key))); + assert!(storage.top.keys().all(|key| !is_child_storage_key(key))); + assert!(storage.children.keys().all(|key| is_child_storage_key(key))); super::set_changes_trie_config( &mut overlay, - storage.0.get(&CHANGES_TRIE_CONFIG.to_vec()).cloned(), + storage.top.get(&CHANGES_TRIE_CONFIG.to_vec()).cloned(), false, ).expect("changes trie configuration is correct in test env; qed"); - storage.0.insert(HEAP_PAGES.to_vec(), 8u64.encode()); - storage.0.insert(CODE.to_vec(), code.to_vec()); - - let backend: HashMap<_, _> = storage.1.into_iter() - .map(|(keyspace, map)| (Some(keyspace), map)) - .chain(Some((None, storage.0)).into_iter()) - .collect(); + storage.top.insert(HEAP_PAGES.to_vec(), 8u64.encode()); + storage.top.insert(CODE.to_vec(), code.to_vec()); TestExternalities { overlay, changes_trie_storage: ChangesTrieInMemoryStorage::new(), - backend: backend.into(), + backend: storage.into(), offchain: None, keystore: None, } @@ -107,9 +101,10 @@ impl TestExternalities { let children = self.overlay.committed.children.clone().into_iter() .chain(self.overlay.prospective.children.clone().into_iter()) - .flat_map(|(keyspace, map)| { - map.into_iter() - .map(|(k, v)| (Some(keyspace.clone()), k, v.value)) + .flat_map(|(_keyspace, changeset)| { + let child_trie = changeset.child_trie; + changeset.values.into_iter() + .map(move |(k, v)| (Some(child_trie.clone()), k, v.value)) .collect::>() }); @@ -135,8 +130,8 @@ impl Default for TestExternalities { fn default() -> Self { Self::new(Default::default()) } } -impl From for TestExternalities { - fn from(storage: StorageTuple) -> Self { +impl From for TestExternalities { + fn from(storage: StorageContent) -> Self { Self::new(storage) } } @@ -148,28 +143,52 @@ impl Externalities for TestExternalities H::Out: Ord + 'static { fn storage(&self, key: &[u8]) -> Option> { - self.overlay.storage(key).map(|x| x.map(|x| x.to_vec())).unwrap_or_else(|| - self.backend.storage(key).expect(EXT_NOT_ALLOWED_TO_FAIL)) + match self.overlay.storage(key) { + OverlayedValueResult::NotFound => + self.backend.storage(key).expect(EXT_NOT_ALLOWED_TO_FAIL), + OverlayedValueResult::Deleted => None, + OverlayedValueResult::Modified(value) => Some(value.to_vec()), + } } fn original_storage(&self, key: &[u8]) -> Option> { self.backend.storage(key).expect(EXT_NOT_ALLOWED_TO_FAIL) } - fn child_storage(&self, storage_key: ChildStorageKey, key: &[u8]) -> Option> { - self.overlay - .child_storage(storage_key.as_ref(), key) - .map(|x| x.map(|x| x.to_vec())) - .unwrap_or_else(|| self.backend - .child_storage(storage_key.as_ref(), key) - .expect(EXT_NOT_ALLOWED_TO_FAIL) - ) + fn child_storage(&self, child_trie: ChildTrieReadRef, key: &[u8]) -> Option> { + match self.overlay.child_storage(child_trie.clone(), key) { + OverlayedValueResult::NotFound => + self.backend.child_storage(child_trie, key).expect(EXT_NOT_ALLOWED_TO_FAIL), + OverlayedValueResult::Deleted => None, + OverlayedValueResult::Modified(value) => Some( value.to_vec()), + } + } + + fn child_trie(&self, storage_key: &[u8]) -> Option { + self.overlay.child_trie(storage_key).unwrap_or_else(|| + self.backend.child_trie(storage_key).expect(EXT_NOT_ALLOWED_TO_FAIL)) } - fn original_child_storage(&self, storage_key: ChildStorageKey, key: &[u8]) -> Option> { + fn set_child_trie(&mut self, ct: ChildTrie) -> bool { + // do check for backend + let ct = match self.child_trie(ct.parent_slice()) { + Some(ct_old) => if ct_old.is_updatable_with(&ct) { + ct + } else { + return false; + }, + None => if ct.is_new() { + ct + } else { + return false; + }, + }; + self.overlay.set_child_trie(ct) + } + + fn original_child_storage(&self, child_trie: ChildTrieReadRef, key: &[u8]) -> Option> { self.backend - .child_storage(storage_key.as_ref(), key) - .map(|x| x.map(|x| x.to_vec())) + .child_storage(child_trie, key) .expect(EXT_NOT_ALLOWED_TO_FAIL) } @@ -183,20 +202,20 @@ impl Externalities for TestExternalities fn place_child_storage( &mut self, - storage_key: ChildStorageKey, + child_trie: &ChildTrie, key: Vec, value: Option> ) { - self.overlay.set_child_storage(storage_key.into_owned(), key, value); + self.overlay.set_child_storage(child_trie, key, value); } - fn kill_child_storage(&mut self, storage_key: ChildStorageKey) { + fn kill_child_storage(&mut self, child_trie: &ChildTrie) { let backend = &self.backend; let overlay = &mut self.overlay; - overlay.clear_child_storage(storage_key.as_ref()); - backend.for_keys_in_child_storage(storage_key.as_ref(), |key| { - overlay.set_child_storage(storage_key.as_ref().to_vec(), key.to_vec(), None); + overlay.clear_child_storage(child_trie); + backend.for_keys_in_child_storage(child_trie.node_ref(), |key| { + overlay.set_child_storage(child_trie, key.to_vec(), None); }); } @@ -214,33 +233,36 @@ impl Externalities for TestExternalities }); } - fn clear_child_prefix(&mut self, storage_key: ChildStorageKey, prefix: &[u8]) { + fn clear_child_prefix(&mut self, child_trie: &ChildTrie, prefix: &[u8]) { - self.overlay.clear_child_prefix(storage_key.as_ref(), prefix); + self.overlay.clear_child_prefix(child_trie, prefix); let backend = &self.backend; let overlay = &mut self.overlay; - backend.for_child_keys_with_prefix(storage_key.as_ref(), prefix, |key| { - overlay.set_child_storage(storage_key.as_ref().to_vec(), key.to_vec(), None); + backend.for_child_keys_with_prefix(child_trie.node_ref(), prefix, |key| { + overlay.set_child_storage(child_trie, key.to_vec(), None); }); } fn chain_id(&self) -> u64 { 42 } fn storage_root(&mut self) -> H::Out { - - let child_storage_keys = - self.overlay.prospective.children.keys() - .chain(self.overlay.committed.children.keys()); - - let child_delta_iter = child_storage_keys.map(|storage_key| - (storage_key.clone(), self.overlay.committed.children.get(storage_key) - .into_iter() - .flat_map(|map| map.iter().map(|(k, v)| (k.clone(), v.value.clone()))) - .chain(self.overlay.prospective.children.get(storage_key) - .into_iter() - .flat_map(|map| map.iter().map(|(k, v)| (k.clone(), v.value.clone())))))); - + let child_storage_tries = + self.overlay.prospective.children.values() + .chain(self.overlay.committed.children.values()) + .map(|v|&v.child_trie); + + let child_delta_iter = child_storage_tries.map(|child_trie| { + let keyspace = child_trie.keyspace(); + let committed_iter = self.overlay.committed.children + .get(keyspace).into_iter() + .flat_map(|map| map.values.iter().map(|(k, v)| (k.clone(), v.value.clone()))); + let prospective_iter = self.overlay.prospective.children + .get(keyspace).into_iter() + .flat_map(|map| map.values.iter().map(|(k, v)| (k.clone(), v.value.clone()))); + + (child_trie, committed_iter.chain(prospective_iter)) + }); // compute and memoize let delta = self.overlay.committed.top.iter().map(|(k, v)| (k.clone(), v.value.clone())) @@ -249,25 +271,24 @@ impl Externalities for TestExternalities } - fn child_storage_root(&mut self, storage_key: ChildStorageKey) -> Vec { - let storage_key = storage_key.as_ref(); - + fn child_storage_root(&mut self, child_trie: &ChildTrie) -> Vec { + let keyspace = child_trie.keyspace(); let (root, is_empty, _) = { - let delta = self.overlay.committed.children.get(storage_key) + let delta = self.overlay.committed.children.get(keyspace) .into_iter() - .flat_map(|map| map.clone().into_iter().map(|(k, v)| (k, v.value))) - .chain(self.overlay.prospective.children.get(storage_key) - .into_iter() - .flat_map(|map| map.clone().into_iter().map(|(k, v)| (k, v.value)))); - - self.backend.child_storage_root(storage_key, delta) + .flat_map(|map| map.values.iter().map(|(k, v)| (k.clone(), v.value.clone()))) + .chain(self.overlay.prospective.children.get(keyspace) + .into_iter() + .flat_map(|map| map.values.iter().map(|(k, v)| (k.clone(), v.value.clone())))); + self.backend.child_storage_root(child_trie, delta) }; if is_empty { - self.overlay.set_storage(storage_key.into(), None); + self.overlay.set_storage(child_trie.parent_trie().clone(), None); } else { - self.overlay.set_storage(storage_key.into(), Some(root.clone())); + self.overlay.set_storage(child_trie.parent_trie().clone(), Some(root.clone())); } root + } fn storage_changes_root(&mut self, parent: H::Out) -> Result, ()> { diff --git a/core/state-machine/src/trie_backend.rs b/core/state-machine/src/trie_backend.rs index 01e36a5810aaf..897d6f0005c1f 100644 --- a/core/state-machine/src/trie_backend.rs +++ b/core/state-machine/src/trie_backend.rs @@ -20,8 +20,10 @@ use log::{warn, debug}; use hash_db::Hasher; use trie::{Trie, delta_trie_root, default_child_trie_root, child_delta_trie_root}; use trie::trie_types::{TrieDB, TrieError, Layout}; +use trie::KeySpacedDB; use crate::trie_backend_essence::{TrieBackendEssence, TrieBackendStorage, Ephemeral}; use crate::Backend; +use primitives::child_trie::{ChildTrie, ChildTrieReadRef}; /// Patricia trie-based backend. Transaction type is an overlay of changes to commit. pub struct TrieBackend, H: Hasher> { @@ -70,8 +72,8 @@ impl, H: Hasher> Backend for TrieBackend where self.essence.storage(key) } - fn child_storage(&self, storage_key: &[u8], key: &[u8]) -> Result>, Self::Error> { - self.essence.child_storage(storage_key, key) + fn child_storage(&self, child_trie: ChildTrieReadRef, key: &[u8]) -> Result>, Self::Error> { + self.essence.child_storage(child_trie, key) } fn for_keys_with_prefix(&self, prefix: &[u8], f: F) { @@ -82,12 +84,17 @@ impl, H: Hasher> Backend for TrieBackend where self.essence.for_key_values_with_prefix(prefix, f) } - fn for_keys_in_child_storage(&self, storage_key: &[u8], f: F) { - self.essence.for_keys_in_child_storage(storage_key, f) + fn for_keys_in_child_storage(&self, child_trie: ChildTrieReadRef, f: F) { + self.essence.for_keys_in_child_storage(child_trie, f) } - fn for_child_keys_with_prefix(&self, storage_key: &[u8], prefix: &[u8], f: F) { - self.essence.for_child_keys_with_prefix(storage_key, prefix, f) + fn for_child_keys_with_prefix( + &self, + child_trie: ChildTrieReadRef, + prefix: &[u8], + f: F, + ) { + self.essence.for_child_keys_with_prefix(child_trie, prefix, f) } fn pairs(&self) -> Vec<(Vec, Vec)> { @@ -95,6 +102,7 @@ impl, H: Hasher> Backend for TrieBackend where let eph = Ephemeral::new(self.essence.backend_storage(), &mut read_overlay); let collect_all = || -> Result<_, Box>> { + let eph = KeySpacedDB::new(&eph, None); let trie = TrieDB::::new(&eph, self.essence.root())?; let mut v = Vec::new(); for x in trie.iter()? { @@ -119,6 +127,7 @@ impl, H: Hasher> Backend for TrieBackend where let eph = Ephemeral::new(self.essence.backend_storage(), &mut read_overlay); let collect_all = || -> Result<_, Box>> { + let eph = KeySpacedDB::new(&eph, None); let trie = TrieDB::::new(&eph, self.essence.root())?; let mut v = Vec::new(); for x in trie.iter()? { @@ -134,7 +143,7 @@ impl, H: Hasher> Backend for TrieBackend where collect_all().map_err(|e| debug!(target: "trie", "Error extracting trie keys: {}", e)).unwrap_or_default() } - fn storage_root(&self, delta: I) -> (H::Out, S::Overlay) + fn storage_root(&self, delta: I) -> (H::Out, Self::Transaction) where I: IntoIterator, Option>)> { let mut write_overlay = S::Overlay::default(); @@ -155,21 +164,15 @@ impl, H: Hasher> Backend for TrieBackend where (root, write_overlay) } - fn child_storage_root(&self, storage_key: &[u8], delta: I) -> (Vec, bool, Self::Transaction) + fn child_storage_root(&self, child_trie: &ChildTrie, delta: I) -> (Vec, bool, Self::Transaction) where I: IntoIterator, Option>)>, H::Out: Ord { - let default_root = default_child_trie_root::>(storage_key); + let default_root = default_child_trie_root::>(); + let mut root = default_root.clone(); let mut write_overlay = S::Overlay::default(); - let mut root = match self.storage(storage_key) { - Ok(value) => value.unwrap_or(default_child_trie_root::>(storage_key)), - Err(e) => { - warn!(target: "trie", "Failed to read child storage root: {}", e); - default_root.clone() - }, - }; { let mut eph = Ephemeral::new( @@ -178,16 +181,15 @@ impl, H: Hasher> Backend for TrieBackend where ); match child_delta_trie_root::, _, _, _, _>( - storage_key, + child_trie.node_ref(), &mut eph, - root.clone(), - delta + default_root.as_slice(), + delta, ) { Ok(ret) => root = ret, Err(e) => warn!(target: "trie", "Failed to write to trie: {}", e), } } - let is_default = root == default_root; (root, is_default, write_overlay) @@ -202,24 +204,28 @@ impl, H: Hasher> Backend for TrieBackend where pub mod tests { use std::collections::HashSet; use primitives::{Blake2Hasher, H256}; - use codec::Encode; - use trie::{TrieMut, PrefixedMemoryDB, trie_types::TrieDBMut}; + use trie::{TrieMut, PrefixedMemoryDB, KeySpacedDBMut, trie_types::TrieDBMut}; use super::*; + fn test_db() -> (PrefixedMemoryDB, H256) { let mut root = H256::default(); let mut mdb = PrefixedMemoryDB::::default(); + + let child_trie1 = ChildTrie::fetch_or_new(|_| None, |_| (), &b"sub1"[..], || 1u128); + let mut sub_root = H256::default(); { - let mut trie = TrieDBMut::new(&mut mdb, &mut root); + let mut mdb = KeySpacedDBMut::new(&mut mdb, Some(child_trie1.keyspace())); + let mut trie = TrieDBMut::new(&mut mdb, &mut sub_root); trie.insert(b"value3", &[142]).expect("insert failed"); trie.insert(b"value4", &[124]).expect("insert failed"); - }; - + } { - let mut sub_root = Vec::new(); - root.encode_to(&mut sub_root); + let enc_sub_root = child_trie1.encoded_with_root(&sub_root[..]); + let mut mdb = KeySpacedDBMut::new(&mut mdb, None); let mut trie = TrieDBMut::new(&mut mdb, &mut root); - trie.insert(b":child_storage:default:sub1", &sub_root).expect("insert failed"); + trie.insert(&child_trie1.parent_trie()[..], &enc_sub_root).expect("insert failed"); + trie.insert(b"key", b"value").expect("insert failed"); trie.insert(b"value1", &[42]).expect("insert failed"); trie.insert(b"value2", &[24]).expect("insert failed"); @@ -228,6 +234,7 @@ pub mod tests { trie.insert(&[i], &[i]).unwrap(); } } + (mdb, root) } @@ -241,6 +248,20 @@ pub mod tests { assert_eq!(test_trie().storage(b"key").unwrap(), Some(b"value".to_vec())); } + #[test] + fn read_from_child_storage_returns_some() { + let test_trie = test_trie(); + let child_trie = test_trie.child_trie(b"sub1"); + if let Ok(Some(child_trie)) = child_trie { + assert_eq!( + test_trie.child_storage(child_trie.node_ref(), b"value3").unwrap(), + Some(vec![142u8]), + ); + } else { + assert!(false, "cannot fetch child trie"); + } + } + #[test] fn read_from_storage_returns_none() { assert_eq!(test_trie().storage(b"non-existing-key").unwrap(), None); diff --git a/core/state-machine/src/trie_backend_essence.rs b/core/state-machine/src/trie_backend_essence.rs index 5a5431963448c..a4a17a94ea1da 100644 --- a/core/state-machine/src/trie_backend_essence.rs +++ b/core/state-machine/src/trie_backend_essence.rs @@ -21,9 +21,12 @@ use std::ops::Deref; use std::sync::Arc; use log::{debug, warn}; use hash_db::{self, Hasher, EMPTY_PREFIX, Prefix}; -use trie::{Trie, MemoryDB, PrefixedMemoryDB, DBValue, - default_child_trie_root, read_trie_value, read_child_trie_value, - for_keys_in_child_trie}; +use primitives::child_trie::ChildTrieReadRef; +use trie::{ + Trie, MemoryDB, PrefixedMemoryDB, DBValue, + read_trie_value, read_child_trie_value, for_keys_in_child_trie, + KeySpacedDB, +}; use trie::trie_types::{TrieDB, TrieError, Layout}; use crate::backend::Consolidate; @@ -77,10 +80,7 @@ impl, H: Hasher> TrieBackendEssence { } /// Get the value of child storage at given key. - pub fn child_storage(&self, storage_key: &[u8], key: &[u8]) -> Result>, String> { - let root = self.storage(storage_key)? - .unwrap_or(default_child_trie_root::>(storage_key)); - + pub fn child_storage(&self, child_trie: ChildTrieReadRef, key: &[u8]) -> Result>, String> { let mut read_overlay = S::Overlay::default(); let eph = Ephemeral { storage: &self.storage, @@ -89,19 +89,11 @@ impl, H: Hasher> TrieBackendEssence { let map_e = |e| format!("Trie lookup error: {}", e); - read_child_trie_value::, _>(storage_key, &eph, &root, key).map_err(map_e) + read_child_trie_value::, _>(child_trie, &eph, key).map_err(map_e) } /// Retrieve all entries keys of child storage and call `f` for each of those keys. - pub fn for_keys_in_child_storage(&self, storage_key: &[u8], f: F) { - let root = match self.storage(storage_key) { - Ok(v) => v.unwrap_or(default_child_trie_root::>(storage_key)), - Err(e) => { - debug!(target: "trie", "Error while iterating child storage: {}", e); - return; - } - }; - + pub fn for_keys_in_child_storage(&self, child_trie: ChildTrieReadRef, f: F) { let mut read_overlay = S::Overlay::default(); let eph = Ephemeral { storage: &self.storage, @@ -109,9 +101,8 @@ impl, H: Hasher> TrieBackendEssence { }; if let Err(e) = for_keys_in_child_trie::, _, Ephemeral>( - storage_key, + child_trie, &eph, - &root, f, ) { debug!(target: "trie", "Error while iterating child storage: {}", e); @@ -119,23 +110,33 @@ impl, H: Hasher> TrieBackendEssence { } /// Execute given closure for all keys starting with prefix. - pub fn for_child_keys_with_prefix(&self, storage_key: &[u8], prefix: &[u8], mut f: F) { - let root_vec = match self.storage(storage_key) { - Ok(v) => v.unwrap_or(default_child_trie_root::>(storage_key)), - Err(e) => { - debug!(target: "trie", "Error while iterating child storage: {}", e); - return; - } + pub fn for_child_keys_with_prefix( + &self, + child_trie: ChildTrieReadRef, + prefix: &[u8], + mut f: F, + ) { + let mut read_overlay = S::Overlay::default(); + let eph = Ephemeral { + storage: &self.storage, + overlay: &mut read_overlay, }; - 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)) - } + let f = |key: &[u8]| { + if !key.starts_with(prefix) { + return; + } - /// 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)) + f(key); + }; + + if let Err(e) = for_keys_in_child_trie::, _, Ephemeral>( + child_trie, + &eph, + f, + ) { + debug!(target: "trie", "Error while iterating child storage: {}", e); + } } @@ -152,6 +153,7 @@ impl, H: Hasher> TrieBackendEssence { }; let mut iter = move || -> Result<(), Box>> { + let eph = KeySpacedDB::new(&eph, None); let trie = TrieDB::::new(&eph, root)?; let mut iter = trie.iter()?; @@ -175,6 +177,11 @@ impl, H: Hasher> TrieBackendEssence { } } + /// 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)) + } + /// 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) diff --git a/core/test-client/src/lib.rs b/core/test-client/src/lib.rs index 17e26708468aa..501e94570e243 100644 --- a/core/test-client/src/lib.rs +++ b/core/test-client/src/lib.rs @@ -31,14 +31,14 @@ pub use keyring::{ sr25519::Keyring as Sr25519Keyring, }; pub use primitives::{Blake2Hasher, traits::BareCryptoStorePtr}; -pub use sr_primitives::{StorageOverlay, ChildrenStorageOverlay}; +pub use sr_primitives::StorageContent; pub use state_machine::ExecutionStrategy; use std::sync::Arc; use std::collections::HashMap; +use primitives::child_trie::ChildTrie; use futures::future::Ready; use hash_db::Hasher; -use primitives::storage::well_known_keys; use sr_primitives::traits::{ Block as BlockT, NumberFor }; @@ -57,11 +57,11 @@ pub struct LightFetcher; /// A genesis storage initialisation trait. pub trait GenesisInit: Default { /// Construct genesis storage. - fn genesis_storage(&self) -> (StorageOverlay, ChildrenStorageOverlay); + fn genesis_storage(&self) -> StorageContent; } impl GenesisInit for () { - fn genesis_storage(&self) -> (StorageOverlay, ChildrenStorageOverlay) { + fn genesis_storage(&self) -> StorageContent { Default::default() } } @@ -180,10 +180,23 @@ impl TestClientBuilder let mut storage = self.genesis_init.genesis_storage(); // Add some child storage keys. - for (key, value) in self.child_storage_extension { - storage.1.insert( - well_known_keys::CHILD_STORAGE_KEY_PREFIX.iter().cloned().chain(key).collect(), - value.into_iter().collect(), + for (_key, value) in self.child_storage_extension { + // warning: no prefix, next child trie creation will go in same keyspace + let child_trie = ChildTrie::fetch_or_new( + // warning current implementation expect empty state + |_| None, + // warning current implementation relies on direct key + // insertion in overlay for creation of the child trie, + // this can break in the future and require an + // actual backend update here. + |_| (), + &b"test"[..], + // block 0 + || 1u128, + ); + storage.children.insert( + child_trie.keyspace().clone(), + (value.into_iter().collect(), child_trie), ); } diff --git a/core/test-runtime/client/src/lib.rs b/core/test-runtime/client/src/lib.rs index aeed1e7ad447e..f03b2fa22b9d3 100644 --- a/core/test-runtime/client/src/lib.rs +++ b/core/test-runtime/client/src/lib.rs @@ -29,6 +29,7 @@ pub use generic_test_client::*; pub use runtime; use primitives::sr25519; +use primitives::child_trie::ChildTrie; use runtime::genesismap::{GenesisConfig, additional_storage_with_genesis}; use sr_primitives::traits::{Block as BlockT, Header as HeaderT, Hash as HashT}; @@ -99,7 +100,7 @@ pub struct GenesisParameters { support_changes_trie: bool, heap_pages_override: Option, extra_storage: HashMap, Vec>, - child_extra_storage: HashMap, HashMap, Vec>>, + child_extra_storage: HashMap, (HashMap, Vec>, ChildTrie)>, } impl GenesisParameters { @@ -125,21 +126,21 @@ impl GenesisParameters { } impl generic_test_client::GenesisInit for GenesisParameters { - fn genesis_storage(&self) -> (StorageOverlay, ChildrenStorageOverlay) { - use codec::Encode; + fn genesis_storage(&self) -> StorageContent { let mut storage = self.genesis_config().genesis_map(); - let child_roots = storage.1.iter().map(|(sk, child_map)| { - let state_root = <<::Header as HeaderT>::Hashing as HashT>::trie_root( - child_map.clone().into_iter() + let child_roots = storage.children.iter().map(|(_, child)| { + let child_root = <<::Header as HeaderT>::Hashing as HashT>::trie_root( + child.0.clone().into_iter() ); - (sk.clone(), state_root.encode()) + + (child.1.parent_trie().clone(), child.1.encoded_with_root(&child_root[..])) }); let state_root = <<::Header as HeaderT>::Hashing as HashT>::trie_root( - storage.0.clone().into_iter().chain(child_roots) + storage.top.clone().into_iter().chain(child_roots) ); let block: runtime::Block = client::genesis::construct_genesis_block(state_root); - storage.0.extend(additional_storage_with_genesis(&block)); + storage.top.extend(additional_storage_with_genesis(&block)); storage } @@ -187,9 +188,9 @@ pub trait TestClientBuilderExt: Sized { /// # Panics /// /// Panics if the key is empty. - fn add_extra_child_storage>, K: Into>, V: Into>>( + fn add_extra_child_storage>, V: Into>>( self, - storage_key: SK, + child_trie: &ChildTrie, key: K, value: V, ) -> Self; @@ -233,19 +234,19 @@ impl TestClientBuilderExt for TestClientBuilder< self } - fn add_extra_child_storage>, K: Into>, V: Into>>( + fn add_extra_child_storage>, V: Into>>( mut self, - storage_key: SK, + child_trie: &ChildTrie, key: K, value: V, ) -> Self { - let storage_key = storage_key.into(); + let storage_key = child_trie.parent_slice().to_vec(); let key = key.into(); assert!(!storage_key.is_empty()); assert!(!key.is_empty()); self.genesis_init_mut().child_extra_storage .entry(storage_key) - .or_insert_with(Default::default) + .or_insert_with(|| (Default::default(), child_trie.clone())).0 .insert(key, value.into()); self } diff --git a/core/test-runtime/src/genesismap.rs b/core/test-runtime/src/genesismap.rs index 34d7eecae0c80..f21792156a3b5 100644 --- a/core/test-runtime/src/genesismap.rs +++ b/core/test-runtime/src/genesismap.rs @@ -21,6 +21,7 @@ use runtime_io::{blake2_256, twox_128}; use super::{AuthorityId, AccountId, WASM_BINARY}; use codec::{Encode, KeyedVec, Joiner}; use primitives::{ChangesTrieConfiguration, map, storage::well_known_keys}; +use primitives::child_trie::ChildTrie; use sr_primitives::traits::{Block as BlockT, Hash as HashT, Header as HeaderT}; /// Configuration of a general Substrate test genesis block. @@ -31,7 +32,7 @@ pub struct GenesisConfig { heap_pages_override: Option, /// Additional storage key pairs that will be added to the genesis map. extra_storage: HashMap, Vec>, - child_extra_storage: HashMap, HashMap, Vec>>, + child_extra_storage: HashMap, (HashMap, Vec>, ChildTrie)>, } impl GenesisConfig { @@ -42,7 +43,7 @@ impl GenesisConfig { balance: u64, heap_pages_override: Option, extra_storage: HashMap, Vec>, - child_extra_storage: HashMap, HashMap, Vec>>, + child_extra_storage: HashMap, (HashMap, Vec>, ChildTrie)>, ) -> Self { GenesisConfig { changes_trie_config: match support_changes_trie { @@ -57,10 +58,7 @@ impl GenesisConfig { } } - pub fn genesis_map(&self) -> ( - HashMap, Vec>, - HashMap, HashMap, Vec>>, - ) { + pub fn genesis_map(&self) -> sr_primitives::StorageContent { let wasm_runtime = WASM_BINARY.to_vec(); let mut map: HashMap, Vec> = self.balances.iter() .map(|&(ref account, balance)| (account.to_keyed_vec(b"balance:"), vec![].and(&balance))) @@ -80,29 +78,24 @@ impl GenesisConfig { // Finally, add the extra storage entries. map.extend(self.extra_storage.clone().into_iter()); - (map, self.child_extra_storage.clone()) + sr_primitives::StorageContent{ top: map, children: self.child_extra_storage.clone()} } } -pub fn insert_genesis_block( - storage: &mut ( - HashMap, Vec>, - HashMap, HashMap, Vec>>, - ) -) -> primitives::hash::H256 { +pub fn insert_genesis_block(storage: &mut sr_primitives::StorageContent) -> primitives::hash::H256 { - let child_roots = storage.1.iter().map(|(sk, child_map)| { - let state_root = <<::Header as HeaderT>::Hashing as HashT>::trie_root( + let child_roots = storage.children.iter().map(|(_ks, (child_map, child_trie))| { + let child_root = <<::Header as HeaderT>::Hashing as HashT>::trie_root( child_map.clone().into_iter() ); - (sk.clone(), state_root.encode()) + (child_trie.parent_trie().to_vec(), child_trie.encoded_with_root(&child_root[..])) }); let state_root = <<::Header as HeaderT>::Hashing as HashT>::trie_root( - storage.0.clone().into_iter().chain(child_roots) + storage.top.clone().into_iter().chain(child_roots) ); let block: crate::Block = substrate_client::genesis::construct_genesis_block(state_root); let genesis_hash = block.header.hash(); - storage.0.extend(additional_storage_with_genesis(&block)); + storage.top.extend(additional_storage_with_genesis(&block)); genesis_hash } diff --git a/core/test-runtime/src/lib.rs b/core/test-runtime/src/lib.rs index ec7ed9670c330..1b8fa7a014323 100644 --- a/core/test-runtime/src/lib.rs +++ b/core/test-runtime/src/lib.rs @@ -29,7 +29,7 @@ use primitives::{Blake2Hasher, OpaqueMetadata}; use app_crypto::{ed25519, sr25519, RuntimeAppPublic}; pub use app_crypto; use trie_db::{TrieMut, Trie}; -use substrate_trie::PrefixedMemoryDB; +use substrate_trie::{KeySpacedDBMut, KeySpacedDB, PrefixedMemoryDB}; use substrate_trie::trie_types::{TrieDB, TrieDBMut}; use substrate_client::{ @@ -414,8 +414,9 @@ fn code_using_trie() -> u64 { let mut mdb = PrefixedMemoryDB::default(); let mut root = rstd::default::Default::default(); - let _ = { + { let v = &pairs; + let mut mdb = KeySpacedDBMut::new(&mut mdb, None); let mut t = TrieDBMut::::new(&mut mdb, &mut root); for i in 0..v.len() { let key: &[u8]= &v[i].0; @@ -424,9 +425,9 @@ fn code_using_trie() -> u64 { return 101; } } - t - }; + } + let mdb = KeySpacedDB::new(&mdb, None); if let Ok(trie) = TrieDB::::new(&mdb, &root) { if let Ok(iter) = trie.iter() { let mut iter_pairs = Vec::new(); diff --git a/core/test-runtime/src/system.rs b/core/test-runtime/src/system.rs index f1288f8b2806f..eaa3b72194023 100644 --- a/core/test-runtime/src/system.rs +++ b/core/test-runtime/src/system.rs @@ -333,13 +333,13 @@ mod tests { Sr25519Keyring::Bob.to_raw_public(), Sr25519Keyring::Charlie.to_raw_public() ]; - TestExternalities::new((map![ + TestExternalities::new(sr_primitives::StorageContent { top: map![ twox_128(b"latest").to_vec() => vec![69u8; 32], twox_128(b"sys:auth").to_vec() => authorities.encode(), blake2_256(&AccountKeyring::Alice.to_raw_public().to_keyed_vec(b"balance:")).to_vec() => { vec![111u8, 0, 0, 0, 0, 0, 0, 0] } - ], map![])) + ], children: map![]}) } fn block_import_works(block_executor: F) where F: Fn(Block, &mut TestExternalities) { diff --git a/core/trie/src/lib.rs b/core/trie/src/lib.rs index e526a27ebefba..6ace7d8999aaa 100644 --- a/core/trie/src/lib.rs +++ b/core/trie/src/lib.rs @@ -23,9 +23,11 @@ mod node_header; mod node_codec; mod trie_stream; +use primitives::child_trie::{ChildTrieReadRef, KeySpace, keyspace_as_prefix_alloc}; use rstd::boxed::Box; use rstd::vec::Vec; -use hash_db::Hasher; +use rstd::marker::PhantomData; +use hash_db::{Hasher, Prefix}; /// Our `NodeCodec`-specific error. pub use error::Error; /// The Substrate format implementation of `TrieStream`. @@ -104,6 +106,7 @@ 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; + /// This module is for non generic definition of trie type. /// Only the `Hasher` trait is generic in this case. pub mod trie_types { @@ -130,7 +133,8 @@ pub fn delta_trie_root( DB: hash_db::HashDB, { { - let mut trie = TrieDBMut::::from_existing(&mut *db, &mut root)?; + let mut db = KeySpacedDBMut::new(db, None); + let mut trie = TrieDBMut::::from_existing(&mut db, &mut root)?; for (key, change) in delta { match change { @@ -149,7 +153,9 @@ pub fn read_trie_value, key: &[u8] ) -> Result>, Box>> { - Ok(TrieDB::::new(&*db, root)?.get(key).map(|x| x.map(|val| val.to_vec()))?) + let db = KeySpacedDB::new(db, None); + let db = TrieDB::::new(&db, root)?; + Ok(db.get(key).map(|x| x.map(|val| val.to_vec()))?) } /// Read a value from the trie with given Query. @@ -163,40 +169,23 @@ pub fn read_trie_value_with< key: &[u8], query: Q ) -> Result>, Box>> { - Ok(TrieDB::::new(&*db, root)?.get_with(key, query).map(|x| x.map(|val| val.to_vec()))?) -} - -/// Determine whether a child trie key is valid. -/// -/// For now, the only valid child trie key is `:child_storage:default:`. -/// -/// `child_trie_root` and `child_delta_trie_root` can panic if invalid value is provided to them. -pub fn is_child_trie_key_valid(storage_key: &[u8]) -> bool { - use primitives::storage::well_known_keys; - let has_right_prefix = storage_key.starts_with(b":child_storage:default:"); - if has_right_prefix { - // This is an attempt to catch a change of `is_child_storage_key`, which - // just checks if the key has prefix `:child_storage:` at the moment of writing. - debug_assert!( - well_known_keys::is_child_storage_key(&storage_key), - "`is_child_trie_key_valid` is a subset of `is_child_storage_key`", - ); - } - has_right_prefix + let db = KeySpacedDB::new(db, None); + let db = TrieDB::::new(&db, root)?; + Ok(db.get_with(key, query).map(|x| x.map(|val| val.to_vec()))?) } /// Determine the default child trie root. -pub fn default_child_trie_root(_storage_key: &[u8]) -> Vec { +/// see issue FIXME #2741, this is not efficient. +pub fn default_child_trie_root() -> Vec { L::trie_root::<_, Vec, Vec>(core::iter::empty()).as_ref().iter().cloned().collect() } /// 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(_storage_key: &[u8], input: I) -> Vec - where - I: IntoIterator, - A: AsRef<[u8]> + Ord, - B: AsRef<[u8]>, +pub fn child_trie_root(input: I) -> Vec where + I: IntoIterator, + A: AsRef<[u8]> + Ord, + B: AsRef<[u8]>, { L::trie_root(input).as_ref().iter().cloned().collect() } @@ -204,9 +193,9 @@ pub fn child_trie_root(_storage_key: &[u8], 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( - _storage_key: &[u8], + child_trie: ChildTrieReadRef, db: &mut DB, - root_vec: Vec, + default_root: &[u8], delta: I ) -> Result, Box>> where @@ -216,12 +205,16 @@ pub fn child_delta_trie_root( DB: hash_db::HashDB + hash_db::PlainDB, trie_db::DBValue>, { - let mut root = TrieHash::::default(); - // root is fetched from DB, not writable by runtime, so it's always valid. - root.as_mut().copy_from_slice(&root_vec); + let (root, keyspace) = match child_trie { + ChildTrieReadRef::Existing(root, keyspace) => (root, keyspace), + ChildTrieReadRef::New(keyspace) => (default_root, keyspace), + }; + // keyspaced is needed (db can be init from this operation, this is not only root calculation) + let mut db = KeySpacedDBMut::new(&mut *db, Some(keyspace)); + let mut root = trie_root_as_hash::(root); { - let mut trie = TrieDBMut::::from_existing(&mut *db, &mut root)?; + let mut trie = TrieDBMut::::from_existing(&mut db, &mut root)?; for (key, change) in delta { match change { @@ -230,31 +223,31 @@ pub fn child_delta_trie_root( }; } } - Ok(root.as_ref().to_vec()) + } /// Call `f` for all keys in a child trie. pub fn for_keys_in_child_trie( - _storage_key: &[u8], + child_trie: ChildTrieReadRef, db: &DB, - root_slice: &[u8], 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); + if let ChildTrieReadRef::Existing(root, keyspace) = child_trie { + let root = trie_root_as_hash::(root); - let trie = TrieDB::::new(&*db, &root)?; - let iter = trie.iter()?; + let db = KeySpacedDB::new(&*db, Some(keyspace)); + let trie = TrieDB::::new(&db, &root)?; + let iter = trie.iter()?; - for x in iter { - let (key, _) = x?; - f(&key); + for x in iter { + let (key, _) = x?; + f(&key); + } } Ok(()) @@ -268,7 +261,8 @@ pub fn record_all_keys( ) -> Result<(), Box>> where DB: hash_db::HashDBRef { - let trie = TrieDB::::new(&*db, root)?; + let db = KeySpacedDB::new(db, None); + let trie = TrieDB::::new(&db, root)?; let iter = trie.iter()?; for x in iter { @@ -285,27 +279,27 @@ pub fn record_all_keys( /// Read a value from the child trie. pub fn read_child_trie_value( - _storage_key: &[u8], + child_trie: ChildTrieReadRef, 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); - - Ok(TrieDB::::new(&*db, &root)?.get(key).map(|x| x.map(|val| val.to_vec()))?) + if let ChildTrieReadRef::Existing(root, keyspace) = child_trie { + let root = trie_root_as_hash::(root); + let db = KeySpacedDB::new(&*db, Some(keyspace)); + Ok(TrieDB::::new(&db, &root)?.get(key).map(|x| x.map(|val| val.to_vec()))?) + } else { + Ok(None) + } } /// Read a value from the child trie with given query. pub fn read_child_trie_value_with, DB>( - _storage_key: &[u8], + child_trie: ChildTrieReadRef, db: &DB, - root_slice: &[u8], key: &[u8], query: Q ) -> Result>, Box>> @@ -313,12 +307,103 @@ pub fn read_child_trie_value_with + hash_db::PlainDBRef, trie_db::DBValue>, { - let mut root = TrieHash::::default(); - // root is fetched from DB, not writable by runtime, so it's always valid. - root.as_mut().copy_from_slice(root_slice); + if let ChildTrieReadRef::Existing(root, keyspace) = child_trie { + let root = trie_root_as_hash::(root); + let db = KeySpacedDB::new(&*db, Some(keyspace)); + Ok(TrieDB::::new(&db, &root)?.get_with(key, query).map(|x| x.map(|val| val.to_vec()))?) + } else { + Ok(None) + } +} + +/// `HashDB` implementation that append a encoded `KeySpace` (unique id in as bytes) with the +/// prefix of every key value. +pub struct KeySpacedDB<'a, DB, H>(&'a DB, Option<&'a KeySpace>, PhantomData); +/// `HashDBMut` implementation that append a encoded `KeySpace` (unique id in as bytes) with the +/// prefix of every key value. +/// +/// Mutable variant of `KeySpacedDB`, see [`KeySpacedDB`]. +pub struct KeySpacedDBMut<'a, DB, H>(&'a mut DB, Option<&'a KeySpace>, PhantomData); + - Ok(TrieDB::::new(&*db, &root)?.get_with(key, query).map(|x| x.map(|val| val.to_vec()))?) +impl<'a, DB, H> KeySpacedDB<'a, DB, H> where + H: Hasher, +{ + /// instantiate new keyspaced db + pub fn new(db: &'a DB, ks: Option<&'a KeySpace>) -> Self { + KeySpacedDB(db, ks, PhantomData) + } } +impl<'a, DB, H> KeySpacedDBMut<'a, DB, H> where + H: Hasher, +{ + /// instantiate new keyspaced db + pub fn new(db: &'a mut DB, ks: Option<&'a KeySpace>) -> Self { + KeySpacedDBMut(db, ks, PhantomData) + } +} + +impl<'a, DB, H, T> hash_db::HashDBRef for KeySpacedDB<'a, DB, H> where + DB: hash_db::HashDBRef, + H: Hasher, + T: From<&'static [u8]>, +{ + fn get(&self, key: &H::Out, prefix: Prefix) -> Option { + let derived_prefix = keyspace_as_prefix_alloc(self.1, prefix); + self.0.get(key, (&derived_prefix.0, derived_prefix.1)) + } + + fn contains(&self, key: &H::Out, prefix: Prefix) -> bool { + let derived_prefix = keyspace_as_prefix_alloc(self.1, prefix); + self.0.contains(key, (&derived_prefix.0, derived_prefix.1)) + } +} + +impl<'a, DB, H, T> hash_db::HashDB for KeySpacedDBMut<'a, DB, H> where + DB: hash_db::HashDB, + H: Hasher, + T: Default + PartialEq + for<'b> From<&'b [u8]> + Clone + Send + Sync, +{ + fn get(&self, key: &H::Out, prefix: Prefix) -> Option { + let derived_prefix = keyspace_as_prefix_alloc(self.1, prefix); + self.0.get(key, (&derived_prefix.0, derived_prefix.1)) + } + + fn contains(&self, key: &H::Out, prefix: Prefix) -> bool { + let derived_prefix = keyspace_as_prefix_alloc(self.1, prefix); + self.0.contains(key, (&derived_prefix.0, derived_prefix.1)) + } + + fn insert(&mut self, prefix: Prefix, value: &[u8]) -> H::Out { + let derived_prefix = keyspace_as_prefix_alloc(self.1, prefix); + self.0.insert((&derived_prefix.0, derived_prefix.1), value) + } + + fn emplace(&mut self, key: H::Out, prefix: Prefix, value: T) { + let derived_prefix = keyspace_as_prefix_alloc(self.1, prefix); + self.0.emplace(key, (&derived_prefix.0, derived_prefix.1), value) + } + + fn remove(&mut self, key: &H::Out, prefix: Prefix) { + let derived_prefix = keyspace_as_prefix_alloc(self.1, prefix); + self.0.remove(key, (&derived_prefix.0, derived_prefix.1)) + } +} + +impl<'a, DB, H, T> hash_db::AsHashDB for KeySpacedDBMut<'a, DB, H> where + DB: hash_db::HashDB, + H: Hasher, + T: Default + PartialEq + for<'b> From<&'b [u8]> + Clone + Send + Sync, +{ + fn as_hash_db(&self) -> &dyn hash_db::HashDB { &*self } + fn as_hash_db_mut<'b>(&'b mut self) -> &'b mut (dyn hash_db::HashDB + 'b) { + &mut *self + } + +} + + +// Utilities (not exported): /// Constants used into trie simplification codec. mod trie_constants { @@ -329,6 +414,13 @@ mod trie_constants { pub const BRANCH_WITH_MASK: u8 = 0b_11 << 6; } +fn trie_root_as_hash> (trie_root: R) -> TrieHash { + let mut root = >::default(); + // root is fetched from DB, not writable by runtime, so it's always valid. + root.as_mut().copy_from_slice(trie_root.as_ref()); + root +} + #[cfg(test)] mod tests { use super::*; diff --git a/node/executor/src/lib.rs b/node/executor/src/lib.rs index fc0ca390497e4..8ad5d12ea66f2 100644 --- a/node/executor/src/lib.rs +++ b/node/executor/src/lib.rs @@ -47,6 +47,7 @@ mod tests { use sr_primitives::{ traits::{Header as HeaderT, Hash as HashT, Convert}, ApplyOutcome, ApplyResult, transaction_validity::InvalidTransaction, weights::{WeightMultiplier, GetDispatchInfo}, + StorageContent, }; use contracts::ContractAddressFor; use system::{EventRecord, Phase}; @@ -119,7 +120,8 @@ mod tests { #[test] fn panic_execution_with_foreign_code_gives_error() { - let mut t = TestExternalities::::new_with_code(BLOATY_CODE, (map![ + let mut t = TestExternalities::::new_with_code(BLOATY_CODE, StorageContent { + top: map![ >::hashed_key_for(alice()) => { 69_u128.encode() }, @@ -132,7 +134,7 @@ mod tests { >::hashed_key_for(0) => { vec![0u8; 32] } - ], map![])); + ], children: map![]}); let r = executor().call::<_, NeverNativeValue, fn() -> _>( &mut t, @@ -155,7 +157,8 @@ mod tests { #[test] fn bad_extrinsic_with_native_equivalent_code_gives_error() { - let mut t = TestExternalities::::new_with_code(COMPACT_CODE, (map![ + let mut t = TestExternalities::::new_with_code(COMPACT_CODE, StorageContent { + top: map![ >::hashed_key_for(alice()) => { 69_u128.encode() }, @@ -168,7 +171,7 @@ mod tests { >::hashed_key_for(0) => { vec![0u8; 32] } - ], map![])); + ], children: map![]}); let r = executor().call::<_, NeverNativeValue, fn() -> _>( &mut t, @@ -191,7 +194,8 @@ mod tests { #[test] fn successful_execution_with_native_equivalent_code_gives_ok() { - let mut t = TestExternalities::::new_with_code(COMPACT_CODE, (map![ + let mut t = TestExternalities::::new_with_code(COMPACT_CODE, StorageContent { + top: map![ >::hashed_key_for(alice()) => { (111 * DOLLARS).encode() }, @@ -200,7 +204,7 @@ mod tests { }, >::hashed_key().to_vec() => vec![0u8; 16], >::hashed_key_for(0) => vec![0u8; 32] - ], map![])); + ], children: map![]}); let r = executor().call::<_, NeverNativeValue, fn() -> _>( &mut t, @@ -227,7 +231,8 @@ mod tests { #[test] fn successful_execution_with_foreign_code_gives_ok() { - let mut t = TestExternalities::::new_with_code(BLOATY_CODE, (map![ + let mut t = TestExternalities::::new_with_code(BLOATY_CODE, StorageContent { + top: map![ >::hashed_key_for(alice()) => { (111 * DOLLARS).encode() }, @@ -236,7 +241,7 @@ mod tests { }, >::hashed_key().to_vec() => vec![0u8; 16], >::hashed_key_for(0) => vec![0u8; 32] - ], map![])); + ], children: map![]}); let r = executor().call::<_, NeverNativeValue, fn() -> _>( &mut t, @@ -746,7 +751,8 @@ mod tests { #[test] fn panic_execution_gives_error() { - let mut t = TestExternalities::::new_with_code(BLOATY_CODE, (map![ + let mut t = TestExternalities::::new_with_code(BLOATY_CODE, StorageContent { + top: map![ >::hashed_key_for(alice()) => { 0_u128.encode() }, @@ -755,7 +761,7 @@ mod tests { }, >::hashed_key().to_vec() => vec![0u8; 16], >::hashed_key_for(0) => vec![0u8; 32] - ], map![])); + ], children: map![]}); let r = WasmExecutor::new() .call(&mut t, 8, COMPACT_CODE, "Core_initialize_block", &vec![].and(&from_block_number(1u32))); @@ -768,7 +774,8 @@ mod tests { #[test] fn successful_execution_gives_ok() { - let mut t = TestExternalities::::new_with_code(COMPACT_CODE, (map![ + let mut t = TestExternalities::::new_with_code(COMPACT_CODE, StorageContent { + top: map![ >::hashed_key_for(alice()) => { (111 * DOLLARS).encode() }, @@ -777,7 +784,7 @@ mod tests { }, >::hashed_key().to_vec() => vec![0u8; 16], >::hashed_key_for(0) => vec![0u8; 32] - ], map![])); + ], children: map![]}); let r = WasmExecutor::new() .call(&mut t, 8, COMPACT_CODE, "Core_initialize_block", &vec![].and(&from_block_number(1u32))); @@ -927,7 +934,8 @@ mod tests { // - 1 MILLICENTS in substrate node. // - 1 milldot based on current polkadot runtime. // (this baed on assigning 0.1 CENT to the cheapest tx with `weight = 100`) - let mut t = TestExternalities::::new_with_code(COMPACT_CODE, (map![ + let mut t = TestExternalities::::new_with_code(COMPACT_CODE, StorageContent { + top: map![ >::hashed_key_for(alice()) => { (100 * DOLLARS).encode() }, @@ -939,7 +947,7 @@ mod tests { }, >::hashed_key().to_vec() => vec![0u8; 16], >::hashed_key_for(0) => vec![0u8; 32] - ], map![])); + ], children: map![]}); let tip = 1_000_000; let xt = sign(CheckedExtrinsic { diff --git a/node/testing/src/client.rs b/node/testing/src/client.rs index 3b16f2cbcb97d..4538ed00d42c9 100644 --- a/node/testing/src/client.rs +++ b/node/testing/src/client.rs @@ -16,7 +16,7 @@ //! Utilites to build a `TestClient` for `node-runtime`. -use sr_primitives::BuildStorage; +use sr_primitives::{BuildStorage, StorageContent}; /// Re-export test-client utilities. pub use test_client::*; @@ -42,7 +42,7 @@ pub struct GenesisParameters { } impl test_client::GenesisInit for GenesisParameters { - fn genesis_storage(&self) -> (StorageOverlay, ChildrenStorageOverlay) { + fn genesis_storage(&self) -> StorageContent { crate::genesis::config(self.support_changes_trie, None).build_storage().unwrap() } } diff --git a/srml/contracts/src/account_db.rs b/srml/contracts/src/account_db.rs index 6ce1dff22bfab..943f83fda2374 100644 --- a/srml/contracts/src/account_db.rs +++ b/srml/contracts/src/account_db.rs @@ -18,7 +18,7 @@ use super::{ AliveContractInfo, BalanceOf, CodeHash, ContractInfo, ContractInfoOf, Trait, TrieId, - TrieIdGenerator, + TrieIdGenerator, prefixed_trie_id, contract_child_trie, }; use crate::exec::StorageKey; use rstd::cell::RefCell; @@ -126,9 +126,12 @@ impl AccountDb for DirectAccountDb { &self, _account: &T::AccountId, trie_id: Option<&TrieId>, - location: &StorageKey + location: &StorageKey, ) -> Option> { - trie_id.and_then(|id| child::get_raw(id, &blake2_256(location))) + trie_id.and_then(|id| { + contract_child_trie(&id) + .and_then(|child_trie| child::get_raw(child_trie.node_ref(), &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)) @@ -168,23 +171,30 @@ impl AccountDb for DirectAccountDb { Some(ContractInfo::Tombstone(_)) => continue, }; + let block_number = >::block_number(); + let mut new_info = match (changed.reset, old_info.clone(), changed.code_hash) { // Existing contract is being modified. (false, Some(info), _) => info, // Existing contract is being removed. (true, Some(info), None) => { - child::kill_storage(&info.trie_id); - >::remove(&address); + if let Some(child_trie) = contract_child_trie(&info.trie_id) { + child::kill_storage(&child_trie); + >::remove(&address); + } + continue; - } + }, // Existing contract is being replaced by a new one. (true, Some(info), Some(code_hash)) => { - child::kill_storage(&info.trie_id); + if let Some(child_trie) = contract_child_trie(&info.trie_id) { + child::kill_storage(&child_trie); + } AliveContractInfo:: { code_hash, storage_size: T::StorageSizeOffset::get(), trie_id: ::TrieIdGenerator::trie_id(&address), - deduct_block: >::block_number(), + deduct_block: block_number, rent_allowance: >::max_value(), last_write: None, } @@ -195,7 +205,7 @@ impl AccountDb for DirectAccountDb { code_hash, storage_size: T::StorageSizeOffset::get(), trie_id: ::TrieIdGenerator::trie_id(&address), - deduct_block: >::block_number(), + deduct_block: block_number, rent_allowance: >::max_value(), last_write: None, } @@ -211,20 +221,22 @@ impl AccountDb for DirectAccountDb { if let Some(code_hash) = changed.code_hash { new_info.code_hash = code_hash; } + let p_key = prefixed_trie_id(&new_info.trie_id); + let child_trie = child::fetch_or_new(p_key.as_slice()); if !changed.storage.is_empty() { - new_info.last_write = Some(>::block_number()); + new_info.last_write = Some(block_number); } for (k, v) in changed.storage.into_iter() { - if let Some(value) = child::get_raw(&new_info.trie_id[..], &blake2_256(&k)) { + if let Some(value) = child::get_raw(child_trie.node_ref(), &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.trie_id[..], &blake2_256(&k), &value[..]); + child::put_raw(&child_trie, &blake2_256(&k), &value[..]); } else { - child::kill(&new_info.trie_id[..], &blake2_256(&k)); + child::kill(&child_trie, &blake2_256(&k)); } } diff --git a/srml/contracts/src/lib.rs b/srml/contracts/src/lib.rs index 74ad5867af3ca..3076a9f4f6d36 100644 --- a/srml/contracts/src/lib.rs +++ b/srml/contracts/src/lib.rs @@ -106,6 +106,7 @@ use crate::wasm::{WasmLoader, WasmVm}; #[cfg(feature = "std")] use serde::{Serialize, Deserialize}; use primitives::crypto::UncheckedFrom; +use primitives::child_trie::ChildTrie; use rstd::{prelude::*, marker::PhantomData}; use codec::{Codec, Encode, Decode}; use runtime_io::blake2_256; @@ -123,10 +124,11 @@ use support::{ }; use support::{traits::{OnFreeBalanceZero, OnUnbalanced, Currency, Get}, IsSubType}; use system::{ensure_signed, RawOrigin, ensure_root}; -use primitives::storage::well_known_keys::CHILD_STORAGE_KEY_PREFIX; use timestamp; pub type CodeHash = ::Hash; +// see FIXME #2744 should result in removing this type, and `TrieIdGenerator` +// and `TrieIdFromParentCounter` (this id being use only for child trie indirection). pub type TrieId = Vec; /// A function that generates an `AccountId` for a contract upon instantiation. @@ -264,8 +266,8 @@ pub trait TrieIdGenerator { /// Get trie id from `account_id`. pub struct TrieIdFromParentCounter(PhantomData); -/// This generator uses inner counter for account id and applies the hash over `AccountId + -/// accountid_counter`. +/// This generator uses inner counter for account id and applies a hash over +/// `AccountId`++ `accountid_counter`. impl TrieIdGenerator for TrieIdFromParentCounter where T::AccountId: AsRef<[u8]> @@ -282,9 +284,7 @@ where buf.extend_from_slice(account_id.as_ref()); buf.extend_from_slice(&new_seed.to_le_bytes()[..]); - // TODO: see https://github.com/paritytech/substrate/issues/2325 - CHILD_STORAGE_KEY_PREFIX.iter() - .chain(b"default:") + b"default:".iter() .chain(T::Hashing::hash(&buf[..]).as_ref().iter()) .cloned() .collect() @@ -736,10 +736,13 @@ impl Module { origin_contract.last_write }; + let child_trie = contract_child_trie(&origin_contract.trie_id) + .ok_or_else(|| "Corrupted contract is missing child storage trie")?; + let key_values_taken = delta.iter() .filter_map(|key| { - child::get_raw(&origin_contract.trie_id, &blake2_256(key)).map(|value| { - child::kill(&origin_contract.trie_id, &blake2_256(key)); + child::get_raw(child_trie.node_ref(), &blake2_256(key)).map(|value| { + child::kill(&child_trie, &blake2_256(key)); (key, value) }) }) @@ -748,13 +751,13 @@ impl Module { let tombstone = >::new( // This operation is cheap enough because last_write (delta not included) // is not this block as it has been checked earlier. - &runtime_io::child_storage_root(&origin_contract.trie_id)[..], + &runtime_io::child_storage_root(&child_trie)[..], code_hash, ); if tombstone != dest_tombstone { for (key, value) in key_values_taken { - child::put_raw(&origin_contract.trie_id, &blake2_256(key), &value); + child::put_raw(&child_trie, &blake2_256(key), &value); } return Err("Tombstones don't match"); @@ -820,7 +823,7 @@ decl_storage! { pub PristineCode: map CodeHash => Option>; /// A mapping between an original code hash and instrumented wasm code, ready for execution. pub CodeStorage: map CodeHash => Option; - /// The subtrie counter. + /// The child_trie counter. pub AccountCounter: u64 = 0; /// The code associated with a given account. pub ContractInfoOf: map T::AccountId => Option>; @@ -829,10 +832,24 @@ decl_storage! { } } +/// contract uses this prefix +pub const CHILD_CONTRACT_PREFIX: &'static [u8] = b"default:"; + +fn contract_child_trie(id: &TrieId) -> Option { + child::child_trie(prefixed_trie_id(id.as_slice()).as_slice()) +} + +fn prefixed_trie_id(trie_id: &[u8]) -> Vec { + let mut res = CHILD_CONTRACT_PREFIX.to_vec(); + res.extend_from_slice(trie_id); + res +} + impl OnFreeBalanceZero for Module { fn on_free_balance_zero(who: &T::AccountId) { if let Some(ContractInfo::Alive(info)) = >::take(who) { - child::kill_storage(&info.trie_id); + contract_child_trie(&info.trie_id) + .map(|child_trie| child::kill_storage(&child_trie)); } } } diff --git a/srml/contracts/src/rent.rs b/srml/contracts/src/rent.rs index 776f804ee7cac..444d5666eb262 100644 --- a/srml/contracts/src/rent.rs +++ b/srml/contracts/src/rent.rs @@ -14,9 +14,10 @@ // You should have received a copy of the GNU General Public License // along with Substrate. If not, see . -use crate::{BalanceOf, ContractInfo, ContractInfoOf, TombstoneContractInfo, Trait, AliveContractInfo}; +use crate::{BalanceOf, ContractInfo, ContractInfoOf, TombstoneContractInfo, + Trait, contract_child_trie, AliveContractInfo}; use sr_primitives::traits::{Bounded, CheckedDiv, CheckedMul, Saturating, Zero, - SaturatedConversion}; + SaturatedConversion, Hash as HashT}; use support::traits::{Currency, ExistenceRequirement, Get, WithdrawReason}; use support::StorageMap; @@ -99,7 +100,10 @@ fn try_evict_or_and_pay_rent( if balance < subsistence_threshold { // The contract cannot afford to leave a tombstone, so remove the contract info altogether. >::remove(account); - runtime_io::kill_child_storage(&contract.trie_id); + + if let Some(child_trie) = contract_child_trie(&contract.trie_id) { + runtime_io::kill_child_storage(&child_trie); + } return (RentOutcome::Evicted, None); } @@ -143,17 +147,25 @@ fn try_evict_or_and_pay_rent( // The contract cannot afford the rent payment and has a balance above the subsistence // threshold, so it leaves a tombstone. - // Note: this operation is heavy. - let child_storage_root = runtime_io::child_storage_root(&contract.trie_id); - + let (o_ct, child_storage_root) = if let Some(child_trie) = contract_child_trie(&contract.trie_id) { + // Note: this operation is heavy. + let child_storage_root = runtime_io::child_storage_root(&child_trie); + (Some(child_trie), child_storage_root) + } else { + // Note that this will fail as soon as we support multiple type + // of hashing for child trie. + let child_storage_root = <::Hashing as HashT>::ordered_trie_root(&[&[]]); + + let child_storage_root_ref: &[u8] = child_storage_root.as_ref(); + (None, child_storage_root_ref.to_vec()) + }; let tombstone = >::new( &child_storage_root[..], contract.code_hash, ); let tombstone_info = ContractInfo::Tombstone(tombstone); >::insert(account, &tombstone_info); - - runtime_io::kill_child_storage(&contract.trie_id); + o_ct.map(|child_trie| runtime_io::kill_child_storage(&child_trie)); return (RentOutcome::Evicted, Some(tombstone_info)); } diff --git a/srml/contracts/src/tests.rs b/srml/contracts/src/tests.rs index f585eddb596e2..dc182df0732f3 100644 --- a/srml/contracts/src/tests.rs +++ b/srml/contracts/src/tests.rs @@ -40,6 +40,7 @@ use support::{ assert_ok, assert_err, impl_outer_dispatch, impl_outer_event, impl_outer_origin, parameter_types, storage::child, StorageMap, StorageValue, traits::{Currency, Get}, }; +use primitives::child_trie::ChildTrie; use std::{cell::RefCell, sync::atomic::{AtomicUsize, Ordering}}; use primitives::{storage::well_known_keys, Blake2Hasher}; use system::{self, EventRecord, Phase}; @@ -357,7 +358,10 @@ fn account_removal_removes_storage() { overlay.set_storage(&2, key2.clone(), Some(b"4".to_vec())); DirectAccountDb.commit(overlay.into_change_set()); } - + assert_eq!( + >::get_storage(&DirectAccountDb, &2, Some(&trie_id2), key1), + Some(b"3".to_vec()) + ); // Transfer funds from account 1 of such amount that after this transfer // the balance of account 1 will be below the existential threshold. // diff --git a/srml/support/procedural/src/storage/transformation.rs b/srml/support/procedural/src/storage/transformation.rs index cb2c7e6ca9398..81f7765d5180f 100644 --- a/srml/support/procedural/src/storage/transformation.rs +++ b/srml/support/procedural/src/storage/transformation.rs @@ -538,13 +538,13 @@ fn decl_store_extra_genesis( #[cfg(feature = "std")] impl#fparam_impl GenesisConfig#sparam #genesis_where_clause { pub fn build_storage #fn_generic (self) -> std::result::Result< - ( - #scrate::sr_primitives::StorageOverlay, - #scrate::sr_primitives::ChildrenStorageOverlay, - ), + #scrate::sr_primitives::StorageContent, String > #fn_where_clause { - let mut storage = (Default::default(), Default::default()); + let mut storage = #scrate::sr_primitives::StorageContent { + top: Default::default(), + children: Default::default(), + }; self.assimilate_storage::<#fn_traitinstance>(&mut storage)?; Ok(storage) } @@ -552,10 +552,7 @@ fn decl_store_extra_genesis( /// Assimilate the storage for this module into pre-existing overlays. pub fn assimilate_storage #fn_generic ( self, - tuple_storage: &mut ( - #scrate::sr_primitives::StorageOverlay, - #scrate::sr_primitives::ChildrenStorageOverlay, - ), + tuple_storage: &mut #scrate::sr_primitives::StorageContent, ) -> std::result::Result<(), String> #fn_where_clause { #scrate::with_storage(tuple_storage, || { #builders @@ -573,10 +570,7 @@ fn decl_store_extra_genesis( { fn build_module_genesis_storage( self, - storage: &mut ( - #scrate::sr_primitives::StorageOverlay, - #scrate::sr_primitives::ChildrenStorageOverlay, - ), + storage: &mut #scrate::sr_primitives::StorageContent, ) -> std::result::Result<(), String> { self.assimilate_storage::<#fn_traitinstance> (storage) } diff --git a/srml/support/src/storage/child.rs b/srml/support/src/storage/child.rs index 6000dd2f17508..d31026fa8a69c 100644 --- a/srml/support/src/storage/child.rs +++ b/srml/support/src/storage/child.rs @@ -21,86 +21,141 @@ //! avoid collision from a resistant hash function (which unique implies)). // NOTE: could replace unhashed by having only one kind of storage (root being null storage key (storage_key can become Option<&[u8]>). -use super::{Codec, Encode, Decode, Vec}; +use super::{Codec, Decode, Vec}; +use primitives::child_trie::ChildTrie; +use primitives::child_trie::ChildTrieReadRef; +use primitives::storage::well_known_keys; +pub use super::unhashed::StorageVec; + +/// Method for fetching or initiating a new child trie. +pub fn next_keyspace() -> u128 { + let key = well_known_keys::CHILD_STORAGE_KEYSPACE_COUNTER; + // do start at 1 (0 is reserved for top trie) + let previous = super::unhashed::get(key).unwrap_or(0u128); + let new = previous + 1; + super::unhashed::put(key, &new); + new +} + +/// Method for fetching or initiating a new child trie. +pub fn fetch_or_new( + parent: &[u8], +) -> ChildTrie { + ChildTrie::fetch_or_new( + |pk| { child_trie(pk) }, + |ct| { + let updated = set_child_trie(ct); + // fetch or new create a new child trie + // so the child trie creation condition + // cannot fail at this point. + debug_assert!(updated); + }, + parent, + || next_keyspace(), + ) +} + +/// Fetch a child trie return None if no child trie for +/// this storage key. +pub fn child_trie(storage_key: &[u8]) -> Option { + runtime_io::child_trie(storage_key) +} + +/// Update or create an existing child trie. +/// Warning in case of update this function does not allow: +/// - root change +/// Return false and do nothing in those cases. +pub fn set_child_trie(ct: ChildTrie) -> bool { + runtime_io::set_child_trie(ct) +} /// Return the value of the item in storage under `key`, or `None` if there is no explicit entry. -pub fn get(storage_key: &[u8], key: &[u8]) -> Option { - runtime_io::child_storage(storage_key, key).map(|v| { +pub fn get(child_trie: ChildTrieReadRef, key: &[u8]) -> Option { + runtime_io::child_storage(child_trie.clone(), key).map(|v| { Decode::decode(&mut &v[..]).expect("storage is not null, therefore must be a valid type") }) } /// Return the value of the item in storage under `key`, or the type's default if there is no /// explicit entry. -pub fn get_or_default(storage_key: &[u8], key: &[u8]) -> T { - get(storage_key, key).unwrap_or_else(Default::default) +pub fn get_or_default(child_trie: ChildTrieReadRef, key: &[u8]) -> T { + get(child_trie, key).unwrap_or_else(Default::default) } /// Return the value of the item in storage under `key`, or `default_value` if there is no /// explicit entry. -pub fn get_or(storage_key: &[u8], key: &[u8], default_value: T) -> T { - get(storage_key, key).unwrap_or(default_value) +pub fn get_or(child_trie: ChildTrieReadRef, key: &[u8], default_value: T) -> T { + get(child_trie, key).unwrap_or(default_value) } /// Return the value of the item in storage under `key`, or `default_value()` if there is no /// explicit entry. -pub fn get_or_else T>(storage_key: &[u8], key: &[u8], default_value: F) -> T { - get(storage_key, key).unwrap_or_else(default_value) +pub fn get_or_else T>( + child_trie: ChildTrieReadRef, + key: &[u8], + default_value: F, +) -> T { + get(child_trie, key).unwrap_or_else(default_value) } /// Put `value` in storage under `key`. -pub fn put(storage_key: &[u8], key: &[u8], value: &T) { - value.using_encoded(|slice| runtime_io::set_child_storage(storage_key, key, slice)); +pub fn put(child_trie: &ChildTrie, key: &[u8], value: &T) { + value.using_encoded(|slice| runtime_io::set_child_storage(child_trie, key, slice)); } /// Remove `key` from storage, returning its value if it had an explicit entry or `None` otherwise. -pub fn take(storage_key: &[u8], key: &[u8]) -> Option { - let r = get(storage_key, key); +pub fn take(child_trie: &ChildTrie, key: &[u8]) -> Option { + let r = get(child_trie.node_ref(), key); if r.is_some() { - kill(storage_key, key); + kill(child_trie, key); } r } /// Remove `key` from storage, returning its value, or, if there was no explicit entry in storage, /// the default for its type. -pub fn take_or_default(storage_key: &[u8], key: &[u8]) -> T { - take(storage_key, key).unwrap_or_else(Default::default) +pub fn take_or_default(child_trie: &ChildTrie, key: &[u8]) -> T { + take(child_trie, key).unwrap_or_else(Default::default) } /// Return the value of the item in storage under `key`, or `default_value` if there is no /// explicit entry. Ensure there is no explicit entry on return. -pub fn take_or(storage_key: &[u8],key: &[u8], default_value: T) -> T { - take(storage_key, key).unwrap_or(default_value) +pub fn take_or(child_trie: &ChildTrie,key: &[u8], default_value: T) -> T { + take(child_trie, key).unwrap_or(default_value) } /// Return the value of the item in storage under `key`, or `default_value()` if there is no /// explicit entry. Ensure there is no explicit entry on return. -pub fn take_or_else T>(storage_key: &[u8], key: &[u8], default_value: F) -> T { - take(storage_key, key).unwrap_or_else(default_value) +pub fn take_or_else T>( + child_trie: &ChildTrie, + key: &[u8], + default_value: F, +) -> T { + take(child_trie, key).unwrap_or_else(default_value) } /// Check to see if `key` has an explicit entry in storage. -pub fn exists(storage_key: &[u8], key: &[u8]) -> bool { - runtime_io::read_child_storage(storage_key, key, &mut [0;0][..], 0).is_some() +pub fn exists(child_trie: ChildTrieReadRef, key: &[u8]) -> bool { + runtime_io::read_child_storage(child_trie, key, &mut [0;0][..], 0).is_some() } /// Remove all `storage_key` key/values -pub fn kill_storage(storage_key: &[u8]) { - runtime_io::kill_child_storage(storage_key) +pub fn kill_storage(child_trie: &ChildTrie) { + runtime_io::kill_child_storage(child_trie) } /// Ensure `key` has no explicit entry in storage. -pub fn kill(storage_key: &[u8], key: &[u8]) { - runtime_io::clear_child_storage(storage_key, key); +pub fn kill(child_trie: &ChildTrie, key: &[u8]) { + runtime_io::clear_child_storage(child_trie, key); } /// Get a Vec of bytes from storage. -pub fn get_raw(storage_key: &[u8], key: &[u8]) -> Option> { - runtime_io::child_storage(storage_key, key) +pub fn get_raw(child_trie: ChildTrieReadRef, key: &[u8]) -> Option> { + runtime_io::child_storage(child_trie, key) } /// Put a raw byte slice into storage. -pub fn put_raw(storage_key: &[u8], key: &[u8], value: &[u8]) { - runtime_io::set_child_storage(storage_key, key, value) +pub fn put_raw(child_trie: &ChildTrie, key: &[u8], value: &[u8]) { + runtime_io::set_child_storage(child_trie, key, value) } + diff --git a/srml/support/test/tests/instance.rs b/srml/support/test/tests/instance.rs index 3370c0d201d57..68bcb4a543c56 100644 --- a/srml/support/test/tests/instance.rs +++ b/srml/support/test/tests/instance.rs @@ -304,7 +304,7 @@ fn new_test_ext() -> runtime_io::TestExternalities { #[test] fn storage_instance_independance() { - let mut storage = (std::collections::HashMap::new(), std::collections::HashMap::new()); + let mut storage = Default::default(); runtime_io::with_storage(&mut storage, || { module2::Value::::put(0); module2::Value::::put(0); @@ -324,7 +324,7 @@ fn storage_instance_independance() { module2::DoubleMap::::insert(&0, &0, &0); }); // 16 storage values + 4 linked_map head. - assert_eq!(storage.0.len(), 16 + 4); + assert_eq!(storage.top.len(), 16 + 4); } #[test] @@ -487,3 +487,4 @@ fn instance_prefix_is_prefix_of_entries() { assert!(module2::Instance2::PREFIX_FOR_LinkedMap.starts_with(prefix)); assert!(module2::Instance2::PREFIX_FOR_DoubleMap.starts_with(prefix)); } + diff --git a/srml/system/src/lib.rs b/srml/system/src/lib.rs index 2f1d0ab9e0e20..14a757f8fe916 100644 --- a/srml/system/src/lib.rs +++ b/srml/system/src/lib.rs @@ -90,6 +90,8 @@ #[cfg(feature = "std")] use serde::Serialize; +#[cfg(feature = "std")] +use sr_primitives::StorageContent; use rstd::prelude::*; #[cfg(any(feature = "std", test))] use rstd::map; @@ -717,11 +719,11 @@ impl Module { /// Get the basic externalities for this module, useful for tests. #[cfg(any(feature = "std", test))] pub fn externalities() -> TestExternalities { - TestExternalities::new((map![ + TestExternalities::new(StorageContent{ top: map![ >::hashed_key_for(T::BlockNumber::zero()) => [69u8; 32].encode(), >::hashed_key().to_vec() => T::BlockNumber::one().encode(), >::hashed_key().to_vec() => [69u8; 32].encode() - ], map![])) + ], children: map![]}) } /// Set the block number to something in particular. Can be used as an alternative to