mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-13 10:31:04 +00:00
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:
@@ -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>,
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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>;
|
||||
|
||||
@@ -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))),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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 }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user