mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-13 17:31:05 +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:
@@ -20,7 +20,7 @@ use std::sync::Arc;
|
||||
use std::collections::HashMap;
|
||||
use primitives::ChangesTrieConfiguration;
|
||||
use primitives::offchain::OffchainStorage;
|
||||
use sp_runtime::{generic::BlockId, Justification, StorageOverlay, ChildrenStorageOverlay};
|
||||
use sp_runtime::{generic::BlockId, Justification, Storage};
|
||||
use sp_runtime::traits::{Block as BlockT, NumberFor};
|
||||
use state_machine::backend::Backend as StateBackend;
|
||||
use state_machine::{ChangesTrieStorage as StateChangesTrieStorage, ChangesTrieTransaction};
|
||||
@@ -134,7 +134,7 @@ pub trait BlockImportOperation<Block, H> where
|
||||
fn update_db_storage(&mut self, update: <Self::State as StateBackend<H>>::Transaction) -> sp_blockchain::Result<()>;
|
||||
|
||||
/// Inject storage data into the database replacing any existing data.
|
||||
fn reset_storage(&mut self, top: StorageOverlay, children: ChildrenStorageOverlay) -> sp_blockchain::Result<H::Out>;
|
||||
fn reset_storage(&mut self, storage: Storage) -> sp_blockchain::Result<H::Out>;
|
||||
|
||||
/// Set storage changes.
|
||||
fn update_storage(
|
||||
|
||||
@@ -26,7 +26,7 @@ use sp_runtime::{
|
||||
},
|
||||
generic::BlockId
|
||||
};
|
||||
use primitives::{ChangesTrieConfiguration};
|
||||
use primitives::ChangesTrieConfiguration;
|
||||
use state_machine::StorageProof;
|
||||
use sp_blockchain::{
|
||||
HeaderMetadata, well_known_cache_keys, HeaderBackend, Cache as BlockchainCache,
|
||||
@@ -81,6 +81,11 @@ pub struct RemoteReadChildRequest<Header: HeaderT> {
|
||||
pub header: Header,
|
||||
/// Storage key for child.
|
||||
pub storage_key: Vec<u8>,
|
||||
/// Child trie source information.
|
||||
pub child_info: Vec<u8>,
|
||||
/// Child type, its required to resolve `child_info`
|
||||
/// content and choose child implementation.
|
||||
pub child_type: u32,
|
||||
/// Child storage key to read.
|
||||
pub keys: Vec<Vec<u8>>,
|
||||
/// Number of times to retry request. None means that default RETRY_COUNT is used.
|
||||
|
||||
@@ -22,8 +22,8 @@ use std::fs::File;
|
||||
use std::path::PathBuf;
|
||||
use std::rc::Rc;
|
||||
use serde::{Serialize, Deserialize};
|
||||
use primitives::storage::{StorageKey, StorageData};
|
||||
use sp_runtime::{BuildStorage, StorageOverlay, ChildrenStorageOverlay};
|
||||
use primitives::storage::{StorageKey, StorageData, ChildInfo, Storage, StorageChild};
|
||||
use sp_runtime::BuildStorage;
|
||||
use serde_json as json;
|
||||
use crate::RuntimeGenesis;
|
||||
use network::Multiaddr;
|
||||
@@ -71,36 +71,62 @@ impl<G: RuntimeGenesis> GenesisSource<G> {
|
||||
}
|
||||
|
||||
impl<'a, G: RuntimeGenesis, E> BuildStorage for &'a ChainSpec<G, E> {
|
||||
fn build_storage(&self) -> Result<(StorageOverlay, ChildrenStorageOverlay), String> {
|
||||
fn build_storage(&self) -> Result<Storage, String> {
|
||||
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(),
|
||||
)).collect(),
|
||||
)),
|
||||
Genesis::Raw(RawGenesis { top: map, children: children_map }) => Ok(Storage {
|
||||
top: map.into_iter().map(|(k, v)| (k.0, v.0)).collect(),
|
||||
children: children_map.into_iter().map(|(sk, child_content)| {
|
||||
let child_info = ChildInfo::resolve_child_info(
|
||||
child_content.child_type,
|
||||
child_content.child_info.as_slice(),
|
||||
).expect("chainspec contains correct content").to_owned();
|
||||
(
|
||||
sk.0,
|
||||
StorageChild {
|
||||
data: child_content.data.into_iter().map(|(k, v)| (k.0, v.0)).collect(),
|
||||
child_info,
|
||||
},
|
||||
)
|
||||
}).collect(),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
fn assimilate_storage(
|
||||
&self,
|
||||
_: &mut (StorageOverlay, ChildrenStorageOverlay)
|
||||
_: &mut Storage,
|
||||
) -> Result<(), String> {
|
||||
Err("`assimilate_storage` not implemented for `ChainSpec`.".into())
|
||||
}
|
||||
}
|
||||
|
||||
type GenesisStorage = HashMap<StorageKey, StorageData>;
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[serde(deny_unknown_fields)]
|
||||
struct ChildRawStorage {
|
||||
data: GenesisStorage,
|
||||
child_info: Vec<u8>,
|
||||
child_type: u32,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[serde(deny_unknown_fields)]
|
||||
/// Storage content for genesis block.
|
||||
struct RawGenesis {
|
||||
pub top: GenesisStorage,
|
||||
pub children: HashMap<StorageKey, ChildRawStorage>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[serde(deny_unknown_fields)]
|
||||
enum Genesis<G> {
|
||||
Runtime(G),
|
||||
Raw(
|
||||
HashMap<StorageKey, StorageData>,
|
||||
HashMap<StorageKey, HashMap<StorageKey, StorageData>>,
|
||||
),
|
||||
Raw(RawGenesis),
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
@@ -255,19 +281,26 @@ impl<G: RuntimeGenesis, E: serde::Serialize> ChainSpec<G, E> {
|
||||
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)| (
|
||||
let children = storage.children.into_iter()
|
||||
.map(|(sk, child)| {
|
||||
let info = child.child_info.as_ref();
|
||||
let (info, ci_type) = info.info();
|
||||
(
|
||||
StorageKey(sk),
|
||||
child.into_iter()
|
||||
.map(|(k, v)| (StorageKey(k), StorageData(v)))
|
||||
.collect(),
|
||||
))
|
||||
ChildRawStorage {
|
||||
data: child.data.into_iter()
|
||||
.map(|(k, v)| (StorageKey(k), StorageData(v)))
|
||||
.collect(),
|
||||
child_info: info.to_vec(),
|
||||
child_type: ci_type,
|
||||
},
|
||||
)})
|
||||
.collect();
|
||||
|
||||
Genesis::Raw(top, children)
|
||||
Genesis::Raw(RawGenesis { top, children })
|
||||
},
|
||||
(_, genesis) => genesis,
|
||||
};
|
||||
@@ -290,9 +323,9 @@ mod tests {
|
||||
impl BuildStorage for Genesis {
|
||||
fn assimilate_storage(
|
||||
&self,
|
||||
storage: &mut (StorageOverlay, ChildrenStorageOverlay),
|
||||
storage: &mut Storage,
|
||||
) -> Result<(), String> {
|
||||
storage.0.extend(
|
||||
storage.top.extend(
|
||||
self.0.iter().map(|(a, b)| (a.clone().into_bytes(), b.clone().into_bytes()))
|
||||
);
|
||||
Ok(())
|
||||
|
||||
@@ -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>> {
|
||||
|
||||
@@ -217,38 +217,6 @@ impl_wasm_host_interface! {
|
||||
Ok(sp_io::storage::set(&key, &value))
|
||||
}
|
||||
|
||||
ext_set_child_storage(
|
||||
storage_key_data: Pointer<u8>,
|
||||
storage_key_len: WordSize,
|
||||
key_data: Pointer<u8>,
|
||||
key_len: WordSize,
|
||||
value_data: Pointer<u8>,
|
||||
value_len: WordSize,
|
||||
) {
|
||||
let storage_key = context.read_memory(storage_key_data, storage_key_len)
|
||||
.map_err(|_| "Invalid attempt to determine storage_key in ext_set_child_storage")?;
|
||||
let key = context.read_memory(key_data, key_len)
|
||||
.map_err(|_| "Invalid attempt to determine key in ext_set_child_storage")?;
|
||||
let value = context.read_memory(value_data, value_len)
|
||||
.map_err(|_| "Invalid attempt to determine value in ext_set_child_storage")?;
|
||||
|
||||
Ok(sp_io::storage::child_set(&storage_key, &key, &value))
|
||||
}
|
||||
|
||||
ext_clear_child_storage(
|
||||
storage_key_data: Pointer<u8>,
|
||||
storage_key_len: WordSize,
|
||||
key_data: Pointer<u8>,
|
||||
key_len: WordSize,
|
||||
) {
|
||||
let storage_key = context.read_memory(storage_key_data, storage_key_len)
|
||||
.map_err(|_| "Invalid attempt to determine storage_key in ext_clear_child_storage")?;
|
||||
let key = context.read_memory(key_data, key_len)
|
||||
.map_err(|_| "Invalid attempt to determine key in ext_clear_child_storage")?;
|
||||
|
||||
Ok(sp_io::storage::child_clear(&storage_key, &key))
|
||||
}
|
||||
|
||||
ext_clear_storage(key_data: Pointer<u8>, key_len: WordSize) {
|
||||
let key = context.read_memory(key_data, key_len)
|
||||
.map_err(|_| "Invalid attempt to determine key in ext_clear_storage")?;
|
||||
@@ -261,45 +229,12 @@ impl_wasm_host_interface! {
|
||||
Ok(if sp_io::storage::exists(&key) { 1 } else { 0 })
|
||||
}
|
||||
|
||||
ext_exists_child_storage(
|
||||
storage_key_data: Pointer<u8>,
|
||||
storage_key_len: WordSize,
|
||||
key_data: Pointer<u8>,
|
||||
key_len: WordSize,
|
||||
) -> u32 {
|
||||
let storage_key = context.read_memory(storage_key_data, storage_key_len)
|
||||
.map_err(|_| "Invalid attempt to determine storage_key in ext_exists_child_storage")?;
|
||||
let key = context.read_memory(key_data, key_len)
|
||||
.map_err(|_| "Invalid attempt to determine key in ext_exists_child_storage")?;
|
||||
|
||||
Ok(if sp_io::storage::child_exists(&storage_key, &key) { 1 } else { 0 })
|
||||
}
|
||||
|
||||
ext_clear_prefix(prefix_data: Pointer<u8>, prefix_len: WordSize) {
|
||||
let prefix = context.read_memory(prefix_data, prefix_len)
|
||||
.map_err(|_| "Invalid attempt to determine prefix in ext_clear_prefix")?;
|
||||
Ok(sp_io::storage::clear_prefix(&prefix))
|
||||
}
|
||||
|
||||
ext_clear_child_prefix(
|
||||
storage_key_data: Pointer<u8>,
|
||||
storage_key_len: WordSize,
|
||||
prefix_data: Pointer<u8>,
|
||||
prefix_len: WordSize,
|
||||
) {
|
||||
let storage_key = context.read_memory(storage_key_data, storage_key_len)
|
||||
.map_err(|_| "Invalid attempt to determine storage_key in ext_clear_child_prefix")?;
|
||||
let prefix = context.read_memory(prefix_data, prefix_len)
|
||||
.map_err(|_| "Invalid attempt to determine prefix in ext_clear_child_prefix")?;
|
||||
Ok(sp_io::storage::child_clear_prefix(&storage_key, &prefix))
|
||||
}
|
||||
|
||||
ext_kill_child_storage(storage_key_data: Pointer<u8>, storage_key_len: WordSize) {
|
||||
let storage_key = context.read_memory(storage_key_data, storage_key_len)
|
||||
.map_err(|_| "Invalid attempt to determine storage_key in ext_kill_child_storage")?;
|
||||
Ok(sp_io::storage::child_storage_kill(&storage_key))
|
||||
}
|
||||
|
||||
ext_get_allocated_storage(
|
||||
key_data: Pointer<u8>,
|
||||
key_len: WordSize,
|
||||
@@ -322,32 +257,6 @@ impl_wasm_host_interface! {
|
||||
}
|
||||
}
|
||||
|
||||
ext_get_allocated_child_storage(
|
||||
storage_key_data: Pointer<u8>,
|
||||
storage_key_len: WordSize,
|
||||
key_data: Pointer<u8>,
|
||||
key_len: WordSize,
|
||||
written_out: Pointer<u32>,
|
||||
) -> Pointer<u8> {
|
||||
let storage_key = context.read_memory(storage_key_data, storage_key_len)
|
||||
.map_err(|_| "Invalid attempt to determine storage_key in ext_get_allocated_child_storage")?;
|
||||
let key = context.read_memory(key_data, key_len)
|
||||
.map_err(|_| "Invalid attempt to determine key in ext_get_allocated_child_storage")?;
|
||||
|
||||
if let Some(value) = sp_io::storage::child_get(&storage_key, &key) {
|
||||
let offset = context.allocate_memory(value.len() as u32)?;
|
||||
context.write_memory(offset, &value)
|
||||
.map_err(|_| "Invalid attempt to set memory in ext_get_allocated_child_storage")?;
|
||||
context.write_primitive(written_out, value.len() as u32)
|
||||
.map_err(|_| "Invalid attempt to write written_out in ext_get_allocated_child_storage")?;
|
||||
Ok(offset)
|
||||
} else {
|
||||
context.write_primitive(written_out, u32::max_value())
|
||||
.map_err(|_| "Invalid attempt to write failed written_out in ext_get_allocated_child_storage")?;
|
||||
Ok(Pointer::null())
|
||||
}
|
||||
}
|
||||
|
||||
ext_get_storage_into(
|
||||
key_data: Pointer<u8>,
|
||||
key_len: WordSize,
|
||||
@@ -369,53 +278,11 @@ impl_wasm_host_interface! {
|
||||
}
|
||||
}
|
||||
|
||||
ext_get_child_storage_into(
|
||||
storage_key_data: Pointer<u8>,
|
||||
storage_key_len: WordSize,
|
||||
key_data: Pointer<u8>,
|
||||
key_len: WordSize,
|
||||
value_data: Pointer<u8>,
|
||||
value_len: WordSize,
|
||||
value_offset: WordSize,
|
||||
) -> WordSize {
|
||||
let storage_key = context.read_memory(storage_key_data, storage_key_len)
|
||||
.map_err(|_| "Invalid attempt to determine storage_key in ext_get_child_storage_into")?;
|
||||
let key = context.read_memory(key_data, key_len)
|
||||
.map_err(|_| "Invalid attempt to get key in ext_get_child_storage_into")?;
|
||||
|
||||
if let Some(value) = sp_io::storage::child_get(&storage_key, &key) {
|
||||
let data = &value[value.len().min(value_offset as usize)..];
|
||||
let written = std::cmp::min(value_len as usize, data.len());
|
||||
context.write_memory(value_data, &data[..written])
|
||||
.map_err(|_| "Invalid attempt to get value in ext_get_child_storage_into")?;
|
||||
Ok(value.len() as u32)
|
||||
} else {
|
||||
Ok(u32::max_value())
|
||||
}
|
||||
}
|
||||
|
||||
ext_storage_root(result: Pointer<u8>) {
|
||||
context.write_memory(result, sp_io::storage::root().as_ref())
|
||||
.map_err(|_| "Invalid attempt to set memory in ext_storage_root".into())
|
||||
}
|
||||
|
||||
ext_child_storage_root(
|
||||
storage_key_data: Pointer<u8>,
|
||||
storage_key_len: WordSize,
|
||||
written_out: Pointer<u32>,
|
||||
) -> Pointer<u8> {
|
||||
let storage_key = context.read_memory(storage_key_data, storage_key_len)
|
||||
.map_err(|_| "Invalid attempt to determine storage_key in ext_child_storage_root")?;
|
||||
let value = sp_io::storage::child_root(&storage_key);
|
||||
|
||||
let offset = context.allocate_memory(value.len() as u32)?;
|
||||
context.write_memory(offset, &value)
|
||||
.map_err(|_| "Invalid attempt to set memory in ext_child_storage_root")?;
|
||||
context.write_primitive(written_out, value.len() as u32)
|
||||
.map_err(|_| "Invalid attempt to write written_out in ext_child_storage_root")?;
|
||||
Ok(offset)
|
||||
}
|
||||
|
||||
ext_storage_changes_root(
|
||||
parent_hash_data: Pointer<u8>,
|
||||
_len: WordSize,
|
||||
|
||||
@@ -128,11 +128,14 @@ fn storage_should_work(wasm_method: WasmExecutionMethod) {
|
||||
assert_eq!(output, b"all ok!".to_vec().encode());
|
||||
}
|
||||
|
||||
let expected = TestExternalities::new((map![
|
||||
let expected = TestExternalities::new(primitives::storage::Storage {
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -162,11 +165,14 @@ fn clear_prefix_should_work(wasm_method: WasmExecutionMethod) {
|
||||
assert_eq!(output, b"all ok!".to_vec().encode());
|
||||
}
|
||||
|
||||
let expected = TestExternalities::new((map![
|
||||
let expected = TestExternalities::new(primitives::storage::Storage {
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@@ -291,7 +291,7 @@ impl AuthoritySetForFinalityProver<Block> for TestApi {
|
||||
fn prove_authorities(&self, block: &BlockId<Block>) -> Result<StorageProof> {
|
||||
let authorities = self.authorities(block)?;
|
||||
let backend = <InMemory<Blake2Hasher>>::from(vec![
|
||||
(None, b"authorities".to_vec(), Some(authorities.encode()))
|
||||
(None, vec![(b"authorities".to_vec(), Some(authorities.encode()))])
|
||||
]);
|
||||
let proof = prove_read(backend, vec![b"authorities"])
|
||||
.expect("failure proving read from in-memory storage backend");
|
||||
|
||||
@@ -23,7 +23,8 @@ use consensus::{BlockImport, BlockStatus, Error as ConsensusError};
|
||||
use sp_runtime::traits::{Block as BlockT, Header as HeaderT};
|
||||
use sp_runtime::generic::{BlockId};
|
||||
use sp_runtime::Justification;
|
||||
use primitives::{H256, Blake2Hasher, storage::StorageKey};
|
||||
use primitives::{H256, Blake2Hasher};
|
||||
use primitives::storage::{StorageKey, ChildInfo};
|
||||
|
||||
/// Local client abstraction for the network.
|
||||
pub trait Client<Block: BlockT>: Send + Sync {
|
||||
@@ -57,6 +58,7 @@ pub trait Client<Block: BlockT>: Send + Sync {
|
||||
&self,
|
||||
block: &Block::Hash,
|
||||
storage_key: &[u8],
|
||||
child_info: ChildInfo,
|
||||
keys: &[Vec<u8>],
|
||||
) -> Result<StorageProof, Error>;
|
||||
|
||||
@@ -135,10 +137,11 @@ impl<B, E, Block, RA> Client<Block> for SubstrateClient<B, E, Block, RA> where
|
||||
&self,
|
||||
block: &Block::Hash,
|
||||
storage_key: &[u8],
|
||||
child_info: ChildInfo,
|
||||
keys: &[Vec<u8>],
|
||||
) -> Result<StorageProof, Error> {
|
||||
(self as &SubstrateClient<B, E, Block, RA>)
|
||||
.read_child_proof(&BlockId::Hash(block.clone()), storage_key, keys)
|
||||
.read_child_proof(&BlockId::Hash(block.clone()), storage_key, child_info, keys)
|
||||
}
|
||||
|
||||
fn execution_proof(&self, block: &Block::Hash, method: &str, data: &[u8]) -> Result<(Vec<u8>, StorageProof), Error> {
|
||||
|
||||
@@ -24,7 +24,7 @@ use libp2p::{Multiaddr, PeerId};
|
||||
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::storage::{StorageKey, ChildInfo};
|
||||
use consensus::{
|
||||
BlockOrigin,
|
||||
block_validation::BlockAnnounceValidator,
|
||||
@@ -238,12 +238,16 @@ impl<'a, B: BlockT> LightDispatchNetwork<B> for LightDispatchIn<'a> {
|
||||
id: RequestId,
|
||||
block: <B as BlockT>::Hash,
|
||||
storage_key: Vec<u8>,
|
||||
child_info: Vec<u8>,
|
||||
child_type: u32,
|
||||
keys: Vec<Vec<u8>>,
|
||||
) {
|
||||
let message: Message<B> = message::generic::Message::RemoteReadChildRequest(message::RemoteReadChildRequest {
|
||||
id,
|
||||
block,
|
||||
storage_key,
|
||||
child_info,
|
||||
child_type,
|
||||
keys,
|
||||
});
|
||||
|
||||
@@ -1554,23 +1558,37 @@ impl<B: BlockT, S: NetworkSpecialization<B>, H: ExHashT> Protocol<B, S, H> {
|
||||
|
||||
trace!(target: "sync", "Remote read child request {} from {} ({} {} at {})",
|
||||
request.id, who, request.storage_key.to_hex::<String>(), keys_str(), request.block);
|
||||
let proof = match self.context_data.chain.read_child_proof(
|
||||
&request.block,
|
||||
&request.storage_key,
|
||||
&request.keys,
|
||||
) {
|
||||
Ok(proof) => proof,
|
||||
Err(error) => {
|
||||
trace!(target: "sync", "Remote read child request {} from {} ({} {} at {}) failed with: {}",
|
||||
request.id,
|
||||
who,
|
||||
request.storage_key.to_hex::<String>(),
|
||||
keys_str(),
|
||||
request.block,
|
||||
error
|
||||
);
|
||||
StorageProof::empty()
|
||||
let proof = if let Some(child_info) = ChildInfo::resolve_child_info(request.child_type, &request.child_info[..]) {
|
||||
match self.context_data.chain.read_child_proof(
|
||||
&request.block,
|
||||
&request.storage_key,
|
||||
child_info,
|
||||
&request.keys,
|
||||
) {
|
||||
Ok(proof) => proof,
|
||||
Err(error) => {
|
||||
trace!(target: "sync", "Remote read child request {} from {} ({} {} at {}) failed with: {}",
|
||||
request.id,
|
||||
who,
|
||||
request.storage_key.to_hex::<String>(),
|
||||
keys_str(),
|
||||
request.block,
|
||||
error
|
||||
);
|
||||
StorageProof::empty()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
trace!(target: "sync", "Remote read child request {} from {} ({} {} at {}) failed with: {}",
|
||||
request.id,
|
||||
who,
|
||||
request.storage_key.to_hex::<String>(),
|
||||
keys_str(),
|
||||
request.block,
|
||||
"invalid child info and type",
|
||||
);
|
||||
|
||||
StorageProof::empty()
|
||||
};
|
||||
self.send_message(
|
||||
&who,
|
||||
|
||||
@@ -69,6 +69,8 @@ pub trait LightDispatchNetwork<B: BlockT> {
|
||||
id: RequestId,
|
||||
block: <B as BlockT>::Hash,
|
||||
storage_key: Vec<u8>,
|
||||
child_info: Vec<u8>,
|
||||
child_type: u32,
|
||||
keys: Vec<Vec<u8>>,
|
||||
);
|
||||
|
||||
@@ -622,6 +624,8 @@ impl<Block: BlockT> Request<Block> {
|
||||
self.id,
|
||||
data.block,
|
||||
data.storage_key.clone(),
|
||||
data.child_info.clone(),
|
||||
data.child_type,
|
||||
data.keys.clone(),
|
||||
),
|
||||
RequestData::RemoteCall(ref data, _) =>
|
||||
@@ -677,6 +681,7 @@ pub mod tests {
|
||||
use std::sync::Arc;
|
||||
use std::time::Instant;
|
||||
use futures::{Future, sync::oneshot};
|
||||
use primitives::storage::ChildInfo;
|
||||
use sp_runtime::traits::{Block as BlockT, NumberFor, Header as HeaderT};
|
||||
use sp_blockchain::{Error as ClientError, Result as ClientResult};
|
||||
use client_api::{FetchChecker, RemoteHeaderRequest,
|
||||
@@ -808,7 +813,7 @@ pub mod tests {
|
||||
fn send_header_request(&mut self, _: &PeerId, _: RequestId, _: <<B as BlockT>::Header as HeaderT>::Number) {}
|
||||
fn send_read_request(&mut self, _: &PeerId, _: RequestId, _: <B as BlockT>::Hash, _: Vec<Vec<u8>>) {}
|
||||
fn send_read_child_request(&mut self, _: &PeerId, _: RequestId, _: <B as BlockT>::Hash, _: Vec<u8>,
|
||||
_: Vec<Vec<u8>>) {}
|
||||
_: Vec<u8>, _: u32, _: Vec<Vec<u8>>) {}
|
||||
fn send_call_request(&mut self, _: &PeerId, _: RequestId, _: <B as BlockT>::Hash, _: String, _: Vec<u8>) {}
|
||||
fn send_changes_request(&mut self, _: &PeerId, _: RequestId, _: <B as BlockT>::Hash, _: <B as BlockT>::Hash,
|
||||
_: <B as BlockT>::Hash, _: <B as BlockT>::Hash, _: Option<Vec<u8>>, _: Vec<u8>) {}
|
||||
@@ -1027,10 +1032,14 @@ pub mod tests {
|
||||
light_dispatch.on_connect(&mut network_interface, peer0.clone(), Roles::FULL, 1000);
|
||||
|
||||
let (tx, response) = oneshot::channel();
|
||||
let child_info = ChildInfo::new_default(b"unique_id_1");
|
||||
let (child_info, child_type) = child_info.info();
|
||||
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_info: child_info.to_vec(),
|
||||
child_type,
|
||||
keys: vec![b":key".to_vec()],
|
||||
retry_count: None,
|
||||
}, tx));
|
||||
|
||||
@@ -368,6 +368,11 @@ pub mod generic {
|
||||
pub block: H,
|
||||
/// Child Storage key.
|
||||
pub storage_key: Vec<u8>,
|
||||
/// Child trie source information.
|
||||
pub child_info: Vec<u8>,
|
||||
/// Child type, its required to resolve `child_info`
|
||||
/// content and choose child implementation.
|
||||
pub child_type: u32,
|
||||
/// Storage key.
|
||||
pub keys: Vec<Vec<u8>>,
|
||||
}
|
||||
|
||||
@@ -60,6 +60,8 @@ pub trait StateApi<Hash> {
|
||||
fn child_storage_keys(
|
||||
&self,
|
||||
child_storage_key: StorageKey,
|
||||
child_info: StorageKey,
|
||||
child_type: u32,
|
||||
prefix: StorageKey,
|
||||
hash: Option<Hash>
|
||||
) -> FutureResult<Vec<StorageKey>>;
|
||||
@@ -69,6 +71,8 @@ pub trait StateApi<Hash> {
|
||||
fn child_storage(
|
||||
&self,
|
||||
child_storage_key: StorageKey,
|
||||
child_info: StorageKey,
|
||||
child_type: u32,
|
||||
key: StorageKey,
|
||||
hash: Option<Hash>
|
||||
) -> FutureResult<Option<StorageData>>;
|
||||
@@ -78,6 +82,8 @@ pub trait StateApi<Hash> {
|
||||
fn child_storage_hash(
|
||||
&self,
|
||||
child_storage_key: StorageKey,
|
||||
child_info: StorageKey,
|
||||
child_type: u32,
|
||||
key: StorageKey,
|
||||
hash: Option<Hash>
|
||||
) -> FutureResult<Option<Hash>>;
|
||||
@@ -87,6 +93,8 @@ pub trait StateApi<Hash> {
|
||||
fn child_storage_size(
|
||||
&self,
|
||||
child_storage_key: StorageKey,
|
||||
child_info: StorageKey,
|
||||
child_type: u32,
|
||||
key: StorageKey,
|
||||
hash: Option<Hash>
|
||||
) -> FutureResult<Option<u64>>;
|
||||
|
||||
@@ -95,6 +95,8 @@ pub trait StateBackend<B, E, Block: BlockT, RA>: Send + Sync + 'static
|
||||
&self,
|
||||
block: Option<Block::Hash>,
|
||||
child_storage_key: StorageKey,
|
||||
child_info: StorageKey,
|
||||
child_type: u32,
|
||||
prefix: StorageKey,
|
||||
) -> FutureResult<Vec<StorageKey>>;
|
||||
|
||||
@@ -103,6 +105,8 @@ pub trait StateBackend<B, E, Block: BlockT, RA>: Send + Sync + 'static
|
||||
&self,
|
||||
block: Option<Block::Hash>,
|
||||
child_storage_key: StorageKey,
|
||||
child_info: StorageKey,
|
||||
child_type: u32,
|
||||
key: StorageKey,
|
||||
) -> FutureResult<Option<StorageData>>;
|
||||
|
||||
@@ -111,6 +115,8 @@ pub trait StateBackend<B, E, Block: BlockT, RA>: Send + Sync + 'static
|
||||
&self,
|
||||
block: Option<Block::Hash>,
|
||||
child_storage_key: StorageKey,
|
||||
child_info: StorageKey,
|
||||
child_type: u32,
|
||||
key: StorageKey,
|
||||
) -> FutureResult<Option<Block::Hash>>;
|
||||
|
||||
@@ -119,9 +125,11 @@ pub trait StateBackend<B, E, Block: BlockT, RA>: Send + Sync + 'static
|
||||
&self,
|
||||
block: Option<Block::Hash>,
|
||||
child_storage_key: StorageKey,
|
||||
child_info: StorageKey,
|
||||
child_type: u32,
|
||||
key: StorageKey,
|
||||
) -> FutureResult<Option<u64>> {
|
||||
Box::new(self.child_storage(block, child_storage_key, key)
|
||||
Box::new(self.child_storage(block, child_storage_key, child_info, child_type, key)
|
||||
.map(|x| x.map(|x| x.0.len() as u64)))
|
||||
}
|
||||
|
||||
@@ -256,37 +264,45 @@ impl<B, E, Block, RA> StateApi<Block::Hash> for State<B, E, Block, RA>
|
||||
fn child_storage(
|
||||
&self,
|
||||
child_storage_key: StorageKey,
|
||||
child_info: StorageKey,
|
||||
child_type: u32,
|
||||
key: StorageKey,
|
||||
block: Option<Block::Hash>
|
||||
) -> FutureResult<Option<StorageData>> {
|
||||
self.backend.child_storage(block, child_storage_key, key)
|
||||
self.backend.child_storage(block, child_storage_key, child_info, child_type, key)
|
||||
}
|
||||
|
||||
fn child_storage_keys(
|
||||
&self,
|
||||
child_storage_key: StorageKey,
|
||||
child_info: StorageKey,
|
||||
child_type: u32,
|
||||
key_prefix: StorageKey,
|
||||
block: Option<Block::Hash>
|
||||
) -> FutureResult<Vec<StorageKey>> {
|
||||
self.backend.child_storage_keys(block, child_storage_key, key_prefix)
|
||||
self.backend.child_storage_keys(block, child_storage_key, child_info, child_type, key_prefix)
|
||||
}
|
||||
|
||||
fn child_storage_hash(
|
||||
&self,
|
||||
child_storage_key: StorageKey,
|
||||
child_info: StorageKey,
|
||||
child_type: u32,
|
||||
key: StorageKey,
|
||||
block: Option<Block::Hash>
|
||||
) -> FutureResult<Option<Block::Hash>> {
|
||||
self.backend.child_storage_hash(block, child_storage_key, key)
|
||||
self.backend.child_storage_hash(block, child_storage_key, child_info, child_type, key)
|
||||
}
|
||||
|
||||
fn child_storage_size(
|
||||
&self,
|
||||
child_storage_key: StorageKey,
|
||||
child_info: StorageKey,
|
||||
child_type: u32,
|
||||
key: StorageKey,
|
||||
block: Option<Block::Hash>
|
||||
) -> FutureResult<Option<u64>> {
|
||||
self.backend.child_storage_size(block, child_storage_key, key)
|
||||
self.backend.child_storage_size(block, child_storage_key, child_info, child_type, key)
|
||||
}
|
||||
|
||||
fn metadata(&self, block: Option<Block::Hash>) -> FutureResult<Bytes> {
|
||||
@@ -335,3 +351,9 @@ impl<B, E, Block, RA> StateApi<Block::Hash> for State<B, E, Block, RA>
|
||||
fn client_err(err: sp_blockchain::Error) -> Error {
|
||||
Error::Client(Box::new(err))
|
||||
}
|
||||
|
||||
const CHILD_RESOLUTION_ERROR: &str = "Unexpected child info and type";
|
||||
|
||||
fn child_resolution_error() -> sp_blockchain::Error {
|
||||
sp_blockchain::Error::Msg(CHILD_RESOLUTION_ERROR.to_string())
|
||||
}
|
||||
|
||||
@@ -36,7 +36,8 @@ use client::{
|
||||
Client, CallExecutor, BlockchainEvents,
|
||||
};
|
||||
use primitives::{
|
||||
H256, Blake2Hasher, Bytes, storage::{well_known_keys, StorageKey, StorageData, StorageChangeSet},
|
||||
H256, Blake2Hasher, Bytes,
|
||||
storage::{well_known_keys, StorageKey, StorageData, StorageChangeSet, ChildInfo},
|
||||
};
|
||||
use runtime_version::RuntimeVersion;
|
||||
use state_machine::ExecutionStrategy;
|
||||
@@ -47,7 +48,7 @@ use sp_runtime::{
|
||||
|
||||
use sp_api::Metadata;
|
||||
|
||||
use super::{StateBackend, error::{FutureResult, Error, Result}, client_err};
|
||||
use super::{StateBackend, error::{FutureResult, Error, Result}, client_err, child_resolution_error};
|
||||
|
||||
/// Ranges to query in state_queryStorage.
|
||||
struct QueryStorageRange<Block: BlockT> {
|
||||
@@ -287,11 +288,19 @@ impl<B, E, Block, RA> StateBackend<B, E, Block, RA> for FullState<B, E, Block, R
|
||||
&self,
|
||||
block: Option<Block::Hash>,
|
||||
child_storage_key: StorageKey,
|
||||
child_info: StorageKey,
|
||||
child_type: u32,
|
||||
prefix: StorageKey,
|
||||
) -> FutureResult<Vec<StorageKey>> {
|
||||
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| self.client.child_storage_keys(
|
||||
&BlockId::Hash(block),
|
||||
&child_storage_key,
|
||||
ChildInfo::resolve_child_info(child_type, &child_info.0[..])
|
||||
.ok_or_else(child_resolution_error)?,
|
||||
&prefix,
|
||||
))
|
||||
.map_err(client_err)))
|
||||
}
|
||||
|
||||
@@ -299,11 +308,19 @@ impl<B, E, Block, RA> StateBackend<B, E, Block, RA> for FullState<B, E, Block, R
|
||||
&self,
|
||||
block: Option<Block::Hash>,
|
||||
child_storage_key: StorageKey,
|
||||
child_info: StorageKey,
|
||||
child_type: u32,
|
||||
key: StorageKey,
|
||||
) -> FutureResult<Option<StorageData>> {
|
||||
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| self.client.child_storage(
|
||||
&BlockId::Hash(block),
|
||||
&child_storage_key,
|
||||
ChildInfo::resolve_child_info(child_type, &child_info.0[..])
|
||||
.ok_or_else(child_resolution_error)?,
|
||||
&key,
|
||||
))
|
||||
.map_err(client_err)))
|
||||
}
|
||||
|
||||
@@ -311,11 +328,19 @@ impl<B, E, Block, RA> StateBackend<B, E, Block, RA> for FullState<B, E, Block, R
|
||||
&self,
|
||||
block: Option<Block::Hash>,
|
||||
child_storage_key: StorageKey,
|
||||
child_info: StorageKey,
|
||||
child_type: u32,
|
||||
key: StorageKey,
|
||||
) -> FutureResult<Option<Block::Hash>> {
|
||||
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| self.client.child_storage_hash(
|
||||
&BlockId::Hash(block),
|
||||
&child_storage_key,
|
||||
ChildInfo::resolve_child_info(child_type, &child_info.0[..])
|
||||
.ok_or_else(child_resolution_error)?,
|
||||
&key,
|
||||
))
|
||||
.map_err(client_err)))
|
||||
}
|
||||
|
||||
|
||||
@@ -236,6 +236,8 @@ impl<Block, F, B, E, RA> StateBackend<B, E, Block, RA> for LightState<Block, F,
|
||||
&self,
|
||||
_block: Option<Block::Hash>,
|
||||
_child_storage_key: StorageKey,
|
||||
_child_info: StorageKey,
|
||||
_child_type: u32,
|
||||
_prefix: StorageKey,
|
||||
) -> FutureResult<Vec<StorageKey>> {
|
||||
Box::new(result(Err(client_err(ClientError::NotAvailableOnLightClient))))
|
||||
@@ -245,6 +247,8 @@ impl<Block, F, B, E, RA> StateBackend<B, E, Block, RA> for LightState<Block, F,
|
||||
&self,
|
||||
block: Option<Block::Hash>,
|
||||
child_storage_key: StorageKey,
|
||||
child_info: StorageKey,
|
||||
child_type: u32,
|
||||
key: StorageKey,
|
||||
) -> FutureResult<Option<StorageData>> {
|
||||
let block = self.block_or_best(block);
|
||||
@@ -255,6 +259,8 @@ impl<Block, F, B, E, RA> StateBackend<B, E, Block, RA> for LightState<Block, F,
|
||||
block,
|
||||
header,
|
||||
storage_key: child_storage_key.0,
|
||||
child_info: child_info.0,
|
||||
child_type,
|
||||
keys: vec![key.0.clone()],
|
||||
retry_count: Default::default(),
|
||||
}).then(move |result| ready(result
|
||||
@@ -275,10 +281,12 @@ impl<Block, F, B, E, RA> StateBackend<B, E, Block, RA> for LightState<Block, F,
|
||||
&self,
|
||||
block: Option<Block::Hash>,
|
||||
child_storage_key: StorageKey,
|
||||
child_info: StorageKey,
|
||||
child_type: u32,
|
||||
key: StorageKey,
|
||||
) -> FutureResult<Option<Block::Hash>> {
|
||||
Box::new(self
|
||||
.child_storage(block, child_storage_key, key)
|
||||
.child_storage(block, child_storage_key, child_info, child_type, key)
|
||||
.and_then(|maybe_storage|
|
||||
result(Ok(maybe_storage.map(|storage| Blake2Hasher::hash(&storage.0))))
|
||||
)
|
||||
|
||||
@@ -21,7 +21,7 @@ use self::error::Error;
|
||||
use std::sync::Arc;
|
||||
use assert_matches::assert_matches;
|
||||
use futures01::stream::Stream;
|
||||
use primitives::storage::well_known_keys;
|
||||
use primitives::storage::{well_known_keys, ChildInfo};
|
||||
use primitives::hash::H256;
|
||||
use sp_io::hashing::blake2_256;
|
||||
use test_client::{
|
||||
@@ -30,6 +30,8 @@ use test_client::{
|
||||
runtime,
|
||||
};
|
||||
|
||||
const CHILD_INFO: ChildInfo<'static> = ChildInfo::new_default(b"unique_id");
|
||||
|
||||
#[test]
|
||||
fn should_return_storage() {
|
||||
const KEY: &[u8] = b":mock";
|
||||
@@ -40,12 +42,14 @@ fn should_return_storage() {
|
||||
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(STORAGE_KEY.to_vec(), CHILD_INFO, 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())));
|
||||
let key = StorageKey(KEY.to_vec());
|
||||
let storage_key = StorageKey(STORAGE_KEY.to_vec());
|
||||
let (child_info, child_type) = CHILD_INFO.info();
|
||||
let child_info = StorageKey(child_info.to_vec());
|
||||
|
||||
assert_eq!(
|
||||
client.storage(key.clone(), Some(genesis_hash).into()).wait()
|
||||
@@ -63,7 +67,7 @@ fn should_return_storage() {
|
||||
);
|
||||
assert_eq!(
|
||||
core.block_on(
|
||||
client.child_storage(storage_key, key, Some(genesis_hash).into())
|
||||
client.child_storage(storage_key, child_info, child_type, key, Some(genesis_hash).into())
|
||||
.map(|x| x.map(|x| x.0.len()))
|
||||
).unwrap().unwrap() as usize,
|
||||
CHILD_VALUE.len(),
|
||||
@@ -73,27 +77,48 @@ fn should_return_storage() {
|
||||
|
||||
#[test]
|
||||
fn should_return_child_storage() {
|
||||
let (child_info, child_type) = CHILD_INFO.info();
|
||||
let child_info = StorageKey(child_info.to_vec());
|
||||
let core = tokio::runtime::Runtime::new().unwrap();
|
||||
let client = Arc::new(test_client::TestClientBuilder::new()
|
||||
.add_child_storage("test", "key", vec![42_u8])
|
||||
.add_child_storage("test", "key", CHILD_INFO, vec![42_u8])
|
||||
.build());
|
||||
let genesis_hash = client.genesis_hash();
|
||||
let client = new_full(client, Subscriptions::new(Arc::new(core.executor())));
|
||||
let child_key = StorageKey(well_known_keys::CHILD_STORAGE_KEY_PREFIX.iter().chain(b"test").cloned().collect());
|
||||
let child_key = StorageKey(
|
||||
well_known_keys::CHILD_STORAGE_KEY_PREFIX.iter().chain(b"test").cloned().collect()
|
||||
);
|
||||
let key = StorageKey(b"key".to_vec());
|
||||
|
||||
|
||||
assert_matches!(
|
||||
client.child_storage(child_key.clone(), key.clone(), Some(genesis_hash).into()).wait(),
|
||||
client.child_storage(
|
||||
child_key.clone(),
|
||||
child_info.clone(),
|
||||
child_type,
|
||||
key.clone(),
|
||||
Some(genesis_hash).into(),
|
||||
).wait(),
|
||||
Ok(Some(StorageData(ref d))) if d[0] == 42 && d.len() == 1
|
||||
);
|
||||
assert_matches!(
|
||||
client.child_storage_hash(child_key.clone(), key.clone(), Some(genesis_hash).into())
|
||||
.wait().map(|x| x.is_some()),
|
||||
client.child_storage_hash(
|
||||
child_key.clone(),
|
||||
child_info.clone(),
|
||||
child_type,
|
||||
key.clone(),
|
||||
Some(genesis_hash).into(),
|
||||
).wait().map(|x| x.is_some()),
|
||||
Ok(true)
|
||||
);
|
||||
assert_matches!(
|
||||
client.child_storage_size(child_key.clone(), key.clone(), None).wait(),
|
||||
client.child_storage_size(
|
||||
child_key.clone(),
|
||||
child_info.clone(),
|
||||
child_type,
|
||||
key.clone(),
|
||||
None,
|
||||
).wait(),
|
||||
Ok(Some(1))
|
||||
);
|
||||
}
|
||||
|
||||
@@ -98,9 +98,9 @@ pub fn build_proof<Header, Hasher, BlocksI, HashesI>(
|
||||
{
|
||||
let transaction = build_pairs::<Header, _>(cht_size, cht_num, hashes)?
|
||||
.into_iter()
|
||||
.map(|(k, v)| (None, k, Some(v)))
|
||||
.map(|(k, v)| (k, Some(v)))
|
||||
.collect::<Vec<_>>();
|
||||
let mut storage = InMemoryState::<Hasher>::default().update(transaction);
|
||||
let mut storage = InMemoryState::<Hasher>::default().update(vec![(None, transaction)]);
|
||||
let trie_storage = storage.as_trie_backend()
|
||||
.expect("InMemoryState::as_trie_backend always returns Some; qed");
|
||||
prove_read_on_trie_backend(
|
||||
|
||||
@@ -28,7 +28,7 @@ use hash_db::{Hasher, Prefix};
|
||||
use primitives::{
|
||||
Blake2Hasher, H256, ChangesTrieConfiguration, convert_hash,
|
||||
NeverNativeValue, ExecutionContext, NativeOrEncoded,
|
||||
storage::{StorageKey, StorageData, well_known_keys},
|
||||
storage::{StorageKey, StorageData, well_known_keys, ChildInfo},
|
||||
traits::CodeExecutor,
|
||||
};
|
||||
use sc_telemetry::{telemetry, SUBSTRATE_INFO};
|
||||
@@ -199,10 +199,10 @@ impl<B, E, Block, RA> Client<B, E, Block, RA> where
|
||||
execution_extensions: ExecutionExtensions<Block>,
|
||||
) -> sp_blockchain::Result<Self> {
|
||||
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)?;
|
||||
let genesis_block = genesis::construct_genesis_block::<Block>(state_root.into());
|
||||
info!("Initializing Genesis block/state (state: {}, header-hash: {})",
|
||||
genesis_block.header().state_root(),
|
||||
@@ -267,10 +267,11 @@ impl<B, E, Block, RA> Client<B, E, Block, RA> where
|
||||
&self,
|
||||
id: &BlockId<Block>,
|
||||
child_storage_key: &StorageKey,
|
||||
child_info: ChildInfo,
|
||||
key_prefix: &StorageKey
|
||||
) -> sp_blockchain::Result<Vec<StorageKey>> {
|
||||
let keys = self.state_at(id)?
|
||||
.child_keys(&child_storage_key.0, &key_prefix.0)
|
||||
.child_keys(&child_storage_key.0, child_info, &key_prefix.0)
|
||||
.into_iter()
|
||||
.map(StorageKey)
|
||||
.collect();
|
||||
@@ -281,11 +282,13 @@ impl<B, E, Block, RA> Client<B, E, Block, RA> where
|
||||
pub fn child_storage(
|
||||
&self,
|
||||
id: &BlockId<Block>,
|
||||
child_storage_key: &StorageKey,
|
||||
storage_key: &StorageKey,
|
||||
child_info: ChildInfo,
|
||||
key: &StorageKey
|
||||
) -> sp_blockchain::Result<Option<StorageData>> {
|
||||
Ok(self.state_at(id)?
|
||||
.child_storage(&child_storage_key.0, &key.0).map_err(|e| sp_blockchain::Error::from_state(Box::new(e)))?
|
||||
.child_storage(&storage_key.0, child_info, &key.0)
|
||||
.map_err(|e| sp_blockchain::Error::from_state(Box::new(e)))?
|
||||
.map(StorageData))
|
||||
}
|
||||
|
||||
@@ -293,11 +296,13 @@ impl<B, E, Block, RA> Client<B, E, Block, RA> where
|
||||
pub fn child_storage_hash(
|
||||
&self,
|
||||
id: &BlockId<Block>,
|
||||
child_storage_key: &StorageKey,
|
||||
storage_key: &StorageKey,
|
||||
child_info: ChildInfo,
|
||||
key: &StorageKey
|
||||
) -> sp_blockchain::Result<Option<Block::Hash>> {
|
||||
Ok(self.state_at(id)?
|
||||
.child_storage_hash(&child_storage_key.0, &key.0).map_err(|e| sp_blockchain::Error::from_state(Box::new(e)))?
|
||||
.child_storage_hash(&storage_key.0, child_info, &key.0)
|
||||
.map_err(|e| sp_blockchain::Error::from_state(Box::new(e)))?
|
||||
)
|
||||
}
|
||||
|
||||
@@ -334,13 +339,14 @@ impl<B, E, Block, RA> Client<B, E, Block, RA> where
|
||||
&self,
|
||||
id: &BlockId<Block>,
|
||||
storage_key: &[u8],
|
||||
child_info: ChildInfo,
|
||||
keys: I,
|
||||
) -> sp_blockchain::Result<StorageProof> where
|
||||
I: IntoIterator,
|
||||
I::Item: AsRef<[u8]>,
|
||||
{
|
||||
self.state_at(id)
|
||||
.and_then(|state| prove_child_read(state, storage_key, keys)
|
||||
.and_then(|state| prove_child_read(state, storage_key, child_info, keys)
|
||||
.map_err(Into::into))
|
||||
}
|
||||
|
||||
@@ -1012,7 +1018,7 @@ impl<B, E, Block, RA> Client<B, E, Block, RA> where
|
||||
overlay.commit_prospective();
|
||||
|
||||
let (top, children) = overlay.into_committed();
|
||||
let children = children.map(|(sk, it)| (sk, it.collect())).collect();
|
||||
let children = children.map(|(sk, it)| (sk, it.0.collect())).collect();
|
||||
if import_headers.post().state_root() != &storage_update.1 {
|
||||
return Err(sp_blockchain::Error::InvalidStateRoot);
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ mod tests {
|
||||
runtime::{Hash, Transfer, Block, BlockNumber, Header, Digest},
|
||||
AccountKeyring, Sr25519Keyring,
|
||||
};
|
||||
use primitives::{Blake2Hasher, map};
|
||||
use primitives::Blake2Hasher;
|
||||
use hex_literal::*;
|
||||
|
||||
native_executor_instance!(
|
||||
@@ -154,8 +154,7 @@ mod tests {
|
||||
vec![AccountKeyring::One.into(), AccountKeyring::Two.into()],
|
||||
1000,
|
||||
None,
|
||||
map![],
|
||||
map![],
|
||||
Default::default(),
|
||||
).genesis_map();
|
||||
let genesis_hash = insert_genesis_block(&mut storage);
|
||||
|
||||
@@ -183,8 +182,7 @@ mod tests {
|
||||
vec![AccountKeyring::One.into(), AccountKeyring::Two.into()],
|
||||
1000,
|
||||
None,
|
||||
map![],
|
||||
map![],
|
||||
Default::default(),
|
||||
).genesis_map();
|
||||
let genesis_hash = insert_genesis_block(&mut storage);
|
||||
|
||||
@@ -212,8 +210,7 @@ mod tests {
|
||||
vec![AccountKeyring::One.into(), AccountKeyring::Two.into()],
|
||||
68,
|
||||
None,
|
||||
map![],
|
||||
map![],
|
||||
Default::default(),
|
||||
).genesis_map();
|
||||
let genesis_hash = insert_genesis_block(&mut storage);
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ use primitives::offchain::storage::{
|
||||
};
|
||||
use sp_runtime::generic::{BlockId, DigestItem};
|
||||
use sp_runtime::traits::{Block as BlockT, Header as HeaderT, Zero, NumberFor};
|
||||
use sp_runtime::{Justification, StorageOverlay, ChildrenStorageOverlay};
|
||||
use sp_runtime::{Justification, Storage};
|
||||
use state_machine::backend::{Backend as StateBackend, InMemory};
|
||||
use state_machine::{self, InMemoryChangesTrieStorage, ChangesTrieAnchorBlockId, ChangesTrieTransaction};
|
||||
use hash_db::{Hasher, Prefix};
|
||||
@@ -505,15 +505,15 @@ where
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn reset_storage(&mut self, top: StorageOverlay, children: ChildrenStorageOverlay) -> sp_blockchain::Result<H::Out> {
|
||||
check_genesis_storage(&top, &children)?;
|
||||
fn reset_storage(&mut self, storage: Storage) -> sp_blockchain::Result<H::Out> {
|
||||
check_genesis_storage(&storage)?;
|
||||
|
||||
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
|
||||
);
|
||||
|
||||
@@ -796,12 +796,12 @@ impl<Block, H> state_machine::ChangesTrieStorage<H, NumberFor<Block>> for Change
|
||||
}
|
||||
|
||||
/// Check that genesis storage is valid.
|
||||
pub fn check_genesis_storage(top: &StorageOverlay, children: &ChildrenStorageOverlay) -> sp_blockchain::Result<()> {
|
||||
if top.iter().any(|(k, _)| well_known_keys::is_child_storage_key(k)) {
|
||||
pub fn check_genesis_storage(storage: &Storage) -> sp_blockchain::Result<()> {
|
||||
if storage.top.iter().any(|(k, _)| well_known_keys::is_child_storage_key(k)) {
|
||||
return Err(sp_blockchain::Error::GenesisInvalid.into());
|
||||
}
|
||||
|
||||
if children.keys().any(|child_key| !well_known_keys::is_child_storage_key(&child_key)) {
|
||||
if storage.children.keys().any(|child_key| !well_known_keys::is_child_storage_key(&child_key)) {
|
||||
return Err(sp_blockchain::Error::GenesisInvalid.into());
|
||||
}
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
//! use std::sync::Arc;
|
||||
//! use sc_client::{Client, in_mem::Backend, LocalCallExecutor};
|
||||
//! use primitives::Blake2Hasher;
|
||||
//! use sp_runtime::{StorageOverlay, ChildrenStorageOverlay};
|
||||
//! use sp_runtime::Storage;
|
||||
//! use executor::{NativeExecutor, WasmExecutionMethod};
|
||||
//!
|
||||
//! // In this example, we're using the `Block` and `RuntimeApi` types from the
|
||||
@@ -65,7 +65,7 @@
|
||||
//! NativeExecutor::<LocalExecutor>::new(WasmExecutionMethod::Interpreted, None),
|
||||
//! ),
|
||||
//! // This parameter provides the storage for the chain genesis.
|
||||
//! <(StorageOverlay, ChildrenStorageOverlay)>::default(),
|
||||
//! <Storage>::default(),
|
||||
//! Default::default(),
|
||||
//! Default::default(),
|
||||
//! );
|
||||
|
||||
@@ -21,11 +21,12 @@ use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
use parking_lot::RwLock;
|
||||
|
||||
use primitives::storage::{ChildInfo, OwnedChildInfo};
|
||||
use state_machine::{
|
||||
Backend as StateBackend, TrieBackend, backend::InMemory as InMemoryState, ChangesTrieTransaction
|
||||
};
|
||||
use primitives::offchain::storage::InMemOffchainStorage;
|
||||
use sp_runtime::{generic::BlockId, Justification, StorageOverlay, ChildrenStorageOverlay};
|
||||
use sp_runtime::{generic::BlockId, Justification, Storage};
|
||||
use sp_runtime::traits::{Block as BlockT, NumberFor, Zero, Header};
|
||||
use crate::in_mem::{self, check_genesis_storage};
|
||||
use sp_blockchain::{ Error as ClientError, Result as ClientResult };
|
||||
@@ -280,22 +281,21 @@ where
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn reset_storage(&mut self, top: StorageOverlay, children: ChildrenStorageOverlay) -> ClientResult<H::Out> {
|
||||
check_genesis_storage(&top, &children)?;
|
||||
fn reset_storage(&mut self, input: Storage) -> ClientResult<H::Out> {
|
||||
check_genesis_storage(&input)?;
|
||||
|
||||
// this is only called when genesis block is imported => shouldn't be performance bottleneck
|
||||
let mut storage: HashMap<Option<Vec<u8>>, StorageOverlay> = HashMap::new();
|
||||
storage.insert(None, top);
|
||||
let mut storage: HashMap<Option<(Vec<u8>, OwnedChildInfo)>, _> = HashMap::new();
|
||||
storage.insert(None, input.top);
|
||||
|
||||
// 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 = input.children.iter()
|
||||
.map(|(storage_key, storage_child)| (storage_key.clone(), None, storage_child.child_info.clone()))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// make sure to persist the child storage
|
||||
for (child_key, child_storage) in children {
|
||||
storage.insert(Some(child_key), child_storage);
|
||||
for (child_key, storage_child) in input.children {
|
||||
storage.insert(Some((child_key, storage_child.child_info)), storage_child.data);
|
||||
}
|
||||
|
||||
let storage_update: InMemoryState<H> = storage.into();
|
||||
@@ -357,10 +357,15 @@ impl<H: Hasher> StateBackend<H> for GenesisOrUnavailableState<H>
|
||||
}
|
||||
}
|
||||
|
||||
fn child_storage(&self, storage_key: &[u8], key: &[u8]) -> ClientResult<Option<Vec<u8>>> {
|
||||
fn child_storage(
|
||||
&self,
|
||||
storage_key: &[u8],
|
||||
child_info: ChildInfo,
|
||||
key: &[u8],
|
||||
) -> ClientResult<Option<Vec<u8>>> {
|
||||
match *self {
|
||||
GenesisOrUnavailableState::Genesis(ref state) =>
|
||||
Ok(state.child_storage(storage_key, key).expect(IN_MEMORY_EXPECT_PROOF)),
|
||||
Ok(state.child_storage(storage_key, child_info, key).expect(IN_MEMORY_EXPECT_PROOF)),
|
||||
GenesisOrUnavailableState::Unavailable => Err(ClientError::NotAvailableOnLightClient),
|
||||
}
|
||||
}
|
||||
@@ -373,10 +378,17 @@ impl<H: Hasher> StateBackend<H> for GenesisOrUnavailableState<H>
|
||||
}
|
||||
}
|
||||
|
||||
fn next_child_storage_key(&self, storage_key: &[u8], key: &[u8]) -> Result<Option<Vec<u8>>, Self::Error> {
|
||||
fn next_child_storage_key(
|
||||
&self,
|
||||
storage_key: &[u8],
|
||||
child_info: ChildInfo,
|
||||
key: &[u8],
|
||||
) -> Result<Option<Vec<u8>>, Self::Error> {
|
||||
match *self {
|
||||
GenesisOrUnavailableState::Genesis(ref state) =>
|
||||
Ok(state.next_child_storage_key(storage_key, key).expect(IN_MEMORY_EXPECT_PROOF)),
|
||||
GenesisOrUnavailableState::Genesis(ref state) => Ok(
|
||||
state.next_child_storage_key(storage_key, child_info, key)
|
||||
.expect(IN_MEMORY_EXPECT_PROOF)
|
||||
),
|
||||
GenesisOrUnavailableState::Unavailable => Err(ClientError::NotAvailableOnLightClient),
|
||||
}
|
||||
}
|
||||
@@ -395,10 +407,15 @@ impl<H: Hasher> StateBackend<H> for GenesisOrUnavailableState<H>
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn for_keys_in_child_storage<A: FnMut(&[u8])>(&self, storage_key: &[u8], action: A) {
|
||||
fn for_keys_in_child_storage<A: FnMut(&[u8])>(
|
||||
&self,
|
||||
storage_key: &[u8],
|
||||
child_info: ChildInfo,
|
||||
action: A,
|
||||
) {
|
||||
match *self {
|
||||
GenesisOrUnavailableState::Genesis(ref state) => state.for_keys_in_child_storage(storage_key, action),
|
||||
GenesisOrUnavailableState::Genesis(ref state) =>
|
||||
state.for_keys_in_child_storage(storage_key, child_info, action),
|
||||
GenesisOrUnavailableState::Unavailable => (),
|
||||
}
|
||||
}
|
||||
@@ -406,12 +423,13 @@ impl<H: Hasher> StateBackend<H> for GenesisOrUnavailableState<H>
|
||||
fn for_child_keys_with_prefix<A: FnMut(&[u8])>(
|
||||
&self,
|
||||
storage_key: &[u8],
|
||||
child_info: ChildInfo,
|
||||
prefix: &[u8],
|
||||
action: A,
|
||||
) {
|
||||
match *self {
|
||||
GenesisOrUnavailableState::Genesis(ref state) =>
|
||||
state.for_child_keys_with_prefix(storage_key, prefix, action),
|
||||
state.for_child_keys_with_prefix(storage_key, child_info, prefix, action),
|
||||
GenesisOrUnavailableState::Unavailable => (),
|
||||
}
|
||||
}
|
||||
@@ -427,13 +445,18 @@ impl<H: Hasher> StateBackend<H> for GenesisOrUnavailableState<H>
|
||||
}
|
||||
}
|
||||
|
||||
fn child_storage_root<I>(&self, 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>>)>
|
||||
{
|
||||
match *self {
|
||||
GenesisOrUnavailableState::Genesis(ref state) => {
|
||||
let (root, is_equal, _) = state.child_storage_root(key, delta);
|
||||
let (root, is_equal, _) = state.child_storage_root(storage_key, child_info, delta);
|
||||
(root, is_equal, ())
|
||||
},
|
||||
GenesisOrUnavailableState::Unavailable => (H::Out::default(), true, ()),
|
||||
@@ -478,7 +501,7 @@ mod tests {
|
||||
let backend: Backend<_, Blake2Hasher> = Backend::new(Arc::new(DummyBlockchain::new(DummyStorage::new())));
|
||||
let mut op = backend.begin_operation().unwrap();
|
||||
op.set_block_data(header0, None, None, NewBlockState::Final).unwrap();
|
||||
op.reset_storage(Default::default(), Default::default()).unwrap();
|
||||
op.reset_storage(Default::default()).unwrap();
|
||||
backend.commit_operation(op).unwrap();
|
||||
|
||||
match backend.state_at(BlockId::Number(0)).unwrap() {
|
||||
|
||||
@@ -339,11 +339,13 @@ pub mod tests {
|
||||
use crate::light::fetcher::{FetchChecker, LightDataChecker, RemoteHeaderRequest};
|
||||
use crate::light::blockchain::tests::{DummyStorage, DummyBlockchain};
|
||||
use primitives::{blake2_256, Blake2Hasher, H256};
|
||||
use primitives::storage::{well_known_keys, StorageKey};
|
||||
use primitives::storage::{well_known_keys, StorageKey, ChildInfo};
|
||||
use sp_runtime::generic::BlockId;
|
||||
use state_machine::Backend;
|
||||
use super::*;
|
||||
|
||||
const CHILD_INFO_1: ChildInfo<'static> = ChildInfo::new_default(b"unique_id_1");
|
||||
|
||||
type TestChecker = LightDataChecker<
|
||||
NativeExecutor<test_client::LocalExecutor>,
|
||||
Blake2Hasher,
|
||||
@@ -394,8 +396,12 @@ pub mod tests {
|
||||
use test_client::TestClientBuilderExt;
|
||||
// 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())
|
||||
.build();
|
||||
.add_extra_child_storage(
|
||||
b":child_storage:default:child1".to_vec(),
|
||||
CHILD_INFO_1,
|
||||
b"key1".to_vec(),
|
||||
b"value1".to_vec(),
|
||||
).build();
|
||||
let remote_block_id = BlockId::Number(0);
|
||||
let remote_block_hash = remote_client.block_hash(0).unwrap().unwrap();
|
||||
let mut remote_block_header = remote_client.header(&remote_block_id).unwrap().unwrap();
|
||||
@@ -406,12 +412,14 @@ pub mod tests {
|
||||
let child_value = remote_client.child_storage(
|
||||
&remote_block_id,
|
||||
&StorageKey(b":child_storage:default:child1".to_vec()),
|
||||
CHILD_INFO_1,
|
||||
&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_INFO_1,
|
||||
&[b"key1"],
|
||||
).unwrap();
|
||||
|
||||
@@ -487,11 +495,14 @@ pub mod tests {
|
||||
remote_read_proof,
|
||||
result,
|
||||
) = prepare_for_read_child_proof_check();
|
||||
let child_infos = CHILD_INFO_1.info();
|
||||
assert_eq!((&local_checker as &dyn FetchChecker<Block>).check_read_child_proof(
|
||||
&RemoteReadChildRequest::<Header> {
|
||||
block: remote_block_header.hash(),
|
||||
header: remote_block_header,
|
||||
storage_key: b":child_storage:default:child1".to_vec(),
|
||||
child_info: child_infos.0.to_vec(),
|
||||
child_type: child_infos.1,
|
||||
keys: vec![b"key1".to_vec()],
|
||||
retry_count: None,
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user