mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-06 02:08:02 +00:00
Fix key collision for child trie (#4162)
* In progress, runtime io must switch to future proof root + child_specific (unique id) + u32 type. * Switch interface, sr-io seems ok, rpc could use similar interface to sr-io, genesis json broken if there is child trie in existing encoding genesis. * test from previous implementation. * fix proving test. * Restore Keyspacedb from other branch, only apply to child trie. * Removing unneeded child_info from child root (child info are stored if things changed, otherwhise the root does not change). * Switch rpc to use same format as ext: more future proof. * use root from child info for trie backend essence. * Breaking long lines. * Update doc and clean pr a bit. * fix error type * Restore removed doc on merge and update sr-io doc. * Switch child storage api to use directly unique id, if managed id where to be put in place, the api will change at this time. * Clean deprecated host interface from child. * Removing assertion on child info (can fail depending on root memoization). * merging child info in the overlay when possible. * child iteration by prefix using child_info. * Using ChainInfo in frame support. ChainInfo gets redesign to avoid buffers allocation on every calls. * Add length of root to the data of child info. * comments * Encode compact. * Remove child info with root. * Fix try_update condition. * Comment Ext child root caching. * Replace tuples by struct with field * remove StorageTuple alias. * Fix doc tests, and remove StorageOverlay and ChildStorageOverlay aliases.
This commit is contained in:
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()),
|
||||
|
||||
@@ -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]));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user