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:
cheme
2019-12-14 03:11:19 +01:00
committed by Gavin Wood
parent 7121837f84
commit 0ece5d9e17
53 changed files with 2121 additions and 918 deletions
+2 -2
View File
@@ -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(
+6 -1
View File
@@ -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.
+58 -25
View File
@@ -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(())
+70 -27
View File
@@ -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(
+53 -16
View File
@@ -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");
+5 -2
View File
@@ -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> {
+35 -17
View File
@@ -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>>;
+27 -5
View File
@@ -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())
}
+30 -5
View File
@@ -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))))
)
+34 -9
View File
@@ -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))
);
}
+2 -2
View File
@@ -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(
+16 -10
View File
@@ -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);
}
+4 -7
View File
@@ -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);
+10 -10
View File
@@ -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());
}
+2 -2
View File
@@ -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(),
//! );
+45 -22
View File
@@ -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() {
+14 -3
View File
@@ -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,
},