mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-12 14:41:11 +00:00
Run cargo fmt on the whole code base (#9394)
* Run cargo fmt on the whole code base * Second run * Add CI check * Fix compilation * More unnecessary braces * Handle weights * Use --all * Use correct attributes... * Fix UI tests * AHHHHHHHHH * 🤦 * Docs * Fix compilation * 🤷 * Please stop * 🤦 x 2 * More * make rustfmt.toml consistent with polkadot Co-authored-by: André Silva <andrerfosilva@gmail.com>
This commit is contained in:
@@ -17,19 +17,16 @@
|
||||
|
||||
//! State machine backends. These manage the code and storage of contracts.
|
||||
|
||||
use hash_db::Hasher;
|
||||
use codec::{Decode, Encode};
|
||||
use sp_core::{
|
||||
storage::{ChildInfo, well_known_keys, TrackedStorageKey}
|
||||
};
|
||||
use crate::{
|
||||
trie_backend::TrieBackend,
|
||||
trie_backend_essence::TrieBackendStorage,
|
||||
UsageInfo, StorageKey, StorageValue, StorageCollection, ChildStorageCollection,
|
||||
trie_backend::TrieBackend, trie_backend_essence::TrieBackendStorage, ChildStorageCollection,
|
||||
StorageCollection, StorageKey, StorageValue, UsageInfo,
|
||||
};
|
||||
use sp_std::vec::Vec;
|
||||
use codec::{Decode, Encode};
|
||||
use hash_db::Hasher;
|
||||
use sp_core::storage::{well_known_keys, ChildInfo, TrackedStorageKey};
|
||||
#[cfg(feature = "std")]
|
||||
use sp_core::traits::RuntimeCode;
|
||||
use sp_std::vec::Vec;
|
||||
|
||||
/// A state backend is used to read state data and can have changes committed
|
||||
/// to it.
|
||||
@@ -90,7 +87,7 @@ pub trait Backend<H: Hasher>: sp_std::fmt::Debug {
|
||||
fn next_child_storage_key(
|
||||
&self,
|
||||
child_info: &ChildInfo,
|
||||
key: &[u8]
|
||||
key: &[u8],
|
||||
) -> Result<Option<StorageKey>, Self::Error>;
|
||||
|
||||
/// Iterate over storage starting at key, for a given prefix and child trie.
|
||||
@@ -128,7 +125,6 @@ pub trait Backend<H: Hasher>: sp_std::fmt::Debug {
|
||||
/// call `f` for each of those keys.
|
||||
fn for_key_values_with_prefix<F: FnMut(&[u8], &[u8])>(&self, prefix: &[u8], f: F);
|
||||
|
||||
|
||||
/// 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])>(
|
||||
@@ -143,8 +139,10 @@ pub trait Backend<H: Hasher>: sp_std::fmt::Debug {
|
||||
/// Does not include child storage updates.
|
||||
fn storage_root<'a>(
|
||||
&self,
|
||||
delta: impl Iterator<Item=(&'a [u8], Option<&'a [u8]>)>,
|
||||
) -> (H::Out, Self::Transaction) where H::Out: Ord;
|
||||
delta: impl Iterator<Item = (&'a [u8], Option<&'a [u8]>)>,
|
||||
) -> (H::Out, Self::Transaction)
|
||||
where
|
||||
H::Out: Ord;
|
||||
|
||||
/// 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
|
||||
@@ -152,8 +150,10 @@ pub trait Backend<H: Hasher>: sp_std::fmt::Debug {
|
||||
fn child_storage_root<'a>(
|
||||
&self,
|
||||
child_info: &ChildInfo,
|
||||
delta: impl Iterator<Item=(&'a [u8], Option<&'a [u8]>)>,
|
||||
) -> (H::Out, bool, Self::Transaction) where H::Out: Ord;
|
||||
delta: impl Iterator<Item = (&'a [u8], Option<&'a [u8]>)>,
|
||||
) -> (H::Out, bool, Self::Transaction)
|
||||
where
|
||||
H::Out: Ord;
|
||||
|
||||
/// Get all key/value pairs into a Vec.
|
||||
fn pairs(&self) -> Vec<(StorageKey, StorageValue)>;
|
||||
@@ -166,11 +166,7 @@ pub trait Backend<H: Hasher>: sp_std::fmt::Debug {
|
||||
}
|
||||
|
||||
/// Get all keys of child storage with given prefix
|
||||
fn child_keys(
|
||||
&self,
|
||||
child_info: &ChildInfo,
|
||||
prefix: &[u8],
|
||||
) -> Vec<StorageKey> {
|
||||
fn child_keys(&self, child_info: &ChildInfo, prefix: &[u8]) -> Vec<StorageKey> {
|
||||
let mut all = Vec::new();
|
||||
self.for_child_keys_with_prefix(child_info, prefix, |k| all.push(k.to_vec()));
|
||||
all
|
||||
@@ -186,18 +182,19 @@ pub trait Backend<H: Hasher>: sp_std::fmt::Debug {
|
||||
/// Does include child storage updates.
|
||||
fn full_storage_root<'a>(
|
||||
&self,
|
||||
delta: impl Iterator<Item=(&'a [u8], Option<&'a [u8]>)>,
|
||||
child_deltas: impl Iterator<Item = (
|
||||
&'a ChildInfo,
|
||||
impl Iterator<Item=(&'a [u8], Option<&'a [u8]>)>,
|
||||
)>,
|
||||
) -> (H::Out, Self::Transaction) where H::Out: Ord + Encode {
|
||||
delta: impl Iterator<Item = (&'a [u8], Option<&'a [u8]>)>,
|
||||
child_deltas: impl Iterator<
|
||||
Item = (&'a ChildInfo, impl Iterator<Item = (&'a [u8], Option<&'a [u8]>)>),
|
||||
>,
|
||||
) -> (H::Out, Self::Transaction)
|
||||
where
|
||||
H::Out: Ord + Encode,
|
||||
{
|
||||
let mut txs: Self::Transaction = Default::default();
|
||||
let mut child_roots: Vec<_> = Default::default();
|
||||
// child first
|
||||
for (child_info, child_delta) in child_deltas {
|
||||
let (child_root, empty, child_txs) =
|
||||
self.child_storage_root(&child_info, child_delta);
|
||||
let (child_root, empty, child_txs) = self.child_storage_root(&child_info, child_delta);
|
||||
let prefixed_storage_key = child_info.prefixed_storage_key();
|
||||
txs.consolidate(child_txs);
|
||||
if empty {
|
||||
@@ -206,13 +203,10 @@ pub trait Backend<H: Hasher>: sp_std::fmt::Debug {
|
||||
child_roots.push((prefixed_storage_key.into_inner(), Some(child_root.encode())));
|
||||
}
|
||||
}
|
||||
let (root, parent_txs) = self.storage_root(delta
|
||||
.map(|(k, v)| (k, v.as_ref().map(|v| &v[..])))
|
||||
.chain(
|
||||
child_roots
|
||||
.iter()
|
||||
.map(|(k, v)| (&k[..], v.as_ref().map(|v| &v[..])))
|
||||
)
|
||||
let (root, parent_txs) = self.storage_root(
|
||||
delta
|
||||
.map(|(k, v)| (k, v.as_ref().map(|v| &v[..])))
|
||||
.chain(child_roots.iter().map(|(k, v)| (&k[..], v.as_ref().map(|v| &v[..])))),
|
||||
);
|
||||
txs.consolidate(parent_txs);
|
||||
(root, txs)
|
||||
@@ -286,10 +280,7 @@ impl Consolidate for () {
|
||||
}
|
||||
}
|
||||
|
||||
impl Consolidate for Vec<(
|
||||
Option<ChildInfo>,
|
||||
StorageCollection,
|
||||
)> {
|
||||
impl Consolidate for Vec<(Option<ChildInfo>, StorageCollection)> {
|
||||
fn consolidate(&mut self, mut other: Self) {
|
||||
self.append(&mut other);
|
||||
}
|
||||
@@ -303,12 +294,15 @@ impl<H: Hasher, KF: sp_trie::KeyFunction<H>> Consolidate for sp_trie::GenericMem
|
||||
|
||||
/// Insert input pairs into memory db.
|
||||
#[cfg(test)]
|
||||
pub(crate) fn insert_into_memory_db<H, I>(mdb: &mut sp_trie::MemoryDB<H>, input: I) -> Option<H::Out>
|
||||
where
|
||||
H: Hasher,
|
||||
I: IntoIterator<Item=(StorageKey, StorageValue)>,
|
||||
pub(crate) fn insert_into_memory_db<H, I>(
|
||||
mdb: &mut sp_trie::MemoryDB<H>,
|
||||
input: I,
|
||||
) -> Option<H::Out>
|
||||
where
|
||||
H: Hasher,
|
||||
I: IntoIterator<Item = (StorageKey, StorageValue)>,
|
||||
{
|
||||
use sp_trie::{TrieMut, trie_types::TrieDBMut};
|
||||
use sp_trie::{trie_types::TrieDBMut, TrieMut};
|
||||
|
||||
let mut root = <H as Hasher>::Out::default();
|
||||
{
|
||||
@@ -316,7 +310,7 @@ pub(crate) fn insert_into_memory_db<H, I>(mdb: &mut sp_trie::MemoryDB<H>, input:
|
||||
for (key, value) in input {
|
||||
if let Err(e) = trie.insert(&key, &value) {
|
||||
log::warn!(target: "trie", "Failed to write to trie: {}", e);
|
||||
return None;
|
||||
return None
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -332,8 +326,8 @@ pub struct BackendRuntimeCode<'a, B, H> {
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl<'a, B: Backend<H>, H: Hasher> sp_core::traits::FetchRuntimeCode for
|
||||
BackendRuntimeCode<'a, B, H>
|
||||
impl<'a, B: Backend<H>, H: Hasher> sp_core::traits::FetchRuntimeCode
|
||||
for BackendRuntimeCode<'a, B, H>
|
||||
{
|
||||
fn fetch_runtime_code<'b>(&'b self) -> Option<std::borrow::Cow<'b, [u8]>> {
|
||||
self.backend.storage(well_known_keys::CODE).ok().flatten().map(Into::into)
|
||||
@@ -341,23 +335,27 @@ impl<'a, B: Backend<H>, H: Hasher> sp_core::traits::FetchRuntimeCode for
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl<'a, B: Backend<H>, H: Hasher> BackendRuntimeCode<'a, B, H> where H::Out: Encode {
|
||||
impl<'a, B: Backend<H>, H: Hasher> BackendRuntimeCode<'a, B, H>
|
||||
where
|
||||
H::Out: Encode,
|
||||
{
|
||||
/// Create a new instance.
|
||||
pub fn new(backend: &'a B) -> Self {
|
||||
Self {
|
||||
backend,
|
||||
_marker: std::marker::PhantomData,
|
||||
}
|
||||
Self { backend, _marker: std::marker::PhantomData }
|
||||
}
|
||||
|
||||
/// Return the [`RuntimeCode`] build from the wrapped `backend`.
|
||||
pub fn runtime_code(&self) -> Result<RuntimeCode, &'static str> {
|
||||
let hash = self.backend.storage_hash(well_known_keys::CODE)
|
||||
let hash = self
|
||||
.backend
|
||||
.storage_hash(well_known_keys::CODE)
|
||||
.ok()
|
||||
.flatten()
|
||||
.ok_or("`:code` hash not found")?
|
||||
.encode();
|
||||
let heap_pages = self.backend.storage(well_known_keys::HEAP_PAGES)
|
||||
let heap_pages = self
|
||||
.backend
|
||||
.storage(well_known_keys::HEAP_PAGES)
|
||||
.ok()
|
||||
.flatten()
|
||||
.and_then(|d| Decode::decode(&mut &d[..]).ok());
|
||||
|
||||
@@ -17,23 +17,25 @@
|
||||
|
||||
//! Basic implementation for Externalities.
|
||||
|
||||
use std::{
|
||||
collections::BTreeMap, any::{TypeId, Any}, iter::FromIterator, ops::Bound,
|
||||
};
|
||||
use crate::{Backend, StorageKey, StorageValue};
|
||||
use codec::Encode;
|
||||
use hash_db::Hasher;
|
||||
use sp_trie::{TrieConfiguration, empty_child_trie_root};
|
||||
use sp_trie::trie_types::Layout;
|
||||
use log::warn;
|
||||
use sp_core::{
|
||||
storage::{
|
||||
well_known_keys::is_child_storage_key, Storage,
|
||||
ChildInfo, StorageChild, TrackedStorageKey,
|
||||
well_known_keys::is_child_storage_key, ChildInfo, Storage, StorageChild, TrackedStorageKey,
|
||||
},
|
||||
traits::Externalities, Blake2Hasher,
|
||||
traits::Externalities,
|
||||
Blake2Hasher,
|
||||
};
|
||||
use sp_externalities::{Extension, Extensions};
|
||||
use sp_trie::{empty_child_trie_root, trie_types::Layout, TrieConfiguration};
|
||||
use std::{
|
||||
any::{Any, TypeId},
|
||||
collections::BTreeMap,
|
||||
iter::FromIterator,
|
||||
ops::Bound,
|
||||
};
|
||||
use log::warn;
|
||||
use codec::Encode;
|
||||
use sp_externalities::{Extensions, Extension};
|
||||
|
||||
/// Simple Map-based Externalities impl.
|
||||
#[derive(Debug)]
|
||||
@@ -105,13 +107,13 @@ impl BasicExternalities {
|
||||
|
||||
impl PartialEq for BasicExternalities {
|
||||
fn eq(&self, other: &BasicExternalities) -> bool {
|
||||
self.inner.top.eq(&other.inner.top)
|
||||
&& self.inner.children_default.eq(&other.inner.children_default)
|
||||
self.inner.top.eq(&other.inner.top) &&
|
||||
self.inner.children_default.eq(&other.inner.children_default)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromIterator<(StorageKey, StorageValue)> for BasicExternalities {
|
||||
fn from_iter<I: IntoIterator<Item=(StorageKey, StorageValue)>>(iter: I) -> Self {
|
||||
fn from_iter<I: IntoIterator<Item = (StorageKey, StorageValue)>>(iter: I) -> Self {
|
||||
let mut t = Self::default();
|
||||
t.inner.top.extend(iter);
|
||||
t
|
||||
@@ -119,16 +121,15 @@ impl FromIterator<(StorageKey, StorageValue)> for BasicExternalities {
|
||||
}
|
||||
|
||||
impl Default for BasicExternalities {
|
||||
fn default() -> Self { Self::new(Default::default()) }
|
||||
fn default() -> Self {
|
||||
Self::new(Default::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<BTreeMap<StorageKey, StorageValue>> for BasicExternalities {
|
||||
fn from(hashmap: BTreeMap<StorageKey, StorageValue>) -> Self {
|
||||
BasicExternalities {
|
||||
inner: Storage {
|
||||
top: hashmap,
|
||||
children_default: Default::default(),
|
||||
},
|
||||
inner: Storage { top: hashmap, children_default: Default::default() },
|
||||
extensions: Default::default(),
|
||||
}
|
||||
}
|
||||
@@ -145,20 +146,15 @@ impl Externalities for BasicExternalities {
|
||||
self.storage(key).map(|v| Blake2Hasher::hash(&v).encode())
|
||||
}
|
||||
|
||||
fn child_storage(
|
||||
&self,
|
||||
child_info: &ChildInfo,
|
||||
key: &[u8],
|
||||
) -> Option<StorageValue> {
|
||||
self.inner.children_default.get(child_info.storage_key())
|
||||
.and_then(|child| child.data.get(key)).cloned()
|
||||
fn child_storage(&self, child_info: &ChildInfo, key: &[u8]) -> Option<StorageValue> {
|
||||
self.inner
|
||||
.children_default
|
||||
.get(child_info.storage_key())
|
||||
.and_then(|child| child.data.get(key))
|
||||
.cloned()
|
||||
}
|
||||
|
||||
fn child_storage_hash(
|
||||
&self,
|
||||
child_info: &ChildInfo,
|
||||
key: &[u8],
|
||||
) -> Option<Vec<u8>> {
|
||||
fn child_storage_hash(&self, child_info: &ChildInfo, key: &[u8]) -> Option<Vec<u8>> {
|
||||
self.child_storage(child_info, key).map(|v| Blake2Hasher::hash(&v).encode())
|
||||
}
|
||||
|
||||
@@ -167,25 +163,27 @@ impl Externalities for BasicExternalities {
|
||||
self.inner.top.range::<[u8], _>(range).next().map(|(k, _)| k).cloned()
|
||||
}
|
||||
|
||||
fn next_child_storage_key(
|
||||
&self,
|
||||
child_info: &ChildInfo,
|
||||
key: &[u8],
|
||||
) -> Option<StorageKey> {
|
||||
fn next_child_storage_key(&self, child_info: &ChildInfo, key: &[u8]) -> Option<StorageKey> {
|
||||
let range = (Bound::Excluded(key), Bound::Unbounded);
|
||||
self.inner.children_default.get(child_info.storage_key())
|
||||
self.inner
|
||||
.children_default
|
||||
.get(child_info.storage_key())
|
||||
.and_then(|child| child.data.range::<[u8], _>(range).next().map(|(k, _)| k).cloned())
|
||||
}
|
||||
|
||||
fn place_storage(&mut self, key: StorageKey, maybe_value: Option<StorageValue>) {
|
||||
if is_child_storage_key(&key) {
|
||||
warn!(target: "trie", "Refuse to set child storage key via main storage");
|
||||
return;
|
||||
return
|
||||
}
|
||||
|
||||
match maybe_value {
|
||||
Some(value) => { self.inner.top.insert(key, value); }
|
||||
None => { self.inner.top.remove(&key); }
|
||||
Some(value) => {
|
||||
self.inner.top.insert(key, value);
|
||||
},
|
||||
None => {
|
||||
self.inner.top.remove(&key);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -195,7 +193,10 @@ impl Externalities for BasicExternalities {
|
||||
key: StorageKey,
|
||||
value: Option<StorageValue>,
|
||||
) {
|
||||
let child_map = self.inner.children_default.entry(child_info.storage_key().to_vec())
|
||||
let child_map = self
|
||||
.inner
|
||||
.children_default
|
||||
.entry(child_info.storage_key().to_vec())
|
||||
.or_insert_with(|| StorageChild {
|
||||
data: Default::default(),
|
||||
child_info: child_info.to_owned(),
|
||||
@@ -207,12 +208,13 @@ impl Externalities for BasicExternalities {
|
||||
}
|
||||
}
|
||||
|
||||
fn kill_child_storage(
|
||||
&mut self,
|
||||
child_info: &ChildInfo,
|
||||
_limit: Option<u32>,
|
||||
) -> (bool, u32) {
|
||||
let num_removed = self.inner.children_default.remove(child_info.storage_key()).map(|c| c.data.len()).unwrap_or(0);
|
||||
fn kill_child_storage(&mut self, child_info: &ChildInfo, _limit: Option<u32>) -> (bool, u32) {
|
||||
let num_removed = self
|
||||
.inner
|
||||
.children_default
|
||||
.remove(child_info.storage_key())
|
||||
.map(|c| c.data.len())
|
||||
.unwrap_or(0);
|
||||
(true, num_removed as u32)
|
||||
}
|
||||
|
||||
@@ -222,10 +224,13 @@ impl Externalities for BasicExternalities {
|
||||
target: "trie",
|
||||
"Refuse to clear prefix that is part of child storage key via main storage"
|
||||
);
|
||||
return (false, 0);
|
||||
return (false, 0)
|
||||
}
|
||||
|
||||
let to_remove = self.inner.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()
|
||||
@@ -245,7 +250,9 @@ impl Externalities for BasicExternalities {
|
||||
_limit: Option<u32>,
|
||||
) -> (bool, u32) {
|
||||
if let Some(child) = self.inner.children_default.get_mut(child_info.storage_key()) {
|
||||
let to_remove = child.data.range::<[u8], _>((Bound::Included(prefix), Bound::Unbounded))
|
||||
let to_remove = child
|
||||
.data
|
||||
.range::<[u8], _>((Bound::Included(prefix), Bound::Unbounded))
|
||||
.map(|(k, _)| k)
|
||||
.take_while(|k| k.starts_with(prefix))
|
||||
.cloned()
|
||||
@@ -261,20 +268,19 @@ impl Externalities for BasicExternalities {
|
||||
}
|
||||
}
|
||||
|
||||
fn storage_append(
|
||||
&mut self,
|
||||
key: Vec<u8>,
|
||||
value: Vec<u8>,
|
||||
) {
|
||||
fn storage_append(&mut self, key: Vec<u8>, value: Vec<u8>) {
|
||||
let current = self.inner.top.entry(key).or_default();
|
||||
crate::ext::StorageAppend::new(current).append(value);
|
||||
}
|
||||
|
||||
fn storage_root(&mut self) -> Vec<u8> {
|
||||
let mut top = self.inner.top.clone();
|
||||
let prefixed_keys: Vec<_> = self.inner.children_default.iter().map(|(_k, v)| {
|
||||
(v.child_info.prefixed_storage_key(), v.child_info.clone())
|
||||
}).collect();
|
||||
let prefixed_keys: Vec<_> = self
|
||||
.inner
|
||||
.children_default
|
||||
.iter()
|
||||
.map(|(_k, v)| (v.child_info.prefixed_storage_key(), v.child_info.clone()))
|
||||
.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.
|
||||
@@ -291,17 +297,16 @@ impl Externalities for BasicExternalities {
|
||||
Layout::<Blake2Hasher>::trie_root(self.inner.top.clone()).as_ref().into()
|
||||
}
|
||||
|
||||
fn child_storage_root(
|
||||
&mut self,
|
||||
child_info: &ChildInfo,
|
||||
) -> Vec<u8> {
|
||||
fn child_storage_root(&mut self, child_info: &ChildInfo) -> Vec<u8> {
|
||||
if let Some(child) = self.inner.children_default.get(child_info.storage_key()) {
|
||||
let delta = child.data.iter().map(|(k, v)| (k.as_ref(), Some(v.as_ref())));
|
||||
crate::in_memory_backend::new_in_mem::<Blake2Hasher>()
|
||||
.child_storage_root(&child.child_info, delta).0
|
||||
.child_storage_root(&child.child_info, delta)
|
||||
.0
|
||||
} else {
|
||||
empty_child_trie_root::<Layout<Blake2Hasher>>()
|
||||
}.encode()
|
||||
}
|
||||
.encode()
|
||||
}
|
||||
|
||||
fn storage_changes_root(&mut self, _parent: &[u8]) -> Result<Option<Vec<u8>>, ()> {
|
||||
@@ -358,7 +363,10 @@ impl sp_externalities::ExtensionStore for BasicExternalities {
|
||||
self.extensions.register_with_type_id(type_id, extension)
|
||||
}
|
||||
|
||||
fn deregister_extension_by_type_id(&mut self, type_id: TypeId) -> Result<(), sp_externalities::Error> {
|
||||
fn deregister_extension_by_type_id(
|
||||
&mut self,
|
||||
type_id: TypeId,
|
||||
) -> Result<(), sp_externalities::Error> {
|
||||
if self.extensions.deregister(type_id) {
|
||||
Ok(())
|
||||
} else {
|
||||
@@ -370,10 +378,11 @@ impl sp_externalities::ExtensionStore for BasicExternalities {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use sp_core::map;
|
||||
use sp_core::storage::{Storage, StorageChild};
|
||||
use sp_core::storage::well_known_keys::CODE;
|
||||
use hex_literal::hex;
|
||||
use sp_core::{
|
||||
map,
|
||||
storage::{well_known_keys::CODE, Storage, StorageChild},
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn commit_should_work() {
|
||||
@@ -381,7 +390,8 @@ mod tests {
|
||||
ext.set_storage(b"doe".to_vec(), b"reindeer".to_vec());
|
||||
ext.set_storage(b"dog".to_vec(), b"puppy".to_vec());
|
||||
ext.set_storage(b"dogglesworth".to_vec(), b"cat".to_vec());
|
||||
const ROOT: [u8; 32] = hex!("39245109cef3758c2eed2ccba8d9b370a917850af3824bc8348d505df2c298fa");
|
||||
const ROOT: [u8; 32] =
|
||||
hex!("39245109cef3758c2eed2ccba8d9b370a917850af3824bc8348d505df2c298fa");
|
||||
|
||||
assert_eq!(&ext.storage_root()[..], &ROOT);
|
||||
}
|
||||
@@ -407,7 +417,7 @@ mod tests {
|
||||
data: map![ b"doe".to_vec() => b"reindeer".to_vec() ],
|
||||
child_info: child_info.to_owned(),
|
||||
}
|
||||
]
|
||||
],
|
||||
});
|
||||
|
||||
assert_eq!(ext.child_storage(child_info, b"doe"), Some(b"reindeer".to_vec()));
|
||||
@@ -437,10 +447,9 @@ mod tests {
|
||||
],
|
||||
child_info: child_info.to_owned(),
|
||||
}
|
||||
]
|
||||
],
|
||||
});
|
||||
|
||||
|
||||
let res = ext.kill_child_storage(child_info, None);
|
||||
assert_eq!(res, (true, 3));
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -78,20 +78,20 @@ pub(crate) struct IncompleteCachedBuildData<N> {
|
||||
}
|
||||
|
||||
impl<H, N> BuildCache<H, N>
|
||||
where
|
||||
N: Eq + ::std::hash::Hash,
|
||||
H: Eq + ::std::hash::Hash + Clone,
|
||||
where
|
||||
N: Eq + ::std::hash::Hash,
|
||||
H: Eq + ::std::hash::Hash + Clone,
|
||||
{
|
||||
/// Create new changes trie build cache.
|
||||
pub fn new() -> Self {
|
||||
BuildCache {
|
||||
roots_by_number: HashMap::new(),
|
||||
changed_keys: HashMap::new(),
|
||||
}
|
||||
BuildCache { roots_by_number: HashMap::new(), changed_keys: HashMap::new() }
|
||||
}
|
||||
|
||||
/// Get cached changed keys for changes trie with given root.
|
||||
pub fn get(&self, root: &H) -> Option<&HashMap<Option<PrefixedStorageKey>, HashSet<StorageKey>>> {
|
||||
pub fn get(
|
||||
&self,
|
||||
root: &H,
|
||||
) -> Option<&HashMap<Option<PrefixedStorageKey>, HashSet<StorageKey>>> {
|
||||
self.changed_keys.get(&root)
|
||||
}
|
||||
|
||||
@@ -158,7 +158,9 @@ impl<N> IncompleteCacheAction<N> {
|
||||
pub(crate) fn set_digest_input_blocks(self, digest_input_blocks: Vec<N>) -> Self {
|
||||
match self {
|
||||
IncompleteCacheAction::CacheBuildData(build_data) =>
|
||||
IncompleteCacheAction::CacheBuildData(build_data.set_digest_input_blocks(digest_input_blocks)),
|
||||
IncompleteCacheAction::CacheBuildData(
|
||||
build_data.set_digest_input_blocks(digest_input_blocks),
|
||||
),
|
||||
IncompleteCacheAction::Clear => IncompleteCacheAction::Clear,
|
||||
}
|
||||
}
|
||||
@@ -180,10 +182,7 @@ impl<N> IncompleteCacheAction<N> {
|
||||
impl<N> IncompleteCachedBuildData<N> {
|
||||
/// Create new cached data.
|
||||
pub(crate) fn new() -> Self {
|
||||
IncompleteCachedBuildData {
|
||||
digest_input_blocks: Vec::new(),
|
||||
changed_keys: HashMap::new(),
|
||||
}
|
||||
IncompleteCachedBuildData { digest_input_blocks: Vec::new(), changed_keys: HashMap::new() }
|
||||
}
|
||||
|
||||
fn complete<H>(self, block: N, trie_root: H) -> CachedBuildData<H, N> {
|
||||
@@ -232,30 +231,42 @@ mod tests {
|
||||
#[test]
|
||||
fn obsolete_entries_are_purged_when_new_ct_is_built() {
|
||||
let mut cache = BuildCache::<u32, u32>::new();
|
||||
cache.perform(CacheAction::CacheBuildData(IncompleteCachedBuildData::new()
|
||||
.insert(None, vec![vec![1]].into_iter().collect())
|
||||
.complete(1, 1)));
|
||||
cache.perform(CacheAction::CacheBuildData(IncompleteCachedBuildData::new()
|
||||
.insert(None, vec![vec![2]].into_iter().collect())
|
||||
.complete(2, 2)));
|
||||
cache.perform(CacheAction::CacheBuildData(IncompleteCachedBuildData::new()
|
||||
.insert(None, vec![vec![3]].into_iter().collect())
|
||||
.complete(3, 3)));
|
||||
cache.perform(CacheAction::CacheBuildData(
|
||||
IncompleteCachedBuildData::new()
|
||||
.insert(None, vec![vec![1]].into_iter().collect())
|
||||
.complete(1, 1),
|
||||
));
|
||||
cache.perform(CacheAction::CacheBuildData(
|
||||
IncompleteCachedBuildData::new()
|
||||
.insert(None, vec![vec![2]].into_iter().collect())
|
||||
.complete(2, 2),
|
||||
));
|
||||
cache.perform(CacheAction::CacheBuildData(
|
||||
IncompleteCachedBuildData::new()
|
||||
.insert(None, vec![vec![3]].into_iter().collect())
|
||||
.complete(3, 3),
|
||||
));
|
||||
|
||||
assert_eq!(cache.changed_keys.len(), 3);
|
||||
|
||||
cache.perform(CacheAction::CacheBuildData(IncompleteCachedBuildData::new()
|
||||
.set_digest_input_blocks(vec![1, 2, 3])
|
||||
.complete(4, 4)));
|
||||
cache.perform(CacheAction::CacheBuildData(
|
||||
IncompleteCachedBuildData::new()
|
||||
.set_digest_input_blocks(vec![1, 2, 3])
|
||||
.complete(4, 4),
|
||||
));
|
||||
|
||||
assert_eq!(cache.changed_keys.len(), 1);
|
||||
|
||||
cache.perform(CacheAction::CacheBuildData(IncompleteCachedBuildData::new()
|
||||
.insert(None, vec![vec![8]].into_iter().collect())
|
||||
.complete(8, 8)));
|
||||
cache.perform(CacheAction::CacheBuildData(IncompleteCachedBuildData::new()
|
||||
.insert(None, vec![vec![12]].into_iter().collect())
|
||||
.complete(12, 12)));
|
||||
cache.perform(CacheAction::CacheBuildData(
|
||||
IncompleteCachedBuildData::new()
|
||||
.insert(None, vec![vec![8]].into_iter().collect())
|
||||
.complete(8, 8),
|
||||
));
|
||||
cache.perform(CacheAction::CacheBuildData(
|
||||
IncompleteCachedBuildData::new()
|
||||
.insert(None, vec![vec![12]].into_iter().collect())
|
||||
.complete(12, 12),
|
||||
));
|
||||
|
||||
assert_eq!(cache.changed_keys.len(), 3);
|
||||
|
||||
|
||||
@@ -18,8 +18,8 @@
|
||||
//! Structures and functions to return blocks whose changes are to be included
|
||||
//! in given block's changes trie.
|
||||
|
||||
use crate::changes_trie::{BlockNumber, ConfigurationRange};
|
||||
use num_traits::Zero;
|
||||
use crate::changes_trie::{ConfigurationRange, BlockNumber};
|
||||
|
||||
/// Returns iterator of OTHER blocks that are required for inclusion into
|
||||
/// changes trie of given block. Blocks are guaranteed to be returned in
|
||||
@@ -31,13 +31,19 @@ pub fn digest_build_iterator<'a, Number: BlockNumber>(
|
||||
block: Number,
|
||||
) -> DigestBuildIterator<Number> {
|
||||
// prepare digest build parameters
|
||||
let (_, _, digest_step) = match config.config.digest_level_at_block(config.zero, block.clone()) {
|
||||
let (_, _, digest_step) = match config.config.digest_level_at_block(config.zero, block.clone())
|
||||
{
|
||||
Some((current_level, digest_interval, digest_step)) =>
|
||||
(current_level, digest_interval, digest_step),
|
||||
None => return DigestBuildIterator::empty(),
|
||||
};
|
||||
|
||||
DigestBuildIterator::new(block.clone(), config.end.unwrap_or(block), config.config.digest_interval, digest_step)
|
||||
DigestBuildIterator::new(
|
||||
block.clone(),
|
||||
config.end.unwrap_or(block),
|
||||
config.config.digest_interval,
|
||||
digest_step,
|
||||
)
|
||||
}
|
||||
|
||||
/// Changes trie build iterator that returns numbers of OTHER blocks that are
|
||||
@@ -56,7 +62,6 @@ pub struct DigestBuildIterator<Number: BlockNumber> {
|
||||
max_step: u32,
|
||||
|
||||
// Mutable data below:
|
||||
|
||||
/// Step of current blocks range.
|
||||
current_step: u32,
|
||||
/// Reverse step of current blocks range.
|
||||
@@ -98,7 +103,7 @@ impl<Number: BlockNumber> Iterator for DigestBuildIterator<Number> {
|
||||
if let Some(next) = self.current_range.as_mut().and_then(|iter| iter.next()) {
|
||||
if next < self.end {
|
||||
self.last_block = Some(next.clone());
|
||||
return Some(next);
|
||||
return Some(next)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,14 +117,16 @@ impl<Number: BlockNumber> Iterator for DigestBuildIterator<Number> {
|
||||
self.current_step_reverse * self.digest_interval
|
||||
};
|
||||
if next_step_reverse > self.max_step {
|
||||
return None;
|
||||
return None
|
||||
}
|
||||
|
||||
self.current_step_reverse = next_step_reverse;
|
||||
self.current_range = Some(BlocksRange::new(
|
||||
match self.last_block.clone() {
|
||||
Some(last_block) => last_block + self.current_step.into(),
|
||||
None => self.block.clone() - (self.current_step * self.digest_interval - self.current_step).into(),
|
||||
None =>
|
||||
self.block.clone() -
|
||||
(self.current_step * self.digest_interval - self.current_step).into(),
|
||||
},
|
||||
self.block.clone(),
|
||||
self.current_step.into(),
|
||||
@@ -143,11 +150,7 @@ struct BlocksRange<Number: BlockNumber> {
|
||||
|
||||
impl<Number: BlockNumber> BlocksRange<Number> {
|
||||
pub fn new(begin: Number, end: Number, step: Number) -> Self {
|
||||
BlocksRange {
|
||||
current: begin,
|
||||
end,
|
||||
step,
|
||||
}
|
||||
BlocksRange { current: begin, end, step }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -156,7 +159,7 @@ impl<Number: BlockNumber> Iterator for BlocksRange<Number> {
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.current >= self.end {
|
||||
return None;
|
||||
return None
|
||||
}
|
||||
|
||||
let current = Some(self.current.clone());
|
||||
@@ -167,8 +170,8 @@ impl<Number: BlockNumber> Iterator for BlocksRange<Number> {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::changes_trie::Configuration;
|
||||
use super::*;
|
||||
use crate::changes_trie::Configuration;
|
||||
|
||||
fn digest_build_iterator(
|
||||
digest_interval: u32,
|
||||
@@ -179,10 +182,7 @@ mod tests {
|
||||
) -> DigestBuildIterator<u64> {
|
||||
super::digest_build_iterator(
|
||||
ConfigurationRange {
|
||||
config: &Configuration {
|
||||
digest_interval,
|
||||
digest_levels,
|
||||
},
|
||||
config: &Configuration { digest_interval, digest_levels },
|
||||
zero,
|
||||
end,
|
||||
},
|
||||
@@ -215,9 +215,21 @@ mod tests {
|
||||
fn test_with_zero(zero: u64) {
|
||||
let empty = (0, 0, 0);
|
||||
assert_eq!(digest_build_iterator_basic(4, 16, zero, zero + 0), empty, "block is 0");
|
||||
assert_eq!(digest_build_iterator_basic(0, 16, zero, zero + 64), empty, "digest_interval is 0");
|
||||
assert_eq!(digest_build_iterator_basic(1, 16, zero, zero + 64), empty, "digest_interval is 1");
|
||||
assert_eq!(digest_build_iterator_basic(4, 0, zero, zero + 64), empty, "digest_levels is 0");
|
||||
assert_eq!(
|
||||
digest_build_iterator_basic(0, 16, zero, zero + 64),
|
||||
empty,
|
||||
"digest_interval is 0"
|
||||
);
|
||||
assert_eq!(
|
||||
digest_build_iterator_basic(1, 16, zero, zero + 64),
|
||||
empty,
|
||||
"digest_interval is 1"
|
||||
);
|
||||
assert_eq!(
|
||||
digest_build_iterator_basic(4, 0, zero, zero + 64),
|
||||
empty,
|
||||
"digest_levels is 0"
|
||||
);
|
||||
assert_eq!(
|
||||
digest_build_iterator_basic(4, 16, zero, zero + 1),
|
||||
empty,
|
||||
@@ -238,12 +250,11 @@ mod tests {
|
||||
empty,
|
||||
"digest is not required for this block",
|
||||
);
|
||||
assert_eq!(digest_build_iterator_basic(
|
||||
::std::u32::MAX / 2 + 1,
|
||||
16,
|
||||
zero,
|
||||
::std::u64::MAX,
|
||||
), empty, "digest_interval * 2 is greater than u64::MAX");
|
||||
assert_eq!(
|
||||
digest_build_iterator_basic(::std::u32::MAX / 2 + 1, 16, zero, ::std::u64::MAX,),
|
||||
empty,
|
||||
"digest_interval * 2 is greater than u64::MAX"
|
||||
);
|
||||
}
|
||||
|
||||
test_with_zero(0);
|
||||
@@ -326,18 +337,37 @@ mod tests {
|
||||
#[test]
|
||||
fn digest_iterator_returns_level1_blocks() {
|
||||
fn test_with_zero(zero: u64) {
|
||||
assert_eq!(digest_build_iterator_blocks(16, 1, zero, zero + 16, None),
|
||||
assert_eq!(
|
||||
digest_build_iterator_blocks(16, 1, zero, zero + 16, None),
|
||||
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
|
||||
.iter().map(|item| zero + item).collect::<Vec<_>>());
|
||||
assert_eq!(digest_build_iterator_blocks(16, 1, zero, zero + 256, None),
|
||||
.iter()
|
||||
.map(|item| zero + item)
|
||||
.collect::<Vec<_>>()
|
||||
);
|
||||
assert_eq!(
|
||||
digest_build_iterator_blocks(16, 1, zero, zero + 256, None),
|
||||
[241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255]
|
||||
.iter().map(|item| zero + item).collect::<Vec<_>>());
|
||||
assert_eq!(digest_build_iterator_blocks(16, 2, zero, zero + 32, None),
|
||||
.iter()
|
||||
.map(|item| zero + item)
|
||||
.collect::<Vec<_>>()
|
||||
);
|
||||
assert_eq!(
|
||||
digest_build_iterator_blocks(16, 2, zero, zero + 32, None),
|
||||
[17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31]
|
||||
.iter().map(|item| zero + item).collect::<Vec<_>>());
|
||||
assert_eq!(digest_build_iterator_blocks(16, 3, zero, zero + 4080, None),
|
||||
[4065, 4066, 4067, 4068, 4069, 4070, 4071, 4072, 4073, 4074, 4075, 4076, 4077, 4078, 4079]
|
||||
.iter().map(|item| zero + item).collect::<Vec<_>>());
|
||||
.iter()
|
||||
.map(|item| zero + item)
|
||||
.collect::<Vec<_>>()
|
||||
);
|
||||
assert_eq!(
|
||||
digest_build_iterator_blocks(16, 3, zero, zero + 4080, None),
|
||||
[
|
||||
4065, 4066, 4067, 4068, 4069, 4070, 4071, 4072, 4073, 4074, 4075, 4076, 4077,
|
||||
4078, 4079
|
||||
]
|
||||
.iter()
|
||||
.map(|item| zero + item)
|
||||
.collect::<Vec<_>>()
|
||||
);
|
||||
}
|
||||
|
||||
test_with_zero(0);
|
||||
@@ -348,21 +378,30 @@ mod tests {
|
||||
#[test]
|
||||
fn digest_iterator_returns_level1_and_level2_blocks() {
|
||||
fn test_with_zero(zero: u64) {
|
||||
assert_eq!(digest_build_iterator_blocks(16, 2, zero, zero + 256, None),
|
||||
assert_eq!(
|
||||
digest_build_iterator_blocks(16, 2, zero, zero + 256, None),
|
||||
[
|
||||
// level2 points to previous 16-1 level1 digests:
|
||||
16, 32, 48, 64, 80, 96, 112, 128, 144, 160, 176, 192, 208, 224, 240,
|
||||
// level2 is a level1 digest of 16-1 previous blocks:
|
||||
241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255,
|
||||
].iter().map(|item| zero + item).collect::<Vec<_>>(),
|
||||
]
|
||||
.iter()
|
||||
.map(|item| zero + item)
|
||||
.collect::<Vec<_>>(),
|
||||
);
|
||||
assert_eq!(digest_build_iterator_blocks(16, 2, zero, zero + 4096, None),
|
||||
assert_eq!(
|
||||
digest_build_iterator_blocks(16, 2, zero, zero + 4096, None),
|
||||
[
|
||||
// level2 points to previous 16-1 level1 digests:
|
||||
3856, 3872, 3888, 3904, 3920, 3936, 3952, 3968, 3984, 4000, 4016, 4032, 4048, 4064, 4080,
|
||||
// level2 is a level1 digest of 16-1 previous blocks:
|
||||
4081, 4082, 4083, 4084, 4085, 4086, 4087, 4088, 4089, 4090, 4091, 4092, 4093, 4094, 4095,
|
||||
].iter().map(|item| zero + item).collect::<Vec<_>>(),
|
||||
3856, 3872, 3888, 3904, 3920, 3936, 3952, 3968, 3984, 4000, 4016, 4032, 4048,
|
||||
4064, 4080, // level2 is a level1 digest of 16-1 previous blocks:
|
||||
4081, 4082, 4083, 4084, 4085, 4086, 4087, 4088, 4089, 4090, 4091, 4092, 4093,
|
||||
4094, 4095,
|
||||
]
|
||||
.iter()
|
||||
.map(|item| zero + item)
|
||||
.collect::<Vec<_>>(),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -374,15 +413,20 @@ mod tests {
|
||||
#[test]
|
||||
fn digest_iterator_returns_level1_and_level2_and_level3_blocks() {
|
||||
fn test_with_zero(zero: u64) {
|
||||
assert_eq!(digest_build_iterator_blocks(16, 3, zero, zero + 4096, None),
|
||||
assert_eq!(
|
||||
digest_build_iterator_blocks(16, 3, zero, zero + 4096, None),
|
||||
[
|
||||
// level3 points to previous 16-1 level2 digests:
|
||||
256, 512, 768, 1024, 1280, 1536, 1792, 2048, 2304, 2560, 2816, 3072, 3328, 3584, 3840,
|
||||
// level3 points to previous 16-1 level1 digests:
|
||||
3856, 3872, 3888, 3904, 3920, 3936, 3952, 3968, 3984, 4000, 4016, 4032, 4048, 4064, 4080,
|
||||
// level3 is a level1 digest of 16-1 previous blocks:
|
||||
4081, 4082, 4083, 4084, 4085, 4086, 4087, 4088, 4089, 4090, 4091, 4092, 4093, 4094, 4095,
|
||||
].iter().map(|item| zero + item).collect::<Vec<_>>(),
|
||||
256, 512, 768, 1024, 1280, 1536, 1792, 2048, 2304, 2560, 2816, 3072, 3328, 3584,
|
||||
3840, // level3 points to previous 16-1 level1 digests:
|
||||
3856, 3872, 3888, 3904, 3920, 3936, 3952, 3968, 3984, 4000, 4016, 4032, 4048,
|
||||
4064, 4080, // level3 is a level1 digest of 16-1 previous blocks:
|
||||
4081, 4082, 4083, 4084, 4085, 4086, 4087, 4088, 4089, 4090, 4091, 4092, 4093,
|
||||
4094, 4095,
|
||||
]
|
||||
.iter()
|
||||
.map(|item| zero + item)
|
||||
.collect::<Vec<_>>(),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -394,7 +438,8 @@ mod tests {
|
||||
#[test]
|
||||
fn digest_iterator_returns_skewed_digest_blocks() {
|
||||
fn test_with_zero(zero: u64) {
|
||||
assert_eq!(digest_build_iterator_blocks(16, 3, zero, zero + 4096, Some(zero + 1338)),
|
||||
assert_eq!(
|
||||
digest_build_iterator_blocks(16, 3, zero, zero + 4096, Some(zero + 1338)),
|
||||
[
|
||||
// level3 MUST point to previous 16-1 level2 digests, BUT there are only 5:
|
||||
256, 512, 768, 1024, 1280,
|
||||
@@ -402,7 +447,10 @@ mod tests {
|
||||
1296, 1312, 1328,
|
||||
// level3 MUST be a level1 digest of 16-1 previous blocks, BUT there are only 9:
|
||||
1329, 1330, 1331, 1332, 1333, 1334, 1335, 1336, 1337,
|
||||
].iter().map(|item| zero + item).collect::<Vec<_>>(),
|
||||
]
|
||||
.iter()
|
||||
.map(|item| zero + item)
|
||||
.collect::<Vec<_>>(),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -414,14 +462,18 @@ mod tests {
|
||||
#[test]
|
||||
fn digest_iterator_returns_skewed_digest_blocks_skipping_level() {
|
||||
fn test_with_zero(zero: u64) {
|
||||
assert_eq!(digest_build_iterator_blocks(16, 3, zero, zero + 4096, Some(zero + 1284)),
|
||||
assert_eq!(
|
||||
digest_build_iterator_blocks(16, 3, zero, zero + 4096, Some(zero + 1284)),
|
||||
[
|
||||
// level3 MUST point to previous 16-1 level2 digests, BUT there are only 5:
|
||||
256, 512, 768, 1024, 1280,
|
||||
// level3 MUST point to previous 16-1 level1 digests, BUT there are NO ANY L1-digests:
|
||||
// level3 MUST be a level1 digest of 16-1 previous blocks, BUT there are only 3:
|
||||
1281, 1282, 1283,
|
||||
].iter().map(|item| zero + item).collect::<Vec<_>>(),
|
||||
]
|
||||
.iter()
|
||||
.map(|item| zero + item)
|
||||
.collect::<Vec<_>>(),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -18,20 +18,22 @@
|
||||
//! Functions + iterator that traverses changes tries and returns all
|
||||
//! (block, extrinsic) pairs where given key has been changed.
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::collections::VecDeque;
|
||||
use codec::{Decode, Encode, Codec};
|
||||
use crate::{
|
||||
changes_trie::{
|
||||
input::{ChildIndex, DigestIndex, DigestIndexValue, ExtrinsicIndex, ExtrinsicIndexValue},
|
||||
storage::{InMemoryStorage, TrieBackendAdapter},
|
||||
surface_iterator::{surface_iterator, SurfaceIterator},
|
||||
AnchorBlockId, BlockNumber, ConfigurationRange, RootsStorage, Storage,
|
||||
},
|
||||
proving_backend::ProvingBackendRecorder,
|
||||
trie_backend_essence::TrieBackendEssence,
|
||||
};
|
||||
use codec::{Codec, Decode, Encode};
|
||||
use hash_db::Hasher;
|
||||
use num_traits::Zero;
|
||||
use sp_core::storage::PrefixedStorageKey;
|
||||
use sp_trie::Recorder;
|
||||
use crate::changes_trie::{AnchorBlockId, ConfigurationRange, RootsStorage, Storage, BlockNumber};
|
||||
use crate::changes_trie::input::{DigestIndex, ExtrinsicIndex, DigestIndexValue, ExtrinsicIndexValue};
|
||||
use crate::changes_trie::storage::{TrieBackendAdapter, InMemoryStorage};
|
||||
use crate::changes_trie::input::ChildIndex;
|
||||
use crate::changes_trie::surface_iterator::{surface_iterator, SurfaceIterator};
|
||||
use crate::proving_backend::ProvingBackendRecorder;
|
||||
use crate::trie_backend_essence::{TrieBackendEssence};
|
||||
use std::{cell::RefCell, collections::VecDeque};
|
||||
|
||||
/// Return changes of given key at given blocks range.
|
||||
/// `max` is the number of best known block.
|
||||
@@ -57,12 +59,7 @@ pub fn key_changes<'a, H: Hasher, Number: BlockNumber>(
|
||||
begin: begin.clone(),
|
||||
end,
|
||||
config: config.clone(),
|
||||
surface: surface_iterator(
|
||||
config,
|
||||
max,
|
||||
begin,
|
||||
end.number.clone(),
|
||||
)?,
|
||||
surface: surface_iterator(config, max, begin, end.number.clone())?,
|
||||
|
||||
extrinsics: Default::default(),
|
||||
blocks: Default::default(),
|
||||
@@ -72,7 +69,6 @@ pub fn key_changes<'a, H: Hasher, Number: BlockNumber>(
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
/// Returns proof of changes of given key at given blocks range.
|
||||
/// `max` is the number of best known block.
|
||||
pub fn key_changes_proof<'a, H: Hasher, Number: BlockNumber>(
|
||||
@@ -83,7 +79,10 @@ pub fn key_changes_proof<'a, H: Hasher, Number: BlockNumber>(
|
||||
max: Number,
|
||||
storage_key: Option<&PrefixedStorageKey>,
|
||||
key: &[u8],
|
||||
) -> Result<Vec<Vec<u8>>, String> where H::Out: Codec {
|
||||
) -> Result<Vec<Vec<u8>>, String>
|
||||
where
|
||||
H::Out: Codec,
|
||||
{
|
||||
// we can't query any roots before root
|
||||
let max = std::cmp::min(max, end.number.clone());
|
||||
|
||||
@@ -96,12 +95,7 @@ pub fn key_changes_proof<'a, H: Hasher, Number: BlockNumber>(
|
||||
begin: begin.clone(),
|
||||
end,
|
||||
config: config.clone(),
|
||||
surface: surface_iterator(
|
||||
config,
|
||||
max,
|
||||
begin,
|
||||
end.number.clone(),
|
||||
)?,
|
||||
surface: surface_iterator(config, max, begin, end.number.clone())?,
|
||||
|
||||
extrinsics: Default::default(),
|
||||
blocks: Default::default(),
|
||||
@@ -130,8 +124,11 @@ pub fn key_changes_proof_check<'a, H: Hasher, Number: BlockNumber>(
|
||||
end: &AnchorBlockId<H::Out, Number>,
|
||||
max: Number,
|
||||
storage_key: Option<&PrefixedStorageKey>,
|
||||
key: &[u8]
|
||||
) -> Result<Vec<(Number, u32)>, String> where H::Out: Encode {
|
||||
key: &[u8],
|
||||
) -> Result<Vec<(Number, u32)>, String>
|
||||
where
|
||||
H::Out: Encode,
|
||||
{
|
||||
key_changes_proof_check_with_db(
|
||||
config,
|
||||
roots_storage,
|
||||
@@ -153,8 +150,11 @@ pub fn key_changes_proof_check_with_db<'a, H: Hasher, Number: BlockNumber>(
|
||||
end: &AnchorBlockId<H::Out, Number>,
|
||||
max: Number,
|
||||
storage_key: Option<&PrefixedStorageKey>,
|
||||
key: &[u8]
|
||||
) -> Result<Vec<(Number, u32)>, String> where H::Out: Encode {
|
||||
key: &[u8],
|
||||
) -> Result<Vec<(Number, u32)>, String>
|
||||
where
|
||||
H::Out: Encode,
|
||||
{
|
||||
// we can't query any roots before root
|
||||
let max = std::cmp::min(max, end.number.clone());
|
||||
|
||||
@@ -167,28 +167,24 @@ pub fn key_changes_proof_check_with_db<'a, H: Hasher, Number: BlockNumber>(
|
||||
begin: begin.clone(),
|
||||
end,
|
||||
config: config.clone(),
|
||||
surface: surface_iterator(
|
||||
config,
|
||||
max,
|
||||
begin,
|
||||
end.number.clone(),
|
||||
)?,
|
||||
surface: surface_iterator(config, max, begin, end.number.clone())?,
|
||||
|
||||
extrinsics: Default::default(),
|
||||
blocks: Default::default(),
|
||||
|
||||
_hasher: ::std::marker::PhantomData::<H>::default(),
|
||||
},
|
||||
}.collect()
|
||||
}
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Drilldown iterator - receives 'digest points' from surface iterator and explores
|
||||
/// every point until extrinsic is found.
|
||||
pub struct DrilldownIteratorEssence<'a, H, Number>
|
||||
where
|
||||
H: Hasher,
|
||||
Number: BlockNumber,
|
||||
H::Out: 'a,
|
||||
where
|
||||
H: Hasher,
|
||||
Number: BlockNumber,
|
||||
H::Out: 'a,
|
||||
{
|
||||
storage_key: Option<&'a PrefixedStorageKey>,
|
||||
key: &'a [u8],
|
||||
@@ -206,14 +202,14 @@ pub struct DrilldownIteratorEssence<'a, H, Number>
|
||||
}
|
||||
|
||||
impl<'a, H, Number> DrilldownIteratorEssence<'a, H, Number>
|
||||
where
|
||||
H: Hasher,
|
||||
Number: BlockNumber,
|
||||
H::Out: 'a,
|
||||
where
|
||||
H: Hasher,
|
||||
Number: BlockNumber,
|
||||
H::Out: 'a,
|
||||
{
|
||||
pub fn next<F>(&mut self, trie_reader: F) -> Option<Result<(Number, u32), String>>
|
||||
where
|
||||
F: FnMut(&dyn Storage<H, Number>, H::Out, &[u8]) -> Result<Option<Vec<u8>>, String>,
|
||||
where
|
||||
F: FnMut(&dyn Storage<H, Number>, H::Out, &[u8]) -> Result<Option<Vec<u8>>, String>,
|
||||
{
|
||||
match self.do_next(trie_reader) {
|
||||
Ok(Some(res)) => Some(Ok(res)),
|
||||
@@ -223,25 +219,26 @@ impl<'a, H, Number> DrilldownIteratorEssence<'a, H, Number>
|
||||
}
|
||||
|
||||
fn do_next<F>(&mut self, mut trie_reader: F) -> Result<Option<(Number, u32)>, String>
|
||||
where
|
||||
F: FnMut(&dyn Storage<H, Number>, H::Out, &[u8]) -> Result<Option<Vec<u8>>, String>,
|
||||
where
|
||||
F: FnMut(&dyn Storage<H, Number>, H::Out, &[u8]) -> Result<Option<Vec<u8>>, String>,
|
||||
{
|
||||
loop {
|
||||
if let Some((block, extrinsic)) = self.extrinsics.pop_front() {
|
||||
return Ok(Some((block, extrinsic)));
|
||||
return Ok(Some((block, extrinsic)))
|
||||
}
|
||||
|
||||
if let Some((block, level)) = self.blocks.pop_front() {
|
||||
// not having a changes trie root is an error because:
|
||||
// we never query roots for future blocks
|
||||
// AND trie roots for old blocks are known (both on full + light node)
|
||||
let trie_root = self.roots_storage.root(&self.end, block.clone())?
|
||||
.ok_or_else(|| format!("Changes trie root for block {} is not found", block.clone()))?;
|
||||
let trie_root =
|
||||
self.roots_storage.root(&self.end, block.clone())?.ok_or_else(|| {
|
||||
format!("Changes trie root for block {} is not found", block.clone())
|
||||
})?;
|
||||
let trie_root = if let Some(storage_key) = self.storage_key {
|
||||
let child_key = ChildIndex {
|
||||
block: block.clone(),
|
||||
storage_key: storage_key.clone(),
|
||||
}.encode();
|
||||
let child_key =
|
||||
ChildIndex { block: block.clone(), storage_key: storage_key.clone() }
|
||||
.encode();
|
||||
if let Some(trie_root) = trie_reader(self.storage, trie_root, &child_key)?
|
||||
.and_then(|v| <Vec<u8>>::decode(&mut &v[..]).ok())
|
||||
.map(|v| {
|
||||
@@ -251,7 +248,7 @@ impl<'a, H, Number> DrilldownIteratorEssence<'a, H, Number>
|
||||
}) {
|
||||
trie_root
|
||||
} else {
|
||||
continue;
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
trie_root
|
||||
@@ -260,18 +257,24 @@ impl<'a, H, Number> DrilldownIteratorEssence<'a, H, Number>
|
||||
// only return extrinsics for blocks before self.max
|
||||
// most of blocks will be filtered out before pushing to `self.blocks`
|
||||
// here we just throwing away changes at digest blocks we're processing
|
||||
debug_assert!(block >= self.begin, "We shall not touch digests earlier than a range' begin");
|
||||
debug_assert!(
|
||||
block >= self.begin,
|
||||
"We shall not touch digests earlier than a range' begin"
|
||||
);
|
||||
if block <= self.end.number {
|
||||
let extrinsics_key = ExtrinsicIndex { block: block.clone(), key: self.key.to_vec() }.encode();
|
||||
let extrinsics_key =
|
||||
ExtrinsicIndex { block: block.clone(), key: self.key.to_vec() }.encode();
|
||||
let extrinsics = trie_reader(self.storage, trie_root, &extrinsics_key);
|
||||
if let Some(extrinsics) = extrinsics? {
|
||||
if let Ok(extrinsics) = ExtrinsicIndexValue::decode(&mut &extrinsics[..]) {
|
||||
self.extrinsics.extend(extrinsics.into_iter().rev().map(|e| (block.clone(), e)));
|
||||
self.extrinsics
|
||||
.extend(extrinsics.into_iter().rev().map(|e| (block.clone(), e)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let blocks_key = DigestIndex { block: block.clone(), key: self.key.to_vec() }.encode();
|
||||
let blocks_key =
|
||||
DigestIndex { block: block.clone(), key: self.key.to_vec() }.encode();
|
||||
let blocks = trie_reader(self.storage, trie_root, &blocks_key);
|
||||
if let Some(blocks) = blocks? {
|
||||
if let Ok(blocks) = <DigestIndexValue<Number>>::decode(&mut &blocks[..]) {
|
||||
@@ -280,23 +283,35 @@ impl<'a, H, Number> DrilldownIteratorEssence<'a, H, Number>
|
||||
let begin = self.begin.clone();
|
||||
let end = self.end.number.clone();
|
||||
let config = self.config.clone();
|
||||
self.blocks.extend(blocks.into_iter()
|
||||
.rev()
|
||||
.filter(|b| level.map(|level| level > 1).unwrap_or(true) || (*b >= begin && *b <= end))
|
||||
.map(|b| {
|
||||
let prev_level = level
|
||||
.map(|level| Some(level - 1))
|
||||
.unwrap_or_else(||
|
||||
Some(config.config.digest_level_at_block(config.zero.clone(), b.clone())
|
||||
.map(|(level, _, _)| level)
|
||||
.unwrap_or_else(|| Zero::zero())));
|
||||
(b, prev_level)
|
||||
})
|
||||
self.blocks.extend(
|
||||
blocks
|
||||
.into_iter()
|
||||
.rev()
|
||||
.filter(|b| {
|
||||
level.map(|level| level > 1).unwrap_or(true) ||
|
||||
(*b >= begin && *b <= end)
|
||||
})
|
||||
.map(|b| {
|
||||
let prev_level =
|
||||
level.map(|level| Some(level - 1)).unwrap_or_else(|| {
|
||||
Some(
|
||||
config
|
||||
.config
|
||||
.digest_level_at_block(
|
||||
config.zero.clone(),
|
||||
b.clone(),
|
||||
)
|
||||
.map(|(level, _, _)| level)
|
||||
.unwrap_or_else(|| Zero::zero()),
|
||||
)
|
||||
});
|
||||
(b, prev_level)
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
continue;
|
||||
continue
|
||||
}
|
||||
|
||||
match self.surface.next() {
|
||||
@@ -310,46 +325,50 @@ impl<'a, H, Number> DrilldownIteratorEssence<'a, H, Number>
|
||||
|
||||
/// Exploring drilldown operator.
|
||||
pub struct DrilldownIterator<'a, H, Number>
|
||||
where
|
||||
Number: BlockNumber,
|
||||
H: Hasher,
|
||||
H::Out: 'a,
|
||||
where
|
||||
Number: BlockNumber,
|
||||
H: Hasher,
|
||||
H::Out: 'a,
|
||||
{
|
||||
essence: DrilldownIteratorEssence<'a, H, Number>,
|
||||
}
|
||||
|
||||
impl<'a, H: Hasher, Number: BlockNumber> Iterator for DrilldownIterator<'a, H, Number>
|
||||
where H::Out: Encode
|
||||
where
|
||||
H::Out: Encode,
|
||||
{
|
||||
type Item = Result<(Number, u32), String>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.essence.next(|storage, root, key|
|
||||
TrieBackendEssence::<_, H>::new(TrieBackendAdapter::new(storage), root).storage(key))
|
||||
self.essence.next(|storage, root, key| {
|
||||
TrieBackendEssence::<_, H>::new(TrieBackendAdapter::new(storage), root).storage(key)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Proving drilldown iterator.
|
||||
struct ProvingDrilldownIterator<'a, H, Number>
|
||||
where
|
||||
Number: BlockNumber,
|
||||
H: Hasher,
|
||||
H::Out: 'a,
|
||||
where
|
||||
Number: BlockNumber,
|
||||
H: Hasher,
|
||||
H::Out: 'a,
|
||||
{
|
||||
essence: DrilldownIteratorEssence<'a, H, Number>,
|
||||
proof_recorder: RefCell<Recorder<H::Out>>,
|
||||
}
|
||||
|
||||
impl<'a, H, Number> ProvingDrilldownIterator<'a, H, Number>
|
||||
where
|
||||
Number: BlockNumber,
|
||||
H: Hasher,
|
||||
H::Out: 'a,
|
||||
where
|
||||
Number: BlockNumber,
|
||||
H: Hasher,
|
||||
H::Out: 'a,
|
||||
{
|
||||
/// Consume the iterator, extracting the gathered proof in lexicographical order
|
||||
/// by value.
|
||||
pub fn extract_proof(self) -> Vec<Vec<u8>> {
|
||||
self.proof_recorder.into_inner().drain()
|
||||
self.proof_recorder
|
||||
.into_inner()
|
||||
.drain()
|
||||
.into_iter()
|
||||
.map(|n| n.data.to_vec())
|
||||
.collect()
|
||||
@@ -357,32 +376,34 @@ impl<'a, H, Number> ProvingDrilldownIterator<'a, H, Number>
|
||||
}
|
||||
|
||||
impl<'a, H, Number> Iterator for ProvingDrilldownIterator<'a, H, Number>
|
||||
where
|
||||
Number: BlockNumber,
|
||||
H: Hasher,
|
||||
H::Out: 'a + Codec,
|
||||
where
|
||||
Number: BlockNumber,
|
||||
H: Hasher,
|
||||
H::Out: 'a + Codec,
|
||||
{
|
||||
type Item = Result<(Number, u32), String>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let proof_recorder = &mut *self.proof_recorder.try_borrow_mut()
|
||||
let proof_recorder = &mut *self
|
||||
.proof_recorder
|
||||
.try_borrow_mut()
|
||||
.expect("only fails when already borrowed; storage() is non-reentrant; qed");
|
||||
self.essence.next(|storage, root, key|
|
||||
self.essence.next(|storage, root, key| {
|
||||
ProvingBackendRecorder::<_, H> {
|
||||
backend: &TrieBackendEssence::new(TrieBackendAdapter::new(storage), root),
|
||||
proof_recorder,
|
||||
}.storage(key))
|
||||
}
|
||||
.storage(key)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::iter::FromIterator;
|
||||
use crate::changes_trie::Configuration;
|
||||
use crate::changes_trie::input::InputPair;
|
||||
use crate::changes_trie::storage::InMemoryStorage;
|
||||
use sp_runtime::traits::BlakeTwo256;
|
||||
use super::*;
|
||||
use crate::changes_trie::{input::InputPair, storage::InMemoryStorage, Configuration};
|
||||
use sp_runtime::traits::BlakeTwo256;
|
||||
use std::iter::FromIterator;
|
||||
|
||||
fn child_key() -> PrefixedStorageKey {
|
||||
let child_info = sp_core::storage::ChildInfo::new_default(&b"1"[..]);
|
||||
@@ -391,64 +412,98 @@ mod tests {
|
||||
|
||||
fn prepare_for_drilldown() -> (Configuration, InMemoryStorage<BlakeTwo256, u64>) {
|
||||
let config = Configuration { digest_interval: 4, digest_levels: 2 };
|
||||
let backend = InMemoryStorage::with_inputs(vec![
|
||||
// digest: 1..4 => [(3, 0)]
|
||||
(1, vec![
|
||||
]),
|
||||
(2, vec![
|
||||
]),
|
||||
(3, vec![
|
||||
InputPair::ExtrinsicIndex(ExtrinsicIndex { block: 3, key: vec![42] }, vec![0]),
|
||||
]),
|
||||
(4, vec![
|
||||
InputPair::DigestIndex(DigestIndex { block: 4, key: vec![42] }, vec![3]),
|
||||
]),
|
||||
// digest: 5..8 => [(6, 3), (8, 1+2)]
|
||||
(5, vec![]),
|
||||
(6, vec![
|
||||
InputPair::ExtrinsicIndex(ExtrinsicIndex { block: 6, key: vec![42] }, vec![3]),
|
||||
]),
|
||||
(7, vec![]),
|
||||
(8, vec![
|
||||
InputPair::ExtrinsicIndex(ExtrinsicIndex { block: 8, key: vec![42] }, vec![1, 2]),
|
||||
InputPair::DigestIndex(DigestIndex { block: 8, key: vec![42] }, vec![6]),
|
||||
]),
|
||||
// digest: 9..12 => []
|
||||
(9, vec![]),
|
||||
(10, vec![]),
|
||||
(11, vec![]),
|
||||
(12, vec![]),
|
||||
// digest: 0..16 => [4, 8]
|
||||
(13, vec![]),
|
||||
(14, vec![]),
|
||||
(15, vec![]),
|
||||
(16, vec![
|
||||
InputPair::DigestIndex(DigestIndex { block: 16, key: vec![42] }, vec![4, 8]),
|
||||
]),
|
||||
], vec![(child_key(), vec![
|
||||
(1, vec![
|
||||
InputPair::ExtrinsicIndex(ExtrinsicIndex { block: 1, key: vec![42] }, vec![0]),
|
||||
]),
|
||||
(2, vec![
|
||||
InputPair::ExtrinsicIndex(ExtrinsicIndex { block: 2, key: vec![42] }, vec![3]),
|
||||
]),
|
||||
(16, vec![
|
||||
InputPair::ExtrinsicIndex(ExtrinsicIndex { block: 16, key: vec![42] }, vec![5]),
|
||||
|
||||
InputPair::DigestIndex(DigestIndex { block: 16, key: vec![42] }, vec![2]),
|
||||
]),
|
||||
]),
|
||||
]);
|
||||
let backend = InMemoryStorage::with_inputs(
|
||||
vec![
|
||||
// digest: 1..4 => [(3, 0)]
|
||||
(1, vec![]),
|
||||
(2, vec![]),
|
||||
(
|
||||
3,
|
||||
vec![InputPair::ExtrinsicIndex(
|
||||
ExtrinsicIndex { block: 3, key: vec![42] },
|
||||
vec![0],
|
||||
)],
|
||||
),
|
||||
(4, vec![InputPair::DigestIndex(DigestIndex { block: 4, key: vec![42] }, vec![3])]),
|
||||
// digest: 5..8 => [(6, 3), (8, 1+2)]
|
||||
(5, vec![]),
|
||||
(
|
||||
6,
|
||||
vec![InputPair::ExtrinsicIndex(
|
||||
ExtrinsicIndex { block: 6, key: vec![42] },
|
||||
vec![3],
|
||||
)],
|
||||
),
|
||||
(7, vec![]),
|
||||
(
|
||||
8,
|
||||
vec![
|
||||
InputPair::ExtrinsicIndex(
|
||||
ExtrinsicIndex { block: 8, key: vec![42] },
|
||||
vec![1, 2],
|
||||
),
|
||||
InputPair::DigestIndex(DigestIndex { block: 8, key: vec![42] }, vec![6]),
|
||||
],
|
||||
),
|
||||
// digest: 9..12 => []
|
||||
(9, vec![]),
|
||||
(10, vec![]),
|
||||
(11, vec![]),
|
||||
(12, vec![]),
|
||||
// digest: 0..16 => [4, 8]
|
||||
(13, vec![]),
|
||||
(14, vec![]),
|
||||
(15, vec![]),
|
||||
(
|
||||
16,
|
||||
vec![InputPair::DigestIndex(
|
||||
DigestIndex { block: 16, key: vec![42] },
|
||||
vec![4, 8],
|
||||
)],
|
||||
),
|
||||
],
|
||||
vec![(
|
||||
child_key(),
|
||||
vec![
|
||||
(
|
||||
1,
|
||||
vec![InputPair::ExtrinsicIndex(
|
||||
ExtrinsicIndex { block: 1, key: vec![42] },
|
||||
vec![0],
|
||||
)],
|
||||
),
|
||||
(
|
||||
2,
|
||||
vec![InputPair::ExtrinsicIndex(
|
||||
ExtrinsicIndex { block: 2, key: vec![42] },
|
||||
vec![3],
|
||||
)],
|
||||
),
|
||||
(
|
||||
16,
|
||||
vec![
|
||||
InputPair::ExtrinsicIndex(
|
||||
ExtrinsicIndex { block: 16, key: vec![42] },
|
||||
vec![5],
|
||||
),
|
||||
InputPair::DigestIndex(
|
||||
DigestIndex { block: 16, key: vec![42] },
|
||||
vec![2],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
)],
|
||||
);
|
||||
|
||||
(config, backend)
|
||||
}
|
||||
|
||||
fn configuration_range<'a>(config: &'a Configuration, zero: u64) -> ConfigurationRange<'a, u64> {
|
||||
ConfigurationRange {
|
||||
config,
|
||||
zero,
|
||||
end: None,
|
||||
}
|
||||
fn configuration_range<'a>(
|
||||
config: &'a Configuration,
|
||||
zero: u64,
|
||||
) -> ConfigurationRange<'a, u64> {
|
||||
ConfigurationRange { config, zero, end: None }
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -462,7 +517,8 @@ mod tests {
|
||||
16,
|
||||
None,
|
||||
&[42],
|
||||
).and_then(Result::from_iter);
|
||||
)
|
||||
.and_then(Result::from_iter);
|
||||
assert_eq!(drilldown_result, Ok(vec![(8, 2), (8, 1), (6, 3), (3, 0)]));
|
||||
|
||||
let drilldown_result = key_changes::<BlakeTwo256, u64>(
|
||||
@@ -473,7 +529,8 @@ mod tests {
|
||||
4,
|
||||
None,
|
||||
&[42],
|
||||
).and_then(Result::from_iter);
|
||||
)
|
||||
.and_then(Result::from_iter);
|
||||
assert_eq!(drilldown_result, Ok(vec![]));
|
||||
|
||||
let drilldown_result = key_changes::<BlakeTwo256, u64>(
|
||||
@@ -484,7 +541,8 @@ mod tests {
|
||||
4,
|
||||
None,
|
||||
&[42],
|
||||
).and_then(Result::from_iter);
|
||||
)
|
||||
.and_then(Result::from_iter);
|
||||
assert_eq!(drilldown_result, Ok(vec![(3, 0)]));
|
||||
|
||||
let drilldown_result = key_changes::<BlakeTwo256, u64>(
|
||||
@@ -495,7 +553,8 @@ mod tests {
|
||||
7,
|
||||
None,
|
||||
&[42],
|
||||
).and_then(Result::from_iter);
|
||||
)
|
||||
.and_then(Result::from_iter);
|
||||
assert_eq!(drilldown_result, Ok(vec![(6, 3), (3, 0)]));
|
||||
|
||||
let drilldown_result = key_changes::<BlakeTwo256, u64>(
|
||||
@@ -506,7 +565,8 @@ mod tests {
|
||||
8,
|
||||
None,
|
||||
&[42],
|
||||
).and_then(Result::from_iter);
|
||||
)
|
||||
.and_then(Result::from_iter);
|
||||
assert_eq!(drilldown_result, Ok(vec![(8, 2), (8, 1)]));
|
||||
|
||||
let drilldown_result = key_changes::<BlakeTwo256, u64>(
|
||||
@@ -517,7 +577,8 @@ mod tests {
|
||||
8,
|
||||
None,
|
||||
&[42],
|
||||
).and_then(Result::from_iter);
|
||||
)
|
||||
.and_then(Result::from_iter);
|
||||
assert_eq!(drilldown_result, Ok(vec![(6, 3)]));
|
||||
}
|
||||
|
||||
@@ -534,7 +595,9 @@ mod tests {
|
||||
1000,
|
||||
None,
|
||||
&[42],
|
||||
).and_then(|i| i.collect::<Result<Vec<_>, _>>()).is_err());
|
||||
)
|
||||
.and_then(|i| i.collect::<Result<Vec<_>, _>>())
|
||||
.is_err());
|
||||
|
||||
assert!(key_changes::<BlakeTwo256, u64>(
|
||||
configuration_range(&config, 0),
|
||||
@@ -544,7 +607,9 @@ mod tests {
|
||||
1000,
|
||||
Some(&child_key()),
|
||||
&[42],
|
||||
).and_then(|i| i.collect::<Result<Vec<_>, _>>()).is_err());
|
||||
)
|
||||
.and_then(|i| i.collect::<Result<Vec<_>, _>>())
|
||||
.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -558,7 +623,8 @@ mod tests {
|
||||
50,
|
||||
None,
|
||||
&[42],
|
||||
).is_err());
|
||||
)
|
||||
.is_err());
|
||||
assert!(key_changes::<BlakeTwo256, u64>(
|
||||
configuration_range(&config, 0),
|
||||
&storage,
|
||||
@@ -567,10 +633,10 @@ mod tests {
|
||||
100,
|
||||
None,
|
||||
&[42],
|
||||
).is_err());
|
||||
)
|
||||
.is_err());
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn proving_drilldown_iterator_works() {
|
||||
// happens on remote full node:
|
||||
@@ -578,13 +644,27 @@ mod tests {
|
||||
// create drilldown iterator that records all trie nodes during drilldown
|
||||
let (remote_config, remote_storage) = prepare_for_drilldown();
|
||||
let remote_proof = key_changes_proof::<BlakeTwo256, u64>(
|
||||
configuration_range(&remote_config, 0), &remote_storage, 1,
|
||||
&AnchorBlockId { hash: Default::default(), number: 16 }, 16, None, &[42]).unwrap();
|
||||
configuration_range(&remote_config, 0),
|
||||
&remote_storage,
|
||||
1,
|
||||
&AnchorBlockId { hash: Default::default(), number: 16 },
|
||||
16,
|
||||
None,
|
||||
&[42],
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let (remote_config, remote_storage) = prepare_for_drilldown();
|
||||
let remote_proof_child = key_changes_proof::<BlakeTwo256, u64>(
|
||||
configuration_range(&remote_config, 0), &remote_storage, 1,
|
||||
&AnchorBlockId { hash: Default::default(), number: 16 }, 16, Some(&child_key()), &[42]).unwrap();
|
||||
configuration_range(&remote_config, 0),
|
||||
&remote_storage,
|
||||
1,
|
||||
&AnchorBlockId { hash: Default::default(), number: 16 },
|
||||
16,
|
||||
Some(&child_key()),
|
||||
&[42],
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// happens on local light node:
|
||||
|
||||
@@ -592,14 +672,28 @@ mod tests {
|
||||
let (local_config, local_storage) = prepare_for_drilldown();
|
||||
local_storage.clear_storage();
|
||||
let local_result = key_changes_proof_check::<BlakeTwo256, u64>(
|
||||
configuration_range(&local_config, 0), &local_storage, remote_proof, 1,
|
||||
&AnchorBlockId { hash: Default::default(), number: 16 }, 16, None, &[42]);
|
||||
configuration_range(&local_config, 0),
|
||||
&local_storage,
|
||||
remote_proof,
|
||||
1,
|
||||
&AnchorBlockId { hash: Default::default(), number: 16 },
|
||||
16,
|
||||
None,
|
||||
&[42],
|
||||
);
|
||||
|
||||
let (local_config, local_storage) = prepare_for_drilldown();
|
||||
local_storage.clear_storage();
|
||||
let local_result_child = key_changes_proof_check::<BlakeTwo256, u64>(
|
||||
configuration_range(&local_config, 0), &local_storage, remote_proof_child, 1,
|
||||
&AnchorBlockId { hash: Default::default(), number: 16 }, 16, Some(&child_key()), &[42]);
|
||||
configuration_range(&local_config, 0),
|
||||
&local_storage,
|
||||
remote_proof_child,
|
||||
1,
|
||||
&AnchorBlockId { hash: Default::default(), number: 16 },
|
||||
16,
|
||||
Some(&child_key()),
|
||||
&[42],
|
||||
);
|
||||
|
||||
// check that drilldown result is the same as if it was happening at the full node
|
||||
assert_eq!(local_result, Ok(vec![(8, 2), (8, 1), (6, 3), (3, 0)]));
|
||||
@@ -620,12 +714,22 @@ mod tests {
|
||||
// regular blocks: 89, 90, 91
|
||||
let mut input = (1u64..92u64).map(|b| (b, vec![])).collect::<Vec<_>>();
|
||||
// changed at block#63 and covered by L3 digest at block#64
|
||||
input[63 - 1].1.push(InputPair::ExtrinsicIndex(ExtrinsicIndex { block: 63, key: vec![42] }, vec![0]));
|
||||
input[64 - 1].1.push(InputPair::DigestIndex(DigestIndex { block: 64, key: vec![42] }, vec![63]));
|
||||
input[63 - 1]
|
||||
.1
|
||||
.push(InputPair::ExtrinsicIndex(ExtrinsicIndex { block: 63, key: vec![42] }, vec![0]));
|
||||
input[64 - 1]
|
||||
.1
|
||||
.push(InputPair::DigestIndex(DigestIndex { block: 64, key: vec![42] }, vec![63]));
|
||||
// changed at block#79 and covered by L2 digest at block#80 + skewed digest at block#91
|
||||
input[79 - 1].1.push(InputPair::ExtrinsicIndex(ExtrinsicIndex { block: 79, key: vec![42] }, vec![1]));
|
||||
input[80 - 1].1.push(InputPair::DigestIndex(DigestIndex { block: 80, key: vec![42] }, vec![79]));
|
||||
input[91 - 1].1.push(InputPair::DigestIndex(DigestIndex { block: 91, key: vec![42] }, vec![80]));
|
||||
input[79 - 1]
|
||||
.1
|
||||
.push(InputPair::ExtrinsicIndex(ExtrinsicIndex { block: 79, key: vec![42] }, vec![1]));
|
||||
input[80 - 1]
|
||||
.1
|
||||
.push(InputPair::DigestIndex(DigestIndex { block: 80, key: vec![42] }, vec![79]));
|
||||
input[91 - 1]
|
||||
.1
|
||||
.push(InputPair::DigestIndex(DigestIndex { block: 91, key: vec![42] }, vec![80]));
|
||||
let storage = InMemoryStorage::with_inputs(input, vec![]);
|
||||
|
||||
let drilldown_result = key_changes::<BlakeTwo256, u64>(
|
||||
@@ -636,7 +740,8 @@ mod tests {
|
||||
100_000u64,
|
||||
None,
|
||||
&[42],
|
||||
).and_then(Result::from_iter);
|
||||
)
|
||||
.and_then(Result::from_iter);
|
||||
assert_eq!(drilldown_result, Ok(vec![(79, 1), (63, 0)]));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,11 +17,8 @@
|
||||
|
||||
//! Different types of changes trie input pairs.
|
||||
|
||||
use codec::{Decode, Encode, Input, Output, Error};
|
||||
use crate::{
|
||||
StorageKey, StorageValue,
|
||||
changes_trie::BlockNumber
|
||||
};
|
||||
use crate::{changes_trie::BlockNumber, StorageKey, StorageValue};
|
||||
use codec::{Decode, Encode, Error, Input, Output};
|
||||
use sp_core::storage::PrefixedStorageKey;
|
||||
|
||||
/// Key of { changed key => set of extrinsic indices } mapping.
|
||||
@@ -140,7 +137,6 @@ impl<Number: BlockNumber> DigestIndex<Number> {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl<Number: BlockNumber> Encode for DigestIndex<Number> {
|
||||
fn encode_to<W: Output + ?Sized>(&self, dest: &mut W) {
|
||||
dest.push_byte(2);
|
||||
|
||||
@@ -58,63 +58,86 @@ mod prune;
|
||||
mod storage;
|
||||
mod surface_iterator;
|
||||
|
||||
pub use self::build_cache::{BuildCache, CachedBuildData, CacheAction};
|
||||
pub use self::storage::InMemoryStorage;
|
||||
pub use self::changes_iterator::{
|
||||
key_changes, key_changes_proof,
|
||||
key_changes_proof_check, key_changes_proof_check_with_db,
|
||||
pub use self::{
|
||||
build_cache::{BuildCache, CacheAction, CachedBuildData},
|
||||
changes_iterator::{
|
||||
key_changes, key_changes_proof, key_changes_proof_check, key_changes_proof_check_with_db,
|
||||
},
|
||||
prune::prune,
|
||||
storage::InMemoryStorage,
|
||||
};
|
||||
pub use self::prune::prune;
|
||||
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::convert::TryInto;
|
||||
use hash_db::{Hasher, Prefix};
|
||||
use num_traits::{One, Zero};
|
||||
use codec::{Decode, Encode};
|
||||
use sp_core;
|
||||
use sp_core::storage::PrefixedStorageKey;
|
||||
use sp_trie::{MemoryDB, DBValue, TrieMut};
|
||||
use sp_trie::trie_types::TrieDBMut;
|
||||
use crate::{
|
||||
StorageKey,
|
||||
backend::Backend,
|
||||
overlayed_changes::OverlayedChanges,
|
||||
changes_trie::{
|
||||
build::prepare_input,
|
||||
build_cache::{IncompleteCachedBuildData, IncompleteCacheAction},
|
||||
build_cache::{IncompleteCacheAction, IncompleteCachedBuildData},
|
||||
},
|
||||
overlayed_changes::OverlayedChanges,
|
||||
StorageKey,
|
||||
};
|
||||
use codec::{Decode, Encode};
|
||||
use hash_db::{Hasher, Prefix};
|
||||
use num_traits::{One, Zero};
|
||||
use sp_core::{self, storage::PrefixedStorageKey};
|
||||
use sp_trie::{trie_types::TrieDBMut, DBValue, MemoryDB, TrieMut};
|
||||
use std::{
|
||||
collections::{HashMap, HashSet},
|
||||
convert::TryInto,
|
||||
};
|
||||
|
||||
/// Requirements for block number that can be used with changes tries.
|
||||
pub trait BlockNumber:
|
||||
Send + Sync + 'static +
|
||||
std::fmt::Display +
|
||||
Clone +
|
||||
From<u32> + TryInto<u32> + One + Zero +
|
||||
PartialEq + Ord +
|
||||
std::hash::Hash +
|
||||
std::ops::Add<Self, Output=Self> + ::std::ops::Sub<Self, Output=Self> +
|
||||
std::ops::Mul<Self, Output=Self> + ::std::ops::Div<Self, Output=Self> +
|
||||
std::ops::Rem<Self, Output=Self> +
|
||||
std::ops::AddAssign<Self> +
|
||||
num_traits::CheckedMul + num_traits::CheckedSub +
|
||||
Decode + Encode
|
||||
{}
|
||||
Send
|
||||
+ Sync
|
||||
+ 'static
|
||||
+ std::fmt::Display
|
||||
+ Clone
|
||||
+ From<u32>
|
||||
+ TryInto<u32>
|
||||
+ One
|
||||
+ Zero
|
||||
+ PartialEq
|
||||
+ Ord
|
||||
+ std::hash::Hash
|
||||
+ std::ops::Add<Self, Output = Self>
|
||||
+ ::std::ops::Sub<Self, Output = Self>
|
||||
+ std::ops::Mul<Self, Output = Self>
|
||||
+ ::std::ops::Div<Self, Output = Self>
|
||||
+ std::ops::Rem<Self, Output = Self>
|
||||
+ std::ops::AddAssign<Self>
|
||||
+ num_traits::CheckedMul
|
||||
+ num_traits::CheckedSub
|
||||
+ Decode
|
||||
+ Encode
|
||||
{
|
||||
}
|
||||
|
||||
impl<T> BlockNumber for T where T:
|
||||
Send + Sync + 'static +
|
||||
std::fmt::Display +
|
||||
Clone +
|
||||
From<u32> + TryInto<u32> + One + Zero +
|
||||
PartialEq + Ord +
|
||||
std::hash::Hash +
|
||||
std::ops::Add<Self, Output=Self> + ::std::ops::Sub<Self, Output=Self> +
|
||||
std::ops::Mul<Self, Output=Self> + ::std::ops::Div<Self, Output=Self> +
|
||||
std::ops::Rem<Self, Output=Self> +
|
||||
std::ops::AddAssign<Self> +
|
||||
num_traits::CheckedMul + num_traits::CheckedSub +
|
||||
Decode + Encode,
|
||||
{}
|
||||
impl<T> BlockNumber for T where
|
||||
T: Send
|
||||
+ Sync
|
||||
+ 'static
|
||||
+ std::fmt::Display
|
||||
+ Clone
|
||||
+ From<u32>
|
||||
+ TryInto<u32>
|
||||
+ One
|
||||
+ Zero
|
||||
+ PartialEq
|
||||
+ Ord
|
||||
+ std::hash::Hash
|
||||
+ std::ops::Add<Self, Output = Self>
|
||||
+ ::std::ops::Sub<Self, Output = Self>
|
||||
+ std::ops::Mul<Self, Output = Self>
|
||||
+ ::std::ops::Div<Self, Output = Self>
|
||||
+ std::ops::Rem<Self, Output = Self>
|
||||
+ std::ops::AddAssign<Self>
|
||||
+ num_traits::CheckedMul
|
||||
+ num_traits::CheckedSub
|
||||
+ Decode
|
||||
+ Encode
|
||||
{
|
||||
}
|
||||
|
||||
/// Block identifier that could be used to determine fork of this block.
|
||||
#[derive(Debug)]
|
||||
@@ -143,7 +166,11 @@ pub trait RootsStorage<H: Hasher, Number: BlockNumber>: Send + Sync {
|
||||
fn build_anchor(&self, hash: H::Out) -> Result<AnchorBlockId<H::Out, Number>, String>;
|
||||
/// Get changes trie root for the block with given number which is an ancestor (or the block
|
||||
/// itself) of the anchor_block (i.e. anchor_block.number >= block).
|
||||
fn root(&self, anchor: &AnchorBlockId<H::Out, Number>, block: Number) -> Result<Option<H::Out>, String>;
|
||||
fn root(
|
||||
&self,
|
||||
anchor: &AnchorBlockId<H::Out, Number>,
|
||||
block: Number,
|
||||
) -> Result<Option<H::Out>, String>;
|
||||
}
|
||||
|
||||
/// Changes trie storage. Provides access to trie roots and trie nodes.
|
||||
@@ -162,9 +189,13 @@ pub trait Storage<H: Hasher, Number: BlockNumber>: RootsStorage<H, Number> {
|
||||
}
|
||||
|
||||
/// Changes trie storage -> trie backend essence adapter.
|
||||
pub struct TrieBackendStorageAdapter<'a, H: Hasher, Number: BlockNumber>(pub &'a dyn Storage<H, Number>);
|
||||
pub struct TrieBackendStorageAdapter<'a, H: Hasher, Number: BlockNumber>(
|
||||
pub &'a dyn Storage<H, Number>,
|
||||
);
|
||||
|
||||
impl<'a, H: Hasher, N: BlockNumber> crate::TrieBackendStorage<H> for TrieBackendStorageAdapter<'a, H, N> {
|
||||
impl<'a, H: Hasher, N: BlockNumber> crate::TrieBackendStorage<H>
|
||||
for TrieBackendStorageAdapter<'a, H, N>
|
||||
{
|
||||
type Overlay = sp_trie::MemoryDB<H>;
|
||||
|
||||
fn get(&self, key: &H::Out, prefix: Prefix) -> Result<Option<DBValue>, String> {
|
||||
@@ -188,26 +219,14 @@ pub struct ConfigurationRange<'a, N> {
|
||||
|
||||
impl<'a, H, Number> State<'a, H, Number> {
|
||||
/// Create state with given config and storage.
|
||||
pub fn new(
|
||||
config: Configuration,
|
||||
zero: Number,
|
||||
storage: &'a dyn Storage<H, Number>,
|
||||
) -> Self {
|
||||
Self {
|
||||
config,
|
||||
zero,
|
||||
storage,
|
||||
}
|
||||
pub fn new(config: Configuration, zero: Number, storage: &'a dyn Storage<H, Number>) -> Self {
|
||||
Self { config, zero, storage }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, H, Number: Clone> Clone for State<'a, H, Number> {
|
||||
fn clone(&self) -> Self {
|
||||
State {
|
||||
config: self.config.clone(),
|
||||
zero: self.zero.clone(),
|
||||
storage: self.storage,
|
||||
}
|
||||
State { config: self.config.clone(), zero: self.zero.clone(), storage: self.storage }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -227,20 +246,24 @@ pub fn build_changes_trie<'a, B: Backend<H>, H: Hasher, Number: BlockNumber>(
|
||||
parent_hash: H::Out,
|
||||
panic_on_storage_error: bool,
|
||||
) -> Result<Option<(MemoryDB<H>, H::Out, CacheAction<H::Out, Number>)>, ()>
|
||||
where
|
||||
H::Out: Ord + 'static + Encode,
|
||||
where
|
||||
H::Out: Ord + 'static + Encode,
|
||||
{
|
||||
/// Panics when `res.is_err() && panic`, otherwise it returns `Err(())` on an error.
|
||||
fn maybe_panic<R, E: std::fmt::Debug>(
|
||||
res: std::result::Result<R, E>,
|
||||
panic: bool,
|
||||
) -> std::result::Result<R, ()> {
|
||||
res.map(Ok)
|
||||
.unwrap_or_else(|e| if panic {
|
||||
panic!("changes trie: storage access is not allowed to fail within runtime: {:?}", e)
|
||||
res.map(Ok).unwrap_or_else(|e| {
|
||||
if panic {
|
||||
panic!(
|
||||
"changes trie: storage access is not allowed to fail within runtime: {:?}",
|
||||
e
|
||||
)
|
||||
} else {
|
||||
Err(())
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// when storage isn't provided, changes tries aren't created
|
||||
@@ -255,11 +278,12 @@ pub fn build_changes_trie<'a, B: Backend<H>, H: Hasher, Number: BlockNumber>(
|
||||
|
||||
// prepare configuration range - we already know zero block. Current block may be the end block if configuration
|
||||
// has been changed in this block
|
||||
let is_config_changed = match changes.storage(sp_core::storage::well_known_keys::CHANGES_TRIE_CONFIG) {
|
||||
Some(Some(new_config)) => new_config != &state.config.encode()[..],
|
||||
Some(None) => true,
|
||||
None => false,
|
||||
};
|
||||
let is_config_changed =
|
||||
match changes.storage(sp_core::storage::well_known_keys::CHANGES_TRIE_CONFIG) {
|
||||
Some(Some(new_config)) => new_config != &state.config.encode()[..],
|
||||
Some(None) => true,
|
||||
None => false,
|
||||
};
|
||||
let config_range = ConfigurationRange {
|
||||
config: &state.config,
|
||||
zero: state.zero.clone(),
|
||||
@@ -303,10 +327,8 @@ pub fn build_changes_trie<'a, B: Backend<H>, H: Hasher, Number: BlockNumber>(
|
||||
maybe_panic(trie.insert(&key, &value), panic_on_storage_error)?;
|
||||
}
|
||||
|
||||
cache_action = cache_action.insert(
|
||||
Some(child_index.storage_key.clone()),
|
||||
storage_changed_keys,
|
||||
);
|
||||
cache_action =
|
||||
cache_action.insert(Some(child_index.storage_key.clone()), storage_changed_keys);
|
||||
}
|
||||
if not_empty {
|
||||
child_roots.push(input::InputPair::ChildIndex(child_index, root.as_ref().to_vec()));
|
||||
@@ -331,10 +353,7 @@ pub fn build_changes_trie<'a, B: Backend<H>, H: Hasher, Number: BlockNumber>(
|
||||
maybe_panic(trie.insert(&key, &value), panic_on_storage_error)?;
|
||||
}
|
||||
|
||||
cache_action = cache_action.insert(
|
||||
None,
|
||||
storage_changed_keys,
|
||||
);
|
||||
cache_action = cache_action.insert(None, storage_changed_keys);
|
||||
}
|
||||
|
||||
let cache_action = cache_action.complete(block, &root);
|
||||
@@ -350,20 +369,21 @@ fn prepare_cached_build_data<Number: BlockNumber>(
|
||||
// because it'll never be used again for building other tries
|
||||
// => let's clear the cache
|
||||
if !config.config.is_digest_build_enabled() {
|
||||
return IncompleteCacheAction::Clear;
|
||||
return IncompleteCacheAction::Clear
|
||||
}
|
||||
|
||||
// when this is the last block where current configuration is active
|
||||
// => let's clear the cache
|
||||
if config.end.as_ref() == Some(&block) {
|
||||
return IncompleteCacheAction::Clear;
|
||||
return IncompleteCacheAction::Clear
|
||||
}
|
||||
|
||||
// we do not need to cache anything when top-level digest trie is created, because
|
||||
// it'll never be used again for building other tries
|
||||
// => let's clear the cache
|
||||
match config.config.digest_level_at_block(config.zero.clone(), block) {
|
||||
Some((digest_level, _, _)) if digest_level == config.config.digest_levels => IncompleteCacheAction::Clear,
|
||||
Some((digest_level, _, _)) if digest_level == config.config.digest_levels =>
|
||||
IncompleteCacheAction::Clear,
|
||||
_ => IncompleteCacheAction::CacheBuildData(IncompleteCachedBuildData::new()),
|
||||
}
|
||||
}
|
||||
@@ -399,6 +419,9 @@ mod tests {
|
||||
fn cache_is_cleared_when_end_block_of_configuration_is_built() {
|
||||
let config = Configuration { digest_interval: 8, digest_levels: 2 };
|
||||
let config_range = ConfigurationRange { zero: 0, end: Some(4u32), config: &config };
|
||||
assert_eq!(prepare_cached_build_data(config_range.clone(), 4u32), IncompleteCacheAction::Clear);
|
||||
assert_eq!(
|
||||
prepare_cached_build_data(config_range.clone(), 4u32),
|
||||
IncompleteCacheAction::Clear
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,16 +17,20 @@
|
||||
|
||||
//! Changes trie pruning-related functions.
|
||||
|
||||
use crate::{
|
||||
changes_trie::{
|
||||
input::{ChildIndex, InputKey},
|
||||
storage::TrieBackendAdapter,
|
||||
AnchorBlockId, BlockNumber, Storage,
|
||||
},
|
||||
proving_backend::ProvingBackendRecorder,
|
||||
trie_backend_essence::TrieBackendEssence,
|
||||
};
|
||||
use codec::{Codec, Decode};
|
||||
use hash_db::Hasher;
|
||||
use sp_trie::Recorder;
|
||||
use log::warn;
|
||||
use num_traits::One;
|
||||
use crate::proving_backend::ProvingBackendRecorder;
|
||||
use crate::trie_backend_essence::TrieBackendEssence;
|
||||
use crate::changes_trie::{AnchorBlockId, Storage, BlockNumber};
|
||||
use crate::changes_trie::storage::TrieBackendAdapter;
|
||||
use crate::changes_trie::input::{ChildIndex, InputKey};
|
||||
use codec::{Decode, Codec};
|
||||
use sp_trie::Recorder;
|
||||
|
||||
/// Prune obsolete changes tries. Pruning happens at the same block, where highest
|
||||
/// level digest is created. Pruning guarantees to save changes tries for last
|
||||
@@ -38,12 +42,14 @@ pub fn prune<H: Hasher, Number: BlockNumber, F: FnMut(H::Out)>(
|
||||
last: Number,
|
||||
current_block: &AnchorBlockId<H::Out, Number>,
|
||||
mut remove_trie_node: F,
|
||||
) where H::Out: Codec {
|
||||
) where
|
||||
H::Out: Codec,
|
||||
{
|
||||
// delete changes trie for every block in range
|
||||
let mut block = first;
|
||||
loop {
|
||||
if block >= last.clone() + One::one() {
|
||||
break;
|
||||
break
|
||||
}
|
||||
|
||||
let prev_block = block.clone();
|
||||
@@ -56,7 +62,7 @@ pub fn prune<H: Hasher, Number: BlockNumber, F: FnMut(H::Out)>(
|
||||
Err(error) => {
|
||||
// try to delete other tries
|
||||
warn!(target: "trie", "Failed to read changes trie root from DB: {}", error);
|
||||
continue;
|
||||
continue
|
||||
},
|
||||
};
|
||||
let children_roots = {
|
||||
@@ -91,8 +97,9 @@ fn prune_trie<H: Hasher, Number: BlockNumber, F: FnMut(H::Out)>(
|
||||
storage: &dyn Storage<H, Number>,
|
||||
root: H::Out,
|
||||
remove_trie_node: &mut F,
|
||||
) where H::Out: Codec {
|
||||
|
||||
) where
|
||||
H::Out: Codec,
|
||||
{
|
||||
// enumerate all changes trie' keys, recording all nodes that have been 'touched'
|
||||
// (effectively - all changes trie nodes)
|
||||
let mut proof_recorder: Recorder<H::Out> = Default::default();
|
||||
@@ -113,14 +120,13 @@ fn prune_trie<H: Hasher, Number: BlockNumber, F: FnMut(H::Out)>(
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::collections::HashSet;
|
||||
use sp_trie::MemoryDB;
|
||||
use sp_core::H256;
|
||||
use crate::backend::insert_into_memory_db;
|
||||
use crate::changes_trie::storage::InMemoryStorage;
|
||||
use codec::Encode;
|
||||
use sp_runtime::traits::BlakeTwo256;
|
||||
use super::*;
|
||||
use crate::{backend::insert_into_memory_db, changes_trie::storage::InMemoryStorage};
|
||||
use codec::Encode;
|
||||
use sp_core::H256;
|
||||
use sp_runtime::traits::BlakeTwo256;
|
||||
use sp_trie::MemoryDB;
|
||||
use std::collections::HashSet;
|
||||
|
||||
fn prune_by_collect(
|
||||
storage: &dyn Storage<BlakeTwo256, u64>,
|
||||
@@ -130,8 +136,9 @@ mod tests {
|
||||
) -> HashSet<H256> {
|
||||
let mut pruned_trie_nodes = HashSet::new();
|
||||
let anchor = AnchorBlockId { hash: Default::default(), number: current_block };
|
||||
prune(storage, first, last, &anchor,
|
||||
|node| { pruned_trie_nodes.insert(node); });
|
||||
prune(storage, first, last, &anchor, |node| {
|
||||
pruned_trie_nodes.insert(node);
|
||||
});
|
||||
pruned_trie_nodes
|
||||
}
|
||||
|
||||
@@ -139,28 +146,36 @@ mod tests {
|
||||
fn prune_works() {
|
||||
fn prepare_storage() -> InMemoryStorage<BlakeTwo256, u64> {
|
||||
let child_info = sp_core::storage::ChildInfo::new_default(&b"1"[..]);
|
||||
let child_key = ChildIndex { block: 67u64, storage_key: child_info.prefixed_storage_key() }.encode();
|
||||
let child_key =
|
||||
ChildIndex { block: 67u64, storage_key: child_info.prefixed_storage_key() }
|
||||
.encode();
|
||||
let mut mdb1 = MemoryDB::<BlakeTwo256>::default();
|
||||
let root1 = insert_into_memory_db::<BlakeTwo256, _>(
|
||||
&mut mdb1, vec![(vec![10], vec![20])]).unwrap();
|
||||
let root1 =
|
||||
insert_into_memory_db::<BlakeTwo256, _>(&mut mdb1, vec![(vec![10], vec![20])])
|
||||
.unwrap();
|
||||
let mut mdb2 = MemoryDB::<BlakeTwo256>::default();
|
||||
let root2 = insert_into_memory_db::<BlakeTwo256, _>(
|
||||
&mut mdb2,
|
||||
vec![(vec![11], vec![21]), (vec![12], vec![22])],
|
||||
).unwrap();
|
||||
)
|
||||
.unwrap();
|
||||
let mut mdb3 = MemoryDB::<BlakeTwo256>::default();
|
||||
let ch_root3 = insert_into_memory_db::<BlakeTwo256, _>(
|
||||
&mut mdb3, vec![(vec![110], vec![120])]).unwrap();
|
||||
let root3 = insert_into_memory_db::<BlakeTwo256, _>(&mut mdb3, vec![
|
||||
(vec![13], vec![23]),
|
||||
(vec![14], vec![24]),
|
||||
(child_key, ch_root3.as_ref().encode()),
|
||||
]).unwrap();
|
||||
let ch_root3 =
|
||||
insert_into_memory_db::<BlakeTwo256, _>(&mut mdb3, vec![(vec![110], vec![120])])
|
||||
.unwrap();
|
||||
let root3 = insert_into_memory_db::<BlakeTwo256, _>(
|
||||
&mut mdb3,
|
||||
vec![
|
||||
(vec![13], vec![23]),
|
||||
(vec![14], vec![24]),
|
||||
(child_key, ch_root3.as_ref().encode()),
|
||||
],
|
||||
)
|
||||
.unwrap();
|
||||
let mut mdb4 = MemoryDB::<BlakeTwo256>::default();
|
||||
let root4 = insert_into_memory_db::<BlakeTwo256, _>(
|
||||
&mut mdb4,
|
||||
vec![(vec![15], vec![25])],
|
||||
).unwrap();
|
||||
let root4 =
|
||||
insert_into_memory_db::<BlakeTwo256, _>(&mut mdb4, vec![(vec![15], vec![25])])
|
||||
.unwrap();
|
||||
let storage = InMemoryStorage::new();
|
||||
storage.insert(65, root1, mdb1);
|
||||
storage.insert(66, root2, mdb2);
|
||||
|
||||
@@ -17,22 +17,21 @@
|
||||
|
||||
//! Changes trie storage utilities.
|
||||
|
||||
use std::collections::{BTreeMap, HashSet, HashMap};
|
||||
use hash_db::{Hasher, Prefix, EMPTY_PREFIX};
|
||||
use sp_core::storage::PrefixedStorageKey;
|
||||
use sp_trie::DBValue;
|
||||
use sp_trie::MemoryDB;
|
||||
use parking_lot::RwLock;
|
||||
use crate::{
|
||||
StorageKey,
|
||||
changes_trie::{AnchorBlockId, BlockNumber, BuildCache, RootsStorage, Storage},
|
||||
trie_backend_essence::TrieBackendStorage,
|
||||
changes_trie::{BuildCache, RootsStorage, Storage, AnchorBlockId, BlockNumber},
|
||||
StorageKey,
|
||||
};
|
||||
use hash_db::{Hasher, Prefix, EMPTY_PREFIX};
|
||||
use parking_lot::RwLock;
|
||||
use sp_core::storage::PrefixedStorageKey;
|
||||
use sp_trie::{DBValue, MemoryDB};
|
||||
use std::collections::{BTreeMap, HashMap, HashSet};
|
||||
|
||||
#[cfg(test)]
|
||||
use crate::backend::insert_into_memory_db;
|
||||
#[cfg(test)]
|
||||
use crate::changes_trie::input::{InputPair, ChildIndex};
|
||||
use crate::changes_trie::input::{ChildIndex, InputPair};
|
||||
|
||||
/// In-memory implementation of changes trie storage.
|
||||
pub struct InMemoryStorage<H: Hasher, Number: BlockNumber> {
|
||||
@@ -55,10 +54,7 @@ impl<H: Hasher, Number: BlockNumber> InMemoryStorage<H, Number> {
|
||||
/// Creates storage from given in-memory database.
|
||||
pub fn with_db(mdb: MemoryDB<H>) -> Self {
|
||||
Self {
|
||||
data: RwLock::new(InMemoryStorageData {
|
||||
roots: BTreeMap::new(),
|
||||
mdb,
|
||||
}),
|
||||
data: RwLock::new(InMemoryStorageData { roots: BTreeMap::new(), mdb }),
|
||||
cache: BuildCache::new(),
|
||||
}
|
||||
}
|
||||
@@ -72,7 +68,7 @@ impl<H: Hasher, Number: BlockNumber> InMemoryStorage<H, Number> {
|
||||
pub fn with_proof(proof: Vec<Vec<u8>>) -> Self {
|
||||
use hash_db::HashDB;
|
||||
|
||||
let mut proof_db = MemoryDB::<H>::default();
|
||||
let mut proof_db = MemoryDB::<H>::default();
|
||||
for item in proof {
|
||||
proof_db.insert(EMPTY_PREFIX, &item);
|
||||
}
|
||||
@@ -104,7 +100,8 @@ impl<H: Hasher, Number: BlockNumber> InMemoryStorage<H, Number> {
|
||||
let mut roots = BTreeMap::new();
|
||||
for (storage_key, child_input) in children_inputs {
|
||||
for (block, pairs) in child_input {
|
||||
let root = insert_into_memory_db::<H, _>(&mut mdb, pairs.into_iter().map(Into::into));
|
||||
let root =
|
||||
insert_into_memory_db::<H, _>(&mut mdb, pairs.into_iter().map(Into::into));
|
||||
|
||||
if let Some(root) = root {
|
||||
let ix = if let Some(ix) = top_inputs.iter().position(|v| v.0 == block) {
|
||||
@@ -129,17 +126,14 @@ impl<H: Hasher, Number: BlockNumber> InMemoryStorage<H, Number> {
|
||||
}
|
||||
|
||||
InMemoryStorage {
|
||||
data: RwLock::new(InMemoryStorageData {
|
||||
roots,
|
||||
mdb,
|
||||
}),
|
||||
data: RwLock::new(InMemoryStorageData { roots, mdb }),
|
||||
cache: BuildCache::new(),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn clear_storage(&self) {
|
||||
self.data.write().mdb = MemoryDB::default(); // use new to be more correct
|
||||
self.data.write().mdb = MemoryDB::default(); // use new to be more correct
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -165,13 +159,20 @@ impl<H: Hasher, Number: BlockNumber> InMemoryStorage<H, Number> {
|
||||
|
||||
impl<H: Hasher, Number: BlockNumber> RootsStorage<H, Number> for InMemoryStorage<H, Number> {
|
||||
fn build_anchor(&self, parent_hash: H::Out) -> Result<AnchorBlockId<H::Out, Number>, String> {
|
||||
self.data.read().roots.iter()
|
||||
self.data
|
||||
.read()
|
||||
.roots
|
||||
.iter()
|
||||
.find(|(_, v)| **v == parent_hash)
|
||||
.map(|(k, _)| AnchorBlockId { hash: parent_hash, number: k.clone() })
|
||||
.ok_or_else(|| format!("Can't find associated number for block {:?}", parent_hash))
|
||||
}
|
||||
|
||||
fn root(&self, _anchor_block: &AnchorBlockId<H::Out, Number>, block: Number) -> Result<Option<H::Out>, String> {
|
||||
fn root(
|
||||
&self,
|
||||
_anchor_block: &AnchorBlockId<H::Out, Number>,
|
||||
block: Number,
|
||||
) -> Result<Option<H::Out>, String> {
|
||||
Ok(self.data.read().roots.get(&block).cloned())
|
||||
}
|
||||
}
|
||||
@@ -201,9 +202,9 @@ impl<'a, H: Hasher, Number: BlockNumber> TrieBackendAdapter<'a, H, Number> {
|
||||
}
|
||||
|
||||
impl<'a, H, Number> TrieBackendStorage<H> for TrieBackendAdapter<'a, H, Number>
|
||||
where
|
||||
Number: BlockNumber,
|
||||
H: Hasher,
|
||||
where
|
||||
Number: BlockNumber,
|
||||
H: Hasher,
|
||||
{
|
||||
type Overlay = MemoryDB<H>;
|
||||
|
||||
|
||||
@@ -21,8 +21,8 @@
|
||||
//! of points at the terrain (mountains and valleys) inside this range that have to be drilled down to
|
||||
//! search for gems.
|
||||
|
||||
use crate::changes_trie::{BlockNumber, ConfigurationRange};
|
||||
use num_traits::One;
|
||||
use crate::changes_trie::{ConfigurationRange, BlockNumber};
|
||||
|
||||
/// Returns surface iterator for given range of blocks.
|
||||
///
|
||||
@@ -34,12 +34,8 @@ pub fn surface_iterator<'a, Number: BlockNumber>(
|
||||
begin: Number,
|
||||
end: Number,
|
||||
) -> Result<SurfaceIterator<'a, Number>, String> {
|
||||
let (current, current_begin, digest_step, digest_level) = lower_bound_max_digest(
|
||||
config.clone(),
|
||||
max.clone(),
|
||||
begin.clone(),
|
||||
end,
|
||||
)?;
|
||||
let (current, current_begin, digest_step, digest_level) =
|
||||
lower_bound_max_digest(config.clone(), max.clone(), begin.clone(), end)?;
|
||||
Ok(SurfaceIterator {
|
||||
config,
|
||||
begin,
|
||||
@@ -89,7 +85,8 @@ impl<'a, Number: BlockNumber> Iterator for SurfaceIterator<'a, Number> {
|
||||
self.begin.clone(),
|
||||
next,
|
||||
);
|
||||
let (current, current_begin, digest_step, digest_level) = match max_digest_interval {
|
||||
let (current, current_begin, digest_step, digest_level) = match max_digest_interval
|
||||
{
|
||||
Err(err) => return Some(Err(err)),
|
||||
Ok(range) => range,
|
||||
};
|
||||
@@ -114,14 +111,21 @@ fn lower_bound_max_digest<'a, Number: BlockNumber>(
|
||||
end: Number,
|
||||
) -> Result<(Number, Number, u32, Option<u32>), String> {
|
||||
if end > max || begin > end {
|
||||
return Err(format!("invalid changes range: {}..{}/{}", begin, end, max));
|
||||
return Err(format!("invalid changes range: {}..{}/{}", begin, end, max))
|
||||
}
|
||||
if begin <= config.zero || config.end.as_ref().map(|config_end| end > *config_end).unwrap_or(false) {
|
||||
return Err(format!("changes trie range is not covered by configuration: {}..{}/{}..{}",
|
||||
begin, end, config.zero, match config.end.as_ref() {
|
||||
if begin <= config.zero ||
|
||||
config.end.as_ref().map(|config_end| end > *config_end).unwrap_or(false)
|
||||
{
|
||||
return Err(format!(
|
||||
"changes trie range is not covered by configuration: {}..{}/{}..{}",
|
||||
begin,
|
||||
end,
|
||||
config.zero,
|
||||
match config.end.as_ref() {
|
||||
Some(config_end) => format!("{}", config_end),
|
||||
None => "None".into(),
|
||||
}));
|
||||
}
|
||||
))
|
||||
}
|
||||
|
||||
let mut digest_level = 0u32;
|
||||
@@ -135,10 +139,16 @@ fn lower_bound_max_digest<'a, Number: BlockNumber>(
|
||||
let new_digest_level = digest_level + 1;
|
||||
let new_digest_step = digest_step * config.config.digest_interval;
|
||||
let new_digest_interval = config.config.digest_interval * {
|
||||
if digest_interval == 0 { 1 } else { digest_interval }
|
||||
if digest_interval == 0 {
|
||||
1
|
||||
} else {
|
||||
digest_interval
|
||||
}
|
||||
};
|
||||
let new_digest_begin = config.zero.clone() + ((current.clone() - One::one() - config.zero.clone())
|
||||
/ new_digest_interval.into()) * new_digest_interval.into();
|
||||
let new_digest_begin = config.zero.clone() +
|
||||
((current.clone() - One::one() - config.zero.clone()) /
|
||||
new_digest_interval.into()) *
|
||||
new_digest_interval.into();
|
||||
let new_digest_end = new_digest_begin.clone() + new_digest_interval.into();
|
||||
let new_current = new_digest_begin.clone() + new_digest_interval.into();
|
||||
|
||||
@@ -150,16 +160,20 @@ fn lower_bound_max_digest<'a, Number: BlockNumber>(
|
||||
skewed_digest_end.clone(),
|
||||
);
|
||||
if let Some(skewed_digest_start) = skewed_digest_start {
|
||||
let skewed_digest_range = (skewed_digest_end.clone() - skewed_digest_start.clone())
|
||||
.try_into().ok()
|
||||
.expect("skewed digest range is always <= max level digest range;\
|
||||
max level digest range always fits u32; qed");
|
||||
let skewed_digest_range = (skewed_digest_end.clone() -
|
||||
skewed_digest_start.clone())
|
||||
.try_into()
|
||||
.ok()
|
||||
.expect(
|
||||
"skewed digest range is always <= max level digest range;\
|
||||
max level digest range always fits u32; qed",
|
||||
);
|
||||
return Ok((
|
||||
skewed_digest_end.clone(),
|
||||
skewed_digest_start,
|
||||
skewed_digest_range,
|
||||
None,
|
||||
));
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -169,7 +183,7 @@ fn lower_bound_max_digest<'a, Number: BlockNumber>(
|
||||
if begin < new_digest_begin {
|
||||
current_begin = new_digest_begin;
|
||||
}
|
||||
break;
|
||||
break
|
||||
}
|
||||
|
||||
// we can (and will) use this digest
|
||||
@@ -181,30 +195,24 @@ fn lower_bound_max_digest<'a, Number: BlockNumber>(
|
||||
|
||||
// if current digest covers the whole range => no need to use next level digest
|
||||
if current_begin <= begin && new_digest_end >= end {
|
||||
break;
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok((
|
||||
current,
|
||||
current_begin,
|
||||
digest_step,
|
||||
Some(digest_level),
|
||||
))
|
||||
Ok((current, current_begin, digest_step, Some(digest_level)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::changes_trie::{Configuration};
|
||||
use super::*;
|
||||
use crate::changes_trie::Configuration;
|
||||
|
||||
fn configuration_range<'a>(config: &'a Configuration, zero: u64) -> ConfigurationRange<'a, u64> {
|
||||
ConfigurationRange {
|
||||
config,
|
||||
zero,
|
||||
end: None,
|
||||
}
|
||||
fn configuration_range<'a>(
|
||||
config: &'a Configuration,
|
||||
zero: u64,
|
||||
) -> ConfigurationRange<'a, u64> {
|
||||
ConfigurationRange { config, zero, end: None }
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -213,13 +221,15 @@ mod tests {
|
||||
|
||||
// when config activates at 0
|
||||
assert_eq!(
|
||||
lower_bound_max_digest(configuration_range(&config, 0u64), 100_000u64, 20u64, 180u64).unwrap(),
|
||||
lower_bound_max_digest(configuration_range(&config, 0u64), 100_000u64, 20u64, 180u64)
|
||||
.unwrap(),
|
||||
(192, 176, 16, Some(2)),
|
||||
);
|
||||
|
||||
// when config activates at 30
|
||||
assert_eq!(
|
||||
lower_bound_max_digest(configuration_range(&config, 30u64), 100_000u64, 50u64, 210u64).unwrap(),
|
||||
lower_bound_max_digest(configuration_range(&config, 30u64), 100_000u64, 50u64, 210u64)
|
||||
.unwrap(),
|
||||
(222, 206, 16, Some(2)),
|
||||
);
|
||||
}
|
||||
@@ -230,40 +240,61 @@ mod tests {
|
||||
|
||||
// when config activates at 0
|
||||
assert_eq!(
|
||||
surface_iterator(
|
||||
configuration_range(&config, 0u64),
|
||||
100_000u64,
|
||||
40u64,
|
||||
180u64,
|
||||
).unwrap().collect::<Vec<_>>(),
|
||||
surface_iterator(configuration_range(&config, 0u64), 100_000u64, 40u64, 180u64,)
|
||||
.unwrap()
|
||||
.collect::<Vec<_>>(),
|
||||
vec![
|
||||
Ok((192, Some(2))), Ok((176, Some(2))), Ok((160, Some(2))), Ok((144, Some(2))),
|
||||
Ok((128, Some(2))), Ok((112, Some(2))), Ok((96, Some(2))), Ok((80, Some(2))),
|
||||
Ok((64, Some(2))), Ok((48, Some(2))),
|
||||
Ok((192, Some(2))),
|
||||
Ok((176, Some(2))),
|
||||
Ok((160, Some(2))),
|
||||
Ok((144, Some(2))),
|
||||
Ok((128, Some(2))),
|
||||
Ok((112, Some(2))),
|
||||
Ok((96, Some(2))),
|
||||
Ok((80, Some(2))),
|
||||
Ok((64, Some(2))),
|
||||
Ok((48, Some(2))),
|
||||
],
|
||||
);
|
||||
|
||||
// when config activates at 30
|
||||
assert_eq!(
|
||||
surface_iterator(
|
||||
configuration_range(&config, 30u64),
|
||||
100_000u64,
|
||||
40u64,
|
||||
180u64,
|
||||
).unwrap().collect::<Vec<_>>(),
|
||||
surface_iterator(configuration_range(&config, 30u64), 100_000u64, 40u64, 180u64,)
|
||||
.unwrap()
|
||||
.collect::<Vec<_>>(),
|
||||
vec![
|
||||
Ok((190, Some(2))), Ok((174, Some(2))), Ok((158, Some(2))), Ok((142, Some(2))), Ok((126, Some(2))),
|
||||
Ok((110, Some(2))), Ok((94, Some(2))), Ok((78, Some(2))), Ok((62, Some(2))), Ok((46, Some(2))),
|
||||
Ok((190, Some(2))),
|
||||
Ok((174, Some(2))),
|
||||
Ok((158, Some(2))),
|
||||
Ok((142, Some(2))),
|
||||
Ok((126, Some(2))),
|
||||
Ok((110, Some(2))),
|
||||
Ok((94, Some(2))),
|
||||
Ok((78, Some(2))),
|
||||
Ok((62, Some(2))),
|
||||
Ok((46, Some(2))),
|
||||
],
|
||||
);
|
||||
|
||||
// when config activates at 0 AND max block is before next digest
|
||||
assert_eq!(
|
||||
surface_iterator(configuration_range(&config, 0u64), 183u64, 40u64, 183u64).unwrap().collect::<Vec<_>>(),
|
||||
surface_iterator(configuration_range(&config, 0u64), 183u64, 40u64, 183u64)
|
||||
.unwrap()
|
||||
.collect::<Vec<_>>(),
|
||||
vec![
|
||||
Ok((183, Some(0))), Ok((182, Some(0))), Ok((181, Some(0))), Ok((180, Some(1))),
|
||||
Ok((176, Some(2))), Ok((160, Some(2))), Ok((144, Some(2))), Ok((128, Some(2))), Ok((112, Some(2))),
|
||||
Ok((96, Some(2))), Ok((80, Some(2))), Ok((64, Some(2))), Ok((48, Some(2))),
|
||||
Ok((183, Some(0))),
|
||||
Ok((182, Some(0))),
|
||||
Ok((181, Some(0))),
|
||||
Ok((180, Some(1))),
|
||||
Ok((176, Some(2))),
|
||||
Ok((160, Some(2))),
|
||||
Ok((144, Some(2))),
|
||||
Ok((128, Some(2))),
|
||||
Ok((112, Some(2))),
|
||||
Ok((96, Some(2))),
|
||||
Ok((80, Some(2))),
|
||||
Ok((64, Some(2))),
|
||||
Ok((48, Some(2))),
|
||||
],
|
||||
);
|
||||
}
|
||||
@@ -276,10 +307,19 @@ mod tests {
|
||||
// when config activates at 0 AND ends at 170
|
||||
config_range.end = Some(170);
|
||||
assert_eq!(
|
||||
surface_iterator(config_range, 100_000u64, 40u64, 170u64).unwrap().collect::<Vec<_>>(),
|
||||
surface_iterator(config_range, 100_000u64, 40u64, 170u64)
|
||||
.unwrap()
|
||||
.collect::<Vec<_>>(),
|
||||
vec![
|
||||
Ok((170, None)), Ok((160, Some(2))), Ok((144, Some(2))), Ok((128, Some(2))), Ok((112, Some(2))),
|
||||
Ok((96, Some(2))), Ok((80, Some(2))), Ok((64, Some(2))), Ok((48, Some(2))),
|
||||
Ok((170, None)),
|
||||
Ok((160, Some(2))),
|
||||
Ok((144, Some(2))),
|
||||
Ok((128, Some(2))),
|
||||
Ok((112, Some(2))),
|
||||
Ok((96, Some(2))),
|
||||
Ok((80, Some(2))),
|
||||
Ok((64, Some(2))),
|
||||
Ok((48, Some(2))),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
// limitations under the License.
|
||||
|
||||
/// State Machine Errors
|
||||
|
||||
use sp_std::fmt;
|
||||
|
||||
/// State Machine Error bound.
|
||||
|
||||
@@ -18,25 +18,28 @@
|
||||
//! Concrete externalities implementation.
|
||||
|
||||
use crate::{
|
||||
StorageKey, StorageValue, OverlayedChanges, IndexOperation,
|
||||
backend::Backend, overlayed_changes::OverlayedExtensions,
|
||||
};
|
||||
use hash_db::Hasher;
|
||||
use sp_core::{
|
||||
storage::{well_known_keys::is_child_storage_key, ChildInfo, TrackedStorageKey},
|
||||
hexdisplay::HexDisplay,
|
||||
};
|
||||
use sp_trie::{trie_types::Layout, empty_child_trie_root};
|
||||
use sp_externalities::{
|
||||
Externalities, Extensions, Extension, ExtensionStore,
|
||||
backend::Backend, overlayed_changes::OverlayedExtensions, IndexOperation, OverlayedChanges,
|
||||
StorageKey, StorageValue,
|
||||
};
|
||||
use codec::{Decode, Encode, EncodeAppend};
|
||||
use hash_db::Hasher;
|
||||
use sp_core::{
|
||||
hexdisplay::HexDisplay,
|
||||
storage::{well_known_keys::is_child_storage_key, ChildInfo, TrackedStorageKey},
|
||||
};
|
||||
use sp_externalities::{Extension, ExtensionStore, Extensions, Externalities};
|
||||
use sp_trie::{empty_child_trie_root, trie_types::Layout};
|
||||
|
||||
use sp_std::{fmt, any::{Any, TypeId}, vec::Vec, vec, boxed::Box, cmp::Ordering};
|
||||
use crate::{warn, trace, log_error};
|
||||
#[cfg(feature = "std")]
|
||||
use crate::changes_trie::State as ChangesTrieState;
|
||||
use crate::StorageTransactionCache;
|
||||
use crate::{log_error, trace, warn, StorageTransactionCache};
|
||||
use sp_std::{
|
||||
any::{Any, TypeId},
|
||||
boxed::Box,
|
||||
cmp::Ordering,
|
||||
fmt, vec,
|
||||
vec::Vec,
|
||||
};
|
||||
#[cfg(feature = "std")]
|
||||
use std::error;
|
||||
|
||||
@@ -46,7 +49,6 @@ const BENCHMARKING_FN: &str = "\
|
||||
For that reason client started transactions before calling into runtime are not allowed.
|
||||
Without client transactions the loop condition garantuees the success of the tx close.";
|
||||
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
fn guard() -> sp_panic_handler::AbortGuard {
|
||||
sp_panic_handler::AbortGuard::force_abort()
|
||||
@@ -91,10 +93,10 @@ impl<B: error::Error, E: error::Error> error::Error for Error<B, E> {
|
||||
|
||||
/// Wraps a read-only backend, call executor, and current overlayed changes.
|
||||
pub struct Ext<'a, H, N, B>
|
||||
where
|
||||
H: Hasher,
|
||||
B: 'a + Backend<H>,
|
||||
N: crate::changes_trie::BlockNumber,
|
||||
where
|
||||
H: Hasher,
|
||||
B: 'a + Backend<H>,
|
||||
N: crate::changes_trie::BlockNumber,
|
||||
{
|
||||
/// The overlayed changes to write to.
|
||||
overlay: &'a mut OverlayedChanges,
|
||||
@@ -114,12 +116,11 @@ pub struct Ext<'a, H, N, B>
|
||||
extensions: Option<OverlayedExtensions<'a>>,
|
||||
}
|
||||
|
||||
|
||||
impl<'a, H, N, B> Ext<'a, H, N, B>
|
||||
where
|
||||
H: Hasher,
|
||||
B: Backend<H>,
|
||||
N: crate::changes_trie::BlockNumber,
|
||||
where
|
||||
H: Hasher,
|
||||
B: Backend<H>,
|
||||
N: crate::changes_trie::BlockNumber,
|
||||
{
|
||||
/// Create a new `Ext`.
|
||||
#[cfg(not(feature = "std"))]
|
||||
@@ -128,13 +129,7 @@ impl<'a, H, N, B> Ext<'a, H, N, B>
|
||||
storage_transaction_cache: &'a mut StorageTransactionCache<B::Transaction, H, N>,
|
||||
backend: &'a B,
|
||||
) -> Self {
|
||||
Ext {
|
||||
overlay,
|
||||
backend,
|
||||
id: 0,
|
||||
storage_transaction_cache,
|
||||
_phantom: Default::default(),
|
||||
}
|
||||
Ext { overlay, backend, id: 0, storage_transaction_cache, _phantom: Default::default() }
|
||||
}
|
||||
|
||||
/// Create a new `Ext` from overlayed changes and read-only backend
|
||||
@@ -176,7 +171,9 @@ where
|
||||
pub fn storage_pairs(&self) -> Vec<(StorageKey, StorageValue)> {
|
||||
use std::collections::HashMap;
|
||||
|
||||
self.backend.pairs().iter()
|
||||
self.backend
|
||||
.pairs()
|
||||
.iter()
|
||||
.map(|&(ref k, ref v)| (k.to_vec(), Some(v.to_vec())))
|
||||
.chain(self.overlay.changes().map(|(k, v)| (k.clone(), v.value().cloned())))
|
||||
.collect::<HashMap<_, _>>()
|
||||
@@ -199,8 +196,11 @@ where
|
||||
|
||||
fn storage(&self, key: &[u8]) -> Option<StorageValue> {
|
||||
let _guard = guard();
|
||||
let result = self.overlay.storage(key).map(|x| x.map(|x| x.to_vec())).unwrap_or_else(||
|
||||
self.backend.storage(key).expect(EXT_NOT_ALLOWED_TO_FAIL));
|
||||
let result = self
|
||||
.overlay
|
||||
.storage(key)
|
||||
.map(|x| x.map(|x| x.to_vec()))
|
||||
.unwrap_or_else(|| self.backend.storage(key).expect(EXT_NOT_ALLOWED_TO_FAIL));
|
||||
|
||||
// NOTE: be careful about touching the key names – used outside substrate!
|
||||
trace!(
|
||||
@@ -222,7 +222,8 @@ where
|
||||
|
||||
fn storage_hash(&self, key: &[u8]) -> Option<Vec<u8>> {
|
||||
let _guard = guard();
|
||||
let result = self.overlay
|
||||
let result = self
|
||||
.overlay
|
||||
.storage(key)
|
||||
.map(|x| x.map(|x| H::hash(x)))
|
||||
.unwrap_or_else(|| self.backend.storage_hash(key).expect(EXT_NOT_ALLOWED_TO_FAIL));
|
||||
@@ -235,19 +236,15 @@ where
|
||||
result.map(|r| r.encode())
|
||||
}
|
||||
|
||||
fn child_storage(
|
||||
&self,
|
||||
child_info: &ChildInfo,
|
||||
key: &[u8],
|
||||
) -> Option<StorageValue> {
|
||||
fn child_storage(&self, child_info: &ChildInfo, key: &[u8]) -> Option<StorageValue> {
|
||||
let _guard = guard();
|
||||
let result = self.overlay
|
||||
let result = self
|
||||
.overlay
|
||||
.child_storage(child_info, key)
|
||||
.map(|x| x.map(|x| x.to_vec()))
|
||||
.unwrap_or_else(||
|
||||
self.backend.child_storage(child_info, key)
|
||||
.expect(EXT_NOT_ALLOWED_TO_FAIL)
|
||||
);
|
||||
.unwrap_or_else(|| {
|
||||
self.backend.child_storage(child_info, key).expect(EXT_NOT_ALLOWED_TO_FAIL)
|
||||
});
|
||||
|
||||
trace!(target: "state", "{:04x}: GetChild({}) {}={:?}",
|
||||
self.id,
|
||||
@@ -259,19 +256,15 @@ where
|
||||
result
|
||||
}
|
||||
|
||||
fn child_storage_hash(
|
||||
&self,
|
||||
child_info: &ChildInfo,
|
||||
key: &[u8],
|
||||
) -> Option<Vec<u8>> {
|
||||
fn child_storage_hash(&self, child_info: &ChildInfo, key: &[u8]) -> Option<Vec<u8>> {
|
||||
let _guard = guard();
|
||||
let result = self.overlay
|
||||
let result = self
|
||||
.overlay
|
||||
.child_storage(child_info, key)
|
||||
.map(|x| x.map(|x| H::hash(x)))
|
||||
.unwrap_or_else(||
|
||||
self.backend.child_storage_hash(child_info, key)
|
||||
.expect(EXT_NOT_ALLOWED_TO_FAIL)
|
||||
);
|
||||
.unwrap_or_else(|| {
|
||||
self.backend.child_storage_hash(child_info, key).expect(EXT_NOT_ALLOWED_TO_FAIL)
|
||||
});
|
||||
|
||||
trace!(target: "state", "{:04x}: ChildHash({}) {}={:?}",
|
||||
self.id,
|
||||
@@ -299,16 +292,13 @@ where
|
||||
result
|
||||
}
|
||||
|
||||
fn exists_child_storage(
|
||||
&self,
|
||||
child_info: &ChildInfo,
|
||||
key: &[u8],
|
||||
) -> bool {
|
||||
fn exists_child_storage(&self, child_info: &ChildInfo, key: &[u8]) -> bool {
|
||||
let _guard = guard();
|
||||
|
||||
let result = match self.overlay.child_storage(child_info, key) {
|
||||
Some(x) => x.is_some(),
|
||||
_ => self.backend
|
||||
_ => self
|
||||
.backend
|
||||
.exists_child_storage(child_info, key)
|
||||
.expect(EXT_NOT_ALLOWED_TO_FAIL),
|
||||
};
|
||||
@@ -323,7 +313,8 @@ where
|
||||
}
|
||||
|
||||
fn next_storage_key(&self, key: &[u8]) -> Option<StorageKey> {
|
||||
let mut next_backend_key = self.backend.next_storage_key(key).expect(EXT_NOT_ALLOWED_TO_FAIL);
|
||||
let mut next_backend_key =
|
||||
self.backend.next_storage_key(key).expect(EXT_NOT_ALLOWED_TO_FAIL);
|
||||
let mut overlay_changes = self.overlay.iter_after(key).peekable();
|
||||
|
||||
match (&next_backend_key, overlay_changes.peek()) {
|
||||
@@ -343,9 +334,10 @@ where
|
||||
// If the `backend_key` and `overlay_key` are equal, it means that we need
|
||||
// to search for the next backend key, because the overlay has overwritten
|
||||
// this key.
|
||||
next_backend_key = self.backend.next_storage_key(
|
||||
&overlay_key.0,
|
||||
).expect(EXT_NOT_ALLOWED_TO_FAIL);
|
||||
next_backend_key = self
|
||||
.backend
|
||||
.next_storage_key(&overlay_key.0)
|
||||
.expect(EXT_NOT_ALLOWED_TO_FAIL);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -358,18 +350,13 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
fn next_child_storage_key(
|
||||
&self,
|
||||
child_info: &ChildInfo,
|
||||
key: &[u8],
|
||||
) -> Option<StorageKey> {
|
||||
let mut next_backend_key = self.backend
|
||||
fn next_child_storage_key(&self, child_info: &ChildInfo, key: &[u8]) -> Option<StorageKey> {
|
||||
let mut next_backend_key = self
|
||||
.backend
|
||||
.next_child_storage_key(child_info, key)
|
||||
.expect(EXT_NOT_ALLOWED_TO_FAIL);
|
||||
let mut overlay_changes = self.overlay.child_iter_after(
|
||||
child_info.storage_key(),
|
||||
key
|
||||
).peekable();
|
||||
let mut overlay_changes =
|
||||
self.overlay.child_iter_after(child_info.storage_key(), key).peekable();
|
||||
|
||||
match (&next_backend_key, overlay_changes.peek()) {
|
||||
(_, None) => next_backend_key,
|
||||
@@ -388,10 +375,10 @@ where
|
||||
// If the `backend_key` and `overlay_key` are equal, it means that we need
|
||||
// to search for the next backend key, because the overlay has overwritten
|
||||
// this key.
|
||||
next_backend_key = self.backend.next_child_storage_key(
|
||||
child_info,
|
||||
&overlay_key.0,
|
||||
).expect(EXT_NOT_ALLOWED_TO_FAIL);
|
||||
next_backend_key = self
|
||||
.backend
|
||||
.next_child_storage_key(child_info, &overlay_key.0)
|
||||
.expect(EXT_NOT_ALLOWED_TO_FAIL);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -408,7 +395,7 @@ where
|
||||
let _guard = guard();
|
||||
if is_child_storage_key(&key) {
|
||||
warn!(target: "trie", "Refuse to directly set child storage key");
|
||||
return;
|
||||
return
|
||||
}
|
||||
|
||||
// NOTE: be careful about touching the key names – used outside substrate!
|
||||
@@ -448,11 +435,7 @@ where
|
||||
self.overlay.set_child_storage(child_info, key, value);
|
||||
}
|
||||
|
||||
fn kill_child_storage(
|
||||
&mut self,
|
||||
child_info: &ChildInfo,
|
||||
limit: Option<u32>,
|
||||
) -> (bool, u32) {
|
||||
fn kill_child_storage(&mut self, child_info: &ChildInfo, limit: Option<u32>) -> (bool, u32) {
|
||||
trace!(target: "state", "{:04x}: KillChild({})",
|
||||
self.id,
|
||||
HexDisplay::from(&child_info.storage_key()),
|
||||
@@ -472,7 +455,7 @@ where
|
||||
|
||||
if sp_core::storage::well_known_keys::starts_with_child_storage_key(prefix) {
|
||||
warn!(target: "trie", "Refuse to directly clear prefix that is part or contains of child storage key");
|
||||
return (false, 0);
|
||||
return (false, 0)
|
||||
}
|
||||
|
||||
self.mark_dirty();
|
||||
@@ -498,11 +481,7 @@ where
|
||||
self.limit_remove_from_backend(Some(child_info), Some(prefix), limit)
|
||||
}
|
||||
|
||||
fn storage_append(
|
||||
&mut self,
|
||||
key: Vec<u8>,
|
||||
value: Vec<u8>,
|
||||
) {
|
||||
fn storage_append(&mut self, key: Vec<u8>, value: Vec<u8>) {
|
||||
trace!(target: "state", "{:04x}: Append {}={}",
|
||||
self.id,
|
||||
HexDisplay::from(&key),
|
||||
@@ -513,10 +492,9 @@ where
|
||||
self.mark_dirty();
|
||||
|
||||
let backend = &mut self.backend;
|
||||
let current_value = self.overlay.value_mut_or_insert_with(
|
||||
&key,
|
||||
|| backend.storage(&key).expect(EXT_NOT_ALLOWED_TO_FAIL).unwrap_or_default()
|
||||
);
|
||||
let current_value = self.overlay.value_mut_or_insert_with(&key, || {
|
||||
backend.storage(&key).expect(EXT_NOT_ALLOWED_TO_FAIL).unwrap_or_default()
|
||||
});
|
||||
StorageAppend::new(current_value).append(value);
|
||||
}
|
||||
|
||||
@@ -527,7 +505,7 @@ where
|
||||
self.id,
|
||||
HexDisplay::from(&root.as_ref()),
|
||||
);
|
||||
return root.encode();
|
||||
return root.encode()
|
||||
}
|
||||
|
||||
let root = self.overlay.storage_root(self.backend, self.storage_transaction_cache);
|
||||
@@ -535,10 +513,7 @@ where
|
||||
root.encode()
|
||||
}
|
||||
|
||||
fn child_storage_root(
|
||||
&mut self,
|
||||
child_info: &ChildInfo,
|
||||
) -> Vec<u8> {
|
||||
fn child_storage_root(&mut self, child_info: &ChildInfo) -> Vec<u8> {
|
||||
let _guard = guard();
|
||||
let storage_key = child_info.storage_key();
|
||||
let prefixed_storage_key = child_info.prefixed_storage_key();
|
||||
@@ -546,9 +521,7 @@ where
|
||||
let root = self
|
||||
.storage(prefixed_storage_key.as_slice())
|
||||
.and_then(|k| Decode::decode(&mut &k[..]).ok())
|
||||
.unwrap_or_else(
|
||||
|| empty_child_trie_root::<Layout<H>>()
|
||||
);
|
||||
.unwrap_or_else(|| empty_child_trie_root::<Layout<H>>());
|
||||
trace!(target: "state", "{:04x}: ChildRoot({})(cached) {}",
|
||||
self.id,
|
||||
HexDisplay::from(&storage_key),
|
||||
@@ -587,9 +560,7 @@ where
|
||||
let root = self
|
||||
.storage(prefixed_storage_key.as_slice())
|
||||
.and_then(|k| Decode::decode(&mut &k[..]).ok())
|
||||
.unwrap_or_else(
|
||||
|| empty_child_trie_root::<Layout<H>>()
|
||||
);
|
||||
.unwrap_or_else(|| empty_child_trie_root::<Layout<H>>());
|
||||
trace!(target: "state", "{:04x}: ChildRoot({})(no_change) {}",
|
||||
self.id,
|
||||
HexDisplay::from(&storage_key.as_ref()),
|
||||
@@ -625,10 +596,8 @@ where
|
||||
index,
|
||||
HexDisplay::from(&hash),
|
||||
);
|
||||
self.overlay.add_transaction_index(IndexOperation::Renew {
|
||||
extrinsic: index,
|
||||
hash: hash.to_vec(),
|
||||
});
|
||||
self.overlay
|
||||
.add_transaction_index(IndexOperation::Renew { extrinsic: index, hash: hash.to_vec() });
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
@@ -639,7 +608,8 @@ where
|
||||
#[cfg(feature = "std")]
|
||||
fn storage_changes_root(&mut self, mut parent_hash: &[u8]) -> Result<Option<Vec<u8>>, ()> {
|
||||
let _guard = guard();
|
||||
if let Some(ref root) = self.storage_transaction_cache.changes_trie_transaction_storage_root {
|
||||
if let Some(ref root) = self.storage_transaction_cache.changes_trie_transaction_storage_root
|
||||
{
|
||||
trace!(
|
||||
target: "state",
|
||||
"{:04x}: ChangesRoot({})(cached) {:?}",
|
||||
@@ -653,13 +623,13 @@ where
|
||||
let root = self.overlay.changes_trie_root(
|
||||
self.backend,
|
||||
self.changes_trie_state.as_ref(),
|
||||
Decode::decode(&mut parent_hash).map_err(|e|
|
||||
Decode::decode(&mut parent_hash).map_err(|e| {
|
||||
trace!(
|
||||
target: "state",
|
||||
"Failed to decode changes root parent hash: {}",
|
||||
e,
|
||||
)
|
||||
)?,
|
||||
})?,
|
||||
true,
|
||||
self.storage_transaction_cache,
|
||||
);
|
||||
@@ -693,13 +663,15 @@ where
|
||||
for _ in 0..self.overlay.transaction_depth() {
|
||||
self.overlay.rollback_transaction().expect(BENCHMARKING_FN);
|
||||
}
|
||||
self.overlay.drain_storage_changes(
|
||||
self.backend,
|
||||
#[cfg(feature = "std")]
|
||||
None,
|
||||
Default::default(),
|
||||
self.storage_transaction_cache,
|
||||
).expect(EXT_NOT_ALLOWED_TO_FAIL);
|
||||
self.overlay
|
||||
.drain_storage_changes(
|
||||
self.backend,
|
||||
#[cfg(feature = "std")]
|
||||
None,
|
||||
Default::default(),
|
||||
self.storage_transaction_cache,
|
||||
)
|
||||
.expect(EXT_NOT_ALLOWED_TO_FAIL);
|
||||
self.backend.wipe().expect(EXT_NOT_ALLOWED_TO_FAIL);
|
||||
self.mark_dirty();
|
||||
self.overlay
|
||||
@@ -711,19 +683,24 @@ where
|
||||
for _ in 0..self.overlay.transaction_depth() {
|
||||
self.overlay.commit_transaction().expect(BENCHMARKING_FN);
|
||||
}
|
||||
let changes = self.overlay.drain_storage_changes(
|
||||
self.backend,
|
||||
#[cfg(feature = "std")]
|
||||
None,
|
||||
Default::default(),
|
||||
self.storage_transaction_cache,
|
||||
).expect(EXT_NOT_ALLOWED_TO_FAIL);
|
||||
self.backend.commit(
|
||||
changes.transaction_storage_root,
|
||||
changes.transaction,
|
||||
changes.main_storage_changes,
|
||||
changes.child_storage_changes,
|
||||
).expect(EXT_NOT_ALLOWED_TO_FAIL);
|
||||
let changes = self
|
||||
.overlay
|
||||
.drain_storage_changes(
|
||||
self.backend,
|
||||
#[cfg(feature = "std")]
|
||||
None,
|
||||
Default::default(),
|
||||
self.storage_transaction_cache,
|
||||
)
|
||||
.expect(EXT_NOT_ALLOWED_TO_FAIL);
|
||||
self.backend
|
||||
.commit(
|
||||
changes.transaction_storage_root,
|
||||
changes.transaction,
|
||||
changes.main_storage_changes,
|
||||
changes.child_storage_changes,
|
||||
)
|
||||
.expect(EXT_NOT_ALLOWED_TO_FAIL);
|
||||
self.mark_dirty();
|
||||
self.overlay
|
||||
.enter_runtime()
|
||||
@@ -775,13 +752,13 @@ where
|
||||
self.backend.apply_to_keys_while(child_info, prefix, |key| {
|
||||
if num_deleted == limit {
|
||||
all_deleted = false;
|
||||
return false;
|
||||
return false
|
||||
}
|
||||
if let Some(num) = num_deleted.checked_add(1) {
|
||||
num_deleted = num;
|
||||
} else {
|
||||
all_deleted = false;
|
||||
return false;
|
||||
return false
|
||||
}
|
||||
if let Some(child_info) = child_info {
|
||||
self.overlay.set_child_storage(child_info, key.to_vec(), None);
|
||||
@@ -840,7 +817,7 @@ impl<'a> StorageAppend<'a> {
|
||||
"Failed to append value, resetting storage item to `[value]`.",
|
||||
);
|
||||
value.encode()
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -896,7 +873,10 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
fn deregister_extension_by_type_id(&mut self, type_id: TypeId) -> Result<(), sp_externalities::Error> {
|
||||
fn deregister_extension_by_type_id(
|
||||
&mut self,
|
||||
type_id: TypeId,
|
||||
) -> Result<(), sp_externalities::Error> {
|
||||
if let Some(ref mut extensions) = self.extensions {
|
||||
if extensions.deregister(type_id) {
|
||||
Ok(())
|
||||
@@ -912,24 +892,19 @@ where
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use hex_literal::hex;
|
||||
use num_traits::Zero;
|
||||
use codec::Encode;
|
||||
use sp_core::{
|
||||
H256,
|
||||
Blake2Hasher,
|
||||
map,
|
||||
storage::{
|
||||
Storage,
|
||||
StorageChild,
|
||||
well_known_keys::EXTRINSIC_INDEX,
|
||||
},
|
||||
};
|
||||
use crate::{
|
||||
changes_trie::{
|
||||
Configuration as ChangesTrieConfiguration,
|
||||
InMemoryStorage as TestChangesTrieStorage,
|
||||
}, InMemoryBackend,
|
||||
Configuration as ChangesTrieConfiguration, InMemoryStorage as TestChangesTrieStorage,
|
||||
},
|
||||
InMemoryBackend,
|
||||
};
|
||||
use codec::Encode;
|
||||
use hex_literal::hex;
|
||||
use num_traits::Zero;
|
||||
use sp_core::{
|
||||
map,
|
||||
storage::{well_known_keys::EXTRINSIC_INDEX, Storage, StorageChild},
|
||||
Blake2Hasher, H256,
|
||||
};
|
||||
|
||||
type TestBackend = InMemoryBackend<Blake2Hasher>;
|
||||
@@ -947,10 +922,7 @@ mod tests {
|
||||
}
|
||||
|
||||
fn changes_trie_config() -> ChangesTrieConfiguration {
|
||||
ChangesTrieConfiguration {
|
||||
digest_interval: 0,
|
||||
digest_levels: 0,
|
||||
}
|
||||
ChangesTrieConfiguration { digest_interval: 0, digest_levels: 0 }
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1013,8 +985,9 @@ mod tests {
|
||||
vec![20] => vec![20],
|
||||
vec![40] => vec![40]
|
||||
],
|
||||
children_default: map![]
|
||||
}.into();
|
||||
children_default: map![],
|
||||
}
|
||||
.into();
|
||||
|
||||
let ext = TestExt::new(&mut overlay, &mut cache, &backend, None, None);
|
||||
|
||||
@@ -1056,8 +1029,9 @@ mod tests {
|
||||
top: map![
|
||||
vec![30] => vec![30]
|
||||
],
|
||||
children_default: map![]
|
||||
}.into();
|
||||
children_default: map![],
|
||||
}
|
||||
.into();
|
||||
|
||||
let ext = TestExt::new(&mut overlay, &mut cache, &backend, None, None);
|
||||
|
||||
@@ -1087,7 +1061,8 @@ mod tests {
|
||||
child_info: child_info.to_owned(),
|
||||
}
|
||||
],
|
||||
}.into();
|
||||
}
|
||||
.into();
|
||||
|
||||
let ext = TestExt::new(&mut overlay, &mut cache, &backend, None, None);
|
||||
|
||||
@@ -1131,7 +1106,8 @@ mod tests {
|
||||
child_info: child_info.to_owned(),
|
||||
}
|
||||
],
|
||||
}.into();
|
||||
}
|
||||
.into();
|
||||
|
||||
let ext = TestExt::new(&mut overlay, &mut cache, &backend, None, None);
|
||||
|
||||
@@ -1142,10 +1118,7 @@ mod tests {
|
||||
);
|
||||
|
||||
assert_eq!(ext.child_storage(child_info, &[20]), None);
|
||||
assert_eq!(
|
||||
ext.child_storage_hash(child_info, &[20]),
|
||||
None,
|
||||
);
|
||||
assert_eq!(ext.child_storage_hash(child_info, &[20]), None,);
|
||||
|
||||
assert_eq!(ext.child_storage(child_info, &[30]), Some(vec![31]));
|
||||
assert_eq!(
|
||||
@@ -1170,7 +1143,8 @@ mod tests {
|
||||
child_info: child_info.to_owned(),
|
||||
}
|
||||
],
|
||||
}.into();
|
||||
}
|
||||
.into();
|
||||
|
||||
let ext = TestExt::new(&mut overlay, &mut cache, &backend, None, None);
|
||||
|
||||
|
||||
@@ -18,13 +18,13 @@
|
||||
//! State machine in memory backend.
|
||||
|
||||
use crate::{
|
||||
StorageKey, StorageValue, StorageCollection, trie_backend::TrieBackend, backend::Backend,
|
||||
backend::Backend, trie_backend::TrieBackend, StorageCollection, StorageKey, StorageValue,
|
||||
};
|
||||
use std::collections::{BTreeMap, HashMap};
|
||||
use hash_db::Hasher;
|
||||
use sp_trie::{MemoryDB, empty_trie_root, Layout};
|
||||
use codec::Codec;
|
||||
use hash_db::Hasher;
|
||||
use sp_core::storage::{ChildInfo, Storage};
|
||||
use sp_trie::{empty_trie_root, Layout, MemoryDB};
|
||||
use std::collections::{BTreeMap, HashMap};
|
||||
|
||||
/// Create a new empty instance of in-memory backend.
|
||||
pub fn new_in_mem<H: Hasher>() -> TrieBackend<MemoryDB<H>, H>
|
||||
@@ -40,9 +40,7 @@ where
|
||||
H::Out: Codec + Ord,
|
||||
{
|
||||
/// Copy the state, with applied updates
|
||||
pub fn update<
|
||||
T: IntoIterator<Item = (Option<ChildInfo>, StorageCollection)>
|
||||
>(
|
||||
pub fn update<T: IntoIterator<Item = (Option<ChildInfo>, StorageCollection)>>(
|
||||
&self,
|
||||
changes: T,
|
||||
) -> Self {
|
||||
@@ -52,19 +50,16 @@ where
|
||||
}
|
||||
|
||||
/// Insert values into backend trie.
|
||||
pub fn insert<
|
||||
T: IntoIterator<Item = (Option<ChildInfo>, StorageCollection)>
|
||||
>(
|
||||
pub fn insert<T: IntoIterator<Item = (Option<ChildInfo>, StorageCollection)>>(
|
||||
&mut self,
|
||||
changes: T,
|
||||
) {
|
||||
let (top, child) = changes.into_iter().partition::<Vec<_>, _>(|v| v.0.is_none());
|
||||
let (root, transaction) = self.full_storage_root(
|
||||
top.iter().map(|(_, v)| v).flatten().map(|(k, v)| (&k[..], v.as_deref())),
|
||||
child.iter()
|
||||
.filter_map(|v|
|
||||
v.0.as_ref().map(|c| (c, v.1.iter().map(|(k, v)| (&k[..], v.as_deref()))))
|
||||
),
|
||||
child.iter().filter_map(|v| {
|
||||
v.0.as_ref().map(|c| (c, v.1.iter().map(|(k, v)| (&k[..], v.as_deref()))))
|
||||
}),
|
||||
);
|
||||
|
||||
self.apply_transaction(root, transaction);
|
||||
@@ -115,7 +110,9 @@ where
|
||||
fn from(inner: HashMap<Option<ChildInfo>, BTreeMap<StorageKey, StorageValue>>) -> Self {
|
||||
let mut backend = new_in_mem();
|
||||
backend.insert(
|
||||
inner.into_iter().map(|(k, m)| (k, m.into_iter().map(|(k, v)| (k, Some(v))).collect())),
|
||||
inner
|
||||
.into_iter()
|
||||
.map(|(k, m)| (k, m.into_iter().map(|(k, v)| (k, Some(v))).collect())),
|
||||
);
|
||||
backend
|
||||
}
|
||||
@@ -126,8 +123,11 @@ where
|
||||
H::Out: Codec + Ord,
|
||||
{
|
||||
fn from(inners: Storage) -> Self {
|
||||
let mut inner: HashMap<Option<ChildInfo>, BTreeMap<StorageKey, StorageValue>>
|
||||
= inners.children_default.into_iter().map(|(_k, c)| (Some(c.child_info), c.data)).collect();
|
||||
let mut inner: HashMap<Option<ChildInfo>, BTreeMap<StorageKey, StorageValue>> = inners
|
||||
.children_default
|
||||
.into_iter()
|
||||
.map(|(_k, c)| (Some(c.child_info), c.data))
|
||||
.collect();
|
||||
inner.insert(None, inners.top);
|
||||
inner.into()
|
||||
}
|
||||
@@ -144,16 +144,13 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<H: Hasher> From<Vec<(Option<ChildInfo>, StorageCollection)>>
|
||||
for TrieBackend<MemoryDB<H>, H>
|
||||
impl<H: Hasher> From<Vec<(Option<ChildInfo>, StorageCollection)>> for TrieBackend<MemoryDB<H>, H>
|
||||
where
|
||||
H::Out: Codec + Ord,
|
||||
{
|
||||
fn from(
|
||||
inner: Vec<(Option<ChildInfo>, StorageCollection)>,
|
||||
) -> Self {
|
||||
let mut expanded: HashMap<Option<ChildInfo>, BTreeMap<StorageKey, StorageValue>>
|
||||
= HashMap::new();
|
||||
fn from(inner: Vec<(Option<ChildInfo>, StorageCollection)>) -> Self {
|
||||
let mut expanded: HashMap<Option<ChildInfo>, BTreeMap<StorageKey, StorageValue>> =
|
||||
HashMap::new();
|
||||
for (child_info, key_values) in inner {
|
||||
let entry = expanded.entry(child_info).or_default();
|
||||
for (key, value) in key_values {
|
||||
@@ -169,8 +166,8 @@ where
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use sp_runtime::traits::BlakeTwo256;
|
||||
use crate::backend::Backend;
|
||||
use sp_runtime::traits::BlakeTwo256;
|
||||
|
||||
/// Assert in memory backend with only child trie keys works as trie backend.
|
||||
#[test]
|
||||
@@ -178,15 +175,10 @@ mod tests {
|
||||
let storage = new_in_mem::<BlakeTwo256>();
|
||||
let child_info = ChildInfo::new_default(b"1");
|
||||
let child_info = &child_info;
|
||||
let mut storage = storage.update(
|
||||
vec![(
|
||||
Some(child_info.clone()),
|
||||
vec![(b"2".to_vec(), Some(b"3".to_vec()))]
|
||||
)]
|
||||
);
|
||||
let mut storage = storage
|
||||
.update(vec![(Some(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(child_info, b"2").unwrap(),
|
||||
Some(b"3".to_vec()));
|
||||
assert_eq!(trie_backend.child_storage(child_info, b"2").unwrap(), Some(b"3".to_vec()));
|
||||
let storage_key = child_info.prefixed_storage_key();
|
||||
assert!(trie_backend.storage(storage_key.as_slice()).unwrap().is_some());
|
||||
}
|
||||
@@ -196,8 +188,10 @@ mod tests {
|
||||
let mut storage = new_in_mem::<BlakeTwo256>();
|
||||
let child_info = ChildInfo::new_default(b"1");
|
||||
|
||||
storage.insert(vec![(Some(child_info.clone()), vec![(b"2".to_vec(), Some(b"3".to_vec()))])]);
|
||||
storage.insert(vec![(Some(child_info.clone()), vec![(b"1".to_vec(), Some(b"3".to_vec()))])]);
|
||||
storage
|
||||
.insert(vec![(Some(child_info.clone()), vec![(b"2".to_vec(), Some(b"3".to_vec()))])]);
|
||||
storage
|
||||
.insert(vec![(Some(child_info.clone()), vec![(b"1".to_vec(), Some(b"3".to_vec()))])]);
|
||||
|
||||
assert_eq!(storage.child_storage(&child_info, &b"2"[..]), Ok(Some(b"3".to_vec())));
|
||||
assert_eq!(storage.child_storage(&child_info, &b"1"[..]), Ok(Some(b"3".to_vec())));
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -17,17 +17,19 @@
|
||||
|
||||
//! Houses the code that implements the transactional overlay storage.
|
||||
|
||||
use super::{StorageKey, StorageValue, Extrinsics};
|
||||
use super::{Extrinsics, StorageKey, StorageValue};
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
use std::collections::HashSet as Set;
|
||||
#[cfg(not(feature = "std"))]
|
||||
use sp_std::collections::btree_set::BTreeSet as Set;
|
||||
#[cfg(feature = "std")]
|
||||
use std::collections::HashSet as Set;
|
||||
|
||||
use sp_std::collections::{btree_map::BTreeMap, btree_set::BTreeSet};
|
||||
use sp_std::hash::Hash;
|
||||
use smallvec::SmallVec;
|
||||
use crate::warn;
|
||||
use smallvec::SmallVec;
|
||||
use sp_std::{
|
||||
collections::{btree_map::BTreeMap, btree_set::BTreeSet},
|
||||
hash::Hash,
|
||||
};
|
||||
|
||||
const PROOF_OVERLAY_NON_EMPTY: &str = "\
|
||||
An OverlayValue is always created with at least one transaction and dropped as soon
|
||||
@@ -82,9 +84,7 @@ pub struct OverlayedEntry<V> {
|
||||
|
||||
impl<V> Default for OverlayedEntry<V> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
transactions: SmallVec::new(),
|
||||
}
|
||||
Self { transactions: SmallVec::new() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,7 +142,9 @@ impl<V> OverlayedEntry<V> {
|
||||
/// Unique list of extrinsic indices which modified the value.
|
||||
pub fn extrinsics(&self) -> BTreeSet<u32> {
|
||||
let mut set = BTreeSet::new();
|
||||
self.transactions.iter().for_each(|t| t.extrinsics.copy_extrinsics_into(&mut set));
|
||||
self.transactions
|
||||
.iter()
|
||||
.for_each(|t| t.extrinsics.copy_extrinsics_into(&mut set));
|
||||
set
|
||||
}
|
||||
|
||||
@@ -165,17 +167,9 @@ impl<V> OverlayedEntry<V> {
|
||||
///
|
||||
/// This makes sure that the old version is not overwritten and can be properly
|
||||
/// rolled back when required.
|
||||
fn set(
|
||||
&mut self,
|
||||
value: V,
|
||||
first_write_in_tx: bool,
|
||||
at_extrinsic: Option<u32>,
|
||||
) {
|
||||
fn set(&mut self, value: V, first_write_in_tx: bool, at_extrinsic: Option<u32>) {
|
||||
if first_write_in_tx || self.transactions.is_empty() {
|
||||
self.transactions.push(InnerValue {
|
||||
value,
|
||||
extrinsics: Default::default(),
|
||||
});
|
||||
self.transactions.push(InnerValue { value, extrinsics: Default::default() });
|
||||
} else {
|
||||
*self.value_mut() = value;
|
||||
}
|
||||
@@ -223,9 +217,9 @@ impl<K: Ord + Hash + Clone, V> OverlayedMap<K, V> {
|
||||
|
||||
/// Get an optional reference to the value stored for the specified key.
|
||||
pub fn get<Q>(&self, key: &Q) -> Option<&OverlayedEntry<V>>
|
||||
where
|
||||
K: sp_std::borrow::Borrow<Q>,
|
||||
Q: Ord + ?Sized,
|
||||
where
|
||||
K: sp_std::borrow::Borrow<Q>,
|
||||
Q: Ord + ?Sized,
|
||||
{
|
||||
self.changes.get(key)
|
||||
}
|
||||
@@ -233,24 +227,19 @@ impl<K: Ord + Hash + Clone, V> OverlayedMap<K, V> {
|
||||
/// Set a new value for the specified key.
|
||||
///
|
||||
/// Can be rolled back or committed when called inside a transaction.
|
||||
pub fn set(
|
||||
&mut self,
|
||||
key: K,
|
||||
value: V,
|
||||
at_extrinsic: Option<u32>,
|
||||
) {
|
||||
pub fn set(&mut self, key: K, value: V, at_extrinsic: Option<u32>) {
|
||||
let overlayed = self.changes.entry(key.clone()).or_default();
|
||||
overlayed.set(value, insert_dirty(&mut self.dirty_keys, key), at_extrinsic);
|
||||
}
|
||||
|
||||
/// Get a list of all changes as seen by current transaction.
|
||||
pub fn changes(&self) -> impl Iterator<Item=(&K, &OverlayedEntry<V>)> {
|
||||
pub fn changes(&self) -> impl Iterator<Item = (&K, &OverlayedEntry<V>)> {
|
||||
self.changes.iter()
|
||||
}
|
||||
|
||||
/// Get a list of all changes as seen by current transaction, consumes
|
||||
/// the overlay.
|
||||
pub fn into_changes(self) -> impl Iterator<Item=(K, OverlayedEntry<V>)> {
|
||||
pub fn into_changes(self) -> impl Iterator<Item = (K, OverlayedEntry<V>)> {
|
||||
self.changes.into_iter()
|
||||
}
|
||||
|
||||
@@ -258,7 +247,7 @@ impl<K: Ord + Hash + Clone, V> OverlayedMap<K, V> {
|
||||
///
|
||||
/// Panics:
|
||||
/// Panics if there are open transactions: `transaction_depth() > 0`
|
||||
pub fn drain_commited(self) -> impl Iterator<Item=(K, V)> {
|
||||
pub fn drain_commited(self) -> impl Iterator<Item = (K, V)> {
|
||||
assert!(self.transaction_depth() == 0, "Drain is not allowed with open transactions.");
|
||||
self.changes.into_iter().map(|(k, mut v)| (k, v.pop_transaction().value))
|
||||
}
|
||||
@@ -276,7 +265,7 @@ impl<K: Ord + Hash + Clone, V> OverlayedMap<K, V> {
|
||||
/// Calling this while already inside the runtime will return an error.
|
||||
pub fn enter_runtime(&mut self) -> Result<(), AlreadyInRuntime> {
|
||||
if let ExecutionMode::Runtime = self.execution_mode {
|
||||
return Err(AlreadyInRuntime);
|
||||
return Err(AlreadyInRuntime)
|
||||
}
|
||||
self.execution_mode = ExecutionMode::Runtime;
|
||||
self.num_client_transactions = self.transaction_depth();
|
||||
@@ -289,7 +278,7 @@ impl<K: Ord + Hash + Clone, V> OverlayedMap<K, V> {
|
||||
/// Calling this while already outside the runtime will return an error.
|
||||
pub fn exit_runtime(&mut self) -> Result<(), NotInRuntime> {
|
||||
if let ExecutionMode::Client = self.execution_mode {
|
||||
return Err(NotInRuntime);
|
||||
return Err(NotInRuntime)
|
||||
}
|
||||
self.execution_mode = ExecutionMode::Client;
|
||||
if self.has_open_runtime_transactions() {
|
||||
@@ -341,11 +330,13 @@ impl<K: Ord + Hash + Clone, V> OverlayedMap<K, V> {
|
||||
}
|
||||
|
||||
for key in self.dirty_keys.pop().ok_or(NoOpenTransaction)? {
|
||||
let overlayed = self.changes.get_mut(&key).expect("\
|
||||
let overlayed = self.changes.get_mut(&key).expect(
|
||||
"\
|
||||
A write to an OverlayedValue is recorded in the dirty key set. Before an
|
||||
OverlayedValue is removed, its containing dirty set is removed. This
|
||||
function is only called for keys that are in the dirty set. qed\
|
||||
");
|
||||
",
|
||||
);
|
||||
|
||||
if rollback {
|
||||
overlayed.pop_transaction();
|
||||
@@ -443,9 +434,12 @@ mod test {
|
||||
type Drained<'a> = Vec<(&'a [u8], Option<&'a [u8]>)>;
|
||||
|
||||
fn assert_changes(is: &OverlayedChangeSet, expected: &Changes) {
|
||||
let is: Changes = is.changes().map(|(k, v)| {
|
||||
(k.as_ref(), (v.value().map(AsRef::as_ref), v.extrinsics().into_iter().collect()))
|
||||
}).collect();
|
||||
let is: Changes = is
|
||||
.changes()
|
||||
.map(|(k, v)| {
|
||||
(k.as_ref(), (v.value().map(AsRef::as_ref), v.extrinsics().into_iter().collect()))
|
||||
})
|
||||
.collect();
|
||||
assert_eq!(&is, expected);
|
||||
}
|
||||
|
||||
@@ -453,7 +447,8 @@ mod test {
|
||||
let is = is.drain_commited().collect::<Vec<_>>();
|
||||
let expected = expected
|
||||
.iter()
|
||||
.map(|(k, v)| (k.to_vec(), v.0.map(From::from))).collect::<Vec<_>>();
|
||||
.map(|(k, v)| (k.to_vec(), v.0.map(From::from)))
|
||||
.collect::<Vec<_>>();
|
||||
assert_eq!(is, expected);
|
||||
}
|
||||
|
||||
@@ -461,7 +456,8 @@ mod test {
|
||||
let is = is.drain_commited().collect::<Vec<_>>();
|
||||
let expected = expected
|
||||
.iter()
|
||||
.map(|(k, v)| (k.to_vec(), v.map(From::from))).collect::<Vec<_>>();
|
||||
.map(|(k, v)| (k.to_vec(), v.map(From::from)))
|
||||
.collect::<Vec<_>>();
|
||||
assert_eq!(is, expected);
|
||||
}
|
||||
|
||||
@@ -474,10 +470,7 @@ mod test {
|
||||
changeset.set(b"key1".to_vec(), Some(b"val1".to_vec()), Some(2));
|
||||
changeset.set(b"key0".to_vec(), Some(b"val0-1".to_vec()), Some(9));
|
||||
|
||||
assert_drained(changeset, vec![
|
||||
(b"key0", Some(b"val0-1")),
|
||||
(b"key1", Some(b"val1")),
|
||||
]);
|
||||
assert_drained(changeset, vec![(b"key0", Some(b"val0-1")), (b"key1", Some(b"val1"))]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -599,10 +592,8 @@ mod test {
|
||||
changeset.rollback_transaction().unwrap();
|
||||
assert_eq!(changeset.transaction_depth(), 0);
|
||||
|
||||
let rolled_back: Changes = vec![
|
||||
(b"key0", (Some(b"val0-1"), vec![1, 10])),
|
||||
(b"key1", (Some(b"val1"), vec![1])),
|
||||
];
|
||||
let rolled_back: Changes =
|
||||
vec![(b"key0", (Some(b"val0-1"), vec![1, 10])), (b"key1", (Some(b"val1"), vec![1]))];
|
||||
assert_changes(&changeset, &rolled_back);
|
||||
|
||||
assert_drained_changes(changeset, rolled_back);
|
||||
@@ -676,21 +667,27 @@ mod test {
|
||||
|
||||
changeset.clear_where(|k, _| k.starts_with(b"del"), Some(5));
|
||||
|
||||
assert_changes(&changeset, &vec![
|
||||
(b"del1", (None, vec![3, 5])),
|
||||
(b"del2", (None, vec![4, 5])),
|
||||
(b"key0", (Some(b"val0"), vec![1])),
|
||||
(b"key1", (Some(b"val1"), vec![2])),
|
||||
]);
|
||||
assert_changes(
|
||||
&changeset,
|
||||
&vec![
|
||||
(b"del1", (None, vec![3, 5])),
|
||||
(b"del2", (None, vec![4, 5])),
|
||||
(b"key0", (Some(b"val0"), vec![1])),
|
||||
(b"key1", (Some(b"val1"), vec![2])),
|
||||
],
|
||||
);
|
||||
|
||||
changeset.rollback_transaction().unwrap();
|
||||
|
||||
assert_changes(&changeset, &vec![
|
||||
(b"del1", (Some(b"delval1"), vec![3])),
|
||||
(b"del2", (Some(b"delval2"), vec![4])),
|
||||
(b"key0", (Some(b"val0"), vec![1])),
|
||||
(b"key1", (Some(b"val1"), vec![2])),
|
||||
]);
|
||||
assert_changes(
|
||||
&changeset,
|
||||
&vec![
|
||||
(b"del1", (Some(b"delval1"), vec![3])),
|
||||
(b"del2", (Some(b"delval2"), vec![4])),
|
||||
(b"key0", (Some(b"val0"), vec![1])),
|
||||
(b"key1", (Some(b"val1"), vec![2])),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -708,29 +705,52 @@ mod test {
|
||||
changeset.set(b"key11".to_vec(), Some(b"val11".to_vec()), Some(11));
|
||||
|
||||
assert_eq!(changeset.changes_after(b"key0").next().unwrap().0, b"key1");
|
||||
assert_eq!(changeset.changes_after(b"key0").next().unwrap().1.value(), Some(&b"val1".to_vec()));
|
||||
assert_eq!(
|
||||
changeset.changes_after(b"key0").next().unwrap().1.value(),
|
||||
Some(&b"val1".to_vec())
|
||||
);
|
||||
assert_eq!(changeset.changes_after(b"key1").next().unwrap().0, b"key11");
|
||||
assert_eq!(changeset.changes_after(b"key1").next().unwrap().1.value(), Some(&b"val11".to_vec()));
|
||||
assert_eq!(
|
||||
changeset.changes_after(b"key1").next().unwrap().1.value(),
|
||||
Some(&b"val11".to_vec())
|
||||
);
|
||||
assert_eq!(changeset.changes_after(b"key11").next().unwrap().0, b"key2");
|
||||
assert_eq!(changeset.changes_after(b"key11").next().unwrap().1.value(), Some(&b"val2".to_vec()));
|
||||
assert_eq!(
|
||||
changeset.changes_after(b"key11").next().unwrap().1.value(),
|
||||
Some(&b"val2".to_vec())
|
||||
);
|
||||
assert_eq!(changeset.changes_after(b"key2").next().unwrap().0, b"key3");
|
||||
assert_eq!(changeset.changes_after(b"key2").next().unwrap().1.value(), Some(&b"val3".to_vec()));
|
||||
assert_eq!(
|
||||
changeset.changes_after(b"key2").next().unwrap().1.value(),
|
||||
Some(&b"val3".to_vec())
|
||||
);
|
||||
assert_eq!(changeset.changes_after(b"key3").next().unwrap().0, b"key4");
|
||||
assert_eq!(changeset.changes_after(b"key3").next().unwrap().1.value(), Some(&b"val4".to_vec()));
|
||||
assert_eq!(
|
||||
changeset.changes_after(b"key3").next().unwrap().1.value(),
|
||||
Some(&b"val4".to_vec())
|
||||
);
|
||||
assert_eq!(changeset.changes_after(b"key4").next(), None);
|
||||
|
||||
changeset.rollback_transaction().unwrap();
|
||||
|
||||
assert_eq!(changeset.changes_after(b"key0").next().unwrap().0, b"key1");
|
||||
assert_eq!(changeset.changes_after(b"key0").next().unwrap().1.value(), Some(&b"val1".to_vec()));
|
||||
assert_eq!(
|
||||
changeset.changes_after(b"key0").next().unwrap().1.value(),
|
||||
Some(&b"val1".to_vec())
|
||||
);
|
||||
assert_eq!(changeset.changes_after(b"key1").next().unwrap().0, b"key2");
|
||||
assert_eq!(changeset.changes_after(b"key1").next().unwrap().1.value(), Some(&b"val2".to_vec()));
|
||||
assert_eq!(
|
||||
changeset.changes_after(b"key1").next().unwrap().1.value(),
|
||||
Some(&b"val2".to_vec())
|
||||
);
|
||||
assert_eq!(changeset.changes_after(b"key11").next().unwrap().0, b"key2");
|
||||
assert_eq!(changeset.changes_after(b"key11").next().unwrap().1.value(), Some(&b"val2".to_vec()));
|
||||
assert_eq!(
|
||||
changeset.changes_after(b"key11").next().unwrap().1.value(),
|
||||
Some(&b"val2".to_vec())
|
||||
);
|
||||
assert_eq!(changeset.changes_after(b"key2").next(), None);
|
||||
assert_eq!(changeset.changes_after(b"key3").next(), None);
|
||||
assert_eq!(changeset.changes_after(b"key4").next(), None);
|
||||
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -790,9 +810,7 @@ mod test {
|
||||
changeset.commit_transaction().unwrap();
|
||||
assert_eq!(changeset.transaction_depth(), 0);
|
||||
|
||||
assert_drained(changeset, vec![
|
||||
(b"key0", Some(b"val0")),
|
||||
]);
|
||||
assert_drained(changeset, vec![(b"key0", Some(b"val0"))]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -20,36 +20,35 @@
|
||||
mod changeset;
|
||||
mod offchain;
|
||||
|
||||
pub use offchain::OffchainOverlayedChanges;
|
||||
use crate::{
|
||||
backend::Backend,
|
||||
stats::StateMachineStats,
|
||||
};
|
||||
use sp_std::{vec::Vec, any::{TypeId, Any}, boxed::Box};
|
||||
use self::changeset::OverlayedChangeSet;
|
||||
use crate::{backend::Backend, stats::StateMachineStats};
|
||||
pub use offchain::OffchainOverlayedChanges;
|
||||
use sp_std::{
|
||||
any::{Any, TypeId},
|
||||
boxed::Box,
|
||||
vec::Vec,
|
||||
};
|
||||
|
||||
use crate::{changes_trie::BlockNumber, DefaultError};
|
||||
#[cfg(feature = "std")]
|
||||
use crate::{
|
||||
changes_trie::{build_changes_trie, State as ChangesTrieState},
|
||||
ChangesTrieTransaction,
|
||||
changes_trie::{
|
||||
build_changes_trie,
|
||||
State as ChangesTrieState,
|
||||
},
|
||||
};
|
||||
use crate::changes_trie::BlockNumber;
|
||||
#[cfg(feature = "std")]
|
||||
use std::collections::{HashMap as Map, hash_map::Entry as MapEntry};
|
||||
use codec::{Decode, Encode};
|
||||
use hash_db::Hasher;
|
||||
use sp_core::{
|
||||
offchain::OffchainOverlayedChange,
|
||||
storage::{well_known_keys::EXTRINSIC_INDEX, ChildInfo},
|
||||
};
|
||||
use sp_externalities::{Extension, Extensions};
|
||||
#[cfg(not(feature = "std"))]
|
||||
use sp_std::collections::btree_map::{BTreeMap as Map, Entry as MapEntry};
|
||||
use sp_std::collections::btree_set::BTreeSet;
|
||||
use codec::{Decode, Encode};
|
||||
use sp_core::storage::{well_known_keys::EXTRINSIC_INDEX, ChildInfo};
|
||||
use sp_core::offchain::OffchainOverlayedChange;
|
||||
use hash_db::Hasher;
|
||||
use crate::DefaultError;
|
||||
use sp_externalities::{Extensions, Extension};
|
||||
#[cfg(feature = "std")]
|
||||
use std::collections::{hash_map::Entry as MapEntry, HashMap as Map};
|
||||
|
||||
pub use self::changeset::{OverlayedValue, NoOpenTransaction, AlreadyInRuntime, NotInRuntime};
|
||||
pub use self::changeset::{AlreadyInRuntime, NoOpenTransaction, NotInRuntime, OverlayedValue};
|
||||
|
||||
/// Changes that are made outside of extrinsics are marked with this index;
|
||||
pub const NO_EXTRINSIC_INDEX: u32 = 0xffffffff;
|
||||
@@ -129,7 +128,7 @@ pub enum IndexOperation {
|
||||
extrinsic: u32,
|
||||
/// Referenced index hash.
|
||||
hash: Vec<u8>,
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
/// A storage changes structure that can be generated by the data collected in [`OverlayedChanges`].
|
||||
@@ -169,7 +168,9 @@ pub struct StorageChanges<Transaction, H: Hasher, N: BlockNumber> {
|
||||
#[cfg(feature = "std")]
|
||||
impl<Transaction, H: Hasher, N: BlockNumber> StorageChanges<Transaction, H, N> {
|
||||
/// Deconstruct into the inner values
|
||||
pub fn into_inner(self) -> (
|
||||
pub fn into_inner(
|
||||
self,
|
||||
) -> (
|
||||
StorageCollection,
|
||||
ChildStorageCollection,
|
||||
OffchainChangesCollection,
|
||||
@@ -216,7 +217,9 @@ impl<Transaction, H: Hasher, N: BlockNumber> StorageTransactionCache<Transaction
|
||||
}
|
||||
}
|
||||
|
||||
impl<Transaction, H: Hasher, N: BlockNumber> Default for StorageTransactionCache<Transaction, H, N> {
|
||||
impl<Transaction, H: Hasher, N: BlockNumber> Default
|
||||
for StorageTransactionCache<Transaction, H, N>
|
||||
{
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
transaction: None,
|
||||
@@ -231,7 +234,9 @@ impl<Transaction, H: Hasher, N: BlockNumber> Default for StorageTransactionCache
|
||||
}
|
||||
}
|
||||
|
||||
impl<Transaction: Default, H: Hasher, N: BlockNumber> Default for StorageChanges<Transaction, H, N> {
|
||||
impl<Transaction: Default, H: Hasher, N: BlockNumber> Default
|
||||
for StorageChanges<Transaction, H, N>
|
||||
{
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
main_storage_changes: Default::default(),
|
||||
@@ -325,12 +330,10 @@ impl OverlayedChanges {
|
||||
self.stats.tally_write_overlay(size_write);
|
||||
let storage_key = child_info.storage_key().to_vec();
|
||||
let top = &self.top;
|
||||
let (changeset, info) = self.children.entry(storage_key).or_insert_with(||
|
||||
(
|
||||
top.spawn_child(),
|
||||
child_info.clone()
|
||||
)
|
||||
);
|
||||
let (changeset, info) = self
|
||||
.children
|
||||
.entry(storage_key)
|
||||
.or_insert_with(|| (top.spawn_child(), child_info.clone()));
|
||||
let updatable = info.try_update(child_info);
|
||||
debug_assert!(updatable);
|
||||
changeset.set(key, val, extrinsic_index);
|
||||
@@ -339,19 +342,14 @@ impl OverlayedChanges {
|
||||
/// Clear child storage of given storage key.
|
||||
///
|
||||
/// Can be rolled back or committed when called inside a transaction.
|
||||
pub(crate) fn clear_child_storage(
|
||||
&mut self,
|
||||
child_info: &ChildInfo,
|
||||
) {
|
||||
pub(crate) fn clear_child_storage(&mut self, child_info: &ChildInfo) {
|
||||
let extrinsic_index = self.extrinsic_index();
|
||||
let storage_key = child_info.storage_key().to_vec();
|
||||
let top = &self.top;
|
||||
let (changeset, info) = self.children.entry(storage_key).or_insert_with(||
|
||||
(
|
||||
top.spawn_child(),
|
||||
child_info.clone()
|
||||
)
|
||||
);
|
||||
let (changeset, info) = self
|
||||
.children
|
||||
.entry(storage_key)
|
||||
.or_insert_with(|| (top.spawn_child(), child_info.clone()));
|
||||
let updatable = info.try_update(child_info);
|
||||
debug_assert!(updatable);
|
||||
changeset.clear_where(|_, _| true, extrinsic_index);
|
||||
@@ -367,20 +365,14 @@ impl OverlayedChanges {
|
||||
/// Removes all key-value pairs which keys share the given prefix.
|
||||
///
|
||||
/// Can be rolled back or committed when called inside a transaction
|
||||
pub(crate) fn clear_child_prefix(
|
||||
&mut self,
|
||||
child_info: &ChildInfo,
|
||||
prefix: &[u8],
|
||||
) {
|
||||
pub(crate) fn clear_child_prefix(&mut self, child_info: &ChildInfo, prefix: &[u8]) {
|
||||
let extrinsic_index = self.extrinsic_index();
|
||||
let storage_key = child_info.storage_key().to_vec();
|
||||
let top = &self.top;
|
||||
let (changeset, info) = self.children.entry(storage_key).or_insert_with(||
|
||||
(
|
||||
top.spawn_child(),
|
||||
child_info.clone()
|
||||
)
|
||||
);
|
||||
let (changeset, info) = self
|
||||
.children
|
||||
.entry(storage_key)
|
||||
.or_insert_with(|| (top.spawn_child(), child_info.clone()));
|
||||
let updatable = info.try_update(child_info);
|
||||
debug_assert!(updatable);
|
||||
changeset.clear_where(|key, _| key.starts_with(prefix), extrinsic_index);
|
||||
@@ -417,11 +409,14 @@ impl OverlayedChanges {
|
||||
pub fn rollback_transaction(&mut self) -> Result<(), NoOpenTransaction> {
|
||||
self.top.rollback_transaction()?;
|
||||
retain_map(&mut self.children, |_, (changeset, _)| {
|
||||
changeset.rollback_transaction()
|
||||
changeset
|
||||
.rollback_transaction()
|
||||
.expect("Top and children changesets are started in lockstep; qed");
|
||||
!changeset.is_empty()
|
||||
});
|
||||
self.offchain.overlay_mut().rollback_transaction()
|
||||
self.offchain
|
||||
.overlay_mut()
|
||||
.rollback_transaction()
|
||||
.expect("Top and offchain changesets are started in lockstep; qed");
|
||||
Ok(())
|
||||
}
|
||||
@@ -433,10 +428,13 @@ impl OverlayedChanges {
|
||||
pub fn commit_transaction(&mut self) -> Result<(), NoOpenTransaction> {
|
||||
self.top.commit_transaction()?;
|
||||
for (_, (changeset, _)) in self.children.iter_mut() {
|
||||
changeset.commit_transaction()
|
||||
changeset
|
||||
.commit_transaction()
|
||||
.expect("Top and children changesets are started in lockstep; qed");
|
||||
}
|
||||
self.offchain.overlay_mut().commit_transaction()
|
||||
self.offchain
|
||||
.overlay_mut()
|
||||
.commit_transaction()
|
||||
.expect("Top and offchain changesets are started in lockstep; qed");
|
||||
Ok(())
|
||||
}
|
||||
@@ -448,10 +446,13 @@ impl OverlayedChanges {
|
||||
pub fn enter_runtime(&mut self) -> Result<(), AlreadyInRuntime> {
|
||||
self.top.enter_runtime()?;
|
||||
for (_, (changeset, _)) in self.children.iter_mut() {
|
||||
changeset.enter_runtime()
|
||||
changeset
|
||||
.enter_runtime()
|
||||
.expect("Top and children changesets are entering runtime in lockstep; qed")
|
||||
}
|
||||
self.offchain.overlay_mut().enter_runtime()
|
||||
self.offchain
|
||||
.overlay_mut()
|
||||
.enter_runtime()
|
||||
.expect("Top and offchain changesets are started in lockstep; qed");
|
||||
Ok(())
|
||||
}
|
||||
@@ -463,10 +464,13 @@ impl OverlayedChanges {
|
||||
pub fn exit_runtime(&mut self) -> Result<(), NotInRuntime> {
|
||||
self.top.exit_runtime()?;
|
||||
for (_, (changeset, _)) in self.children.iter_mut() {
|
||||
changeset.exit_runtime()
|
||||
changeset
|
||||
.exit_runtime()
|
||||
.expect("Top and children changesets are entering runtime in lockstep; qed");
|
||||
}
|
||||
self.offchain.overlay_mut().exit_runtime()
|
||||
self.offchain
|
||||
.overlay_mut()
|
||||
.exit_runtime()
|
||||
.expect("Top and offchain changesets are started in lockstep; qed");
|
||||
Ok(())
|
||||
}
|
||||
@@ -477,19 +481,23 @@ impl OverlayedChanges {
|
||||
///
|
||||
/// Panics:
|
||||
/// Panics if `transaction_depth() > 0`
|
||||
fn drain_committed(&mut self) -> (
|
||||
impl Iterator<Item=(StorageKey, Option<StorageValue>)>,
|
||||
impl Iterator<Item=(StorageKey, (impl Iterator<Item=(StorageKey, Option<StorageValue>)>, ChildInfo))>,
|
||||
fn drain_committed(
|
||||
&mut self,
|
||||
) -> (
|
||||
impl Iterator<Item = (StorageKey, Option<StorageValue>)>,
|
||||
impl Iterator<
|
||||
Item = (
|
||||
StorageKey,
|
||||
(impl Iterator<Item = (StorageKey, Option<StorageValue>)>, ChildInfo),
|
||||
),
|
||||
>,
|
||||
) {
|
||||
use sp_std::mem::take;
|
||||
(
|
||||
take(&mut self.top).drain_commited(),
|
||||
take(&mut self.children).into_iter()
|
||||
.map(|(key, (val, info))| (
|
||||
key,
|
||||
(val.drain_commited(), info)
|
||||
)
|
||||
),
|
||||
take(&mut self.children)
|
||||
.into_iter()
|
||||
.map(|(key, (val, info))| (key, (val.drain_commited(), info))),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -499,24 +507,29 @@ impl OverlayedChanges {
|
||||
///
|
||||
/// Panics:
|
||||
/// Panics if `transaction_depth() > 0`
|
||||
pub fn offchain_drain_committed(&mut self) -> impl Iterator<Item=((StorageKey, StorageKey), OffchainOverlayedChange)> {
|
||||
pub fn offchain_drain_committed(
|
||||
&mut self,
|
||||
) -> impl Iterator<Item = ((StorageKey, StorageKey), OffchainOverlayedChange)> {
|
||||
self.offchain.drain()
|
||||
}
|
||||
|
||||
/// Get an iterator over all child changes as seen by the current transaction.
|
||||
pub fn children(&self)
|
||||
-> impl Iterator<Item=(impl Iterator<Item=(&StorageKey, &OverlayedValue)>, &ChildInfo)> {
|
||||
pub fn children(
|
||||
&self,
|
||||
) -> impl Iterator<Item = (impl Iterator<Item = (&StorageKey, &OverlayedValue)>, &ChildInfo)> {
|
||||
self.children.iter().map(|(_, v)| (v.0.changes(), &v.1))
|
||||
}
|
||||
|
||||
/// Get an iterator over all top changes as been by the current transaction.
|
||||
pub fn changes(&self) -> impl Iterator<Item=(&StorageKey, &OverlayedValue)> {
|
||||
pub fn changes(&self) -> impl Iterator<Item = (&StorageKey, &OverlayedValue)> {
|
||||
self.top.changes()
|
||||
}
|
||||
|
||||
/// Get an optional iterator over all child changes stored under the supplied key.
|
||||
pub fn child_changes(&self, key: &[u8])
|
||||
-> Option<(impl Iterator<Item=(&StorageKey, &OverlayedValue)>, &ChildInfo)> {
|
||||
pub fn child_changes(
|
||||
&self,
|
||||
key: &[u8],
|
||||
) -> Option<(impl Iterator<Item = (&StorageKey, &OverlayedValue)>, &ChildInfo)> {
|
||||
self.children.get(key).map(|(overlay, info)| (overlay.changes(), info))
|
||||
}
|
||||
|
||||
@@ -527,16 +540,16 @@ impl OverlayedChanges {
|
||||
|
||||
/// Convert this instance with all changes into a [`StorageChanges`] instance.
|
||||
#[cfg(feature = "std")]
|
||||
pub fn into_storage_changes<
|
||||
B: Backend<H>, H: Hasher, N: BlockNumber
|
||||
>(
|
||||
pub fn into_storage_changes<B: Backend<H>, H: Hasher, N: BlockNumber>(
|
||||
mut self,
|
||||
backend: &B,
|
||||
changes_trie_state: Option<&ChangesTrieState<H, N>>,
|
||||
parent_hash: H::Out,
|
||||
mut cache: StorageTransactionCache<B::Transaction, H, N>,
|
||||
) -> Result<StorageChanges<B::Transaction, H, N>, DefaultError>
|
||||
where H::Out: Ord + Encode + 'static {
|
||||
where
|
||||
H::Out: Ord + Encode + 'static,
|
||||
{
|
||||
self.drain_storage_changes(backend, changes_trie_state, parent_hash, &mut cache)
|
||||
}
|
||||
|
||||
@@ -544,35 +557,34 @@ impl OverlayedChanges {
|
||||
pub fn drain_storage_changes<B: Backend<H>, H: Hasher, N: BlockNumber>(
|
||||
&mut self,
|
||||
backend: &B,
|
||||
#[cfg(feature = "std")]
|
||||
changes_trie_state: Option<&ChangesTrieState<H, N>>,
|
||||
#[cfg(feature = "std")] changes_trie_state: Option<&ChangesTrieState<H, N>>,
|
||||
parent_hash: H::Out,
|
||||
mut cache: &mut StorageTransactionCache<B::Transaction, H, N>,
|
||||
) -> Result<StorageChanges<B::Transaction, H, N>, DefaultError>
|
||||
where H::Out: Ord + Encode + 'static {
|
||||
where
|
||||
H::Out: Ord + Encode + 'static,
|
||||
{
|
||||
// If the transaction does not exist, we generate it.
|
||||
if cache.transaction.is_none() {
|
||||
self.storage_root(backend, &mut cache);
|
||||
}
|
||||
|
||||
let (transaction, transaction_storage_root) = cache.transaction.take()
|
||||
let (transaction, transaction_storage_root) = cache
|
||||
.transaction
|
||||
.take()
|
||||
.and_then(|t| cache.transaction_storage_root.take().map(|tr| (t, tr)))
|
||||
.expect("Transaction was be generated as part of `storage_root`; qed");
|
||||
|
||||
// If the transaction does not exist, we generate it.
|
||||
#[cfg(feature = "std")]
|
||||
if cache.changes_trie_transaction.is_none() {
|
||||
self.changes_trie_root(
|
||||
backend,
|
||||
changes_trie_state,
|
||||
parent_hash,
|
||||
false,
|
||||
&mut cache,
|
||||
).map_err(|_| "Failed to generate changes trie transaction")?;
|
||||
self.changes_trie_root(backend, changes_trie_state, parent_hash, false, &mut cache)
|
||||
.map_err(|_| "Failed to generate changes trie transaction")?;
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
let changes_trie_transaction = cache.changes_trie_transaction
|
||||
let changes_trie_transaction = cache
|
||||
.changes_trie_transaction
|
||||
.take()
|
||||
.expect("Changes trie transaction was generated by `changes_trie_root`; qed");
|
||||
|
||||
@@ -584,7 +596,9 @@ impl OverlayedChanges {
|
||||
|
||||
Ok(StorageChanges {
|
||||
main_storage_changes: main_storage_changes.collect(),
|
||||
child_storage_changes: child_storage_changes.map(|(sk, it)| (sk, it.0.collect())).collect(),
|
||||
child_storage_changes: child_storage_changes
|
||||
.map(|(sk, it)| (sk, it.0.collect()))
|
||||
.collect(),
|
||||
offchain_storage_changes,
|
||||
transaction,
|
||||
transaction_storage_root,
|
||||
@@ -614,7 +628,8 @@ impl OverlayedChanges {
|
||||
true => Some(
|
||||
self.storage(EXTRINSIC_INDEX)
|
||||
.and_then(|idx| idx.and_then(|idx| Decode::decode(&mut &*idx).ok()))
|
||||
.unwrap_or(NO_EXTRINSIC_INDEX)),
|
||||
.unwrap_or(NO_EXTRINSIC_INDEX),
|
||||
),
|
||||
false => None,
|
||||
}
|
||||
}
|
||||
@@ -628,13 +643,13 @@ impl OverlayedChanges {
|
||||
backend: &B,
|
||||
cache: &mut StorageTransactionCache<B::Transaction, H, N>,
|
||||
) -> H::Out
|
||||
where H::Out: Ord + Encode,
|
||||
where
|
||||
H::Out: Ord + Encode,
|
||||
{
|
||||
let delta = self.changes().map(|(k, v)| (&k[..], v.value().map(|v| &v[..])));
|
||||
let child_delta = self.children()
|
||||
.map(|(changes, info)| (info, changes.map(
|
||||
|(k, v)| (&k[..], v.value().map(|v| &v[..]))
|
||||
)));
|
||||
let child_delta = self.children().map(|(changes, info)| {
|
||||
(info, changes.map(|(k, v)| (&k[..], v.value().map(|v| &v[..]))))
|
||||
});
|
||||
|
||||
let (root, transaction) = backend.full_storage_root(delta, child_delta);
|
||||
|
||||
@@ -659,14 +674,18 @@ impl OverlayedChanges {
|
||||
parent_hash: H::Out,
|
||||
panic_on_storage_error: bool,
|
||||
cache: &mut StorageTransactionCache<B::Transaction, H, N>,
|
||||
) -> Result<Option<H::Out>, ()> where H::Out: Ord + Encode + 'static {
|
||||
) -> Result<Option<H::Out>, ()>
|
||||
where
|
||||
H::Out: Ord + Encode + 'static,
|
||||
{
|
||||
build_changes_trie::<_, H, N>(
|
||||
backend,
|
||||
changes_trie_state,
|
||||
self,
|
||||
parent_hash,
|
||||
panic_on_storage_error,
|
||||
).map(|r| {
|
||||
)
|
||||
.map(|r| {
|
||||
let root = r.as_ref().map(|r| r.1).clone();
|
||||
cache.changes_trie_transaction = Some(r.map(|(db, _, cache)| (db, cache)));
|
||||
cache.changes_trie_transaction_storage_root = Some(root);
|
||||
@@ -685,7 +704,7 @@ impl OverlayedChanges {
|
||||
pub fn child_iter_after(
|
||||
&self,
|
||||
storage_key: &[u8],
|
||||
key: &[u8]
|
||||
key: &[u8],
|
||||
) -> impl Iterator<Item = (&[u8], &OverlayedValue)> {
|
||||
self.children
|
||||
.get(storage_key)
|
||||
@@ -716,18 +735,18 @@ impl OverlayedChanges {
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
fn retain_map<K, V, F>(map: &mut Map<K, V>, f: F)
|
||||
where
|
||||
K: std::cmp::Eq + std::hash::Hash,
|
||||
F: FnMut(&K, &mut V) -> bool,
|
||||
where
|
||||
K: std::cmp::Eq + std::hash::Hash,
|
||||
F: FnMut(&K, &mut V) -> bool,
|
||||
{
|
||||
map.retain(f);
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
fn retain_map<K, V, F>(map: &mut Map<K, V>, mut f: F)
|
||||
where
|
||||
K: Ord,
|
||||
F: FnMut(&K, &mut V) -> bool,
|
||||
where
|
||||
K: Ord,
|
||||
F: FnMut(&K, &mut V) -> bool,
|
||||
{
|
||||
let old = sp_std::mem::replace(map, Map::default());
|
||||
for (k, mut v) in old.into_iter() {
|
||||
@@ -799,18 +818,13 @@ impl<'a> OverlayedExtensions<'a> {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use hex_literal::hex;
|
||||
use sp_core::{Blake2Hasher, traits::Externalities};
|
||||
use crate::InMemoryBackend;
|
||||
use crate::ext::Ext;
|
||||
use super::*;
|
||||
use crate::{ext::Ext, InMemoryBackend};
|
||||
use hex_literal::hex;
|
||||
use sp_core::{traits::Externalities, Blake2Hasher};
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
fn assert_extrinsics(
|
||||
overlay: &OverlayedChangeSet,
|
||||
key: impl AsRef<[u8]>,
|
||||
expected: Vec<u32>,
|
||||
) {
|
||||
fn assert_extrinsics(overlay: &OverlayedChangeSet, key: impl AsRef<[u8]>, expected: Vec<u32>) {
|
||||
assert_eq!(
|
||||
overlay.get(key.as_ref()).unwrap().extrinsics().into_iter().collect::<Vec<_>>(),
|
||||
expected
|
||||
@@ -863,13 +877,16 @@ mod tests {
|
||||
state.commit_transaction().unwrap();
|
||||
}
|
||||
let offchain_data: Vec<_> = state.offchain_drain_committed().collect();
|
||||
let expected: Vec<_> = expected.into_iter().map(|(key, value)| {
|
||||
let change = match value {
|
||||
Some(value) => OffchainOverlayedChange::SetValue(value),
|
||||
None => OffchainOverlayedChange::Remove,
|
||||
};
|
||||
((STORAGE_PREFIX.to_vec(), key), change)
|
||||
}).collect();
|
||||
let expected: Vec<_> = expected
|
||||
.into_iter()
|
||||
.map(|(key, value)| {
|
||||
let change = match value {
|
||||
Some(value) => OffchainOverlayedChange::SetValue(value),
|
||||
None => OffchainOverlayedChange::Remove,
|
||||
};
|
||||
((STORAGE_PREFIX.to_vec(), key), change)
|
||||
})
|
||||
.collect();
|
||||
assert_eq!(offchain_data, expected);
|
||||
}
|
||||
|
||||
@@ -904,7 +921,6 @@ mod tests {
|
||||
check_offchain_content(&overlayed, 0, vec![(key.clone(), None)]);
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn overlayed_storage_root_works() {
|
||||
let initial: BTreeMap<_, _> = vec![
|
||||
@@ -912,7 +928,9 @@ mod tests {
|
||||
(b"dog".to_vec(), b"puppyXXX".to_vec()),
|
||||
(b"dogglesworth".to_vec(), b"catXXX".to_vec()),
|
||||
(b"doug".to_vec(), b"notadog".to_vec()),
|
||||
].into_iter().collect();
|
||||
]
|
||||
.into_iter()
|
||||
.collect();
|
||||
let backend = InMemoryBackend::<Blake2Hasher>::from(initial);
|
||||
let mut overlay = OverlayedChanges::default();
|
||||
overlay.set_collect_extrinsics(false);
|
||||
@@ -935,7 +953,8 @@ mod tests {
|
||||
crate::changes_trie::disabled_state::<_, u64>(),
|
||||
None,
|
||||
);
|
||||
const ROOT: [u8; 32] = hex!("39245109cef3758c2eed2ccba8d9b370a917850af3824bc8348d505df2c298fa");
|
||||
const ROOT: [u8; 32] =
|
||||
hex!("39245109cef3758c2eed2ccba8d9b370a917850af3824bc8348d505df2c298fa");
|
||||
|
||||
assert_eq!(&ext.storage_root()[..], &ROOT);
|
||||
}
|
||||
|
||||
@@ -17,9 +17,9 @@
|
||||
|
||||
//! Overlayed changes for offchain indexing.
|
||||
|
||||
use super::changeset::OverlayedMap;
|
||||
use sp_core::offchain::OffchainOverlayedChange;
|
||||
use sp_std::prelude::Vec;
|
||||
use super::changeset::OverlayedMap;
|
||||
|
||||
/// In-memory storage for offchain workers recoding changes for the actual offchain storage implementation.
|
||||
#[derive(Debug, Clone, Default)]
|
||||
@@ -52,11 +52,9 @@ impl OffchainOverlayedChanges {
|
||||
|
||||
/// Remove a key and its associated value from the offchain database.
|
||||
pub fn remove(&mut self, prefix: &[u8], key: &[u8]) {
|
||||
let _ = self.0.set(
|
||||
(prefix.to_vec(), key.to_vec()),
|
||||
OffchainOverlayedChange::Remove,
|
||||
None,
|
||||
);
|
||||
let _ = self
|
||||
.0
|
||||
.set((prefix.to_vec(), key.to_vec()), OffchainOverlayedChange::Remove, None);
|
||||
}
|
||||
|
||||
/// Set the value associated with a key under a prefix to the value provided.
|
||||
@@ -80,7 +78,9 @@ impl OffchainOverlayedChanges {
|
||||
}
|
||||
|
||||
/// Mutable reference to inner change set.
|
||||
pub fn overlay_mut(&mut self) -> &mut OverlayedMap<(Vec<u8>, Vec<u8>), OffchainOverlayedChange> {
|
||||
pub fn overlay_mut(
|
||||
&mut self,
|
||||
) -> &mut OverlayedMap<(Vec<u8>, Vec<u8>), OffchainOverlayedChange> {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
@@ -120,10 +120,10 @@ mod test {
|
||||
let mut iter = ooc.into_iter();
|
||||
assert_eq!(
|
||||
iter.next(),
|
||||
Some(
|
||||
((STORAGE_PREFIX.to_vec(), b"ppp".to_vec()),
|
||||
OffchainOverlayedChange::SetValue(b"rrr".to_vec()))
|
||||
)
|
||||
Some((
|
||||
(STORAGE_PREFIX.to_vec(), b"ppp".to_vec()),
|
||||
OffchainOverlayedChange::SetValue(b"rrr".to_vec())
|
||||
))
|
||||
);
|
||||
assert_eq!(iter.next(), None);
|
||||
}
|
||||
|
||||
@@ -17,20 +17,28 @@
|
||||
|
||||
//! Proving state machine backend.
|
||||
|
||||
use std::{sync::Arc, collections::{HashMap, hash_map::Entry}};
|
||||
use parking_lot::RwLock;
|
||||
use codec::{Decode, Codec, Encode};
|
||||
use log::debug;
|
||||
use hash_db::{Hasher, HashDB, EMPTY_PREFIX, Prefix};
|
||||
use sp_trie::{
|
||||
MemoryDB, empty_child_trie_root, read_trie_value_with, read_child_trie_value_with,
|
||||
record_all_keys, StorageProof,
|
||||
use crate::{
|
||||
trie_backend::TrieBackend,
|
||||
trie_backend_essence::{Ephemeral, TrieBackendEssence, TrieBackendStorage},
|
||||
Backend, DBValue, Error, ExecutionError,
|
||||
};
|
||||
pub use sp_trie::{Recorder, trie_types::{Layout, TrieError}};
|
||||
use crate::trie_backend::TrieBackend;
|
||||
use crate::trie_backend_essence::{Ephemeral, TrieBackendEssence, TrieBackendStorage};
|
||||
use crate::{Error, ExecutionError, Backend, DBValue};
|
||||
use codec::{Codec, Decode, Encode};
|
||||
use hash_db::{HashDB, Hasher, Prefix, EMPTY_PREFIX};
|
||||
use log::debug;
|
||||
use parking_lot::RwLock;
|
||||
use sp_core::storage::ChildInfo;
|
||||
use sp_trie::{
|
||||
empty_child_trie_root, read_child_trie_value_with, read_trie_value_with, record_all_keys,
|
||||
MemoryDB, StorageProof,
|
||||
};
|
||||
pub use sp_trie::{
|
||||
trie_types::{Layout, TrieError},
|
||||
Recorder,
|
||||
};
|
||||
use std::{
|
||||
collections::{hash_map::Entry, HashMap},
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
/// Patricia trie-based backend specialized in get value proofs.
|
||||
pub struct ProvingBackendRecorder<'a, S: 'a + TrieBackendStorage<H>, H: 'a + Hasher> {
|
||||
@@ -39,18 +47,15 @@ pub struct ProvingBackendRecorder<'a, S: 'a + TrieBackendStorage<H>, H: 'a + Has
|
||||
}
|
||||
|
||||
impl<'a, S, H> ProvingBackendRecorder<'a, S, H>
|
||||
where
|
||||
S: TrieBackendStorage<H>,
|
||||
H: Hasher,
|
||||
H::Out: Codec,
|
||||
where
|
||||
S: TrieBackendStorage<H>,
|
||||
H: Hasher,
|
||||
H::Out: Codec,
|
||||
{
|
||||
/// Produce proof for a key query.
|
||||
pub fn storage(&mut self, key: &[u8]) -> Result<Option<Vec<u8>>, String> {
|
||||
let mut read_overlay = S::Overlay::default();
|
||||
let eph = Ephemeral::new(
|
||||
self.backend.backend_storage(),
|
||||
&mut read_overlay,
|
||||
);
|
||||
let eph = Ephemeral::new(self.backend.backend_storage(), &mut read_overlay);
|
||||
|
||||
let map_e = |e| format!("Trie lookup error: {}", e);
|
||||
|
||||
@@ -59,25 +64,24 @@ impl<'a, S, H> ProvingBackendRecorder<'a, S, H>
|
||||
self.backend.root(),
|
||||
key,
|
||||
&mut *self.proof_recorder,
|
||||
).map_err(map_e)
|
||||
)
|
||||
.map_err(map_e)
|
||||
}
|
||||
|
||||
/// Produce proof for a child key query.
|
||||
pub fn child_storage(
|
||||
&mut self,
|
||||
child_info: &ChildInfo,
|
||||
key: &[u8]
|
||||
key: &[u8],
|
||||
) -> Result<Option<Vec<u8>>, String> {
|
||||
let storage_key = child_info.storage_key();
|
||||
let root = self.storage(storage_key)?
|
||||
let root = self
|
||||
.storage(storage_key)?
|
||||
.and_then(|r| Decode::decode(&mut &r[..]).ok())
|
||||
.unwrap_or_else(|| empty_child_trie_root::<Layout<H>>());
|
||||
|
||||
let mut read_overlay = S::Overlay::default();
|
||||
let eph = Ephemeral::new(
|
||||
self.backend.backend_storage(),
|
||||
&mut read_overlay,
|
||||
);
|
||||
let eph = Ephemeral::new(self.backend.backend_storage(), &mut read_overlay);
|
||||
|
||||
let map_e = |e| format!("Trie lookup error: {}", e);
|
||||
|
||||
@@ -86,17 +90,15 @@ impl<'a, S, H> ProvingBackendRecorder<'a, S, H>
|
||||
&eph,
|
||||
&root.as_ref(),
|
||||
key,
|
||||
&mut *self.proof_recorder
|
||||
).map_err(map_e)
|
||||
&mut *self.proof_recorder,
|
||||
)
|
||||
.map_err(map_e)
|
||||
}
|
||||
|
||||
/// Produce proof for the whole backend.
|
||||
pub fn record_all_keys(&mut self) {
|
||||
let mut read_overlay = S::Overlay::default();
|
||||
let eph = Ephemeral::new(
|
||||
self.backend.backend_storage(),
|
||||
&mut read_overlay,
|
||||
);
|
||||
let eph = Ephemeral::new(self.backend.backend_storage(), &mut read_overlay);
|
||||
|
||||
let mut iter = move || -> Result<(), Box<TrieError<H::Out>>> {
|
||||
let root = self.backend.root();
|
||||
@@ -150,13 +152,14 @@ impl<Hash: std::hash::Hash + Eq> ProofRecorder<Hash> {
|
||||
/// encoded proof.
|
||||
pub fn estimate_encoded_size(&self) -> usize {
|
||||
let inner = self.inner.read();
|
||||
inner.encoded_size
|
||||
+ codec::Compact(inner.records.len() as u32).encoded_size()
|
||||
inner.encoded_size + codec::Compact(inner.records.len() as u32).encoded_size()
|
||||
}
|
||||
|
||||
/// Convert into a [`StorageProof`].
|
||||
pub fn to_storage_proof(&self) -> StorageProof {
|
||||
let trie_nodes = self.inner.read()
|
||||
let trie_nodes = self
|
||||
.inner
|
||||
.read()
|
||||
.records
|
||||
.iter()
|
||||
.filter_map(|(_k, v)| v.as_ref().map(|v| v.to_vec()))
|
||||
@@ -175,7 +178,7 @@ impl<Hash: std::hash::Hash + Eq> ProofRecorder<Hash> {
|
||||
|
||||
/// Patricia trie-based backend which also tracks all touched storage trie values.
|
||||
/// These can be sent to remote node and used as a proof of execution.
|
||||
pub struct ProvingBackend<'a, S: 'a + TrieBackendStorage<H>, H: 'a + Hasher> (
|
||||
pub struct ProvingBackend<'a, S: 'a + TrieBackendStorage<H>, H: 'a + Hasher>(
|
||||
TrieBackend<ProofRecorderBackend<'a, S, H>, H>,
|
||||
);
|
||||
|
||||
@@ -186,7 +189,8 @@ pub struct ProofRecorderBackend<'a, S: 'a + TrieBackendStorage<H>, H: 'a + Hashe
|
||||
}
|
||||
|
||||
impl<'a, S: 'a + TrieBackendStorage<H>, H: 'a + Hasher> ProvingBackend<'a, S, H>
|
||||
where H::Out: Codec
|
||||
where
|
||||
H::Out: Codec,
|
||||
{
|
||||
/// Create new proving backend.
|
||||
pub fn new(backend: &'a TrieBackend<S, H>) -> Self {
|
||||
@@ -201,10 +205,7 @@ impl<'a, S: 'a + TrieBackendStorage<H>, H: 'a + Hasher> ProvingBackend<'a, S, H>
|
||||
) -> Self {
|
||||
let essence = backend.essence();
|
||||
let root = essence.root().clone();
|
||||
let recorder = ProofRecorderBackend {
|
||||
backend: essence.backend_storage(),
|
||||
proof_recorder,
|
||||
};
|
||||
let recorder = ProofRecorderBackend { backend: essence.backend_storage(), proof_recorder };
|
||||
ProvingBackend(TrieBackend::new(recorder, root))
|
||||
}
|
||||
|
||||
@@ -229,7 +230,7 @@ impl<'a, S: 'a + TrieBackendStorage<H>, H: 'a + Hasher> TrieBackendStorage<H>
|
||||
|
||||
fn get(&self, key: &H::Out, prefix: Prefix) -> Result<Option<DBValue>, String> {
|
||||
if let Some(v) = self.proof_recorder.get(key) {
|
||||
return Ok(v);
|
||||
return Ok(v)
|
||||
}
|
||||
|
||||
let backend_value = self.backend.get(key, prefix)?;
|
||||
@@ -247,10 +248,10 @@ impl<'a, S: 'a + TrieBackendStorage<H>, H: 'a + Hasher> std::fmt::Debug
|
||||
}
|
||||
|
||||
impl<'a, S, H> Backend<H> for ProvingBackend<'a, S, H>
|
||||
where
|
||||
S: 'a + TrieBackendStorage<H>,
|
||||
H: 'a + Hasher,
|
||||
H::Out: Ord + Codec,
|
||||
where
|
||||
S: 'a + TrieBackendStorage<H>,
|
||||
H: 'a + Hasher,
|
||||
H::Out: Ord + Codec,
|
||||
{
|
||||
type Error = String;
|
||||
type Transaction = S::Overlay;
|
||||
@@ -314,7 +315,7 @@ impl<'a, S, H> Backend<H> for ProvingBackend<'a, S, H>
|
||||
prefix: &[u8],
|
||||
f: F,
|
||||
) {
|
||||
self.0.for_child_keys_with_prefix( child_info, prefix, f)
|
||||
self.0.for_child_keys_with_prefix(child_info, prefix, f)
|
||||
}
|
||||
|
||||
fn pairs(&self) -> Vec<(Vec<u8>, Vec<u8>)> {
|
||||
@@ -325,30 +326,32 @@ impl<'a, S, H> Backend<H> for ProvingBackend<'a, S, H>
|
||||
self.0.keys(prefix)
|
||||
}
|
||||
|
||||
fn child_keys(
|
||||
&self,
|
||||
child_info: &ChildInfo,
|
||||
prefix: &[u8],
|
||||
) -> Vec<Vec<u8>> {
|
||||
fn child_keys(&self, child_info: &ChildInfo, prefix: &[u8]) -> Vec<Vec<u8>> {
|
||||
self.0.child_keys(child_info, prefix)
|
||||
}
|
||||
|
||||
fn storage_root<'b>(
|
||||
&self,
|
||||
delta: impl Iterator<Item=(&'b [u8], Option<&'b [u8]>)>,
|
||||
) -> (H::Out, Self::Transaction) where H::Out: Ord {
|
||||
delta: impl Iterator<Item = (&'b [u8], Option<&'b [u8]>)>,
|
||||
) -> (H::Out, Self::Transaction)
|
||||
where
|
||||
H::Out: Ord,
|
||||
{
|
||||
self.0.storage_root(delta)
|
||||
}
|
||||
|
||||
fn child_storage_root<'b>(
|
||||
&self,
|
||||
child_info: &ChildInfo,
|
||||
delta: impl Iterator<Item=(&'b [u8], Option<&'b [u8]>)>,
|
||||
) -> (H::Out, bool, Self::Transaction) where H::Out: Ord {
|
||||
delta: impl Iterator<Item = (&'b [u8], Option<&'b [u8]>)>,
|
||||
) -> (H::Out, bool, Self::Transaction)
|
||||
where
|
||||
H::Out: Ord,
|
||||
{
|
||||
self.0.child_storage_root(child_info, delta)
|
||||
}
|
||||
|
||||
fn register_overlay_stats(&self, _stats: &crate::stats::StateMachineStats) { }
|
||||
fn register_overlay_stats(&self, _stats: &crate::stats::StateMachineStats) {}
|
||||
|
||||
fn usage_info(&self) -> crate::stats::UsageInfo {
|
||||
self.0.usage_info()
|
||||
@@ -375,15 +378,16 @@ where
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::InMemoryBackend;
|
||||
use crate::trie_backend::tests::test_trie;
|
||||
use super::*;
|
||||
use crate::proving_backend::create_proof_check_backend;
|
||||
use sp_trie::PrefixedMemoryDB;
|
||||
use crate::{
|
||||
proving_backend::create_proof_check_backend, trie_backend::tests::test_trie,
|
||||
InMemoryBackend,
|
||||
};
|
||||
use sp_runtime::traits::BlakeTwo256;
|
||||
use sp_trie::PrefixedMemoryDB;
|
||||
|
||||
fn test_proving<'a>(
|
||||
trie_backend: &'a TrieBackend<PrefixedMemoryDB<BlakeTwo256>,BlakeTwo256>,
|
||||
trie_backend: &'a TrieBackend<PrefixedMemoryDB<BlakeTwo256>, BlakeTwo256>,
|
||||
) -> ProvingBackend<'a, PrefixedMemoryDB<BlakeTwo256>, BlakeTwo256> {
|
||||
ProvingBackend::new(trie_backend)
|
||||
}
|
||||
@@ -407,7 +411,7 @@ mod tests {
|
||||
use sp_core::H256;
|
||||
let result = create_proof_check_backend::<BlakeTwo256>(
|
||||
H256::from_low_u64_be(1),
|
||||
StorageProof::empty()
|
||||
StorageProof::empty(),
|
||||
);
|
||||
assert!(result.is_err());
|
||||
}
|
||||
@@ -443,7 +447,8 @@ mod tests {
|
||||
|
||||
let proof = proving.extract_proof();
|
||||
|
||||
let proof_check = create_proof_check_backend::<BlakeTwo256>(in_memory_root.into(), proof).unwrap();
|
||||
let proof_check =
|
||||
create_proof_check_backend::<BlakeTwo256>(in_memory_root.into(), proof).unwrap();
|
||||
assert_eq!(proof_check.storage(&[42]).unwrap().unwrap(), vec![42]);
|
||||
}
|
||||
|
||||
@@ -455,48 +460,38 @@ mod tests {
|
||||
let child_info_2 = &child_info_2;
|
||||
let contents = vec![
|
||||
(None, (0..64).map(|i| (vec![i], Some(vec![i]))).collect()),
|
||||
(Some(child_info_1.clone()),
|
||||
(28..65).map(|i| (vec![i], Some(vec![i]))).collect()),
|
||||
(Some(child_info_2.clone()),
|
||||
(10..15).map(|i| (vec![i], Some(vec![i]))).collect()),
|
||||
(Some(child_info_1.clone()), (28..65).map(|i| (vec![i], Some(vec![i]))).collect()),
|
||||
(Some(child_info_2.clone()), (10..15).map(|i| (vec![i], Some(vec![i]))).collect()),
|
||||
];
|
||||
let in_memory = InMemoryBackend::<BlakeTwo256>::default();
|
||||
let mut in_memory = in_memory.update(contents);
|
||||
let child_storage_keys = vec![child_info_1.to_owned(), child_info_2.to_owned()];
|
||||
let in_memory_root = in_memory.full_storage_root(
|
||||
std::iter::empty(),
|
||||
child_storage_keys.iter().map(|k|(k, std::iter::empty()))
|
||||
).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(child_info_1, &[i]).unwrap().unwrap(),
|
||||
vec![i]
|
||||
));
|
||||
(10..15).for_each(|i| assert_eq!(
|
||||
in_memory.child_storage(child_info_2, &[i]).unwrap().unwrap(),
|
||||
vec![i]
|
||||
));
|
||||
let in_memory_root = in_memory
|
||||
.full_storage_root(
|
||||
std::iter::empty(),
|
||||
child_storage_keys.iter().map(|k| (k, std::iter::empty())),
|
||||
)
|
||||
.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(child_info_1, &[i]).unwrap().unwrap(), vec![i])
|
||||
});
|
||||
(10..15).for_each(|i| {
|
||||
assert_eq!(in_memory.child_storage(child_info_2, &[i]).unwrap().unwrap(), vec![i])
|
||||
});
|
||||
|
||||
let trie = in_memory.as_trie_backend().unwrap();
|
||||
let trie_root = trie.storage_root(std::iter::empty()).0;
|
||||
assert_eq!(in_memory_root, trie_root);
|
||||
(0..64).for_each(|i| assert_eq!(
|
||||
trie.storage(&[i]).unwrap().unwrap(),
|
||||
vec![i]
|
||||
));
|
||||
(0..64).for_each(|i| assert_eq!(trie.storage(&[i]).unwrap().unwrap(), vec![i]));
|
||||
|
||||
let proving = ProvingBackend::new(trie);
|
||||
assert_eq!(proving.storage(&[42]).unwrap().unwrap(), vec![42]);
|
||||
|
||||
let proof = proving.extract_proof();
|
||||
|
||||
let proof_check = create_proof_check_backend::<BlakeTwo256>(
|
||||
in_memory_root.into(),
|
||||
proof
|
||||
).unwrap();
|
||||
let proof_check =
|
||||
create_proof_check_backend::<BlakeTwo256>(in_memory_root.into(), proof).unwrap();
|
||||
assert!(proof_check.storage(&[0]).is_err());
|
||||
assert_eq!(proof_check.storage(&[42]).unwrap().unwrap(), vec![42]);
|
||||
// note that it is include in root because proof close
|
||||
@@ -507,14 +502,9 @@ mod tests {
|
||||
assert_eq!(proving.child_storage(child_info_1, &[64]), Ok(Some(vec![64])));
|
||||
|
||||
let proof = proving.extract_proof();
|
||||
let proof_check = create_proof_check_backend::<BlakeTwo256>(
|
||||
in_memory_root.into(),
|
||||
proof
|
||||
).unwrap();
|
||||
assert_eq!(
|
||||
proof_check.child_storage(child_info_1, &[64]).unwrap().unwrap(),
|
||||
vec![64]
|
||||
);
|
||||
let proof_check =
|
||||
create_proof_check_backend::<BlakeTwo256>(in_memory_root.into(), proof).unwrap();
|
||||
assert_eq!(proof_check.child_storage(child_info_1, &[64]).unwrap().unwrap(), vec![64]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -522,15 +512,14 @@ mod tests {
|
||||
let trie_backend = test_trie();
|
||||
let backend = test_proving(&trie_backend);
|
||||
|
||||
let check_estimation = |backend: &ProvingBackend<'_, PrefixedMemoryDB<BlakeTwo256>, BlakeTwo256>| {
|
||||
let storage_proof = backend.extract_proof();
|
||||
let estimation = backend.0.essence()
|
||||
.backend_storage()
|
||||
.proof_recorder
|
||||
.estimate_encoded_size();
|
||||
let check_estimation =
|
||||
|backend: &ProvingBackend<'_, PrefixedMemoryDB<BlakeTwo256>, BlakeTwo256>| {
|
||||
let storage_proof = backend.extract_proof();
|
||||
let estimation =
|
||||
backend.0.essence().backend_storage().proof_recorder.estimate_encoded_size();
|
||||
|
||||
assert_eq!(storage_proof.encoded_size(), estimation);
|
||||
};
|
||||
assert_eq!(storage_proof.encoded_size(), estimation);
|
||||
};
|
||||
|
||||
assert_eq!(backend.storage(b"key").unwrap(), Some(b"value".to_vec()));
|
||||
check_estimation(&backend);
|
||||
|
||||
@@ -17,17 +17,18 @@
|
||||
|
||||
//! Read-only version of Externalities.
|
||||
|
||||
use std::{
|
||||
any::{TypeId, Any},
|
||||
marker::PhantomData,
|
||||
};
|
||||
use crate::{Backend, StorageKey, StorageValue};
|
||||
use codec::Encode;
|
||||
use hash_db::Hasher;
|
||||
use sp_core::{
|
||||
storage::{ChildInfo, TrackedStorageKey},
|
||||
traits::Externalities, Blake2Hasher,
|
||||
traits::Externalities,
|
||||
Blake2Hasher,
|
||||
};
|
||||
use std::{
|
||||
any::{Any, TypeId},
|
||||
marker::PhantomData,
|
||||
};
|
||||
use codec::Encode;
|
||||
|
||||
/// Trait for inspecting state in any backend.
|
||||
///
|
||||
@@ -79,39 +80,34 @@ impl<'a, H: Hasher, B: 'a + Backend<H>> Externalities for ReadOnlyExternalities<
|
||||
}
|
||||
|
||||
fn storage(&self, key: &[u8]) -> Option<StorageValue> {
|
||||
self.backend.storage(key).expect("Backed failed for storage in ReadOnlyExternalities")
|
||||
self.backend
|
||||
.storage(key)
|
||||
.expect("Backed failed for storage in ReadOnlyExternalities")
|
||||
}
|
||||
|
||||
fn storage_hash(&self, key: &[u8]) -> Option<Vec<u8>> {
|
||||
self.storage(key).map(|v| Blake2Hasher::hash(&v).encode())
|
||||
}
|
||||
|
||||
fn child_storage(
|
||||
&self,
|
||||
child_info: &ChildInfo,
|
||||
key: &[u8],
|
||||
) -> Option<StorageValue> {
|
||||
self.backend.child_storage(child_info, key).expect("Backed failed for child_storage in ReadOnlyExternalities")
|
||||
fn child_storage(&self, child_info: &ChildInfo, key: &[u8]) -> Option<StorageValue> {
|
||||
self.backend
|
||||
.child_storage(child_info, key)
|
||||
.expect("Backed failed for child_storage in ReadOnlyExternalities")
|
||||
}
|
||||
|
||||
fn child_storage_hash(
|
||||
&self,
|
||||
child_info: &ChildInfo,
|
||||
key: &[u8],
|
||||
) -> Option<Vec<u8>> {
|
||||
fn child_storage_hash(&self, child_info: &ChildInfo, key: &[u8]) -> Option<Vec<u8>> {
|
||||
self.child_storage(child_info, key).map(|v| Blake2Hasher::hash(&v).encode())
|
||||
}
|
||||
|
||||
fn next_storage_key(&self, key: &[u8]) -> Option<StorageKey> {
|
||||
self.backend.next_storage_key(key).expect("Backed failed for next_storage_key in ReadOnlyExternalities")
|
||||
self.backend
|
||||
.next_storage_key(key)
|
||||
.expect("Backed failed for next_storage_key in ReadOnlyExternalities")
|
||||
}
|
||||
|
||||
fn next_child_storage_key(
|
||||
&self,
|
||||
child_info: &ChildInfo,
|
||||
key: &[u8],
|
||||
) -> Option<StorageKey> {
|
||||
self.backend.next_child_storage_key(child_info, key)
|
||||
fn next_child_storage_key(&self, child_info: &ChildInfo, key: &[u8]) -> Option<StorageKey> {
|
||||
self.backend
|
||||
.next_child_storage_key(child_info, key)
|
||||
.expect("Backed failed for next_child_storage_key in ReadOnlyExternalities")
|
||||
}
|
||||
|
||||
@@ -128,11 +124,7 @@ impl<'a, H: Hasher, B: 'a + Backend<H>> Externalities for ReadOnlyExternalities<
|
||||
unimplemented!("place_child_storage not supported in ReadOnlyExternalities")
|
||||
}
|
||||
|
||||
fn kill_child_storage(
|
||||
&mut self,
|
||||
_child_info: &ChildInfo,
|
||||
_limit: Option<u32>,
|
||||
) -> (bool, u32) {
|
||||
fn kill_child_storage(&mut self, _child_info: &ChildInfo, _limit: Option<u32>) -> (bool, u32) {
|
||||
unimplemented!("kill_child_storage is not supported in ReadOnlyExternalities")
|
||||
}
|
||||
|
||||
@@ -149,11 +141,7 @@ impl<'a, H: Hasher, B: 'a + Backend<H>> Externalities for ReadOnlyExternalities<
|
||||
unimplemented!("clear_child_prefix is not supported in ReadOnlyExternalities")
|
||||
}
|
||||
|
||||
fn storage_append(
|
||||
&mut self,
|
||||
_key: Vec<u8>,
|
||||
_value: Vec<u8>,
|
||||
) {
|
||||
fn storage_append(&mut self, _key: Vec<u8>, _value: Vec<u8>) {
|
||||
unimplemented!("storage_append is not supported in ReadOnlyExternalities")
|
||||
}
|
||||
|
||||
@@ -161,10 +149,7 @@ impl<'a, H: Hasher, B: 'a + Backend<H>> Externalities for ReadOnlyExternalities<
|
||||
unimplemented!("storage_root is not supported in ReadOnlyExternalities")
|
||||
}
|
||||
|
||||
fn child_storage_root(
|
||||
&mut self,
|
||||
_child_info: &ChildInfo,
|
||||
) -> Vec<u8> {
|
||||
fn child_storage_root(&mut self, _child_info: &ChildInfo) -> Vec<u8> {
|
||||
unimplemented!("child_storage_root is not supported in ReadOnlyExternalities")
|
||||
}
|
||||
|
||||
@@ -209,7 +194,9 @@ impl<'a, H: Hasher, B: 'a + Backend<H>> Externalities for ReadOnlyExternalities<
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, H: Hasher, B: 'a + Backend<H>> sp_externalities::ExtensionStore for ReadOnlyExternalities<'a, H, B> {
|
||||
impl<'a, H: Hasher, B: 'a + Backend<H>> sp_externalities::ExtensionStore
|
||||
for ReadOnlyExternalities<'a, H, B>
|
||||
{
|
||||
fn extension_by_type_id(&mut self, _type_id: TypeId) -> Option<&mut dyn Any> {
|
||||
unimplemented!("extension_by_type_id is not supported in ReadOnlyExternalities")
|
||||
}
|
||||
@@ -222,7 +209,10 @@ impl<'a, H: Hasher, B: 'a + Backend<H>> sp_externalities::ExtensionStore for Rea
|
||||
unimplemented!("register_extension_with_type_id is not supported in ReadOnlyExternalities")
|
||||
}
|
||||
|
||||
fn deregister_extension_by_type_id(&mut self, _type_id: TypeId) -> Result<(), sp_externalities::Error> {
|
||||
fn deregister_extension_by_type_id(
|
||||
&mut self,
|
||||
_type_id: TypeId,
|
||||
) -> Result<(), sp_externalities::Error> {
|
||||
unimplemented!("deregister_extension_by_type_id is not supported in ReadOnlyExternalities")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,9 +17,9 @@
|
||||
|
||||
//! Usage statistics for state db
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
use std::time::{Instant, Duration};
|
||||
use sp_std::cell::RefCell;
|
||||
#[cfg(feature = "std")]
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
/// Measured count of operations and total bytes.
|
||||
#[derive(Clone, Debug, Default)]
|
||||
|
||||
@@ -17,17 +17,19 @@
|
||||
|
||||
//! Test implementation for Externalities.
|
||||
|
||||
use std::{any::{Any, TypeId}, panic::{AssertUnwindSafe, UnwindSafe}};
|
||||
use std::{
|
||||
any::{Any, TypeId},
|
||||
panic::{AssertUnwindSafe, UnwindSafe},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
backend::Backend, OverlayedChanges, StorageTransactionCache, ext::Ext, InMemoryBackend,
|
||||
StorageKey, StorageValue,
|
||||
backend::Backend,
|
||||
changes_trie::{
|
||||
Configuration as ChangesTrieConfiguration,
|
||||
InMemoryStorage as ChangesTrieInMemoryStorage,
|
||||
BlockNumber as ChangesTrieBlockNumber,
|
||||
State as ChangesTrieState,
|
||||
BlockNumber as ChangesTrieBlockNumber, Configuration as ChangesTrieConfiguration,
|
||||
InMemoryStorage as ChangesTrieInMemoryStorage, State as ChangesTrieState,
|
||||
},
|
||||
ext::Ext,
|
||||
InMemoryBackend, OverlayedChanges, StorageKey, StorageTransactionCache, StorageValue,
|
||||
};
|
||||
|
||||
use codec::Decode;
|
||||
@@ -35,13 +37,13 @@ use hash_db::Hasher;
|
||||
use sp_core::{
|
||||
offchain::testing::TestPersistentOffchainDB,
|
||||
storage::{
|
||||
well_known_keys::{CHANGES_TRIE_CONFIG, CODE, is_child_storage_key},
|
||||
well_known_keys::{is_child_storage_key, CHANGES_TRIE_CONFIG, CODE},
|
||||
Storage,
|
||||
},
|
||||
traits::TaskExecutorExt,
|
||||
testing::TaskExecutor,
|
||||
traits::TaskExecutorExt,
|
||||
};
|
||||
use sp_externalities::{Extensions, Extension, ExtensionStore};
|
||||
use sp_externalities::{Extension, ExtensionStore, Extensions};
|
||||
|
||||
/// Simple HashMap-based Externalities impl.
|
||||
pub struct TestExternalities<H: Hasher, N: ChangesTrieBlockNumber = u64>
|
||||
@@ -96,7 +98,9 @@ where
|
||||
/// Create a new instance of `TestExternalities` with code and storage.
|
||||
pub fn new_with_code(code: &[u8], mut storage: Storage) -> Self {
|
||||
let mut overlay = OverlayedChanges::default();
|
||||
let changes_trie_config = storage.top.get(CHANGES_TRIE_CONFIG)
|
||||
let changes_trie_config = storage
|
||||
.top
|
||||
.get(CHANGES_TRIE_CONFIG)
|
||||
.and_then(|v| Decode::decode(&mut &v[..]).ok());
|
||||
overlay.set_collect_extrinsics(changes_trie_config.is_some());
|
||||
|
||||
@@ -156,17 +160,14 @@ where
|
||||
/// In contrast to [`commit_all`](Self::commit_all) this will not panic if there are open
|
||||
/// transactions.
|
||||
fn as_backend(&self) -> InMemoryBackend<H> {
|
||||
let top: Vec<_> = self.overlay.changes()
|
||||
.map(|(k, v)| (k.clone(), v.value().cloned()))
|
||||
.collect();
|
||||
let top: Vec<_> =
|
||||
self.overlay.changes().map(|(k, v)| (k.clone(), v.value().cloned())).collect();
|
||||
let mut transaction = vec![(None, top)];
|
||||
|
||||
for (child_changes, child_info) in self.overlay.children() {
|
||||
transaction.push((
|
||||
Some(child_info.clone()),
|
||||
child_changes
|
||||
.map(|(k, v)| (k.clone(), v.value().cloned()))
|
||||
.collect(),
|
||||
child_changes.map(|(k, v)| (k.clone(), v.value().cloned())).collect(),
|
||||
))
|
||||
}
|
||||
|
||||
@@ -186,7 +187,8 @@ where
|
||||
&mut Default::default(),
|
||||
)?;
|
||||
|
||||
self.backend.apply_transaction(changes.transaction_storage_root, changes.transaction);
|
||||
self.backend
|
||||
.apply_transaction(changes.transaction_storage_root, changes.transaction);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -202,18 +204,21 @@ where
|
||||
///
|
||||
/// Returns the result of the given closure, if no panics occured.
|
||||
/// Otherwise, returns `Err`.
|
||||
pub fn execute_with_safe<R>(&mut self, f: impl FnOnce() -> R + UnwindSafe) -> Result<R, String> {
|
||||
pub fn execute_with_safe<R>(
|
||||
&mut self,
|
||||
f: impl FnOnce() -> R + UnwindSafe,
|
||||
) -> Result<R, String> {
|
||||
let mut ext = AssertUnwindSafe(self.ext());
|
||||
std::panic::catch_unwind(move ||
|
||||
std::panic::catch_unwind(move || {
|
||||
sp_externalities::set_and_run_with_externalities(&mut *ext, f)
|
||||
).map_err(|e| {
|
||||
format!("Closure panicked: {:?}", e)
|
||||
})
|
||||
.map_err(|e| format!("Closure panicked: {:?}", e))
|
||||
}
|
||||
}
|
||||
|
||||
impl<H: Hasher, N: ChangesTrieBlockNumber> std::fmt::Debug for TestExternalities<H, N>
|
||||
where H::Out: Ord + codec::Codec,
|
||||
where
|
||||
H::Out: Ord + codec::Codec,
|
||||
{
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
write!(f, "overlay: {:?}\nbackend: {:?}", self.overlay, self.backend.pairs())
|
||||
@@ -221,8 +226,8 @@ impl<H: Hasher, N: ChangesTrieBlockNumber> std::fmt::Debug for TestExternalities
|
||||
}
|
||||
|
||||
impl<H: Hasher, N: ChangesTrieBlockNumber> PartialEq for TestExternalities<H, N>
|
||||
where
|
||||
H::Out: Ord + 'static + codec::Codec
|
||||
where
|
||||
H::Out: Ord + 'static + codec::Codec,
|
||||
{
|
||||
/// This doesn't test if they are in the same state, only if they contains the
|
||||
/// same data at this state
|
||||
@@ -232,22 +237,25 @@ impl<H: Hasher, N: ChangesTrieBlockNumber> PartialEq for TestExternalities<H, N>
|
||||
}
|
||||
|
||||
impl<H: Hasher, N: ChangesTrieBlockNumber> Default for TestExternalities<H, N>
|
||||
where
|
||||
H::Out: Ord + 'static + codec::Codec,
|
||||
where
|
||||
H::Out: Ord + 'static + codec::Codec,
|
||||
{
|
||||
fn default() -> Self { Self::new(Default::default()) }
|
||||
fn default() -> Self {
|
||||
Self::new(Default::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl<H: Hasher, N: ChangesTrieBlockNumber> From<Storage> for TestExternalities<H, N>
|
||||
where
|
||||
H::Out: Ord + 'static + codec::Codec,
|
||||
where
|
||||
H::Out: Ord + 'static + codec::Codec,
|
||||
{
|
||||
fn from(storage: Storage) -> Self {
|
||||
Self::new(storage)
|
||||
}
|
||||
}
|
||||
|
||||
impl<H, N> sp_externalities::ExtensionStore for TestExternalities<H, N> where
|
||||
impl<H, N> sp_externalities::ExtensionStore for TestExternalities<H, N>
|
||||
where
|
||||
H: Hasher,
|
||||
H::Out: Ord + codec::Codec,
|
||||
N: ChangesTrieBlockNumber,
|
||||
@@ -264,7 +272,10 @@ impl<H, N> sp_externalities::ExtensionStore for TestExternalities<H, N> where
|
||||
self.extensions.register_with_type_id(type_id, extension)
|
||||
}
|
||||
|
||||
fn deregister_extension_by_type_id(&mut self, type_id: TypeId) -> Result<(), sp_externalities::Error> {
|
||||
fn deregister_extension_by_type_id(
|
||||
&mut self,
|
||||
type_id: TypeId,
|
||||
) -> Result<(), sp_externalities::Error> {
|
||||
if self.extensions.deregister(type_id) {
|
||||
Ok(())
|
||||
} else {
|
||||
@@ -274,14 +285,13 @@ impl<H, N> sp_externalities::ExtensionStore for TestExternalities<H, N> where
|
||||
}
|
||||
|
||||
impl<H, N> sp_externalities::ExternalitiesExt for TestExternalities<H, N>
|
||||
where
|
||||
H: Hasher,
|
||||
H::Out: Ord + codec::Codec,
|
||||
N: ChangesTrieBlockNumber,
|
||||
where
|
||||
H: Hasher,
|
||||
H::Out: Ord + codec::Codec,
|
||||
N: ChangesTrieBlockNumber,
|
||||
{
|
||||
fn extension<T: Any + Extension>(&mut self) -> Option<&mut T> {
|
||||
self.extension_by_type_id(TypeId::of::<T>())
|
||||
.and_then(<dyn Any>::downcast_mut)
|
||||
self.extension_by_type_id(TypeId::of::<T>()).and_then(<dyn Any>::downcast_mut)
|
||||
}
|
||||
|
||||
fn register_extension<T: Extension>(&mut self, ext: T) -> Result<(), sp_externalities::Error> {
|
||||
@@ -296,9 +306,9 @@ impl<H, N> sp_externalities::ExternalitiesExt for TestExternalities<H, N>
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use sp_core::{H256, traits::Externalities, storage::ChildInfo};
|
||||
use sp_runtime::traits::BlakeTwo256;
|
||||
use hex_literal::hex;
|
||||
use sp_core::{storage::ChildInfo, traits::Externalities, H256};
|
||||
use sp_runtime::traits::BlakeTwo256;
|
||||
|
||||
#[test]
|
||||
fn commit_should_work() {
|
||||
@@ -307,7 +317,8 @@ mod tests {
|
||||
ext.set_storage(b"doe".to_vec(), b"reindeer".to_vec());
|
||||
ext.set_storage(b"dog".to_vec(), b"puppy".to_vec());
|
||||
ext.set_storage(b"dogglesworth".to_vec(), b"cat".to_vec());
|
||||
let root = H256::from(hex!("ed4d8c799d996add422395a6abd7545491d40bd838d738afafa1b8a4de625489"));
|
||||
let root =
|
||||
H256::from(hex!("ed4d8c799d996add422395a6abd7545491d40bd838d738afafa1b8a4de625489"));
|
||||
assert_eq!(H256::from_slice(ext.storage_root().as_slice()), root);
|
||||
}
|
||||
|
||||
@@ -325,7 +336,7 @@ mod tests {
|
||||
#[test]
|
||||
fn check_send() {
|
||||
fn assert_send<T: Send>() {}
|
||||
assert_send::<TestExternalities::<BlakeTwo256, u64>>();
|
||||
assert_send::<TestExternalities<BlakeTwo256, u64>>();
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -17,29 +17,33 @@
|
||||
|
||||
//! Trie-based state machine backend.
|
||||
|
||||
use crate::{warn, debug};
|
||||
use hash_db::Hasher;
|
||||
use sp_trie::{Trie, delta_trie_root, empty_child_trie_root, child_delta_trie_root};
|
||||
use sp_trie::trie_types::{TrieDB, TrieError, Layout};
|
||||
use sp_core::storage::{ChildInfo, ChildType};
|
||||
use codec::{Codec, Decode};
|
||||
use crate::{
|
||||
StorageKey, StorageValue, Backend,
|
||||
trie_backend_essence::{TrieBackendEssence, TrieBackendStorage, Ephemeral},
|
||||
debug,
|
||||
trie_backend_essence::{Ephemeral, TrieBackendEssence, TrieBackendStorage},
|
||||
warn, Backend, StorageKey, StorageValue,
|
||||
};
|
||||
use codec::{Codec, Decode};
|
||||
use hash_db::Hasher;
|
||||
use sp_core::storage::{ChildInfo, ChildType};
|
||||
use sp_std::{boxed::Box, vec::Vec};
|
||||
use sp_trie::{
|
||||
child_delta_trie_root, delta_trie_root, empty_child_trie_root,
|
||||
trie_types::{Layout, TrieDB, TrieError},
|
||||
Trie,
|
||||
};
|
||||
|
||||
/// Patricia trie-based backend. Transaction type is an overlay of changes to commit.
|
||||
pub struct TrieBackend<S: TrieBackendStorage<H>, H: Hasher> {
|
||||
pub (crate) essence: TrieBackendEssence<S, H>,
|
||||
pub(crate) essence: TrieBackendEssence<S, H>,
|
||||
}
|
||||
|
||||
impl<S: TrieBackendStorage<H>, H: Hasher> TrieBackend<S, H> where H::Out: Codec {
|
||||
impl<S: TrieBackendStorage<H>, H: Hasher> TrieBackend<S, H>
|
||||
where
|
||||
H::Out: Codec,
|
||||
{
|
||||
/// Create new trie-based backend.
|
||||
pub fn new(storage: S, root: H::Out) -> Self {
|
||||
TrieBackend {
|
||||
essence: TrieBackendEssence::new(storage, root),
|
||||
}
|
||||
TrieBackend { essence: TrieBackendEssence::new(storage, root) }
|
||||
}
|
||||
|
||||
/// Get backend essence reference.
|
||||
@@ -74,7 +78,8 @@ impl<S: TrieBackendStorage<H>, H: Hasher> sp_std::fmt::Debug for TrieBackend<S,
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: TrieBackendStorage<H>, H: Hasher> Backend<H> for TrieBackend<S, H> where
|
||||
impl<S: TrieBackendStorage<H>, H: Hasher> Backend<H> for TrieBackend<S, H>
|
||||
where
|
||||
H::Out: Ord + Codec,
|
||||
{
|
||||
type Error = crate::DefaultError;
|
||||
@@ -121,7 +126,8 @@ impl<S: TrieBackendStorage<H>, H: Hasher> Backend<H> for TrieBackend<S, H> where
|
||||
f: F,
|
||||
allow_missing: bool,
|
||||
) -> Result<bool, Self::Error> {
|
||||
self.essence.apply_to_key_values_while(child_info, prefix, start_at, f, allow_missing)
|
||||
self.essence
|
||||
.apply_to_key_values_while(child_info, prefix, start_at, f, allow_missing)
|
||||
}
|
||||
|
||||
fn apply_to_keys_while<F: FnMut(&[u8]) -> bool>(
|
||||
@@ -159,7 +165,7 @@ impl<S: TrieBackendStorage<H>, H: Hasher> Backend<H> for TrieBackend<S, H> where
|
||||
Err(e) => {
|
||||
debug!(target: "trie", "Error extracting trie values: {}", e);
|
||||
Vec::new()
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,21 +183,23 @@ impl<S: TrieBackendStorage<H>, H: Hasher> Backend<H> for TrieBackend<S, H> where
|
||||
Ok(v)
|
||||
};
|
||||
|
||||
collect_all().map_err(|e| debug!(target: "trie", "Error extracting trie keys: {}", e)).unwrap_or_default()
|
||||
collect_all()
|
||||
.map_err(|e| debug!(target: "trie", "Error extracting trie keys: {}", e))
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
fn storage_root<'a>(
|
||||
&self,
|
||||
delta: impl Iterator<Item=(&'a [u8], Option<&'a [u8]>)>,
|
||||
) -> (H::Out, Self::Transaction) where H::Out: Ord {
|
||||
delta: impl Iterator<Item = (&'a [u8], Option<&'a [u8]>)>,
|
||||
) -> (H::Out, Self::Transaction)
|
||||
where
|
||||
H::Out: Ord,
|
||||
{
|
||||
let mut write_overlay = S::Overlay::default();
|
||||
let mut root = *self.essence.root();
|
||||
|
||||
{
|
||||
let mut eph = Ephemeral::new(
|
||||
self.essence.backend_storage(),
|
||||
&mut write_overlay,
|
||||
);
|
||||
let mut eph = Ephemeral::new(self.essence.backend_storage(), &mut write_overlay);
|
||||
|
||||
match delta_trie_root::<Layout<H>, _, _, _, _, _>(&mut eph, root, delta) {
|
||||
Ok(ret) => root = ret,
|
||||
@@ -205,17 +213,21 @@ impl<S: TrieBackendStorage<H>, H: Hasher> Backend<H> for TrieBackend<S, H> where
|
||||
fn child_storage_root<'a>(
|
||||
&self,
|
||||
child_info: &ChildInfo,
|
||||
delta: impl Iterator<Item=(&'a [u8], Option<&'a [u8]>)>,
|
||||
) -> (H::Out, bool, Self::Transaction) where H::Out: Ord {
|
||||
delta: impl Iterator<Item = (&'a [u8], Option<&'a [u8]>)>,
|
||||
) -> (H::Out, bool, Self::Transaction)
|
||||
where
|
||||
H::Out: Ord,
|
||||
{
|
||||
let default_root = match child_info.child_type() {
|
||||
ChildType::ParentKeyId => empty_child_trie_root::<Layout<H>>()
|
||||
ChildType::ParentKeyId => empty_child_trie_root::<Layout<H>>(),
|
||||
};
|
||||
|
||||
let mut write_overlay = S::Overlay::default();
|
||||
let prefixed_storage_key = child_info.prefixed_storage_key();
|
||||
let mut root = match self.storage(prefixed_storage_key.as_slice()) {
|
||||
Ok(value) =>
|
||||
value.and_then(|r| Decode::decode(&mut &r[..]).ok()).unwrap_or_else(|| default_root.clone()),
|
||||
Ok(value) => value
|
||||
.and_then(|r| Decode::decode(&mut &r[..]).ok())
|
||||
.unwrap_or_else(|| default_root.clone()),
|
||||
Err(e) => {
|
||||
warn!(target: "trie", "Failed to read child storage root: {}", e);
|
||||
default_root.clone()
|
||||
@@ -223,10 +235,7 @@ impl<S: TrieBackendStorage<H>, H: Hasher> Backend<H> for TrieBackend<S, H> where
|
||||
};
|
||||
|
||||
{
|
||||
let mut eph = Ephemeral::new(
|
||||
self.essence.backend_storage(),
|
||||
&mut write_overlay,
|
||||
);
|
||||
let mut eph = Ephemeral::new(self.essence.backend_storage(), &mut write_overlay);
|
||||
|
||||
match child_delta_trie_root::<Layout<H>, _, _, _, _, _, _>(
|
||||
child_info.keyspace(),
|
||||
@@ -248,7 +257,7 @@ impl<S: TrieBackendStorage<H>, H: Hasher> Backend<H> for TrieBackend<S, H> where
|
||||
Some(self)
|
||||
}
|
||||
|
||||
fn register_overlay_stats(&self, _stats: &crate::stats::StateMachineStats) { }
|
||||
fn register_overlay_stats(&self, _stats: &crate::stats::StateMachineStats) {}
|
||||
|
||||
fn usage_info(&self) -> crate::UsageInfo {
|
||||
crate::UsageInfo::empty()
|
||||
@@ -261,12 +270,12 @@ impl<S: TrieBackendStorage<H>, H: Hasher> Backend<H> for TrieBackend<S, H> where
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod tests {
|
||||
use std::{collections::HashSet, iter};
|
||||
use sp_core::H256;
|
||||
use codec::Encode;
|
||||
use sp_trie::{TrieMut, PrefixedMemoryDB, trie_types::TrieDBMut, KeySpacedDBMut};
|
||||
use sp_runtime::traits::BlakeTwo256;
|
||||
use super::*;
|
||||
use codec::Encode;
|
||||
use sp_core::H256;
|
||||
use sp_runtime::traits::BlakeTwo256;
|
||||
use sp_trie::{trie_types::TrieDBMut, KeySpacedDBMut, PrefixedMemoryDB, TrieMut};
|
||||
use std::{collections::HashSet, iter};
|
||||
|
||||
const CHILD_KEY_1: &[u8] = b"sub1";
|
||||
|
||||
@@ -312,7 +321,9 @@ pub mod tests {
|
||||
fn read_from_child_storage_returns_some() {
|
||||
let test_trie = test_trie();
|
||||
assert_eq!(
|
||||
test_trie.child_storage(&ChildInfo::new_default(CHILD_KEY_1), b"value3").unwrap(),
|
||||
test_trie
|
||||
.child_storage(&ChildInfo::new_default(CHILD_KEY_1), b"value3")
|
||||
.unwrap(),
|
||||
Some(vec![142u8]),
|
||||
);
|
||||
}
|
||||
@@ -332,7 +343,9 @@ pub mod tests {
|
||||
assert!(TrieBackend::<PrefixedMemoryDB<BlakeTwo256>, BlakeTwo256>::new(
|
||||
PrefixedMemoryDB::default(),
|
||||
Default::default(),
|
||||
).pairs().is_empty());
|
||||
)
|
||||
.pairs()
|
||||
.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -347,9 +360,8 @@ pub mod tests {
|
||||
|
||||
#[test]
|
||||
fn storage_root_transaction_is_non_empty() {
|
||||
let (new_root, mut tx) = test_trie().storage_root(
|
||||
iter::once((&b"new-key"[..], Some(&b"new-value"[..]))),
|
||||
);
|
||||
let (new_root, mut tx) =
|
||||
test_trie().storage_root(iter::once((&b"new-key"[..], Some(&b"new-value"[..]))));
|
||||
assert!(!tx.drain().is_empty());
|
||||
assert!(new_root != test_trie().storage_root(iter::empty()).0);
|
||||
}
|
||||
|
||||
@@ -18,24 +18,24 @@
|
||||
//! Trie-based state machine backend essence used to read values
|
||||
//! from storage.
|
||||
|
||||
use crate::{backend::Consolidate, debug, warn, StorageKey, StorageValue};
|
||||
use codec::Encode;
|
||||
use hash_db::{self, Hasher, Prefix};
|
||||
use sp_core::storage::ChildInfo;
|
||||
use sp_std::{boxed::Box, ops::Deref, vec::Vec};
|
||||
use sp_trie::{
|
||||
empty_child_trie_root, read_child_trie_value, read_trie_value,
|
||||
trie_types::{Layout, TrieDB, TrieError},
|
||||
DBValue, KeySpacedDB, MemoryDB, PrefixedMemoryDB, Trie, TrieDBIterator,
|
||||
};
|
||||
#[cfg(feature = "std")]
|
||||
use std::sync::Arc;
|
||||
use sp_std::{ops::Deref, boxed::Box, vec::Vec};
|
||||
use crate::{warn, debug};
|
||||
use hash_db::{self, Hasher, Prefix};
|
||||
use sp_trie::{Trie, MemoryDB, PrefixedMemoryDB, DBValue,
|
||||
empty_child_trie_root, read_trie_value, read_child_trie_value,
|
||||
KeySpacedDB, TrieDBIterator};
|
||||
use sp_trie::trie_types::{TrieDB, TrieError, Layout};
|
||||
use crate::{backend::Consolidate, StorageKey, StorageValue};
|
||||
use sp_core::storage::ChildInfo;
|
||||
use codec::Encode;
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
macro_rules! format {
|
||||
($($arg:tt)+) => (
|
||||
($($arg:tt)+) => {
|
||||
crate::DefaultError
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
type Result<V> = sp_std::result::Result<V, crate::DefaultError>;
|
||||
@@ -53,14 +53,13 @@ pub struct TrieBackendEssence<S: TrieBackendStorage<H>, H: Hasher> {
|
||||
empty: H::Out,
|
||||
}
|
||||
|
||||
impl<S: TrieBackendStorage<H>, H: Hasher> TrieBackendEssence<S, H> where H::Out: Encode {
|
||||
impl<S: TrieBackendStorage<H>, H: Hasher> TrieBackendEssence<S, H>
|
||||
where
|
||||
H::Out: Encode,
|
||||
{
|
||||
/// Create new trie-based backend.
|
||||
pub fn new(storage: S, root: H::Out) -> Self {
|
||||
TrieBackendEssence {
|
||||
storage,
|
||||
root,
|
||||
empty: H::hash(&[0u8]),
|
||||
}
|
||||
TrieBackendEssence { storage, root, empty: H::hash(&[0u8]) }
|
||||
}
|
||||
|
||||
/// Get backend storage reference.
|
||||
@@ -114,7 +113,7 @@ impl<S: TrieBackendStorage<H>, H: Hasher> TrieBackendEssence<S, H> where H::Out:
|
||||
let mut hash = H::Out::default();
|
||||
|
||||
if child_root.len() != hash.as_ref().len() {
|
||||
return Err(format!("Invalid child storage hash at {:?}", child_info.storage_key()));
|
||||
return Err(format!("Invalid child storage hash at {:?}", child_info.storage_key()))
|
||||
}
|
||||
// note: child_root and hash must be same size, panics otherwise.
|
||||
hash.as_mut().copy_from_slice(&child_root[..]);
|
||||
@@ -138,10 +137,9 @@ impl<S: TrieBackendStorage<H>, H: Hasher> TrieBackendEssence<S, H> where H::Out:
|
||||
dyn_eph = self;
|
||||
}
|
||||
|
||||
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))?;
|
||||
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))?;
|
||||
|
||||
// The key just after the one given in input, basically `key++0`.
|
||||
// Note: We are sure this is the next key if:
|
||||
@@ -157,8 +155,8 @@ impl<S: TrieBackendStorage<H>, H: Hasher> TrieBackendEssence<S, H> where H::Out:
|
||||
let next_element = iter.next();
|
||||
|
||||
let next_key = if let Some(next_element) = next_element {
|
||||
let (next_key, _) = next_element
|
||||
.map_err(|e| format!("TrieDB iterator next error: {}", e))?;
|
||||
let (next_key, _) =
|
||||
next_element.map_err(|e| format!("TrieDB iterator next error: {}", e))?;
|
||||
Some(next_key)
|
||||
} else {
|
||||
None
|
||||
@@ -180,7 +178,8 @@ impl<S: TrieBackendStorage<H>, H: Hasher> TrieBackendEssence<S, H> where H::Out:
|
||||
child_info: &ChildInfo,
|
||||
key: &[u8],
|
||||
) -> Result<Option<StorageValue>> {
|
||||
let root = self.child_root(child_info)?
|
||||
let root = self
|
||||
.child_root(child_info)?
|
||||
.unwrap_or_else(|| empty_child_trie_root::<Layout<H>>().encode());
|
||||
|
||||
let map_e = |e| format!("Trie lookup error: {}", e);
|
||||
@@ -210,20 +209,13 @@ impl<S: TrieBackendStorage<H>, H: Hasher> TrieBackendEssence<S, H> where H::Out:
|
||||
|
||||
&child_root
|
||||
} else {
|
||||
return Ok(true);
|
||||
return Ok(true)
|
||||
}
|
||||
} else {
|
||||
&self.root
|
||||
};
|
||||
|
||||
self.trie_iter_inner(
|
||||
&root,
|
||||
prefix,
|
||||
f,
|
||||
child_info,
|
||||
start_at,
|
||||
allow_missing_nodes,
|
||||
)
|
||||
self.trie_iter_inner(&root, prefix, f, child_info, start_at, allow_missing_nodes)
|
||||
}
|
||||
|
||||
/// Retrieve all entries keys of a storage and call `f` for each of those keys.
|
||||
@@ -240,8 +232,8 @@ impl<S: TrieBackendStorage<H>, H: Hasher> TrieBackendEssence<S, H> where H::Out:
|
||||
Ok(v) => v.unwrap_or_else(|| empty_child_trie_root::<Layout<H>>().encode()),
|
||||
Err(e) => {
|
||||
debug!(target: "trie", "Error while iterating child storage: {}", e);
|
||||
return;
|
||||
}
|
||||
return
|
||||
},
|
||||
};
|
||||
child_root.as_mut().copy_from_slice(&root_vec);
|
||||
&child_root
|
||||
@@ -249,7 +241,17 @@ impl<S: TrieBackendStorage<H>, H: Hasher> TrieBackendEssence<S, H> where H::Out:
|
||||
&self.root
|
||||
};
|
||||
|
||||
let _ = self.trie_iter_inner(root, prefix, |k, _v| { f(&k); true}, child_info, None, false);
|
||||
let _ = self.trie_iter_inner(
|
||||
root,
|
||||
prefix,
|
||||
|k, _v| {
|
||||
f(&k);
|
||||
true
|
||||
},
|
||||
child_info,
|
||||
None,
|
||||
false,
|
||||
);
|
||||
}
|
||||
|
||||
/// Execute given closure for all keys starting with prefix.
|
||||
@@ -263,17 +265,37 @@ impl<S: TrieBackendStorage<H>, H: Hasher> TrieBackendEssence<S, H> where H::Out:
|
||||
Ok(v) => v.unwrap_or_else(|| empty_child_trie_root::<Layout<H>>().encode()),
|
||||
Err(e) => {
|
||||
debug!(target: "trie", "Error while iterating child storage: {}", e);
|
||||
return;
|
||||
}
|
||||
return
|
||||
},
|
||||
};
|
||||
let mut root = H::Out::default();
|
||||
root.as_mut().copy_from_slice(&root_vec);
|
||||
let _ = self.trie_iter_inner(&root, Some(prefix), |k, _v| { f(&k); true }, Some(child_info), None, false);
|
||||
let _ = self.trie_iter_inner(
|
||||
&root,
|
||||
Some(prefix),
|
||||
|k, _v| {
|
||||
f(&k);
|
||||
true
|
||||
},
|
||||
Some(child_info),
|
||||
None,
|
||||
false,
|
||||
);
|
||||
}
|
||||
|
||||
/// Execute given closure for all keys starting with prefix.
|
||||
pub fn for_keys_with_prefix<F: FnMut(&[u8])>(&self, prefix: &[u8], mut f: F) {
|
||||
let _ = self.trie_iter_inner(&self.root, Some(prefix), |k, _v| { f(&k); true }, None, None, false);
|
||||
let _ = self.trie_iter_inner(
|
||||
&self.root,
|
||||
Some(prefix),
|
||||
|k, _v| {
|
||||
f(&k);
|
||||
true
|
||||
},
|
||||
None,
|
||||
None,
|
||||
false,
|
||||
);
|
||||
}
|
||||
|
||||
fn trie_iter_inner<F: FnMut(Vec<u8>, Vec<u8>) -> bool>(
|
||||
@@ -315,14 +337,25 @@ impl<S: TrieBackendStorage<H>, H: Hasher> TrieBackendEssence<S, H> where H::Out:
|
||||
};
|
||||
match result {
|
||||
Ok(completed) => Ok(completed),
|
||||
Err(e) if matches!(*e, TrieError::IncompleteDatabase(_)) && allow_missing_nodes => Ok(false),
|
||||
Err(e) if matches!(*e, TrieError::IncompleteDatabase(_)) && allow_missing_nodes =>
|
||||
Ok(false),
|
||||
Err(e) => Err(format!("TrieDB iteration error: {}", 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], mut f: F) {
|
||||
let _ = self.trie_iter_inner(&self.root, Some(prefix), |k, v| {f(&k, &v); true}, None, None, false);
|
||||
let _ = self.trie_iter_inner(
|
||||
&self.root,
|
||||
Some(prefix),
|
||||
|k, v| {
|
||||
f(&k, &v);
|
||||
true
|
||||
},
|
||||
None,
|
||||
None,
|
||||
false,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -334,16 +367,17 @@ pub(crate) struct Ephemeral<'a, S: 'a + TrieBackendStorage<H>, H: 'a + Hasher> {
|
||||
impl<'a, S: 'a + TrieBackendStorage<H>, H: 'a + Hasher> hash_db::AsHashDB<H, DBValue>
|
||||
for Ephemeral<'a, S, H>
|
||||
{
|
||||
fn as_hash_db<'b>(&'b self) -> &'b (dyn hash_db::HashDB<H, DBValue> + 'b) { self }
|
||||
fn as_hash_db_mut<'b>(&'b mut self) -> &'b mut (dyn hash_db::HashDB<H, DBValue> + 'b) { self }
|
||||
fn as_hash_db<'b>(&'b self) -> &'b (dyn hash_db::HashDB<H, DBValue> + 'b) {
|
||||
self
|
||||
}
|
||||
fn as_hash_db_mut<'b>(&'b mut self) -> &'b mut (dyn hash_db::HashDB<H, DBValue> + 'b) {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, S: TrieBackendStorage<H>, H: Hasher> Ephemeral<'a, S, H> {
|
||||
pub fn new(storage: &'a S, overlay: &'a mut S::Overlay) -> Self {
|
||||
Ephemeral {
|
||||
storage,
|
||||
overlay,
|
||||
}
|
||||
Ephemeral { storage, overlay }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -431,13 +465,15 @@ impl<H: Hasher> TrieBackendStorage<H> for MemoryDB<H> {
|
||||
impl<S: TrieBackendStorage<H>, H: Hasher> hash_db::AsHashDB<H, DBValue>
|
||||
for TrieBackendEssence<S, H>
|
||||
{
|
||||
fn as_hash_db<'b>(&'b self) -> &'b (dyn hash_db::HashDB<H, DBValue> + 'b) { self }
|
||||
fn as_hash_db_mut<'b>(&'b mut self) -> &'b mut (dyn hash_db::HashDB<H, DBValue> + 'b) { self }
|
||||
fn as_hash_db<'b>(&'b self) -> &'b (dyn hash_db::HashDB<H, DBValue> + 'b) {
|
||||
self
|
||||
}
|
||||
fn as_hash_db_mut<'b>(&'b mut self) -> &'b mut (dyn hash_db::HashDB<H, DBValue> + 'b) {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: TrieBackendStorage<H>, H: Hasher> hash_db::HashDB<H, DBValue>
|
||||
for TrieBackendEssence<S, H>
|
||||
{
|
||||
impl<S: TrieBackendStorage<H>, H: Hasher> hash_db::HashDB<H, DBValue> for TrieBackendEssence<S, H> {
|
||||
fn get(&self, key: &H::Out, prefix: Prefix) -> Option<DBValue> {
|
||||
if *key == self.empty {
|
||||
return Some([0u8].to_vec())
|
||||
@@ -480,12 +516,11 @@ impl<S: TrieBackendStorage<H>, H: Hasher> hash_db::HashDBRef<H, DBValue>
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use sp_core::{Blake2Hasher, H256};
|
||||
use sp_trie::{TrieMut, PrefixedMemoryDB, trie_types::TrieDBMut, KeySpacedDBMut};
|
||||
use super::*;
|
||||
use sp_core::{Blake2Hasher, H256};
|
||||
use sp_trie::{trie_types::TrieDBMut, KeySpacedDBMut, PrefixedMemoryDB, TrieMut};
|
||||
|
||||
#[test]
|
||||
fn next_storage_key_and_next_child_storage_key_work() {
|
||||
@@ -529,20 +564,10 @@ mod test {
|
||||
let mdb = essence_1.into_storage();
|
||||
let essence_2 = TrieBackendEssence::new(mdb, root_2);
|
||||
|
||||
assert_eq!(
|
||||
essence_2.next_child_storage_key(child_info, b"2"), Ok(Some(b"3".to_vec()))
|
||||
);
|
||||
assert_eq!(
|
||||
essence_2.next_child_storage_key(child_info, b"3"), Ok(Some(b"4".to_vec()))
|
||||
);
|
||||
assert_eq!(
|
||||
essence_2.next_child_storage_key(child_info, b"4"), Ok(Some(b"6".to_vec()))
|
||||
);
|
||||
assert_eq!(
|
||||
essence_2.next_child_storage_key(child_info, b"5"), Ok(Some(b"6".to_vec()))
|
||||
);
|
||||
assert_eq!(
|
||||
essence_2.next_child_storage_key(child_info, b"6"), Ok(None)
|
||||
);
|
||||
assert_eq!(essence_2.next_child_storage_key(child_info, b"2"), Ok(Some(b"3".to_vec())));
|
||||
assert_eq!(essence_2.next_child_storage_key(child_info, b"3"), Ok(Some(b"4".to_vec())));
|
||||
assert_eq!(essence_2.next_child_storage_key(child_info, b"4"), Ok(Some(b"6".to_vec())));
|
||||
assert_eq!(essence_2.next_child_storage_key(child_info, b"5"), Ok(Some(b"6".to_vec())));
|
||||
assert_eq!(essence_2.next_child_storage_key(child_info, b"6"), Ok(None));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user