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
+103 -82
View File
@@ -42,7 +42,7 @@ mod tests {
use state_machine::TestExternalities as CoreTestExternalities;
use primitives::{
Blake2Hasher, NeverNativeValue, NativeOrEncoded, map,
traits::{CodeExecutor, Externalities}, storage::well_known_keys,
traits::{CodeExecutor, Externalities}, storage::{well_known_keys, Storage},
};
use sp_runtime::{
Fixed64, traits::{Header as HeaderT, Hash as HashT, Convert}, ApplyExtrinsicResult,
@@ -143,20 +143,23 @@ mod tests {
#[test]
fn panic_execution_with_foreign_code_gives_error() {
let mut t = TestExternalities::<Blake2Hasher>::new_with_code(BLOATY_CODE, (map![
<balances::FreeBalance<Runtime>>::hashed_key_for(alice()) => {
69_u128.encode()
},
<balances::TotalIssuance<Runtime>>::hashed_key().to_vec() => {
69_u128.encode()
},
<indices::NextEnumSet<Runtime>>::hashed_key().to_vec() => {
0_u128.encode()
},
<system::BlockHash<Runtime>>::hashed_key_for(0) => {
vec![0u8; 32]
}
], map![]));
let mut t = TestExternalities::<Blake2Hasher>::new_with_code(BLOATY_CODE, Storage {
top: map![
<balances::FreeBalance<Runtime>>::hashed_key_for(alice()) => {
69_u128.encode()
},
<balances::TotalIssuance<Runtime>>::hashed_key().to_vec() => {
69_u128.encode()
},
<indices::NextEnumSet<Runtime>>::hashed_key().to_vec() => {
0_u128.encode()
},
<system::BlockHash<Runtime>>::hashed_key_for(0) => {
vec![0u8; 32]
}
],
children: map![],
});
let r = executor_call::<NeverNativeValue, fn() -> _>(
&mut t,
@@ -179,20 +182,23 @@ mod tests {
#[test]
fn bad_extrinsic_with_native_equivalent_code_gives_error() {
let mut t = TestExternalities::<Blake2Hasher>::new_with_code(COMPACT_CODE, (map![
<balances::FreeBalance<Runtime>>::hashed_key_for(alice()) => {
69_u128.encode()
},
<balances::TotalIssuance<Runtime>>::hashed_key().to_vec() => {
69_u128.encode()
},
<indices::NextEnumSet<Runtime>>::hashed_key().to_vec() => {
0_u128.encode()
},
<system::BlockHash<Runtime>>::hashed_key_for(0) => {
vec![0u8; 32]
}
], map![]));
let mut t = TestExternalities::<Blake2Hasher>::new_with_code(COMPACT_CODE, Storage {
top: map![
<balances::FreeBalance<Runtime>>::hashed_key_for(alice()) => {
69_u128.encode()
},
<balances::TotalIssuance<Runtime>>::hashed_key().to_vec() => {
69_u128.encode()
},
<indices::NextEnumSet<Runtime>>::hashed_key().to_vec() => {
0_u128.encode()
},
<system::BlockHash<Runtime>>::hashed_key_for(0) => {
vec![0u8; 32]
}
],
children: map![],
});
let r = executor_call::<NeverNativeValue, fn() -> _>(
&mut t,
@@ -215,16 +221,19 @@ mod tests {
#[test]
fn successful_execution_with_native_equivalent_code_gives_ok() {
let mut t = TestExternalities::<Blake2Hasher>::new_with_code(COMPACT_CODE, (map![
<balances::FreeBalance<Runtime>>::hashed_key_for(alice()) => {
(111 * DOLLARS).encode()
},
<balances::TotalIssuance<Runtime>>::hashed_key().to_vec() => {
(111 * DOLLARS).encode()
},
<indices::NextEnumSet<Runtime>>::hashed_key().to_vec() => vec![0u8; 16],
<system::BlockHash<Runtime>>::hashed_key_for(0) => vec![0u8; 32]
], map![]));
let mut t = TestExternalities::<Blake2Hasher>::new_with_code(COMPACT_CODE, Storage {
top: map![
<balances::FreeBalance<Runtime>>::hashed_key_for(alice()) => {
(111 * DOLLARS).encode()
},
<balances::TotalIssuance<Runtime>>::hashed_key().to_vec() => {
(111 * DOLLARS).encode()
},
<indices::NextEnumSet<Runtime>>::hashed_key().to_vec() => vec![0u8; 16],
<system::BlockHash<Runtime>>::hashed_key_for(0) => vec![0u8; 32]
],
children: map![],
});
let r = executor_call::<NeverNativeValue, fn() -> _>(
&mut t,
@@ -254,16 +263,19 @@ mod tests {
#[test]
fn successful_execution_with_foreign_code_gives_ok() {
let mut t = TestExternalities::<Blake2Hasher>::new_with_code(BLOATY_CODE, (map![
<balances::FreeBalance<Runtime>>::hashed_key_for(alice()) => {
(111 * DOLLARS).encode()
},
<balances::TotalIssuance<Runtime>>::hashed_key().to_vec() => {
(111 * DOLLARS).encode()
},
<indices::NextEnumSet<Runtime>>::hashed_key().to_vec() => vec![0u8; 16],
<system::BlockHash<Runtime>>::hashed_key_for(0) => vec![0u8; 32]
], map![]));
let mut t = TestExternalities::<Blake2Hasher>::new_with_code(BLOATY_CODE, Storage {
top: map![
<balances::FreeBalance<Runtime>>::hashed_key_for(alice()) => {
(111 * DOLLARS).encode()
},
<balances::TotalIssuance<Runtime>>::hashed_key().to_vec() => {
(111 * DOLLARS).encode()
},
<indices::NextEnumSet<Runtime>>::hashed_key().to_vec() => vec![0u8; 16],
<system::BlockHash<Runtime>>::hashed_key_for(0) => vec![0u8; 32]
],
children: map![],
});
let r = executor_call::<NeverNativeValue, fn() -> _>(
&mut t,
@@ -828,16 +840,19 @@ mod tests {
#[test]
fn panic_execution_gives_error() {
let mut t = TestExternalities::<Blake2Hasher>::new_with_code(BLOATY_CODE, (map![
<balances::FreeBalance<Runtime>>::hashed_key_for(alice()) => {
0_u128.encode()
},
<balances::TotalIssuance<Runtime>>::hashed_key().to_vec() => {
0_u128.encode()
},
<indices::NextEnumSet<Runtime>>::hashed_key().to_vec() => vec![0u8; 16],
<system::BlockHash<Runtime>>::hashed_key_for(0) => vec![0u8; 32]
], map![]));
let mut t = TestExternalities::<Blake2Hasher>::new_with_code(BLOATY_CODE, Storage {
top: map![
<balances::FreeBalance<Runtime>>::hashed_key_for(alice()) => {
0_u128.encode()
},
<balances::TotalIssuance<Runtime>>::hashed_key().to_vec() => {
0_u128.encode()
},
<indices::NextEnumSet<Runtime>>::hashed_key().to_vec() => vec![0u8; 16],
<system::BlockHash<Runtime>>::hashed_key_for(0) => vec![0u8; 32]
],
children: map![],
});
let r = executor_call::<NeverNativeValue, fn() -> _>(
&mut t,
@@ -860,16 +875,19 @@ mod tests {
#[test]
fn successful_execution_gives_ok() {
let mut t = TestExternalities::<Blake2Hasher>::new_with_code(COMPACT_CODE, (map![
<balances::FreeBalance<Runtime>>::hashed_key_for(alice()) => {
(111 * DOLLARS).encode()
},
<balances::TotalIssuance<Runtime>>::hashed_key().to_vec() => {
(111 * DOLLARS).encode()
},
<indices::NextEnumSet<Runtime>>::hashed_key().to_vec() => vec![0u8; 16],
<system::BlockHash<Runtime>>::hashed_key_for(0) => vec![0u8; 32]
], map![]));
let mut t = TestExternalities::<Blake2Hasher>::new_with_code(COMPACT_CODE, Storage {
top: map![
<balances::FreeBalance<Runtime>>::hashed_key_for(alice()) => {
(111 * DOLLARS).encode()
},
<balances::TotalIssuance<Runtime>>::hashed_key().to_vec() => {
(111 * DOLLARS).encode()
},
<indices::NextEnumSet<Runtime>>::hashed_key().to_vec() => vec![0u8; 16],
<system::BlockHash<Runtime>>::hashed_key_for(0) => vec![0u8; 32]
],
children: map![],
});
let r = executor_call::<NeverNativeValue, fn() -> _>(
&mut t,
@@ -1037,19 +1055,22 @@ mod tests {
// - 1 MILLICENTS in substrate node.
// - 1 milli-dot based on current polkadot runtime.
// (this baed on assigning 0.1 CENT to the cheapest tx with `weight = 100`)
let mut t = TestExternalities::<Blake2Hasher>::new_with_code(COMPACT_CODE, (map![
<balances::FreeBalance<Runtime>>::hashed_key_for(alice()) => {
(100 * DOLLARS).encode()
},
<balances::FreeBalance<Runtime>>::hashed_key_for(bob()) => {
(10 * DOLLARS).encode()
},
<balances::TotalIssuance<Runtime>>::hashed_key().to_vec() => {
(110 * DOLLARS).encode()
},
<indices::NextEnumSet<Runtime>>::hashed_key().to_vec() => vec![0u8; 16],
<system::BlockHash<Runtime>>::hashed_key_for(0) => vec![0u8; 32]
], map![]));
let mut t = TestExternalities::<Blake2Hasher>::new_with_code(COMPACT_CODE, Storage {
top: map![
<balances::FreeBalance<Runtime>>::hashed_key_for(alice()) => {
(100 * DOLLARS).encode()
},
<balances::FreeBalance<Runtime>>::hashed_key_for(bob()) => {
(10 * DOLLARS).encode()
},
<balances::TotalIssuance<Runtime>>::hashed_key().to_vec() => {
(110 * DOLLARS).encode()
},
<indices::NextEnumSet<Runtime>>::hashed_key().to_vec() => vec![0u8; 16],
<system::BlockHash<Runtime>>::hashed_key_for(0) => vec![0u8; 32]
],
children: map![],
});
let tip = 1_000_000;
let xt = sign(CheckedExtrinsic {
+1 -1
View File
@@ -42,7 +42,7 @@ pub struct GenesisParameters {
}
impl test_client::GenesisInit for GenesisParameters {
fn genesis_storage(&self) -> (StorageOverlay, ChildrenStorageOverlay) {
fn genesis_storage(&self) -> Storage {
crate::genesis::config(self.support_changes_trie, None).build_storage().unwrap()
}
}
+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,
},
+10 -6
View File
@@ -128,7 +128,7 @@ impl<T: Trait> AccountDb<T> for DirectAccountDb {
trie_id: Option<&TrieId>,
location: &StorageKey
) -> Option<Vec<u8>> {
trie_id.and_then(|id| child::get_raw(id, &blake2_256(location)))
trie_id.and_then(|id| child::get_raw(id, crate::trie_unique_id(&id[..]), &blake2_256(location)))
}
fn get_code_hash(&self, account: &T::AccountId) -> Option<CodeHash<T>> {
<ContractInfoOf<T>>::get(account).and_then(|i| i.as_alive().map(|i| i.code_hash))
@@ -173,13 +173,13 @@ impl<T: Trait> AccountDb<T> for DirectAccountDb {
(false, Some(info), _) => info,
// Existing contract is being removed.
(true, Some(info), None) => {
child::kill_storage(&info.trie_id);
child::kill_storage(&info.trie_id, info.child_trie_unique_id());
<ContractInfoOf<T>>::remove(&address);
continue;
}
// Existing contract is being replaced by a new one.
(true, Some(info), Some(code_hash)) => {
child::kill_storage(&info.trie_id);
child::kill_storage(&info.trie_id, info.child_trie_unique_id());
AliveContractInfo::<T> {
code_hash,
storage_size: T::StorageSizeOffset::get(),
@@ -217,14 +217,18 @@ impl<T: Trait> AccountDb<T> for DirectAccountDb {
}
for (k, v) in changed.storage.into_iter() {
if let Some(value) = child::get_raw(&new_info.trie_id[..], &blake2_256(&k)) {
if let Some(value) = child::get_raw(
&new_info.trie_id[..],
new_info.child_trie_unique_id(),
&blake2_256(&k),
) {
new_info.storage_size -= value.len() as u32;
}
if let Some(value) = v {
new_info.storage_size += value.len() as u32;
child::put_raw(&new_info.trie_id[..], &blake2_256(&k), &value[..]);
child::put_raw(&new_info.trie_id[..], new_info.child_trie_unique_id(), &blake2_256(&k), &value[..]);
} else {
child::kill(&new_info.trie_id[..], &blake2_256(&k));
child::kill(&new_info.trie_id[..], new_info.child_trie_unique_id(), &blake2_256(&k));
}
}
+34 -5
View File
@@ -223,6 +223,19 @@ pub struct RawAliveContractInfo<CodeHash, Balance, BlockNumber> {
pub last_write: Option<BlockNumber>,
}
impl<CodeHash, Balance, BlockNumber> RawAliveContractInfo<CodeHash, Balance, BlockNumber> {
/// Associated child trie unique id is built from the hash part of the trie id.
pub fn child_trie_unique_id(&self) -> child::ChildInfo {
trie_unique_id(&self.trie_id[..])
}
}
/// Associated child trie unique id is built from the hash part of the trie id.
pub(crate) fn trie_unique_id(trie_id: &[u8]) -> child::ChildInfo {
let start = CHILD_STORAGE_KEY_PREFIX.len() + b"default:".len();
child::ChildInfo::new_default(&trie_id[start ..])
}
pub type TombstoneContractInfo<T> =
RawTombstoneContractInfo<<T as system::Trait>::Hash, <T as system::Trait>::Hashing>;
@@ -793,8 +806,17 @@ impl<T: Trait> Module<T> {
let key_values_taken = delta.iter()
.filter_map(|key| {
child::get_raw(&origin_contract.trie_id, &blake2_256(key)).map(|value| {
child::kill(&origin_contract.trie_id, &blake2_256(key));
child::get_raw(
&origin_contract.trie_id,
origin_contract.child_trie_unique_id(),
&blake2_256(key),
).map(|value| {
child::kill(
&origin_contract.trie_id,
origin_contract.child_trie_unique_id(),
&blake2_256(key),
);
(key, value)
})
})
@@ -803,13 +825,20 @@ impl<T: Trait> Module<T> {
let tombstone = <TombstoneContractInfo<T>>::new(
// This operation is cheap enough because last_write (delta not included)
// is not this block as it has been checked earlier.
&sp_io::storage::child_root(&origin_contract.trie_id)[..],
&child::child_root(
&origin_contract.trie_id,
)[..],
code_hash,
);
if tombstone != dest_tombstone {
for (key, value) in key_values_taken {
child::put_raw(&origin_contract.trie_id, &blake2_256(key), &value);
child::put_raw(
&origin_contract.trie_id,
origin_contract.child_trie_unique_id(),
&blake2_256(key),
&value,
);
}
return Err("Tombstones don't match");
@@ -887,7 +916,7 @@ decl_storage! {
impl<T: Trait> OnFreeBalanceZero<T::AccountId> for Module<T> {
fn on_free_balance_zero(who: &T::AccountId) {
if let Some(ContractInfo::Alive(info)) = <ContractInfoOf<T>>::take(who) {
child::kill_storage(&info.trie_id);
child::kill_storage(&info.trie_id, info.child_trie_unique_id());
}
}
}
+6 -3
View File
@@ -19,6 +19,7 @@ use sp_runtime::traits::{Bounded, CheckedDiv, CheckedMul, Saturating, Zero,
SaturatedConversion};
use support::traits::{Currency, ExistenceRequirement, Get, WithdrawReason, OnUnbalanced};
use support::StorageMap;
use support::storage::child;
#[derive(PartialEq, Eq, Copy, Clone)]
#[must_use]
@@ -99,7 +100,7 @@ fn try_evict_or_and_pay_rent<T: Trait>(
if balance < subsistence_threshold {
// The contract cannot afford to leave a tombstone, so remove the contract info altogether.
<ContractInfoOf<T>>::remove(account);
sp_io::storage::child_storage_kill(&contract.trie_id);
child::kill_storage(&contract.trie_id, contract.child_trie_unique_id());
return (RentOutcome::Evicted, None);
}
@@ -146,7 +147,9 @@ fn try_evict_or_and_pay_rent<T: Trait>(
// threshold, so it leaves a tombstone.
// Note: this operation is heavy.
let child_storage_root = sp_io::storage::child_root(&contract.trie_id);
let child_storage_root = child::child_root(
&contract.trie_id,
);
let tombstone = <TombstoneContractInfo<T>>::new(
&child_storage_root[..],
@@ -155,7 +158,7 @@ fn try_evict_or_and_pay_rent<T: Trait>(
let tombstone_info = ContractInfo::Tombstone(tombstone);
<ContractInfoOf<T>>::insert(account, &tombstone_info);
sp_io::storage::child_storage_kill(&contract.trie_id);
child::kill_storage(&contract.trie_id, contract.child_trie_unique_id());
return (RentOutcome::Evicted, Some(tombstone_info));
}
@@ -139,13 +139,10 @@ fn impl_build_storage(
#[cfg(feature = "std")]
impl#genesis_impl GenesisConfig#genesis_struct #genesis_where_clause {
pub fn build_storage #fn_generic (&self) -> std::result::Result<
(
#scrate::sp_runtime::StorageOverlay,
#scrate::sp_runtime::ChildrenStorageOverlay,
),
#scrate::sp_runtime::Storage,
String
> #fn_where_clause {
let mut storage = (Default::default(), Default::default());
let mut storage = Default::default();
self.assimilate_storage::<#fn_traitinstance>(&mut storage)?;
Ok(storage)
}
@@ -153,12 +150,9 @@ fn impl_build_storage(
/// Assimilate the storage for this module into pre-existing overlays.
pub fn assimilate_storage #fn_generic (
&self,
tuple_storage: &mut (
#scrate::sp_runtime::StorageOverlay,
#scrate::sp_runtime::ChildrenStorageOverlay,
),
storage: &mut #scrate::sp_runtime::Storage,
) -> std::result::Result<(), String> #fn_where_clause {
#scrate::BasicExternalities::execute_with_storage(tuple_storage, || {
#scrate::BasicExternalities::execute_with_storage(storage, || {
#( #builder_blocks )*
Ok(())
})
@@ -171,10 +165,7 @@ fn impl_build_storage(
{
fn build_module_genesis_storage(
&self,
storage: &mut (
#scrate::sp_runtime::StorageOverlay,
#scrate::sp_runtime::ChildrenStorageOverlay,
),
storage: &mut #scrate::sp_runtime::Storage,
) -> std::result::Result<(), String> {
self.assimilate_storage::<#fn_traitinstance> (storage)
}
+137 -27
View File
@@ -19,14 +19,29 @@
//! This module is a currently only a variant of unhashed with additional `storage_key`.
//! Note that `storage_key` must be unique and strong (strong in the sense of being long enough to
//! avoid collision from a resistant hash function (which unique implies)).
//!
//! A **key collision free** unique id is required as parameter to avoid key collision
//! between child tries.
//! This unique id management and generation responsability is delegated to pallet module.
// NOTE: could replace unhashed by having only one kind of storage (root being null storage key (storage_key can become Option<&[u8]>).
use crate::sp_std::prelude::*;
use codec::{Codec, Encode, Decode};
pub use primitives::storage::ChildInfo;
/// Return the value of the item in storage under `key`, or `None` if there is no explicit entry.
pub fn get<T: Decode + Sized>(storage_key: &[u8], key: &[u8]) -> Option<T> {
sp_io::storage::child_get(storage_key, key).and_then(|v| {
pub fn get<T: Decode + Sized>(
storage_key: &[u8],
child_info: ChildInfo,
key: &[u8],
) -> Option<T> {
let (data, child_type) = child_info.info();
sp_io::storage::child_get(
storage_key,
data,
child_type,
key,
).and_then(|v| {
Decode::decode(&mut &v[..]).map(Some).unwrap_or_else(|_| {
// TODO #3700: error should be handleable.
runtime_print!("ERROR: Corrupted state in child trie at {:?}/{:?}", storage_key, key);
@@ -37,83 +52,178 @@ pub fn get<T: Decode + Sized>(storage_key: &[u8], key: &[u8]) -> Option<T> {
/// Return the value of the item in storage under `key`, or the type's default if there is no
/// explicit entry.
pub fn get_or_default<T: Decode + Sized + Default>(storage_key: &[u8], key: &[u8]) -> T {
get(storage_key, key).unwrap_or_else(Default::default)
pub fn get_or_default<T: Decode + Sized + Default>(
storage_key: &[u8],
child_info: ChildInfo,
key: &[u8],
) -> T {
get(storage_key, child_info, key).unwrap_or_else(Default::default)
}
/// Return the value of the item in storage under `key`, or `default_value` if there is no
/// explicit entry.
pub fn get_or<T: Decode + Sized>(storage_key: &[u8], key: &[u8], default_value: T) -> T {
get(storage_key, key).unwrap_or(default_value)
pub fn get_or<T: Decode + Sized>(
storage_key: &[u8],
child_info: ChildInfo,
key: &[u8],
default_value: T,
) -> T {
get(storage_key, child_info, key).unwrap_or(default_value)
}
/// Return the value of the item in storage under `key`, or `default_value()` if there is no
/// explicit entry.
pub fn get_or_else<T: Decode + Sized, F: FnOnce() -> T>(
storage_key: &[u8],
child_info: ChildInfo,
key: &[u8],
default_value: F,
) -> T {
get(storage_key, key).unwrap_or_else(default_value)
get(storage_key, child_info, key).unwrap_or_else(default_value)
}
/// Put `value` in storage under `key`.
pub fn put<T: Encode>(storage_key: &[u8], key: &[u8], value: &T) {
value.using_encoded(|slice| sp_io::storage::child_set(storage_key, key, slice));
pub fn put<T: Encode>(
storage_key: &[u8],
child_info: ChildInfo,
key: &[u8],
value: &T,
) {
let (data, child_type) = child_info.info();
value.using_encoded(|slice|
sp_io::storage::child_set(
storage_key,
data,
child_type,
key,
slice,
)
);
}
/// Remove `key` from storage, returning its value if it had an explicit entry or `None` otherwise.
pub fn take<T: Decode + Sized>(storage_key: &[u8], key: &[u8]) -> Option<T> {
let r = get(storage_key, key);
pub fn take<T: Decode + Sized>(
storage_key: &[u8],
child_info: ChildInfo,
key: &[u8],
) -> Option<T> {
let r = get(storage_key, child_info, key);
if r.is_some() {
kill(storage_key, key);
kill(storage_key, child_info, key);
}
r
}
/// Remove `key` from storage, returning its value, or, if there was no explicit entry in storage,
/// the default for its type.
pub fn take_or_default<T: Codec + Sized + Default>(storage_key: &[u8], key: &[u8]) -> T {
take(storage_key, key).unwrap_or_else(Default::default)
pub fn take_or_default<T: Codec + Sized + Default>(
storage_key: &[u8],
child_info: ChildInfo,
key: &[u8],
) -> T {
take(storage_key, child_info, key).unwrap_or_else(Default::default)
}
/// Return the value of the item in storage under `key`, or `default_value` if there is no
/// explicit entry. Ensure there is no explicit entry on return.
pub fn take_or<T: Codec + Sized>(storage_key: &[u8],key: &[u8], default_value: T) -> T {
take(storage_key, key).unwrap_or(default_value)
pub fn take_or<T: Codec + Sized>(
storage_key: &[u8],
child_info: ChildInfo,
key: &[u8],
default_value: T,
) -> T {
take(storage_key, child_info, key).unwrap_or(default_value)
}
/// Return the value of the item in storage under `key`, or `default_value()` if there is no
/// explicit entry. Ensure there is no explicit entry on return.
pub fn take_or_else<T: Codec + Sized, F: FnOnce() -> T>(
storage_key: &[u8],
child_info: ChildInfo,
key: &[u8],
default_value: F,
) -> T {
take(storage_key, key).unwrap_or_else(default_value)
take(storage_key, child_info, key).unwrap_or_else(default_value)
}
/// Check to see if `key` has an explicit entry in storage.
pub fn exists(storage_key: &[u8], key: &[u8]) -> bool {
sp_io::storage::child_read(storage_key, key, &mut [0;0][..], 0).is_some()
pub fn exists(
storage_key: &[u8],
child_info: ChildInfo,
key: &[u8],
) -> bool {
let (data, child_type) = child_info.info();
sp_io::storage::child_read(
storage_key, data, child_type,
key, &mut [0;0][..], 0,
).is_some()
}
/// Remove all `storage_key` key/values
pub fn kill_storage(storage_key: &[u8]) {
sp_io::storage::child_storage_kill(storage_key)
pub fn kill_storage(
storage_key: &[u8],
child_info: ChildInfo,
) {
let (data, child_type) = child_info.info();
sp_io::storage::child_storage_kill(
storage_key,
data,
child_type,
)
}
/// Ensure `key` has no explicit entry in storage.
pub fn kill(storage_key: &[u8], key: &[u8]) {
sp_io::storage::child_clear(storage_key, key);
pub fn kill(
storage_key: &[u8],
child_info: ChildInfo,
key: &[u8],
) {
let (data, child_type) = child_info.info();
sp_io::storage::child_clear(
storage_key,
data,
child_type,
key,
);
}
/// Get a Vec of bytes from storage.
pub fn get_raw(storage_key: &[u8], key: &[u8]) -> Option<Vec<u8>> {
sp_io::storage::child_get(storage_key, key)
pub fn get_raw(
storage_key: &[u8],
child_info: ChildInfo,
key: &[u8],
) -> Option<Vec<u8>> {
let (data, child_type) = child_info.info();
sp_io::storage::child_get(
storage_key,
data,
child_type,
key,
)
}
/// Put a raw byte slice into storage.
pub fn put_raw(storage_key: &[u8], key: &[u8], value: &[u8]) {
sp_io::storage::child_set(storage_key, key, value)
pub fn put_raw(
storage_key: &[u8],
child_info: ChildInfo,
key: &[u8],
value: &[u8],
) {
let (data, child_type) = child_info.info();
sp_io::storage::child_set(
storage_key,
data,
child_type,
key,
value,
)
}
/// Calculate current child root value.
pub fn child_root(
storage_key: &[u8],
) -> Vec<u8> {
sp_io::storage::child_root(
storage_key,
)
}
@@ -300,7 +300,10 @@ fn new_test_ext() -> sp_io::TestExternalities {
#[test]
fn storage_instance_independance() {
let mut storage = Default::default();
let mut storage = primitives::storage::Storage {
top: std::collections::BTreeMap::new(),
children: std::collections::HashMap::new()
};
state_machine::BasicExternalities::execute_with_storage(&mut storage, || {
module2::Value::<Runtime>::put(0);
module2::Value::<Runtime, module2::Instance1>::put(0);
@@ -320,7 +323,7 @@ fn storage_instance_independance() {
module2::DoubleMap::<module2::Instance3>::insert(&0, &0, &0);
});
// 16 storage values + 4 linked_map head.
assert_eq!(storage.0.len(), 16 + 4);
assert_eq!(storage.top.len(), 16 + 4);
}
#[test]
+8 -5
View File
@@ -703,11 +703,14 @@ impl<T: Trait> Module<T> {
/// Get the basic externalities for this module, useful for tests.
#[cfg(any(feature = "std", test))]
pub fn externalities() -> TestExternalities {
TestExternalities::new((map![
<BlockHash<T>>::hashed_key_for(T::BlockNumber::zero()) => [69u8; 32].encode(),
<Number<T>>::hashed_key().to_vec() => T::BlockNumber::one().encode(),
<ParentHash<T>>::hashed_key().to_vec() => [69u8; 32].encode()
], map![]))
TestExternalities::new(primitives::storage::Storage {
top: map![
<BlockHash<T>>::hashed_key_for(T::BlockNumber::zero()) => [69u8; 32].encode(),
<Number<T>>::hashed_key().to_vec() => T::BlockNumber::one().encode(),
<ParentHash<T>>::hashed_key().to_vec() => [69u8; 32].encode()
],
children: map![],
})
}
/// Set the block number to something in particular. Can be used as an alternative to
+60 -14
View File
@@ -24,7 +24,7 @@
use std::any::{Any, TypeId};
use primitives_storage::ChildStorageKey;
use primitives_storage::{ChildStorageKey, ChildInfo};
pub use scope_limited::{set_and_run_with_externalities, with_externalities};
pub use extensions::{Extension, Extensions, ExtensionStore};
@@ -45,7 +45,12 @@ pub trait Externalities: ExtensionStore {
/// Get child storage value hash. This may be optimized for large values.
///
/// Returns an `Option` that holds the SCALE encoded hash.
fn child_storage_hash(&self, storage_key: ChildStorageKey, key: &[u8]) -> Option<Vec<u8>>;
fn child_storage_hash(
&self,
storage_key: ChildStorageKey,
child_info: ChildInfo,
key: &[u8],
) -> Option<Vec<u8>>;
/// Read original runtime storage, ignoring any overlayed changes.
fn original_storage(&self, key: &[u8]) -> Option<Vec<u8>>;
@@ -53,7 +58,12 @@ pub trait Externalities: ExtensionStore {
/// Read original runtime child storage, ignoring any overlayed changes.
///
/// Returns an `Option` that holds the SCALE encoded hash.
fn original_child_storage(&self, storage_key: ChildStorageKey, key: &[u8]) -> Option<Vec<u8>>;
fn original_child_storage(
&self,
storage_key: ChildStorageKey,
child_info: ChildInfo,
key: &[u8],
) -> Option<Vec<u8>>;
/// Get original storage value hash, ignoring any overlayed changes.
/// This may be optimized for large values.
@@ -68,13 +78,19 @@ pub trait Externalities: ExtensionStore {
fn original_child_storage_hash(
&self,
storage_key: ChildStorageKey,
child_info: ChildInfo,
key: &[u8],
) -> Option<Vec<u8>>;
/// Read child runtime storage.
///
/// Returns an `Option` that holds the SCALE encoded hash.
fn child_storage(&self, storage_key: ChildStorageKey, key: &[u8]) -> Option<Vec<u8>>;
fn child_storage(
&self,
storage_key: ChildStorageKey,
child_info: ChildInfo,
key: &[u8],
) -> Option<Vec<u8>>;
/// Set storage entry `key` of current contract being called (effective immediately).
fn set_storage(&mut self, key: Vec<u8>, value: Vec<u8>) {
@@ -82,8 +98,14 @@ pub trait Externalities: ExtensionStore {
}
/// Set child storage entry `key` of current contract being called (effective immediately).
fn set_child_storage(&mut self, storage_key: ChildStorageKey, key: Vec<u8>, value: Vec<u8>) {
self.place_child_storage(storage_key, key, Some(value))
fn set_child_storage(
&mut self,
storage_key: ChildStorageKey,
child_info: ChildInfo,
key: Vec<u8>,
value: Vec<u8>,
) {
self.place_child_storage(storage_key, child_info, key, Some(value))
}
/// Clear a storage entry (`key`) of current contract being called (effective immediately).
@@ -92,8 +114,13 @@ pub trait Externalities: ExtensionStore {
}
/// Clear a child storage entry (`key`) of current contract being called (effective immediately).
fn clear_child_storage(&mut self, storage_key: ChildStorageKey, key: &[u8]) {
self.place_child_storage(storage_key, key.to_vec(), None)
fn clear_child_storage(
&mut self,
storage_key: ChildStorageKey,
child_info: ChildInfo,
key: &[u8],
) {
self.place_child_storage(storage_key, child_info, key.to_vec(), None)
}
/// Whether a storage entry exists.
@@ -102,24 +129,39 @@ pub trait Externalities: ExtensionStore {
}
/// Whether a child storage entry exists.
fn exists_child_storage(&self, storage_key: ChildStorageKey, key: &[u8]) -> bool {
self.child_storage(storage_key, key).is_some()
fn exists_child_storage(
&self,
storage_key: ChildStorageKey,
child_info: ChildInfo,
key: &[u8],
) -> bool {
self.child_storage(storage_key, child_info, key).is_some()
}
/// Returns the key immediately following the given key, if it exists.
fn next_storage_key(&self, key: &[u8]) -> Option<Vec<u8>>;
/// Returns the key immediately following the given key, if it exists, in child storage.
fn next_child_storage_key(&self, storage_key: ChildStorageKey, key: &[u8]) -> Option<Vec<u8>>;
fn next_child_storage_key(
&self,
storage_key: ChildStorageKey,
child_info: ChildInfo,
key: &[u8],
) -> Option<Vec<u8>>;
/// Clear an entire child storage.
fn kill_child_storage(&mut self, storage_key: ChildStorageKey);
fn kill_child_storage(&mut self, storage_key: ChildStorageKey, child_info: ChildInfo);
/// Clear storage entries which keys are start with the given prefix.
fn clear_prefix(&mut self, prefix: &[u8]);
/// Clear child storage entries which keys are start with the given prefix.
fn clear_child_prefix(&mut self, storage_key: ChildStorageKey, prefix: &[u8]);
fn clear_child_prefix(
&mut self,
storage_key: ChildStorageKey,
child_info: ChildInfo,
prefix: &[u8],
);
/// Set or clear a storage entry (`key`) of current contract being called (effective immediately).
fn place_storage(&mut self, key: Vec<u8>, value: Option<Vec<u8>>);
@@ -128,6 +170,7 @@ pub trait Externalities: ExtensionStore {
fn place_child_storage(
&mut self,
storage_key: ChildStorageKey,
child_info: ChildInfo,
key: Vec<u8>,
value: Option<Vec<u8>>,
);
@@ -147,7 +190,10 @@ pub trait Externalities: ExtensionStore {
/// storage keys in the top-level storage map.
/// If the storage root equals the default hash as defined by the trie, the key in the top-level
/// storage map will be removed.
fn child_storage_root(&mut self, storage_key: ChildStorageKey) -> Vec<u8>;
fn child_storage_root(
&mut self,
storage_key: ChildStorageKey,
) -> Vec<u8>;
/// Get the change trie root of the current storage overlay at a block with given parent.
/// `parent` is expects a SCALE endcoded hash.
+118 -27
View File
@@ -39,7 +39,7 @@ use primitives::{
traits::KeystoreExt,
offchain::{OffchainExt, TransactionPoolExt},
hexdisplay::HexDisplay,
storage::ChildStorageKey,
storage::{ChildStorageKey, ChildInfo},
};
use primitives::{
@@ -91,10 +91,28 @@ pub trait Storage {
self.storage(key).map(|s| s.to_vec())
}
/// Returns the data for `key` in the child storage or `None` if the key can not be found.
fn child_get(&self, child_storage_key: &[u8], key: &[u8]) -> Option<Vec<u8>> {
/// All Child api uses :
/// - A `child_storage_key` to define the anchor point for the child proof
/// (commonly the location where the child root is stored in its parent trie).
/// - A `child_storage_types` to identify the kind of the child type and how its
/// `child definition` parameter is encoded.
/// - A `child_definition_parameter` which is the additional information required
/// to use the child trie. For instance defaults child tries requires this to
/// contain a collision free unique id.
///
/// This function specifically returns the data for `key` in the child storage or `None`
/// if the key can not be found.
fn child_get(
&self,
child_storage_key: &[u8],
child_definition: &[u8],
child_type: u32,
key: &[u8],
) -> Option<Vec<u8>> {
let storage_key = child_storage_key_or_panic(child_storage_key);
self.child_storage(storage_key, key).map(|s| s.to_vec())
let child_info = ChildInfo::resolve_child_info(child_type, child_definition)
.expect("Invalid child definition");
self.child_storage(storage_key, child_info, key).map(|s| s.to_vec())
}
/// Get `key` from storage, placing the value into `value_out` and return the number of
@@ -117,15 +135,21 @@ pub trait Storage {
/// doesn't exist at all.
/// If `value_out` length is smaller than the returned length, only `value_out` length bytes
/// are copied into `value_out`.
///
/// See `child_get` for common child api parameters.
fn child_read(
&self,
child_storage_key: &[u8],
child_definition: &[u8],
child_type: u32,
key: &[u8],
value_out: &mut [u8],
value_offset: u32,
) -> Option<u32> {
let storage_key = child_storage_key_or_panic(child_storage_key);
self.child_storage(storage_key, key)
let child_info = ChildInfo::resolve_child_info(child_type, child_definition)
.expect("Invalid child definition");
self.child_storage(storage_key, child_info, key)
.map(|value| {
let value_offset = value_offset as usize;
let data = &value[value_offset.min(value.len())..];
@@ -141,9 +165,20 @@ pub trait Storage {
}
/// Set `key` to `value` in the child storage denoted by `child_storage_key`.
fn child_set(&mut self, child_storage_key: &[u8], key: &[u8], value: &[u8]) {
///
/// See `child_get` for common child api parameters.
fn child_set(
&mut self,
child_storage_key: &[u8],
child_definition: &[u8],
child_type: u32,
key: &[u8],
value: &[u8],
) {
let storage_key = child_storage_key_or_panic(child_storage_key);
self.set_child_storage(storage_key, key.to_vec(), value.to_vec());
let child_info = ChildInfo::resolve_child_info(child_type, child_definition)
.expect("Invalid child definition");
self.set_child_storage(storage_key, child_info, key.to_vec(), value.to_vec());
}
/// Clear the storage of the given `key` and its value.
@@ -152,15 +187,34 @@ pub trait Storage {
}
/// Clear the given child storage of the given `key` and its value.
fn child_clear(&mut self, child_storage_key: &[u8], key: &[u8]) {
///
/// See `child_get` for common child api parameters.
fn child_clear(
&mut self,
child_storage_key: &[u8],
child_definition: &[u8],
child_type: u32,
key: &[u8],
) {
let storage_key = child_storage_key_or_panic(child_storage_key);
self.clear_child_storage(storage_key, key);
let child_info = ChildInfo::resolve_child_info(child_type, child_definition)
.expect("Invalid child definition");
self.clear_child_storage(storage_key, child_info, key);
}
/// Clear an entire child storage.
fn child_storage_kill(&mut self, child_storage_key: &[u8]) {
///
/// See `child_get` for common child api parameters.
fn child_storage_kill(
&mut self,
child_storage_key: &[u8],
child_definition: &[u8],
child_type: u32,
) {
let storage_key = child_storage_key_or_panic(child_storage_key);
self.kill_child_storage(storage_key);
let child_info = ChildInfo::resolve_child_info(child_type, child_definition)
.expect("Invalid child definition");
self.kill_child_storage(storage_key, child_info);
}
/// Check whether the given `key` exists in storage.
@@ -169,9 +223,19 @@ pub trait Storage {
}
/// Check whether the given `key` exists in storage.
fn child_exists(&self, child_storage_key: &[u8], key: &[u8]) -> bool {
///
/// See `child_get` for common child api parameters.
fn child_exists(
&self,
child_storage_key: &[u8],
child_definition: &[u8],
child_type: u32,
key: &[u8],
) -> bool {
let storage_key = child_storage_key_or_panic(child_storage_key);
self.exists_child_storage(storage_key, key)
let child_info = ChildInfo::resolve_child_info(child_type, child_definition)
.expect("Invalid child definition");
self.exists_child_storage(storage_key, child_info, key)
}
/// Clear the storage of each key-value pair where the key starts with the given `prefix`.
@@ -180,9 +244,19 @@ pub trait Storage {
}
/// Clear the child storage of each key-value pair where the key starts with the given `prefix`.
fn child_clear_prefix(&mut self, child_storage_key: &[u8], prefix: &[u8]) {
///
/// See `child_get` for common child api parameters.
fn child_clear_prefix(
&mut self,
child_storage_key: &[u8],
child_definition: &[u8],
child_type: u32,
prefix: &[u8],
) {
let storage_key = child_storage_key_or_panic(child_storage_key);
self.clear_child_prefix(storage_key, prefix);
let child_info = ChildInfo::resolve_child_info(child_type, child_definition)
.expect("Invalid child definition");
self.clear_child_prefix(storage_key, child_info, prefix);
}
/// "Commit" all existing operations and compute the resulting storage root.
@@ -199,7 +273,12 @@ pub trait Storage {
/// The hashing algorithm is defined by the `Block`.
///
/// Returns the SCALE encoded hash.
fn child_root(&mut self, child_storage_key: &[u8]) -> Vec<u8> {
///
/// See `child_get` for common child api parameters.
fn child_root(
&mut self,
child_storage_key: &[u8],
) -> Vec<u8> {
let storage_key = child_storage_key_or_panic(child_storage_key);
self.child_storage_root(storage_key)
}
@@ -220,9 +299,17 @@ pub trait Storage {
}
/// Get the next key in storage after the given one in lexicographic order in child storage.
fn child_next_key(&mut self, child_storage_key: &[u8], key: &[u8]) -> Option<Vec<u8>> {
fn child_next_key(
&mut self,
child_storage_key: &[u8],
child_definition: &[u8],
child_type: u32,
key: &[u8],
) -> Option<Vec<u8>> {
let storage_key = child_storage_key_or_panic(child_storage_key);
self.next_child_storage_key(storage_key, key)
let child_info = ChildInfo::resolve_child_info(child_type, child_definition)
.expect("Invalid child definition");
self.next_child_storage_key(storage_key, child_info, key)
}
}
@@ -817,6 +904,7 @@ mod tests {
use super::*;
use primitives::map;
use sp_state_machine::BasicExternalities;
use primitives::storage::Storage;
#[test]
fn storage_works() {
@@ -829,7 +917,10 @@ mod tests {
storage::set(b"foo", &[1, 2, 3][..]);
});
t = BasicExternalities::new(map![b"foo".to_vec() => b"bar".to_vec()], map![]);
t = BasicExternalities::new(Storage {
top: map![b"foo".to_vec() => b"bar".to_vec()],
children: map![],
});
t.execute_with(|| {
assert_eq!(storage::get(b"hello"), None);
@@ -839,10 +930,10 @@ mod tests {
#[test]
fn read_storage_works() {
let mut t = BasicExternalities::new(
map![b":test".to_vec() => b"\x0b\0\0\0Hello world".to_vec()],
map![],
);
let mut t = BasicExternalities::new(Storage {
top: map![b":test".to_vec() => b"\x0b\0\0\0Hello world".to_vec()],
children: map![],
});
t.execute_with(|| {
let mut v = [0u8; 4];
@@ -856,15 +947,15 @@ mod tests {
#[test]
fn clear_prefix_works() {
let mut t = BasicExternalities::new(
map![
let mut t = BasicExternalities::new(Storage {
top: map![
b":a".to_vec() => b"\x0b\0\0\0Hello world".to_vec(),
b":abcd".to_vec() => b"\x0b\0\0\0Hello world".to_vec(),
b":abc".to_vec() => b"\x0b\0\0\0Hello world".to_vec(),
b":abdd".to_vec() => b"\x0b\0\0\0Hello world".to_vec()
],
map![],
);
children: map![],
});
t.execute_with(|| {
storage::clear_prefix(b":abc");
+16 -13
View File
@@ -38,7 +38,7 @@ pub use paste;
pub use app_crypto;
#[cfg(feature = "std")]
pub use primitives::storage::{StorageOverlay, ChildrenStorageOverlay};
pub use primitives::storage::{Storage, StorageChild};
use sp_std::prelude::*;
use sp_std::convert::TryFrom;
@@ -121,15 +121,15 @@ use crate::traits::IdentifyAccount;
#[cfg(feature = "std")]
pub trait BuildStorage: Sized {
/// Build the storage out of this builder.
fn build_storage(&self) -> Result<(StorageOverlay, ChildrenStorageOverlay), String> {
let mut storage = (Default::default(), Default::default());
fn build_storage(&self) -> Result<primitives::storage::Storage, String> {
let mut storage = Default::default();
self.assimilate_storage(&mut storage)?;
Ok(storage)
}
/// Assimilate the storage for this module into pre-existing overlays.
fn assimilate_storage(
&self,
storage: &mut (StorageOverlay, ChildrenStorageOverlay),
storage: &mut primitives::storage::Storage,
) -> Result<(), String>;
}
@@ -139,23 +139,26 @@ pub trait BuildModuleGenesisStorage<T, I>: Sized {
/// Create the module genesis storage into the given `storage` and `child_storage`.
fn build_module_genesis_storage(
&self,
storage: &mut (StorageOverlay, ChildrenStorageOverlay),
storage: &mut primitives::storage::Storage,
) -> Result<(), String>;
}
#[cfg(feature = "std")]
impl BuildStorage for (StorageOverlay, ChildrenStorageOverlay) {
impl BuildStorage for primitives::storage::Storage {
fn assimilate_storage(
&self,
storage: &mut (StorageOverlay, ChildrenStorageOverlay),
storage: &mut primitives::storage::Storage,
)-> Result<(), String> {
storage.0.extend(self.0.iter().map(|(k, v)| (k.clone(), v.clone())));
for (k, other_map) in self.1.iter() {
storage.top.extend(self.top.iter().map(|(k, v)| (k.clone(), v.clone())));
for (k, other_map) in self.children.iter() {
let k = k.clone();
if let Some(map) = storage.1.get_mut(&k) {
map.extend(other_map.iter().map(|(k, v)| (k.clone(), v.clone())));
if let Some(map) = storage.children.get_mut(&k) {
map.data.extend(other_map.data.iter().map(|(k, v)| (k.clone(), v.clone())));
if !map.child_info.try_update(other_map.child_info.as_ref()) {
return Err("Incompatible child info update".to_string());
}
} else {
storage.1.insert(k, other_map.clone());
storage.children.insert(k, other_map.clone());
}
}
Ok(())
@@ -532,7 +535,7 @@ macro_rules! impl_outer_config {
impl $crate::BuildStorage for $main {
fn assimilate_storage(
&self,
storage: &mut ($crate::StorageOverlay, $crate::ChildrenStorageOverlay),
storage: &mut $crate::Storage,
) -> std::result::Result<(), String> {
$(
if let Some(ref extra) = self.[< $snake $(_ $instance )? >] {
+191 -74
View File
@@ -26,6 +26,7 @@ use trie::{
trie_types::{TrieDBMut, Layout},
};
use codec::{Encode, Codec};
use primitives::storage::{ChildInfo, OwnedChildInfo, Storage};
/// A state backend is used to read state data and can have changes committed
/// to it.
@@ -50,11 +51,21 @@ pub trait Backend<H: Hasher>: std::fmt::Debug {
}
/// Get keyed child storage or None if there is nothing associated.
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>;
/// Get child keyed storage value hash or None if there is nothing associated.
fn child_storage_hash(&self, storage_key: &[u8], key: &[u8]) -> Result<Option<H::Out>, Self::Error> {
self.child_storage(storage_key, key).map(|v| v.map(|v| H::hash(&v)))
fn child_storage_hash(
&self,
storage_key: &[u8],
child_info: ChildInfo,
key: &[u8],
) -> Result<Option<H::Out>, Self::Error> {
self.child_storage(storage_key, child_info, key).map(|v| v.map(|v| H::hash(&v)))
}
/// true if a key exists in storage.
@@ -63,8 +74,13 @@ pub trait Backend<H: Hasher>: std::fmt::Debug {
}
/// true if a key exists in child storage.
fn exists_child_storage(&self, storage_key: &[u8], key: &[u8]) -> Result<bool, Self::Error> {
Ok(self.child_storage(storage_key, key)?.is_some())
fn exists_child_storage(
&self,
storage_key: &[u8],
child_info: ChildInfo,
key: &[u8],
) -> Result<bool, Self::Error> {
Ok(self.child_storage(storage_key, child_info, key)?.is_some())
}
/// Return the next key in storage in lexicographic order or `None` if there is no value.
@@ -74,11 +90,17 @@ pub trait Backend<H: Hasher>: std::fmt::Debug {
fn next_child_storage_key(
&self,
storage_key: &[u8],
child_info: ChildInfo,
key: &[u8]
) -> Result<Option<Vec<u8>>, Self::Error>;
/// Retrieve all entries keys of child storage and call `f` for each of those keys.
fn for_keys_in_child_storage<F: FnMut(&[u8])>(&self, storage_key: &[u8], f: F);
fn for_keys_in_child_storage<F: FnMut(&[u8])>(
&self,
storage_key: &[u8],
child_info: ChildInfo,
f: F,
);
/// Retrieve all entries keys which start with the given prefix and
/// call `f` for each of those keys.
@@ -93,7 +115,13 @@ pub trait Backend<H: Hasher>: std::fmt::Debug {
/// Retrieve all child entries keys which start with the given prefix and
/// call `f` for each of those keys.
fn for_child_keys_with_prefix<F: FnMut(&[u8])>(&self, storage_key: &[u8], prefix: &[u8], f: F);
fn for_child_keys_with_prefix<F: FnMut(&[u8])>(
&self,
storage_key: &[u8],
child_info: ChildInfo,
prefix: &[u8],
f: F,
);
/// Calculate the storage root, with given delta over what is already stored in
/// the backend, and produce a "transaction" that can be used to commit.
@@ -106,7 +134,12 @@ pub trait Backend<H: Hasher>: std::fmt::Debug {
/// Calculate the child storage root, with given delta over what is already stored in
/// the backend, and produce a "transaction" that can be used to commit. The second argument
/// is true if child storage root equals default storage root.
fn child_storage_root<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;
@@ -122,9 +155,14 @@ pub trait Backend<H: Hasher>: std::fmt::Debug {
}
/// Get all keys of child storage with given prefix
fn child_keys(&self, child_storage_key: &[u8], prefix: &[u8]) -> Vec<Vec<u8>> {
fn child_keys(
&self,
storage_key: &[u8],
child_info: ChildInfo,
prefix: &[u8],
) -> Vec<Vec<u8>> {
let mut all = Vec::new();
self.for_child_keys_with_prefix(child_storage_key, prefix, |k| all.push(k.to_vec()));
self.for_child_keys_with_prefix(storage_key, child_info, prefix, |k| all.push(k.to_vec()));
all
}
@@ -144,15 +182,15 @@ pub trait Backend<H: Hasher>: std::fmt::Debug {
where
I1: IntoIterator<Item=(Vec<u8>, Option<Vec<u8>>)>,
I2i: IntoIterator<Item=(Vec<u8>, Option<Vec<u8>>)>,
I2: IntoIterator<Item=(Vec<u8>, I2i)>,
I2: IntoIterator<Item=(Vec<u8>, I2i, OwnedChildInfo)>,
H::Out: Ord + Encode,
{
let mut txs: Self::Transaction = Default::default();
let mut child_roots: Vec<_> = Default::default();
// child first
for (storage_key, child_delta) in child_deltas {
for (storage_key, child_delta, child_info) in child_deltas {
let (child_root, empty, child_txs) =
self.child_storage_root(&storage_key[..], child_delta);
self.child_storage_root(&storage_key[..], child_info.as_ref(), child_delta);
txs.consolidate(child_txs);
if empty {
child_roots.push((storage_key, None));
@@ -177,28 +215,49 @@ impl<'a, T: Backend<H>, H: Hasher> Backend<H> for &'a T {
(*self).storage(key)
}
fn child_storage(&self, storage_key: &[u8], key: &[u8]) -> Result<Option<Vec<u8>>, Self::Error> {
(*self).child_storage(storage_key, key)
fn child_storage(
&self,
storage_key: &[u8],
child_info: ChildInfo,
key: &[u8],
) -> Result<Option<Vec<u8>>, Self::Error> {
(*self).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).for_keys_in_child_storage(storage_key, child_info, f)
}
fn next_storage_key(&self, key: &[u8]) -> Result<Option<Vec<u8>>, Self::Error> {
(*self).next_storage_key(key)
}
fn next_child_storage_key(&self, storage_key: &[u8], key: &[u8]) -> Result<Option<Vec<u8>>, Self::Error> {
(*self).next_child_storage_key(storage_key, key)
}
fn for_keys_in_child_storage<F: FnMut(&[u8])>(&self, storage_key: &[u8], f: F) {
(*self).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).next_child_storage_key(storage_key, child_info, key)
}
fn for_keys_with_prefix<F: FnMut(&[u8])>(&self, prefix: &[u8], f: F) {
(*self).for_keys_with_prefix(prefix, f)
}
fn for_child_keys_with_prefix<F: FnMut(&[u8])>(&self, storage_key: &[u8], prefix: &[u8], f: F) {
(*self).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).for_child_keys_with_prefix(storage_key, child_info, prefix, f)
}
fn storage_root<I>(&self, delta: I) -> (H::Out, Self::Transaction)
@@ -209,12 +268,17 @@ impl<'a, T: Backend<H>, H: Hasher> Backend<H> for &'a T {
(*self).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).child_storage_root(storage_key, delta)
(*self).child_storage_root(storage_key, child_info, delta)
}
fn pairs(&self) -> Vec<(Vec<u8>, Vec<u8>)> {
@@ -238,7 +302,10 @@ impl Consolidate for () {
}
}
impl Consolidate for Vec<(Option<Vec<u8>>, Vec<u8>, Option<Vec<u8>>)> {
impl Consolidate for Vec<(
Option<(Vec<u8>, OwnedChildInfo)>,
Vec<(Vec<u8>, Option<Vec<u8>>)>,
)> {
fn consolidate(&mut self, mut other: Self) {
self.append(&mut other);
}
@@ -268,7 +335,7 @@ impl error::Error for Void {
/// In-memory backend. Fully recomputes tries each time `as_trie_backend` is called but useful for
/// tests and proof checking.
pub struct InMemory<H: Hasher> {
inner: HashMap<Option<Vec<u8>>, BTreeMap<Vec<u8>, Vec<u8>>>,
inner: HashMap<Option<(Vec<u8>, OwnedChildInfo)>, BTreeMap<Vec<u8>, Vec<u8>>>,
// This field is only needed for returning reference in `as_trie_backend`.
trie: Option<TrieBackend<MemoryDB<H>, H>>,
_hasher: PhantomData<H>,
@@ -310,19 +377,21 @@ impl<H: Hasher> InMemory<H> where H::Out: Codec {
/// Copy the state, with applied updates
pub fn update(&self, changes: <Self as Backend<H>>::Transaction) -> Self {
let mut inner = self.inner.clone();
for (storage_key, key, val) in changes {
match val {
Some(v) => { inner.entry(storage_key).or_default().insert(key, v); },
None => { inner.entry(storage_key).or_default().remove(&key); },
for (child_info, key_values) in changes {
let entry = inner.entry(child_info).or_default();
for (key, val) in key_values {
match val {
Some(v) => { entry.insert(key, v); },
None => { entry.remove(&key); },
}
}
}
inner.into()
}
}
impl<H: Hasher> From<HashMap<Option<Vec<u8>>, BTreeMap<Vec<u8>, Vec<u8>>>> for InMemory<H> {
fn from(inner: HashMap<Option<Vec<u8>>, BTreeMap<Vec<u8>, Vec<u8>>>) -> Self {
impl<H: Hasher> From<HashMap<Option<(Vec<u8>, OwnedChildInfo)>, BTreeMap<Vec<u8>, Vec<u8>>>> for InMemory<H> {
fn from(inner: HashMap<Option<(Vec<u8>, OwnedChildInfo)>, BTreeMap<Vec<u8>, Vec<u8>>>) -> Self {
InMemory {
inner: inner,
trie: None,
@@ -331,17 +400,11 @@ impl<H: Hasher> From<HashMap<Option<Vec<u8>>, BTreeMap<Vec<u8>, Vec<u8>>>> for I
}
}
impl<H: Hasher> From<(
BTreeMap<Vec<u8>, Vec<u8>>,
HashMap<Vec<u8>, BTreeMap<Vec<u8>, Vec<u8>>>,
)> for InMemory<H> {
fn from(inners: (
BTreeMap<Vec<u8>, Vec<u8>>,
HashMap<Vec<u8>, BTreeMap<Vec<u8>, Vec<u8>>>,
)) -> Self {
let mut inner: HashMap<Option<Vec<u8>>, BTreeMap<Vec<u8>, Vec<u8>>>
= inners.1.into_iter().map(|(k, v)| (Some(k), v)).collect();
inner.insert(None, inners.0);
impl<H: Hasher> From<Storage> for InMemory<H> {
fn from(inners: Storage) -> Self {
let mut inner: HashMap<Option<(Vec<u8>, OwnedChildInfo)>, BTreeMap<Vec<u8>, Vec<u8>>>
= inners.children.into_iter().map(|(k, c)| (Some((k, c.child_info)), c.data)).collect();
inner.insert(None, inners.top);
InMemory {
inner: inner,
trie: None,
@@ -362,12 +425,19 @@ impl<H: Hasher> From<BTreeMap<Vec<u8>, Vec<u8>>> for InMemory<H> {
}
}
impl<H: Hasher> From<Vec<(Option<Vec<u8>>, Vec<u8>, Option<Vec<u8>>)>> for InMemory<H> {
fn from(inner: Vec<(Option<Vec<u8>>, Vec<u8>, Option<Vec<u8>>)>) -> Self {
let mut expanded: HashMap<Option<Vec<u8>>, BTreeMap<Vec<u8>, Vec<u8>>> = HashMap::new();
for (child_key, key, value) in inner {
if let Some(value) = value {
expanded.entry(child_key).or_default().insert(key, value);
impl<H: Hasher> From<Vec<(Option<(Vec<u8>, OwnedChildInfo)>, Vec<(Vec<u8>, Option<Vec<u8>>)>)>>
for InMemory<H> {
fn from(
inner: Vec<(Option<(Vec<u8>, OwnedChildInfo)>, Vec<(Vec<u8>, Option<Vec<u8>>)>)>,
) -> Self {
let mut expanded: HashMap<Option<(Vec<u8>, OwnedChildInfo)>, BTreeMap<Vec<u8>, Vec<u8>>>
= HashMap::new();
for (child_info, key_values) in inner {
let entry = expanded.entry(child_info).or_default();
for (key, value) in key_values {
if let Some(value) = value {
entry.insert(key, value);
}
}
}
expanded.into()
@@ -376,22 +446,33 @@ impl<H: Hasher> From<Vec<(Option<Vec<u8>>, Vec<u8>, Option<Vec<u8>>)>> for InMem
impl<H: Hasher> InMemory<H> {
/// child storage key iterator
pub fn child_storage_keys(&self) -> impl Iterator<Item=&[u8]> {
self.inner.iter().filter_map(|item| item.0.as_ref().map(|v|&v[..]))
pub fn child_storage_keys(&self) -> impl Iterator<Item=(&[u8], ChildInfo)> {
self.inner.iter().filter_map(|item|
item.0.as_ref().map(|v|(&v.0[..], v.1.as_ref()))
)
}
}
impl<H: Hasher> Backend<H> for InMemory<H> where H::Out: Codec {
type Error = Void;
type Transaction = Vec<(Option<Vec<u8>>, Vec<u8>, Option<Vec<u8>>)>;
type Transaction = Vec<(
Option<(Vec<u8>, OwnedChildInfo)>,
Vec<(Vec<u8>, Option<Vec<u8>>)>,
)>;
type TrieBackendStorage = MemoryDB<H>;
fn storage(&self, key: &[u8]) -> Result<Option<Vec<u8>>, Self::Error> {
Ok(self.inner.get(&None).and_then(|map| map.get(key).map(Clone::clone)))
}
fn child_storage(&self, storage_key: &[u8], key: &[u8]) -> Result<Option<Vec<u8>>, Self::Error> {
Ok(self.inner.get(&Some(storage_key.to_vec())).and_then(|map| map.get(key).map(Clone::clone)))
fn child_storage(
&self,
storage_key: &[u8],
child_info: ChildInfo,
key: &[u8],
) -> Result<Option<Vec<u8>>, Self::Error> {
Ok(self.inner.get(&Some((storage_key.to_vec(), child_info.to_owned())))
.and_then(|map| map.get(key).map(Clone::clone)))
}
fn exists_storage(&self, key: &[u8]) -> Result<bool, Self::Error> {
@@ -406,9 +487,14 @@ impl<H: Hasher> Backend<H> for InMemory<H> where H::Out: Codec {
Ok(next_key)
}
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> {
let range = (ops::Bound::Excluded(key), ops::Bound::Unbounded);
let next_key = self.inner.get(&Some(storage_key.to_vec()))
let next_key = self.inner.get(&Some((storage_key.to_vec(), child_info.to_owned())))
.and_then(|map| map.range::<[u8], _>(range).next().map(|(k, _)| k).cloned());
Ok(next_key)
@@ -423,12 +509,24 @@ impl<H: Hasher> Backend<H> for InMemory<H> where H::Out: Codec {
.for_each(|(k, v)| f(k, v)));
}
fn for_keys_in_child_storage<F: FnMut(&[u8])>(&self, storage_key: &[u8], mut f: F) {
self.inner.get(&Some(storage_key.to_vec())).map(|map| map.keys().for_each(|k| f(&k)));
fn for_keys_in_child_storage<F: FnMut(&[u8])>(
&self,
storage_key: &[u8],
child_info: ChildInfo,
mut f: F,
) {
self.inner.get(&Some((storage_key.to_vec(), child_info.to_owned())))
.map(|map| map.keys().for_each(|k| f(&k)));
}
fn for_child_keys_with_prefix<F: FnMut(&[u8])>(&self, storage_key: &[u8], prefix: &[u8], f: F) {
self.inner.get(&Some(storage_key.to_vec()))
fn for_child_keys_with_prefix<F: FnMut(&[u8])>(
&self,
storage_key: &[u8],
child_info: ChildInfo,
prefix: &[u8],
f: F,
) {
self.inner.get(&Some((storage_key.to_vec(), child_info.to_owned())))
.map(|map| map.keys().filter(|key| key.starts_with(prefix)).map(|k| &**k).for_each(f));
}
@@ -448,19 +546,26 @@ impl<H: Hasher> Backend<H> for InMemory<H> where H::Out: Codec {
.filter_map(|(k, maybe_val)| maybe_val.map(|val| (k, val)))
);
let full_transaction = transaction.into_iter().map(|(k, v)| (None, k, v)).collect();
let full_transaction = transaction.into_iter().collect();
(root, full_transaction)
(root, vec![(None, full_transaction)])
}
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
{
let storage_key = storage_key.to_vec();
let child_info = Some((storage_key.clone(), child_info.to_owned()));
let existing_pairs = self.inner.get(&Some(storage_key.clone()))
let existing_pairs = self.inner.get(&child_info)
.into_iter()
.flat_map(|map| map.iter().map(|(k, v)| (k.clone(), Some(v.clone()))));
@@ -473,11 +578,11 @@ impl<H: Hasher> Backend<H> for InMemory<H> where H::Out: Codec {
.filter_map(|(k, maybe_val)| maybe_val.map(|val| (k, val)))
);
let full_transaction = transaction.into_iter().map(|(k, v)| (Some(storage_key.clone()), k, v)).collect();
let full_transaction = transaction.into_iter().collect();
let is_default = root == default_child_trie_root::<Layout<H>>(&storage_key);
(root, is_default, full_transaction)
(root, is_default, vec![(child_info, full_transaction)])
}
fn pairs(&self) -> Vec<(Vec<u8>, Vec<u8>)> {
@@ -494,8 +599,13 @@ impl<H: Hasher> Backend<H> for InMemory<H> where H::Out: Codec {
.collect()
}
fn child_keys(&self, storage_key: &[u8], prefix: &[u8]) -> Vec<Vec<u8>> {
self.inner.get(&Some(storage_key.to_vec()))
fn child_keys(
&self,
storage_key: &[u8],
child_info: ChildInfo,
prefix: &[u8],
) -> Vec<Vec<u8>> {
self.inner.get(&Some((storage_key.to_vec(), child_info.to_owned())))
.into_iter()
.flat_map(|map| map.keys().filter(|k| k.starts_with(prefix)).cloned())
.collect()
@@ -505,8 +615,10 @@ impl<H: Hasher> Backend<H> for InMemory<H> where H::Out: Codec {
let mut mdb = MemoryDB::default();
let mut new_child_roots = Vec::new();
let mut root_map = None;
for (storage_key, map) in &self.inner {
if let Some(storage_key) = storage_key.as_ref() {
for (child_info, map) in &self.inner {
if let Some((storage_key, _child_info)) = child_info.as_ref() {
// no need to use child_info at this point because we use a MemoryDB for
// proof (with PrefixedMemoryDB it would be needed).
let ch = insert_into_memory_db::<H, _>(&mut mdb, map.clone().into_iter())?;
new_child_roots.push((storage_key.clone(), ch.as_ref().into()));
} else {
@@ -556,11 +668,16 @@ mod tests {
#[test]
fn in_memory_with_child_trie_only() {
let storage = InMemory::<primitives::Blake2Hasher>::default();
let child_info = OwnedChildInfo::new_default(b"unique_id_1".to_vec());
let mut storage = storage.update(
vec![(Some(b"1".to_vec()), b"2".to_vec(), Some(b"3".to_vec()))]
vec![(
Some((b"1".to_vec(), child_info.clone())),
vec![(b"2".to_vec(), Some(b"3".to_vec()))]
)]
);
let trie_backend = storage.as_trie_backend().unwrap();
assert_eq!(trie_backend.child_storage(b"1", b"2").unwrap(), Some(b"3".to_vec()));
assert_eq!(trie_backend.child_storage(b"1", child_info.as_ref(), b"2").unwrap(),
Some(b"3".to_vec()));
assert!(trie_backend.storage(b"1").unwrap().is_some());
}
}
+114 -80
View File
@@ -17,7 +17,7 @@
//! Basic implementation for Externalities.
use std::{
collections::{HashMap, BTreeMap}, any::{TypeId, Any}, iter::FromIterator, mem, ops::Bound
collections::BTreeMap, any::{TypeId, Any}, iter::FromIterator, ops::Bound
};
use crate::backend::{Backend, InMemory};
use hash_db::Hasher;
@@ -25,54 +25,47 @@ use trie::{TrieConfiguration, default_child_trie_root};
use trie::trie_types::Layout;
use primitives::{
storage::{
well_known_keys::is_child_storage_key, ChildStorageKey, StorageOverlay,
ChildrenStorageOverlay
well_known_keys::is_child_storage_key, ChildStorageKey, Storage,
ChildInfo, StorageChild,
},
traits::Externalities, Blake2Hasher,
};
use log::warn;
use codec::Encode;
/// Simple HashMap-based Externalities impl.
/// Simple Map-based Externalities impl.
#[derive(Debug)]
pub struct BasicExternalities {
top: StorageOverlay,
children: ChildrenStorageOverlay,
inner: Storage,
}
impl BasicExternalities {
/// Create a new instance of `BasicExternalities`
pub fn new(top: StorageOverlay, children: ChildrenStorageOverlay) -> Self {
BasicExternalities {
top,
children,
}
pub fn new(inner: Storage) -> Self {
BasicExternalities { inner }
}
/// Insert key/value
pub fn insert(&mut self, k: Vec<u8>, v: Vec<u8>) -> Option<Vec<u8>> {
self.top.insert(k, v)
self.inner.top.insert(k, v)
}
/// Consume self and returns inner storages
pub fn into_storages(self) -> (
BTreeMap<Vec<u8>, Vec<u8>>,
HashMap<Vec<u8>, BTreeMap<Vec<u8>, Vec<u8>>>,
) {
(self.top, self.children)
pub fn into_storages(self) -> Storage {
self.inner
}
/// Execute the given closure `f` with the externalities set and initialized with `storage`.
///
/// Returns the result of the closure and updates `storage` with all changes.
pub fn execute_with_storage<R>(
storage: &mut (StorageOverlay, ChildrenStorageOverlay),
storage: &mut primitives::storage::Storage,
f: impl FnOnce() -> R,
) -> R {
let mut ext = Self {
top: mem::replace(&mut storage.0, BTreeMap::default()),
children: mem::replace(&mut storage.1, HashMap::default()),
};
let mut ext = Self { inner: Storage {
top: std::mem::replace(&mut storage.top, Default::default()),
children: std::mem::replace(&mut storage.children, Default::default()),
}};
let r = ext.execute_with(f);
@@ -91,34 +84,35 @@ impl BasicExternalities {
impl PartialEq for BasicExternalities {
fn eq(&self, other: &BasicExternalities) -> bool {
self.top.eq(&other.top) && self.children.eq(&other.children)
self.inner.top.eq(&other.inner.top)
&& self.inner.children.eq(&other.inner.children)
}
}
impl FromIterator<(Vec<u8>, Vec<u8>)> for BasicExternalities {
fn from_iter<I: IntoIterator<Item=(Vec<u8>, Vec<u8>)>>(iter: I) -> Self {
let mut t = Self::default();
t.top.extend(iter);
t.inner.top.extend(iter);
t
}
}
impl Default for BasicExternalities {
fn default() -> Self { Self::new(Default::default(), Default::default()) }
fn default() -> Self { Self::new(Default::default()) }
}
impl From<BTreeMap<Vec<u8>, Vec<u8>>> for BasicExternalities {
fn from(hashmap: BTreeMap<Vec<u8>, Vec<u8>>) -> Self {
BasicExternalities {
BasicExternalities { inner: Storage {
top: hashmap,
children: Default::default(),
}
}}
}
}
impl Externalities for BasicExternalities {
fn storage(&self, key: &[u8]) -> Option<Vec<u8>> {
self.top.get(key).cloned()
self.inner.top.get(key).cloned()
}
fn storage_hash(&self, key: &[u8]) -> Option<Vec<u8>> {
@@ -133,35 +127,56 @@ impl Externalities for BasicExternalities {
self.storage_hash(key)
}
fn child_storage(&self, storage_key: ChildStorageKey, key: &[u8]) -> Option<Vec<u8>> {
self.children.get(storage_key.as_ref()).and_then(|child| child.get(key)).cloned()
fn child_storage(
&self,
storage_key: ChildStorageKey,
_child_info: ChildInfo,
key: &[u8],
) -> Option<Vec<u8>> {
self.inner.children.get(storage_key.as_ref()).and_then(|child| child.data.get(key)).cloned()
}
fn child_storage_hash(&self, storage_key: ChildStorageKey, key: &[u8]) -> Option<Vec<u8>> {
self.child_storage(storage_key, key).map(|v| Blake2Hasher::hash(&v).encode())
fn child_storage_hash(
&self,
storage_key: ChildStorageKey,
child_info: ChildInfo,
key: &[u8],
) -> Option<Vec<u8>> {
self.child_storage(storage_key, child_info, key).map(|v| Blake2Hasher::hash(&v).encode())
}
fn original_child_storage_hash(
&self,
storage_key: ChildStorageKey,
child_info: ChildInfo,
key: &[u8],
) -> Option<Vec<u8>> {
self.child_storage_hash(storage_key, key)
self.child_storage_hash(storage_key, child_info, key)
}
fn original_child_storage(&self, storage_key: ChildStorageKey, key: &[u8]) -> Option<Vec<u8>> {
Externalities::child_storage(self, storage_key, key)
fn original_child_storage(
&self,
storage_key: ChildStorageKey,
child_info: ChildInfo,
key: &[u8],
) -> Option<Vec<u8>> {
Externalities::child_storage(self, storage_key, child_info, key)
}
fn next_storage_key(&self, key: &[u8]) -> Option<Vec<u8>> {
let range = (Bound::Excluded(key), Bound::Unbounded);
self.top.range::<[u8], _>(range).next().map(|(k, _)| k).cloned()
self.inner.top.range::<[u8], _>(range).next().map(|(k, _)| k).cloned()
}
fn next_child_storage_key(&self, storage_key: ChildStorageKey, key: &[u8]) -> Option<Vec<u8>> {
fn next_child_storage_key(
&self,
storage_key: ChildStorageKey,
_child_info: ChildInfo,
key: &[u8],
) -> Option<Vec<u8>> {
let range = (Bound::Excluded(key), Bound::Unbounded);
self.children.get(storage_key.as_ref())
.and_then(|child| child.range::<[u8], _>(range).next().map(|(k, _)| k).cloned())
self.inner.children.get(storage_key.as_ref())
.and_then(|child| child.data.range::<[u8], _>(range).next().map(|(k, _)| k).cloned())
}
fn place_storage(&mut self, key: Vec<u8>, maybe_value: Option<Vec<u8>>) {
@@ -171,27 +186,36 @@ impl Externalities for BasicExternalities {
}
match maybe_value {
Some(value) => { self.top.insert(key, value); }
None => { self.top.remove(&key); }
Some(value) => { self.inner.top.insert(key, value); }
None => { self.inner.top.remove(&key); }
}
}
fn place_child_storage(
&mut self,
storage_key: ChildStorageKey,
child_info: ChildInfo,
key: Vec<u8>,
value: Option<Vec<u8>>,
) {
let child_map = self.children.entry(storage_key.into_owned()).or_default();
let child_map = self.inner.children.entry(storage_key.into_owned())
.or_insert_with(|| StorageChild {
data: Default::default(),
child_info: child_info.to_owned(),
});
if let Some(value) = value {
child_map.insert(key, value);
child_map.data.insert(key, value);
} else {
child_map.remove(&key);
child_map.data.remove(&key);
}
}
fn kill_child_storage(&mut self, storage_key: ChildStorageKey) {
self.children.remove(storage_key.as_ref());
fn kill_child_storage(
&mut self,
storage_key: ChildStorageKey,
_child_info: ChildInfo,
) {
self.inner.children.remove(storage_key.as_ref());
}
fn clear_prefix(&mut self, prefix: &[u8]) {
@@ -203,27 +227,32 @@ impl Externalities for BasicExternalities {
return;
}
let to_remove = self.top.range::<[u8], _>((Bound::Included(prefix), Bound::Unbounded))
let to_remove = self.inner.top.range::<[u8], _>((Bound::Included(prefix), Bound::Unbounded))
.map(|(k, _)| k)
.take_while(|k| k.starts_with(prefix))
.cloned()
.collect::<Vec<_>>();
for key in to_remove {
self.top.remove(&key);
self.inner.top.remove(&key);
}
}
fn clear_child_prefix(&mut self, storage_key: ChildStorageKey, prefix: &[u8]) {
if let Some(child) = self.children.get_mut(storage_key.as_ref()) {
let to_remove = child.range::<[u8], _>((Bound::Included(prefix), Bound::Unbounded))
fn clear_child_prefix(
&mut self,
storage_key: ChildStorageKey,
_child_info: ChildInfo,
prefix: &[u8],
) {
if let Some(child) = self.inner.children.get_mut(storage_key.as_ref()) {
let to_remove = child.data.range::<[u8], _>((Bound::Included(prefix), Bound::Unbounded))
.map(|(k, _)| k)
.take_while(|k| k.starts_with(prefix))
.cloned()
.collect::<Vec<_>>();
for key in to_remove {
child.remove(&key);
child.data.remove(&key);
}
}
}
@@ -231,8 +260,8 @@ impl Externalities for BasicExternalities {
fn chain_id(&self) -> u64 { 42 }
fn storage_root(&mut self) -> Vec<u8> {
let mut top = self.top.clone();
let keys: Vec<_> = self.children.keys().map(|k| k.to_vec()).collect();
let mut top = self.inner.top.clone();
let keys: Vec<_> = self.inner.children.keys().map(|k| k.to_vec()).collect();
// Single child trie implementation currently allows using the same child
// empty root for all child trie. Using null storage key until multiple
// type of child trie support.
@@ -243,20 +272,24 @@ impl Externalities for BasicExternalities {
.expect("Map only feed by valid keys; qed"),
);
if &empty_hash[..] == &child_root[..] {
top.remove(&storage_key);
top.remove(storage_key.as_slice());
} else {
top.insert(storage_key, child_root);
}
}
Layout::<Blake2Hasher>::trie_root(self.top.clone()).as_ref().into()
Layout::<Blake2Hasher>::trie_root(self.inner.top.clone()).as_ref().into()
}
fn child_storage_root(&mut self, storage_key: ChildStorageKey) -> Vec<u8> {
if let Some(child) = self.children.get(storage_key.as_ref()) {
let delta = child.clone().into_iter().map(|(k, v)| (k, Some(v)));
fn child_storage_root(
&mut self,
storage_key: ChildStorageKey,
) -> Vec<u8> {
if let Some(child) = self.inner.children.get(storage_key.as_ref()) {
let delta = child.data.clone().into_iter().map(|(k, v)| (k, Some(v)));
InMemory::<Blake2Hasher>::default().child_storage_root(storage_key.as_ref(), delta).0
InMemory::<Blake2Hasher>::default()
.child_storage_root(storage_key.as_ref(), child.child_info.as_ref(), delta).0
} else {
default_child_trie_root::<Layout<Blake2Hasher>>(storage_key.as_ref())
}.encode()
@@ -278,9 +311,12 @@ impl externalities::ExtensionStore for BasicExternalities {
mod tests {
use super::*;
use primitives::map;
use primitives::storage::{Storage, StorageChild};
use primitives::storage::well_known_keys::CODE;
use hex_literal::hex;
const CHILD_INFO_1: ChildInfo<'static> = ChildInfo::new_default(b"unique_id_1");
#[test]
fn commit_should_work() {
let mut ext = BasicExternalities::default();
@@ -306,37 +342,35 @@ mod tests {
fn children_works() {
let child_storage = b":child_storage:default:test".to_vec();
let mut ext = BasicExternalities::new(
Default::default(),
map![
child_storage.clone() => map![
b"doe".to_vec() => b"reindeer".to_vec()
]
let mut ext = BasicExternalities::new(Storage {
top: Default::default(),
children: map![
child_storage.clone() => StorageChild {
data: map![ b"doe".to_vec() => b"reindeer".to_vec() ],
child_info: CHILD_INFO_1.to_owned(),
}
]
);
});
let child = || ChildStorageKey::from_vec(child_storage.clone()).unwrap();
assert_eq!(ext.child_storage(child(), b"doe"), Some(b"reindeer".to_vec()));
assert_eq!(ext.child_storage(child(), CHILD_INFO_1, b"doe"), Some(b"reindeer".to_vec()));
ext.set_child_storage(child(), b"dog".to_vec(), b"puppy".to_vec());
assert_eq!(ext.child_storage(child(), b"dog"), Some(b"puppy".to_vec()));
ext.set_child_storage(child(), CHILD_INFO_1, b"dog".to_vec(), b"puppy".to_vec());
assert_eq!(ext.child_storage(child(), CHILD_INFO_1, b"dog"), Some(b"puppy".to_vec()));
ext.clear_child_storage(child(), b"dog");
assert_eq!(ext.child_storage(child(), b"dog"), None);
ext.clear_child_storage(child(), CHILD_INFO_1, b"dog");
assert_eq!(ext.child_storage(child(), CHILD_INFO_1, b"dog"), None);
ext.kill_child_storage(child());
assert_eq!(ext.child_storage(child(), b"doe"), None);
ext.kill_child_storage(child(), CHILD_INFO_1);
assert_eq!(ext.child_storage(child(), CHILD_INFO_1, b"doe"), None);
}
#[test]
fn basic_externalities_is_empty() {
// Make sure no values are set by default in `BasicExternalities`.
let (storage, child_storage) = BasicExternalities::new(
Default::default(),
Default::default(),
).into_storages();
assert!(storage.is_empty());
assert!(child_storage.is_empty());
let storage = BasicExternalities::new(Default::default()).into_storages();
assert!(storage.top.is_empty());
assert!(storage.children.is_empty());
}
}
@@ -133,10 +133,15 @@ fn prepare_extrinsics_input_inner<'a, B, H, Number>(
H: Hasher,
Number: BlockNumber,
{
let (committed, prospective) = if let Some(sk) = storage_key.as_ref() {
(changes.committed.children.get(sk), changes.prospective.children.get(sk))
let (committed, prospective, child_info) = if let Some(sk) = storage_key.as_ref() {
let child_info = changes.child_info(sk).cloned();
(
changes.committed.children.get(sk).map(|c| &c.0),
changes.prospective.children.get(sk).map(|c| &c.0),
child_info,
)
} else {
(Some(&changes.committed.top), Some(&changes.prospective.top))
(Some(&changes.committed.top), Some(&changes.prospective.top), None)
};
committed.iter().flat_map(|c| c.iter())
.chain(prospective.iter().flat_map(|c| c.iter()))
@@ -148,8 +153,11 @@ fn prepare_extrinsics_input_inner<'a, B, H, Number>(
// AND are not in storage at the beginning of operation
if let Some(sk) = storage_key.as_ref() {
if !changes.child_storage(sk, k).map(|v| v.is_some()).unwrap_or_default() {
if !backend.exists_child_storage(sk, k).map_err(|e| format!("{}", e))? {
return Ok(map);
if let Some(child_info) = child_info.as_ref() {
if !backend.exists_child_storage(sk, child_info.as_ref(), k)
.map_err(|e| format!("{}", e))? {
return Ok(map);
}
}
}
} else {
@@ -332,12 +340,16 @@ mod test {
use codec::Encode;
use primitives::Blake2Hasher;
use primitives::storage::well_known_keys::{EXTRINSIC_INDEX};
use primitives::storage::ChildInfo;
use crate::backend::InMemory;
use crate::changes_trie::{RootsStorage, Configuration, storage::InMemoryStorage};
use crate::changes_trie::build_cache::{IncompleteCacheAction, IncompleteCachedBuildData};
use crate::overlayed_changes::{OverlayedValue, OverlayedChangeSet};
use super::*;
const CHILD_INFO_1: ChildInfo<'static> = ChildInfo::new_default(b"unique_id_1");
const CHILD_INFO_2: ChildInfo<'static> = ChildInfo::new_default(b"unique_id_2");
fn prepare_for_build(zero: u64) -> (
InMemory<Blake2Hasher>,
InMemoryStorage<Blake2Hasher, u64>,
@@ -416,18 +428,18 @@ mod test {
}),
].into_iter().collect(),
children: vec![
(child_trie_key1.clone(), vec![
(child_trie_key1.clone(), (vec![
(vec![100], OverlayedValue {
value: Some(vec![200]),
extrinsics: Some(vec![0, 2].into_iter().collect())
})
].into_iter().collect()),
(child_trie_key2, vec![
].into_iter().collect(), CHILD_INFO_1.to_owned())),
(child_trie_key2, (vec![
(vec![100], OverlayedValue {
value: Some(vec![200]),
extrinsics: Some(vec![0, 2].into_iter().collect())
})
].into_iter().collect()),
].into_iter().collect(), CHILD_INFO_2.to_owned())),
].into_iter().collect()
},
committed: OverlayedChangeSet { top: vec![
@@ -445,12 +457,12 @@ mod test {
}),
].into_iter().collect(),
children: vec![
(child_trie_key1, vec![
(child_trie_key1, (vec![
(vec![100], OverlayedValue {
value: Some(vec![202]),
extrinsics: Some(vec![3].into_iter().collect())
})
].into_iter().collect()),
].into_iter().collect(), CHILD_INFO_1.to_owned())),
].into_iter().collect(),
},
changes_trie_config: Some(config.clone()),
+163 -68
View File
@@ -25,7 +25,7 @@ use crate::{
use hash_db::Hasher;
use primitives::{
storage::{ChildStorageKey, well_known_keys::is_child_storage_key},
storage::{ChildStorageKey, well_known_keys::is_child_storage_key, ChildInfo},
traits::Externalities, hexdisplay::HexDisplay, hash::H256,
};
use trie::{trie_types::Layout, MemoryDB, default_child_trie_root};
@@ -229,13 +229,19 @@ where
result.map(|r| r.encode())
}
fn child_storage(&self, storage_key: ChildStorageKey, key: &[u8]) -> Option<Vec<u8>> {
fn child_storage(
&self,
storage_key: ChildStorageKey,
child_info: ChildInfo,
key: &[u8],
) -> Option<Vec<u8>> {
let _guard = panic_handler::AbortGuard::force_abort();
let result = self.overlay
.child_storage(storage_key.as_ref(), key)
.map(|x| x.map(|x| x.to_vec()))
.unwrap_or_else(||
self.backend.child_storage(storage_key.as_ref(), key).expect(EXT_NOT_ALLOWED_TO_FAIL)
self.backend.child_storage(storage_key.as_ref(), child_info, key)
.expect(EXT_NOT_ALLOWED_TO_FAIL)
);
trace!(target: "state-trace", "{:04x}: GetChild({}) {}={:?}",
@@ -248,7 +254,12 @@ where
result
}
fn child_storage_hash(&self, storage_key: ChildStorageKey, key: &[u8]) -> Option<Vec<u8>> {
fn child_storage_hash(
&self,
storage_key: ChildStorageKey,
_child_info: ChildInfo,
key: &[u8],
) -> Option<Vec<u8>> {
let _guard = panic_handler::AbortGuard::force_abort();
let result = self.overlay
.child_storage(storage_key.as_ref(), key)
@@ -267,10 +278,15 @@ where
result.map(|r| r.encode())
}
fn original_child_storage(&self, storage_key: ChildStorageKey, key: &[u8]) -> Option<Vec<u8>> {
fn original_child_storage(
&self,
storage_key: ChildStorageKey,
child_info: ChildInfo,
key: &[u8],
) -> Option<Vec<u8>> {
let _guard = panic_handler::AbortGuard::force_abort();
let result = self.backend
.child_storage(storage_key.as_ref(), key)
.child_storage(storage_key.as_ref(), child_info, key)
.expect(EXT_NOT_ALLOWED_TO_FAIL);
trace!(target: "state-trace", "{:04x}: ChildOriginal({}) {}={:?}",
@@ -285,11 +301,12 @@ where
fn original_child_storage_hash(
&self,
storage_key: ChildStorageKey,
child_info: ChildInfo,
key: &[u8],
) -> Option<Vec<u8>> {
let _guard = panic_handler::AbortGuard::force_abort();
let result = self.backend
.child_storage_hash(storage_key.as_ref(), key)
.child_storage_hash(storage_key.as_ref(), child_info, key)
.expect(EXT_NOT_ALLOWED_TO_FAIL);
trace!(target: "state-trace", "{}: ChildHashOriginal({}) {}={:?}",
@@ -317,13 +334,18 @@ where
}
fn exists_child_storage(&self, storage_key: ChildStorageKey, key: &[u8]) -> bool {
fn exists_child_storage(
&self,
storage_key: ChildStorageKey,
child_info: ChildInfo,
key: &[u8],
) -> bool {
let _guard = panic_handler::AbortGuard::force_abort();
let result = match self.overlay.child_storage(storage_key.as_ref(), key) {
Some(x) => x.is_some(),
_ => self.backend
.exists_child_storage(storage_key.as_ref(), key)
.exists_child_storage(storage_key.as_ref(), child_info, key)
.expect(EXT_NOT_ALLOWED_TO_FAIL),
};
@@ -351,8 +373,14 @@ where
}
}
fn next_child_storage_key(&self, storage_key: ChildStorageKey, key: &[u8]) -> Option<Vec<u8>> {
let next_backend_key = self.backend.next_child_storage_key(storage_key.as_ref(), key)
fn next_child_storage_key(
&self,
storage_key: ChildStorageKey,
child_info: ChildInfo,
key: &[u8],
) -> Option<Vec<u8>> {
let next_backend_key = self.backend
.next_child_storage_key(storage_key.as_ref(), child_info, key)
.expect(EXT_NOT_ALLOWED_TO_FAIL);
let next_overlay_key_change = self.overlay.next_child_storage_key_change(
storage_key.as_ref(),
@@ -365,7 +393,11 @@ where
(_, Some(overlay_key)) => if overlay_key.1.value.is_some() {
Some(overlay_key.0.to_vec())
} else {
self.next_child_storage_key(storage_key, &overlay_key.0[..])
self.next_child_storage_key(
storage_key,
child_info,
&overlay_key.0[..],
)
},
}
}
@@ -389,6 +421,7 @@ where
fn place_child_storage(
&mut self,
storage_key: ChildStorageKey,
child_info: ChildInfo,
key: Vec<u8>,
value: Option<Vec<u8>>,
) {
@@ -401,10 +434,14 @@ where
let _guard = panic_handler::AbortGuard::force_abort();
self.mark_dirty();
self.overlay.set_child_storage(storage_key.into_owned(), key, value);
self.overlay.set_child_storage(storage_key.into_owned(), child_info, key, value);
}
fn kill_child_storage(&mut self, storage_key: ChildStorageKey) {
fn kill_child_storage(
&mut self,
storage_key: ChildStorageKey,
child_info: ChildInfo,
) {
trace!(target: "state-trace", "{:04x}: KillChild({})",
self.id,
HexDisplay::from(&storage_key.as_ref()),
@@ -412,9 +449,9 @@ where
let _guard = panic_handler::AbortGuard::force_abort();
self.mark_dirty();
self.overlay.clear_child_storage(storage_key.as_ref());
self.backend.for_keys_in_child_storage(storage_key.as_ref(), |key| {
self.overlay.set_child_storage(storage_key.as_ref().to_vec(), key.to_vec(), None);
self.overlay.clear_child_storage(storage_key.as_ref(), child_info);
self.backend.for_keys_in_child_storage(storage_key.as_ref(), child_info, |key| {
self.overlay.set_child_storage(storage_key.as_ref().to_vec(), child_info, key.to_vec(), None);
});
}
@@ -436,7 +473,12 @@ where
});
}
fn clear_child_prefix(&mut self, storage_key: ChildStorageKey, prefix: &[u8]) {
fn clear_child_prefix(
&mut self,
storage_key: ChildStorageKey,
child_info: ChildInfo,
prefix: &[u8],
) {
trace!(target: "state-trace", "{:04x}: ClearChildPrefix({}) {}",
self.id,
HexDisplay::from(&storage_key.as_ref()),
@@ -445,9 +487,9 @@ where
let _guard = panic_handler::AbortGuard::force_abort();
self.mark_dirty();
self.overlay.clear_child_prefix(storage_key.as_ref(), prefix);
self.backend.for_child_keys_with_prefix(storage_key.as_ref(), prefix, |key| {
self.overlay.set_child_storage(storage_key.as_ref().to_vec(), key.to_vec(), None);
self.overlay.clear_child_prefix(storage_key.as_ref(), child_info, prefix);
self.backend.for_child_keys_with_prefix(storage_key.as_ref(), child_info, prefix, |key| {
self.overlay.set_child_storage(storage_key.as_ref().to_vec(), child_info, key.to_vec(), None);
});
}
@@ -465,16 +507,23 @@ where
return root.encode();
}
let child_storage_keys =
self.overlay.prospective.children.keys()
let child_storage_keys = self.overlay.prospective.children.keys()
.chain(self.overlay.committed.children.keys());
let child_delta_iter = child_storage_keys.map(|storage_key|
(storage_key.clone(), self.overlay.committed.children.get(storage_key)
.into_iter()
.flat_map(|map| map.iter().map(|(k, v)| (k.clone(), v.value.clone())))
.chain(self.overlay.prospective.children.get(storage_key)
(
storage_key.clone(),
self.overlay.committed.children.get(storage_key)
.into_iter()
.flat_map(|map| map.iter().map(|(k, v)| (k.clone(), v.value.clone()))))));
.flat_map(|(map, _)| map.iter().map(|(k, v)| (k.clone(), v.value.clone())))
.chain(
self.overlay.prospective.children.get(storage_key)
.into_iter()
.flat_map(|(map, _)| map.iter().map(|(k, v)| (k.clone(), v.value.clone())))
),
self.overlay.child_info(storage_key).cloned()
.expect("child info initialized in either committed or prospective"),
)
);
// compute and memoize
@@ -490,7 +539,10 @@ where
root.encode()
}
fn child_storage_root(&mut self, storage_key: ChildStorageKey) -> Vec<u8> {
fn child_storage_root(
&mut self,
storage_key: ChildStorageKey,
) -> Vec<u8> {
let _guard = panic_handler::AbortGuard::force_abort();
if self.storage_transaction.is_some() {
let root = self
@@ -508,29 +560,53 @@ where
} else {
let storage_key = storage_key.as_ref();
let (root, is_empty, _) = {
let delta = self.overlay.committed.children.get(storage_key)
.into_iter()
.flat_map(|map| map.clone().into_iter().map(|(k, v)| (k, v.value)))
.chain(self.overlay.prospective.children.get(storage_key)
.into_iter()
.flat_map(|map| map.clone().into_iter().map(|(k, v)| (k, v.value))));
if let Some(child_info) = self.overlay.child_info(storage_key).cloned() {
let (root, is_empty, _) = {
let delta = self.overlay.committed.children.get(storage_key)
.into_iter()
.flat_map(|(map, _)| map.clone().into_iter().map(|(k, v)| (k, v.value)))
.chain(
self.overlay.prospective.children.get(storage_key)
.into_iter()
.flat_map(|(map, _)| map.clone().into_iter().map(|(k, v)| (k, v.value)))
);
self.backend.child_storage_root(storage_key, delta)
};
self.backend.child_storage_root(storage_key, child_info.as_ref(), delta)
};
if is_empty {
self.overlay.set_storage(storage_key.into(), None);
let root = root.encode();
// We store update in the overlay in order to be able to use 'self.storage_transaction'
// cache. This is brittle as it rely on Ext only querying the trie backend for
// storage root.
// A better design would be to manage 'child_storage_transaction' in a
// similar way as 'storage_transaction' but for each child trie.
if is_empty {
self.overlay.set_storage(storage_key.into(), None);
} else {
self.overlay.set_storage(storage_key.into(), Some(root.clone()));
}
trace!(target: "state-trace", "{:04x}: ChildRoot({}) {}",
self.id,
HexDisplay::from(&storage_key.as_ref()),
HexDisplay::from(&root.as_ref()),
);
root
} else {
self.overlay.set_storage(storage_key.into(), Some(root.encode()));
// empty overlay
let root = self
.storage(storage_key.as_ref())
.and_then(|k| Decode::decode(&mut &k[..]).ok())
.unwrap_or(
default_child_trie_root::<Layout<H>>(storage_key.as_ref())
);
trace!(target: "state-trace", "{:04x}: ChildRoot({}) (no change) {}",
self.id,
HexDisplay::from(&storage_key.as_ref()),
HexDisplay::from(&root.as_ref()),
);
root.encode()
}
trace!(target: "state-trace", "{:04x}: ChildRoot({}) {}",
self.id,
HexDisplay::from(&storage_key.as_ref()),
HexDisplay::from(&root.as_ref()),
);
root.encode()
}
}
@@ -579,13 +655,14 @@ mod tests {
use super::*;
use hex_literal::hex;
use codec::Encode;
use primitives::{Blake2Hasher, storage::well_known_keys::EXTRINSIC_INDEX};
use primitives::{Blake2Hasher, storage::well_known_keys::EXTRINSIC_INDEX, map};
use crate::{
changes_trie::{
Configuration as ChangesTrieConfiguration,
InMemoryStorage as InMemoryChangesTrieStorage,
}, backend::InMemory, overlayed_changes::OverlayedValue,
};
use primitives::storage::{Storage, StorageChild};
type TestBackend = InMemory<Blake2Hasher>;
type TestChangesTrieStorage = InMemoryChangesTrieStorage<Blake2Hasher, u64>;
@@ -659,11 +736,14 @@ mod tests {
let mut overlay = OverlayedChanges::default();
overlay.set_storage(vec![20], None);
overlay.set_storage(vec![30], Some(vec![31]));
let backend = vec![
(None, vec![10], Some(vec![10])),
(None, vec![20], Some(vec![20])),
(None, vec![40], Some(vec![40])),
].into();
let backend = Storage {
top: map![
vec![10] => vec![10],
vec![20] => vec![20],
vec![40] => vec![40]
],
children: map![]
}.into();
let ext = TestExt::new(&mut overlay, &backend, None, None);
@@ -689,35 +769,50 @@ mod tests {
#[test]
fn next_child_storage_key_works() {
let child = || ChildStorageKey::from_slice(b":child_storage:default:Child1").unwrap();
const CHILD_KEY_1: &[u8] = b":child_storage:default:Child1";
const CHILD_UUID_1: &[u8] = b"unique_id_1";
const CHILD_INFO_1: ChildInfo<'static> = ChildInfo::new_default(CHILD_UUID_1);
let child = || ChildStorageKey::from_slice(CHILD_KEY_1).unwrap();
let mut overlay = OverlayedChanges::default();
overlay.set_child_storage(child().as_ref().to_vec(), vec![20], None);
overlay.set_child_storage(child().as_ref().to_vec(), vec![30], Some(vec![31]));
let backend = vec![
(Some(child().as_ref().to_vec()), vec![10], Some(vec![10])),
(Some(child().as_ref().to_vec()), vec![20], Some(vec![20])),
(Some(child().as_ref().to_vec()), vec![40], Some(vec![40])),
].into();
overlay.set_child_storage(child().as_ref().to_vec(), CHILD_INFO_1, vec![20], None);
overlay.set_child_storage(child().as_ref().to_vec(), CHILD_INFO_1, vec![30], Some(vec![31]));
let backend = Storage {
top: map![],
children: map![
child().as_ref().to_vec() => StorageChild {
data: map![
vec![10] => vec![10],
vec![20] => vec![20],
vec![40] => vec![40]
],
child_info: CHILD_INFO_1.to_owned(),
}
],
}.into();
let ext = TestExt::new(&mut overlay, &backend, None, None);
// next_backend < next_overlay
assert_eq!(ext.next_child_storage_key(child(), &[5]), Some(vec![10]));
assert_eq!(ext.next_child_storage_key(child(), CHILD_INFO_1, &[5]), Some(vec![10]));
// next_backend == next_overlay but next_overlay is a delete
assert_eq!(ext.next_child_storage_key(child(), &[10]), Some(vec![30]));
assert_eq!(ext.next_child_storage_key(child(), CHILD_INFO_1, &[10]), Some(vec![30]));
// next_overlay < next_backend
assert_eq!(ext.next_child_storage_key(child(), &[20]), Some(vec![30]));
assert_eq!(ext.next_child_storage_key(child(), CHILD_INFO_1, &[20]), Some(vec![30]));
// next_backend exist but next_overlay doesn't exist
assert_eq!(ext.next_child_storage_key(child(), &[30]), Some(vec![40]));
assert_eq!(ext.next_child_storage_key(child(), CHILD_INFO_1, &[30]), Some(vec![40]));
drop(ext);
overlay.set_child_storage(child().as_ref().to_vec(), vec![50], Some(vec![50]));
overlay.set_child_storage(child().as_ref().to_vec(), CHILD_INFO_1, vec![50], Some(vec![50]));
let ext = TestExt::new(&mut overlay, &backend, None, None);
// next_overlay exist but next_backend doesn't exist
assert_eq!(ext.next_child_storage_key(child(), &[40]), Some(vec![50]));
assert_eq!(ext.next_child_storage_key(child(), CHILD_INFO_1, &[40]), Some(vec![50]));
}
}
+50 -5
View File
@@ -23,7 +23,7 @@ use log::{warn, trace};
use hash_db::Hasher;
use codec::{Decode, Encode, Codec};
use primitives::{
storage::well_known_keys, NativeOrEncoded, NeverNativeValue,
storage::{well_known_keys, ChildInfo}, NativeOrEncoded, NeverNativeValue,
traits::CodeExecutor, hexdisplay::HexDisplay, hash::H256,
};
use overlayed_changes::OverlayedChangeSet;
@@ -562,6 +562,7 @@ where
pub fn prove_child_read<B, H, I>(
mut backend: B,
storage_key: &[u8],
child_info: ChildInfo,
keys: I,
) -> Result<StorageProof, Box<dyn Error>>
where
@@ -573,7 +574,7 @@ where
{
let trie_backend = backend.as_trie_backend()
.ok_or_else(|| Box::new(ExecutionError::UnableToGenerateProof) as Box<dyn Error>)?;
prove_child_read_on_trie_backend(trie_backend, storage_key, keys)
prove_child_read_on_trie_backend(trie_backend, storage_key, child_info, keys)
}
/// Generate storage read proof on pre-created trie backend.
@@ -601,6 +602,7 @@ where
pub fn prove_child_read_on_trie_backend<S, H, I>(
trie_backend: &TrieBackend<S, H>,
storage_key: &[u8],
child_info: ChildInfo,
keys: I,
) -> Result<StorageProof, Box<dyn Error>>
where
@@ -613,7 +615,7 @@ where
let proving_backend = proving_backend::ProvingBackend::<_, H>::new(trie_backend);
for key in keys.into_iter() {
proving_backend
.child_storage(storage_key, key.as_ref())
.child_storage(storage_key, child_info.clone(), key.as_ref())
.map_err(|e| Box::new(e) as Box<dyn Error>)?;
}
Ok(proving_backend.extract_proof())
@@ -688,7 +690,9 @@ where
H: Hasher,
H::Out: Ord + Codec,
{
proving_backend.child_storage(storage_key, key).map_err(|e| Box::new(e) as Box<dyn Error>)
// Not a prefixed memory db, using empty unique id and include root resolution.
proving_backend.child_storage(storage_key, ChildInfo::new_default(&[]), key)
.map_err(|e| Box::new(e) as Box<dyn Error>)
}
/// Sets overlayed changes' changes trie configuration. Returns error if configuration
@@ -750,6 +754,8 @@ mod tests {
fallback_succeeds: bool,
}
const CHILD_INFO_1: ChildInfo<'static> = ChildInfo::new_default(b"unique_id_1");
impl CodeExecutor for DummyCodeExecutor {
type Error = u8;
@@ -982,22 +988,26 @@ mod tests {
ext.set_child_storage(
ChildStorageKey::from_slice(b":child_storage:default:testchild").unwrap(),
CHILD_INFO_1,
b"abc".to_vec(),
b"def".to_vec()
);
assert_eq!(
ext.child_storage(
ChildStorageKey::from_slice(b":child_storage:default:testchild").unwrap(),
CHILD_INFO_1,
b"abc"
),
Some(b"def".to_vec())
);
ext.kill_child_storage(
ChildStorageKey::from_slice(b":child_storage:default:testchild").unwrap()
ChildStorageKey::from_slice(b":child_storage:default:testchild").unwrap(),
CHILD_INFO_1,
);
assert_eq!(
ext.child_storage(
ChildStorageKey::from_slice(b":child_storage:default:testchild").unwrap(),
CHILD_INFO_1,
b"abc"
),
None
@@ -1033,6 +1043,7 @@ mod tests {
let remote_proof = prove_child_read(
remote_backend,
b":child_storage:default:sub1",
CHILD_INFO_1,
&[b"value3"],
).unwrap();
let local_result1 = read_child_proof_check::<Blake2Hasher, _>(
@@ -1081,6 +1092,40 @@ mod tests {
assert!(state_machine.execute(ExecutionStrategy::NativeWhenPossible).is_err());
}
#[test]
fn child_storage_uuid() {
const CHILD_INFO_1: ChildInfo<'static> = ChildInfo::new_default(b"unique_id_1");
const CHILD_INFO_2: ChildInfo<'static> = ChildInfo::new_default(b"unique_id_2");
use crate::trie_backend::tests::test_trie;
let mut overlay = OverlayedChanges::default();
let subtrie1 = ChildStorageKey::from_slice(b":child_storage:default:sub_test1").unwrap();
let subtrie2 = ChildStorageKey::from_slice(b":child_storage:default:sub_test2").unwrap();
let mut transaction = {
let backend = test_trie();
let changes_trie_storage = InMemoryChangesTrieStorage::<Blake2Hasher, u64>::new();
let mut ext = Ext::new(
&mut overlay,
&backend,
Some(&changes_trie_storage),
None,
);
ext.set_child_storage(subtrie1, CHILD_INFO_1, b"abc".to_vec(), b"def".to_vec());
ext.set_child_storage(subtrie2, CHILD_INFO_2, b"abc".to_vec(), b"def".to_vec());
ext.storage_root();
(ext.transaction().0).0
};
let mut duplicate = false;
for (k, (value, rc)) in transaction.drain().iter() {
// look for a key inserted twice: transaction rc is 2
if *rc == 2 {
duplicate = true;
println!("test duplicate for {:?} {:?}", k, value);
}
}
assert!(!duplicate);
}
#[test]
fn cannot_change_changes_trie_config_with_native_else_wasm() {
let backend = trie_backend::tests::test_trie();
@@ -21,7 +21,7 @@ use std::iter::FromIterator;
use std::collections::{HashMap, BTreeMap, BTreeSet};
use codec::Decode;
use crate::changes_trie::{NO_EXTRINSIC_INDEX, Configuration as ChangesTrieConfig};
use primitives::storage::well_known_keys::EXTRINSIC_INDEX;
use primitives::storage::{well_known_keys::EXTRINSIC_INDEX, OwnedChildInfo, ChildInfo};
use std::{mem, ops};
/// The overlayed changes to state to be queried on top of the backend.
@@ -57,7 +57,7 @@ pub struct OverlayedChangeSet {
/// Top level storage changes.
pub top: BTreeMap<Vec<u8>, OverlayedValue>,
/// Child storage changes.
pub children: HashMap<Vec<u8>, BTreeMap<Vec<u8>, OverlayedValue>>,
pub children: HashMap<Vec<u8>, (BTreeMap<Vec<u8>, OverlayedValue>, OwnedChildInfo)>,
}
#[cfg(test)]
@@ -119,13 +119,13 @@ impl OverlayedChanges {
/// value has been set.
pub fn child_storage(&self, storage_key: &[u8], key: &[u8]) -> Option<Option<&[u8]>> {
if let Some(map) = self.prospective.children.get(storage_key) {
if let Some(val) = map.get(key) {
if let Some(val) = map.0.get(key) {
return Some(val.value.as_ref().map(AsRef::as_ref));
}
}
if let Some(map) = self.committed.children.get(storage_key) {
if let Some(val) = map.get(key) {
if let Some(val) = map.0.get(key) {
return Some(val.value.as_ref().map(AsRef::as_ref));
}
}
@@ -150,10 +150,20 @@ impl OverlayedChanges {
/// Inserts the given key-value pair into the prospective child change set.
///
/// `None` can be used to delete a value specified by the given key.
pub(crate) fn set_child_storage(&mut self, storage_key: Vec<u8>, key: Vec<u8>, val: Option<Vec<u8>>) {
pub(crate) fn set_child_storage(
&mut self,
storage_key: Vec<u8>,
child_info: ChildInfo,
key: Vec<u8>,
val: Option<Vec<u8>>,
) {
let extrinsic_index = self.extrinsic_index();
let map_entry = self.prospective.children.entry(storage_key).or_default();
let entry = map_entry.entry(key).or_default();
let map_entry = self.prospective.children.entry(storage_key)
.or_insert_with(|| (Default::default(), child_info.to_owned()));
let updatable = map_entry.1.try_update(child_info);
debug_assert!(updatable);
let entry = map_entry.0.entry(key).or_default();
entry.value = val;
if let Some(extrinsic) = extrinsic_index {
@@ -168,11 +178,18 @@ impl OverlayedChanges {
/// change set, and still can be reverted by [`discard_prospective`].
///
/// [`discard_prospective`]: #method.discard_prospective
pub(crate) fn clear_child_storage(&mut self, storage_key: &[u8]) {
pub(crate) fn clear_child_storage(
&mut self,
storage_key: &[u8],
child_info: ChildInfo,
) {
let extrinsic_index = self.extrinsic_index();
let map_entry = self.prospective.children.entry(storage_key.to_vec()).or_default();
let map_entry = self.prospective.children.entry(storage_key.to_vec())
.or_insert_with(|| (Default::default(), child_info.to_owned()));
let updatable = map_entry.1.try_update(child_info);
debug_assert!(updatable);
map_entry.values_mut().for_each(|e| {
map_entry.0.values_mut().for_each(|e| {
if let Some(extrinsic) = extrinsic_index {
e.extrinsics.get_or_insert_with(Default::default)
.insert(extrinsic);
@@ -181,10 +198,10 @@ impl OverlayedChanges {
e.value = None;
});
if let Some(committed_map) = self.committed.children.get(storage_key) {
if let Some((committed_map, _child_info)) = self.committed.children.get(storage_key) {
for (key, value) in committed_map.iter() {
if !map_entry.contains_key(key) {
map_entry.insert(key.clone(), OverlayedValue {
if !map_entry.0.contains_key(key) {
map_entry.0.insert(key.clone(), OverlayedValue {
value: None,
extrinsics: extrinsic_index.map(|i| {
let mut e = value.extrinsics.clone()
@@ -235,11 +252,19 @@ impl OverlayedChanges {
}
}
pub(crate) fn clear_child_prefix(&mut self, storage_key: &[u8], prefix: &[u8]) {
pub(crate) fn clear_child_prefix(
&mut self,
storage_key: &[u8],
child_info: ChildInfo,
prefix: &[u8],
) {
let extrinsic_index = self.extrinsic_index();
let map_entry = self.prospective.children.entry(storage_key.to_vec()).or_default();
let map_entry = self.prospective.children.entry(storage_key.to_vec())
.or_insert_with(|| (Default::default(), child_info.to_owned()));
let updatable = map_entry.1.try_update(child_info);
debug_assert!(updatable);
for (key, entry) in map_entry.iter_mut() {
for (key, entry) in map_entry.0.iter_mut() {
if key.starts_with(prefix) {
entry.value = None;
@@ -250,12 +275,12 @@ impl OverlayedChanges {
}
}
if let Some(child_committed) = self.committed.children.get(storage_key) {
if let Some((child_committed, _child_info)) = self.committed.children.get(storage_key) {
// Then do the same with keys from commited changes.
// NOTE that we are making changes in the prospective change set.
for key in child_committed.keys() {
if key.starts_with(prefix) {
let entry = map_entry.entry(key.clone()).or_default();
let entry = map_entry.0.entry(key.clone()).or_default();
entry.value = None;
if let Some(extrinsic) = extrinsic_index {
@@ -287,10 +312,12 @@ impl OverlayedChanges {
.extend(prospective_extrinsics);
}
}
for (storage_key, map) in self.prospective.children.drain() {
let map_dest = self.committed.children.entry(storage_key).or_default();
for (storage_key, (map, child_info)) in self.prospective.children.drain() {
let child_content = self.committed.children.entry(storage_key)
.or_insert_with(|| (Default::default(), child_info));
// No update to child info at this point (will be needed for deletion).
for (key, val) in map.into_iter() {
let entry = map_dest.entry(key).or_default();
let entry = child_content.0.entry(key).or_default();
entry.value = val.value;
if let Some(prospective_extrinsics) = val.extrinsics {
@@ -308,12 +335,12 @@ impl OverlayedChanges {
/// Will panic if there are any uncommitted prospective changes.
pub fn into_committed(self) -> (
impl Iterator<Item=(Vec<u8>, Option<Vec<u8>>)>,
impl Iterator<Item=(Vec<u8>, impl Iterator<Item=(Vec<u8>, Option<Vec<u8>>)>)>,
impl Iterator<Item=(Vec<u8>, (impl Iterator<Item=(Vec<u8>, Option<Vec<u8>>)>, OwnedChildInfo))>,
){
assert!(self.prospective.is_empty());
(self.committed.top.into_iter().map(|(k, v)| (k, v.value)),
self.committed.children.into_iter()
.map(|(sk, v)| (sk, v.into_iter().map(|(k, v)| (k, v.value)))))
.map(|(sk, (v, ci))| (sk, (v.into_iter().map(|(k, v)| (k, v.value)), ci))))
}
/// Inserts storage entry responsible for current extrinsic index.
@@ -342,6 +369,18 @@ impl OverlayedChanges {
}
}
/// Get child info for a storage key.
/// Take the latest value so prospective first.
pub fn child_info(&self, storage_key: &[u8]) -> Option<&OwnedChildInfo> {
if let Some((_, ci)) = self.prospective.children.get(storage_key) {
return Some(&ci);
}
if let Some((_, ci)) = self.committed.children.get(storage_key) {
return Some(&ci);
}
None
}
/// Returns the next (in lexicographic order) storage key in the overlayed alongside its value.
/// If no value is next then `None` is returned.
pub fn next_storage_key_change(&self, key: &[u8]) -> Option<(&[u8], &OverlayedValue)> {
@@ -377,10 +416,10 @@ impl OverlayedChanges {
let range = (ops::Bound::Excluded(key), ops::Bound::Unbounded);
let next_prospective_key = self.prospective.children.get(storage_key)
.and_then(|map| map.range::<[u8], _>(range).next().map(|(k, v)| (&k[..], v)));
.and_then(|(map, _)| map.range::<[u8], _>(range).next().map(|(k, v)| (&k[..], v)));
let next_committed_key = self.committed.children.get(storage_key)
.and_then(|map| map.range::<[u8], _>(range).next().map(|(k, v)| (&k[..], v)));
.and_then(|(map, _)| map.range::<[u8], _>(range).next().map(|(k, v)| (&k[..], v)));
match (next_committed_key, next_prospective_key) {
// Committed is strictly less than prospective
@@ -636,13 +675,14 @@ mod tests {
#[test]
fn next_child_storage_key_change_works() {
let child = b"Child1".to_vec();
let child_info = ChildInfo::new_default(b"uniqueid");
let mut overlay = OverlayedChanges::default();
overlay.set_child_storage(child.clone(), vec![20], Some(vec![20]));
overlay.set_child_storage(child.clone(), vec![30], Some(vec![30]));
overlay.set_child_storage(child.clone(), vec![40], Some(vec![40]));
overlay.set_child_storage(child.clone(), child_info, vec![20], Some(vec![20]));
overlay.set_child_storage(child.clone(), child_info, vec![30], Some(vec![30]));
overlay.set_child_storage(child.clone(), child_info, vec![40], Some(vec![40]));
overlay.commit_prospective();
overlay.set_child_storage(child.clone(), vec![10], Some(vec![10]));
overlay.set_child_storage(child.clone(), vec![30], None);
overlay.set_child_storage(child.clone(), child_info, vec![10], Some(vec![10]));
overlay.set_child_storage(child.clone(), child_info, vec![30], None);
// next_prospective < next_committed
let next_to_5 = overlay.next_child_storage_key_change(&child, &[5]).unwrap();
@@ -664,7 +704,7 @@ mod tests {
assert_eq!(next_to_30.0.to_vec(), vec![40]);
assert_eq!(next_to_30.1.value, Some(vec![40]));
overlay.set_child_storage(child.clone(), vec![50], Some(vec![50]));
overlay.set_child_storage(child.clone(), child_info, vec![50], Some(vec![50]));
// next_prospective, no next_committed
let next_to_40 = overlay.next_child_storage_key_change(&child, &[40]).unwrap();
assert_eq!(next_to_40.0.to_vec(), vec![50]);
@@ -32,6 +32,7 @@ use crate::trie_backend_essence::{Ephemeral, TrieBackendEssence, TrieBackendStor
use crate::{Error, ExecutionError, Backend};
use std::collections::{HashMap, HashSet};
use crate::DBValue;
use primitives::storage::ChildInfo;
/// Patricia trie-based backend specialized in get value proofs.
pub struct ProvingBackendRecorder<'a, S: 'a + TrieBackendStorage<H>, H: 'a + Hasher> {
@@ -143,6 +144,7 @@ impl<'a, S, H> ProvingBackendRecorder<'a, S, H>
pub fn child_storage(
&mut self,
storage_key: &[u8],
child_info: ChildInfo,
key: &[u8]
) -> Result<Option<Vec<u8>>, String> {
let root = self.storage(storage_key)?
@@ -159,6 +161,7 @@ impl<'a, S, H> ProvingBackendRecorder<'a, S, H>
read_child_trie_value_with::<Layout<H>, _, _>(
storage_key,
child_info.keyspace(),
&eph,
&root.as_ref(),
key,
@@ -268,20 +271,35 @@ impl<'a, S, H> Backend<H> for ProvingBackend<'a, S, H>
self.0.storage(key)
}
fn child_storage(&self, storage_key: &[u8], key: &[u8]) -> Result<Option<Vec<u8>>, Self::Error> {
self.0.child_storage(storage_key, key)
fn child_storage(
&self,
storage_key: &[u8],
child_info: ChildInfo,
key: &[u8],
) -> Result<Option<Vec<u8>>, Self::Error> {
self.0.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.0.for_keys_in_child_storage(storage_key, child_info, f)
}
fn next_storage_key(&self, key: &[u8]) -> Result<Option<Vec<u8>>, Self::Error> {
self.0.next_storage_key(key)
}
fn next_child_storage_key(&self, storage_key: &[u8], key: &[u8]) -> Result<Option<Vec<u8>>, Self::Error> {
self.0.next_child_storage_key(storage_key, key)
}
fn for_keys_in_child_storage<F: FnMut(&[u8])>(&self, storage_key: &[u8], f: F) {
self.0.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.0.next_child_storage_key(storage_key, child_info, key)
}
fn for_keys_with_prefix<F: FnMut(&[u8])>(&self, prefix: &[u8], f: F) {
@@ -292,8 +310,14 @@ impl<'a, S, H> Backend<H> for ProvingBackend<'a, S, H>
self.0.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.0.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.0.for_child_keys_with_prefix(storage_key, child_info, prefix, f)
}
fn pairs(&self) -> Vec<(Vec<u8>, Vec<u8>)> {
@@ -304,8 +328,13 @@ impl<'a, S, H> Backend<H> for ProvingBackend<'a, S, H>
self.0.keys(prefix)
}
fn child_keys(&self, child_storage_key: &[u8], prefix: &[u8]) -> Vec<Vec<u8>> {
self.0.child_keys(child_storage_key, prefix)
fn child_keys(
&self,
storage_key: &[u8],
child_info: ChildInfo,
prefix: &[u8],
) -> Vec<Vec<u8>> {
self.0.child_keys(storage_key, child_info, prefix)
}
fn storage_root<I>(&self, delta: I) -> (H::Out, Self::Transaction)
@@ -314,12 +343,17 @@ impl<'a, S, H> Backend<H> for ProvingBackend<'a, S, H>
self.0.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.0.child_storage_root(storage_key, delta)
self.0.child_storage_root(storage_key, child_info, delta)
}
}
@@ -363,6 +397,9 @@ mod tests {
use primitives::{Blake2Hasher, storage::ChildStorageKey};
use crate::proving_backend::create_proof_check_backend;
const CHILD_INFO_1: ChildInfo<'static> = ChildInfo::new_default(b"unique_id_1");
const CHILD_INFO_2: ChildInfo<'static> = ChildInfo::new_default(b"unique_id_2");
fn test_proving<'a>(
trie_backend: &'a TrieBackend<PrefixedMemoryDB<Blake2Hasher>,Blake2Hasher>,
) -> ProvingBackend<'a, PrefixedMemoryDB<Blake2Hasher>, Blake2Hasher> {
@@ -408,9 +445,9 @@ mod tests {
#[test]
fn proof_recorded_and_checked() {
let contents = (0..64).map(|i| (None, vec![i], Some(vec![i]))).collect::<Vec<_>>();
let contents = (0..64).map(|i| (vec![i], Some(vec![i]))).collect::<Vec<_>>();
let in_memory = InMemory::<Blake2Hasher>::default();
let mut in_memory = in_memory.update(contents);
let mut in_memory = in_memory.update(vec![(None, contents)]);
let in_memory_root = in_memory.storage_root(::std::iter::empty()).0;
(0..64).for_each(|i| assert_eq!(in_memory.storage(&[i]).unwrap().unwrap(), vec![i]));
@@ -434,26 +471,29 @@ mod tests {
let subtrie2 = ChildStorageKey::from_slice(b":child_storage:default:sub2").unwrap();
let own1 = subtrie1.into_owned();
let own2 = subtrie2.into_owned();
let contents = (0..64).map(|i| (None, vec![i], Some(vec![i])))
.chain((28..65).map(|i| (Some(own1.clone()), vec![i], Some(vec![i]))))
.chain((10..15).map(|i| (Some(own2.clone()), vec![i], Some(vec![i]))))
.collect::<Vec<_>>();
let contents = vec![
(None, (0..64).map(|i| (vec![i], Some(vec![i]))).collect()),
(Some((own1.clone(), CHILD_INFO_1.to_owned())),
(28..65).map(|i| (vec![i], Some(vec![i]))).collect()),
(Some((own2.clone(), CHILD_INFO_2.to_owned())),
(10..15).map(|i| (vec![i], Some(vec![i]))).collect()),
];
let in_memory = InMemory::<Blake2Hasher>::default();
let mut in_memory = in_memory.update(contents);
let in_memory_root = in_memory.full_storage_root::<_, Vec<_>, _>(
::std::iter::empty(),
in_memory.child_storage_keys().map(|k|(k.to_vec(), Vec::new()))
in_memory.child_storage_keys().map(|k|(k.0.to_vec(), Vec::new(), k.1.to_owned()))
).0;
(0..64).for_each(|i| assert_eq!(
in_memory.storage(&[i]).unwrap().unwrap(),
vec![i]
));
(28..65).for_each(|i| assert_eq!(
in_memory.child_storage(&own1[..], &[i]).unwrap().unwrap(),
in_memory.child_storage(&own1[..], CHILD_INFO_1, &[i]).unwrap().unwrap(),
vec![i]
));
(10..15).for_each(|i| assert_eq!(
in_memory.child_storage(&own2[..], &[i]).unwrap().unwrap(),
in_memory.child_storage(&own2[..], CHILD_INFO_2, &[i]).unwrap().unwrap(),
vec![i]
));
@@ -481,7 +521,7 @@ mod tests {
assert_eq!(proof_check.storage(&[64]).unwrap(), None);
let proving = ProvingBackend::new(trie);
assert_eq!(proving.child_storage(&own1[..], &[64]), Ok(Some(vec![64])));
assert_eq!(proving.child_storage(&own1[..], CHILD_INFO_1, &[64]), Ok(Some(vec![64])));
let proof = proving.extract_proof();
let proof_check = create_proof_check_backend::<Blake2Hasher>(
@@ -489,7 +529,7 @@ mod tests {
proof
).unwrap();
assert_eq!(
proof_check.child_storage(&own1[..], &[64]).unwrap().unwrap(),
proof_check.child_storage(&own1[..], CHILD_INFO_1, &[64]).unwrap().unwrap(),
vec![64]
);
}
@@ -16,7 +16,7 @@
//! Test implementation for Externalities.
use std::{collections::{HashMap, BTreeMap}, any::{Any, TypeId}};
use std::any::{Any, TypeId};
use hash_db::Hasher;
use crate::{
backend::{InMemory, Backend}, OverlayedChanges,
@@ -28,15 +28,14 @@ use crate::{
};
use primitives::{
storage::{
well_known_keys::{CHANGES_TRIE_CONFIG, CODE, HEAP_PAGES, is_child_storage_key}
well_known_keys::{CHANGES_TRIE_CONFIG, CODE, HEAP_PAGES, is_child_storage_key},
Storage,
},
hash::H256, Blake2Hasher,
};
use codec::Encode;
use externalities::{Extensions, Extension};
type StorageTuple = (BTreeMap<Vec<u8>, Vec<u8>>, HashMap<Vec<u8>, BTreeMap<Vec<u8>, Vec<u8>>>);
/// Simple HashMap-based Externalities impl.
pub struct TestExternalities<H: Hasher<Out=H256>=Blake2Hasher, N: ChangesTrieBlockNumber=u64> {
overlay: OverlayedChanges,
@@ -57,42 +56,37 @@ impl<H: Hasher<Out=H256>, N: ChangesTrieBlockNumber> TestExternalities<H, N> {
}
/// Create a new instance of `TestExternalities` with storage.
pub fn new(storage: StorageTuple) -> Self {
pub fn new(storage: Storage) -> Self {
Self::new_with_code(&[], storage)
}
/// Create a new instance of `TestExternalities` with code and storage.
pub fn new_with_code(code: &[u8], mut storage: StorageTuple) -> Self {
pub fn new_with_code(code: &[u8], mut storage: Storage) -> Self {
let mut overlay = OverlayedChanges::default();
assert!(storage.0.keys().all(|key| !is_child_storage_key(key)));
assert!(storage.1.keys().all(|key| is_child_storage_key(key)));
assert!(storage.top.keys().all(|key| !is_child_storage_key(key)));
assert!(storage.children.keys().all(|key| is_child_storage_key(key)));
super::set_changes_trie_config(
&mut overlay,
storage.0.get(&CHANGES_TRIE_CONFIG.to_vec()).cloned(),
storage.top.get(&CHANGES_TRIE_CONFIG.to_vec()).cloned(),
false,
).expect("changes trie configuration is correct in test env; qed");
storage.0.insert(HEAP_PAGES.to_vec(), 8u64.encode());
storage.0.insert(CODE.to_vec(), code.to_vec());
let backend: HashMap<_, _> = storage.1.into_iter()
.map(|(keyspace, map)| (Some(keyspace), map))
.chain(Some((None, storage.0)).into_iter())
.collect();
storage.top.insert(HEAP_PAGES.to_vec(), 8u64.encode());
storage.top.insert(CODE.to_vec(), code.to_vec());
TestExternalities {
overlay,
changes_trie_storage: ChangesTrieInMemoryStorage::new(),
backend: backend.into(),
backend: storage.into(),
extensions: Default::default(),
}
}
/// Insert key/value into backend
pub fn insert(&mut self, k: Vec<u8>, v: Vec<u8>) {
self.backend = self.backend.update(vec![(None, k, Some(v))]);
self.backend = self.backend.update(vec![(None, vec![(k, Some(v))])]);
}
/// Registers the given extension for this instance.
@@ -107,19 +101,23 @@ impl<H: Hasher<Out=H256>, N: ChangesTrieBlockNumber> TestExternalities<H, N> {
/// Return a new backend with all pending value.
pub fn commit_all(&self) -> InMemory<H> {
let top = self.overlay.committed.top.clone().into_iter()
let top: Vec<_> = self.overlay.committed.top.clone().into_iter()
.chain(self.overlay.prospective.top.clone().into_iter())
.map(|(k, v)| (None, k, v.value));
.map(|(k, v)| (k, v.value)).collect();
let mut transaction = vec![(None, top)];
let children = self.overlay.committed.children.clone().into_iter()
self.overlay.committed.children.clone().into_iter()
.chain(self.overlay.prospective.children.clone().into_iter())
.flat_map(|(keyspace, map)| {
map.into_iter()
.map(|(k, v)| (Some(keyspace.clone()), k, v.value))
.collect::<Vec<_>>()
.for_each(|(keyspace, (map, child_info))| {
transaction.push((
Some((keyspace, child_info)),
map.into_iter()
.map(|(k, v)| (k, v.value))
.collect::<Vec<_>>(),
))
});
self.backend.update(top.chain(children).collect())
self.backend.update(transaction)
}
/// Execute the given closure while `self` is set as externalities.
@@ -149,8 +147,8 @@ impl<H: Hasher<Out=H256>, N: ChangesTrieBlockNumber> Default for TestExternaliti
fn default() -> Self { Self::new(Default::default()) }
}
impl<H: Hasher<Out=H256>, N: ChangesTrieBlockNumber> From<StorageTuple> for TestExternalities<H, N> {
fn from(storage: StorageTuple) -> Self {
impl<H: Hasher<Out=H256>, N: ChangesTrieBlockNumber> From<Storage> for TestExternalities<H, N> {
fn from(storage: Storage) -> Self {
Self::new(storage)
}
}
@@ -22,6 +22,7 @@ use trie::{Trie, delta_trie_root, default_child_trie_root, child_delta_trie_root
use trie::trie_types::{TrieDB, TrieError, Layout};
use crate::trie_backend_essence::{TrieBackendEssence, TrieBackendStorage, Ephemeral};
use crate::Backend;
use primitives::storage::ChildInfo;
use codec::{Codec, Decode};
/// Patricia trie-based backend. Transaction type is an overlay of changes to commit.
@@ -75,16 +76,26 @@ impl<S: TrieBackendStorage<H>, H: Hasher> Backend<H> for TrieBackend<S, H> where
self.essence.storage(key)
}
fn child_storage(&self, storage_key: &[u8], key: &[u8]) -> Result<Option<Vec<u8>>, Self::Error> {
self.essence.child_storage(storage_key, key)
fn child_storage(
&self,
storage_key: &[u8],
child_info: ChildInfo,
key: &[u8],
) -> Result<Option<Vec<u8>>, Self::Error> {
self.essence.child_storage(storage_key, child_info, key)
}
fn next_storage_key(&self, key: &[u8]) -> Result<Option<Vec<u8>>, Self::Error> {
self.essence.next_storage_key(key)
}
fn next_child_storage_key(&self, storage_key: &[u8], key: &[u8]) -> Result<Option<Vec<u8>>, Self::Error> {
self.essence.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.essence.next_child_storage_key(storage_key, child_info, key)
}
fn for_keys_with_prefix<F: FnMut(&[u8])>(&self, prefix: &[u8], f: F) {
@@ -95,12 +106,23 @@ impl<S: TrieBackendStorage<H>, H: Hasher> Backend<H> for TrieBackend<S, H> where
self.essence.for_key_values_with_prefix(prefix, f)
}
fn for_keys_in_child_storage<F: FnMut(&[u8])>(&self, storage_key: &[u8], f: F) {
self.essence.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.essence.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.essence.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.essence.for_child_keys_with_prefix(storage_key, child_info, prefix, f)
}
fn pairs(&self) -> Vec<(Vec<u8>, Vec<u8>)> {
@@ -168,7 +190,12 @@ impl<S: TrieBackendStorage<H>, H: Hasher> Backend<H> for TrieBackend<S, H> where
(root, write_overlay)
}
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,
@@ -193,6 +220,7 @@ impl<S: TrieBackendStorage<H>, H: Hasher> Backend<H> for TrieBackend<S, H> where
match child_delta_trie_root::<Layout<H>, _, _, _, _, _>(
storage_key,
child_info.keyspace(),
&mut eph,
root,
delta
@@ -217,13 +245,19 @@ pub mod tests {
use std::collections::HashSet;
use primitives::{Blake2Hasher, H256};
use codec::Encode;
use trie::{TrieMut, PrefixedMemoryDB, trie_types::TrieDBMut};
use trie::{TrieMut, PrefixedMemoryDB, trie_types::TrieDBMut, KeySpacedDBMut};
use super::*;
const CHILD_KEY_1: &[u8] = b":child_storage:default:sub1";
const CHILD_UUID_1: &[u8] = b"unique_id_1";
const CHILD_INFO_1: ChildInfo<'static> = ChildInfo::new_default(CHILD_UUID_1);
fn test_db() -> (PrefixedMemoryDB<Blake2Hasher>, H256) {
let mut root = H256::default();
let mut mdb = PrefixedMemoryDB::<Blake2Hasher>::default();
{
let mut mdb = KeySpacedDBMut::new(&mut mdb, CHILD_UUID_1);
let mut trie = TrieDBMut::new(&mut mdb, &mut root);
trie.insert(b"value3", &[142]).expect("insert failed");
trie.insert(b"value4", &[124]).expect("insert failed");
@@ -233,7 +267,7 @@ pub mod tests {
let mut sub_root = Vec::new();
root.encode_to(&mut sub_root);
let mut trie = TrieDBMut::new(&mut mdb, &mut root);
trie.insert(b":child_storage:default:sub1", &sub_root).expect("insert failed");
trie.insert(CHILD_KEY_1, &sub_root[..]).expect("insert failed");
trie.insert(b"key", b"value").expect("insert failed");
trie.insert(b"value1", &[42]).expect("insert failed");
trie.insert(b"value2", &[24]).expect("insert failed");
@@ -255,6 +289,15 @@ pub mod tests {
assert_eq!(test_trie().storage(b"key").unwrap(), Some(b"value".to_vec()));
}
#[test]
fn read_from_child_storage_returns_some() {
let test_trie = test_trie();
assert_eq!(
test_trie.child_storage(CHILD_KEY_1, CHILD_INFO_1, b"value3").unwrap(),
Some(vec![142u8]),
);
}
#[test]
fn read_from_storage_returns_none() {
assert_eq!(test_trie().storage(b"non-existing-key").unwrap(), None);
@@ -23,9 +23,10 @@ use log::{debug, warn};
use hash_db::{self, Hasher, EMPTY_PREFIX, Prefix};
use trie::{Trie, MemoryDB, PrefixedMemoryDB, DBValue,
default_child_trie_root, read_trie_value, read_child_trie_value,
for_keys_in_child_trie};
for_keys_in_child_trie, KeySpacedDB};
use trie::trie_types::{TrieDB, TrieError, Layout};
use crate::backend::Consolidate;
use primitives::storage::ChildInfo;
use codec::Encode;
/// Patricia trie-based storage trait.
@@ -67,7 +68,7 @@ impl<S: TrieBackendStorage<H>, H: Hasher> TrieBackendEssence<S, H> where H::Out:
/// Return the next key in the trie i.e. the minimum key that is strictly superior to `key` in
/// lexicographic order.
pub fn next_storage_key(&self, key: &[u8]) -> Result<Option<Vec<u8>>, String> {
self.next_storage_key_from_root(&self.root, key)
self.next_storage_key_from_root(&self.root, None, key)
}
/// Return the next key in the child trie i.e. the minimum key that is strictly superior to
@@ -75,6 +76,7 @@ impl<S: TrieBackendStorage<H>, H: Hasher> TrieBackendEssence<S, H> where H::Out:
pub fn next_child_storage_key(
&self,
storage_key: &[u8],
child_info: ChildInfo,
key: &[u8],
) -> Result<Option<Vec<u8>>, String> {
let child_root = match self.storage(storage_key)? {
@@ -90,13 +92,14 @@ impl<S: TrieBackendStorage<H>, H: Hasher> TrieBackendEssence<S, H> where H::Out:
// note: child_root and hash must be same size, panics otherwise.
hash.as_mut().copy_from_slice(&child_root[..]);
self.next_storage_key_from_root(&hash, key)
self.next_storage_key_from_root(&hash, Some(child_info), key)
}
/// Return next key from main trie or child trie by providing corresponding root.
fn next_storage_key_from_root(
&self,
root: &H::Out,
child_info: Option<ChildInfo>,
key: &[u8],
) -> Result<Option<Vec<u8>>, String> {
let mut read_overlay = S::Overlay::default();
@@ -104,8 +107,16 @@ impl<S: TrieBackendStorage<H>, H: Hasher> TrieBackendEssence<S, H> where H::Out:
storage: &self.storage,
overlay: &mut read_overlay,
};
let dyn_eph: &dyn hash_db::HashDBRef<_, _>;
let keyspace_eph;
if let Some(child_info) = child_info.as_ref() {
keyspace_eph = KeySpacedDB::new(&eph, child_info.keyspace());
dyn_eph = &keyspace_eph;
} else {
dyn_eph = &eph;
}
let trie = TrieDB::<H>::new(&eph, root)
let trie = TrieDB::<H>::new(dyn_eph, root)
.map_err(|e| format!("TrieDB creation error: {}", e))?;
let mut iter = trie.iter()
.map_err(|e| format!("TrieDB iteration error: {}", e))?;
@@ -148,7 +159,12 @@ impl<S: TrieBackendStorage<H>, H: Hasher> TrieBackendEssence<S, H> where H::Out:
}
/// Get the value of child storage at given key.
pub fn child_storage(&self, storage_key: &[u8], key: &[u8]) -> Result<Option<Vec<u8>>, String> {
pub fn child_storage(
&self,
storage_key: &[u8],
child_info: ChildInfo,
key: &[u8],
) -> Result<Option<Vec<u8>>, String> {
let root = self.storage(storage_key)?
.unwrap_or(default_child_trie_root::<Layout<H>>(storage_key).encode());
@@ -160,11 +176,17 @@ impl<S: TrieBackendStorage<H>, H: Hasher> TrieBackendEssence<S, H> where H::Out:
let map_e = |e| format!("Trie lookup error: {}", e);
read_child_trie_value::<Layout<H>, _>(storage_key, &eph, &root, key).map_err(map_e)
read_child_trie_value::<Layout<H>, _>(storage_key, child_info.keyspace(), &eph, &root, key)
.map_err(map_e)
}
/// Retrieve all entries keys of child storage and call `f` for each of those keys.
pub fn for_keys_in_child_storage<F: FnMut(&[u8])>(&self, storage_key: &[u8], f: F) {
pub fn for_keys_in_child_storage<F: FnMut(&[u8])>(
&self,
storage_key: &[u8],
child_info: ChildInfo,
f: F,
) {
let root = match self.storage(storage_key) {
Ok(v) => v.unwrap_or(default_child_trie_root::<Layout<H>>(storage_key).encode()),
Err(e) => {
@@ -181,6 +203,7 @@ impl<S: TrieBackendStorage<H>, H: Hasher> TrieBackendEssence<S, H> where H::Out:
if let Err(e) = for_keys_in_child_trie::<Layout<H>, _, Ephemeral<S, H>>(
storage_key,
child_info.keyspace(),
&eph,
&root,
f,
@@ -193,6 +216,7 @@ impl<S: TrieBackendStorage<H>, H: Hasher> TrieBackendEssence<S, H> where H::Out:
pub fn for_child_keys_with_prefix<F: FnMut(&[u8])>(
&self,
storage_key: &[u8],
child_info: ChildInfo,
prefix: &[u8],
mut f: F,
) {
@@ -205,13 +229,12 @@ impl<S: TrieBackendStorage<H>, H: Hasher> TrieBackendEssence<S, H> where H::Out:
};
let mut root = H::Out::default();
root.as_mut().copy_from_slice(&root_vec);
self.keys_values_with_prefix_inner(&root, prefix, |k, _v| f(k))
self.keys_values_with_prefix_inner(&root, prefix, |k, _v| f(k), Some(child_info))
}
/// Execute given closure for all keys starting with prefix.
pub fn for_keys_with_prefix<F: FnMut(&[u8])>(&self, prefix: &[u8], mut f: F) {
self.keys_values_with_prefix_inner(&self.root, prefix, |k, _v| f(k))
self.keys_values_with_prefix_inner(&self.root, prefix, |k, _v| f(k), None)
}
@@ -220,15 +243,16 @@ impl<S: TrieBackendStorage<H>, H: Hasher> TrieBackendEssence<S, H> where H::Out:
root: &H::Out,
prefix: &[u8],
mut f: F,
child_info: Option<ChildInfo>,
) {
let mut read_overlay = S::Overlay::default();
let eph = Ephemeral {
storage: &self.storage,
overlay: &mut read_overlay,
};
let mut iter = move || -> Result<(), Box<TrieError<H::Out>>> {
let trie = TrieDB::<H>::new(&eph, root)?;
let mut iter = move |db| -> Result<(), Box<TrieError<H::Out>>> {
let trie = TrieDB::<H>::new(db, root)?;
let mut iter = trie.iter()?;
iter.seek(prefix)?;
@@ -246,14 +270,20 @@ impl<S: TrieBackendStorage<H>, H: Hasher> TrieBackendEssence<S, H> where H::Out:
Ok(())
};
if let Err(e) = iter() {
let result = if let Some(child_info) = child_info {
let db = KeySpacedDB::new(&eph, child_info.keyspace());
iter(&db)
} else {
iter(&eph)
};
if let Err(e) = result {
debug!(target: "trie", "Error while iterating by prefix: {}", e);
}
}
/// Execute given closure for all key and values starting with prefix.
pub fn for_key_values_with_prefix<F: FnMut(&[u8], &[u8])>(&self, prefix: &[u8], f: F) {
self.keys_values_with_prefix_inner(&self.root, prefix, f)
self.keys_values_with_prefix_inner(&self.root, prefix, f, None)
}
}
@@ -419,11 +449,12 @@ impl<H: Hasher> TrieBackendStorage<H> for MemoryDB<H> {
#[cfg(test)]
mod test {
use primitives::{Blake2Hasher, H256};
use trie::{TrieMut, PrefixedMemoryDB, trie_types::TrieDBMut};
use trie::{TrieMut, PrefixedMemoryDB, trie_types::TrieDBMut, KeySpacedDBMut};
use super::*;
#[test]
fn next_storage_key_and_next_child_storage_key_work() {
let child_info = ChildInfo::new_default(b"uniqueid");
// Contains values
let mut root_1 = H256::default();
// Contains child trie
@@ -436,6 +467,15 @@ mod test {
trie.insert(b"4", &[1]).expect("insert failed");
trie.insert(b"6", &[1]).expect("insert failed");
}
{
let mut mdb = KeySpacedDBMut::new(&mut mdb, child_info.keyspace());
// reuse of root_1 implicitly assert child trie root is same
// as top trie (contents must remain the same).
let mut trie = TrieDBMut::new(&mut mdb, &mut root_1);
trie.insert(b"3", &[1]).expect("insert failed");
trie.insert(b"4", &[1]).expect("insert failed");
trie.insert(b"6", &[1]).expect("insert failed");
}
{
let mut trie = TrieDBMut::new(&mut mdb, &mut root_2);
trie.insert(b"MyChild", root_1.as_ref()).expect("insert failed");
@@ -452,10 +492,20 @@ mod test {
let mdb = essence_1.into_storage();
let essence_2 = TrieBackendEssence::new(mdb, root_2);
assert_eq!(essence_2.next_child_storage_key(b"MyChild", b"2"), Ok(Some(b"3".to_vec())));
assert_eq!(essence_2.next_child_storage_key(b"MyChild", b"3"), Ok(Some(b"4".to_vec())));
assert_eq!(essence_2.next_child_storage_key(b"MyChild", b"4"), Ok(Some(b"6".to_vec())));
assert_eq!(essence_2.next_child_storage_key(b"MyChild", b"5"), Ok(Some(b"6".to_vec())));
assert_eq!(essence_2.next_child_storage_key(b"MyChild", b"6"), Ok(None));
assert_eq!(
essence_2.next_child_storage_key(b"MyChild", child_info, b"2"), Ok(Some(b"3".to_vec()))
);
assert_eq!(
essence_2.next_child_storage_key(b"MyChild", child_info, b"3"), Ok(Some(b"4".to_vec()))
);
assert_eq!(
essence_2.next_child_storage_key(b"MyChild", child_info, b"4"), Ok(Some(b"6".to_vec()))
);
assert_eq!(
essence_2.next_child_storage_key(b"MyChild", child_info, b"5"), Ok(Some(b"6".to_vec()))
);
assert_eq!(
essence_2.next_child_storage_key(b"MyChild", child_info, b"6"), Ok(None)
);
}
}
+151 -4
View File
@@ -40,13 +40,31 @@ pub struct StorageData(
pub Vec<u8>,
);
/// A set of key value pairs for storage.
/// Map of data to use in a storage, it is a collection of
/// byte key and values.
#[cfg(feature = "std")]
pub type StorageOverlay = std::collections::BTreeMap<Vec<u8>, Vec<u8>>;
pub type StorageMap = std::collections::BTreeMap<Vec<u8>, Vec<u8>>;
/// A set of key value pairs for children storage;
#[cfg(feature = "std")]
pub type ChildrenStorageOverlay = std::collections::HashMap<Vec<u8>, StorageOverlay>;
#[derive(Debug, PartialEq, Eq, Clone)]
/// Child trie storage data.
pub struct StorageChild {
/// Child data for storage.
pub data: StorageMap,
/// Associated child info for a child
/// trie.
pub child_info: OwnedChildInfo,
}
#[cfg(feature = "std")]
#[derive(Default, Debug, Clone)]
/// Struct containing data needed for a storage.
pub struct Storage {
/// Top trie storage data.
pub top: StorageMap,
/// Children trie storage data by storage key.
pub children: std::collections::HashMap<Vec<u8>, StorageChild>,
}
/// Storage change set
#[derive(RuntimeDebug)]
@@ -156,3 +174,132 @@ impl<'a> ChildStorageKey<'a> {
self.storage_key.into_owned()
}
}
#[derive(Clone, Copy)]
/// Information related to a child state.
pub enum ChildInfo<'a> {
Default(ChildTrie<'a>),
}
/// Owned version of `ChildInfo`.
/// To be use in persistence layers.
#[derive(Debug, Clone)]
#[cfg_attr(feature = "std", derive(PartialEq, Eq, Hash, PartialOrd, Ord))]
pub enum OwnedChildInfo {
Default(OwnedChildTrie),
}
impl<'a> ChildInfo<'a> {
/// Instantiates information for a default child trie.
pub const fn new_default(unique_id: &'a[u8]) -> Self {
ChildInfo::Default(ChildTrie {
data: unique_id,
})
}
/// Instantiates a owned version of this child info.
pub fn to_owned(&self) -> OwnedChildInfo {
match self {
ChildInfo::Default(ChildTrie { data })
=> OwnedChildInfo::Default(OwnedChildTrie {
data: data.to_vec(),
}),
}
}
/// Create child info from a linear byte packed value and a given type.
pub fn resolve_child_info(child_type: u32, data: &'a[u8]) -> Option<Self> {
match child_type {
x if x == ChildType::CryptoUniqueId as u32 => Some(ChildInfo::new_default(data)),
_ => None,
}
}
/// Return a single byte vector containing packed child info content and its child info type.
/// This can be use as input for `resolve_child_info`.
pub fn info(&self) -> (&[u8], u32) {
match self {
ChildInfo::Default(ChildTrie {
data,
}) => (data, ChildType::CryptoUniqueId as u32),
}
}
/// Return byte sequence (keyspace) that can be use by underlying db to isolate keys.
/// This is a unique id of the child trie. The collision resistance of this value
/// depends on the type of child info use. For `ChildInfo::Default` it is and need to be.
pub fn keyspace(&self) -> &[u8] {
match self {
ChildInfo::Default(ChildTrie {
data,
}) => &data[..],
}
}
}
/// Type of child.
/// It does not strictly define different child type, it can also
/// be related to technical consideration or api variant.
#[repr(u32)]
pub enum ChildType {
/// Default, it uses a cryptographic strong unique id as input.
CryptoUniqueId = 1,
}
impl OwnedChildInfo {
/// Instantiates info for a default child trie.
pub fn new_default(unique_id: Vec<u8>) -> Self {
OwnedChildInfo::Default(OwnedChildTrie {
data: unique_id,
})
}
/// Try to update with another instance, return false if both instance
/// are not compatible.
pub fn try_update(&mut self, other: ChildInfo) -> bool {
match self {
OwnedChildInfo::Default(owned_child_trie) => owned_child_trie.try_update(other),
}
}
/// Get `ChildInfo` reference to this owned child info.
pub fn as_ref(&self) -> ChildInfo {
match self {
OwnedChildInfo::Default(OwnedChildTrie { data })
=> ChildInfo::Default(ChildTrie {
data: data.as_slice(),
}),
}
}
}
/// A child trie of default type.
/// Default is the same implementation as the top trie.
/// It share its trie node storage with any kind of key,
/// and its unique id needs to be collision free (eg strong
/// crypto hash).
#[derive(Clone, Copy)]
pub struct ChildTrie<'a> {
/// Data containing unique id.
/// Unique id must but unique and free of any possible key collision
/// (depending on its storage behavior).
data: &'a[u8],
}
/// Owned version of default child trie `ChildTrie`.
#[derive(Debug, Clone)]
#[cfg_attr(feature = "std", derive(PartialEq, Eq, Hash, PartialOrd, Ord))]
pub struct OwnedChildTrie {
/// See `ChildTrie` reference field documentation.
data: Vec<u8>,
}
impl OwnedChildTrie {
/// Try to update with another instance, return false if both instance
/// are not compatible.
fn try_update(&mut self, other: ChildInfo) -> bool {
match other {
ChildInfo::Default(other) => self.data[..] == other.data[..],
}
}
}
+110 -5
View File
@@ -24,8 +24,9 @@ mod node_codec;
mod trie_stream;
use sp_std::boxed::Box;
use sp_std::marker::PhantomData;
use sp_std::vec::Vec;
use hash_db::Hasher;
use hash_db::{Hasher, Prefix};
/// Our `NodeCodec`-specific error.
pub use error::Error;
/// The Substrate format implementation of `TrieStream`.
@@ -191,6 +192,7 @@ pub fn child_trie_root<L: TrieConfiguration, I, A, B>(
/// but a generic implementation may ignore this type parameter and use other hashers.
pub fn child_delta_trie_root<L: TrieConfiguration, I, A, B, DB, RD>(
_storage_key: &[u8],
keyspace: &[u8],
db: &mut DB,
root_data: RD,
delta: I,
@@ -208,7 +210,8 @@ pub fn child_delta_trie_root<L: TrieConfiguration, I, A, B, DB, RD>(
root.as_mut().copy_from_slice(root_data.as_ref());
{
let mut trie = TrieDBMut::<L>::from_existing(&mut *db, &mut root)?;
let mut db = KeySpacedDBMut::new(&mut *db, keyspace);
let mut trie = TrieDBMut::<L>::from_existing(&mut db, &mut root)?;
for (key, change) in delta {
match change {
@@ -224,6 +227,7 @@ pub fn child_delta_trie_root<L: TrieConfiguration, I, A, B, DB, RD>(
/// Call `f` for all keys in a child trie.
pub fn for_keys_in_child_trie<L: TrieConfiguration, F: FnMut(&[u8]), DB>(
_storage_key: &[u8],
keyspace: &[u8],
db: &DB,
root_slice: &[u8],
mut f: F
@@ -236,7 +240,8 @@ pub fn for_keys_in_child_trie<L: TrieConfiguration, F: FnMut(&[u8]), DB>(
// root is fetched from DB, not writable by runtime, so it's always valid.
root.as_mut().copy_from_slice(root_slice);
let trie = TrieDB::<L>::new(&*db, &root)?;
let db = KeySpacedDB::new(&*db, keyspace);
let trie = TrieDB::<L>::new(&db, &root)?;
let iter = trie.iter()?;
for x in iter {
@@ -273,6 +278,7 @@ pub fn record_all_keys<L: TrieConfiguration, DB>(
/// Read a value from the child trie.
pub fn read_child_trie_value<L: TrieConfiguration, DB>(
_storage_key: &[u8],
keyspace: &[u8],
db: &DB,
root_slice: &[u8],
key: &[u8]
@@ -285,12 +291,14 @@ pub fn read_child_trie_value<L: TrieConfiguration, DB>(
// root is fetched from DB, not writable by runtime, so it's always valid.
root.as_mut().copy_from_slice(root_slice);
Ok(TrieDB::<L>::new(&*db, &root)?.get(key).map(|x| x.map(|val| val.to_vec()))?)
let db = KeySpacedDB::new(&*db, keyspace);
Ok(TrieDB::<L>::new(&db, &root)?.get(key).map(|x| x.map(|val| val.to_vec()))?)
}
/// Read a value from the child trie with given query.
pub fn read_child_trie_value_with<L: TrieConfiguration, Q: Query<L::Hash, Item=DBValue>, DB>(
_storage_key: &[u8],
keyspace: &[u8],
db: &DB,
root_slice: &[u8],
key: &[u8],
@@ -304,7 +312,104 @@ pub fn read_child_trie_value_with<L: TrieConfiguration, Q: Query<L::Hash, Item=D
// root is fetched from DB, not writable by runtime, so it's always valid.
root.as_mut().copy_from_slice(root_slice);
Ok(TrieDB::<L>::new(&*db, &root)?.get_with(key, query).map(|x| x.map(|val| val.to_vec()))?)
let db = KeySpacedDB::new(&*db, keyspace);
Ok(TrieDB::<L>::new(&db, &root)?.get_with(key, query).map(|x| x.map(|val| val.to_vec()))?)
}
/// `HashDB` implementation that append a encoded prefix (unique id bytes) in addition to the
/// prefix of every key value.
pub struct KeySpacedDB<'a, DB, H>(&'a DB, &'a [u8], PhantomData<H>);
/// `HashDBMut` implementation that append a encoded prefix (unique id bytes) in addition to the
/// prefix of every key value.
///
/// Mutable variant of `KeySpacedDB`, see [`KeySpacedDB`].
pub struct KeySpacedDBMut<'a, DB, H>(&'a mut DB, &'a [u8], PhantomData<H>);
/// Utility function used to merge some byte data (keyspace) and `prefix` data
/// before calling key value database primitives.
fn keyspace_as_prefix_alloc(ks: &[u8], prefix: Prefix) -> (Vec<u8>, Option<u8>) {
let mut result = sp_std::vec![0; ks.len() + prefix.0.len()];
result[..ks.len()].copy_from_slice(ks);
result[ks.len()..].copy_from_slice(prefix.0);
(result, prefix.1)
}
impl<'a, DB, H> KeySpacedDB<'a, DB, H> where
H: Hasher,
{
/// instantiate new keyspaced db
pub fn new(db: &'a DB, ks: &'a [u8]) -> Self {
KeySpacedDB(db, ks, PhantomData)
}
}
impl<'a, DB, H> KeySpacedDBMut<'a, DB, H> where
H: Hasher,
{
/// instantiate new keyspaced db
pub fn new(db: &'a mut DB, ks: &'a [u8]) -> Self {
KeySpacedDBMut(db, ks, PhantomData)
}
}
impl<'a, DB, H, T> hash_db::HashDBRef<H, T> for KeySpacedDB<'a, DB, H> where
DB: hash_db::HashDBRef<H, T>,
H: Hasher,
T: From<&'static [u8]>,
{
fn get(&self, key: &H::Out, prefix: Prefix) -> Option<T> {
let derived_prefix = keyspace_as_prefix_alloc(self.1, prefix);
self.0.get(key, (&derived_prefix.0, derived_prefix.1))
}
fn contains(&self, key: &H::Out, prefix: Prefix) -> bool {
let derived_prefix = keyspace_as_prefix_alloc(self.1, prefix);
self.0.contains(key, (&derived_prefix.0, derived_prefix.1))
}
}
impl<'a, DB, H, T> hash_db::HashDB<H, T> for KeySpacedDBMut<'a, DB, H> where
DB: hash_db::HashDB<H, T>,
H: Hasher,
T: Default + PartialEq<T> + for<'b> From<&'b [u8]> + Clone + Send + Sync,
{
fn get(&self, key: &H::Out, prefix: Prefix) -> Option<T> {
let derived_prefix = keyspace_as_prefix_alloc(self.1, prefix);
self.0.get(key, (&derived_prefix.0, derived_prefix.1))
}
fn contains(&self, key: &H::Out, prefix: Prefix) -> bool {
let derived_prefix = keyspace_as_prefix_alloc(self.1, prefix);
self.0.contains(key, (&derived_prefix.0, derived_prefix.1))
}
fn insert(&mut self, prefix: Prefix, value: &[u8]) -> H::Out {
let derived_prefix = keyspace_as_prefix_alloc(self.1, prefix);
self.0.insert((&derived_prefix.0, derived_prefix.1), value)
}
fn emplace(&mut self, key: H::Out, prefix: Prefix, value: T) {
let derived_prefix = keyspace_as_prefix_alloc(self.1, prefix);
self.0.emplace(key, (&derived_prefix.0, derived_prefix.1), value)
}
fn remove(&mut self, key: &H::Out, prefix: Prefix) {
let derived_prefix = keyspace_as_prefix_alloc(self.1, prefix);
self.0.remove(key, (&derived_prefix.0, derived_prefix.1))
}
}
impl<'a, DB, H, T> hash_db::AsHashDB<H, T> for KeySpacedDBMut<'a, DB, H> where
DB: hash_db::HashDB<H, T>,
H: Hasher,
T: Default + PartialEq<T> + for<'b> From<&'b [u8]> + Clone + Send + Sync,
{
fn as_hash_db(&self) -> &dyn hash_db::HashDB<H, T> { &*self }
fn as_hash_db_mut<'b>(&'b mut self) -> &'b mut (dyn hash_db::HashDB<H, T> + 'b) {
&mut *self
}
}
/// Constants used into trie simplification codec.
+18 -10
View File
@@ -32,13 +32,13 @@ pub use keyring::{
sr25519::Keyring as Sr25519Keyring,
};
pub use primitives::{Blake2Hasher, traits::BareCryptoStorePtr};
pub use sp_runtime::{StorageOverlay, ChildrenStorageOverlay};
pub use sp_runtime::{Storage, StorageChild};
pub use state_machine::ExecutionStrategy;
use std::sync::Arc;
use std::collections::HashMap;
use hash_db::Hasher;
use primitives::storage::well_known_keys;
use primitives::storage::{well_known_keys, ChildInfo};
use sp_runtime::traits::Block as BlockT;
use client::LocalCallExecutor;
@@ -51,11 +51,11 @@ pub type LightBackend<Block> = client::light::backend::Backend<
/// A genesis storage initialisation trait.
pub trait GenesisInit: Default {
/// Construct genesis storage.
fn genesis_storage(&self) -> (StorageOverlay, ChildrenStorageOverlay);
fn genesis_storage(&self) -> Storage;
}
impl GenesisInit for () {
fn genesis_storage(&self) -> (StorageOverlay, ChildrenStorageOverlay) {
fn genesis_storage(&self) -> Storage {
Default::default()
}
}
@@ -64,7 +64,7 @@ impl GenesisInit for () {
pub struct TestClientBuilder<Executor, Backend, G: GenesisInit> {
execution_strategies: ExecutionStrategies,
genesis_init: G,
child_storage_extension: HashMap<Vec<u8>, Vec<(Vec<u8>, Vec<u8>)>>,
child_storage_extension: HashMap<Vec<u8>, StorageChild>,
backend: Arc<Backend>,
_executor: std::marker::PhantomData<Executor>,
keystore: Option<BareCryptoStorePtr>,
@@ -136,10 +136,15 @@ impl<Executor, Backend, G: GenesisInit> TestClientBuilder<Executor, Backend, G>
mut self,
key: impl AsRef<[u8]>,
child_key: impl AsRef<[u8]>,
child_info: ChildInfo,
value: impl AsRef<[u8]>,
) -> Self {
let entry = self.child_storage_extension.entry(key.as_ref().to_vec()).or_insert_with(Vec::new);
entry.push((child_key.as_ref().to_vec(), value.as_ref().to_vec()));
let entry = self.child_storage_extension.entry(key.as_ref().to_vec())
.or_insert_with(|| StorageChild {
data: Default::default(),
child_info: child_info.to_owned(),
});
entry.data.insert(child_key.as_ref().to_vec(), value.as_ref().to_vec());
self
}
@@ -180,10 +185,13 @@ impl<Executor, Backend, G: GenesisInit> TestClientBuilder<Executor, Backend, G>
let mut storage = self.genesis_init.genesis_storage();
// Add some child storage keys.
for (key, value) in self.child_storage_extension {
storage.1.insert(
for (key, child_content) in self.child_storage_extension {
storage.children.insert(
well_known_keys::CHILD_STORAGE_KEY_PREFIX.iter().cloned().chain(key).collect(),
value.into_iter().collect(),
StorageChild {
data: child_content.data.into_iter().collect(),
child_info: child_content.child_info,
},
);
}
+16 -13
View File
@@ -23,12 +23,13 @@ pub mod trait_tests;
mod block_builder_ext;
use std::sync::Arc;
use std::collections::{HashMap, BTreeMap};
use std::collections::HashMap;
pub use block_builder_ext::BlockBuilderExt;
pub use generic_test_client::*;
pub use runtime;
use primitives::sr25519;
use primitives::storage::{ChildInfo, Storage, StorageChild};
use runtime::genesismap::{GenesisConfig, additional_storage_with_genesis};
use sp_runtime::traits::{Block as BlockT, Header as HeaderT, Hash as HashT, NumberFor};
use client::{
@@ -97,8 +98,7 @@ pub type LightExecutor = client::light::call_executor::GenesisCallExecutor<
pub struct GenesisParameters {
support_changes_trie: bool,
heap_pages_override: Option<u64>,
extra_storage: BTreeMap<Vec<u8>, Vec<u8>>,
child_extra_storage: HashMap<Vec<u8>, BTreeMap<Vec<u8>, Vec<u8>>>,
extra_storage: Storage,
}
impl GenesisParameters {
@@ -118,27 +118,26 @@ impl GenesisParameters {
1000,
self.heap_pages_override,
self.extra_storage.clone(),
self.child_extra_storage.clone(),
)
}
}
impl generic_test_client::GenesisInit for GenesisParameters {
fn genesis_storage(&self) -> (StorageOverlay, ChildrenStorageOverlay) {
fn genesis_storage(&self) -> Storage {
use codec::Encode;
let mut storage = self.genesis_config().genesis_map();
let child_roots = storage.1.iter().map(|(sk, child_map)| {
let child_roots = storage.children.iter().map(|(sk, child_content)| {
let state_root = <<<runtime::Block as BlockT>::Header as HeaderT>::Hashing as HashT>::trie_root(
child_map.clone().into_iter().collect()
child_content.data.clone().into_iter().collect()
);
(sk.clone(), state_root.encode())
});
let state_root = <<<runtime::Block as BlockT>::Header as HeaderT>::Hashing as HashT>::trie_root(
storage.0.clone().into_iter().chain(child_roots).collect()
storage.top.clone().into_iter().chain(child_roots).collect()
);
let block: runtime::Block = client::genesis::construct_genesis_block(state_root);
storage.0.extend(additional_storage_with_genesis(&block));
storage.top.extend(additional_storage_with_genesis(&block));
storage
}
@@ -189,6 +188,7 @@ pub trait TestClientBuilderExt<B>: Sized {
fn add_extra_child_storage<SK: Into<Vec<u8>>, K: Into<Vec<u8>>, V: Into<Vec<u8>>>(
self,
storage_key: SK,
child_info: ChildInfo,
key: K,
value: V,
) -> Self;
@@ -228,13 +228,14 @@ impl<B> TestClientBuilderExt<B> for TestClientBuilder<
fn add_extra_storage<K: Into<Vec<u8>>, V: Into<Vec<u8>>>(mut self, key: K, value: V) -> Self {
let key = key.into();
assert!(!key.is_empty());
self.genesis_init_mut().extra_storage.insert(key, value.into());
self.genesis_init_mut().extra_storage.top.insert(key, value.into());
self
}
fn add_extra_child_storage<SK: Into<Vec<u8>>, K: Into<Vec<u8>>, V: Into<Vec<u8>>>(
mut self,
storage_key: SK,
child_info: ChildInfo,
key: K,
value: V,
) -> Self {
@@ -242,10 +243,12 @@ impl<B> TestClientBuilderExt<B> for TestClientBuilder<
let key = key.into();
assert!(!storage_key.is_empty());
assert!(!key.is_empty());
self.genesis_init_mut().child_extra_storage
self.genesis_init_mut().extra_storage.children
.entry(storage_key)
.or_insert_with(Default::default)
.insert(key, value.into());
.or_insert_with(|| StorageChild {
data: Default::default(),
child_info: child_info.to_owned(),
}).data.insert(key, value.into());
self
}
+15 -21
View File
@@ -16,11 +16,12 @@
//! Tool for creating the genesis block.
use std::collections::{BTreeMap, HashMap};
use std::collections::BTreeMap;
use sp_io::hashing::{blake2_256, twox_128};
use super::{AuthorityId, AccountId, WASM_BINARY, system};
use codec::{Encode, KeyedVec, Joiner};
use primitives::{ChangesTrieConfiguration, map, storage::well_known_keys};
use primitives::{ChangesTrieConfiguration, map};
use primitives::storage::{well_known_keys, Storage};
use sp_runtime::traits::{Block as BlockT, Hash as HashT, Header as HeaderT};
/// Configuration of a general Substrate test genesis block.
@@ -30,8 +31,7 @@ pub struct GenesisConfig {
balances: Vec<(AccountId, u64)>,
heap_pages_override: Option<u64>,
/// Additional storage key pairs that will be added to the genesis map.
extra_storage: BTreeMap<Vec<u8>, Vec<u8>>,
child_extra_storage: HashMap<Vec<u8>, BTreeMap<Vec<u8>, Vec<u8>>>,
extra_storage: Storage,
}
impl GenesisConfig {
@@ -41,8 +41,7 @@ impl GenesisConfig {
endowed_accounts: Vec<AccountId>,
balance: u64,
heap_pages_override: Option<u64>,
extra_storage: BTreeMap<Vec<u8>, Vec<u8>>,
child_extra_storage: HashMap<Vec<u8>, BTreeMap<Vec<u8>, Vec<u8>>>,
extra_storage: Storage,
) -> Self {
GenesisConfig {
changes_trie_config: match support_changes_trie {
@@ -53,14 +52,10 @@ impl GenesisConfig {
balances: endowed_accounts.into_iter().map(|a| (a, balance)).collect(),
heap_pages_override,
extra_storage,
child_extra_storage,
}
}
pub fn genesis_map(&self) -> (
BTreeMap<Vec<u8>, Vec<u8>>,
HashMap<Vec<u8>, BTreeMap<Vec<u8>, Vec<u8>>>,
) {
pub fn genesis_map(&self) -> Storage {
let wasm_runtime = WASM_BINARY.to_vec();
let mut map: BTreeMap<Vec<u8>, Vec<u8>> = self.balances.iter()
.map(|&(ref account, balance)| (account.to_keyed_vec(b"balance:"), vec![].and(&balance)))
@@ -78,10 +73,10 @@ impl GenesisConfig {
}
map.insert(twox_128(&b"sys:auth"[..])[..].to_vec(), self.authorities.encode());
// Add the extra storage entries.
map.extend(self.extra_storage.clone().into_iter());
map.extend(self.extra_storage.top.clone().into_iter());
// Assimilate the system genesis config.
let mut storage = (map, self.child_extra_storage.clone());
let mut storage = Storage { top: map, children: self.extra_storage.children.clone()};
let mut config = system::GenesisConfig::default();
config.authorities = self.authorities.clone();
config.assimilate_storage(&mut storage).expect("Adding `system::GensisConfig` to the genesis");
@@ -91,23 +86,22 @@ impl GenesisConfig {
}
pub fn insert_genesis_block(
storage: &mut (
BTreeMap<Vec<u8>, Vec<u8>>,
HashMap<Vec<u8>, BTreeMap<Vec<u8>, Vec<u8>>>,
)
storage: &mut Storage,
) -> primitives::hash::H256 {
let child_roots = storage.1.iter().map(|(sk, child_map)| {
let child_roots = storage.children.iter().map(|(sk, child_content)| {
let state_root = <<<crate::Block as BlockT>::Header as HeaderT>::Hashing as HashT>::trie_root(
child_map.clone().into_iter().collect(),
child_content.data.clone().into_iter().collect(),
);
(sk.clone(), state_root.encode())
});
// add child roots to storage
storage.top.extend(child_roots);
let state_root = <<<crate::Block as BlockT>::Header as HeaderT>::Hashing as HashT>::trie_root(
storage.0.clone().into_iter().chain(child_roots).collect()
storage.top.clone().into_iter().collect()
);
let block: crate::Block = sc_client::genesis::construct_genesis_block(state_root);
let genesis_hash = block.header.hash();
storage.0.extend(additional_storage_with_genesis(&block));
storage.top.extend(additional_storage_with_genesis(&block));
genesis_hash
}
+20 -3
View File
@@ -50,6 +50,7 @@ use runtime_version::NativeVersion;
use runtime_support::{impl_outer_origin, parameter_types, weights::Weight};
use inherents::{CheckInherentsResult, InherentData};
use cfg_if::cfg_if;
use primitives::storage::ChildType;
// Ensure Babe and Aura use the same crypto to simplify things a bit.
pub use babe_primitives::AuthorityId;
@@ -910,21 +911,37 @@ fn test_read_storage() {
fn test_read_child_storage() {
const CHILD_KEY: &[u8] = b":child_storage:default:read_child_storage";
const UNIQUE_ID: &[u8] = b":unique_id";
const KEY: &[u8] = b":read_child_storage";
sp_io::storage::child_set(CHILD_KEY, KEY, b"test");
sp_io::storage::child_set(
CHILD_KEY,
UNIQUE_ID,
ChildType::CryptoUniqueId as u32,
KEY,
b"test",
);
let mut v = [0u8; 4];
let r = sp_io::storage::child_read(
CHILD_KEY,
UNIQUE_ID,
ChildType::CryptoUniqueId as u32,
KEY,
&mut v,
0
0,
);
assert_eq!(r, Some(4));
assert_eq!(&v, b"test");
let mut v = [0u8; 4];
let r = sp_io::storage::child_read(CHILD_KEY, KEY, &mut v, 8);
let r = sp_io::storage::child_read(
CHILD_KEY,
UNIQUE_ID,
ChildType::CryptoUniqueId as u32,
KEY,
&mut v,
8,
);
assert_eq!(r, Some(4));
assert_eq!(&v, &[0, 0, 0, 0]);
}
+4 -4
View File
@@ -361,16 +361,16 @@ mod tests {
];
TestExternalities::new_with_code(
WASM_BINARY,
(
map![
primitives::storage::Storage {
top: map![
twox_128(b"latest").to_vec() => vec![69u8; 32],
twox_128(b"sys:auth").to_vec() => authorities.encode(),
blake2_256(&AccountKeyring::Alice.to_raw_public().to_keyed_vec(b"balance:")).to_vec() => {
vec![111u8, 0, 0, 0, 0, 0, 0, 0]
}
],
map![],
)
children: map![],
},
)
}