Kill the light client, CHTs and change tries. (#10080)

* Remove light client, change tries and CHTs

* Update tests

* fmt

* Restore changes_root

* Fixed benches

* Cargo fmt

* fmt

* fmt
This commit is contained in:
Arkadiy Paronyan
2021-11-12 14:15:01 +01:00
committed by GitHub
parent 112b7dac47
commit 4cbbf0cf43
141 changed files with 532 additions and 17807 deletions
@@ -288,10 +288,6 @@ fn generate_runtime_api_base_structures() -> Result<TokenStream> {
fn into_storage_changes(
&self,
backend: &Self::StateBackend,
changes_trie_state: Option<&#crate_::ChangesTrieState<
#crate_::HashFor<Block>,
#crate_::NumberFor<Block>,
>>,
parent_hash: Block::Hash,
) -> std::result::Result<
#crate_::StorageChanges<C::StateBackend, Block>,
@@ -299,7 +295,6 @@ fn generate_runtime_api_base_structures() -> Result<TokenStream> {
> where Self: Sized {
self.changes.replace(Default::default()).into_storage_changes(
backend,
changes_trie_state,
parent_hash,
self.storage_transaction_cache.replace(Default::default()),
)
@@ -116,10 +116,6 @@ fn implement_common_api_traits(block_type: TypePath, self_ty: Type) -> Result<To
fn into_storage_changes(
&self,
_: &Self::StateBackend,
_: Option<&#crate_::ChangesTrieState<
#crate_::HashFor<#block_type>,
#crate_::NumberFor<#block_type>,
>>,
_: <#block_type as #crate_::BlockT>::Hash,
) -> std::result::Result<
#crate_::StorageChanges<Self::StateBackend, #block_type>,
+1 -4
View File
@@ -97,7 +97,7 @@ pub use sp_runtime::{
#[doc(hidden)]
#[cfg(feature = "std")]
pub use sp_state_machine::{
Backend as StateBackend, ChangesTrieState, InMemoryBackend, OverlayedChanges, StorageProof,
Backend as StateBackend, InMemoryBackend, OverlayedChanges, StorageProof,
};
#[cfg(feature = "std")]
use sp_std::result;
@@ -394,14 +394,12 @@ pub type ProofRecorder<B> = sp_state_machine::ProofRecorder<<B as BlockT>::Hash>
pub type StorageTransactionCache<Block, Backend> = sp_state_machine::StorageTransactionCache<
<Backend as StateBackend<HashFor<Block>>>::Transaction,
HashFor<Block>,
NumberFor<Block>,
>;
#[cfg(feature = "std")]
pub type StorageChanges<SBackend, Block> = sp_state_machine::StorageChanges<
<SBackend as StateBackend<HashFor<Block>>>::Transaction,
HashFor<Block>,
NumberFor<Block>,
>;
/// Extract the state backend type for a type that implements `ProvideRuntimeApi`.
@@ -514,7 +512,6 @@ pub trait ApiExt<Block: BlockT> {
fn into_storage_changes(
&self,
backend: &Self::StateBackend,
changes_trie_state: Option<&ChangesTrieState<HashFor<Block>, NumberFor<Block>>>,
parent_hash: Block::Hash,
) -> Result<StorageChanges<Self::StateBackend, Block>, String>
where
@@ -211,7 +211,7 @@ fn record_proof_works() {
None,
8,
);
execution_proof_check_on_trie_backend::<_, u64, _, _>(
execution_proof_check_on_trie_backend(
&backend,
&mut overlay,
&executor,
@@ -17,8 +17,6 @@
//! Substrate blockchain trait
use std::sync::Arc;
use log::warn;
use parking_lot::RwLock;
use sp_runtime::{
@@ -96,8 +94,6 @@ pub trait Backend<Block: BlockT>:
fn justifications(&self, id: BlockId<Block>) -> Result<Option<Justifications>>;
/// Get last finalized block hash.
fn last_finalized(&self) -> Result<Block::Hash>;
/// Returns data cache reference, if it is enabled on this backend.
fn cache(&self) -> Option<Arc<dyn Cache<Block>>>;
/// Returns hashes of all blocks that are leaves of the block tree.
/// in other words, that have no children, are chain heads.
@@ -237,33 +233,6 @@ pub trait Backend<Block: BlockT>:
fn block_indexed_body(&self, id: BlockId<Block>) -> Result<Option<Vec<Vec<u8>>>>;
}
/// Provides access to the optional cache.
pub trait ProvideCache<Block: BlockT> {
/// Returns data cache reference, if it is enabled on this backend.
fn cache(&self) -> Option<Arc<dyn Cache<Block>>>;
}
/// Blockchain optional data cache.
pub trait Cache<Block: BlockT>: Send + Sync {
/// Initialize genesis value for the given cache.
///
/// The operation should be performed once before anything else is inserted in the cache.
/// Otherwise cache may end up in inconsistent state.
fn initialize(&self, key: &well_known_cache_keys::Id, value_at_genesis: Vec<u8>) -> Result<()>;
/// Returns cached value by the given key.
///
/// Returned tuple is the range where value has been active and the value itself.
/// Fails if read from cache storage fails or if the value for block is discarded
/// (i.e. if block is earlier that best finalized, but it is not in canonical chain).
fn get_at(
&self,
key: &well_known_cache_keys::Id,
block: &BlockId<Block>,
) -> Result<
Option<((NumberFor<Block>, Block::Hash), Option<(NumberFor<Block>, Block::Hash)>, Vec<u8>)>,
>;
}
/// Blockchain info
#[derive(Debug, Eq, PartialEq)]
pub struct Info<Block: BlockT> {
@@ -25,7 +25,6 @@ use crate::AURA_ENGINE_ID;
use codec::{Codec, Encode};
use sp_consensus_slots::Slot;
use sp_runtime::generic::DigestItem;
use sp_std::fmt::Debug;
/// A digest item which is usable with aura consensus.
pub trait CompatibleDigestItem<Signature>: Sized {
@@ -42,10 +41,9 @@ pub trait CompatibleDigestItem<Signature>: Sized {
fn as_aura_pre_digest(&self) -> Option<Slot>;
}
impl<Signature, Hash> CompatibleDigestItem<Signature> for DigestItem<Hash>
impl<Signature> CompatibleDigestItem<Signature> for DigestItem
where
Signature: Codec,
Hash: Debug + Send + Sync + Eq + Clone + Codec + 'static,
{
fn aura_seal(signature: Signature) -> Self {
DigestItem::Seal(AURA_ENGINE_ID, signature.encode())
@@ -21,7 +21,7 @@ use super::{
AllowedSlots, AuthorityId, AuthorityIndex, AuthoritySignature, BabeAuthorityWeight,
BabeEpochConfiguration, Slot, BABE_ENGINE_ID,
};
use codec::{Codec, Decode, Encode, MaxEncodedLen};
use codec::{Decode, Encode, MaxEncodedLen};
use sp_runtime::{DigestItem, RuntimeDebug};
use sp_std::vec::Vec;
@@ -177,10 +177,7 @@ pub trait CompatibleDigestItem: Sized {
fn as_next_config_descriptor(&self) -> Option<NextConfigDescriptor>;
}
impl<Hash> CompatibleDigestItem for DigestItem<Hash>
where
Hash: Send + Sync + Eq + Clone + Codec + 'static,
{
impl CompatibleDigestItem for DigestItem {
fn babe_pre_digest(digest: PreDigest) -> Self {
DigestItem::PreRuntime(BABE_ENGINE_ID, digest.encode())
}
@@ -26,7 +26,8 @@ use std::{sync::Arc, time::Duration};
use futures::prelude::*;
use sp_runtime::{
generic::BlockId,
traits::{Block as BlockT, DigestFor, HashFor, NumberFor},
traits::{Block as BlockT, HashFor},
Digest,
};
use sp_state_machine::StorageProof;
@@ -111,8 +112,7 @@ pub struct Proposal<Block: BlockT, Transaction, Proof> {
/// Proof that was recorded while building the block.
pub proof: Proof,
/// The storage changes while building this block.
pub storage_changes:
sp_state_machine::StorageChanges<Transaction, HashFor<Block>, NumberFor<Block>>,
pub storage_changes: sp_state_machine::StorageChanges<Transaction, HashFor<Block>>,
}
/// Error that is returned when [`ProofRecording`] requested to record a proof,
@@ -224,7 +224,7 @@ pub trait Proposer<B: BlockT> {
fn propose(
self,
inherent_data: InherentData,
inherent_digests: DigestFor<B>,
inherent_digests: Digest,
max_duration: Duration,
block_size_limit: Option<usize>,
) -> Self::Proposal;
@@ -1,321 +0,0 @@
// This file is part of Substrate.
// Copyright (C) 2018-2021 Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Substrate changes trie configuration.
use codec::{Decode, Encode};
use num_traits::Zero;
#[cfg(any(feature = "std", test))]
use serde::{Deserialize, Serialize};
/// Substrate changes trie configuration.
#[cfg_attr(
any(feature = "std", test),
derive(Serialize, Deserialize, parity_util_mem::MallocSizeOf)
)]
#[derive(Debug, Clone, PartialEq, Eq, Default, Encode, Decode, scale_info::TypeInfo)]
pub struct ChangesTrieConfiguration {
/// Interval (in blocks) at which level1-digests are created. Digests are not
/// created when this is less or equal to 1.
pub digest_interval: u32,
/// Maximal number of digest levels in hierarchy. 0 means that digests are not
/// created at all (even level1 digests). 1 means only level1-digests are created.
/// 2 means that every digest_interval^2 there will be a level2-digest, and so on.
/// Please ensure that maximum digest interval (i.e. digest_interval^digest_levels)
/// is within `u32` limits. Otherwise you'll never see digests covering such intervals
/// && maximal digests interval will be truncated to the last interval that fits
/// `u32` limits.
pub digest_levels: u32,
}
/// Substrate changes trie configuration range.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ChangesTrieConfigurationRange<Number, Hash> {
/// Zero block of configuration.
pub zero: (Number, Hash),
/// Last block of configuration (if configuration has been deactivated at some point).
pub end: Option<(Number, Hash)>,
/// The configuration itself. None if changes tries were disabled in this range.
pub config: Option<ChangesTrieConfiguration>,
}
impl ChangesTrieConfiguration {
/// Create new configuration given digest interval and levels.
pub fn new(digest_interval: u32, digest_levels: u32) -> Self {
Self { digest_interval, digest_levels }
}
/// Is digest build enabled?
pub fn is_digest_build_enabled(&self) -> bool {
self.digest_interval > 1 && self.digest_levels > 0
}
/// Do we need to build digest at given block?
pub fn is_digest_build_required_at_block<Number>(&self, zero: Number, block: Number) -> bool
where
Number: From<u32>
+ PartialEq
+ ::sp_std::ops::Rem<Output = Number>
+ ::sp_std::ops::Sub<Output = Number>
+ ::sp_std::cmp::PartialOrd
+ Zero,
{
block > zero &&
self.is_digest_build_enabled() &&
((block - zero) % self.digest_interval.into()).is_zero()
}
/// Returns max digest interval. One if digests are not created at all.
pub fn max_digest_interval(&self) -> u32 {
if !self.is_digest_build_enabled() {
return 1
}
// we'll get >1 loop iteration only when bad configuration parameters are selected
let mut current_level = self.digest_levels;
loop {
if let Some(max_digest_interval) = self.digest_interval.checked_pow(current_level) {
return max_digest_interval
}
current_level -= 1;
}
}
/// Returns max level digest block number that has been created at block <= passed block number.
///
/// Returns None if digests are not created at all.
pub fn prev_max_level_digest_block<Number>(&self, zero: Number, block: Number) -> Option<Number>
where
Number: Clone
+ From<u32>
+ PartialOrd
+ PartialEq
+ ::sp_std::ops::Add<Output = Number>
+ ::sp_std::ops::Sub<Output = Number>
+ ::sp_std::ops::Div<Output = Number>
+ ::sp_std::ops::Mul<Output = Number>
+ Zero,
{
if block <= zero {
return None
}
let (next_begin, next_end) =
self.next_max_level_digest_range(zero.clone(), block.clone())?;
// if 'next' digest includes our block, then it is a also a previous digest
if next_end == block {
return Some(block)
}
// if previous digest ends at zero block, then there are no previous digest
let prev_end = next_begin - 1.into();
if prev_end == zero {
None
} else {
Some(prev_end)
}
}
/// Returns max level digest blocks range (inclusive) which includes passed block.
///
/// Returns None if digests are not created at all.
/// It will return the first max-level digest if block is <= zero.
pub fn next_max_level_digest_range<Number>(
&self,
zero: Number,
mut block: Number,
) -> Option<(Number, Number)>
where
Number: Clone
+ From<u32>
+ PartialOrd
+ PartialEq
+ ::sp_std::ops::Add<Output = Number>
+ ::sp_std::ops::Sub<Output = Number>
+ ::sp_std::ops::Div<Output = Number>
+ ::sp_std::ops::Mul<Output = Number>,
{
if !self.is_digest_build_enabled() {
return None
}
if block <= zero {
block = zero.clone() + 1.into();
}
let max_digest_interval: Number = self.max_digest_interval().into();
let max_digests_since_zero = (block.clone() - zero.clone()) / max_digest_interval.clone();
if max_digests_since_zero == 0.into() {
return Some((zero.clone() + 1.into(), zero + max_digest_interval))
}
let last_max_digest_block = zero + max_digests_since_zero * max_digest_interval.clone();
Some(if block == last_max_digest_block {
(block.clone() - max_digest_interval + 1.into(), block)
} else {
(last_max_digest_block.clone() + 1.into(), last_max_digest_block + max_digest_interval)
})
}
/// Returns Some if digest must be built at given block number.
/// The tuple is:
/// (
/// digest level
/// digest interval (in blocks)
/// step between blocks we're interested in when digest is built
/// )
pub fn digest_level_at_block<Number>(
&self,
zero: Number,
block: Number,
) -> Option<(u32, u32, u32)>
where
Number: Clone
+ From<u32>
+ PartialEq
+ ::sp_std::ops::Rem<Output = Number>
+ ::sp_std::ops::Sub<Output = Number>
+ ::sp_std::cmp::PartialOrd
+ Zero,
{
if !self.is_digest_build_required_at_block(zero.clone(), block.clone()) {
return None
}
let relative_block = block - zero;
let mut digest_interval = self.digest_interval;
let mut current_level = 1u32;
let mut digest_step = 1u32;
while current_level < self.digest_levels {
let new_digest_interval = match digest_interval.checked_mul(self.digest_interval) {
Some(new_digest_interval)
if (relative_block.clone() % new_digest_interval.into()).is_zero() =>
new_digest_interval,
_ => break,
};
digest_step = digest_interval;
digest_interval = new_digest_interval;
current_level += 1;
}
Some((current_level, digest_interval, digest_step))
}
}
#[cfg(test)]
mod tests {
use super::ChangesTrieConfiguration;
fn config(interval: u32, levels: u32) -> ChangesTrieConfiguration {
ChangesTrieConfiguration { digest_interval: interval, digest_levels: levels }
}
#[test]
fn is_digest_build_enabled_works() {
assert!(!config(0, 100).is_digest_build_enabled());
assert!(!config(1, 100).is_digest_build_enabled());
assert!(config(2, 100).is_digest_build_enabled());
assert!(!config(100, 0).is_digest_build_enabled());
assert!(config(100, 1).is_digest_build_enabled());
}
#[test]
fn is_digest_build_required_at_block_works() {
fn test_with_zero(zero: u64) {
assert!(!config(8, 4).is_digest_build_required_at_block(zero, zero));
assert!(!config(8, 4).is_digest_build_required_at_block(zero, zero + 1u64));
assert!(!config(8, 4).is_digest_build_required_at_block(zero, zero + 2u64));
assert!(!config(8, 4).is_digest_build_required_at_block(zero, zero + 4u64));
assert!(config(8, 4).is_digest_build_required_at_block(zero, zero + 8u64));
assert!(!config(8, 4).is_digest_build_required_at_block(zero, zero + 9u64));
assert!(config(8, 4).is_digest_build_required_at_block(zero, zero + 64u64));
assert!(config(8, 4).is_digest_build_required_at_block(zero, zero + 64u64));
assert!(config(8, 4).is_digest_build_required_at_block(zero, zero + 512u64));
assert!(config(8, 4).is_digest_build_required_at_block(zero, zero + 4096u64));
assert!(!config(8, 4).is_digest_build_required_at_block(zero, zero + 4103u64));
assert!(config(8, 4).is_digest_build_required_at_block(zero, zero + 4104u64));
assert!(!config(8, 4).is_digest_build_required_at_block(zero, zero + 4108u64));
}
test_with_zero(0);
test_with_zero(8);
test_with_zero(17);
}
#[test]
fn digest_level_at_block_works() {
fn test_with_zero(zero: u64) {
assert_eq!(config(8, 4).digest_level_at_block(zero, zero), None);
assert_eq!(config(8, 4).digest_level_at_block(zero, zero + 7u64), None);
assert_eq!(config(8, 4).digest_level_at_block(zero, zero + 63u64), None);
assert_eq!(config(8, 4).digest_level_at_block(zero, zero + 8u64), Some((1, 8, 1)));
assert_eq!(config(8, 4).digest_level_at_block(zero, zero + 64u64), Some((2, 64, 8)));
assert_eq!(config(8, 4).digest_level_at_block(zero, zero + 512u64), Some((3, 512, 64)));
assert_eq!(
config(8, 4).digest_level_at_block(zero, zero + 4096u64),
Some((4, 4096, 512))
);
assert_eq!(config(8, 4).digest_level_at_block(zero, zero + 4112u64), Some((1, 8, 1)));
}
test_with_zero(0);
test_with_zero(8);
test_with_zero(17);
}
#[test]
fn max_digest_interval_works() {
assert_eq!(config(0, 0).max_digest_interval(), 1);
assert_eq!(config(2, 2).max_digest_interval(), 4);
assert_eq!(config(8, 4).max_digest_interval(), 4096);
assert_eq!(config(::std::u32::MAX, 1024).max_digest_interval(), ::std::u32::MAX);
}
#[test]
fn next_max_level_digest_range_works() {
assert_eq!(config(0, 0).next_max_level_digest_range(0u64, 16), None);
assert_eq!(config(1, 1).next_max_level_digest_range(0u64, 16), None);
assert_eq!(config(2, 1).next_max_level_digest_range(0u64, 16), Some((15, 16)));
assert_eq!(config(4, 1).next_max_level_digest_range(0u64, 16), Some((13, 16)));
assert_eq!(config(32, 1).next_max_level_digest_range(0u64, 16), Some((1, 32)));
assert_eq!(config(2, 3).next_max_level_digest_range(0u64, 10), Some((9, 16)));
assert_eq!(config(2, 3).next_max_level_digest_range(0u64, 8), Some((1, 8)));
assert_eq!(config(2, 1).next_max_level_digest_range(1u64, 1), Some((2, 3)));
assert_eq!(config(2, 2).next_max_level_digest_range(7u64, 9), Some((8, 11)));
assert_eq!(config(2, 2).next_max_level_digest_range(7u64, 5), Some((8, 11)));
}
#[test]
fn prev_max_level_digest_block_works() {
assert_eq!(config(0, 0).prev_max_level_digest_block(0u64, 16), None);
assert_eq!(config(1, 1).prev_max_level_digest_block(0u64, 16), None);
assert_eq!(config(2, 1).prev_max_level_digest_block(0u64, 16), Some(16));
assert_eq!(config(4, 1).prev_max_level_digest_block(0u64, 16), Some(16));
assert_eq!(config(4, 2).prev_max_level_digest_block(0u64, 16), Some(16));
assert_eq!(config(4, 2).prev_max_level_digest_block(0u64, 17), Some(16));
assert_eq!(config(4, 2).prev_max_level_digest_block(0u64, 33), Some(32));
assert_eq!(config(32, 1).prev_max_level_digest_block(0u64, 16), None);
assert_eq!(config(2, 3).prev_max_level_digest_block(0u64, 10), Some(8));
assert_eq!(config(2, 3).prev_max_level_digest_block(0u64, 8), Some(8));
assert_eq!(config(2, 2).prev_max_level_digest_block(7u64, 8), None);
assert_eq!(config(2, 2).prev_max_level_digest_block(7u64, 5), None);
}
}
-2
View File
@@ -58,7 +58,6 @@ pub mod hexdisplay;
pub mod u32_trait;
mod changes_trie;
pub mod ecdsa;
pub mod ed25519;
pub mod hash;
@@ -76,7 +75,6 @@ pub use self::{
hash::{convert_hash, H160, H256, H512},
uint::{U256, U512},
};
pub use changes_trie::{ChangesTrieConfiguration, ChangesTrieConfigurationRange};
#[cfg(feature = "full_crypto")]
pub use crypto::{DeriveJunction, Pair, Public};
@@ -173,13 +173,6 @@ pub trait Externalities: ExtensionStore {
/// operation.
fn storage_append(&mut self, key: Vec<u8>, value: Vec<u8>);
/// Get the changes trie root of the current storage overlay at a block with given `parent`.
///
/// `parent` expects a SCALE encoded hash.
///
/// The returned hash is defined by the `Block` and is SCALE encoded.
fn storage_changes_root(&mut self, parent: &[u8]) -> Result<Option<Vec<u8>>, ()>;
/// Start a new nested transaction.
///
/// This allows to either commit or roll back all changes made after this call to the
+4 -11
View File
@@ -195,16 +195,9 @@ pub trait Storage {
self.storage_root()
}
/// "Commit" all existing operations and get the resulting storage change root.
/// `parent_hash` is a SCALE encoded hash.
///
/// The hashing algorithm is defined by the `Block`.
///
/// Returns `Some(Vec<u8>)` which holds the SCALE encoded hash or `None` when
/// changes trie is disabled.
fn changes_root(&mut self, parent_hash: &[u8]) -> Option<Vec<u8>> {
self.storage_changes_root(parent_hash)
.expect("Invalid `parent_hash` given to `changes_root`.")
/// Always returns `None`. This function exists for compatibility reasons.
fn changes_root(&mut self, _parent_hash: &[u8]) -> Option<Vec<u8>> {
None
}
/// Get the next key in storage after the given one in lexicographic order.
@@ -1497,7 +1490,7 @@ pub fn oom(_: core::alloc::Layout) -> ! {
/// Type alias for Externalities implementation used in tests.
#[cfg(feature = "std")]
pub type TestExternalities = sp_state_machine::TestExternalities<sp_core::Blake2Hasher, u64>;
pub type TestExternalities = sp_state_machine::TestExternalities<sp_core::Blake2Hasher>;
/// The host functions Substrate provides for the Wasm runtime environment.
///
@@ -31,7 +31,7 @@ use std::{
sync::{Arc, Mutex},
};
type TestExternalities = sp_state_machine::TestExternalities<sp_runtime::traits::BlakeTwo256, u64>;
type TestExternalities = sp_state_machine::TestExternalities<sp_runtime::traits::BlakeTwo256>;
fn call_wasm_method_with_result<HF: HostFunctionsT>(
binary: &[u8],
@@ -26,59 +26,49 @@ use crate::{
codec::{Decode, Encode, Error, Input},
scale_info::{
build::{Fields, Variants},
meta_type, Path, Type, TypeInfo, TypeParameter,
Path, Type, TypeInfo,
},
ConsensusEngineId,
};
use sp_core::{ChangesTrieConfiguration, RuntimeDebug};
use sp_core::RuntimeDebug;
/// Generic header digest.
#[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug, TypeInfo)]
#[cfg_attr(feature = "std", derive(Serialize, Deserialize, parity_util_mem::MallocSizeOf))]
pub struct Digest<Hash> {
pub struct Digest {
/// A list of logs in the digest.
#[cfg_attr(
feature = "std",
serde(bound(serialize = "Hash: codec::Codec", deserialize = "Hash: codec::Codec"))
)]
pub logs: Vec<DigestItem<Hash>>,
pub logs: Vec<DigestItem>,
}
impl<Item> Default for Digest<Item> {
impl Default for Digest {
fn default() -> Self {
Self { logs: Vec::new() }
}
}
impl<Hash> Digest<Hash> {
impl Digest {
/// Get reference to all digest items.
pub fn logs(&self) -> &[DigestItem<Hash>] {
pub fn logs(&self) -> &[DigestItem] {
&self.logs
}
/// Push new digest item.
pub fn push(&mut self, item: DigestItem<Hash>) {
pub fn push(&mut self, item: DigestItem) {
self.logs.push(item);
}
/// Pop a digest item.
pub fn pop(&mut self) -> Option<DigestItem<Hash>> {
pub fn pop(&mut self) -> Option<DigestItem> {
self.logs.pop()
}
/// Get reference to the first digest item that matches the passed predicate.
pub fn log<T: ?Sized, F: Fn(&DigestItem<Hash>) -> Option<&T>>(
&self,
predicate: F,
) -> Option<&T> {
pub fn log<T: ?Sized, F: Fn(&DigestItem) -> Option<&T>>(&self, predicate: F) -> Option<&T> {
self.logs().iter().find_map(predicate)
}
/// Get a conversion of the first digest item that successfully converts using the function.
pub fn convert_first<T, F: Fn(&DigestItem<Hash>) -> Option<T>>(
&self,
predicate: F,
) -> Option<T> {
pub fn convert_first<T, F: Fn(&DigestItem) -> Option<T>>(&self, predicate: F) -> Option<T> {
self.logs().iter().find_map(predicate)
}
}
@@ -87,12 +77,7 @@ impl<Hash> Digest<Hash> {
/// provide opaque access to other items.
#[derive(PartialEq, Eq, Clone, RuntimeDebug)]
#[cfg_attr(feature = "std", derive(parity_util_mem::MallocSizeOf))]
pub enum DigestItem<Hash> {
/// System digest item that contains the root of changes trie at given
/// block. It is created for every block iff runtime supports changes
/// trie creation.
ChangesTrieRoot(Hash),
pub enum DigestItem {
/// A pre-runtime digest.
///
/// These are messages from the consensus engine to the runtime, although
@@ -116,10 +101,6 @@ pub enum DigestItem<Hash> {
/// by runtimes.
Seal(ConsensusEngineId, Vec<u8>),
/// Digest item that contains signal from changes tries manager to the
/// native code.
ChangesTrieSignal(ChangesTrieSignal),
/// Some other thing. Unsupported and experimental.
Other(Vec<u8>),
@@ -132,25 +113,8 @@ pub enum DigestItem<Hash> {
RuntimeEnvironmentUpdated,
}
/// Available changes trie signals.
#[derive(PartialEq, Eq, Clone, Encode, Decode, TypeInfo)]
#[cfg_attr(feature = "std", derive(Debug, parity_util_mem::MallocSizeOf))]
pub enum ChangesTrieSignal {
/// New changes trie configuration is enacted, starting from **next block**.
///
/// The block that emits this signal will contain changes trie (CT) that covers
/// blocks range [BEGIN; current block], where BEGIN is (order matters):
/// - LAST_TOP_LEVEL_DIGEST_BLOCK+1 if top level digest CT has ever been created using current
/// configuration AND the last top level digest CT has been created at block
/// LAST_TOP_LEVEL_DIGEST_BLOCK;
/// - LAST_CONFIGURATION_CHANGE_BLOCK+1 if there has been CT configuration change before and
/// the last configuration change happened at block LAST_CONFIGURATION_CHANGE_BLOCK;
/// - 1 otherwise.
NewConfiguration(Option<ChangesTrieConfiguration>),
}
#[cfg(feature = "std")]
impl<Hash: Encode> serde::Serialize for DigestItem<Hash> {
impl serde::Serialize for DigestItem {
fn serialize<S>(&self, seq: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
@@ -160,7 +124,7 @@ impl<Hash: Encode> serde::Serialize for DigestItem<Hash> {
}
#[cfg(feature = "std")]
impl<'a, Hash: Decode> serde::Deserialize<'a> for DigestItem<Hash> {
impl<'a> serde::Deserialize<'a> for DigestItem {
fn deserialize<D>(de: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'a>,
@@ -171,75 +135,48 @@ impl<'a, Hash: Decode> serde::Deserialize<'a> for DigestItem<Hash> {
}
}
impl<Hash> TypeInfo for DigestItem<Hash>
where
Hash: TypeInfo + 'static,
{
impl TypeInfo for DigestItem {
type Identity = Self;
fn type_info() -> Type {
Type::builder()
.path(Path::new("DigestItem", module_path!()))
.type_params(vec![TypeParameter::new("Hash", Some(meta_type::<Hash>()))])
.variant(
Variants::new()
.variant("ChangesTrieRoot", |v| {
v.index(DigestItemType::ChangesTrieRoot as u8)
.fields(Fields::unnamed().field(|f| f.ty::<Hash>().type_name("Hash")))
})
.variant("PreRuntime", |v| {
v.index(DigestItemType::PreRuntime as u8).fields(
Fields::unnamed()
.field(|f| {
f.ty::<ConsensusEngineId>().type_name("ConsensusEngineId")
})
.field(|f| f.ty::<Vec<u8>>().type_name("Vec<u8>")),
)
})
.variant("Consensus", |v| {
v.index(DigestItemType::Consensus as u8).fields(
Fields::unnamed()
.field(|f| {
f.ty::<ConsensusEngineId>().type_name("ConsensusEngineId")
})
.field(|f| f.ty::<Vec<u8>>().type_name("Vec<u8>")),
)
})
.variant("Seal", |v| {
v.index(DigestItemType::Seal as u8).fields(
Fields::unnamed()
.field(|f| {
f.ty::<ConsensusEngineId>().type_name("ConsensusEngineId")
})
.field(|f| f.ty::<Vec<u8>>().type_name("Vec<u8>")),
)
})
.variant("ChangesTrieSignal", |v| {
v.index(DigestItemType::ChangesTrieSignal as u8).fields(
Fields::unnamed().field(|f| {
f.ty::<ChangesTrieSignal>().type_name("ChangesTrieSignal")
}),
)
})
.variant("Other", |v| {
v.index(DigestItemType::Other as u8).fields(
Fields::unnamed().field(|f| f.ty::<Vec<u8>>().type_name("Vec<u8>")),
)
})
.variant("RuntimeEnvironmentUpdated", |v| {
v.index(DigestItemType::RuntimeEnvironmentUpdated as u8)
.fields(Fields::unit())
}),
)
Type::builder().path(Path::new("DigestItem", module_path!())).variant(
Variants::new()
.variant("PreRuntime", |v| {
v.index(DigestItemType::PreRuntime as u8).fields(
Fields::unnamed()
.field(|f| f.ty::<ConsensusEngineId>().type_name("ConsensusEngineId"))
.field(|f| f.ty::<Vec<u8>>().type_name("Vec<u8>")),
)
})
.variant("Consensus", |v| {
v.index(DigestItemType::Consensus as u8).fields(
Fields::unnamed()
.field(|f| f.ty::<ConsensusEngineId>().type_name("ConsensusEngineId"))
.field(|f| f.ty::<Vec<u8>>().type_name("Vec<u8>")),
)
})
.variant("Seal", |v| {
v.index(DigestItemType::Seal as u8).fields(
Fields::unnamed()
.field(|f| f.ty::<ConsensusEngineId>().type_name("ConsensusEngineId"))
.field(|f| f.ty::<Vec<u8>>().type_name("Vec<u8>")),
)
})
.variant("Other", |v| {
v.index(DigestItemType::Other as u8)
.fields(Fields::unnamed().field(|f| f.ty::<Vec<u8>>().type_name("Vec<u8>")))
})
.variant("RuntimeEnvironmentUpdated", |v| {
v.index(DigestItemType::RuntimeEnvironmentUpdated as u8).fields(Fields::unit())
}),
)
}
}
/// A 'referencing view' for digest item. Does not own its contents. Used by
/// final runtime implementations for encoding/decoding its log items.
#[derive(PartialEq, Eq, Clone, RuntimeDebug)]
pub enum DigestItemRef<'a, Hash: 'a> {
/// Reference to `DigestItem::ChangesTrieRoot`.
ChangesTrieRoot(&'a Hash),
pub enum DigestItemRef<'a> {
/// A pre-runtime digest.
///
/// These are messages from the consensus engine to the runtime, although
@@ -254,9 +191,6 @@ pub enum DigestItemRef<'a, Hash: 'a> {
/// Put a Seal on it. This is only used by native code, and is never seen
/// by runtimes.
Seal(&'a ConsensusEngineId, &'a Vec<u8>),
/// Digest item that contains signal from changes tries manager to the
/// native code.
ChangesTrieSignal(&'a ChangesTrieSignal),
/// Any 'non-system' digest item, opaque to the native code.
Other(&'a Vec<u8>),
/// Runtime code or heap pages updated.
@@ -271,11 +205,9 @@ pub enum DigestItemRef<'a, Hash: 'a> {
#[derive(Encode, Decode)]
pub enum DigestItemType {
Other = 0,
ChangesTrieRoot = 2,
Consensus = 4,
Seal = 5,
PreRuntime = 6,
ChangesTrieSignal = 7,
RuntimeEnvironmentUpdated = 8,
}
@@ -293,25 +225,18 @@ pub enum OpaqueDigestItemId<'a> {
Other,
}
impl<Hash> DigestItem<Hash> {
impl DigestItem {
/// Returns a 'referencing view' for this digest item.
pub fn dref(&self) -> DigestItemRef<Hash> {
pub fn dref(&self) -> DigestItemRef {
match *self {
Self::ChangesTrieRoot(ref v) => DigestItemRef::ChangesTrieRoot(v),
Self::PreRuntime(ref v, ref s) => DigestItemRef::PreRuntime(v, s),
Self::Consensus(ref v, ref s) => DigestItemRef::Consensus(v, s),
Self::Seal(ref v, ref s) => DigestItemRef::Seal(v, s),
Self::ChangesTrieSignal(ref s) => DigestItemRef::ChangesTrieSignal(s),
Self::Other(ref v) => DigestItemRef::Other(v),
Self::RuntimeEnvironmentUpdated => DigestItemRef::RuntimeEnvironmentUpdated,
}
}
/// Returns `Some` if the entry is the `ChangesTrieRoot` entry.
pub fn as_changes_trie_root(&self) -> Option<&Hash> {
self.dref().as_changes_trie_root()
}
/// Returns `Some` if this entry is the `PreRuntime` entry.
pub fn as_pre_runtime(&self) -> Option<(ConsensusEngineId, &[u8])> {
self.dref().as_pre_runtime()
@@ -327,11 +252,6 @@ impl<Hash> DigestItem<Hash> {
self.dref().as_seal()
}
/// Returns `Some` if the entry is the `ChangesTrieSignal` entry.
pub fn as_changes_trie_signal(&self) -> Option<&ChangesTrieSignal> {
self.dref().as_changes_trie_signal()
}
/// Returns Some if `self` is a `DigestItem::Other`.
pub fn as_other(&self) -> Option<&[u8]> {
self.dref().as_other()
@@ -372,20 +292,19 @@ impl<Hash> DigestItem<Hash> {
}
}
impl<Hash: Encode> Encode for DigestItem<Hash> {
impl Encode for DigestItem {
fn encode(&self) -> Vec<u8> {
self.dref().encode()
}
}
impl<Hash: Encode> codec::EncodeLike for DigestItem<Hash> {}
impl codec::EncodeLike for DigestItem {}
impl<Hash: Decode> Decode for DigestItem<Hash> {
impl Decode for DigestItem {
#[allow(deprecated)]
fn decode<I: Input>(input: &mut I) -> Result<Self, Error> {
let item_type: DigestItemType = Decode::decode(input)?;
match item_type {
DigestItemType::ChangesTrieRoot => Ok(Self::ChangesTrieRoot(Decode::decode(input)?)),
DigestItemType::PreRuntime => {
let vals: (ConsensusEngineId, Vec<u8>) = Decode::decode(input)?;
Ok(Self::PreRuntime(vals.0, vals.1))
@@ -398,23 +317,13 @@ impl<Hash: Decode> Decode for DigestItem<Hash> {
let vals: (ConsensusEngineId, Vec<u8>) = Decode::decode(input)?;
Ok(Self::Seal(vals.0, vals.1))
},
DigestItemType::ChangesTrieSignal =>
Ok(Self::ChangesTrieSignal(Decode::decode(input)?)),
DigestItemType::Other => Ok(Self::Other(Decode::decode(input)?)),
DigestItemType::RuntimeEnvironmentUpdated => Ok(Self::RuntimeEnvironmentUpdated),
}
}
}
impl<'a, Hash> DigestItemRef<'a, Hash> {
/// Cast this digest item into `ChangesTrieRoot`.
pub fn as_changes_trie_root(&self) -> Option<&'a Hash> {
match *self {
Self::ChangesTrieRoot(ref changes_trie_root) => Some(changes_trie_root),
_ => None,
}
}
impl<'a> DigestItemRef<'a> {
/// Cast this digest item into `PreRuntime`
pub fn as_pre_runtime(&self) -> Option<(ConsensusEngineId, &'a [u8])> {
match *self {
@@ -439,14 +348,6 @@ impl<'a, Hash> DigestItemRef<'a, Hash> {
}
}
/// Cast this digest item into `ChangesTrieSignal`.
pub fn as_changes_trie_signal(&self) -> Option<&'a ChangesTrieSignal> {
match *self {
Self::ChangesTrieSignal(ref changes_trie_signal) => Some(changes_trie_signal),
_ => None,
}
}
/// Cast this digest item into `PreRuntime`
pub fn as_other(&self) -> Option<&'a [u8]> {
match *self {
@@ -508,15 +409,11 @@ impl<'a, Hash> DigestItemRef<'a, Hash> {
}
}
impl<'a, Hash: Encode> Encode for DigestItemRef<'a, Hash> {
impl<'a> Encode for DigestItemRef<'a> {
fn encode(&self) -> Vec<u8> {
let mut v = Vec::new();
match *self {
Self::ChangesTrieRoot(changes_trie_root) => {
DigestItemType::ChangesTrieRoot.encode_to(&mut v);
changes_trie_root.encode_to(&mut v);
},
Self::Consensus(val, data) => {
DigestItemType::Consensus.encode_to(&mut v);
(val, data).encode_to(&mut v);
@@ -529,10 +426,6 @@ impl<'a, Hash: Encode> Encode for DigestItemRef<'a, Hash> {
DigestItemType::PreRuntime.encode_to(&mut v);
(val, data).encode_to(&mut v);
},
Self::ChangesTrieSignal(changes_trie_signal) => {
DigestItemType::ChangesTrieSignal.encode_to(&mut v);
changes_trie_signal.encode_to(&mut v);
},
Self::Other(val) => {
DigestItemType::Other.encode_to(&mut v);
val.encode_to(&mut v);
@@ -546,16 +439,7 @@ impl<'a, Hash: Encode> Encode for DigestItemRef<'a, Hash> {
}
}
impl ChangesTrieSignal {
/// Try to cast this signal to NewConfiguration.
pub fn as_new_configuration(&self) -> Option<&Option<ChangesTrieConfiguration>> {
match self {
Self::NewConfiguration(config) => Some(config),
}
}
}
impl<'a, Hash: Encode> codec::EncodeLike for DigestItemRef<'a, Hash> {}
impl<'a> codec::EncodeLike for DigestItemRef<'a> {}
#[cfg(test)]
mod tests {
@@ -564,22 +448,18 @@ mod tests {
#[test]
fn should_serialize_digest() {
let digest = Digest {
logs: vec![
DigestItem::ChangesTrieRoot(4),
DigestItem::Other(vec![1, 2, 3]),
DigestItem::Seal(*b"test", vec![1, 2, 3]),
],
logs: vec![DigestItem::Other(vec![1, 2, 3]), DigestItem::Seal(*b"test", vec![1, 2, 3])],
};
assert_eq!(
serde_json::to_string(&digest).unwrap(),
r#"{"logs":["0x0204000000","0x000c010203","0x05746573740c010203"]}"#
r#"{"logs":["0x000c010203","0x05746573740c010203"]}"#
);
}
#[test]
fn digest_item_type_info() {
let type_info = DigestItem::<u32>::type_info();
let type_info = DigestItem::type_info();
let variants = if let scale_info::TypeDef::Variant(variant) = type_info.type_def() {
variant.variants()
} else {
@@ -589,21 +469,13 @@ mod tests {
// ensure that all variants are covered by manual TypeInfo impl
let check = |digest_item_type: DigestItemType| {
let (variant_name, digest_item) = match digest_item_type {
DigestItemType::Other => ("Other", DigestItem::<u32>::Other(Default::default())),
DigestItemType::ChangesTrieRoot =>
("ChangesTrieRoot", DigestItem::ChangesTrieRoot(Default::default())),
DigestItemType::Other => ("Other", DigestItem::Other(Default::default())),
DigestItemType::Consensus =>
("Consensus", DigestItem::Consensus(Default::default(), Default::default())),
DigestItemType::Seal =>
("Seal", DigestItem::Seal(Default::default(), Default::default())),
DigestItemType::PreRuntime =>
("PreRuntime", DigestItem::PreRuntime(Default::default(), Default::default())),
DigestItemType::ChangesTrieSignal => (
"ChangesTrieSignal",
DigestItem::ChangesTrieSignal(ChangesTrieSignal::NewConfiguration(
Default::default(),
)),
),
DigestItemType::RuntimeEnvironmentUpdated =>
("RuntimeEnvironmentUpdated", DigestItem::RuntimeEnvironmentUpdated),
};
@@ -617,11 +489,9 @@ mod tests {
};
check(DigestItemType::Other);
check(DigestItemType::ChangesTrieRoot);
check(DigestItemType::Consensus);
check(DigestItemType::Seal);
check(DigestItemType::PreRuntime);
check(DigestItemType::ChangesTrieSignal);
check(DigestItemType::RuntimeEnvironmentUpdated);
}
}
@@ -51,7 +51,7 @@ pub struct Header<Number: Copy + Into<U256> + TryFrom<U256>, Hash: HashT> {
/// The merkle root of the extrinsics.
pub extrinsics_root: Hash::Output,
/// A chain-specific digest of data useful for light clients or referencing auxiliary data.
pub digest: Digest<Hash::Output>,
pub digest: Digest,
}
#[cfg(feature = "std")]
@@ -150,11 +150,11 @@ where
self.parent_hash = hash
}
fn digest(&self) -> &Digest<Self::Hash> {
fn digest(&self) -> &Digest {
&self.digest
}
fn digest_mut(&mut self) -> &mut Digest<Self::Hash> {
fn digest_mut(&mut self) -> &mut Digest {
#[cfg(feature = "std")]
log::debug!(target: "header", "Retrieving mutable reference to digest");
&mut self.digest
@@ -165,7 +165,7 @@ where
extrinsics_root: Self::Hash,
state_root: Self::Hash,
parent_hash: Self::Hash,
digest: Digest<Self::Hash>,
digest: Digest,
) -> Self {
Self { number, extrinsics_root, state_root, parent_hash, digest }
}
@@ -235,10 +235,7 @@ mod tests {
state_root: BlakeTwo256::hash(b"3"),
extrinsics_root: BlakeTwo256::hash(b"4"),
digest: crate::generic::Digest {
logs: vec![
crate::generic::DigestItem::ChangesTrieRoot(BlakeTwo256::hash(b"5")),
crate::generic::DigestItem::Other(b"6".to_vec()),
],
logs: vec![crate::generic::DigestItem::Other(b"6".to_vec())],
},
};
@@ -251,9 +248,7 @@ mod tests {
72, 51, 123, 15, 62, 20, 134, 32, 23, 61, 170, 165, 249, 77, 0, 216, 129, 112, 93,
203, 240, 170, 131, 239, 218, 186, 97, 210, 237, 225, 235, 134, 73, 33, 73, 151,
87, 78, 32, 196, 100, 56, 138, 23, 36, 32, 210, 84, 3, 104, 43, 187, 184, 12, 73,
104, 49, 200, 204, 31, 143, 13, 8, 2, 112, 178, 1, 53, 47, 36, 191, 28, 151, 112,
185, 159, 143, 113, 32, 24, 33, 65, 28, 244, 20, 55, 124, 155, 140, 45, 188, 238,
97, 219, 135, 214, 0, 4, 54
104, 49, 200, 204, 31, 143, 13, 4, 0, 4, 54
],
);
assert_eq!(header, Header::<u32, BlakeTwo256>::decode(&mut &header_encoded[..]).unwrap());
@@ -264,10 +259,7 @@ mod tests {
state_root: BlakeTwo256::hash(b"3000"),
extrinsics_root: BlakeTwo256::hash(b"4000"),
digest: crate::generic::Digest {
logs: vec![
crate::generic::DigestItem::Other(b"5000".to_vec()),
crate::generic::DigestItem::ChangesTrieRoot(BlakeTwo256::hash(b"6000")),
],
logs: vec![crate::generic::DigestItem::Other(b"5000".to_vec())],
},
};
@@ -280,9 +272,7 @@ mod tests {
47, 12, 107, 88, 153, 146, 55, 21, 226, 186, 110, 48, 167, 187, 67, 183, 228, 232,
118, 136, 30, 254, 11, 87, 48, 112, 7, 97, 31, 82, 146, 110, 96, 87, 152, 68, 98,
162, 227, 222, 78, 14, 244, 194, 120, 154, 112, 97, 222, 144, 174, 101, 220, 44,
111, 126, 54, 34, 155, 220, 253, 124, 8, 0, 16, 53, 48, 48, 48, 2, 42, 105, 109,
150, 206, 223, 24, 44, 164, 77, 27, 137, 177, 220, 25, 170, 140, 35, 156, 246, 233,
112, 26, 23, 192, 61, 226, 14, 84, 219, 144, 252
111, 126, 54, 34, 155, 220, 253, 124, 4, 0, 16, 53, 48, 48, 48
],
);
assert_eq!(header, Header::<u32, BlakeTwo256>::decode(&mut &header_encoded[..]).unwrap());
@@ -31,7 +31,7 @@ mod unchecked_extrinsic;
pub use self::{
block::{Block, BlockId, SignedBlock},
checked_extrinsic::CheckedExtrinsic,
digest::{ChangesTrieSignal, Digest, DigestItem, DigestItemRef, OpaqueDigestItemId},
digest::{Digest, DigestItem, DigestItemRef, OpaqueDigestItemId},
era::{Era, Phase},
header::Header,
unchecked_extrinsic::{SignedPayload, UncheckedExtrinsic},
@@ -19,29 +19,26 @@
use super::DigestItem;
use crate::codec::{Decode, Encode};
use sp_core::H256;
#[test]
fn system_digest_item_encoding() {
let item = DigestItem::ChangesTrieRoot::<H256>(H256::default());
let item = DigestItem::Consensus([1, 2, 3, 4], vec![5, 6, 7, 8]);
let encoded = item.encode();
assert_eq!(
encoded,
vec![
// type = DigestItemType::ChangesTrieRoot
2, // trie root
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0,
4, // type = DigestItemType::Consensus
1, 2, 3, 4, 16, 5, 6, 7, 8,
]
);
let decoded: DigestItem<H256> = Decode::decode(&mut &encoded[..]).unwrap();
let decoded: DigestItem = Decode::decode(&mut &encoded[..]).unwrap();
assert_eq!(item, decoded);
}
#[test]
fn non_system_digest_item_encoding() {
let item = DigestItem::Other::<H256>(vec![10, 20, 30]);
let item = DigestItem::Other(vec![10, 20, 30]);
let encoded = item.encode();
assert_eq!(
encoded,
@@ -53,6 +50,6 @@ fn non_system_digest_item_encoding() {
]
);
let decoded: DigestItem<H256> = Decode::decode(&mut &encoded[..]).unwrap();
let decoded: DigestItem = Decode::decode(&mut &encoded[..]).unwrap();
assert_eq!(item, decoded);
}
+2 -2
View File
@@ -182,10 +182,10 @@ impl traits::Verify for TestSignature {
}
/// Digest item
pub type DigestItem = generic::DigestItem<H256>;
pub type DigestItem = generic::DigestItem;
/// Header Digest
pub type Digest = generic::Digest<H256>;
pub type Digest = generic::Digest;
/// Block Header
pub type Header = generic::Header<u64, BlakeTwo256>;
+5 -11
View File
@@ -19,7 +19,7 @@
use crate::{
codec::{Codec, Decode, Encode, MaxEncodedLen},
generic::{Digest, DigestItem},
generic::Digest,
scale_info::{MetaType, StaticTypeInfo, TypeInfo},
transaction_validity::{
TransactionSource, TransactionValidity, TransactionValidityError, UnknownTransaction,
@@ -548,10 +548,7 @@ impl CheckEqual for sp_core::H256 {
}
}
impl<H: PartialEq + Eq + Debug> CheckEqual for super::generic::DigestItem<H>
where
H: Encode,
{
impl CheckEqual for super::generic::DigestItem {
#[cfg(feature = "std")]
fn check_equal(&self, other: &Self) {
if self != other {
@@ -642,7 +639,7 @@ pub trait Header:
extrinsics_root: Self::Hash,
state_root: Self::Hash,
parent_hash: Self::Hash,
digest: Digest<Self::Hash>,
digest: Digest,
) -> Self;
/// Returns a reference to the header number.
@@ -666,9 +663,9 @@ pub trait Header:
fn set_parent_hash(&mut self, hash: Self::Hash);
/// Returns a reference to the digest.
fn digest(&self) -> &Digest<Self::Hash>;
fn digest(&self) -> &Digest;
/// Get a mutable reference to the digest.
fn digest_mut(&mut self) -> &mut Digest<Self::Hash>;
fn digest_mut(&mut self) -> &mut Digest;
/// Returns the hash of the header.
fn hash(&self) -> Self::Hash {
@@ -763,9 +760,6 @@ pub type HashFor<B> = <<B as Block>::Header as Header>::Hashing;
/// Extract the number type for a block.
pub type NumberFor<B> = <<B as Block>::Header as Header>::Number;
/// Extract the digest type for a block.
pub type DigestFor<B> = Digest<<<B as Block>::Header as Header>::Hash>;
/// Extract the digest item type for a block.
pub type DigestItemFor<B> = DigestItem<<<B as Block>::Header as Header>::Hash>;
/// A "checkable" piece of information, used by the standard Substrate Executive in order to
/// check the validity of a piece of extrinsic information, usually by verifying the signature.
@@ -292,32 +292,6 @@ 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)>,
{
use sp_trie::{trie_types::TrieDBMut, TrieMut};
let mut root = <H as Hasher>::Out::default();
{
let mut trie = TrieDBMut::<H>::new(mdb, &mut root);
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
}
}
}
Some(root)
}
/// Wrapper to create a [`RuntimeCode`] from a type that implements [`Backend`].
#[cfg(feature = "std")]
pub struct BackendRuntimeCode<'a, B, H> {
@@ -309,10 +309,6 @@ impl Externalities for BasicExternalities {
.encode()
}
fn storage_changes_root(&mut self, _parent: &[u8]) -> Result<Option<Vec<u8>>, ()> {
Ok(None)
}
fn storage_start_transaction(&mut self) {
unimplemented!("Transactions are not supported by BasicExternalities");
}
File diff suppressed because it is too large Load Diff
@@ -1,278 +0,0 @@
// This file is part of Substrate.
// Copyright (C) 2019-2021 Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Changes tries build cache.
use std::collections::{HashMap, HashSet};
use crate::StorageKey;
use sp_core::storage::PrefixedStorageKey;
/// Changes trie build cache.
///
/// Helps to avoid read of changes tries from the database when digest trie
/// is built. It holds changed keys for every block (indexed by changes trie
/// root) that could be referenced by future digest items. For digest entries
/// it also holds keys covered by this digest. Entries for top level digests
/// are never created, because they'll never be used to build other digests.
///
/// Entries are pruned from the cache once digest block that is using this entry
/// is inserted (because digest block will includes all keys from this entry).
/// When there's a fork, entries are pruned when first changes trie is inserted.
pub struct BuildCache<H, N> {
/// Map of block (implies changes trie) number => changes trie root.
roots_by_number: HashMap<N, H>,
/// Map of changes trie root => set of storage keys that are in this trie.
/// The `Option<Vec<u8>>` in inner `HashMap` stands for the child storage key.
/// If it is `None`, then the `HashSet` contains keys changed in top-level storage.
/// If it is `Some`, then the `HashSet` contains keys changed in child storage, identified by
/// the key.
changed_keys: HashMap<H, HashMap<Option<PrefixedStorageKey>, HashSet<StorageKey>>>,
}
/// The action to perform when block-with-changes-trie is imported.
#[derive(Debug, PartialEq)]
pub enum CacheAction<H, N> {
/// Cache data that has been collected when CT has been built.
CacheBuildData(CachedBuildData<H, N>),
/// Clear cache from all existing entries.
Clear,
}
/// The data that has been cached during changes trie building.
#[derive(Debug, PartialEq)]
pub struct CachedBuildData<H, N> {
block: N,
trie_root: H,
digest_input_blocks: Vec<N>,
changed_keys: HashMap<Option<PrefixedStorageKey>, HashSet<StorageKey>>,
}
/// The action to perform when block-with-changes-trie is imported.
#[derive(Debug, PartialEq)]
pub(crate) enum IncompleteCacheAction<N> {
/// Cache data that has been collected when CT has been built.
CacheBuildData(IncompleteCachedBuildData<N>),
/// Clear cache from all existing entries.
Clear,
}
/// The data (without changes trie root) that has been cached during changes trie building.
#[derive(Debug, PartialEq)]
pub(crate) struct IncompleteCachedBuildData<N> {
digest_input_blocks: Vec<N>,
changed_keys: HashMap<Option<PrefixedStorageKey>, HashSet<StorageKey>>,
}
impl<H, N> BuildCache<H, N>
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() }
}
/// Get cached changed keys for changes trie with given root.
pub fn get(
&self,
root: &H,
) -> Option<&HashMap<Option<PrefixedStorageKey>, HashSet<StorageKey>>> {
self.changed_keys.get(&root)
}
/// Execute given functor with cached entry for given block.
/// Returns true if the functor has been called and false otherwise.
pub fn with_changed_keys(
&self,
root: &H,
functor: &mut dyn FnMut(&HashMap<Option<PrefixedStorageKey>, HashSet<StorageKey>>),
) -> bool {
match self.changed_keys.get(&root) {
Some(changed_keys) => {
functor(changed_keys);
true
},
None => false,
}
}
/// Insert data into cache.
pub fn perform(&mut self, action: CacheAction<H, N>) {
match action {
CacheAction::CacheBuildData(data) => {
self.roots_by_number.insert(data.block, data.trie_root.clone());
self.changed_keys.insert(data.trie_root, data.changed_keys);
for digest_input_block in data.digest_input_blocks {
let digest_input_block_hash = self.roots_by_number.remove(&digest_input_block);
if let Some(digest_input_block_hash) = digest_input_block_hash {
self.changed_keys.remove(&digest_input_block_hash);
}
}
},
CacheAction::Clear => {
self.roots_by_number.clear();
self.changed_keys.clear();
},
}
}
}
impl<N> IncompleteCacheAction<N> {
/// Returns true if we need to collect changed keys for this action.
pub fn collects_changed_keys(&self) -> bool {
match *self {
IncompleteCacheAction::CacheBuildData(_) => true,
IncompleteCacheAction::Clear => false,
}
}
/// Complete cache action with computed changes trie root.
pub(crate) fn complete<H: Clone>(self, block: N, trie_root: &H) -> CacheAction<H, N> {
match self {
IncompleteCacheAction::CacheBuildData(build_data) =>
CacheAction::CacheBuildData(build_data.complete(block, trie_root.clone())),
IncompleteCacheAction::Clear => CacheAction::Clear,
}
}
/// Set numbers of blocks that are superseded by this new entry.
///
/// If/when this build data is committed to the cache, entries for these blocks
/// will be removed from the cache.
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::Clear => IncompleteCacheAction::Clear,
}
}
/// Insert changed keys of given storage into cached data.
pub(crate) fn insert(
self,
storage_key: Option<PrefixedStorageKey>,
changed_keys: HashSet<StorageKey>,
) -> Self {
match self {
IncompleteCacheAction::CacheBuildData(build_data) =>
IncompleteCacheAction::CacheBuildData(build_data.insert(storage_key, changed_keys)),
IncompleteCacheAction::Clear => IncompleteCacheAction::Clear,
}
}
}
impl<N> IncompleteCachedBuildData<N> {
/// Create new cached data.
pub(crate) fn new() -> Self {
IncompleteCachedBuildData { digest_input_blocks: Vec::new(), changed_keys: HashMap::new() }
}
fn complete<H>(self, block: N, trie_root: H) -> CachedBuildData<H, N> {
CachedBuildData {
block,
trie_root,
digest_input_blocks: self.digest_input_blocks,
changed_keys: self.changed_keys,
}
}
fn set_digest_input_blocks(mut self, digest_input_blocks: Vec<N>) -> Self {
self.digest_input_blocks = digest_input_blocks;
self
}
fn insert(
mut self,
storage_key: Option<PrefixedStorageKey>,
changed_keys: HashSet<StorageKey>,
) -> Self {
self.changed_keys.insert(storage_key, changed_keys);
self
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn updated_keys_are_stored_when_non_top_level_digest_is_built() {
let mut data = IncompleteCachedBuildData::<u32>::new();
data = data.insert(None, vec![vec![1]].into_iter().collect());
assert_eq!(data.changed_keys.len(), 1);
let mut cache = BuildCache::new();
cache.perform(CacheAction::CacheBuildData(data.complete(1, 1)));
assert_eq!(cache.changed_keys.len(), 1);
assert_eq!(
cache.get(&1).unwrap().clone(),
vec![(None, vec![vec![1]].into_iter().collect())].into_iter().collect(),
);
}
#[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),
));
assert_eq!(cache.changed_keys.len(), 3);
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),
));
assert_eq!(cache.changed_keys.len(), 3);
cache.perform(CacheAction::Clear);
assert_eq!(cache.changed_keys.len(), 0);
}
}
@@ -1,487 +0,0 @@
// This file is part of Substrate.
// Copyright (C) 2017-2021 Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! 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;
/// Returns iterator of OTHER blocks that are required for inclusion into
/// changes trie of given block. Blocks are guaranteed to be returned in
/// ascending order.
///
/// Skewed digest is built IF block >= config.end.
pub fn digest_build_iterator<'a, Number: BlockNumber>(
config: ConfigurationRange<'a, Number>,
block: Number,
) -> DigestBuildIterator<Number> {
// prepare digest build parameters
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,
)
}
/// Changes trie build iterator that returns numbers of OTHER blocks that are
/// required for inclusion into changes trie of given block.
#[derive(Debug)]
pub struct DigestBuildIterator<Number: BlockNumber> {
/// Block we're building changes trie for. It could (logically) be a post-end block if we are
/// creating skewed digest.
block: Number,
/// Block that is a last block where current configuration is active. We have never yet created
/// anything after this block => digest that we're creating can't reference any blocks that are
/// >= end.
end: Number,
/// Interval of L1 digest blocks.
digest_interval: u32,
/// Max step that could be used when digest is created.
max_step: u32,
// Mutable data below:
/// Step of current blocks range.
current_step: u32,
/// Reverse step of current blocks range.
current_step_reverse: u32,
/// Current blocks range.
current_range: Option<BlocksRange<Number>>,
/// Last block that we have returned.
last_block: Option<Number>,
}
impl<Number: BlockNumber> DigestBuildIterator<Number> {
/// Create new digest build iterator.
pub fn new(block: Number, end: Number, digest_interval: u32, max_step: u32) -> Self {
DigestBuildIterator {
block,
end,
digest_interval,
max_step,
current_step: max_step,
current_step_reverse: 0,
current_range: None,
last_block: None,
}
}
/// Create empty digest build iterator.
pub fn empty() -> Self {
Self::new(Zero::zero(), Zero::zero(), 0, 0)
}
}
impl<Number: BlockNumber> Iterator for DigestBuildIterator<Number> {
type Item = Number;
fn next(&mut self) -> Option<Self::Item> {
// when we're building skewed digest, we might want to skip some blocks if
// they're not covered by current configuration
loop {
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)
}
}
// we are safe to use non-checking mul/sub versions here because:
// DigestBuildIterator is created only by internal function that is checking
// that all multiplications/subtractions are safe within max_step limit
let next_step_reverse = if self.current_step_reverse == 0 {
1
} else {
self.current_step_reverse * self.digest_interval
};
if next_step_reverse > self.max_step {
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(),
},
self.block.clone(),
self.current_step.into(),
));
self.current_step = self.current_step / self.digest_interval;
if self.current_step == 0 {
self.current_step = 1;
}
}
}
}
/// Blocks range iterator with builtin step_by support.
#[derive(Debug)]
struct BlocksRange<Number: BlockNumber> {
current: Number,
end: Number,
step: Number,
}
impl<Number: BlockNumber> BlocksRange<Number> {
pub fn new(begin: Number, end: Number, step: Number) -> Self {
BlocksRange { current: begin, end, step }
}
}
impl<Number: BlockNumber> Iterator for BlocksRange<Number> {
type Item = Number;
fn next(&mut self) -> Option<Self::Item> {
if self.current >= self.end {
return None
}
let current = Some(self.current.clone());
self.current += self.step.clone();
current
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::changes_trie::Configuration;
fn digest_build_iterator(
digest_interval: u32,
digest_levels: u32,
zero: u64,
block: u64,
end: Option<u64>,
) -> DigestBuildIterator<u64> {
super::digest_build_iterator(
ConfigurationRange {
config: &Configuration { digest_interval, digest_levels },
zero,
end,
},
block,
)
}
fn digest_build_iterator_basic(
digest_interval: u32,
digest_levels: u32,
zero: u64,
block: u64,
) -> (u64, u32, u32) {
let iter = digest_build_iterator(digest_interval, digest_levels, zero, block, None);
(iter.block, iter.digest_interval, iter.max_step)
}
fn digest_build_iterator_blocks(
digest_interval: u32,
digest_levels: u32,
zero: u64,
block: u64,
end: Option<u64>,
) -> Vec<u64> {
digest_build_iterator(digest_interval, digest_levels, zero, block, end).collect()
}
#[test]
fn suggest_digest_inclusion_returns_empty_iterator() {
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(4, 16, zero, zero + 1),
empty,
"digest is not required for this block",
);
assert_eq!(
digest_build_iterator_basic(4, 16, zero, zero + 2),
empty,
"digest is not required for this block",
);
assert_eq!(
digest_build_iterator_basic(4, 16, zero, zero + 15),
empty,
"digest is not required for this block",
);
assert_eq!(
digest_build_iterator_basic(4, 16, zero, zero + 17),
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"
);
}
test_with_zero(0);
test_with_zero(1);
test_with_zero(2);
test_with_zero(4);
test_with_zero(17);
}
#[test]
fn suggest_digest_inclusion_returns_level1_iterator() {
fn test_with_zero(zero: u64) {
assert_eq!(
digest_build_iterator_basic(16, 1, zero, zero + 16),
(zero + 16, 16, 1),
"!(block % interval) && first digest level == block",
);
assert_eq!(
digest_build_iterator_basic(16, 1, zero, zero + 256),
(zero + 256, 16, 1),
"!(block % interval^2), but there's only 1 digest level",
);
assert_eq!(
digest_build_iterator_basic(16, 2, zero, zero + 32),
(zero + 32, 16, 1),
"second level digest is not required for this block",
);
assert_eq!(
digest_build_iterator_basic(16, 3, zero, zero + 4080),
(zero + 4080, 16, 1),
"second && third level digest are not required for this block",
);
}
test_with_zero(0);
test_with_zero(16);
test_with_zero(17);
}
#[test]
fn suggest_digest_inclusion_returns_level2_iterator() {
fn test_with_zero(zero: u64) {
assert_eq!(
digest_build_iterator_basic(16, 2, zero, zero + 256),
(zero + 256, 16, 16),
"second level digest",
);
assert_eq!(
digest_build_iterator_basic(16, 2, zero, zero + 4096),
(zero + 4096, 16, 16),
"!(block % interval^3), but there's only 2 digest levels",
);
}
test_with_zero(0);
test_with_zero(16);
test_with_zero(17);
}
#[test]
fn suggest_digest_inclusion_returns_level3_iterator() {
fn test_with_zero(zero: u64) {
assert_eq!(
digest_build_iterator_basic(16, 3, zero, zero + 4096),
(zero + 4096, 16, 256),
"third level digest: beginning",
);
assert_eq!(
digest_build_iterator_basic(16, 3, zero, zero + 8192),
(zero + 8192, 16, 256),
"third level digest: next",
);
}
test_with_zero(0);
test_with_zero(16);
test_with_zero(17);
}
#[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),
[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),
[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),
[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<_>>()
);
}
test_with_zero(0);
test_with_zero(16);
test_with_zero(17);
}
#[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),
[
// 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<_>>(),
);
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<_>>(),
);
}
test_with_zero(0);
test_with_zero(16);
test_with_zero(17);
}
#[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),
[
// 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<_>>(),
);
}
test_with_zero(0);
test_with_zero(16);
test_with_zero(17);
}
#[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)),
[
// 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 only 3:
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<_>>(),
);
}
test_with_zero(0);
test_with_zero(16);
test_with_zero(17);
}
#[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)),
[
// 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<_>>(),
);
}
test_with_zero(0);
test_with_zero(16);
test_with_zero(17);
}
}
@@ -1,748 +0,0 @@
// This file is part of Substrate.
// Copyright (C) 2017-2021 Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Functions + iterator that traverses changes tries and returns all
//! (block, extrinsic) pairs where given key has been changed.
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 std::{cell::RefCell, collections::VecDeque};
/// Return changes of given key at given blocks range.
/// `max` is the number of best known block.
/// Changes are returned in descending order (i.e. last block comes first).
pub fn key_changes<'a, H: Hasher, Number: BlockNumber>(
config: ConfigurationRange<'a, Number>,
storage: &'a dyn Storage<H, Number>,
begin: Number,
end: &'a AnchorBlockId<H::Out, Number>,
max: Number,
storage_key: Option<&'a PrefixedStorageKey>,
key: &'a [u8],
) -> Result<DrilldownIterator<'a, H, Number>, String> {
// we can't query any roots before root
let max = std::cmp::min(max, end.number.clone());
Ok(DrilldownIterator {
essence: DrilldownIteratorEssence {
storage_key,
key,
roots_storage: storage.as_roots_storage(),
storage,
begin: begin.clone(),
end,
config: config.clone(),
surface: surface_iterator(config, max, begin, end.number.clone())?,
extrinsics: Default::default(),
blocks: Default::default(),
_hasher: ::std::marker::PhantomData::<H>::default(),
},
})
}
/// 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>(
config: ConfigurationRange<'a, Number>,
storage: &dyn Storage<H, Number>,
begin: Number,
end: &AnchorBlockId<H::Out, Number>,
max: Number,
storage_key: Option<&PrefixedStorageKey>,
key: &[u8],
) -> 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());
let mut iter = ProvingDrilldownIterator {
essence: DrilldownIteratorEssence {
storage_key,
key,
roots_storage: storage.as_roots_storage(),
storage,
begin: begin.clone(),
end,
config: config.clone(),
surface: surface_iterator(config, max, begin, end.number.clone())?,
extrinsics: Default::default(),
blocks: Default::default(),
_hasher: ::std::marker::PhantomData::<H>::default(),
},
proof_recorder: Default::default(),
};
// iterate to collect proof
while let Some(item) = iter.next() {
item?;
}
Ok(iter.extract_proof())
}
/// Check key changes proof and return changes of the key at given blocks range.
/// `max` is the number of best known block.
/// Changes are returned in descending order (i.e. last block comes first).
pub fn key_changes_proof_check<'a, H: Hasher, Number: BlockNumber>(
config: ConfigurationRange<'a, Number>,
roots_storage: &dyn RootsStorage<H, Number>,
proof: Vec<Vec<u8>>,
begin: Number,
end: &AnchorBlockId<H::Out, Number>,
max: Number,
storage_key: Option<&PrefixedStorageKey>,
key: &[u8],
) -> Result<Vec<(Number, u32)>, String>
where
H::Out: Encode,
{
key_changes_proof_check_with_db(
config,
roots_storage,
&InMemoryStorage::with_proof(proof),
begin,
end,
max,
storage_key,
key,
)
}
/// Similar to the `key_changes_proof_check` function, but works with prepared proof storage.
pub fn key_changes_proof_check_with_db<'a, H: Hasher, Number: BlockNumber>(
config: ConfigurationRange<'a, Number>,
roots_storage: &dyn RootsStorage<H, Number>,
proof_db: &InMemoryStorage<H, Number>,
begin: Number,
end: &AnchorBlockId<H::Out, Number>,
max: Number,
storage_key: Option<&PrefixedStorageKey>,
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());
DrilldownIterator {
essence: DrilldownIteratorEssence {
storage_key,
key,
roots_storage,
storage: proof_db,
begin: begin.clone(),
end,
config: config.clone(),
surface: surface_iterator(config, max, begin, end.number.clone())?,
extrinsics: Default::default(),
blocks: Default::default(),
_hasher: ::std::marker::PhantomData::<H>::default(),
},
}
.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,
{
storage_key: Option<&'a PrefixedStorageKey>,
key: &'a [u8],
roots_storage: &'a dyn RootsStorage<H, Number>,
storage: &'a dyn Storage<H, Number>,
begin: Number,
end: &'a AnchorBlockId<H::Out, Number>,
config: ConfigurationRange<'a, Number>,
surface: SurfaceIterator<'a, Number>,
extrinsics: VecDeque<(Number, u32)>,
blocks: VecDeque<(Number, Option<u32>)>,
_hasher: ::std::marker::PhantomData<H>,
}
impl<'a, H, Number> DrilldownIteratorEssence<'a, H, Number>
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>,
{
match self.do_next(trie_reader) {
Ok(Some(res)) => Some(Ok(res)),
Ok(None) => None,
Err(err) => Some(Err(err)),
}
}
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>,
{
loop {
if let Some((block, extrinsic)) = self.extrinsics.pop_front() {
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 = if let Some(storage_key) = self.storage_key {
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| {
let mut hash = H::Out::default();
hash.as_mut().copy_from_slice(&v[..]);
hash
}) {
trie_root
} else {
continue
}
} else {
trie_root
};
// 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"
);
if block <= self.end.number {
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)));
}
}
}
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[..]) {
// filter level0 blocks here because we tend to use digest blocks,
// AND digest block changes could also include changes for out-of-range
// blocks
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)
}),
);
}
}
continue
}
match self.surface.next() {
Some(Ok(block)) => self.blocks.push_back(block),
Some(Err(err)) => return Err(err),
None => return Ok(None),
}
}
}
}
/// Exploring drilldown operator.
pub struct DrilldownIterator<'a, H, Number>
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,
{
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)
})
}
}
/// Proving drilldown iterator.
struct ProvingDrilldownIterator<'a, H, Number>
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,
{
/// 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()
.into_iter()
.map(|n| n.data.to_vec())
.collect()
}
}
impl<'a, H, Number> Iterator for ProvingDrilldownIterator<'a, H, Number>
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()
.expect("only fails when already borrowed; storage() is non-reentrant; qed");
self.essence.next(|storage, root, key| {
ProvingBackendRecorder::<_, H> {
backend: &TrieBackendEssence::new(TrieBackendAdapter::new(storage), root),
proof_recorder,
}
.storage(key)
})
}
}
#[cfg(test)]
mod tests {
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"[..]);
child_info.prefixed_storage_key()
}
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],
),
],
),
],
)],
);
(config, backend)
}
fn configuration_range<'a>(
config: &'a Configuration,
zero: u64,
) -> ConfigurationRange<'a, u64> {
ConfigurationRange { config, zero, end: None }
}
#[test]
fn drilldown_iterator_works() {
let (config, storage) = prepare_for_drilldown();
let drilldown_result = key_changes::<BlakeTwo256, u64>(
configuration_range(&config, 0),
&storage,
1,
&AnchorBlockId { hash: Default::default(), number: 16 },
16,
None,
&[42],
)
.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>(
configuration_range(&config, 0),
&storage,
1,
&AnchorBlockId { hash: Default::default(), number: 2 },
4,
None,
&[42],
)
.and_then(Result::from_iter);
assert_eq!(drilldown_result, Ok(vec![]));
let drilldown_result = key_changes::<BlakeTwo256, u64>(
configuration_range(&config, 0),
&storage,
1,
&AnchorBlockId { hash: Default::default(), number: 3 },
4,
None,
&[42],
)
.and_then(Result::from_iter);
assert_eq!(drilldown_result, Ok(vec![(3, 0)]));
let drilldown_result = key_changes::<BlakeTwo256, u64>(
configuration_range(&config, 0),
&storage,
1,
&AnchorBlockId { hash: Default::default(), number: 7 },
7,
None,
&[42],
)
.and_then(Result::from_iter);
assert_eq!(drilldown_result, Ok(vec![(6, 3), (3, 0)]));
let drilldown_result = key_changes::<BlakeTwo256, u64>(
configuration_range(&config, 0),
&storage,
7,
&AnchorBlockId { hash: Default::default(), number: 8 },
8,
None,
&[42],
)
.and_then(Result::from_iter);
assert_eq!(drilldown_result, Ok(vec![(8, 2), (8, 1)]));
let drilldown_result = key_changes::<BlakeTwo256, u64>(
configuration_range(&config, 0),
&storage,
5,
&AnchorBlockId { hash: Default::default(), number: 7 },
8,
None,
&[42],
)
.and_then(Result::from_iter);
assert_eq!(drilldown_result, Ok(vec![(6, 3)]));
}
#[test]
fn drilldown_iterator_fails_when_storage_fails() {
let (config, storage) = prepare_for_drilldown();
storage.clear_storage();
assert!(key_changes::<BlakeTwo256, u64>(
configuration_range(&config, 0),
&storage,
1,
&AnchorBlockId { hash: Default::default(), number: 100 },
1000,
None,
&[42],
)
.and_then(|i| i.collect::<Result<Vec<_>, _>>())
.is_err());
assert!(key_changes::<BlakeTwo256, u64>(
configuration_range(&config, 0),
&storage,
1,
&AnchorBlockId { hash: Default::default(), number: 100 },
1000,
Some(&child_key()),
&[42],
)
.and_then(|i| i.collect::<Result<Vec<_>, _>>())
.is_err());
}
#[test]
fn drilldown_iterator_fails_when_range_is_invalid() {
let (config, storage) = prepare_for_drilldown();
assert!(key_changes::<BlakeTwo256, u64>(
configuration_range(&config, 0),
&storage,
1,
&AnchorBlockId { hash: Default::default(), number: 100 },
50,
None,
&[42],
)
.is_err());
assert!(key_changes::<BlakeTwo256, u64>(
configuration_range(&config, 0),
&storage,
20,
&AnchorBlockId { hash: Default::default(), number: 10 },
100,
None,
&[42],
)
.is_err());
}
#[test]
fn proving_drilldown_iterator_works() {
// happens on remote full node:
// 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();
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();
// happens on local light node:
// create drilldown iterator that works the same, but only depends on trie
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],
);
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],
);
// 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)]));
assert_eq!(local_result_child, Ok(vec![(16, 5), (2, 3)]));
}
#[test]
fn drilldown_iterator_works_with_skewed_digest() {
let config = Configuration { digest_interval: 4, digest_levels: 3 };
let mut config_range = configuration_range(&config, 0);
config_range.end = Some(91);
// when 4^3 deactivates at block 91:
// last L3 digest has been created at block#64
// skewed digest covers:
// L2 digests at blocks: 80
// L1 digests at blocks: 84, 88
// 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]));
// 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]));
let storage = InMemoryStorage::with_inputs(input, vec![]);
let drilldown_result = key_changes::<BlakeTwo256, u64>(
config_range,
&storage,
1,
&AnchorBlockId { hash: Default::default(), number: 91 },
100_000u64,
None,
&[42],
)
.and_then(Result::from_iter);
assert_eq!(drilldown_result, Ok(vec![(79, 1), (63, 0)]));
}
}
@@ -1,207 +0,0 @@
// This file is part of Substrate.
// Copyright (C) 2017-2021 Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Different types of changes trie input pairs.
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.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ExtrinsicIndex<Number: BlockNumber> {
/// Block at which this key has been inserted in the trie.
pub block: Number,
/// Storage key this node is responsible for.
pub key: StorageKey,
}
/// Value of { changed key => set of extrinsic indices } mapping.
pub type ExtrinsicIndexValue = Vec<u32>;
/// Key of { changed key => block/digest block numbers } mapping.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct DigestIndex<Number: BlockNumber> {
/// Block at which this key has been inserted in the trie.
pub block: Number,
/// Storage key this node is responsible for.
pub key: StorageKey,
}
/// Key of { childtrie key => Childchange trie } mapping.
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct ChildIndex<Number: BlockNumber> {
/// Block at which this key has been inserted in the trie.
pub block: Number,
/// Storage key this node is responsible for.
pub storage_key: PrefixedStorageKey,
}
/// Value of { changed key => block/digest block numbers } mapping.
pub type DigestIndexValue<Number> = Vec<Number>;
/// Value of { changed key => block/digest block numbers } mapping.
/// That is the root of the child change trie.
pub type ChildIndexValue = Vec<u8>;
/// Single input pair of changes trie.
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum InputPair<Number: BlockNumber> {
/// Element of { key => set of extrinsics where key has been changed } element mapping.
ExtrinsicIndex(ExtrinsicIndex<Number>, ExtrinsicIndexValue),
/// Element of { key => set of blocks/digest blocks where key has been changed } element
/// mapping.
DigestIndex(DigestIndex<Number>, DigestIndexValue<Number>),
/// Element of { childtrie key => Childchange trie } where key has been changed } element
/// mapping.
ChildIndex(ChildIndex<Number>, ChildIndexValue),
}
/// Single input key of changes trie.
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum InputKey<Number: BlockNumber> {
/// Key of { key => set of extrinsics where key has been changed } element mapping.
ExtrinsicIndex(ExtrinsicIndex<Number>),
/// Key of { key => set of blocks/digest blocks where key has been changed } element mapping.
DigestIndex(DigestIndex<Number>),
/// Key of { childtrie key => Childchange trie } where key has been changed } element mapping.
ChildIndex(ChildIndex<Number>),
}
impl<Number: BlockNumber> InputPair<Number> {
/// Extract storage key that this pair corresponds to.
pub fn key(&self) -> Option<&[u8]> {
match *self {
InputPair::ExtrinsicIndex(ref key, _) => Some(&key.key),
InputPair::DigestIndex(ref key, _) => Some(&key.key),
InputPair::ChildIndex(_, _) => None,
}
}
}
impl<Number: BlockNumber> Into<(StorageKey, StorageValue)> for InputPair<Number> {
fn into(self) -> (StorageKey, StorageValue) {
match self {
InputPair::ExtrinsicIndex(key, value) => (key.encode(), value.encode()),
InputPair::DigestIndex(key, value) => (key.encode(), value.encode()),
InputPair::ChildIndex(key, value) => (key.encode(), value.encode()),
}
}
}
impl<Number: BlockNumber> Into<InputKey<Number>> for InputPair<Number> {
fn into(self) -> InputKey<Number> {
match self {
InputPair::ExtrinsicIndex(key, _) => InputKey::ExtrinsicIndex(key),
InputPair::DigestIndex(key, _) => InputKey::DigestIndex(key),
InputPair::ChildIndex(key, _) => InputKey::ChildIndex(key),
}
}
}
impl<Number: BlockNumber> ExtrinsicIndex<Number> {
pub fn key_neutral_prefix(block: Number) -> Vec<u8> {
let mut prefix = vec![1];
prefix.extend(block.encode());
prefix
}
}
impl<Number: BlockNumber> Encode for ExtrinsicIndex<Number> {
fn encode_to<W: Output + ?Sized>(&self, dest: &mut W) {
dest.push_byte(1);
self.block.encode_to(dest);
self.key.encode_to(dest);
}
}
impl<Number: BlockNumber> codec::EncodeLike for ExtrinsicIndex<Number> {}
impl<Number: BlockNumber> DigestIndex<Number> {
pub fn key_neutral_prefix(block: Number) -> Vec<u8> {
let mut prefix = vec![2];
prefix.extend(block.encode());
prefix
}
}
impl<Number: BlockNumber> Encode for DigestIndex<Number> {
fn encode_to<W: Output + ?Sized>(&self, dest: &mut W) {
dest.push_byte(2);
self.block.encode_to(dest);
self.key.encode_to(dest);
}
}
impl<Number: BlockNumber> ChildIndex<Number> {
pub fn key_neutral_prefix(block: Number) -> Vec<u8> {
let mut prefix = vec![3];
prefix.extend(block.encode());
prefix
}
}
impl<Number: BlockNumber> Encode for ChildIndex<Number> {
fn encode_to<W: Output + ?Sized>(&self, dest: &mut W) {
dest.push_byte(3);
self.block.encode_to(dest);
self.storage_key.encode_to(dest);
}
}
impl<Number: BlockNumber> codec::EncodeLike for DigestIndex<Number> {}
impl<Number: BlockNumber> Decode for InputKey<Number> {
fn decode<I: Input>(input: &mut I) -> Result<Self, Error> {
match input.read_byte()? {
1 => Ok(InputKey::ExtrinsicIndex(ExtrinsicIndex {
block: Decode::decode(input)?,
key: Decode::decode(input)?,
})),
2 => Ok(InputKey::DigestIndex(DigestIndex {
block: Decode::decode(input)?,
key: Decode::decode(input)?,
})),
3 => Ok(InputKey::ChildIndex(ChildIndex {
block: Decode::decode(input)?,
storage_key: PrefixedStorageKey::new(Decode::decode(input)?),
})),
_ => Err("Invalid input key variant".into()),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn extrinsic_index_serialized_and_deserialized() {
let original = ExtrinsicIndex { block: 777u64, key: vec![42] };
let serialized = original.encode();
let deserialized: InputKey<u64> = Decode::decode(&mut &serialized[..]).unwrap();
assert_eq!(InputKey::ExtrinsicIndex(original), deserialized);
}
#[test]
fn digest_index_serialized_and_deserialized() {
let original = DigestIndex { block: 777u64, key: vec![42] };
let serialized = original.encode();
let deserialized: InputKey<u64> = Decode::decode(&mut &serialized[..]).unwrap();
assert_eq!(InputKey::DigestIndex(original), deserialized);
}
}
@@ -1,428 +0,0 @@
// This file is part of Substrate.
// Copyright (C) 2017-2021 Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Changes trie related structures and functions.
//!
//! Changes trie is a trie built of { storage key => extrinsics } pairs
//! at the end of each block. For every changed storage key it contains
//! a pair, mapping key to the set of extrinsics where it has been changed.
//!
//! Optionally, every N blocks, additional level1-digest nodes are appended
//! to the changes trie, containing pairs { storage key => blocks }. For every
//! storage key that has been changed in PREVIOUS N-1 blocks (except for genesis
//! block) it contains a pair, mapping this key to the set of blocks where it
//! has been changed.
//!
//! Optionally, every N^digest_level (where digest_level > 1) blocks, additional
//! digest_level digest is created. It is built out of pairs { storage key => digest
//! block }, containing entries for every storage key that has been changed in
//! the last N*digest_level-1 blocks (except for genesis block), mapping these keys
//! to the set of lower-level digest blocks.
//!
//! Changes trie configuration could change within a time. The range of blocks, where
//! configuration has been active, is given by two blocks: zero and end. Zero block is
//! the block where configuration has been set. But the first changes trie that uses
//! this configuration will be built at the block zero+1. If configuration deactivates
//! at some block, this will be the end block of the configuration. It is also the
//! zero block of the next configuration.
//!
//! If configuration has the end block, it also means that 'skewed digest' has/should
//! been built at that block. If this is the block where max-level digest should have
//! been created, than it is simply max-level digest of this configuration. Otherwise,
//! it is the digest that covers all blocks since last max-level digest block was
//! created.
//!
//! Changes trie only contains the top level storage changes. Sub-level changes
//! are propagated through its storage root on the top level storage.
mod build;
mod build_cache;
mod build_iterator;
mod changes_iterator;
mod input;
mod prune;
mod storage;
mod surface_iterator;
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,
};
use crate::{
backend::Backend,
changes_trie::{
build::prepare_input,
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
{
}
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)]
pub struct AnchorBlockId<Hash: std::fmt::Debug, Number: BlockNumber> {
/// Hash of this block.
pub hash: Hash,
/// Number of this block.
pub number: Number,
}
/// Changes tries state at some block.
pub struct State<'a, H, Number> {
/// Configuration that is active at given block.
pub config: Configuration,
/// Configuration activation block number. Zero if it is the first configuration on the chain,
/// or number of the block that have emit NewConfiguration signal (thus activating
/// configuration starting from the **next** block).
pub zero: Number,
/// Underlying changes tries storage reference.
pub storage: &'a dyn Storage<H, Number>,
}
/// Changes trie storage. Provides access to trie roots and trie nodes.
pub trait RootsStorage<H: Hasher, Number: BlockNumber>: Send + Sync {
/// Resolve hash of the block into anchor.
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>;
}
/// Changes trie storage. Provides access to trie roots and trie nodes.
pub trait Storage<H: Hasher, Number: BlockNumber>: RootsStorage<H, Number> {
/// Casts from self reference to RootsStorage reference.
fn as_roots_storage(&self) -> &dyn RootsStorage<H, Number>;
/// Execute given functor with cached entry for given trie root.
/// Returns true if the functor has been called (cache entry exists) and false otherwise.
fn with_cached_changed_keys(
&self,
root: &H::Out,
functor: &mut dyn FnMut(&HashMap<Option<PrefixedStorageKey>, HashSet<StorageKey>>),
) -> bool;
/// Get a trie node.
fn get(&self, key: &H::Out, prefix: Prefix) -> Result<Option<DBValue>, String>;
}
/// Changes trie storage -> trie backend essence adapter.
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>
{
type Overlay = sp_trie::MemoryDB<H>;
fn get(&self, key: &H::Out, prefix: Prefix) -> Result<Option<DBValue>, String> {
self.0.get(key, prefix)
}
}
/// Changes trie configuration.
pub type Configuration = sp_core::ChangesTrieConfiguration;
/// Blocks range where configuration has been constant.
#[derive(Clone)]
pub struct ConfigurationRange<'a, N> {
/// Active configuration.
pub config: &'a Configuration,
/// Zero block of this configuration. The configuration is active starting from the next block.
pub zero: N,
/// End block of this configuration. It is the last block where configuration has been active.
pub end: Option<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 }
}
}
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 }
}
}
/// Create state where changes tries are disabled.
pub fn disabled_state<'a, H, Number>() -> Option<State<'a, H, Number>> {
None
}
/// Compute the changes trie root and transaction for given block.
/// Returns Err(()) if unknown `parent_hash` has been passed.
/// Returns Ok(None) if there's no data to perform computation.
/// Panics if background storage returns an error OR if insert to MemoryDB fails.
pub fn build_changes_trie<'a, B: Backend<H>, H: Hasher, Number: BlockNumber>(
backend: &B,
state: Option<&'a State<'a, H, Number>>,
changes: &OverlayedChanges,
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,
{
/// 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
)
} else {
Err(())
}
})
}
// when storage isn't provided, changes tries aren't created
let state = match state {
Some(state) => state,
None => return Ok(None),
};
// build_anchor error should not be considered fatal
let parent = state.storage.build_anchor(parent_hash).map_err(|_| ())?;
let block = parent.number.clone() + One::one();
// 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 config_range = ConfigurationRange {
config: &state.config,
zero: state.zero.clone(),
end: if is_config_changed { Some(block.clone()) } else { None },
};
// storage errors are considered fatal (similar to situations when runtime fetches values from
// storage)
let (input_pairs, child_input_pairs, digest_input_blocks) = maybe_panic(
prepare_input::<B, H, Number>(
backend,
state.storage,
config_range.clone(),
changes,
&parent,
),
panic_on_storage_error,
)?;
// prepare cached data
let mut cache_action = prepare_cached_build_data(config_range, block.clone());
let needs_changed_keys = cache_action.collects_changed_keys();
cache_action = cache_action.set_digest_input_blocks(digest_input_blocks);
let mut mdb = MemoryDB::default();
let mut child_roots = Vec::with_capacity(child_input_pairs.len());
for (child_index, input_pairs) in child_input_pairs {
let mut not_empty = false;
let mut root = Default::default();
{
let mut trie = TrieDBMut::<H>::new(&mut mdb, &mut root);
let mut storage_changed_keys = HashSet::new();
for input_pair in input_pairs {
if needs_changed_keys {
if let Some(key) = input_pair.key() {
storage_changed_keys.insert(key.to_vec());
}
}
let (key, value) = input_pair.into();
not_empty = true;
maybe_panic(trie.insert(&key, &value), panic_on_storage_error)?;
}
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()));
}
}
let mut root = Default::default();
{
let mut trie = TrieDBMut::<H>::new(&mut mdb, &mut root);
for (key, value) in child_roots.into_iter().map(Into::into) {
maybe_panic(trie.insert(&key, &value), panic_on_storage_error)?;
}
let mut storage_changed_keys = HashSet::new();
for input_pair in input_pairs {
if needs_changed_keys {
if let Some(key) = input_pair.key() {
storage_changed_keys.insert(key.to_vec());
}
}
let (key, value) = input_pair.into();
maybe_panic(trie.insert(&key, &value), panic_on_storage_error)?;
}
cache_action = cache_action.insert(None, storage_changed_keys);
}
let cache_action = cache_action.complete(block, &root);
Ok(Some((mdb, root, cache_action)))
}
/// Prepare empty cached build data for given block.
fn prepare_cached_build_data<Number: BlockNumber>(
config: ConfigurationRange<Number>,
block: Number,
) -> IncompleteCacheAction<Number> {
// when digests are not enabled in configuration, we do not need to cache anything
// 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
}
// 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
}
// 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,
_ => IncompleteCacheAction::CacheBuildData(IncompleteCachedBuildData::new()),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn cache_is_cleared_when_digests_are_disabled() {
let config = Configuration { digest_interval: 0, digest_levels: 0 };
let config_range = ConfigurationRange { zero: 0, end: None, config: &config };
assert_eq!(prepare_cached_build_data(config_range, 8u32), IncompleteCacheAction::Clear);
}
#[test]
fn build_data_is_cached_when_digests_are_enabled() {
let config = Configuration { digest_interval: 8, digest_levels: 2 };
let config_range = ConfigurationRange { zero: 0, end: None, config: &config };
assert!(prepare_cached_build_data(config_range.clone(), 4u32).collects_changed_keys());
assert!(prepare_cached_build_data(config_range.clone(), 7u32).collects_changed_keys());
assert!(prepare_cached_build_data(config_range, 8u32).collects_changed_keys());
}
#[test]
fn cache_is_cleared_when_digests_are_enabled_and_top_level_digest_is_built() {
let config = Configuration { digest_interval: 8, digest_levels: 2 };
let config_range = ConfigurationRange { zero: 0, end: None, config: &config };
assert_eq!(prepare_cached_build_data(config_range, 64u32), IncompleteCacheAction::Clear);
}
#[test]
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
);
}
}
@@ -1,204 +0,0 @@
// This file is part of Substrate.
// Copyright (C) 2017-2021 Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! 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 log::warn;
use num_traits::One;
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
/// `min_blocks_to_keep` blocks. We only prune changes tries at `max_digest_interval`
/// ranges.
pub fn prune<H: Hasher, Number: BlockNumber, F: FnMut(H::Out)>(
storage: &dyn Storage<H, Number>,
first: Number,
last: Number,
current_block: &AnchorBlockId<H::Out, Number>,
mut remove_trie_node: F,
) where
H::Out: Codec,
{
// delete changes trie for every block in range
let mut block = first;
loop {
if block >= last.clone() + One::one() {
break
}
let prev_block = block.clone();
block += One::one();
let block = prev_block;
let root = match storage.root(current_block, block.clone()) {
Ok(Some(root)) => root,
Ok(None) => continue,
Err(error) => {
// try to delete other tries
warn!(target: "trie", "Failed to read changes trie root from DB: {}", error);
continue
},
};
let children_roots = {
let trie_storage = TrieBackendEssence::<_, H>::new(
crate::changes_trie::TrieBackendStorageAdapter(storage),
root,
);
let child_prefix = ChildIndex::key_neutral_prefix(block.clone());
let mut children_roots = Vec::new();
trie_storage.for_key_values_with_prefix(&child_prefix, |mut key, mut value| {
if let Ok(InputKey::ChildIndex::<Number>(_trie_key)) = Decode::decode(&mut key) {
if let Ok(value) = <Vec<u8>>::decode(&mut value) {
let mut trie_root = <H as Hasher>::Out::default();
trie_root.as_mut().copy_from_slice(&value[..]);
children_roots.push(trie_root);
}
}
});
children_roots
};
for root in children_roots.into_iter() {
prune_trie(storage, root, &mut remove_trie_node);
}
prune_trie(storage, root, &mut remove_trie_node);
}
}
// Prune a trie.
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,
{
// 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();
{
let mut trie = ProvingBackendRecorder::<_, H> {
backend: &TrieBackendEssence::new(TrieBackendAdapter::new(storage), root),
proof_recorder: &mut proof_recorder,
};
trie.record_all_keys();
}
// all nodes of this changes trie should be pruned
remove_trie_node(root);
for node in proof_recorder.drain().into_iter().map(|n| n.hash) {
remove_trie_node(node);
}
}
#[cfg(test)]
mod tests {
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>,
first: u64,
last: u64,
current_block: u64,
) -> 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);
});
pruned_trie_nodes
}
#[test]
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 mut mdb1 = MemoryDB::<BlakeTwo256>::default();
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();
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 mut mdb4 = MemoryDB::<BlakeTwo256>::default();
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);
storage.insert(67, root3, mdb3);
storage.insert(68, root4, mdb4);
storage
}
let storage = prepare_storage();
assert!(prune_by_collect(&storage, 20, 30, 90).is_empty());
assert!(!storage.into_mdb().drain().is_empty());
let storage = prepare_storage();
let prune60_65 = prune_by_collect(&storage, 60, 65, 90);
assert!(!prune60_65.is_empty());
storage.remove_from_storage(&prune60_65);
assert!(!storage.into_mdb().drain().is_empty());
let storage = prepare_storage();
let prune60_70 = prune_by_collect(&storage, 60, 70, 90);
assert!(!prune60_70.is_empty());
storage.remove_from_storage(&prune60_70);
assert!(storage.into_mdb().drain().is_empty());
}
}
@@ -1,214 +0,0 @@
// This file is part of Substrate.
// Copyright (C) 2017-2021 Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Changes trie storage utilities.
use crate::{
changes_trie::{AnchorBlockId, BlockNumber, BuildCache, RootsStorage, Storage},
trie_backend_essence::TrieBackendStorage,
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::{ChildIndex, InputPair};
/// In-memory implementation of changes trie storage.
pub struct InMemoryStorage<H: Hasher, Number: BlockNumber> {
data: RwLock<InMemoryStorageData<H, Number>>,
cache: BuildCache<H::Out, Number>,
}
/// Adapter for using changes trie storage as a TrieBackendEssence' storage.
pub struct TrieBackendAdapter<'a, H: Hasher, Number: BlockNumber> {
storage: &'a dyn Storage<H, Number>,
_hasher: std::marker::PhantomData<(H, Number)>,
}
struct InMemoryStorageData<H: Hasher, Number: BlockNumber> {
roots: BTreeMap<Number, H::Out>,
mdb: MemoryDB<H>,
}
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 }),
cache: BuildCache::new(),
}
}
/// Creates storage with empty database.
pub fn new() -> Self {
Self::with_db(Default::default())
}
/// Creates storage with given proof.
pub fn with_proof(proof: Vec<Vec<u8>>) -> Self {
use hash_db::HashDB;
let mut proof_db = MemoryDB::<H>::default();
for item in proof {
proof_db.insert(EMPTY_PREFIX, &item);
}
Self::with_db(proof_db)
}
/// Get mutable cache reference.
pub fn cache_mut(&mut self) -> &mut BuildCache<H::Out, Number> {
&mut self.cache
}
/// Create the storage with given blocks.
pub fn with_blocks(blocks: Vec<(Number, H::Out)>) -> Self {
Self {
data: RwLock::new(InMemoryStorageData {
roots: blocks.into_iter().collect(),
mdb: MemoryDB::default(),
}),
cache: BuildCache::new(),
}
}
#[cfg(test)]
pub fn with_inputs(
mut top_inputs: Vec<(Number, Vec<InputPair<Number>>)>,
children_inputs: Vec<(PrefixedStorageKey, Vec<(Number, Vec<InputPair<Number>>)>)>,
) -> Self {
let mut mdb = MemoryDB::default();
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));
if let Some(root) = root {
let ix = if let Some(ix) = top_inputs.iter().position(|v| v.0 == block) {
ix
} else {
top_inputs.push((block.clone(), Default::default()));
top_inputs.len() - 1
};
top_inputs[ix].1.push(InputPair::ChildIndex(
ChildIndex { block: block.clone(), storage_key: storage_key.clone() },
root.as_ref().to_vec(),
));
}
}
}
for (block, pairs) in top_inputs {
let root = insert_into_memory_db::<H, _>(&mut mdb, pairs.into_iter().map(Into::into));
if let Some(root) = root {
roots.insert(block, root);
}
}
InMemoryStorage {
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
}
#[cfg(test)]
pub fn remove_from_storage(&self, keys: &HashSet<H::Out>) {
let mut data = self.data.write();
for key in keys {
data.mdb.remove_and_purge(key, hash_db::EMPTY_PREFIX);
}
}
#[cfg(test)]
pub fn into_mdb(self) -> MemoryDB<H> {
self.data.into_inner().mdb
}
/// Insert changes trie for given block.
pub fn insert(&self, block: Number, changes_trie_root: H::Out, trie: MemoryDB<H>) {
let mut data = self.data.write();
data.roots.insert(block, changes_trie_root);
data.mdb.consolidate(trie);
}
}
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()
.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> {
Ok(self.data.read().roots.get(&block).cloned())
}
}
impl<H: Hasher, Number: BlockNumber> Storage<H, Number> for InMemoryStorage<H, Number> {
fn as_roots_storage(&self) -> &dyn RootsStorage<H, Number> {
self
}
fn with_cached_changed_keys(
&self,
root: &H::Out,
functor: &mut dyn FnMut(&HashMap<Option<PrefixedStorageKey>, HashSet<StorageKey>>),
) -> bool {
self.cache.with_changed_keys(root, functor)
}
fn get(&self, key: &H::Out, prefix: Prefix) -> Result<Option<DBValue>, String> {
MemoryDB::<H>::get(&self.data.read().mdb, key, prefix)
}
}
impl<'a, H: Hasher, Number: BlockNumber> TrieBackendAdapter<'a, H, Number> {
pub fn new(storage: &'a dyn Storage<H, Number>) -> Self {
Self { storage, _hasher: Default::default() }
}
}
impl<'a, H, Number> TrieBackendStorage<H> for TrieBackendAdapter<'a, H, Number>
where
Number: BlockNumber,
H: Hasher,
{
type Overlay = MemoryDB<H>;
fn get(&self, key: &H::Out, prefix: Prefix) -> Result<Option<DBValue>, String> {
self.storage.get(key, prefix)
}
}
@@ -1,326 +0,0 @@
// This file is part of Substrate.
// Copyright (C) 2017-2021 Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! The best way to understand how this iterator works is to imagine some 2D terrain that have some
//! mountains (digest changes tries) and valleys (changes tries for regular blocks). There are gems
//! (blocks) beneath the terrain. Given the request to find all gems in the range [X1; X2] this
//! iterator will return **minimal set** 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;
/// Returns surface iterator for given range of blocks.
///
/// `max` is the number of best block, known to caller. We can't access any changes tries
/// that are built after this block, even though we may have them built already.
pub fn surface_iterator<'a, Number: BlockNumber>(
config: ConfigurationRange<'a, Number>,
max: Number,
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)?;
Ok(SurfaceIterator {
config,
begin,
max,
current: Some(current),
current_begin,
digest_step,
digest_level,
})
}
/// Surface iterator - only traverses top-level digests from given range and tries to find
/// all valid digest changes.
///
/// Iterator item is the tuple of (last block of the current point + digest level of the current
/// point). Digest level is Some(0) when it is regular block, is Some(non-zero) when it is digest
/// block and None if it is skewed digest block.
pub struct SurfaceIterator<'a, Number: BlockNumber> {
config: ConfigurationRange<'a, Number>,
begin: Number,
max: Number,
current: Option<Number>,
current_begin: Number,
digest_step: u32,
digest_level: Option<u32>,
}
impl<'a, Number: BlockNumber> Iterator for SurfaceIterator<'a, Number> {
type Item = Result<(Number, Option<u32>), String>;
fn next(&mut self) -> Option<Self::Item> {
let current = self.current.clone()?;
let digest_level = self.digest_level;
if current < self.digest_step.into() {
self.current = None;
} else {
let next = current.clone() - self.digest_step.into();
if next.is_zero() || next < self.begin {
self.current = None;
} else if next > self.current_begin {
self.current = Some(next);
} else {
let max_digest_interval = lower_bound_max_digest(
self.config.clone(),
self.max.clone(),
self.begin.clone(),
next,
);
let (current, current_begin, digest_step, digest_level) = match max_digest_interval
{
Err(err) => return Some(Err(err)),
Ok(range) => range,
};
self.current = Some(current);
self.current_begin = current_begin;
self.digest_step = digest_step;
self.digest_level = digest_level;
}
}
Some(Ok((current, digest_level)))
}
}
/// Returns parameters of highest level digest block that includes the end of given range
/// and tends to include the whole range.
fn lower_bound_max_digest<'a, Number: BlockNumber>(
config: ConfigurationRange<'a, Number>,
max: Number,
begin: Number,
end: Number,
) -> Result<(Number, Number, u32, Option<u32>), String> {
if end > max || begin > end {
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() {
Some(config_end) => format!("{}", config_end),
None => "None".into(),
}
))
}
let mut digest_level = 0u32;
let mut digest_step = 1u32;
let mut digest_interval = 0u32;
let mut current = end.clone();
let mut current_begin = begin.clone();
if current_begin != current {
while digest_level != config.config.digest_levels {
// try to use next level digest
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
}
};
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();
// check if we met skewed digest
if let Some(skewed_digest_end) = config.end.as_ref() {
if new_digest_end > *skewed_digest_end {
let skewed_digest_start = config.config.prev_max_level_digest_block(
config.zero.clone(),
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",
);
return Ok((
skewed_digest_end.clone(),
skewed_digest_start,
skewed_digest_range,
None,
))
}
}
}
// we can't use next level digest if it touches any unknown (> max) blocks
if new_digest_end > max {
if begin < new_digest_begin {
current_begin = new_digest_begin;
}
break
}
// we can (and will) use this digest
digest_level = new_digest_level;
digest_step = new_digest_step;
digest_interval = new_digest_interval;
current = new_current;
current_begin = new_digest_begin;
// if current digest covers the whole range => no need to use next level digest
if current_begin <= begin && new_digest_end >= end {
break
}
}
}
Ok((current, current_begin, digest_step, Some(digest_level)))
}
#[cfg(test)]
mod tests {
use super::*;
use crate::changes_trie::Configuration;
fn configuration_range<'a>(
config: &'a Configuration,
zero: u64,
) -> ConfigurationRange<'a, u64> {
ConfigurationRange { config, zero, end: None }
}
#[test]
fn lower_bound_max_digest_works() {
let config = Configuration { digest_interval: 4, digest_levels: 2 };
// when config activates at 0
assert_eq!(
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(),
(222, 206, 16, Some(2)),
);
}
#[test]
fn surface_iterator_works() {
let config = Configuration { digest_interval: 4, digest_levels: 2 };
// when config activates at 0
assert_eq!(
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))),
],
);
// when config activates at 30
assert_eq!(
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))),
],
);
// 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<_>>(),
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))),
],
);
}
#[test]
fn surface_iterator_works_with_skewed_digest() {
let config = Configuration { digest_interval: 4, digest_levels: 2 };
let mut config_range = configuration_range(&config, 0u64);
// 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<_>>(),
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))),
],
);
}
}
+24 -171
View File
@@ -28,8 +28,6 @@ use sp_core::storage::{well_known_keys::is_child_storage_key, ChildInfo, Tracked
use sp_externalities::{Extension, ExtensionStore, Externalities};
use sp_trie::{empty_child_trie_root, trie_types::Layout};
#[cfg(feature = "std")]
use crate::changes_trie::State as ChangesTrieState;
use crate::{log_error, trace, warn, StorageTransactionCache};
use sp_std::{
any::{Any, TypeId},
@@ -90,62 +88,52 @@ 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>
pub struct Ext<'a, H, B>
where
H: Hasher,
B: 'a + Backend<H>,
N: crate::changes_trie::BlockNumber,
{
/// The overlayed changes to write to.
overlay: &'a mut OverlayedChanges,
/// The storage backend to read from.
backend: &'a B,
/// The cache for the storage transactions.
storage_transaction_cache: &'a mut StorageTransactionCache<B::Transaction, H, N>,
/// Changes trie state to read from.
#[cfg(feature = "std")]
changes_trie_state: Option<ChangesTrieState<'a, H, N>>,
storage_transaction_cache: &'a mut StorageTransactionCache<B::Transaction, H>,
/// Pseudo-unique id used for tracing.
pub id: u16,
/// Dummy usage of N arg.
_phantom: sp_std::marker::PhantomData<N>,
/// Extensions registered with this instance.
#[cfg(feature = "std")]
extensions: Option<OverlayedExtensions<'a>>,
}
impl<'a, H, N, B> Ext<'a, H, N, B>
impl<'a, H, B> Ext<'a, H, B>
where
H: Hasher,
B: Backend<H>,
N: crate::changes_trie::BlockNumber,
{
/// Create a new `Ext`.
#[cfg(not(feature = "std"))]
pub fn new(
overlay: &'a mut OverlayedChanges,
storage_transaction_cache: &'a mut StorageTransactionCache<B::Transaction, H, N>,
storage_transaction_cache: &'a mut StorageTransactionCache<B::Transaction, H>,
backend: &'a B,
) -> Self {
Ext { overlay, backend, id: 0, storage_transaction_cache, _phantom: Default::default() }
Ext { overlay, backend, id: 0, storage_transaction_cache }
}
/// Create a new `Ext` from overlayed changes and read-only backend
#[cfg(feature = "std")]
pub fn new(
overlay: &'a mut OverlayedChanges,
storage_transaction_cache: &'a mut StorageTransactionCache<B::Transaction, H, N>,
storage_transaction_cache: &'a mut StorageTransactionCache<B::Transaction, H>,
backend: &'a B,
changes_trie_state: Option<ChangesTrieState<'a, H, N>>,
extensions: Option<&'a mut sp_externalities::Extensions>,
) -> Self {
Self {
overlay,
backend,
changes_trie_state,
storage_transaction_cache,
id: rand::random(),
_phantom: Default::default(),
extensions: extensions.map(OverlayedExtensions::new),
}
}
@@ -159,12 +147,11 @@ where
}
#[cfg(test)]
impl<'a, H, N, B> Ext<'a, H, N, B>
impl<'a, H, B> Ext<'a, H, B>
where
H: Hasher,
H::Out: Ord + 'static,
B: 'a + Backend<H>,
N: crate::changes_trie::BlockNumber,
{
pub fn storage_pairs(&self) -> Vec<(StorageKey, StorageValue)> {
use std::collections::HashMap;
@@ -181,12 +168,11 @@ where
}
}
impl<'a, H, N, B> Externalities for Ext<'a, H, N, B>
impl<'a, H, B> Externalities for Ext<'a, H, B>
where
H: Hasher,
H::Out: Ord + 'static + codec::Codec,
B: Backend<H>,
N: crate::changes_trie::BlockNumber,
{
fn set_offchain_storage(&mut self, key: &[u8], value: Option<&[u8]>) {
self.overlay.set_offchain_storage(key, value)
@@ -644,54 +630,6 @@ where
.add_transaction_index(IndexOperation::Renew { extrinsic: index, hash: hash.to_vec() });
}
#[cfg(not(feature = "std"))]
fn storage_changes_root(&mut self, _parent_hash: &[u8]) -> Result<Option<Vec<u8>>, ()> {
Ok(None)
}
#[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
{
trace!(
target: "state",
method = "ChangesRoot",
ext_id = %HexDisplay::from(&self.id.to_le_bytes()),
parent_hash = %HexDisplay::from(&parent_hash),
?root,
cached = true,
);
Ok(Some(root.encode()))
} else {
let root = self.overlay.changes_trie_root(
self.backend,
self.changes_trie_state.as_ref(),
Decode::decode(&mut parent_hash).map_err(|e| {
trace!(
target: "state",
error = %e,
"Failed to decode changes root parent hash",
)
})?,
true,
self.storage_transaction_cache,
);
trace!(
target: "state",
method = "ChangesRoot",
ext_id = %HexDisplay::from(&self.id.to_le_bytes()),
parent_hash = %HexDisplay::from(&parent_hash),
?root,
cached = false,
);
root.map(|r| r.map(|o| o.encode()))
}
}
fn storage_start_transaction(&mut self) {
self.overlay.start_transaction()
}
@@ -710,13 +648,7 @@ where
self.overlay.rollback_transaction().expect(BENCHMARKING_FN);
}
self.overlay
.drain_storage_changes(
self.backend,
#[cfg(feature = "std")]
None,
Default::default(),
self.storage_transaction_cache,
)
.drain_storage_changes(self.backend, Default::default(), self.storage_transaction_cache)
.expect(EXT_NOT_ALLOWED_TO_FAIL);
self.backend.wipe().expect(EXT_NOT_ALLOWED_TO_FAIL);
self.mark_dirty();
@@ -731,13 +663,7 @@ where
}
let changes = self
.overlay
.drain_storage_changes(
self.backend,
#[cfg(feature = "std")]
None,
Default::default(),
self.storage_transaction_cache,
)
.drain_storage_changes(self.backend, Default::default(), self.storage_transaction_cache)
.expect(EXT_NOT_ALLOWED_TO_FAIL);
self.backend
.commit(
@@ -778,12 +704,11 @@ where
}
}
impl<'a, H, N, B> Ext<'a, H, N, B>
impl<'a, H, B> Ext<'a, H, B>
where
H: Hasher,
H::Out: Ord + 'static + codec::Codec,
B: Backend<H>,
N: crate::changes_trie::BlockNumber,
{
fn limit_remove_from_backend(
&mut self,
@@ -869,12 +794,11 @@ impl<'a> StorageAppend<'a> {
}
#[cfg(not(feature = "std"))]
impl<'a, H, N, B> ExtensionStore for Ext<'a, H, N, B>
impl<'a, H, B> ExtensionStore for Ext<'a, H, B>
where
H: Hasher,
H::Out: Ord + 'static + codec::Codec,
B: Backend<H>,
N: crate::changes_trie::BlockNumber,
{
fn extension_by_type_id(&mut self, _type_id: TypeId) -> Option<&mut dyn Any> {
None
@@ -897,11 +821,10 @@ where
}
#[cfg(feature = "std")]
impl<'a, H, N, B> ExtensionStore for Ext<'a, H, N, B>
impl<'a, H, B> ExtensionStore for Ext<'a, H, B>
where
H: Hasher,
B: 'a + Backend<H>,
N: crate::changes_trie::BlockNumber,
{
fn extension_by_type_id(&mut self, type_id: TypeId) -> Option<&mut dyn Any> {
self.extensions.as_mut().and_then(|exts| exts.get_mut(type_id))
@@ -938,86 +861,16 @@ where
#[cfg(test)]
mod tests {
use super::*;
use crate::{
changes_trie::{
Configuration as ChangesTrieConfiguration, InMemoryStorage as TestChangesTrieStorage,
},
InMemoryBackend,
};
use crate::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,
storage::{Storage, StorageChild},
Blake2Hasher,
};
type TestBackend = InMemoryBackend<Blake2Hasher>;
type TestExt<'a> = Ext<'a, Blake2Hasher, u64, TestBackend>;
fn prepare_overlay_with_changes() -> OverlayedChanges {
let mut changes = OverlayedChanges::default();
changes.set_collect_extrinsics(true);
changes.set_extrinsic_index(1);
changes.set_storage(vec![1], Some(vec![100]));
changes.set_storage(EXTRINSIC_INDEX.to_vec(), Some(3u32.encode()));
changes.set_offchain_storage(b"k1", Some(b"v1"));
changes.set_offchain_storage(b"k2", Some(b"v2"));
changes
}
fn changes_trie_config() -> ChangesTrieConfiguration {
ChangesTrieConfiguration { digest_interval: 0, digest_levels: 0 }
}
#[test]
fn storage_changes_root_is_none_when_storage_is_not_provided() {
let mut overlay = prepare_overlay_with_changes();
let mut cache = StorageTransactionCache::default();
let backend = TestBackend::default();
let mut ext = TestExt::new(&mut overlay, &mut cache, &backend, None, None);
assert_eq!(ext.storage_changes_root(&H256::default().encode()).unwrap(), None);
}
#[test]
fn storage_changes_root_is_none_when_state_is_not_provided() {
let mut overlay = prepare_overlay_with_changes();
let mut cache = StorageTransactionCache::default();
let backend = TestBackend::default();
let mut ext = TestExt::new(&mut overlay, &mut cache, &backend, None, None);
assert_eq!(ext.storage_changes_root(&H256::default().encode()).unwrap(), None);
}
#[test]
fn storage_changes_root_is_some_when_extrinsic_changes_are_non_empty() {
let mut overlay = prepare_overlay_with_changes();
let mut cache = StorageTransactionCache::default();
let storage = TestChangesTrieStorage::with_blocks(vec![(99, Default::default())]);
let state = Some(ChangesTrieState::new(changes_trie_config(), Zero::zero(), &storage));
let backend = TestBackend::default();
let mut ext = TestExt::new(&mut overlay, &mut cache, &backend, state, None);
assert_eq!(
ext.storage_changes_root(&H256::default().encode()).unwrap(),
Some(hex!("bb0c2ef6e1d36d5490f9766cfcc7dfe2a6ca804504c3bb206053890d6dd02376").to_vec()),
);
}
#[test]
fn storage_changes_root_is_some_when_extrinsic_changes_are_empty() {
let mut overlay = prepare_overlay_with_changes();
let mut cache = StorageTransactionCache::default();
overlay.set_collect_extrinsics(false);
overlay.set_storage(vec![1], None);
let storage = TestChangesTrieStorage::with_blocks(vec![(99, Default::default())]);
let state = Some(ChangesTrieState::new(changes_trie_config(), Zero::zero(), &storage));
let backend = TestBackend::default();
let mut ext = TestExt::new(&mut overlay, &mut cache, &backend, state, None);
assert_eq!(
ext.storage_changes_root(&H256::default().encode()).unwrap(),
Some(hex!("96f5aae4690e7302737b6f9b7f8567d5bbb9eac1c315f80101235a92d9ec27f4").to_vec()),
);
}
type TestExt<'a> = Ext<'a, Blake2Hasher, TestBackend>;
#[test]
fn next_storage_key_works() {
@@ -1035,7 +888,7 @@ mod tests {
}
.into();
let ext = TestExt::new(&mut overlay, &mut cache, &backend, None, None);
let ext = TestExt::new(&mut overlay, &mut cache, &backend, None);
// next_backend < next_overlay
assert_eq!(ext.next_storage_key(&[5]), Some(vec![10]));
@@ -1051,7 +904,7 @@ mod tests {
drop(ext);
overlay.set_storage(vec![50], Some(vec![50]));
let ext = TestExt::new(&mut overlay, &mut cache, &backend, None, None);
let ext = TestExt::new(&mut overlay, &mut cache, &backend, None);
// next_overlay exist but next_backend doesn't exist
assert_eq!(ext.next_storage_key(&[40]), Some(vec![50]));
@@ -1079,7 +932,7 @@ mod tests {
}
.into();
let ext = TestExt::new(&mut overlay, &mut cache, &backend, None, None);
let ext = TestExt::new(&mut overlay, &mut cache, &backend, None);
assert_eq!(ext.next_storage_key(&[5]), Some(vec![30]));
@@ -1110,7 +963,7 @@ mod tests {
}
.into();
let ext = TestExt::new(&mut overlay, &mut cache, &backend, None, None);
let ext = TestExt::new(&mut overlay, &mut cache, &backend, None);
// next_backend < next_overlay
assert_eq!(ext.next_child_storage_key(child_info, &[5]), Some(vec![10]));
@@ -1126,7 +979,7 @@ mod tests {
drop(ext);
overlay.set_child_storage(child_info, vec![50], Some(vec![50]));
let ext = TestExt::new(&mut overlay, &mut cache, &backend, None, None);
let ext = TestExt::new(&mut overlay, &mut cache, &backend, None);
// next_overlay exist but next_backend doesn't exist
assert_eq!(ext.next_child_storage_key(child_info, &[40]), Some(vec![50]));
@@ -1155,7 +1008,7 @@ mod tests {
}
.into();
let ext = TestExt::new(&mut overlay, &mut cache, &backend, None, None);
let ext = TestExt::new(&mut overlay, &mut cache, &backend, None);
assert_eq!(ext.child_storage(child_info, &[10]), Some(vec![10]));
assert_eq!(
@@ -1192,7 +1045,7 @@ mod tests {
}
.into();
let ext = TestExt::new(&mut overlay, &mut cache, &backend, None, None);
let ext = TestExt::new(&mut overlay, &mut cache, &backend, None);
use sp_core::storage::well_known_keys;
let mut ext = ext;
+32 -178
View File
@@ -23,8 +23,6 @@
pub mod backend;
#[cfg(feature = "std")]
mod basic;
#[cfg(feature = "std")]
mod changes_trie;
mod error;
mod ext;
#[cfg(feature = "std")]
@@ -140,28 +138,10 @@ pub use crate::{
};
pub use error::{Error, ExecutionError};
#[cfg(not(feature = "std"))]
mod changes_trie {
/// Stub for change trie block number until
/// change trie move to no_std.
pub trait BlockNumber {}
impl<N> BlockNumber for N {}
}
#[cfg(feature = "std")]
mod std_reexport {
pub use crate::{
basic::BasicExternalities,
changes_trie::{
disabled_state as disabled_changes_trie_state, key_changes, key_changes_proof,
key_changes_proof_check, key_changes_proof_check_with_db, prune as prune_changes_tries,
AnchorBlockId as ChangesTrieAnchorBlockId, BlockNumber as ChangesTrieBlockNumber,
BuildCache as ChangesTrieBuildCache, CacheAction as ChangesTrieCacheAction,
ConfigurationRange as ChangesTrieConfigurationRange,
InMemoryStorage as InMemoryChangesTrieStorage, RootsStorage as ChangesTrieRootsStorage,
State as ChangesTrieState, Storage as ChangesTrieStorage,
},
error::{Error, ExecutionError},
in_memory_backend::new_in_mem,
proving_backend::{
@@ -205,10 +185,6 @@ mod execution {
/// Default handler of the execution manager.
pub type DefaultHandler<R, E> = fn(CallResult<R, E>, CallResult<R, E>) -> CallResult<R, E>;
/// Type of changes trie transaction.
pub type ChangesTrieTransaction<H, N> =
(MemoryDB<H>, ChangesTrieCacheAction<<H as Hasher>::Out, N>);
/// Trie backend with in-memory storage.
pub type InMemoryBackend<H> = TrieBackend<MemoryDB<H>, H>;
@@ -308,11 +284,10 @@ mod execution {
}
/// The substrate state machine.
pub struct StateMachine<'a, B, H, N, Exec>
pub struct StateMachine<'a, B, H, Exec>
where
H: Hasher,
B: Backend<H>,
N: ChangesTrieBlockNumber,
{
backend: &'a B,
exec: &'a Exec,
@@ -320,8 +295,7 @@ mod execution {
call_data: &'a [u8],
overlay: &'a mut OverlayedChanges,
extensions: Extensions,
changes_trie_state: Option<ChangesTrieState<'a, H, N>>,
storage_transaction_cache: Option<&'a mut StorageTransactionCache<B::Transaction, H, N>>,
storage_transaction_cache: Option<&'a mut StorageTransactionCache<B::Transaction, H>>,
runtime_code: &'a RuntimeCode<'a>,
stats: StateMachineStats,
/// The hash of the block the state machine will be executed on.
@@ -330,29 +304,26 @@ mod execution {
parent_hash: Option<H::Out>,
}
impl<'a, B, H, N, Exec> Drop for StateMachine<'a, B, H, N, Exec>
impl<'a, B, H, Exec> Drop for StateMachine<'a, B, H, Exec>
where
H: Hasher,
B: Backend<H>,
N: ChangesTrieBlockNumber,
{
fn drop(&mut self) {
self.backend.register_overlay_stats(&self.stats);
}
}
impl<'a, B, H, N, Exec> StateMachine<'a, B, H, N, Exec>
impl<'a, B, H, Exec> StateMachine<'a, B, H, Exec>
where
H: Hasher,
H::Out: Ord + 'static + codec::Codec,
Exec: CodeExecutor + Clone + 'static,
B: Backend<H>,
N: crate::changes_trie::BlockNumber,
{
/// Creates new substrate state machine.
pub fn new(
backend: &'a B,
changes_trie_state: Option<ChangesTrieState<'a, H, N>>,
overlay: &'a mut OverlayedChanges,
exec: &'a Exec,
method: &'a str,
@@ -371,7 +342,6 @@ mod execution {
call_data,
extensions,
overlay,
changes_trie_state,
storage_transaction_cache: None,
runtime_code,
stats: StateMachineStats::default(),
@@ -386,7 +356,7 @@ mod execution {
/// build that will be cached.
pub fn with_storage_transaction_cache(
mut self,
cache: Option<&'a mut StorageTransactionCache<B::Transaction, H, N>>,
cache: Option<&'a mut StorageTransactionCache<B::Transaction, H>>,
) -> Self {
self.storage_transaction_cache = cache;
self
@@ -439,13 +409,7 @@ mod execution {
.enter_runtime()
.expect("StateMachine is never called from the runtime; qed");
let mut ext = Ext::new(
self.overlay,
cache,
self.backend,
self.changes_trie_state.clone(),
Some(&mut self.extensions),
);
let mut ext = Ext::new(self.overlay, cache, self.backend, Some(&mut self.extensions));
let ext_id = ext.id;
@@ -562,9 +526,6 @@ mod execution {
CallResult<R, Exec::Error>,
) -> CallResult<R, Exec::Error>,
{
let changes_tries_enabled = self.changes_trie_state.is_some();
self.overlay.set_collect_extrinsics(changes_tries_enabled);
let result = {
match manager {
ExecutionManager::Both(on_consensus_failure) => self
@@ -588,7 +549,7 @@ mod execution {
}
/// Prove execution using the given state backend, overlayed changes, and call executor.
pub fn prove_execution<B, H, N, Exec, Spawn>(
pub fn prove_execution<B, H, Exec, Spawn>(
backend: &mut B,
overlay: &mut OverlayedChanges,
exec: &Exec,
@@ -602,13 +563,12 @@ mod execution {
H: Hasher,
H::Out: Ord + 'static + codec::Codec,
Exec: CodeExecutor + Clone + 'static,
N: crate::changes_trie::BlockNumber,
Spawn: SpawnNamed + Send + 'static,
{
let trie_backend = backend
.as_trie_backend()
.ok_or_else(|| Box::new(ExecutionError::UnableToGenerateProof) as Box<dyn Error>)?;
prove_execution_on_trie_backend::<_, _, N, _, _>(
prove_execution_on_trie_backend::<_, _, _, _>(
trie_backend,
overlay,
exec,
@@ -628,7 +588,7 @@ mod execution {
///
/// Note: changes to code will be in place if this call is made again. For running partial
/// blocks (e.g. a transaction at a time), ensure a different method is used.
pub fn prove_execution_on_trie_backend<S, H, N, Exec, Spawn>(
pub fn prove_execution_on_trie_backend<S, H, Exec, Spawn>(
trie_backend: &TrieBackend<S, H>,
overlay: &mut OverlayedChanges,
exec: &Exec,
@@ -642,13 +602,11 @@ mod execution {
H: Hasher,
H::Out: Ord + 'static + codec::Codec,
Exec: CodeExecutor + 'static + Clone,
N: crate::changes_trie::BlockNumber,
Spawn: SpawnNamed + Send + 'static,
{
let proving_backend = proving_backend::ProvingBackend::new(trie_backend);
let mut sm = StateMachine::<_, H, N, Exec>::new(
let mut sm = StateMachine::<_, H, Exec>::new(
&proving_backend,
None,
overlay,
exec,
method,
@@ -667,7 +625,7 @@ mod execution {
}
/// Check execution proof, generated by `prove_execution` call.
pub fn execution_proof_check<H, N, Exec, Spawn>(
pub fn execution_proof_check<H, Exec, Spawn>(
root: H::Out,
proof: StorageProof,
overlay: &mut OverlayedChanges,
@@ -681,11 +639,10 @@ mod execution {
H: Hasher,
Exec: CodeExecutor + Clone + 'static,
H::Out: Ord + 'static + codec::Codec,
N: crate::changes_trie::BlockNumber,
Spawn: SpawnNamed + Send + 'static,
{
let trie_backend = create_proof_check_backend::<H>(root.into(), proof)?;
execution_proof_check_on_trie_backend::<_, N, _, _>(
execution_proof_check_on_trie_backend::<_, _, _>(
&trie_backend,
overlay,
exec,
@@ -697,7 +654,7 @@ mod execution {
}
/// Check execution proof on proving backend, generated by `prove_execution` call.
pub fn execution_proof_check_on_trie_backend<H, N, Exec, Spawn>(
pub fn execution_proof_check_on_trie_backend<H, Exec, Spawn>(
trie_backend: &TrieBackend<MemoryDB<H>, H>,
overlay: &mut OverlayedChanges,
exec: &Exec,
@@ -710,12 +667,10 @@ mod execution {
H: Hasher,
H::Out: Ord + 'static + codec::Codec,
Exec: CodeExecutor + Clone + 'static,
N: crate::changes_trie::BlockNumber,
Spawn: SpawnNamed + Send + 'static,
{
let mut sm = StateMachine::<_, H, N, Exec>::new(
let mut sm = StateMachine::<_, H, Exec>::new(
trie_backend,
None,
overlay,
exec,
method,
@@ -1390,7 +1345,7 @@ mod execution {
#[cfg(test)]
mod tests {
use super::{changes_trie::Configuration as ChangesTrieConfig, ext::Ext, *};
use super::{ext::Ext, *};
use crate::execution::CallResult;
use codec::{Decode, Encode};
use sp_core::{
@@ -1409,7 +1364,6 @@ mod tests {
#[derive(Clone)]
struct DummyCodeExecutor {
change_changes_trie_config: bool,
native_available: bool,
native_succeeds: bool,
fallback_succeeds: bool,
@@ -1430,13 +1384,6 @@ mod tests {
use_native: bool,
native_call: Option<NC>,
) -> (CallResult<R, Self::Error>, bool) {
if self.change_changes_trie_config {
ext.place_storage(
sp_core::storage::well_known_keys::CHANGES_TRIE_CONFIG.to_vec(),
Some(ChangesTrieConfig { digest_interval: 777, digest_levels: 333 }.encode()),
);
}
let using_native = use_native && self.native_available;
match (using_native, self.native_succeeds, self.fallback_succeeds, native_call) {
(true, true, _, Some(call)) => {
@@ -1472,10 +1419,8 @@ mod tests {
let mut state_machine = StateMachine::new(
&backend,
changes_trie::disabled_state::<_, u64>(),
&mut overlayed_changes,
&DummyCodeExecutor {
change_changes_trie_config: false,
native_available: true,
native_succeeds: true,
fallback_succeeds: true,
@@ -1498,10 +1443,8 @@ mod tests {
let mut state_machine = StateMachine::new(
&backend,
changes_trie::disabled_state::<_, u64>(),
&mut overlayed_changes,
&DummyCodeExecutor {
change_changes_trie_config: false,
native_available: true,
native_succeeds: true,
fallback_succeeds: true,
@@ -1525,10 +1468,8 @@ mod tests {
let mut state_machine = StateMachine::new(
&backend,
changes_trie::disabled_state::<_, u64>(),
&mut overlayed_changes,
&DummyCodeExecutor {
change_changes_trie_config: false,
native_available: true,
native_succeeds: true,
fallback_succeeds: false,
@@ -1555,7 +1496,6 @@ mod tests {
#[test]
fn prove_execution_and_proof_check_works() {
let executor = DummyCodeExecutor {
change_changes_trie_config: false,
native_available: true,
native_succeeds: true,
fallback_succeeds: true,
@@ -1564,7 +1504,7 @@ mod tests {
// fetch execution proof from 'remote' full node
let mut remote_backend = trie_backend::tests::test_trie();
let remote_root = remote_backend.storage_root(std::iter::empty()).0;
let (remote_result, remote_proof) = prove_execution::<_, _, u64, _, _>(
let (remote_result, remote_proof) = prove_execution(
&mut remote_backend,
&mut Default::default(),
&executor,
@@ -1576,7 +1516,7 @@ mod tests {
.unwrap();
// check proof locally
let local_result = execution_proof_check::<BlakeTwo256, u64, _, _>(
let local_result = execution_proof_check::<BlakeTwo256, _, _>(
remote_root,
remote_proof,
&mut Default::default(),
@@ -1614,13 +1554,7 @@ mod tests {
let overlay_limit = overlay.clone();
{
let mut cache = StorageTransactionCache::default();
let mut ext = Ext::new(
&mut overlay,
&mut cache,
backend,
changes_trie::disabled_state::<_, u64>(),
None,
);
let mut ext = Ext::new(&mut overlay, &mut cache, backend, None);
ext.clear_prefix(b"ab", None);
}
overlay.commit_transaction().unwrap();
@@ -1644,13 +1578,7 @@ mod tests {
let mut overlay = overlay_limit;
{
let mut cache = StorageTransactionCache::default();
let mut ext = Ext::new(
&mut overlay,
&mut cache,
backend,
changes_trie::disabled_state::<_, u64>(),
None,
);
let mut ext = Ext::new(&mut overlay, &mut cache, backend, None);
assert_eq!((false, 1), ext.clear_prefix(b"ab", Some(1)));
}
overlay.commit_transaction().unwrap();
@@ -1692,13 +1620,7 @@ mod tests {
{
let mut cache = StorageTransactionCache::default();
let mut ext = Ext::new(
&mut overlay,
&mut cache,
&backend,
changes_trie::disabled_state::<_, u64>(),
None,
);
let mut ext = Ext::new(&mut overlay, &mut cache, &backend, None);
assert_eq!(ext.kill_child_storage(&child_info, Some(2)), (false, 2));
}
@@ -1733,13 +1655,7 @@ mod tests {
let backend = InMemoryBackend::<BlakeTwo256>::from(initial);
let mut overlay = OverlayedChanges::default();
let mut cache = StorageTransactionCache::default();
let mut ext = Ext::new(
&mut overlay,
&mut cache,
&backend,
changes_trie::disabled_state::<_, u64>(),
None,
);
let mut ext = Ext::new(&mut overlay, &mut cache, &backend, None);
assert_eq!(ext.kill_child_storage(&child_info, Some(0)), (false, 0));
assert_eq!(ext.kill_child_storage(&child_info, Some(1)), (false, 1));
assert_eq!(ext.kill_child_storage(&child_info, Some(2)), (false, 2));
@@ -1758,13 +1674,7 @@ mod tests {
let backend = state.as_trie_backend().unwrap();
let mut overlay = OverlayedChanges::default();
let mut cache = StorageTransactionCache::default();
let mut ext = Ext::new(
&mut overlay,
&mut cache,
backend,
changes_trie::disabled_state::<_, u64>(),
None,
);
let mut ext = Ext::new(&mut overlay, &mut cache, backend, None);
ext.set_child_storage(child_info, b"abc".to_vec(), b"def".to_vec());
assert_eq!(ext.child_storage(child_info, b"abc"), Some(b"def".to_vec()));
@@ -1781,26 +1691,14 @@ mod tests {
let mut overlay = OverlayedChanges::default();
let mut cache = StorageTransactionCache::default();
{
let mut ext = Ext::new(
&mut overlay,
&mut cache,
backend,
changes_trie::disabled_state::<_, u64>(),
None,
);
let mut ext = Ext::new(&mut overlay, &mut cache, backend, None);
ext.storage_append(key.clone(), reference_data[0].encode());
assert_eq!(ext.storage(key.as_slice()), Some(vec![reference_data[0].clone()].encode()));
}
overlay.start_transaction();
{
let mut ext = Ext::new(
&mut overlay,
&mut cache,
backend,
changes_trie::disabled_state::<_, u64>(),
None,
);
let mut ext = Ext::new(&mut overlay, &mut cache, backend, None);
for i in reference_data.iter().skip(1) {
ext.storage_append(key.clone(), i.encode());
@@ -1809,13 +1707,7 @@ mod tests {
}
overlay.rollback_transaction().unwrap();
{
let ext = Ext::new(
&mut overlay,
&mut cache,
backend,
changes_trie::disabled_state::<_, u64>(),
None,
);
let ext = Ext::new(&mut overlay, &mut cache, backend, None);
assert_eq!(ext.storage(key.as_slice()), Some(vec![reference_data[0].clone()].encode()));
}
}
@@ -1837,13 +1729,7 @@ mod tests {
// For example, block initialization with event.
{
let mut ext = Ext::new(
&mut overlay,
&mut cache,
backend,
changes_trie::disabled_state::<_, u64>(),
None,
);
let mut ext = Ext::new(&mut overlay, &mut cache, backend, None);
ext.clear_storage(key.as_slice());
ext.storage_append(key.clone(), Item::InitializationItem.encode());
}
@@ -1851,13 +1737,7 @@ mod tests {
// For example, first transaction resulted in panic during block building
{
let mut ext = Ext::new(
&mut overlay,
&mut cache,
backend,
changes_trie::disabled_state::<_, u64>(),
None,
);
let mut ext = Ext::new(&mut overlay, &mut cache, backend, None);
assert_eq!(ext.storage(key.as_slice()), Some(vec![Item::InitializationItem].encode()));
@@ -1872,13 +1752,7 @@ mod tests {
// Then we apply next transaction which is valid this time.
{
let mut ext = Ext::new(
&mut overlay,
&mut cache,
backend,
changes_trie::disabled_state::<_, u64>(),
None,
);
let mut ext = Ext::new(&mut overlay, &mut cache, backend, None);
assert_eq!(ext.storage(key.as_slice()), Some(vec![Item::InitializationItem].encode()));
@@ -1893,13 +1767,7 @@ mod tests {
// Then only initlaization item and second (committed) item should persist.
{
let ext = Ext::new(
&mut overlay,
&mut cache,
backend,
changes_trie::disabled_state::<_, u64>(),
None,
);
let ext = Ext::new(&mut overlay, &mut cache, backend, None);
assert_eq!(
ext.storage(key.as_slice()),
Some(vec![Item::InitializationItem, Item::CommitedItem].encode()),
@@ -2214,13 +2082,7 @@ mod tests {
let mut transaction = {
let backend = test_trie();
let mut cache = StorageTransactionCache::default();
let mut ext = Ext::new(
&mut overlay,
&mut cache,
&backend,
changes_trie::disabled_state::<_, u64>(),
None,
);
let mut ext = Ext::new(&mut overlay, &mut cache, &backend, None);
ext.set_child_storage(&child_info_1, b"abc".to_vec(), b"def".to_vec());
ext.set_child_storage(&child_info_2, b"abc".to_vec(), b"def".to_vec());
ext.storage_root();
@@ -2257,13 +2119,7 @@ mod tests {
{
let mut cache = StorageTransactionCache::default();
let mut ext = Ext::new(
&mut overlay,
&mut cache,
backend,
changes_trie::disabled_state::<_, u64>(),
None,
);
let mut ext = Ext::new(&mut overlay, &mut cache, backend, None);
assert_eq!(ext.storage(b"bbb"), Some(vec![]));
assert_eq!(ext.storage(b"ccc"), Some(vec![]));
ext.clear_storage(b"ccc");
@@ -2286,10 +2142,8 @@ mod tests {
let mut state_machine = StateMachine::new(
&backend,
changes_trie::disabled_state::<_, u64>(),
&mut overlayed_changes,
&DummyCodeExecutor {
change_changes_trie_config: false,
native_available: true,
native_succeeds: true,
fallback_succeeds: false,
@@ -2301,7 +2155,7 @@ mod tests {
TaskExecutor::new(),
);
let run_state_machine = |state_machine: &mut StateMachine<_, _, _, _>| {
let run_state_machine = |state_machine: &mut StateMachine<_, _, _>| {
state_machine
.execute_using_consensus_failure_handler::<fn(_, _) -> _, _, _>(
ExecutionManager::NativeWhenPossible,
@@ -69,7 +69,6 @@ struct InnerValue<V> {
/// Current value. None if value has been deleted.
value: V,
/// The set of extrinsic indices where the values has been changed.
/// Is filled only if runtime has announced changes trie support.
extrinsics: Extrinsics,
}
@@ -21,12 +21,7 @@ mod changeset;
mod offchain;
use self::changeset::OverlayedChangeSet;
use crate::{backend::Backend, changes_trie::BlockNumber, stats::StateMachineStats, DefaultError};
#[cfg(feature = "std")]
use crate::{
changes_trie::{build_changes_trie, State as ChangesTrieState},
ChangesTrieTransaction,
};
use crate::{backend::Backend, stats::StateMachineStats, DefaultError};
use codec::{Decode, Encode};
use hash_db::Hasher;
pub use offchain::OffchainOverlayedChanges;
@@ -134,7 +129,7 @@ pub enum IndexOperation {
///
/// This contains all the changes to the storage and transactions to apply theses changes to the
/// backend.
pub struct StorageChanges<Transaction, H: Hasher, N: BlockNumber> {
pub struct StorageChanges<Transaction, H: Hasher> {
/// All changes to the main storage.
///
/// A value of `None` means that it was deleted.
@@ -150,22 +145,13 @@ pub struct StorageChanges<Transaction, H: Hasher, N: BlockNumber> {
pub transaction: Transaction,
/// The storage root after applying the transaction.
pub transaction_storage_root: H::Out,
/// Contains the transaction for the backend for the changes trie.
///
/// If changes trie is disabled the value is set to `None`.
#[cfg(feature = "std")]
pub changes_trie_transaction: Option<ChangesTrieTransaction<H, N>>,
/// Phantom data for block number until change trie support no_std.
#[cfg(not(feature = "std"))]
pub _ph: sp_std::marker::PhantomData<N>,
/// Changes to the transaction index,
#[cfg(feature = "std")]
pub transaction_index_changes: Vec<IndexOperation>,
}
#[cfg(feature = "std")]
impl<Transaction, H: Hasher, N: BlockNumber> StorageChanges<Transaction, H, N> {
impl<Transaction, H: Hasher> StorageChanges<Transaction, H> {
/// Deconstruct into the inner values
pub fn into_inner(
self,
@@ -175,7 +161,6 @@ impl<Transaction, H: Hasher, N: BlockNumber> StorageChanges<Transaction, H, N> {
OffchainChangesCollection,
Transaction,
H::Out,
Option<ChangesTrieTransaction<H, N>>,
Vec<IndexOperation>,
) {
(
@@ -184,58 +169,35 @@ impl<Transaction, H: Hasher, N: BlockNumber> StorageChanges<Transaction, H, N> {
self.offchain_storage_changes,
self.transaction,
self.transaction_storage_root,
self.changes_trie_transaction,
self.transaction_index_changes,
)
}
}
/// The storage transaction are calculated as part of the `storage_root` and
/// `changes_trie_storage_root`. These transactions can be reused for importing the block into the
/// Storage transactions are calculated as part of the `storage_root`.
/// These transactions can be reused for importing the block into the
/// storage. So, we cache them to not require a recomputation of those transactions.
pub struct StorageTransactionCache<Transaction, H: Hasher, N: BlockNumber> {
pub struct StorageTransactionCache<Transaction, H: Hasher> {
/// Contains the changes for the main and the child storages as one transaction.
pub(crate) transaction: Option<Transaction>,
/// The storage root after applying the transaction.
pub(crate) transaction_storage_root: Option<H::Out>,
/// Contains the changes trie transaction.
#[cfg(feature = "std")]
pub(crate) changes_trie_transaction: Option<Option<ChangesTrieTransaction<H, N>>>,
/// The storage root after applying the changes trie transaction.
#[cfg(feature = "std")]
pub(crate) changes_trie_transaction_storage_root: Option<Option<H::Out>>,
/// Phantom data for block number until change trie support no_std.
#[cfg(not(feature = "std"))]
pub(crate) _ph: sp_std::marker::PhantomData<N>,
}
impl<Transaction, H: Hasher, N: BlockNumber> StorageTransactionCache<Transaction, H, N> {
impl<Transaction, H: Hasher> StorageTransactionCache<Transaction, H> {
/// Reset the cached transactions.
pub fn reset(&mut self) {
*self = Self::default();
}
}
impl<Transaction, H: Hasher, N: BlockNumber> Default
for StorageTransactionCache<Transaction, H, N>
{
impl<Transaction, H: Hasher> Default for StorageTransactionCache<Transaction, H> {
fn default() -> Self {
Self {
transaction: None,
transaction_storage_root: None,
#[cfg(feature = "std")]
changes_trie_transaction: None,
#[cfg(feature = "std")]
changes_trie_transaction_storage_root: None,
#[cfg(not(feature = "std"))]
_ph: Default::default(),
}
Self { transaction: None, transaction_storage_root: None }
}
}
impl<Transaction: Default, H: Hasher, N: BlockNumber> Default
for StorageChanges<Transaction, H, N>
{
impl<Transaction: Default, H: Hasher> Default for StorageChanges<Transaction, H> {
fn default() -> Self {
Self {
main_storage_changes: Default::default(),
@@ -244,10 +206,6 @@ impl<Transaction: Default, H: Hasher, N: BlockNumber> Default
transaction: Default::default(),
transaction_storage_root: Default::default(),
#[cfg(feature = "std")]
changes_trie_transaction: None,
#[cfg(not(feature = "std"))]
_ph: Default::default(),
#[cfg(feature = "std")]
transaction_index_changes: Default::default(),
}
}
@@ -539,27 +497,25 @@ 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>(
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>
mut cache: StorageTransactionCache<B::Transaction, H>,
) -> Result<StorageChanges<B::Transaction, H>, DefaultError>
where
H::Out: Ord + Encode + 'static,
{
self.drain_storage_changes(backend, changes_trie_state, parent_hash, &mut cache)
self.drain_storage_changes(backend, parent_hash, &mut cache)
}
/// Drain all changes into a [`StorageChanges`] instance. Leave empty overlay in place.
pub fn drain_storage_changes<B: Backend<H>, H: Hasher, N: BlockNumber>(
pub fn drain_storage_changes<B: Backend<H>, H: Hasher>(
&mut self,
backend: &B,
#[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>
_parent_hash: H::Out,
mut cache: &mut StorageTransactionCache<B::Transaction, H>,
) -> Result<StorageChanges<B::Transaction, H>, DefaultError>
where
H::Out: Ord + Encode + 'static,
{
@@ -574,21 +530,6 @@ impl OverlayedChanges {
.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")?;
}
#[cfg(not(feature = "std"))]
let _ = parent_hash;
#[cfg(feature = "std")]
let changes_trie_transaction = cache
.changes_trie_transaction
.take()
.expect("Changes trie transaction was generated by `changes_trie_root`; qed");
let (main_storage_changes, child_storage_changes) = self.drain_committed();
let offchain_storage_changes = self.offchain_drain_committed().collect();
@@ -604,11 +545,7 @@ impl OverlayedChanges {
transaction,
transaction_storage_root,
#[cfg(feature = "std")]
changes_trie_transaction,
#[cfg(feature = "std")]
transaction_index_changes,
#[cfg(not(feature = "std"))]
_ph: Default::default(),
})
}
@@ -639,10 +576,10 @@ impl OverlayedChanges {
/// as seen by the current transaction.
///
/// Returns the storage root and caches storage transaction in the given `cache`.
pub fn storage_root<H: Hasher, N: BlockNumber, B: Backend<H>>(
pub fn storage_root<H: Hasher, B: Backend<H>>(
&self,
backend: &B,
cache: &mut StorageTransactionCache<B::Transaction, H, N>,
cache: &mut StorageTransactionCache<B::Transaction, H>,
) -> H::Out
where
H::Out: Ord + Encode,
@@ -660,40 +597,6 @@ impl OverlayedChanges {
root
}
/// Generate the changes trie root.
///
/// Returns the changes trie root and caches the storage transaction into the given `cache`.
///
/// # Panics
///
/// Panics on storage error, when `panic_on_storage_error` is set.
#[cfg(feature = "std")]
pub fn changes_trie_root<'a, H: Hasher, N: BlockNumber, B: Backend<H>>(
&self,
backend: &B,
changes_trie_state: Option<&'a ChangesTrieState<'a, H, N>>,
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,
{
build_changes_trie::<_, H, N>(
backend,
changes_trie_state,
self,
parent_hash,
panic_on_storage_error,
)
.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);
root
})
}
/// Returns an iterator over the keys (in lexicographic order) following `key` (excluding `key`)
/// alongside its value.
pub fn iter_after(&self, key: &[u8]) -> impl Iterator<Item = (&[u8], &OverlayedValue)> {
@@ -937,7 +840,6 @@ mod tests {
.collect();
let backend = InMemoryBackend::<Blake2Hasher>::from(initial);
let mut overlay = OverlayedChanges::default();
overlay.set_collect_extrinsics(false);
overlay.start_transaction();
overlay.set_storage(b"dog".to_vec(), Some(b"puppy".to_vec()));
@@ -950,13 +852,7 @@ mod tests {
overlay.set_storage(b"doug".to_vec(), None);
let mut cache = StorageTransactionCache::default();
let mut ext = Ext::new(
&mut overlay,
&mut cache,
&backend,
crate::changes_trie::disabled_state::<_, u64>(),
None,
);
let mut ext = Ext::new(&mut overlay, &mut cache, &backend, None);
const ROOT: [u8; 32] =
hex!("39245109cef3758c2eed2ccba8d9b370a917850af3824bc8348d505df2c298fa");
@@ -153,10 +153,6 @@ impl<'a, H: Hasher, B: 'a + Backend<H>> Externalities for ReadOnlyExternalities<
unimplemented!("child_storage_root is not supported in ReadOnlyExternalities")
}
fn storage_changes_root(&mut self, _parent: &[u8]) -> Result<Option<Vec<u8>>, ()> {
unimplemented!("storage_changes_root is not supported in ReadOnlyExternalities")
}
fn storage_start_transaction(&mut self) {
unimplemented!("Transactions are not supported by ReadOnlyExternalities");
}
@@ -23,21 +23,15 @@ use std::{
};
use crate::{
backend::Backend,
changes_trie::{
BlockNumber as ChangesTrieBlockNumber, Configuration as ChangesTrieConfiguration,
InMemoryStorage as ChangesTrieInMemoryStorage, State as ChangesTrieState,
},
ext::Ext,
InMemoryBackend, OverlayedChanges, StorageKey, StorageTransactionCache, StorageValue,
backend::Backend, ext::Ext, InMemoryBackend, OverlayedChanges, StorageKey,
StorageTransactionCache, StorageValue,
};
use codec::Decode;
use hash_db::Hasher;
use sp_core::{
offchain::testing::TestPersistentOffchainDB,
storage::{
well_known_keys::{is_child_storage_key, CHANGES_TRIE_CONFIG, CODE},
well_known_keys::{is_child_storage_key, CODE},
Storage,
},
testing::TaskExecutor,
@@ -46,7 +40,7 @@ use sp_core::{
use sp_externalities::{Extension, ExtensionStore, Extensions};
/// Simple HashMap-based Externalities impl.
pub struct TestExternalities<H: Hasher, N: ChangesTrieBlockNumber = u64>
pub struct TestExternalities<H: Hasher>
where
H::Out: codec::Codec + Ord,
{
@@ -54,33 +48,23 @@ where
overlay: OverlayedChanges,
offchain_db: TestPersistentOffchainDB,
storage_transaction_cache:
StorageTransactionCache<<InMemoryBackend<H> as Backend<H>>::Transaction, H, N>,
StorageTransactionCache<<InMemoryBackend<H> as Backend<H>>::Transaction, H>,
/// Storage backend.
pub backend: InMemoryBackend<H>,
changes_trie_config: Option<ChangesTrieConfiguration>,
changes_trie_storage: ChangesTrieInMemoryStorage<H, N>,
/// Extensions.
pub extensions: Extensions,
}
impl<H: Hasher, N: ChangesTrieBlockNumber> TestExternalities<H, N>
impl<H: Hasher> TestExternalities<H>
where
H::Out: Ord + 'static + codec::Codec,
{
/// Get externalities implementation.
pub fn ext(&mut self) -> Ext<H, N, InMemoryBackend<H>> {
pub fn ext(&mut self) -> Ext<H, InMemoryBackend<H>> {
Ext::new(
&mut self.overlay,
&mut self.storage_transaction_cache,
&self.backend,
match self.changes_trie_config.clone() {
Some(config) => Some(ChangesTrieState {
config,
zero: 0.into(),
storage: &self.changes_trie_storage,
}),
None => None,
},
Some(&mut self.extensions),
)
}
@@ -97,12 +81,7 @@ 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)
.and_then(|v| Decode::decode(&mut &v[..]).ok());
overlay.set_collect_extrinsics(changes_trie_config.is_some());
let overlay = OverlayedChanges::default();
assert!(storage.top.keys().all(|key| !is_child_storage_key(key)));
assert!(storage.children_default.keys().all(|key| is_child_storage_key(key)));
@@ -117,9 +96,7 @@ where
TestExternalities {
overlay,
offchain_db,
changes_trie_config,
extensions,
changes_trie_storage: ChangesTrieInMemoryStorage::new(),
backend: storage.into(),
storage_transaction_cache: Default::default(),
}
@@ -150,11 +127,6 @@ where
self.extensions.register(ext);
}
/// Get mutable reference to changes trie storage.
pub fn changes_trie_storage(&mut self) -> &mut ChangesTrieInMemoryStorage<H, N> {
&mut self.changes_trie_storage
}
/// Return a new backend with all pending changes.
///
/// In contrast to [`commit_all`](Self::commit_all) this will not panic if there are open
@@ -180,9 +152,8 @@ where
///
/// This will panic if there are still open transactions.
pub fn commit_all(&mut self) -> Result<(), String> {
let changes = self.overlay.drain_storage_changes::<_, _, N>(
let changes = self.overlay.drain_storage_changes::<_, _>(
&self.backend,
None,
Default::default(),
&mut Default::default(),
)?;
@@ -216,7 +187,7 @@ where
}
}
impl<H: Hasher, N: ChangesTrieBlockNumber> std::fmt::Debug for TestExternalities<H, N>
impl<H: Hasher> std::fmt::Debug for TestExternalities<H>
where
H::Out: Ord + codec::Codec,
{
@@ -225,18 +196,18 @@ where
}
}
impl<H: Hasher, N: ChangesTrieBlockNumber> PartialEq for TestExternalities<H, N>
impl<H: Hasher> PartialEq for TestExternalities<H>
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
fn eq(&self, other: &TestExternalities<H, N>) -> bool {
fn eq(&self, other: &TestExternalities<H>) -> bool {
self.as_backend().eq(&other.as_backend())
}
}
impl<H: Hasher, N: ChangesTrieBlockNumber> Default for TestExternalities<H, N>
impl<H: Hasher> Default for TestExternalities<H>
where
H::Out: Ord + 'static + codec::Codec,
{
@@ -245,7 +216,7 @@ where
}
}
impl<H: Hasher, N: ChangesTrieBlockNumber> From<Storage> for TestExternalities<H, N>
impl<H: Hasher> From<Storage> for TestExternalities<H>
where
H::Out: Ord + 'static + codec::Codec,
{
@@ -254,11 +225,10 @@ where
}
}
impl<H, N> sp_externalities::ExtensionStore for TestExternalities<H, N>
impl<H> sp_externalities::ExtensionStore for TestExternalities<H>
where
H: Hasher,
H::Out: Ord + codec::Codec,
N: ChangesTrieBlockNumber,
{
fn extension_by_type_id(&mut self, type_id: TypeId) -> Option<&mut dyn Any> {
self.extensions.get_mut(type_id)
@@ -284,11 +254,10 @@ where
}
}
impl<H, N> sp_externalities::ExternalitiesExt for TestExternalities<H, N>
impl<H> sp_externalities::ExternalitiesExt for TestExternalities<H>
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)
@@ -312,7 +281,7 @@ mod tests {
#[test]
fn commit_should_work() {
let mut ext = TestExternalities::<BlakeTwo256, u64>::default();
let mut ext = TestExternalities::<BlakeTwo256>::default();
let mut ext = ext.ext();
ext.set_storage(b"doe".to_vec(), b"reindeer".to_vec());
ext.set_storage(b"dog".to_vec(), b"puppy".to_vec());
@@ -324,7 +293,7 @@ mod tests {
#[test]
fn set_and_retrieve_code() {
let mut ext = TestExternalities::<BlakeTwo256, u64>::default();
let mut ext = TestExternalities::<BlakeTwo256>::default();
let mut ext = ext.ext();
let code = vec![1, 2, 3];
@@ -336,12 +305,12 @@ mod tests {
#[test]
fn check_send() {
fn assert_send<T: Send>() {}
assert_send::<TestExternalities<BlakeTwo256, u64>>();
assert_send::<TestExternalities<BlakeTwo256>>();
}
#[test]
fn commit_all_and_kill_child_storage() {
let mut ext = TestExternalities::<BlakeTwo256, u64>::default();
let mut ext = TestExternalities::<BlakeTwo256>::default();
let child_info = ChildInfo::new_default(&b"test_child"[..]);
{
@@ -366,7 +335,7 @@ mod tests {
#[test]
fn as_backend_generates_same_backend_as_commit_all() {
let mut ext = TestExternalities::<BlakeTwo256, u64>::default();
let mut ext = TestExternalities::<BlakeTwo256>::default();
{
let mut ext = ext.ext();
ext.set_storage(b"doe".to_vec(), b"reindeer".to_vec());
-3
View File
@@ -204,9 +204,6 @@ pub mod well_known_keys {
/// Current extrinsic index (u32) is stored under this key.
pub const EXTRINSIC_INDEX: &'static [u8] = b":extrinsic_index";
/// Changes trie configuration is stored under this key.
pub const CHANGES_TRIE_CONFIG: &'static [u8] = b":changes_trie";
/// Prefix of child storage keys.
pub const CHILD_STORAGE_KEY_PREFIX: &'static [u8] = b":child_storage:";
@@ -134,10 +134,6 @@ impl Externalities for AsyncExternalities {
panic!("`child_storage_root`: should not be used in async externalities!")
}
fn storage_changes_root(&mut self, _parent: &[u8]) -> Result<Option<Vec<u8>>, ()> {
panic!("`storage_changes_root`: should not be used in async externalities!")
}
fn storage_start_transaction(&mut self) {
unimplemented!("Transactions are not supported by AsyncExternalities");
}
@@ -73,15 +73,10 @@ pub type BlockNumber = u64;
/// Index of a transaction.
pub type Index = u64;
/// The item of a block digest.
pub type DigestItem = sp_runtime::generic::DigestItem<H256>;
pub type DigestItem = sp_runtime::generic::DigestItem;
/// The digest of a block.
pub type Digest = sp_runtime::generic::Digest<H256>;
pub type Digest = sp_runtime::generic::Digest;
/// A test block.
pub type Block = sp_runtime::generic::Block<Header, Extrinsic>;
/// A test block's header.
pub type Header = sp_runtime::generic::Header<BlockNumber, BlakeTwo256>;
/// Changes trie configuration (optionally) used in tests.
pub fn changes_trie_config() -> sp_core::ChangesTrieConfiguration {
sp_core::ChangesTrieConfiguration { digest_interval: 4, digest_levels: 2 }
}