mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-12 18:11:10 +00:00
Fix key collision for child trie (#4162)
* In progress, runtime io must switch to future proof root + child_specific (unique id) + u32 type. * Switch interface, sr-io seems ok, rpc could use similar interface to sr-io, genesis json broken if there is child trie in existing encoding genesis. * test from previous implementation. * fix proving test. * Restore Keyspacedb from other branch, only apply to child trie. * Removing unneeded child_info from child root (child info are stored if things changed, otherwhise the root does not change). * Switch rpc to use same format as ext: more future proof. * use root from child info for trie backend essence. * Breaking long lines. * Update doc and clean pr a bit. * fix error type * Restore removed doc on merge and update sr-io doc. * Switch child storage api to use directly unique id, if managed id where to be put in place, the api will change at this time. * Clean deprecated host interface from child. * Removing assertion on child info (can fail depending on root memoization). * merging child info in the overlay when possible. * child iteration by prefix using child_info. * Using ChainInfo in frame support. ChainInfo gets redesign to avoid buffers allocation on every calls. * Add length of root to the data of child info. * comments * Encode compact. * Remove child info with root. * Fix try_update condition. * Comment Ext child root caching. * Replace tuples by struct with field * remove StorageTuple alias. * Fix doc tests, and remove StorageOverlay and ChildStorageOverlay aliases.
This commit is contained in:
@@ -52,9 +52,9 @@ use kvdb::{KeyValueDB, DBTransaction};
|
||||
use trie::{MemoryDB, PrefixedMemoryDB, prefixed_key};
|
||||
use parking_lot::{Mutex, RwLock};
|
||||
use primitives::{H256, Blake2Hasher, ChangesTrieConfiguration, convert_hash, traits::CodeExecutor};
|
||||
use primitives::storage::well_known_keys;
|
||||
use primitives::storage::{well_known_keys, ChildInfo};
|
||||
use sp_runtime::{
|
||||
generic::{BlockId, DigestItem}, Justification, StorageOverlay, ChildrenStorageOverlay,
|
||||
generic::{BlockId, DigestItem}, Justification, Storage,
|
||||
BuildStorage,
|
||||
};
|
||||
use sp_runtime::traits::{
|
||||
@@ -139,24 +139,39 @@ impl<B: BlockT> StateBackend<Blake2Hasher> for RefTrackingState<B> {
|
||||
self.state.storage_hash(key)
|
||||
}
|
||||
|
||||
fn child_storage(&self, storage_key: &[u8], key: &[u8]) -> Result<Option<Vec<u8>>, Self::Error> {
|
||||
self.state.child_storage(storage_key, key)
|
||||
fn child_storage(
|
||||
&self,
|
||||
storage_key: &[u8],
|
||||
child_info: ChildInfo,
|
||||
key: &[u8],
|
||||
) -> Result<Option<Vec<u8>>, Self::Error> {
|
||||
self.state.child_storage(storage_key, child_info, key)
|
||||
}
|
||||
|
||||
fn exists_storage(&self, key: &[u8]) -> Result<bool, Self::Error> {
|
||||
self.state.exists_storage(key)
|
||||
}
|
||||
|
||||
fn exists_child_storage(&self, storage_key: &[u8], key: &[u8]) -> Result<bool, Self::Error> {
|
||||
self.state.exists_child_storage(storage_key, key)
|
||||
fn exists_child_storage(
|
||||
&self,
|
||||
storage_key: &[u8],
|
||||
child_info: ChildInfo,
|
||||
key: &[u8],
|
||||
) -> Result<bool, Self::Error> {
|
||||
self.state.exists_child_storage(storage_key, child_info, key)
|
||||
}
|
||||
|
||||
fn next_storage_key(&self, key: &[u8]) -> Result<Option<Vec<u8>>, Self::Error> {
|
||||
self.state.next_storage_key(key)
|
||||
}
|
||||
|
||||
fn next_child_storage_key(&self, storage_key: &[u8], key: &[u8]) -> Result<Option<Vec<u8>>, Self::Error> {
|
||||
self.state.next_child_storage_key(storage_key, key)
|
||||
fn next_child_storage_key(
|
||||
&self,
|
||||
storage_key: &[u8],
|
||||
child_info: ChildInfo,
|
||||
key: &[u8],
|
||||
) -> Result<Option<Vec<u8>>, Self::Error> {
|
||||
self.state.next_child_storage_key(storage_key, child_info, key)
|
||||
}
|
||||
|
||||
fn for_keys_with_prefix<F: FnMut(&[u8])>(&self, prefix: &[u8], f: F) {
|
||||
@@ -167,12 +182,23 @@ impl<B: BlockT> StateBackend<Blake2Hasher> for RefTrackingState<B> {
|
||||
self.state.for_key_values_with_prefix(prefix, f)
|
||||
}
|
||||
|
||||
fn for_keys_in_child_storage<F: FnMut(&[u8])>(&self, storage_key: &[u8], f: F) {
|
||||
self.state.for_keys_in_child_storage(storage_key, f)
|
||||
fn for_keys_in_child_storage<F: FnMut(&[u8])>(
|
||||
&self,
|
||||
storage_key: &[u8],
|
||||
child_info: ChildInfo,
|
||||
f: F,
|
||||
) {
|
||||
self.state.for_keys_in_child_storage(storage_key, child_info, f)
|
||||
}
|
||||
|
||||
fn for_child_keys_with_prefix<F: FnMut(&[u8])>(&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<F: FnMut(&[u8])>(
|
||||
&self,
|
||||
storage_key: &[u8],
|
||||
child_info: ChildInfo,
|
||||
prefix: &[u8],
|
||||
f: F,
|
||||
) {
|
||||
self.state.for_child_keys_with_prefix(storage_key, child_info, prefix, f)
|
||||
}
|
||||
|
||||
fn storage_root<I>(&self, delta: I) -> (H256, Self::Transaction)
|
||||
@@ -182,11 +208,16 @@ impl<B: BlockT> StateBackend<Blake2Hasher> for RefTrackingState<B> {
|
||||
self.state.storage_root(delta)
|
||||
}
|
||||
|
||||
fn child_storage_root<I>(&self, storage_key: &[u8], delta: I) -> (H256, bool, Self::Transaction)
|
||||
fn child_storage_root<I>(
|
||||
&self,
|
||||
storage_key: &[u8],
|
||||
child_info: ChildInfo,
|
||||
delta: I,
|
||||
) -> (H256, bool, Self::Transaction)
|
||||
where
|
||||
I: IntoIterator<Item=(Vec<u8>, Option<Vec<u8>>)>,
|
||||
{
|
||||
self.state.child_storage_root(storage_key, delta)
|
||||
self.state.child_storage_root(storage_key, child_info, delta)
|
||||
}
|
||||
|
||||
fn pairs(&self) -> Vec<(Vec<u8>, Vec<u8>)> {
|
||||
@@ -197,8 +228,13 @@ impl<B: BlockT> StateBackend<Blake2Hasher> for RefTrackingState<B> {
|
||||
self.state.keys(prefix)
|
||||
}
|
||||
|
||||
fn child_keys(&self, child_key: &[u8], prefix: &[u8]) -> Vec<Vec<u8>> {
|
||||
self.state.child_keys(child_key, prefix)
|
||||
fn child_keys(
|
||||
&self,
|
||||
storage_key: &[u8],
|
||||
child_info: ChildInfo,
|
||||
prefix: &[u8],
|
||||
) -> Vec<Vec<u8>> {
|
||||
self.state.child_keys(storage_key, child_info, prefix)
|
||||
}
|
||||
|
||||
fn as_trie_backend(
|
||||
@@ -523,26 +559,26 @@ impl<Block> client_api::backend::BlockImportOperation<Block, Blake2Hasher>
|
||||
|
||||
fn reset_storage(
|
||||
&mut self,
|
||||
top: StorageOverlay,
|
||||
children: ChildrenStorageOverlay
|
||||
storage: Storage,
|
||||
) -> ClientResult<H256> {
|
||||
|
||||
if top.iter().any(|(k, _)| well_known_keys::is_child_storage_key(k)) {
|
||||
if storage.top.iter().any(|(k, _)| well_known_keys::is_child_storage_key(k)) {
|
||||
return Err(sp_blockchain::Error::GenesisInvalid.into());
|
||||
}
|
||||
|
||||
for child_key in children.keys() {
|
||||
for child_key in storage.children.keys() {
|
||||
if !well_known_keys::is_child_storage_key(&child_key) {
|
||||
return Err(sp_blockchain::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)))));
|
||||
let child_delta = storage.children.into_iter().map(|(storage_key, child_content)| (
|
||||
storage_key,
|
||||
child_content.data.into_iter().map(|(k, v)| (k, Some(v))), child_content.child_info),
|
||||
);
|
||||
|
||||
let (root, transaction) = self.old_state.full_storage_root(
|
||||
top.into_iter().map(|(k, v)| (k, Some(v))),
|
||||
storage.top.into_iter().map(|(k, v)| (k, Some(v))),
|
||||
child_delta
|
||||
);
|
||||
|
||||
@@ -903,7 +939,8 @@ impl<Block: BlockT<Hash=H256>> Backend<Block> {
|
||||
};
|
||||
let mut op = inmem.begin_operation().unwrap();
|
||||
op.set_block_data(header, body, justification, new_block_state).unwrap();
|
||||
op.update_db_storage(state.into_iter().map(|(k, v)| (None, k, Some(v))).collect()).unwrap();
|
||||
op.update_db_storage(vec![(None, state.into_iter().map(|(k, v)| (k, Some(v))).collect())])
|
||||
.unwrap();
|
||||
inmem.commit_operation(op).unwrap();
|
||||
}
|
||||
|
||||
@@ -1711,7 +1748,10 @@ mod tests {
|
||||
).0.into();
|
||||
let hash = header.hash();
|
||||
|
||||
op.reset_storage(storage.iter().cloned().collect(), Default::default()).unwrap();
|
||||
op.reset_storage(Storage {
|
||||
top: storage.iter().cloned().collect(),
|
||||
children: Default::default(),
|
||||
}).unwrap();
|
||||
op.set_block_data(
|
||||
header.clone(),
|
||||
Some(vec![]),
|
||||
@@ -1793,7 +1833,10 @@ mod tests {
|
||||
).0.into();
|
||||
let hash = header.hash();
|
||||
|
||||
op.reset_storage(storage.iter().cloned().collect(), Default::default()).unwrap();
|
||||
op.reset_storage(Storage {
|
||||
top: storage.iter().cloned().collect(),
|
||||
children: Default::default(),
|
||||
}).unwrap();
|
||||
|
||||
key = op.db_updates.insert(EMPTY_PREFIX, b"hello");
|
||||
op.set_block_data(
|
||||
|
||||
@@ -23,6 +23,7 @@ use linked_hash_map::{LinkedHashMap, Entry};
|
||||
use hash_db::Hasher;
|
||||
use sp_runtime::traits::{Block as BlockT, Header};
|
||||
use primitives::hexdisplay::HexDisplay;
|
||||
use primitives::storage::ChildInfo;
|
||||
use state_machine::{backend::Backend as StateBackend, TrieBackend};
|
||||
use log::trace;
|
||||
use client_api::backend::{StorageCollection, ChildStorageCollection};
|
||||
@@ -516,7 +517,12 @@ impl<H: Hasher, S: StateBackend<H>, B: BlockT> StateBackend<H> for CachingState<
|
||||
Ok(hash)
|
||||
}
|
||||
|
||||
fn child_storage(&self, storage_key: &[u8], key: &[u8]) -> Result<Option<Vec<u8>>, Self::Error> {
|
||||
fn child_storage(
|
||||
&self,
|
||||
storage_key: &[u8],
|
||||
child_info: ChildInfo,
|
||||
key: &[u8],
|
||||
) -> Result<Option<Vec<u8>>, Self::Error> {
|
||||
let key = (storage_key.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() {
|
||||
@@ -531,7 +537,7 @@ impl<H: Hasher, S: StateBackend<H>, B: BlockT> StateBackend<H> for CachingState<
|
||||
}
|
||||
}
|
||||
trace!("Cache miss: {:?}", key);
|
||||
let value = self.state.child_storage(storage_key, &key.1[..])?;
|
||||
let value = self.state.child_storage(storage_key, child_info, &key.1[..])?;
|
||||
RwLockUpgradableReadGuard::upgrade(local_cache).child_storage.insert(key, value.clone());
|
||||
Ok(value)
|
||||
}
|
||||
@@ -540,20 +546,35 @@ impl<H: Hasher, S: StateBackend<H>, B: BlockT> StateBackend<H> for CachingState<
|
||||
Ok(self.storage(key)?.is_some())
|
||||
}
|
||||
|
||||
fn exists_child_storage(&self, storage_key: &[u8], key: &[u8]) -> Result<bool, Self::Error> {
|
||||
self.state.exists_child_storage(storage_key, key)
|
||||
fn exists_child_storage(
|
||||
&self,
|
||||
storage_key: &[u8],
|
||||
child_info: ChildInfo,
|
||||
key: &[u8],
|
||||
) -> Result<bool, Self::Error> {
|
||||
self.state.exists_child_storage(storage_key, child_info, key)
|
||||
}
|
||||
|
||||
fn for_keys_in_child_storage<F: FnMut(&[u8])>(
|
||||
&self,
|
||||
storage_key: &[u8],
|
||||
child_info: ChildInfo,
|
||||
f: F,
|
||||
) {
|
||||
self.state.for_keys_in_child_storage(storage_key, child_info, f)
|
||||
}
|
||||
|
||||
fn next_storage_key(&self, key: &[u8]) -> Result<Option<Vec<u8>>, Self::Error> {
|
||||
self.state.next_storage_key(key)
|
||||
}
|
||||
|
||||
fn next_child_storage_key(&self, storage_key: &[u8], key: &[u8]) -> Result<Option<Vec<u8>>, Self::Error> {
|
||||
self.state.next_child_storage_key(storage_key, key)
|
||||
}
|
||||
|
||||
fn for_keys_in_child_storage<F: FnMut(&[u8])>(&self, storage_key: &[u8], f: F) {
|
||||
self.state.for_keys_in_child_storage(storage_key, f)
|
||||
fn next_child_storage_key(
|
||||
&self,
|
||||
storage_key: &[u8],
|
||||
child_info: ChildInfo,
|
||||
key: &[u8],
|
||||
) -> Result<Option<Vec<u8>>, Self::Error> {
|
||||
self.state.next_child_storage_key(storage_key, child_info, key)
|
||||
}
|
||||
|
||||
fn for_keys_with_prefix<F: FnMut(&[u8])>(&self, prefix: &[u8], f: F) {
|
||||
@@ -564,8 +585,14 @@ impl<H: Hasher, S: StateBackend<H>, B: BlockT> StateBackend<H> for CachingState<
|
||||
self.state.for_key_values_with_prefix(prefix, f)
|
||||
}
|
||||
|
||||
fn for_child_keys_with_prefix<F: FnMut(&[u8])>(&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<F: FnMut(&[u8])>(
|
||||
&self,
|
||||
storage_key: &[u8],
|
||||
child_info: ChildInfo,
|
||||
prefix: &[u8],
|
||||
f: F,
|
||||
) {
|
||||
self.state.for_child_keys_with_prefix(storage_key, child_info, prefix, f)
|
||||
}
|
||||
|
||||
fn storage_root<I>(&self, delta: I) -> (H::Out, Self::Transaction)
|
||||
@@ -576,12 +603,17 @@ impl<H: Hasher, S: StateBackend<H>, B: BlockT> StateBackend<H> for CachingState<
|
||||
self.state.storage_root(delta)
|
||||
}
|
||||
|
||||
fn child_storage_root<I>(&self, storage_key: &[u8], delta: I) -> (H::Out, bool, Self::Transaction)
|
||||
fn child_storage_root<I>(
|
||||
&self,
|
||||
storage_key: &[u8],
|
||||
child_info: ChildInfo,
|
||||
delta: I,
|
||||
) -> (H::Out, bool, Self::Transaction)
|
||||
where
|
||||
I: IntoIterator<Item=(Vec<u8>, Option<Vec<u8>>)>,
|
||||
H::Out: Ord
|
||||
{
|
||||
self.state.child_storage_root(storage_key, delta)
|
||||
self.state.child_storage_root(storage_key, child_info, delta)
|
||||
}
|
||||
|
||||
fn pairs(&self) -> Vec<(Vec<u8>, Vec<u8>)> {
|
||||
@@ -592,8 +624,13 @@ impl<H: Hasher, S: StateBackend<H>, B: BlockT> StateBackend<H> for CachingState<
|
||||
self.state.keys(prefix)
|
||||
}
|
||||
|
||||
fn child_keys(&self, child_key: &[u8], prefix: &[u8]) -> Vec<Vec<u8>> {
|
||||
self.state.child_keys(child_key, prefix)
|
||||
fn child_keys(
|
||||
&self,
|
||||
storage_key: &[u8],
|
||||
child_info: ChildInfo,
|
||||
prefix: &[u8],
|
||||
) -> Vec<Vec<u8>> {
|
||||
self.state.child_keys(storage_key, child_info, prefix)
|
||||
}
|
||||
|
||||
fn as_trie_backend(&mut self) -> Option<&TrieBackend<Self::TrieBackendStorage, H>> {
|
||||
|
||||
Reference in New Issue
Block a user