feat: Rebrand Polkadot/Substrate references to PezkuwiChain
This commit systematically rebrands various references from Parity Technologies' Polkadot/Substrate ecosystem to PezkuwiChain within the kurdistan-sdk. Key changes include: - Updated external repository URLs (zombienet-sdk, parity-db, parity-scale-codec, wasm-instrument) to point to pezkuwichain forks. - Modified internal documentation and code comments to reflect PezkuwiChain naming and structure. - Replaced direct references to with or specific paths within the for XCM, Pezkuwi, and other modules. - Cleaned up deprecated issue and PR references in various and files, particularly in and modules. - Adjusted image and logo URLs in documentation to point to PezkuwiChain assets. - Removed or rephrased comments related to external Polkadot/Substrate PRs and issues. This is a significant step towards fully customizing the SDK for the PezkuwiChain ecosystem.
This commit is contained in:
@@ -0,0 +1,45 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) 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.
|
||||
|
||||
//! Benchmarks for the MMR pallet.
|
||||
|
||||
#![cfg(feature = "runtime-benchmarks")]
|
||||
|
||||
use crate::*;
|
||||
use frame::{
|
||||
benchmarking::prelude::v1::benchmarks_instance_pallet,
|
||||
deps::pezframe_support::traits::OnInitialize,
|
||||
};
|
||||
|
||||
benchmarks_instance_pallet! {
|
||||
on_initialize {
|
||||
let x in 1 .. 1_000;
|
||||
|
||||
let leaves = x as NodeIndex;
|
||||
|
||||
<<T as pallet::Config::<I>>::BenchmarkHelper as BenchmarkHelper>::setup();
|
||||
for leaf in 0..(leaves - 1) {
|
||||
<Pallet::<T, I> as OnInitialize<BlockNumberFor<T>>>::on_initialize((leaf as u32).into());
|
||||
}
|
||||
}: {
|
||||
<Pallet::<T, I> as OnInitialize<BlockNumberFor<T>>>::on_initialize((leaves as u32 - 1).into());
|
||||
} verify {
|
||||
assert_eq!(crate::NumberOfLeaves::<T, I>::get(), leaves);
|
||||
}
|
||||
|
||||
impl_benchmark_test_suite!(Pallet, crate::tests::new_test_ext(), crate::mock::Test);
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) 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.
|
||||
|
||||
//! Default weights for the MMR Pallet
|
||||
//! This file was not auto-generated.
|
||||
|
||||
use frame::{deps::pezframe_support::weights::constants::*, weights_prelude::*};
|
||||
|
||||
impl crate::WeightInfo for () {
|
||||
fn on_initialize(peaks: u32) -> Weight {
|
||||
let peaks = u64::from(peaks);
|
||||
// Reading the parent hash.
|
||||
let leaf_weight = RocksDbWeight::get().reads(1);
|
||||
// Blake2 hash cost.
|
||||
let hash_weight = Weight::from_parts(2u64 * WEIGHT_REF_TIME_PER_NANOS, 0);
|
||||
// No-op hook.
|
||||
let hook_weight = Weight::zero();
|
||||
|
||||
leaf_weight
|
||||
.saturating_add(hash_weight)
|
||||
.saturating_add(hook_weight)
|
||||
.saturating_add(RocksDbWeight::get().reads_writes(2 + peaks, 2 + peaks))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,465 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) 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.
|
||||
|
||||
//! # Merkle Mountain Range
|
||||
//!
|
||||
//! ## Overview
|
||||
//!
|
||||
//! Details on Merkle Mountain Ranges (MMRs) can be found here:
|
||||
//! <https://github.com/mimblewimble/grin/blob/master/doc/mmr.md>
|
||||
//!
|
||||
//! The MMR pallet constructs an MMR from leaf data obtained on every block from
|
||||
//! `LeafDataProvider`. MMR nodes are stored both in:
|
||||
//! - on-chain storage - hashes only; not full leaf content;
|
||||
//! - off-chain storage - via Indexing API we push full leaf content (and all internal nodes as
|
||||
//! well) to the Off-chain DB, so that the data is available for Off-chain workers.
|
||||
//! Hashing used for MMR is configurable independently from the rest of the runtime (i.e. not using
|
||||
//! `pezframe_system::Hashing`) so something compatible with external chains can be used (like
|
||||
//! Keccak256 for Ethereum compatibility).
|
||||
//!
|
||||
//! Depending on the usage context (off-chain vs on-chain) the pallet is able to:
|
||||
//! - verify MMR leaf proofs (on-chain)
|
||||
//! - generate leaf proofs (off-chain)
|
||||
//!
|
||||
//! See [primitives::Compact] documentation for how you can optimize proof size for leafs that are
|
||||
//! composed from multiple elements.
|
||||
//!
|
||||
//! ## What for?
|
||||
//!
|
||||
//! Primary use case for this pallet is to generate MMR root hashes, that can latter on be used by
|
||||
//! BEEFY protocol (see <https://github.com/paritytech/grandpa-bridge-gadget>).
|
||||
//! MMR root hashes along with BEEFY will make it possible to build Super Light Clients (SLC) of
|
||||
//! Bizinikiwi-based chains. The SLC will be able to follow finality and can be shown proofs of more
|
||||
//! details that happened on the source chain.
|
||||
//! In that case the chain which contains the pallet generates the Root Hashes and Proofs, which
|
||||
//! are then presented to another chain acting as a light client which can verify them.
|
||||
//!
|
||||
//! Secondary use case is to archive historical data, but still be able to retrieve them on-demand
|
||||
//! if needed. For instance if parent block hashes are stored in the MMR it's possible at any point
|
||||
//! in time to provide an MMR proof about some past block hash, while this data can be safely pruned
|
||||
//! from on-chain storage.
|
||||
//!
|
||||
//! NOTE This pallet is experimental and not proven to work in production.
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
use alloc::vec::Vec;
|
||||
use log;
|
||||
|
||||
use frame::prelude::*;
|
||||
|
||||
pub use pezsp_mmr_primitives::{
|
||||
self as primitives, utils, utils::NodesUtils, AncestryProof, Error, FullLeaf, LeafDataProvider,
|
||||
LeafIndex, LeafProof, NodeIndex, OnNewRoot,
|
||||
};
|
||||
|
||||
pub use pallet::*;
|
||||
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
mod benchmarking;
|
||||
mod default_weights;
|
||||
mod mmr;
|
||||
#[cfg(test)]
|
||||
mod mock;
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
/// The most common use case for MMRs is to store historical block hashes,
|
||||
/// so that any point in time in the future we can receive a proof about some past
|
||||
/// blocks without using excessive on-chain storage.
|
||||
///
|
||||
/// Hence we implement the [LeafDataProvider] for [ParentNumberAndHash] which is a
|
||||
/// crate-local wrapper over [pezframe_system::Pallet]. Since the current block hash
|
||||
/// is not available (since the block is not finished yet),
|
||||
/// we use the `parent_hash` here along with parent block number.
|
||||
pub struct ParentNumberAndHash<T: Config> {
|
||||
_phantom: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T: Config> LeafDataProvider for ParentNumberAndHash<T> {
|
||||
type LeafData = (BlockNumberFor<T>, <T as pezframe_system::Config>::Hash);
|
||||
|
||||
fn leaf_data() -> Self::LeafData {
|
||||
(
|
||||
pezframe_system::Pallet::<T>::block_number().saturating_sub(One::one()),
|
||||
pezframe_system::Pallet::<T>::parent_hash(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Block hash provider for a given block number.
|
||||
pub trait BlockHashProvider<BlockNumber, BlockHash> {
|
||||
fn block_hash(block_number: BlockNumber) -> BlockHash;
|
||||
}
|
||||
|
||||
/// Default implementation of BlockHashProvider using pezframe_system.
|
||||
pub struct DefaultBlockHashProvider<T: Config> {
|
||||
_phantom: core::marker::PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T: Config> BlockHashProvider<BlockNumberFor<T>, T::Hash> for DefaultBlockHashProvider<T> {
|
||||
fn block_hash(block_number: BlockNumberFor<T>) -> T::Hash {
|
||||
pezframe_system::Pallet::<T>::block_hash(block_number)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait WeightInfo {
|
||||
fn on_initialize(peaks: u32) -> Weight;
|
||||
}
|
||||
|
||||
/// This trait decoples dependencies on pallets needed for benchmarking.
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
pub trait BenchmarkHelper {
|
||||
fn setup();
|
||||
}
|
||||
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
impl BenchmarkHelper for () {
|
||||
fn setup() {}
|
||||
}
|
||||
|
||||
/// An MMR specific to the pallet.
|
||||
type ModuleMmr<StorageType, T, I> = mmr::Mmr<StorageType, T, I, LeafOf<T, I>>;
|
||||
|
||||
/// Leaf data.
|
||||
type LeafOf<T, I> = <<T as Config<I>>::LeafData as LeafDataProvider>::LeafData;
|
||||
|
||||
/// Hashing used for the pallet.
|
||||
pub(crate) type HashingOf<T, I> = <T as Config<I>>::Hashing;
|
||||
/// Hash type used for the pallet.
|
||||
pub(crate) type HashOf<T, I> = <<T as Config<I>>::Hashing as Hash>::Output;
|
||||
|
||||
#[frame::pallet]
|
||||
pub mod pallet {
|
||||
use super::*;
|
||||
|
||||
#[pallet::pallet]
|
||||
pub struct Pallet<T, I = ()>(PhantomData<(T, I)>);
|
||||
|
||||
/// This pallet's configuration trait
|
||||
#[pallet::config]
|
||||
pub trait Config<I: 'static = ()>: pezframe_system::Config {
|
||||
/// Prefix for elements stored in the Off-chain DB via Indexing API.
|
||||
///
|
||||
/// Each node of the MMR is inserted both on-chain and off-chain via Indexing API.
|
||||
/// The former does not store full leaf content, just its compact version (hash),
|
||||
/// and some of the inner mmr nodes might be pruned from on-chain storage.
|
||||
/// The latter will contain all the entries in their full form.
|
||||
///
|
||||
/// Each node is stored in the Off-chain DB under key derived from the
|
||||
/// [`Self::INDEXING_PREFIX`] and its in-tree index (MMR position).
|
||||
const INDEXING_PREFIX: &'static [u8];
|
||||
|
||||
/// A hasher type for MMR.
|
||||
///
|
||||
/// To construct trie nodes that result in merging (bagging) two peaks, depending on the
|
||||
/// node kind we take either:
|
||||
/// - The node (hash) itself if it's an inner node.
|
||||
/// - The hash of SCALE-encoding of the leaf data if it's a leaf node.
|
||||
///
|
||||
/// Then we create a tuple of these two hashes, SCALE-encode it (concatenate) and
|
||||
/// hash, to obtain a new MMR inner node - the new peak.
|
||||
type Hashing: Hash;
|
||||
|
||||
/// Data stored in the leaf nodes.
|
||||
///
|
||||
/// The [LeafData](primitives::LeafDataProvider) is responsible for returning the entire
|
||||
/// leaf data that will be inserted to the MMR.
|
||||
/// [LeafDataProvider](primitives::LeafDataProvider)s can be composed into tuples to put
|
||||
/// multiple elements into the tree. In such a case it might be worth using
|
||||
/// [primitives::Compact] to make MMR proof for one element of the tuple leaner.
|
||||
///
|
||||
/// Note that the leaf at each block MUST be unique. You may want to include a block hash or
|
||||
/// block number as an easiest way to ensure that.
|
||||
/// Also note that the leaf added by each block is expected to only reference data coming
|
||||
/// from ancestor blocks (leaves are saved offchain using `(pos, parent_hash)` key to be
|
||||
/// fork-resistant, as such conflicts could only happen on 1-block deep forks, which means
|
||||
/// two forks with identical line of ancestors compete to write the same offchain key, but
|
||||
/// that's fine as long as leaves only contain data coming from ancestors - conflicting
|
||||
/// writes are identical).
|
||||
type LeafData: LeafDataProvider;
|
||||
|
||||
/// A hook to act on the new MMR root.
|
||||
///
|
||||
/// For some applications it might be beneficial to make the MMR root available externally
|
||||
/// apart from having it in the storage. For instance you might output it in the header
|
||||
/// digest (see [`pezframe_system::Pallet::deposit_log`]) to make it available for Light
|
||||
/// Clients. Hook complexity should be `O(1)`.
|
||||
type OnNewRoot: OnNewRoot<HashOf<Self, I>>;
|
||||
|
||||
/// Block hash provider for a given block number.
|
||||
type BlockHashProvider: BlockHashProvider<
|
||||
BlockNumberFor<Self>,
|
||||
<Self as pezframe_system::Config>::Hash,
|
||||
>;
|
||||
|
||||
/// Weights for this pallet.
|
||||
type WeightInfo: WeightInfo;
|
||||
|
||||
/// Benchmarking setup helper trait.
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
type BenchmarkHelper: BenchmarkHelper;
|
||||
}
|
||||
|
||||
/// Latest MMR Root hash.
|
||||
#[pallet::storage]
|
||||
pub type RootHash<T: Config<I>, I: 'static = ()> = StorageValue<_, HashOf<T, I>, ValueQuery>;
|
||||
|
||||
/// Current size of the MMR (number of leaves).
|
||||
#[pallet::storage]
|
||||
#[pallet::getter(fn mmr_leaves)]
|
||||
pub type NumberOfLeaves<T, I = ()> = StorageValue<_, LeafIndex, ValueQuery>;
|
||||
|
||||
/// Hashes of the nodes in the MMR.
|
||||
///
|
||||
/// Note this collection only contains MMR peaks, the inner nodes (and leaves)
|
||||
/// are pruned and only stored in the Offchain DB.
|
||||
#[pallet::storage]
|
||||
#[pallet::getter(fn mmr_peak)]
|
||||
pub type Nodes<T: Config<I>, I: 'static = ()> =
|
||||
StorageMap<_, Identity, NodeIndex, HashOf<T, I>, OptionQuery>;
|
||||
|
||||
/// Helper flag used in the runtime benchmarks for the initial setup.
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
#[pallet::storage]
|
||||
pub type UseLocalStorage<T, I = ()> = StorageValue<_, bool, ValueQuery>;
|
||||
|
||||
#[pallet::hooks]
|
||||
impl<T: Config<I>, I: 'static> Hooks<BlockNumberFor<T>> for Pallet<T, I> {
|
||||
fn on_initialize(_n: BlockNumberFor<T>) -> Weight {
|
||||
let leaves = NumberOfLeaves::<T, I>::get();
|
||||
let peaks_before = NodesUtils::new(leaves).number_of_peaks();
|
||||
let data = T::LeafData::leaf_data();
|
||||
|
||||
// append new leaf to MMR
|
||||
let mut mmr: ModuleMmr<mmr::storage::RuntimeStorage, T, I> = mmr::Mmr::new(leaves);
|
||||
// MMR push never fails, but better safe than sorry.
|
||||
if mmr.push(data).is_none() {
|
||||
log::error!(target: "runtime::mmr", "MMR push failed");
|
||||
return T::WeightInfo::on_initialize(peaks_before as u32);
|
||||
}
|
||||
// Update the size, `mmr.finalize()` should also never fail.
|
||||
let (leaves, root) = match mmr.finalize() {
|
||||
Ok((leaves, root)) => (leaves, root),
|
||||
Err(e) => {
|
||||
log::error!(target: "runtime::mmr", "MMR finalize failed: {:?}", e);
|
||||
return T::WeightInfo::on_initialize(peaks_before as u32);
|
||||
},
|
||||
};
|
||||
<T::OnNewRoot as OnNewRoot<_>>::on_new_root(&root);
|
||||
|
||||
NumberOfLeaves::<T, I>::put(leaves);
|
||||
RootHash::<T, I>::put(root);
|
||||
|
||||
let peaks_after = NodesUtils::new(leaves).number_of_peaks();
|
||||
|
||||
T::WeightInfo::on_initialize(peaks_before.max(peaks_after) as u32)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Stateless MMR proof verification for batch of leaves.
|
||||
///
|
||||
/// This function can be used to verify received MMR [primitives::LeafProof] (`proof`)
|
||||
/// for given leaves set (`leaves`) against a known MMR root hash (`root`).
|
||||
/// Note, the leaves should be sorted such that corresponding leaves and leaf indices have the
|
||||
/// same position in both the `leaves` vector and the `leaf_indices` vector contained in the
|
||||
/// [primitives::LeafProof].
|
||||
pub fn verify_leaves_proof<H, L>(
|
||||
root: H::Output,
|
||||
leaves: Vec<mmr::Node<H, L>>,
|
||||
proof: LeafProof<H::Output>,
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
H: Hash,
|
||||
L: FullLeaf,
|
||||
{
|
||||
let is_valid = mmr::verify_leaves_proof::<H, L>(root, leaves, proof)?;
|
||||
if is_valid {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::Verify.log_debug(("The proof is incorrect.", root)))
|
||||
}
|
||||
}
|
||||
|
||||
/// Stateless ancestry proof verification.
|
||||
pub fn verify_ancestry_proof<H, L>(
|
||||
root: H::Output,
|
||||
ancestry_proof: AncestryProof<H::Output>,
|
||||
) -> Result<H::Output, Error>
|
||||
where
|
||||
H: Hash,
|
||||
L: FullLeaf,
|
||||
{
|
||||
mmr::verify_ancestry_proof::<H, L>(root, ancestry_proof)
|
||||
.map_err(|_| Error::Verify.log_debug(("The ancestry proof is incorrect.", root)))
|
||||
}
|
||||
|
||||
impl<T: Config<I>, I: 'static> Pallet<T, I> {
|
||||
/// Build offchain key from `parent_hash` of block that originally added node `pos` to MMR.
|
||||
///
|
||||
/// This combination makes the offchain (key,value) entry resilient to chain forks.
|
||||
fn node_temp_offchain_key(
|
||||
pos: NodeIndex,
|
||||
parent_hash: <T as pezframe_system::Config>::Hash,
|
||||
) -> Vec<u8> {
|
||||
NodesUtils::node_temp_offchain_key::<HeaderFor<T>>(&T::INDEXING_PREFIX, pos, parent_hash)
|
||||
}
|
||||
|
||||
/// Build canonical offchain key for node `pos` in MMR.
|
||||
///
|
||||
/// Used for nodes added by now finalized blocks.
|
||||
/// Never read keys using `node_canon_offchain_key` unless you sure that
|
||||
/// there's no `node_offchain_key` key in the storage.
|
||||
fn node_canon_offchain_key(pos: NodeIndex) -> Vec<u8> {
|
||||
NodesUtils::node_canon_offchain_key(&T::INDEXING_PREFIX, pos)
|
||||
}
|
||||
|
||||
/// Provide the parent number for the block that added `leaf_index` to the MMR.
|
||||
fn leaf_index_to_parent_block_num(leaf_index: LeafIndex) -> BlockNumberFor<T> {
|
||||
// leaves are zero-indexed and were added one per block since pallet activation,
|
||||
// while block numbers are one-indexed, so block number that added `leaf_idx` is:
|
||||
// `block_num = block_num_when_pallet_activated + leaf_idx + 1`
|
||||
// `block_num = (current_block_num - leaves_count) + leaf_idx + 1`
|
||||
// `parent_block_num = current_block_num - leaves_count + leaf_idx`.
|
||||
<pezframe_system::Pallet<T>>::block_number()
|
||||
.saturating_sub(Self::mmr_leaves().saturated_into())
|
||||
.saturating_add(leaf_index.saturated_into())
|
||||
}
|
||||
|
||||
/// Convert a block number into a leaf index.
|
||||
fn block_num_to_leaf_index(block_num: BlockNumberFor<T>) -> Result<LeafIndex, Error>
|
||||
where
|
||||
T: pezframe_system::Config,
|
||||
{
|
||||
let first_mmr_block = utils::first_mmr_block_num::<HeaderFor<T>>(
|
||||
<pezframe_system::Pallet<T>>::block_number(),
|
||||
NumberOfLeaves::<T, I>::get(),
|
||||
)?;
|
||||
|
||||
utils::block_num_to_leaf_index::<HeaderFor<T>>(block_num, first_mmr_block)
|
||||
}
|
||||
|
||||
/// Convert a block number into a leaf index.
|
||||
pub fn block_num_to_leaf_count(block_num: BlockNumberFor<T>) -> Result<LeafIndex, Error>
|
||||
where
|
||||
T: pezframe_system::Config,
|
||||
{
|
||||
let leaf_index = Self::block_num_to_leaf_index(block_num)?;
|
||||
Ok(leaf_index.saturating_add(1))
|
||||
}
|
||||
|
||||
/// Generate an MMR proof for the given `block_numbers`.
|
||||
/// If `best_known_block_number = Some(n)`, this generates a historical proof for
|
||||
/// the chain with head at height `n`.
|
||||
/// Else it generates a proof for the MMR at the current block height.
|
||||
///
|
||||
/// Note this method can only be used from an off-chain context
|
||||
/// (Offchain Worker or Runtime API call), since it requires
|
||||
/// all the leaves to be present.
|
||||
/// It may return an error or panic if used incorrectly.
|
||||
pub fn generate_proof(
|
||||
block_numbers: Vec<BlockNumberFor<T>>,
|
||||
best_known_block_number: Option<BlockNumberFor<T>>,
|
||||
) -> Result<(Vec<LeafOf<T, I>>, LeafProof<HashOf<T, I>>), Error> {
|
||||
// check whether best_known_block_number provided, else use current best block
|
||||
let best_known_block_number =
|
||||
best_known_block_number.unwrap_or_else(|| <pezframe_system::Pallet<T>>::block_number());
|
||||
|
||||
let leaf_count = Self::block_num_to_leaf_count(best_known_block_number)?;
|
||||
|
||||
// we need to translate the block_numbers into leaf indices.
|
||||
let leaf_indices = block_numbers
|
||||
.iter()
|
||||
.map(|block_num| -> Result<LeafIndex, Error> {
|
||||
Self::block_num_to_leaf_index(*block_num)
|
||||
})
|
||||
.collect::<Result<Vec<LeafIndex>, _>>()?;
|
||||
|
||||
let mmr: ModuleMmr<mmr::storage::OffchainStorage, T, I> = mmr::Mmr::new(leaf_count);
|
||||
mmr.generate_proof(leaf_indices)
|
||||
}
|
||||
|
||||
/// Verify MMR proof for given `leaves`.
|
||||
///
|
||||
/// This method is safe to use within the runtime code.
|
||||
/// It will return `Ok(())` if the proof is valid
|
||||
/// and an `Err(..)` if MMR is inconsistent (some leaves are missing)
|
||||
/// or the proof is invalid.
|
||||
pub fn verify_leaves(
|
||||
leaves: Vec<LeafOf<T, I>>,
|
||||
proof: LeafProof<HashOf<T, I>>,
|
||||
) -> Result<(), Error> {
|
||||
if proof.leaf_count > NumberOfLeaves::<T, I>::get() ||
|
||||
proof.leaf_count == 0 ||
|
||||
proof.items.len().saturating_add(leaves.len()) as u64 > proof.leaf_count
|
||||
{
|
||||
return Err(
|
||||
Error::Verify.log_debug("The proof has incorrect number of leaves or proof items.")
|
||||
);
|
||||
}
|
||||
|
||||
let mmr: ModuleMmr<mmr::storage::OffchainStorage, T, I> = mmr::Mmr::new(proof.leaf_count);
|
||||
let is_valid = mmr.verify_leaves_proof(leaves, proof)?;
|
||||
if is_valid {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::Verify.log_debug("The proof is incorrect."))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn generate_ancestry_proof(
|
||||
prev_block_number: BlockNumberFor<T>,
|
||||
best_known_block_number: Option<BlockNumberFor<T>>,
|
||||
) -> Result<AncestryProof<HashOf<T, I>>, Error> {
|
||||
// check whether best_known_block_number provided, else use current best block
|
||||
let best_known_block_number =
|
||||
best_known_block_number.unwrap_or_else(|| <pezframe_system::Pallet<T>>::block_number());
|
||||
|
||||
let leaf_count = Self::block_num_to_leaf_count(best_known_block_number)?;
|
||||
let prev_leaf_count = Self::block_num_to_leaf_count(prev_block_number)?;
|
||||
|
||||
let mmr: ModuleMmr<mmr::storage::OffchainStorage, T, I> = mmr::Mmr::new(leaf_count);
|
||||
mmr.generate_ancestry_proof(prev_leaf_count)
|
||||
}
|
||||
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
pub fn generate_mock_ancestry_proof() -> Result<AncestryProof<HashOf<T, I>>, Error> {
|
||||
let leaf_count = Self::block_num_to_leaf_count(<pezframe_system::Pallet<T>>::block_number())?;
|
||||
let mmr: ModuleMmr<mmr::storage::OffchainStorage, T, I> = mmr::Mmr::new(leaf_count);
|
||||
mmr.generate_mock_ancestry_proof()
|
||||
}
|
||||
|
||||
pub fn is_ancestry_proof_optimal(
|
||||
ancestry_proof: &primitives::AncestryProof<HashOf<T, I>>,
|
||||
) -> bool {
|
||||
mmr::is_ancestry_proof_optimal::<HashingOf<T, I>>(ancestry_proof)
|
||||
}
|
||||
|
||||
pub fn verify_ancestry_proof(
|
||||
root: HashOf<T, I>,
|
||||
ancestry_proof: AncestryProof<HashOf<T, I>>,
|
||||
) -> Result<HashOf<T, I>, Error> {
|
||||
verify_ancestry_proof::<HashingOf<T, I>, LeafOf<T, I>>(root, ancestry_proof)
|
||||
}
|
||||
|
||||
/// Return the on-chain MMR root hash.
|
||||
pub fn mmr_root() -> HashOf<T, I> {
|
||||
RootHash::<T, I>::get()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,312 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) 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.
|
||||
|
||||
use crate::{
|
||||
mmr::{
|
||||
storage::{OffchainStorage, RuntimeStorage, Storage},
|
||||
Hasher, Node, NodeOf,
|
||||
},
|
||||
primitives::{
|
||||
mmr_lib, mmr_lib::MMRStoreReadOps, utils::NodesUtils, AncestryProof, Error, FullLeaf,
|
||||
LeafIndex, LeafProof, NodeIndex,
|
||||
},
|
||||
Config, HashOf, HashingOf,
|
||||
};
|
||||
use alloc::vec::Vec;
|
||||
use frame::prelude::*;
|
||||
|
||||
/// Stateless verification of the proof for a batch of leaves.
|
||||
/// Note, the leaves should be sorted such that corresponding leaves and leaf indices have the
|
||||
/// same position in both the `leaves` vector and the `leaf_indices` vector contained in the
|
||||
/// [primitives::LeafProof]
|
||||
pub fn verify_leaves_proof<H, L>(
|
||||
root: H::Output,
|
||||
leaves: Vec<Node<H, L>>,
|
||||
proof: LeafProof<H::Output>,
|
||||
) -> Result<bool, Error>
|
||||
where
|
||||
H: Hash,
|
||||
L: FullLeaf,
|
||||
{
|
||||
let size = NodesUtils::new(proof.leaf_count).size();
|
||||
|
||||
if leaves.len() != proof.leaf_indices.len() {
|
||||
return Err(Error::Verify.log_debug("Proof leaf_indices not same length with leaves"));
|
||||
}
|
||||
|
||||
let leaves_and_position_data = proof
|
||||
.leaf_indices
|
||||
.into_iter()
|
||||
.map(|index| mmr_lib::leaf_index_to_pos(index))
|
||||
.zip(leaves.into_iter())
|
||||
.collect();
|
||||
|
||||
let p = mmr_lib::MerkleProof::<Node<H, L>, Hasher<H, L>>::new(
|
||||
size,
|
||||
proof.items.into_iter().map(Node::Hash).collect(),
|
||||
);
|
||||
p.verify(Node::Hash(root), leaves_and_position_data)
|
||||
.map_err(|e| Error::Verify.log_debug(e))
|
||||
}
|
||||
|
||||
pub fn is_ancestry_proof_optimal<H>(ancestry_proof: &AncestryProof<H::Output>) -> bool
|
||||
where
|
||||
H: frame::traits::Hash,
|
||||
{
|
||||
let prev_mmr_size = NodesUtils::new(ancestry_proof.prev_leaf_count).size();
|
||||
let mmr_size = NodesUtils::new(ancestry_proof.leaf_count).size();
|
||||
|
||||
let expected_proof_size =
|
||||
mmr_lib::ancestry_proof::expected_ancestry_proof_size(prev_mmr_size, mmr_size);
|
||||
ancestry_proof.items.len() == expected_proof_size
|
||||
}
|
||||
|
||||
pub fn verify_ancestry_proof<H, L>(
|
||||
root: H::Output,
|
||||
ancestry_proof: AncestryProof<H::Output>,
|
||||
) -> Result<H::Output, Error>
|
||||
where
|
||||
H: Hash,
|
||||
L: FullLeaf,
|
||||
{
|
||||
let mmr_size = NodesUtils::new(ancestry_proof.leaf_count).size();
|
||||
|
||||
let prev_peaks_proof = mmr_lib::NodeMerkleProof::<Node<H, L>, Hasher<H, L>>::new(
|
||||
mmr_size,
|
||||
ancestry_proof
|
||||
.items
|
||||
.into_iter()
|
||||
.map(|(index, hash)| (index, Node::Hash(hash)))
|
||||
.collect(),
|
||||
);
|
||||
|
||||
let raw_ancestry_proof = mmr_lib::AncestryProof::<Node<H, L>, Hasher<H, L>> {
|
||||
prev_mmr_size: mmr_lib::helper::leaf_index_to_mmr_size(ancestry_proof.prev_leaf_count - 1),
|
||||
prev_peaks: ancestry_proof.prev_peaks.into_iter().map(|hash| Node::Hash(hash)).collect(),
|
||||
prev_peaks_proof,
|
||||
};
|
||||
|
||||
let prev_root = mmr_lib::ancestry_proof::bagging_peaks_hashes::<Node<H, L>, Hasher<H, L>>(
|
||||
raw_ancestry_proof.prev_peaks.clone(),
|
||||
)
|
||||
.map_err(|e| Error::Verify.log_debug(e))?;
|
||||
raw_ancestry_proof
|
||||
.verify_ancestor(Node::Hash(root), prev_root.clone())
|
||||
.map_err(|e| Error::Verify.log_debug(e))?;
|
||||
|
||||
Ok(prev_root.hash())
|
||||
}
|
||||
|
||||
/// A wrapper around an MMR library to expose limited functionality.
|
||||
///
|
||||
/// Available functions depend on the storage kind ([Runtime](crate::mmr::storage::RuntimeStorage)
|
||||
/// vs [Off-chain](crate::mmr::storage::OffchainStorage)).
|
||||
pub struct Mmr<StorageType, T, I, L>
|
||||
where
|
||||
T: Config<I>,
|
||||
I: 'static,
|
||||
L: FullLeaf,
|
||||
Storage<StorageType, T, I, L>:
|
||||
MMRStoreReadOps<NodeOf<T, I, L>> + mmr_lib::MMRStoreWriteOps<NodeOf<T, I, L>>,
|
||||
{
|
||||
mmr: mmr_lib::MMR<NodeOf<T, I, L>, Hasher<HashingOf<T, I>, L>, Storage<StorageType, T, I, L>>,
|
||||
leaves: NodeIndex,
|
||||
}
|
||||
|
||||
impl<StorageType, T, I, L> Mmr<StorageType, T, I, L>
|
||||
where
|
||||
T: Config<I>,
|
||||
I: 'static,
|
||||
L: FullLeaf,
|
||||
Storage<StorageType, T, I, L>:
|
||||
MMRStoreReadOps<NodeOf<T, I, L>> + mmr_lib::MMRStoreWriteOps<NodeOf<T, I, L>>,
|
||||
{
|
||||
/// Create a pointer to an existing MMR with given number of leaves.
|
||||
pub fn new(leaves: NodeIndex) -> Self {
|
||||
let size = NodesUtils::new(leaves).size();
|
||||
Self { mmr: mmr_lib::MMR::new(size, Default::default()), leaves }
|
||||
}
|
||||
|
||||
/// Verify proof for a set of leaves.
|
||||
/// Note, the leaves should be sorted such that corresponding leaves and leaf indices have
|
||||
/// the same position in both the `leaves` vector and the `leaf_indices` vector contained in the
|
||||
/// [primitives::LeafProof]
|
||||
pub fn verify_leaves_proof(
|
||||
&self,
|
||||
leaves: Vec<L>,
|
||||
proof: LeafProof<HashOf<T, I>>,
|
||||
) -> Result<bool, Error> {
|
||||
let p = mmr_lib::MerkleProof::<NodeOf<T, I, L>, Hasher<HashingOf<T, I>, L>>::new(
|
||||
self.mmr.mmr_size(),
|
||||
proof.items.into_iter().map(Node::Hash).collect(),
|
||||
);
|
||||
|
||||
if leaves.len() != proof.leaf_indices.len() {
|
||||
return Err(Error::Verify.log_debug("Proof leaf_indices not same length with leaves"));
|
||||
}
|
||||
|
||||
let leaves_positions_and_data = proof
|
||||
.leaf_indices
|
||||
.into_iter()
|
||||
.map(|index| mmr_lib::leaf_index_to_pos(index))
|
||||
.zip(leaves.into_iter().map(|leaf| Node::Data(leaf)))
|
||||
.collect();
|
||||
let root = self.mmr.get_root().map_err(|e| Error::GetRoot.log_error(e))?;
|
||||
p.verify(root, leaves_positions_and_data)
|
||||
.map_err(|e| Error::Verify.log_debug(e))
|
||||
}
|
||||
|
||||
/// Return the internal size of the MMR (number of nodes).
|
||||
#[cfg(any(test, feature = "runtime-benchmarks"))]
|
||||
pub fn size(&self) -> NodeIndex {
|
||||
self.mmr.mmr_size()
|
||||
}
|
||||
}
|
||||
|
||||
/// Runtime specific MMR functions.
|
||||
impl<T, I, L> Mmr<RuntimeStorage, T, I, L>
|
||||
where
|
||||
T: Config<I>,
|
||||
I: 'static,
|
||||
L: FullLeaf,
|
||||
{
|
||||
/// Push another item to the MMR.
|
||||
///
|
||||
/// Returns element position (index) in the MMR.
|
||||
pub fn push(&mut self, leaf: L) -> Option<NodeIndex> {
|
||||
let position =
|
||||
self.mmr.push(Node::Data(leaf)).map_err(|e| Error::Push.log_error(e)).ok()?;
|
||||
|
||||
self.leaves += 1;
|
||||
|
||||
Some(position)
|
||||
}
|
||||
|
||||
/// Commit the changes to underlying storage, return current number of leaves and
|
||||
/// calculate the new MMR's root hash.
|
||||
pub fn finalize(mut self) -> Result<(NodeIndex, HashOf<T, I>), Error> {
|
||||
let root = self.mmr.get_root().map_err(|e| Error::GetRoot.log_error(e))?;
|
||||
self.mmr.commit().map_err(|e| Error::Commit.log_error(e))?;
|
||||
Ok((self.leaves, root.hash()))
|
||||
}
|
||||
}
|
||||
|
||||
/// Off-chain specific MMR functions.
|
||||
impl<T, I, L> Mmr<OffchainStorage, T, I, L>
|
||||
where
|
||||
T: Config<I>,
|
||||
I: 'static,
|
||||
L: FullLeaf + codec::Decode,
|
||||
{
|
||||
/// Generate a proof for given leaf indices.
|
||||
///
|
||||
/// Proof generation requires all the nodes (or their hashes) to be available in the storage.
|
||||
/// (i.e. you can't run the function in the pruned storage).
|
||||
pub fn generate_proof(
|
||||
&self,
|
||||
leaf_indices: Vec<NodeIndex>,
|
||||
) -> Result<(Vec<L>, LeafProof<HashOf<T, I>>), Error> {
|
||||
let positions = leaf_indices
|
||||
.iter()
|
||||
.map(|index| mmr_lib::leaf_index_to_pos(*index))
|
||||
.collect::<Vec<_>>();
|
||||
let store = <Storage<OffchainStorage, T, I, L>>::default();
|
||||
let leaves = positions
|
||||
.iter()
|
||||
.map(|pos| match store.get_elem(*pos) {
|
||||
Ok(Some(Node::Data(leaf))) => Ok(leaf),
|
||||
e => Err(Error::LeafNotFound.log_debug(e)),
|
||||
})
|
||||
.collect::<Result<Vec<_>, Error>>()?;
|
||||
|
||||
let leaf_count = self.leaves;
|
||||
self.mmr
|
||||
.gen_proof(positions)
|
||||
.map_err(|e| Error::GenerateProof.log_error(e))
|
||||
.map(|p| LeafProof {
|
||||
leaf_indices,
|
||||
leaf_count,
|
||||
items: p.proof_items().iter().map(|x| x.hash()).collect(),
|
||||
})
|
||||
.map(|p| (leaves, p))
|
||||
}
|
||||
|
||||
pub fn generate_ancestry_proof(
|
||||
&self,
|
||||
prev_leaf_count: LeafIndex,
|
||||
) -> Result<AncestryProof<HashOf<T, I>>, Error> {
|
||||
let prev_mmr_size = NodesUtils::new(prev_leaf_count).size();
|
||||
let raw_ancestry_proof = self
|
||||
.mmr
|
||||
.gen_ancestry_proof(prev_mmr_size)
|
||||
.map_err(|e| Error::GenerateProof.log_error(e))?;
|
||||
|
||||
Ok(AncestryProof {
|
||||
prev_peaks: raw_ancestry_proof.prev_peaks.into_iter().map(|p| p.hash()).collect(),
|
||||
prev_leaf_count,
|
||||
leaf_count: self.leaves,
|
||||
items: raw_ancestry_proof
|
||||
.prev_peaks_proof
|
||||
.proof_items()
|
||||
.iter()
|
||||
.map(|(index, item)| (*index, item.hash()))
|
||||
.collect(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Generate an inflated ancestry proof for the latest leaf in the MMR.
|
||||
///
|
||||
/// The generated proof contains all the leafs in the MMR, so this way we can generate a proof
|
||||
/// with exactly `leaf_count` items.
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
pub fn generate_mock_ancestry_proof(&self) -> Result<AncestryProof<HashOf<T, I>>, Error> {
|
||||
use crate::ModuleMmr;
|
||||
use alloc::vec;
|
||||
use mmr_lib::helper;
|
||||
|
||||
let mmr: ModuleMmr<OffchainStorage, T, I> = Mmr::new(self.leaves);
|
||||
let store = <Storage<OffchainStorage, T, I, L>>::default();
|
||||
|
||||
let mut prev_peaks = vec![];
|
||||
for peak_pos in helper::get_peaks(mmr.size()) {
|
||||
let peak = store
|
||||
.get_elem(peak_pos)
|
||||
.map_err(|_| Error::GenerateProof)?
|
||||
.ok_or(Error::GenerateProof)?
|
||||
.hash();
|
||||
prev_peaks.push(peak);
|
||||
}
|
||||
|
||||
let mut proof_items = vec![];
|
||||
for leaf_idx in 0..self.leaves {
|
||||
let leaf_pos = NodesUtils::leaf_index_to_leaf_node_index(leaf_idx);
|
||||
let leaf = store
|
||||
.get_elem(leaf_pos)
|
||||
.map_err(|_| Error::GenerateProof)?
|
||||
.ok_or(Error::GenerateProof)?
|
||||
.hash();
|
||||
proof_items.push((leaf_pos, leaf));
|
||||
}
|
||||
|
||||
Ok(AncestryProof {
|
||||
prev_peaks,
|
||||
prev_leaf_count: self.leaves,
|
||||
leaf_count: self.leaves,
|
||||
items: proof_items,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) 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.
|
||||
|
||||
mod mmr;
|
||||
pub mod storage;
|
||||
|
||||
pub use self::mmr::{is_ancestry_proof_optimal, verify_ancestry_proof, verify_leaves_proof, Mmr};
|
||||
use crate::primitives::{mmr_lib, DataOrHash, FullLeaf};
|
||||
use frame::traits;
|
||||
|
||||
/// Node type for runtime `T`.
|
||||
pub type NodeOf<T, I, L> = Node<<T as crate::Config<I>>::Hashing, L>;
|
||||
|
||||
/// A node stored in the MMR.
|
||||
pub type Node<H, L> = DataOrHash<H, L>;
|
||||
|
||||
/// Default Merging & Hashing behavior for MMR.
|
||||
pub struct Hasher<H, L>(core::marker::PhantomData<(H, L)>);
|
||||
|
||||
impl<H: traits::Hash, L: FullLeaf> mmr_lib::Merge for Hasher<H, L> {
|
||||
type Item = Node<H, L>;
|
||||
|
||||
fn merge(left: &Self::Item, right: &Self::Item) -> mmr_lib::Result<Self::Item> {
|
||||
let mut concat = left.hash().as_ref().to_vec();
|
||||
concat.extend_from_slice(right.hash().as_ref());
|
||||
|
||||
Ok(Node::Hash(<H as traits::Hash>::hash(&concat)))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,256 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) 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.
|
||||
|
||||
//! An MMR storage implementation.
|
||||
|
||||
use crate::{
|
||||
mmr::{Node, NodeOf},
|
||||
primitives::{mmr_lib, mmr_lib::helper, utils::NodesUtils, FullLeaf, NodeIndex},
|
||||
BlockHashProvider, Config, Nodes, NumberOfLeaves, Pallet,
|
||||
};
|
||||
use alloc::{vec, vec::Vec};
|
||||
use codec::Encode;
|
||||
use core::iter::Peekable;
|
||||
use frame::{
|
||||
deps::{
|
||||
pezsp_core::offchain::StorageKind,
|
||||
pezsp_io::{offchain, offchain_index},
|
||||
},
|
||||
prelude::*,
|
||||
};
|
||||
use log::{debug, trace};
|
||||
|
||||
/// A marker type for runtime-specific storage implementation.
|
||||
///
|
||||
/// Allows appending new items to the MMR and proof verification.
|
||||
/// MMR nodes are appended to two different storages:
|
||||
/// 1. We add nodes (leaves) hashes to the on-chain storage (see [crate::Nodes]).
|
||||
/// 2. We add full leaves (and all inner nodes as well) into the `IndexingAPI` during block
|
||||
/// processing, so the values end up in the Offchain DB if indexing is enabled.
|
||||
pub struct RuntimeStorage;
|
||||
|
||||
/// A marker type for offchain-specific storage implementation.
|
||||
///
|
||||
/// Allows proof generation and verification, but does not support appending new items.
|
||||
/// MMR nodes are assumed to be stored in the Off-Chain DB. Note this storage type
|
||||
/// DOES NOT support adding new items to the MMR.
|
||||
pub struct OffchainStorage;
|
||||
|
||||
impl OffchainStorage {
|
||||
fn get(key: &[u8]) -> Option<Vec<u8>> {
|
||||
offchain::local_storage_get(StorageKind::PERSISTENT, &key)
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "runtime-benchmarks"))]
|
||||
fn set<T: Config<I>, I: 'static>(key: &[u8], value: &[u8]) {
|
||||
offchain_index::set(key, value);
|
||||
}
|
||||
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
fn set<T: Config<I>, I: 'static>(key: &[u8], value: &[u8]) {
|
||||
if crate::pallet::UseLocalStorage::<T, I>::get() {
|
||||
offchain::local_storage_set(StorageKind::PERSISTENT, key, value);
|
||||
} else {
|
||||
offchain_index::set(key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A storage layer for MMR.
|
||||
///
|
||||
/// There are two different implementations depending on the use case.
|
||||
/// See docs for [RuntimeStorage] and [OffchainStorage].
|
||||
pub struct Storage<StorageType, T, I, L>(core::marker::PhantomData<(StorageType, T, I, L)>);
|
||||
|
||||
impl<StorageType, T, I, L> Default for Storage<StorageType, T, I, L> {
|
||||
fn default() -> Self {
|
||||
Self(Default::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, I, L> mmr_lib::MMRStoreReadOps<NodeOf<T, I, L>> for Storage<OffchainStorage, T, I, L>
|
||||
where
|
||||
T: Config<I>,
|
||||
I: 'static,
|
||||
L: FullLeaf + Decode,
|
||||
{
|
||||
fn get_elem(&self, pos: NodeIndex) -> mmr_lib::Result<Option<NodeOf<T, I, L>>> {
|
||||
// Find out which leaf added node `pos` in the MMR.
|
||||
let ancestor_leaf_idx = NodesUtils::leaf_index_that_added_node(pos);
|
||||
|
||||
// We should only get here when trying to generate proofs. The client requests
|
||||
// for proofs for finalized blocks, which should usually be already canonicalized,
|
||||
// unless the MMR client gadget has a delay.
|
||||
let key = Pallet::<T, I>::node_canon_offchain_key(pos);
|
||||
debug!(
|
||||
target: "runtime::mmr::offchain", "offchain db get {}: leaf idx {:?}, canon key {:?}",
|
||||
pos, ancestor_leaf_idx, key
|
||||
);
|
||||
// Try to retrieve the element from Off-chain DB.
|
||||
if let Some(elem) = OffchainStorage::get(&key) {
|
||||
return Ok(codec::Decode::decode(&mut &*elem).ok());
|
||||
}
|
||||
|
||||
// Fall through to searching node using fork-specific key.
|
||||
let ancestor_parent_block_num =
|
||||
Pallet::<T, I>::leaf_index_to_parent_block_num(ancestor_leaf_idx);
|
||||
let ancestor_parent_hash = T::BlockHashProvider::block_hash(ancestor_parent_block_num);
|
||||
let temp_key = Pallet::<T, I>::node_temp_offchain_key(pos, ancestor_parent_hash);
|
||||
debug!(
|
||||
target: "runtime::mmr::offchain",
|
||||
"offchain db get {}: leaf idx {:?}, hash {:?}, temp key {:?}",
|
||||
pos, ancestor_leaf_idx, ancestor_parent_hash, temp_key
|
||||
);
|
||||
// Retrieve the element from Off-chain DB.
|
||||
Ok(OffchainStorage::get(&temp_key).and_then(|v| codec::Decode::decode(&mut &*v).ok()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, I, L> mmr_lib::MMRStoreWriteOps<NodeOf<T, I, L>> for Storage<OffchainStorage, T, I, L>
|
||||
where
|
||||
T: Config<I>,
|
||||
I: 'static,
|
||||
L: FullLeaf + Decode,
|
||||
{
|
||||
fn append(&mut self, _: NodeIndex, _: Vec<NodeOf<T, I, L>>) -> mmr_lib::Result<()> {
|
||||
panic!("MMR must not be altered in the off-chain context.")
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, I, L> mmr_lib::MMRStoreReadOps<NodeOf<T, I, L>> for Storage<RuntimeStorage, T, I, L>
|
||||
where
|
||||
T: Config<I>,
|
||||
I: 'static,
|
||||
L: FullLeaf,
|
||||
{
|
||||
fn get_elem(&self, pos: NodeIndex) -> mmr_lib::Result<Option<NodeOf<T, I, L>>> {
|
||||
Ok(Nodes::<T, I>::get(pos).map(Node::Hash))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, I, L> mmr_lib::MMRStoreWriteOps<NodeOf<T, I, L>> for Storage<RuntimeStorage, T, I, L>
|
||||
where
|
||||
T: Config<I>,
|
||||
I: 'static,
|
||||
L: FullLeaf,
|
||||
{
|
||||
fn append(&mut self, pos: NodeIndex, elems: Vec<NodeOf<T, I, L>>) -> mmr_lib::Result<()> {
|
||||
if elems.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
trace!(
|
||||
target: "runtime::mmr", "elems: {:?}",
|
||||
elems.iter().map(|elem| elem.hash()).collect::<Vec<_>>()
|
||||
);
|
||||
|
||||
let leaves = NumberOfLeaves::<T, I>::get();
|
||||
let size = NodesUtils::new(leaves).size();
|
||||
|
||||
if pos != size {
|
||||
return Err(mmr_lib::Error::InconsistentStore);
|
||||
}
|
||||
|
||||
let new_size = size + elems.len() as NodeIndex;
|
||||
|
||||
// A sorted (ascending) iterator over peak indices to prune and persist.
|
||||
let (peaks_to_prune, mut peaks_to_store) = peaks_to_prune_and_store(size, new_size);
|
||||
|
||||
// Now we are going to iterate over elements to insert
|
||||
// and keep track of the current `node_index` and `leaf_index`.
|
||||
let mut leaf_index = leaves;
|
||||
let mut node_index = size;
|
||||
|
||||
// Use parent hash of block adding new nodes (this block) as extra identifier
|
||||
// in offchain DB to avoid DB collisions and overwrites in case of forks.
|
||||
let parent_hash = <pezframe_system::Pallet<T>>::parent_hash();
|
||||
for elem in elems {
|
||||
// On-chain we are going to only store new peaks.
|
||||
if peaks_to_store.next_if_eq(&node_index).is_some() {
|
||||
Nodes::<T, I>::insert(node_index, elem.hash());
|
||||
}
|
||||
// We are storing full node off-chain (using indexing API).
|
||||
Self::store_to_offchain(node_index, parent_hash, &elem);
|
||||
|
||||
// Increase the indices.
|
||||
if let Node::Data(..) = elem {
|
||||
leaf_index += 1;
|
||||
}
|
||||
node_index += 1;
|
||||
}
|
||||
|
||||
// Update current number of leaves.
|
||||
NumberOfLeaves::<T, I>::put(leaf_index);
|
||||
|
||||
// And remove all remaining items from `peaks_before` collection.
|
||||
for pos in peaks_to_prune {
|
||||
Nodes::<T, I>::remove(pos);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, I, L> Storage<RuntimeStorage, T, I, L>
|
||||
where
|
||||
T: Config<I>,
|
||||
I: 'static,
|
||||
L: FullLeaf,
|
||||
{
|
||||
fn store_to_offchain(
|
||||
pos: NodeIndex,
|
||||
parent_hash: <T as pezframe_system::Config>::Hash,
|
||||
node: &NodeOf<T, I, L>,
|
||||
) {
|
||||
let encoded_node = node.encode();
|
||||
// We store this leaf offchain keyed by `(parent_hash, node_index)` to make it
|
||||
// fork-resistant. The MMR client gadget task will "canonicalize" it on the first
|
||||
// finality notification that follows, when we are not worried about forks anymore.
|
||||
let temp_key = Pallet::<T, I>::node_temp_offchain_key(pos, parent_hash);
|
||||
debug!(
|
||||
target: "runtime::mmr::offchain", "offchain db set: pos {} parent_hash {:?} key {:?}",
|
||||
pos, parent_hash, temp_key
|
||||
);
|
||||
OffchainStorage::set::<T, I>(&temp_key, &encoded_node);
|
||||
}
|
||||
}
|
||||
|
||||
fn peaks_to_prune_and_store(
|
||||
old_size: NodeIndex,
|
||||
new_size: NodeIndex,
|
||||
) -> (impl Iterator<Item = NodeIndex>, Peekable<impl Iterator<Item = NodeIndex>>) {
|
||||
// A sorted (ascending) collection of peak indices before and after insertion.
|
||||
// both collections may share a common prefix.
|
||||
let peaks_before = if old_size == 0 { vec![] } else { helper::get_peaks(old_size) };
|
||||
let peaks_after = helper::get_peaks(new_size);
|
||||
trace!(target: "runtime::mmr", "peaks_before: {:?}", peaks_before);
|
||||
trace!(target: "runtime::mmr", "peaks_after: {:?}", peaks_after);
|
||||
let mut peaks_before = peaks_before.into_iter().peekable();
|
||||
let mut peaks_after = peaks_after.into_iter().peekable();
|
||||
|
||||
// Consume a common prefix between `peaks_before` and `peaks_after`,
|
||||
// since that's something we will not be touching anyway.
|
||||
while peaks_before.peek() == peaks_after.peek() {
|
||||
peaks_before.next();
|
||||
peaks_after.next();
|
||||
}
|
||||
|
||||
// what's left in both collections is:
|
||||
// 1. Old peaks to remove from storage
|
||||
// 2. New peaks to persist in storage
|
||||
(peaks_before, peaks_after)
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) 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.
|
||||
|
||||
use crate as pezpallet_mmr;
|
||||
use crate::*;
|
||||
|
||||
use crate::{
|
||||
pezframe_system::DefaultConfig,
|
||||
primitives::{Compact, LeafDataProvider},
|
||||
};
|
||||
use codec::{Decode, Encode};
|
||||
use frame::{
|
||||
deps::pezframe_support::derive_impl,
|
||||
prelude::{pezframe_system, pezframe_system::config_preludes::TestDefaultConfig},
|
||||
testing_prelude::*,
|
||||
};
|
||||
|
||||
type Block = MockBlock<Test>;
|
||||
|
||||
construct_runtime!(
|
||||
pub enum Test
|
||||
{
|
||||
System: pezframe_system,
|
||||
MMR: pezpallet_mmr,
|
||||
}
|
||||
);
|
||||
|
||||
#[derive_impl(TestDefaultConfig)]
|
||||
impl pezframe_system::Config for Test {
|
||||
type Block = Block;
|
||||
}
|
||||
|
||||
impl Config for Test {
|
||||
const INDEXING_PREFIX: &'static [u8] = b"mmr-";
|
||||
|
||||
type Hashing = Keccak256;
|
||||
type LeafData = Compact<Keccak256, (ParentNumberAndHash<Test>, LeafData)>;
|
||||
type OnNewRoot = ();
|
||||
type BlockHashProvider = DefaultBlockHashProvider<Test>;
|
||||
type WeightInfo = ();
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
type BenchmarkHelper = ();
|
||||
}
|
||||
|
||||
#[derive(Encode, Decode, Clone, Default, Eq, PartialEq, Debug)]
|
||||
pub struct LeafData {
|
||||
pub a: u64,
|
||||
pub b: Vec<u8>,
|
||||
}
|
||||
|
||||
impl LeafData {
|
||||
pub fn new(a: u64) -> Self {
|
||||
Self { a, b: Default::default() }
|
||||
}
|
||||
}
|
||||
|
||||
parameter_types! {
|
||||
pub static LeafDataTestValue: LeafData = Default::default();
|
||||
}
|
||||
|
||||
impl LeafDataProvider for LeafData {
|
||||
type LeafData = Self;
|
||||
|
||||
fn leaf_data() -> Self::LeafData {
|
||||
LeafDataTestValue::get().clone()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,824 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) 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.
|
||||
|
||||
use crate::{mock::*, *};
|
||||
|
||||
use crate::primitives::{mmr_lib::helper, utils, Compact, LeafProof};
|
||||
|
||||
use frame::{
|
||||
deps::pezsp_core::{
|
||||
offchain::{testing::TestOffchainExt, OffchainDbExt, OffchainWorkerExt},
|
||||
H256,
|
||||
},
|
||||
testing_prelude::*,
|
||||
};
|
||||
|
||||
pub(crate) fn new_test_ext() -> TestState {
|
||||
pezframe_system::GenesisConfig::<Test>::default().build_storage().unwrap().into()
|
||||
}
|
||||
|
||||
fn register_offchain_ext(ext: &mut TestState) {
|
||||
let (offchain, _offchain_state) = TestOffchainExt::with_offchain_db(ext.offchain_db());
|
||||
ext.register_extension(OffchainDbExt::new(offchain.clone()));
|
||||
ext.register_extension(OffchainWorkerExt::new(offchain));
|
||||
}
|
||||
|
||||
fn new_block() -> Weight {
|
||||
let number = pezframe_system::Pallet::<Test>::block_number() + 1;
|
||||
let hash = H256::repeat_byte(number as u8);
|
||||
LeafDataTestValue::mutate(|r| r.a = number);
|
||||
|
||||
pezframe_system::Pallet::<Test>::reset_events();
|
||||
pezframe_system::Pallet::<Test>::initialize(&number, &hash, &Default::default());
|
||||
MMR::on_initialize(number)
|
||||
}
|
||||
|
||||
fn peaks_from_leaves_count(leaves_count: NodeIndex) -> Vec<NodeIndex> {
|
||||
let size = utils::NodesUtils::new(leaves_count).size();
|
||||
helper::get_peaks(size)
|
||||
}
|
||||
|
||||
pub(crate) fn hex(s: &str) -> H256 {
|
||||
s.parse().unwrap()
|
||||
}
|
||||
|
||||
type BlockNumber = BlockNumberFor<Test>;
|
||||
|
||||
fn decode_node(
|
||||
v: Vec<u8>,
|
||||
) -> mmr::Node<<Test as Config>::Hashing, ((BlockNumber, H256), LeafData)> {
|
||||
use crate::primitives::DataOrHash;
|
||||
type A = DataOrHash<<Test as Config>::Hashing, (BlockNumber, H256)>;
|
||||
type B = DataOrHash<<Test as Config>::Hashing, LeafData>;
|
||||
type Node = mmr::Node<<Test as Config>::Hashing, (A, B)>;
|
||||
let tuple: Node = codec::Decode::decode(&mut &v[..]).unwrap();
|
||||
|
||||
match tuple {
|
||||
mmr::Node::Data((DataOrHash::Data(a), DataOrHash::Data(b))) => mmr::Node::Data((a, b)),
|
||||
mmr::Node::Hash(hash) => mmr::Node::Hash(hash),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn add_blocks(blocks: usize) {
|
||||
// given
|
||||
for _ in 0..blocks {
|
||||
new_block();
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_start_empty() {
|
||||
pezsp_tracing::init_for_tests();
|
||||
new_test_ext().execute_with(|| {
|
||||
// given
|
||||
assert_eq!(
|
||||
crate::RootHash::<Test>::get(),
|
||||
"0000000000000000000000000000000000000000000000000000000000000000"
|
||||
.parse()
|
||||
.unwrap()
|
||||
);
|
||||
assert_eq!(crate::NumberOfLeaves::<Test>::get(), 0);
|
||||
assert_eq!(crate::Nodes::<Test>::get(0), None);
|
||||
|
||||
// when
|
||||
let weight = new_block();
|
||||
|
||||
// then
|
||||
assert_eq!(crate::NumberOfLeaves::<Test>::get(), 1);
|
||||
assert_eq!(
|
||||
crate::Nodes::<Test>::get(0),
|
||||
Some(hex("4320435e8c3318562dba60116bdbcc0b82ffcecb9bb39aae3300cfda3ad0b8b0"))
|
||||
);
|
||||
assert_eq!(
|
||||
crate::RootHash::<Test>::get(),
|
||||
hex("4320435e8c3318562dba60116bdbcc0b82ffcecb9bb39aae3300cfda3ad0b8b0")
|
||||
);
|
||||
assert!(weight != Weight::zero());
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_append_to_mmr_when_on_initialize_is_called() {
|
||||
pezsp_tracing::init_for_tests();
|
||||
let mut ext = new_test_ext();
|
||||
let (parent_b1, parent_b2) = ext.execute_with(|| {
|
||||
// when
|
||||
new_block();
|
||||
let parent_b1 = <pezframe_system::Pallet<Test>>::parent_hash();
|
||||
|
||||
// then
|
||||
assert_eq!(crate::NumberOfLeaves::<Test>::get(), 1);
|
||||
assert_eq!(
|
||||
(
|
||||
crate::Nodes::<Test>::get(0),
|
||||
crate::Nodes::<Test>::get(1),
|
||||
crate::RootHash::<Test>::get(),
|
||||
),
|
||||
(
|
||||
Some(hex("4320435e8c3318562dba60116bdbcc0b82ffcecb9bb39aae3300cfda3ad0b8b0")),
|
||||
None,
|
||||
hex("0x4320435e8c3318562dba60116bdbcc0b82ffcecb9bb39aae3300cfda3ad0b8b0"),
|
||||
)
|
||||
);
|
||||
|
||||
// when
|
||||
new_block();
|
||||
let parent_b2 = <pezframe_system::Pallet<Test>>::parent_hash();
|
||||
|
||||
// then
|
||||
assert_eq!(crate::NumberOfLeaves::<Test>::get(), 2);
|
||||
let peaks = peaks_from_leaves_count(2);
|
||||
assert_eq!(peaks, vec![2]);
|
||||
assert_eq!(
|
||||
(
|
||||
crate::Nodes::<Test>::get(0),
|
||||
crate::Nodes::<Test>::get(1),
|
||||
crate::Nodes::<Test>::get(2),
|
||||
crate::Nodes::<Test>::get(3),
|
||||
crate::RootHash::<Test>::get(),
|
||||
),
|
||||
(
|
||||
None,
|
||||
None,
|
||||
Some(hex("672c04a9cd05a644789d769daa552d35d8de7c33129f8a7cbf49e595234c4854")),
|
||||
None,
|
||||
hex("672c04a9cd05a644789d769daa552d35d8de7c33129f8a7cbf49e595234c4854"),
|
||||
)
|
||||
);
|
||||
|
||||
(parent_b1, parent_b2)
|
||||
});
|
||||
// make sure the leaves end up in the offchain DB
|
||||
ext.persist_offchain_overlay();
|
||||
|
||||
let offchain_db = ext.offchain_db();
|
||||
|
||||
let expected = Some(mmr::Node::Data(((0, H256::repeat_byte(1)), LeafData::new(1))));
|
||||
assert_eq!(
|
||||
offchain_db.get(&MMR::node_temp_offchain_key(0, parent_b1)).map(decode_node),
|
||||
expected
|
||||
);
|
||||
|
||||
let expected = Some(mmr::Node::Data(((1, H256::repeat_byte(2)), LeafData::new(2))));
|
||||
assert_eq!(
|
||||
offchain_db.get(&MMR::node_temp_offchain_key(1, parent_b2)).map(decode_node),
|
||||
expected
|
||||
);
|
||||
|
||||
let expected = Some(mmr::Node::Hash(hex(
|
||||
"672c04a9cd05a644789d769daa552d35d8de7c33129f8a7cbf49e595234c4854",
|
||||
)));
|
||||
assert_eq!(
|
||||
offchain_db.get(&MMR::node_temp_offchain_key(2, parent_b2)).map(decode_node),
|
||||
expected
|
||||
);
|
||||
|
||||
assert_eq!(offchain_db.get(&MMR::node_temp_offchain_key(3, parent_b2)), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_construct_larger_mmr_correctly() {
|
||||
pezsp_tracing::init_for_tests();
|
||||
new_test_ext().execute_with(|| {
|
||||
// when
|
||||
add_blocks(7);
|
||||
|
||||
// then
|
||||
assert_eq!(crate::NumberOfLeaves::<Test>::get(), 7);
|
||||
let peaks = peaks_from_leaves_count(7);
|
||||
assert_eq!(peaks, vec![6, 9, 10]);
|
||||
for i in (0..=10).filter(|p| !peaks.contains(p)) {
|
||||
assert!(crate::Nodes::<Test>::get(i).is_none());
|
||||
}
|
||||
assert_eq!(
|
||||
(
|
||||
crate::Nodes::<Test>::get(6),
|
||||
crate::Nodes::<Test>::get(9),
|
||||
crate::Nodes::<Test>::get(10),
|
||||
crate::RootHash::<Test>::get(),
|
||||
),
|
||||
(
|
||||
Some(hex("ae88a0825da50e953e7a359c55fe13c8015e48d03d301b8bdfc9193874da9252")),
|
||||
Some(hex("7e4316ae2ebf7c3b6821cb3a46ca8b7a4f9351a9b40fcf014bb0a4fd8e8f29da")),
|
||||
Some(hex("611c2174c6164952a66d985cfe1ec1a623794393e3acff96b136d198f37a648c")),
|
||||
hex("e45e25259f7930626431347fa4dd9aae7ac83b4966126d425ca70ab343709d2c"),
|
||||
)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_calculate_the_size_correctly() {
|
||||
pezsp_tracing::init_for_tests();
|
||||
|
||||
let leaves = vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 21];
|
||||
let sizes = vec![0, 1, 3, 4, 7, 8, 10, 11, 15, 16, 18, 19, 22, 23, 25, 26, 39];
|
||||
|
||||
// size cross-check
|
||||
let mut actual_sizes = vec![];
|
||||
for s in &leaves[1..] {
|
||||
new_test_ext().execute_with(|| {
|
||||
let mut mmr = mmr::Mmr::<mmr::storage::RuntimeStorage, crate::mock::Test, _, _>::new(0);
|
||||
for i in 0..*s {
|
||||
mmr.push(i);
|
||||
}
|
||||
actual_sizes.push(mmr.size());
|
||||
})
|
||||
}
|
||||
assert_eq!(sizes[1..], actual_sizes[..]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_generate_proofs_correctly() {
|
||||
pezsp_tracing::init_for_tests();
|
||||
let mut ext = new_test_ext();
|
||||
// given
|
||||
let num_blocks: u64 = 7;
|
||||
ext.execute_with(|| add_blocks(num_blocks as usize));
|
||||
ext.persist_offchain_overlay();
|
||||
|
||||
// Try to generate proofs now. This requires the offchain extensions to be present
|
||||
// to retrieve full leaf data.
|
||||
register_offchain_ext(&mut ext);
|
||||
ext.execute_with(|| {
|
||||
let best_block_number = pezframe_system::Pallet::<Test>::block_number();
|
||||
// when generate proofs for all leaves.
|
||||
let proofs = (1_u64..=best_block_number)
|
||||
.into_iter()
|
||||
.map(|block_num| crate::Pallet::<Test>::generate_proof(vec![block_num], None).unwrap())
|
||||
.collect::<Vec<_>>();
|
||||
// when generate historical proofs for all leaves
|
||||
let historical_proofs = (1_u64..best_block_number)
|
||||
.into_iter()
|
||||
.map(|block_num| {
|
||||
let mut proofs = vec![];
|
||||
for historical_best_block in block_num..=num_blocks {
|
||||
proofs.push(
|
||||
crate::Pallet::<Test>::generate_proof(
|
||||
vec![block_num],
|
||||
Some(historical_best_block),
|
||||
)
|
||||
.unwrap(),
|
||||
)
|
||||
}
|
||||
proofs
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// then
|
||||
assert_eq!(
|
||||
proofs[0],
|
||||
(
|
||||
vec![Compact::new(((0, H256::repeat_byte(1)).into(), LeafData::new(1).into(),))],
|
||||
LeafProof {
|
||||
leaf_indices: vec![0],
|
||||
leaf_count: 7,
|
||||
items: vec![
|
||||
hex("ad4cbc033833612ccd4626d5f023b9dfc50a35e838514dd1f3c86f8506728705"),
|
||||
hex("cb24f4614ad5b2a5430344c99545b421d9af83c46fd632d70a332200884b4d46"),
|
||||
hex("dca421199bdcc55bb773c6b6967e8d16675de69062b52285ca63685241fdf626"),
|
||||
],
|
||||
}
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
historical_proofs[0][0],
|
||||
(
|
||||
vec![Compact::new(((0, H256::repeat_byte(1)).into(), LeafData::new(1).into(),))],
|
||||
LeafProof { leaf_indices: vec![0], leaf_count: 1, items: vec![] }
|
||||
)
|
||||
);
|
||||
|
||||
// D
|
||||
// / \
|
||||
// / \
|
||||
// A B C
|
||||
// / \ / \ / \
|
||||
// 1 2 3 4 5 6 7
|
||||
//
|
||||
// we're proving 3 => we need { 4, A, C++7 }
|
||||
assert_eq!(
|
||||
proofs[2],
|
||||
(
|
||||
vec![Compact::new(((2, H256::repeat_byte(3)).into(), LeafData::new(3).into(),))],
|
||||
LeafProof {
|
||||
leaf_indices: vec![2],
|
||||
leaf_count: 7,
|
||||
items: vec![
|
||||
hex("1b14c1dc7d3e4def11acdf31be0584f4b85c3673f1ff72a3af467b69a3b0d9d0"),
|
||||
hex("672c04a9cd05a644789d769daa552d35d8de7c33129f8a7cbf49e595234c4854"),
|
||||
hex("dca421199bdcc55bb773c6b6967e8d16675de69062b52285ca63685241fdf626"),
|
||||
],
|
||||
}
|
||||
)
|
||||
);
|
||||
// A
|
||||
// / \
|
||||
// 1 2 3
|
||||
//
|
||||
// we're proving 3 => we need { A }
|
||||
assert_eq!(
|
||||
historical_proofs[2][0],
|
||||
(
|
||||
vec![Compact::new(((2, H256::repeat_byte(3)).into(), LeafData::new(3).into(),))],
|
||||
LeafProof {
|
||||
leaf_indices: vec![2],
|
||||
leaf_count: 3,
|
||||
items: vec![hex(
|
||||
"672c04a9cd05a644789d769daa552d35d8de7c33129f8a7cbf49e595234c4854"
|
||||
)],
|
||||
}
|
||||
)
|
||||
);
|
||||
// D
|
||||
// / \
|
||||
// / \
|
||||
// A B
|
||||
// / \ / \
|
||||
// 1 2 3 4 5
|
||||
// we're proving 3 => we need { 4, A, 5 }
|
||||
assert_eq!(
|
||||
historical_proofs[2][2],
|
||||
(
|
||||
vec![Compact::new(((2, H256::repeat_byte(3)).into(), LeafData::new(3).into(),))],
|
||||
LeafProof {
|
||||
leaf_indices: vec![2],
|
||||
leaf_count: 5,
|
||||
items: vec![
|
||||
hex("1b14c1dc7d3e4def11acdf31be0584f4b85c3673f1ff72a3af467b69a3b0d9d0"),
|
||||
hex("672c04a9cd05a644789d769daa552d35d8de7c33129f8a7cbf49e595234c4854"),
|
||||
hex("3b031d22e24f1126c8f7d2f394b663f9b960ed7abbedb7152e17ce16112656d0")
|
||||
],
|
||||
}
|
||||
)
|
||||
);
|
||||
assert_eq!(historical_proofs[2][4], proofs[2]);
|
||||
|
||||
assert_eq!(
|
||||
proofs[4],
|
||||
(
|
||||
// NOTE: the leaf index is equivalent to the block number(in this case 5) - 1
|
||||
vec![Compact::new(((4, H256::repeat_byte(5)).into(), LeafData::new(5).into(),))],
|
||||
LeafProof {
|
||||
leaf_indices: vec![4],
|
||||
leaf_count: 7,
|
||||
items: vec![
|
||||
hex("ae88a0825da50e953e7a359c55fe13c8015e48d03d301b8bdfc9193874da9252"),
|
||||
hex("8ed25570209d8f753d02df07c1884ddb36a3d9d4770e4608b188322151c657fe"),
|
||||
hex("611c2174c6164952a66d985cfe1ec1a623794393e3acff96b136d198f37a648c"),
|
||||
],
|
||||
}
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
historical_proofs[4][0],
|
||||
(
|
||||
vec![Compact::new(((4, H256::repeat_byte(5)).into(), LeafData::new(5).into(),))],
|
||||
LeafProof {
|
||||
leaf_indices: vec![4],
|
||||
leaf_count: 5,
|
||||
items: vec![hex(
|
||||
"ae88a0825da50e953e7a359c55fe13c8015e48d03d301b8bdfc9193874da9252"
|
||||
),],
|
||||
}
|
||||
)
|
||||
);
|
||||
assert_eq!(historical_proofs[4][2], proofs[4]);
|
||||
|
||||
assert_eq!(
|
||||
proofs[6],
|
||||
(
|
||||
vec![Compact::new(((6, H256::repeat_byte(7)).into(), LeafData::new(7).into(),))],
|
||||
LeafProof {
|
||||
leaf_indices: vec![6],
|
||||
leaf_count: 7,
|
||||
items: vec![
|
||||
hex("ae88a0825da50e953e7a359c55fe13c8015e48d03d301b8bdfc9193874da9252"),
|
||||
hex("7e4316ae2ebf7c3b6821cb3a46ca8b7a4f9351a9b40fcf014bb0a4fd8e8f29da"),
|
||||
],
|
||||
}
|
||||
)
|
||||
);
|
||||
assert_eq!(historical_proofs[5][1], proofs[5]);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_generate_batch_proof_correctly() {
|
||||
pezsp_tracing::init_for_tests();
|
||||
let mut ext = new_test_ext();
|
||||
// given
|
||||
ext.execute_with(|| add_blocks(7));
|
||||
ext.persist_offchain_overlay();
|
||||
|
||||
// Try to generate proofs now. This requires the offchain extensions to be present
|
||||
// to retrieve full leaf data.
|
||||
register_offchain_ext(&mut ext);
|
||||
ext.execute_with(|| {
|
||||
// when generate proofs for a batch of leaves
|
||||
let (.., proof) = crate::Pallet::<Test>::generate_proof(vec![1, 5, 6], None).unwrap();
|
||||
// then
|
||||
assert_eq!(
|
||||
proof,
|
||||
LeafProof {
|
||||
// the leaf indices are equivalent to the above specified block numbers - 1.
|
||||
leaf_indices: vec![0, 4, 5],
|
||||
leaf_count: 7,
|
||||
items: vec![
|
||||
hex("ad4cbc033833612ccd4626d5f023b9dfc50a35e838514dd1f3c86f8506728705"),
|
||||
hex("cb24f4614ad5b2a5430344c99545b421d9af83c46fd632d70a332200884b4d46"),
|
||||
hex("611c2174c6164952a66d985cfe1ec1a623794393e3acff96b136d198f37a648c"),
|
||||
],
|
||||
}
|
||||
);
|
||||
|
||||
// when generate historical proofs for a batch of leaves
|
||||
let (.., historical_proof) =
|
||||
crate::Pallet::<Test>::generate_proof(vec![1, 5, 6], Some(6)).unwrap();
|
||||
// then
|
||||
assert_eq!(
|
||||
historical_proof,
|
||||
LeafProof {
|
||||
leaf_indices: vec![0, 4, 5],
|
||||
leaf_count: 6,
|
||||
items: vec![
|
||||
hex("ad4cbc033833612ccd4626d5f023b9dfc50a35e838514dd1f3c86f8506728705"),
|
||||
hex("cb24f4614ad5b2a5430344c99545b421d9af83c46fd632d70a332200884b4d46"),
|
||||
],
|
||||
}
|
||||
);
|
||||
|
||||
// when generate historical proofs for a batch of leaves
|
||||
let (.., historical_proof) =
|
||||
crate::Pallet::<Test>::generate_proof(vec![1, 5, 6], None).unwrap();
|
||||
// then
|
||||
assert_eq!(historical_proof, proof);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_verify() {
|
||||
pezsp_tracing::init_for_tests();
|
||||
|
||||
// Start off with chain initialisation and storing indexing data off-chain
|
||||
// (MMR Leafs)
|
||||
let mut ext = new_test_ext();
|
||||
ext.execute_with(|| add_blocks(7));
|
||||
ext.persist_offchain_overlay();
|
||||
|
||||
// Try to generate proof now. This requires the offchain extensions to be present
|
||||
// to retrieve full leaf data.
|
||||
register_offchain_ext(&mut ext);
|
||||
let (leaves, proof5) = ext.execute_with(|| {
|
||||
// when
|
||||
crate::Pallet::<Test>::generate_proof(vec![5], None).unwrap()
|
||||
});
|
||||
let (simple_historical_leaves, simple_historical_proof5) = ext.execute_with(|| {
|
||||
// when
|
||||
crate::Pallet::<Test>::generate_proof(vec![5], Some(6)).unwrap()
|
||||
});
|
||||
let (advanced_historical_leaves, advanced_historical_proof5) = ext.execute_with(|| {
|
||||
// when
|
||||
crate::Pallet::<Test>::generate_proof(vec![5], Some(7)).unwrap()
|
||||
});
|
||||
|
||||
ext.execute_with(|| {
|
||||
add_blocks(7);
|
||||
// then
|
||||
assert_eq!(crate::Pallet::<Test>::verify_leaves(leaves, proof5), Ok(()));
|
||||
assert_eq!(
|
||||
crate::Pallet::<Test>::verify_leaves(
|
||||
simple_historical_leaves,
|
||||
simple_historical_proof5
|
||||
),
|
||||
Ok(())
|
||||
);
|
||||
assert_eq!(
|
||||
crate::Pallet::<Test>::verify_leaves(
|
||||
advanced_historical_leaves,
|
||||
advanced_historical_proof5
|
||||
),
|
||||
Ok(())
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
fn generate_and_verify_batch_proof(
|
||||
ext: &mut TestExternalities,
|
||||
block_numbers: &Vec<u64>,
|
||||
blocks_to_add: usize,
|
||||
) {
|
||||
let (leaves, proof) = ext.execute_with(|| {
|
||||
crate::Pallet::<Test>::generate_proof(block_numbers.to_vec(), None).unwrap()
|
||||
});
|
||||
|
||||
let max_block_number = ext.execute_with(|| pezframe_system::Pallet::<Test>::block_number());
|
||||
let min_block_number = block_numbers.iter().max().unwrap();
|
||||
|
||||
// generate all possible historical proofs for the given blocks
|
||||
let historical_proofs = (*min_block_number..=max_block_number)
|
||||
.map(|best_block| {
|
||||
ext.execute_with(|| {
|
||||
crate::Pallet::<Test>::generate_proof(block_numbers.to_vec(), Some(best_block))
|
||||
.unwrap()
|
||||
})
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
ext.execute_with(|| {
|
||||
add_blocks(blocks_to_add);
|
||||
// then
|
||||
assert_eq!(crate::Pallet::<Test>::verify_leaves(leaves, proof), Ok(()));
|
||||
historical_proofs.iter().for_each(|(leaves, proof)| {
|
||||
assert_eq!(crate::Pallet::<Test>::verify_leaves(leaves.clone(), proof.clone()), Ok(()));
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_verify_batch_proofs() {
|
||||
pezsp_tracing::init_for_tests();
|
||||
|
||||
use itertools::Itertools;
|
||||
|
||||
let mut ext = new_test_ext();
|
||||
// require the offchain extensions to be present
|
||||
// to retrieve full leaf data when generating proofs
|
||||
register_offchain_ext(&mut ext);
|
||||
|
||||
// verify that up to n=10, valid proofs are generated for all possible block number
|
||||
// combinations.
|
||||
for n in 1..=10 {
|
||||
ext.execute_with(|| new_block());
|
||||
ext.persist_offchain_overlay();
|
||||
|
||||
// generate powerset (skipping empty set) of all possible block number combinations for mmr
|
||||
// size n.
|
||||
let blocks_set: Vec<Vec<u64>> = (1..=n).into_iter().powerset().skip(1).collect();
|
||||
|
||||
blocks_set.iter().for_each(|blocks_subset| {
|
||||
generate_and_verify_batch_proof(&mut ext, &blocks_subset, 0);
|
||||
ext.persist_offchain_overlay();
|
||||
});
|
||||
}
|
||||
|
||||
// verify that up to n=15, valid proofs are generated for all possible 2-block number
|
||||
// combinations.
|
||||
for n in 11..=15 {
|
||||
ext.execute_with(|| new_block());
|
||||
ext.persist_offchain_overlay();
|
||||
|
||||
// generate all possible 2-block number combinations for mmr size n.
|
||||
let blocks_set: Vec<Vec<u64>> = (1..=n).into_iter().combinations(2).collect();
|
||||
|
||||
blocks_set.iter().for_each(|blocks_subset| {
|
||||
generate_and_verify_batch_proof(&mut ext, &blocks_subset, 0);
|
||||
ext.persist_offchain_overlay();
|
||||
});
|
||||
}
|
||||
|
||||
generate_and_verify_batch_proof(&mut ext, &vec![8, 12], 20);
|
||||
ext.execute_with(|| add_blocks(1000));
|
||||
ext.persist_offchain_overlay();
|
||||
generate_and_verify_batch_proof(&mut ext, &vec![8, 12, 100, 800], 100);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn verification_should_be_stateless() {
|
||||
pezsp_tracing::init_for_tests();
|
||||
|
||||
// Start off with chain initialisation and storing indexing data off-chain
|
||||
// (MMR Leafs)
|
||||
let mut ext = new_test_ext();
|
||||
let (root_6, root_7) = ext.execute_with(|| {
|
||||
add_blocks(6);
|
||||
let root_6 = crate::Pallet::<Test>::mmr_root();
|
||||
add_blocks(1);
|
||||
let root_7 = crate::Pallet::<Test>::mmr_root();
|
||||
(root_6, root_7)
|
||||
});
|
||||
ext.persist_offchain_overlay();
|
||||
|
||||
// Try to generate proof now. This requires the offchain extensions to be present
|
||||
// to retrieve full leaf data.
|
||||
register_offchain_ext(&mut ext);
|
||||
let (leaves, proof5) = ext.execute_with(|| {
|
||||
// when
|
||||
crate::Pallet::<Test>::generate_proof(vec![5], None).unwrap()
|
||||
});
|
||||
let (_, historical_proof5) = ext.execute_with(|| {
|
||||
// when
|
||||
crate::Pallet::<Test>::generate_proof(vec![5], Some(6)).unwrap()
|
||||
});
|
||||
|
||||
// Verify proof without relying on any on-chain data.
|
||||
let leaf = crate::primitives::DataOrHash::Data(leaves[0].clone());
|
||||
assert_eq!(
|
||||
crate::verify_leaves_proof::<<Test as Config>::Hashing, _>(
|
||||
root_7,
|
||||
vec![leaf.clone()],
|
||||
proof5
|
||||
),
|
||||
Ok(())
|
||||
);
|
||||
assert_eq!(
|
||||
crate::verify_leaves_proof::<<Test as Config>::Hashing, _>(
|
||||
root_6,
|
||||
vec![leaf],
|
||||
historical_proof5
|
||||
),
|
||||
Ok(())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_verify_batch_proof_statelessly() {
|
||||
pezsp_tracing::init_for_tests();
|
||||
|
||||
// Start off with chain initialisation and storing indexing data off-chain
|
||||
// (MMR Leafs)
|
||||
let mut ext = new_test_ext();
|
||||
let (root_6, root_7) = ext.execute_with(|| {
|
||||
add_blocks(6);
|
||||
let root_6 = crate::Pallet::<Test>::mmr_root();
|
||||
add_blocks(1);
|
||||
let root_7 = crate::Pallet::<Test>::mmr_root();
|
||||
(root_6, root_7)
|
||||
});
|
||||
ext.persist_offchain_overlay();
|
||||
|
||||
// Try to generate proof now. This requires the offchain extensions to be present
|
||||
// to retrieve full leaf data.
|
||||
register_offchain_ext(&mut ext);
|
||||
let (leaves, proof) = ext.execute_with(|| {
|
||||
// when
|
||||
crate::Pallet::<Test>::generate_proof(vec![1, 4, 5], None).unwrap()
|
||||
});
|
||||
let (historical_leaves, historical_proof) = ext.execute_with(|| {
|
||||
// when
|
||||
crate::Pallet::<Test>::generate_proof(vec![1, 4, 5], Some(6)).unwrap()
|
||||
});
|
||||
|
||||
// Verify proof without relying on any on-chain data.
|
||||
assert_eq!(
|
||||
crate::verify_leaves_proof::<<Test as Config>::Hashing, _>(
|
||||
root_7,
|
||||
leaves
|
||||
.into_iter()
|
||||
.map(|leaf| crate::primitives::DataOrHash::Data(leaf))
|
||||
.collect(),
|
||||
proof
|
||||
),
|
||||
Ok(())
|
||||
);
|
||||
assert_eq!(
|
||||
crate::verify_leaves_proof::<<Test as Config>::Hashing, _>(
|
||||
root_6,
|
||||
historical_leaves
|
||||
.into_iter()
|
||||
.map(|leaf| crate::primitives::DataOrHash::Data(leaf))
|
||||
.collect(),
|
||||
historical_proof
|
||||
),
|
||||
Ok(())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_verify_on_the_next_block_since_there_is_no_pruning_yet() {
|
||||
pezsp_tracing::init_for_tests();
|
||||
let mut ext = new_test_ext();
|
||||
// given
|
||||
ext.execute_with(|| add_blocks(7));
|
||||
|
||||
ext.persist_offchain_overlay();
|
||||
register_offchain_ext(&mut ext);
|
||||
|
||||
ext.execute_with(|| {
|
||||
// when
|
||||
let (leaves, proof5) = crate::Pallet::<Test>::generate_proof(vec![5], None).unwrap();
|
||||
new_block();
|
||||
|
||||
// then
|
||||
assert_eq!(crate::Pallet::<Test>::verify_leaves(leaves, proof5), Ok(()));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_verify_canonicalized() {
|
||||
pezsp_tracing::init_for_tests();
|
||||
|
||||
// How deep is our fork-aware storage (in terms of blocks/leaves, nodes will be more).
|
||||
let block_hash_size: u64 = <Test as pezframe_system::Config>::BlockHashCount::get();
|
||||
|
||||
// Start off with chain initialisation and storing indexing data off-chain.
|
||||
// Create twice as many leaf entries than our fork-aware capacity,
|
||||
// resulting in ~half of MMR storage to use canonical keys and the other half fork-aware keys.
|
||||
// Verify that proofs can be generated (using leaves and nodes from full set) and verified.
|
||||
let mut ext = new_test_ext();
|
||||
register_offchain_ext(&mut ext);
|
||||
for blocknum in 0u32..(2 * block_hash_size).try_into().unwrap() {
|
||||
ext.execute_with(|| {
|
||||
new_block();
|
||||
<Pallet<Test> as Hooks<BlockNumber>>::offchain_worker(blocknum.into());
|
||||
});
|
||||
ext.persist_offchain_overlay();
|
||||
}
|
||||
|
||||
// Generate proofs for some blocks.
|
||||
let (leaves, proofs) =
|
||||
ext.execute_with(|| crate::Pallet::<Test>::generate_proof(vec![1, 4, 5, 7], None).unwrap());
|
||||
// Verify all previously generated proofs.
|
||||
ext.execute_with(|| {
|
||||
assert_eq!(crate::Pallet::<Test>::verify_leaves(leaves, proofs), Ok(()));
|
||||
});
|
||||
|
||||
// Generate proofs for some new blocks.
|
||||
let (leaves, proofs) = ext.execute_with(|| {
|
||||
crate::Pallet::<Test>::generate_proof(vec![block_hash_size + 7], None).unwrap()
|
||||
});
|
||||
// Add some more blocks then verify all previously generated proofs.
|
||||
ext.execute_with(|| {
|
||||
add_blocks(7);
|
||||
assert_eq!(crate::Pallet::<Test>::verify_leaves(leaves, proofs), Ok(()));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn does_not_panic_when_generating_historical_proofs() {
|
||||
pezsp_tracing::init_for_tests();
|
||||
let mut ext = new_test_ext();
|
||||
|
||||
// given 7 blocks (7 MMR leaves)
|
||||
ext.execute_with(|| add_blocks(7));
|
||||
ext.persist_offchain_overlay();
|
||||
|
||||
// Try to generate historical proof with invalid arguments. This requires the offchain
|
||||
// extensions to be present to retrieve full leaf data.
|
||||
register_offchain_ext(&mut ext);
|
||||
ext.execute_with(|| {
|
||||
// when leaf index is invalid
|
||||
assert_eq!(crate::Pallet::<Test>::generate_proof(vec![10], None), Err(Error::LeafNotFound),);
|
||||
|
||||
// when leaves count is invalid
|
||||
assert_eq!(
|
||||
crate::Pallet::<Test>::generate_proof(vec![3], Some(100)),
|
||||
Err(Error::GenerateProof),
|
||||
);
|
||||
|
||||
// when both leaf index and leaves count are invalid
|
||||
assert_eq!(
|
||||
crate::Pallet::<Test>::generate_proof(vec![10], Some(100)),
|
||||
Err(Error::LeafNotFound),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn generating_and_verifying_ancestry_proofs_works_correctly() {
|
||||
pezsp_tracing::init_for_tests();
|
||||
let mut ext = new_test_ext();
|
||||
|
||||
let mut prev_roots = vec![];
|
||||
ext.execute_with(|| {
|
||||
for _ in 1..=500 {
|
||||
add_blocks(1);
|
||||
prev_roots.push(Pallet::<Test>::mmr_root())
|
||||
}
|
||||
});
|
||||
ext.persist_offchain_overlay();
|
||||
register_offchain_ext(&mut ext);
|
||||
|
||||
ext.execute_with(|| {
|
||||
let root = Pallet::<Test>::mmr_root();
|
||||
// Check that generating and verifying ancestry proofs works correctly
|
||||
// for each previous block
|
||||
for prev_block_number in 1usize..=500 {
|
||||
let proof =
|
||||
Pallet::<Test>::generate_ancestry_proof(prev_block_number as u64, None).unwrap();
|
||||
assert!(Pallet::<Test>::is_ancestry_proof_optimal(&proof));
|
||||
assert_eq!(
|
||||
Pallet::<Test>::verify_ancestry_proof(root, proof),
|
||||
Ok(prev_roots[prev_block_number - 1])
|
||||
);
|
||||
}
|
||||
|
||||
// Check that we can't generate ancestry proofs for a future block.
|
||||
assert_eq!(Pallet::<Test>::generate_ancestry_proof(501, None), Err(Error::GenerateProof));
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) 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.
|
||||
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) 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.
|
||||
|
||||
//! Autogenerated weights for `pezpallet_mmr`
|
||||
//!
|
||||
//! THIS FILE WAS AUTO-GENERATED USING THE BIZINIKIWI BENCHMARK CLI VERSION 32.0.0
|
||||
//! DATE: 2025-02-21, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
|
||||
//! WORST CASE MAP SIZE: `1000000`
|
||||
//! HOSTNAME: `4563561839a5`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz`
|
||||
//! WASM-EXECUTION: `Compiled`, CHAIN: `None`, DB CACHE: `1024`
|
||||
|
||||
// Executed Command:
|
||||
// frame-omni-bencher
|
||||
// v1
|
||||
// benchmark
|
||||
// pallet
|
||||
// --extrinsic=*
|
||||
// --runtime=target/production/wbuild/kitchensink-runtime/kitchensink_runtime.wasm
|
||||
// --pallet=pezpallet_mmr
|
||||
// --header=/__w/pezkuwi-sdk/pezkuwi-sdk/bizinikiwi/HEADER-APACHE2
|
||||
// --output=/__w/pezkuwi-sdk/pezkuwi-sdk/bizinikiwi/pezframe/merkle-mountain-range/src/weights.rs
|
||||
// --wasm-execution=compiled
|
||||
// --steps=50
|
||||
// --repeat=20
|
||||
// --heap-pages=4096
|
||||
// --template=bizinikiwi/.maintain/frame-umbrella-weight-template.hbs
|
||||
// --no-storage-info
|
||||
// --no-min-squares
|
||||
// --no-median-slopes
|
||||
// --genesis-builder-policy=none
|
||||
// --exclude-pallets=pezpallet_xcm,pezpallet_xcm_benchmarks::fungible,pezpallet_xcm_benchmarks::generic,pezpallet_nomination_pools,pezpallet_remark,pezpallet_transaction_storage,pezpallet_election_provider_multi_block,pezpallet_election_provider_multi_block::signed,pezpallet_election_provider_multi_block::unsigned,pezpallet_election_provider_multi_block::verifier
|
||||
|
||||
#![cfg_attr(rustfmt, rustfmt_skip)]
|
||||
#![allow(unused_parens)]
|
||||
#![allow(unused_imports)]
|
||||
#![allow(missing_docs)]
|
||||
#![allow(dead_code)]
|
||||
|
||||
use frame::weights_prelude::*;
|
||||
|
||||
/// Weight functions needed for `pezpallet_mmr`.
|
||||
pub trait WeightInfo {
|
||||
fn on_initialize(x: u32, ) -> Weight;
|
||||
}
|
||||
|
||||
/// Weights for `pezpallet_mmr` using the Bizinikiwi node and recommended hardware.
|
||||
pub struct BizinikiwiWeight<T>(PhantomData<T>);
|
||||
impl<T: pezframe_system::Config> WeightInfo for BizinikiwiWeight<T> {
|
||||
/// Storage: `Mmr::NumberOfLeaves` (r:1 w:1)
|
||||
/// Proof: `Mmr::NumberOfLeaves` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `MaxEncodedLen`)
|
||||
/// Storage: `System::ParentHash` (r:1 w:0)
|
||||
/// Proof: `System::ParentHash` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`)
|
||||
/// Storage: `Mmr::Nodes` (r:7 w:1)
|
||||
/// Proof: `Mmr::Nodes` (`max_values`: None, `max_size`: Some(40), added: 2515, mode: `MaxEncodedLen`)
|
||||
/// Storage: `Mmr::UseLocalStorage` (r:1 w:0)
|
||||
/// Proof: `Mmr::UseLocalStorage` (`max_values`: Some(1), `max_size`: Some(1), added: 496, mode: `MaxEncodedLen`)
|
||||
/// Storage: `Mmr::RootHash` (r:0 w:1)
|
||||
/// Proof: `Mmr::RootHash` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`)
|
||||
/// The range of component `x` is `[1, 1000]`.
|
||||
fn on_initialize(x: u32, ) -> Weight {
|
||||
// Proof Size summary in bytes:
|
||||
// Measured: `227`
|
||||
// Estimated: `9242 + x * (8 ±0)`
|
||||
// Minimum execution time: 8_924_000 picoseconds.
|
||||
Weight::from_parts(26_954_406, 9242)
|
||||
// Standard Error: 940
|
||||
.saturating_add(Weight::from_parts(24_147, 0).saturating_mul(x.into()))
|
||||
.saturating_add(T::DbWeight::get().reads(6_u64))
|
||||
.saturating_add(T::DbWeight::get().writes(4_u64))
|
||||
.saturating_add(Weight::from_parts(0, 8).saturating_mul(x.into()))
|
||||
}
|
||||
}
|
||||
|
||||
// For backwards compatibility and tests.
|
||||
impl WeightInfo for () {
|
||||
/// Storage: `Mmr::NumberOfLeaves` (r:1 w:1)
|
||||
/// Proof: `Mmr::NumberOfLeaves` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `MaxEncodedLen`)
|
||||
/// Storage: `System::ParentHash` (r:1 w:0)
|
||||
/// Proof: `System::ParentHash` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`)
|
||||
/// Storage: `Mmr::Nodes` (r:7 w:1)
|
||||
/// Proof: `Mmr::Nodes` (`max_values`: None, `max_size`: Some(40), added: 2515, mode: `MaxEncodedLen`)
|
||||
/// Storage: `Mmr::UseLocalStorage` (r:1 w:0)
|
||||
/// Proof: `Mmr::UseLocalStorage` (`max_values`: Some(1), `max_size`: Some(1), added: 496, mode: `MaxEncodedLen`)
|
||||
/// Storage: `Mmr::RootHash` (r:0 w:1)
|
||||
/// Proof: `Mmr::RootHash` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`)
|
||||
/// The range of component `x` is `[1, 1000]`.
|
||||
fn on_initialize(x: u32, ) -> Weight {
|
||||
// Proof Size summary in bytes:
|
||||
// Measured: `227`
|
||||
// Estimated: `9242 + x * (8 ±0)`
|
||||
// Minimum execution time: 8_924_000 picoseconds.
|
||||
Weight::from_parts(26_954_406, 9242)
|
||||
// Standard Error: 940
|
||||
.saturating_add(Weight::from_parts(24_147, 0).saturating_mul(x.into()))
|
||||
.saturating_add(RocksDbWeight::get().reads(6_u64))
|
||||
.saturating_add(RocksDbWeight::get().writes(4_u64))
|
||||
.saturating_add(Weight::from_parts(0, 8).saturating_mul(x.into()))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user