Skip to content

Commit

Permalink
Migrate pallet-mmr to umbrella crate (#7081)
Browse files Browse the repository at this point in the history
Part of #6504
  • Loading branch information
yrong authored Jan 20, 2025
1 parent 115ff4e commit 569ce71
Show file tree
Hide file tree
Showing 11 changed files with 137 additions and 142 deletions.
8 changes: 1 addition & 7 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 14 additions & 0 deletions prdoc/pr_7081.prdoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0
# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json

title: '[pallet-mmr] Migrate to using frame umbrella crate'

doc:
- audience: Runtime Dev
description: This PR migrates the pallet-mmr to use the frame umbrella crate. This
is part of the ongoing effort to migrate all pallets to use the frame umbrella crate.
The effort is tracked [here](https://github.com/paritytech/polkadot-sdk/issues/6504).

crates:
- name: pallet-mmr
bump: minor
24 changes: 4 additions & 20 deletions substrate/frame/merkle-mountain-range/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,43 +16,27 @@ targets = ["x86_64-unknown-linux-gnu"]

[dependencies]
codec = { workspace = true }
frame-benchmarking = { optional = true, workspace = true }
frame-support = { workspace = true }
frame-system = { workspace = true }
frame = { workspace = true, features = ["runtime"] }
log = { workspace = true }
scale-info = { features = ["derive"], workspace = true }
sp-core = { workspace = true }
sp-io = { workspace = true }
sp-mmr-primitives = { workspace = true }
sp-runtime = { workspace = true }

[dev-dependencies]
array-bytes = { workspace = true, default-features = true }
itertools = { workspace = true }
sp-tracing = { workspace = true, default-features = true }

[features]
default = ["std"]
std = [
"codec/std",
"frame-benchmarking?/std",
"frame-support/std",
"frame-system/std",
"frame/std",
"log/std",
"scale-info/std",
"sp-core/std",
"sp-io/std",
"sp-mmr-primitives/std",
"sp-runtime/std",
]
runtime-benchmarks = [
"frame-benchmarking/runtime-benchmarks",
"frame-support/runtime-benchmarks",
"frame-system/runtime-benchmarks",
"sp-runtime/runtime-benchmarks",
"frame/runtime-benchmarks",
]
try-runtime = [
"frame-support/try-runtime",
"frame-system/try-runtime",
"sp-runtime/try-runtime",
"frame/try-runtime",
]
10 changes: 6 additions & 4 deletions substrate/frame/merkle-mountain-range/src/benchmarking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@
#![cfg(feature = "runtime-benchmarks")]

use crate::*;
use frame_benchmarking::v1::benchmarks_instance_pallet;
use frame_support::traits::OnInitialize;
use frame::{
benchmarking::prelude::v1::benchmarks_instance_pallet,
deps::frame_support::traits::OnInitialize,
};

benchmarks_instance_pallet! {
on_initialize {
Expand All @@ -31,10 +33,10 @@ benchmarks_instance_pallet! {

<<T as pallet::Config::<I>>::BenchmarkHelper as BenchmarkHelper>::setup();
for leaf in 0..(leaves - 1) {
Pallet::<T, I>::on_initialize((leaf as u32).into());
<Pallet::<T, I> as OnInitialize<BlockNumberFor<T>>>::on_initialize((leaf as u32).into());
}
}: {
Pallet::<T, I>::on_initialize((leaves as u32 - 1).into());
<Pallet::<T, I> as OnInitialize<BlockNumberFor<T>>>::on_initialize((leaves as u32 - 1).into());
} verify {
assert_eq!(crate::NumberOfLeaves::<T, I>::get(), leaves);
}
Expand Down
9 changes: 3 additions & 6 deletions substrate/frame/merkle-mountain-range/src/default_weights.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,13 @@
//! Default weights for the MMR Pallet
//! This file was not auto-generated.
use frame_support::weights::{
constants::{RocksDbWeight as DbWeight, WEIGHT_REF_TIME_PER_NANOS},
Weight,
};
use frame::{deps::frame_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 = DbWeight::get().reads(1);
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.
Expand All @@ -36,6 +33,6 @@ impl crate::WeightInfo for () {
leaf_weight
.saturating_add(hash_weight)
.saturating_add(hook_weight)
.saturating_add(DbWeight::get().reads_writes(2 + peaks, 2 + peaks))
.saturating_add(RocksDbWeight::get().reads_writes(2 + peaks, 2 + peaks))
}
}
85 changes: 39 additions & 46 deletions substrate/frame/merkle-mountain-range/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,20 +59,17 @@
extern crate alloc;

use alloc::vec::Vec;
use frame_support::weights::Weight;
use frame_system::pallet_prelude::{BlockNumberFor, HeaderFor};
use log;
use sp_mmr_primitives::utils;
use sp_runtime::{
traits::{self, One, Saturating},
SaturatedConversion,
};

pub use pallet::*;
use frame::prelude::*;

pub use sp_mmr_primitives::{
self as primitives, utils::NodesUtils, Error, LeafDataProvider, LeafIndex, NodeIndex,
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;
Expand All @@ -90,11 +87,11 @@ mod tests;
/// crate-local wrapper over [frame_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: frame_system::Config> {
_phantom: core::marker::PhantomData<T>,
pub struct ParentNumberAndHash<T: Config> {
_phantom: PhantomData<T>,
}

impl<T: frame_system::Config> LeafDataProvider for ParentNumberAndHash<T> {
impl<T: Config> LeafDataProvider for ParentNumberAndHash<T> {
type LeafData = (BlockNumberFor<T>, <T as frame_system::Config>::Hash);

fn leaf_data() -> Self::LeafData {
Expand All @@ -111,13 +108,11 @@ pub trait BlockHashProvider<BlockNumber, BlockHash> {
}

/// Default implementation of BlockHashProvider using frame_system.
pub struct DefaultBlockHashProvider<T: frame_system::Config> {
pub struct DefaultBlockHashProvider<T: Config> {
_phantom: core::marker::PhantomData<T>,
}

impl<T: frame_system::Config> BlockHashProvider<BlockNumberFor<T>, T::Hash>
for DefaultBlockHashProvider<T>
{
impl<T: Config> BlockHashProvider<BlockNumberFor<T>, T::Hash> for DefaultBlockHashProvider<T> {
fn block_hash(block_number: BlockNumberFor<T>) -> T::Hash {
frame_system::Pallet::<T>::block_hash(block_number)
}
Expand All @@ -142,17 +137,16 @@ impl BenchmarkHelper for () {
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 primitives::LeafDataProvider>::LeafData;
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 traits::Hash>::Output;
pub(crate) type HashOf<T, I> = <<T as Config<I>>::Hashing as Hash>::Output;

#[frame_support::pallet]
#[frame::pallet]
pub mod pallet {
use super::*;
use frame_support::pallet_prelude::*;

#[pallet::pallet]
pub struct Pallet<T, I = ()>(PhantomData<(T, I)>);
Expand Down Expand Up @@ -180,7 +174,7 @@ pub mod pallet {
///
/// 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: traits::Hash;
type Hashing: Hash;

/// Data stored in the leaf nodes.
///
Expand All @@ -198,15 +192,15 @@ pub mod pallet {
/// 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: primitives::LeafDataProvider;
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 [`frame_system::Pallet::deposit_log`]) to make it available for Light
/// Clients. Hook complexity should be `O(1)`.
type OnNewRoot: primitives::OnNewRoot<HashOf<Self, I>>;
type OnNewRoot: OnNewRoot<HashOf<Self, I>>;

/// Block hash provider for a given block number.
type BlockHashProvider: BlockHashProvider<
Expand Down Expand Up @@ -248,9 +242,8 @@ pub mod pallet {
#[pallet::hooks]
impl<T: Config<I>, I: 'static> Hooks<BlockNumberFor<T>> for Pallet<T, I> {
fn on_initialize(_n: BlockNumberFor<T>) -> Weight {
use primitives::LeafDataProvider;
let leaves = NumberOfLeaves::<T, I>::get();
let peaks_before = sp_mmr_primitives::utils::NodesUtils::new(leaves).number_of_peaks();
let peaks_before = NodesUtils::new(leaves).number_of_peaks();
let data = T::LeafData::leaf_data();

// append new leaf to MMR
Expand All @@ -268,12 +261,12 @@ pub mod pallet {
return T::WeightInfo::on_initialize(peaks_before as u32)
},
};
<T::OnNewRoot as primitives::OnNewRoot<_>>::on_new_root(&root);
<T::OnNewRoot as OnNewRoot<_>>::on_new_root(&root);

NumberOfLeaves::<T, I>::put(leaves);
RootHash::<T, I>::put(root);

let peaks_after = sp_mmr_primitives::utils::NodesUtils::new(leaves).number_of_peaks();
let peaks_after = NodesUtils::new(leaves).number_of_peaks();

T::WeightInfo::on_initialize(peaks_before.max(peaks_after) as u32)
}
Expand All @@ -290,28 +283,28 @@ pub mod pallet {
pub fn verify_leaves_proof<H, L>(
root: H::Output,
leaves: Vec<mmr::Node<H, L>>,
proof: primitives::LeafProof<H::Output>,
) -> Result<(), primitives::Error>
proof: LeafProof<H::Output>,
) -> Result<(), Error>
where
H: traits::Hash,
L: primitives::FullLeaf,
H: Hash,
L: FullLeaf,
{
let is_valid = mmr::verify_leaves_proof::<H, L>(root, leaves, proof)?;
if is_valid {
Ok(())
} else {
Err(primitives::Error::Verify.log_debug(("The proof is incorrect.", root)))
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: primitives::AncestryProof<H::Output>,
ancestry_proof: AncestryProof<H::Output>,
) -> Result<H::Output, Error>
where
H: traits::Hash,
L: primitives::FullLeaf,
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)))
Expand Down Expand Up @@ -383,7 +376,7 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> {
pub fn generate_proof(
block_numbers: Vec<BlockNumberFor<T>>,
best_known_block_number: Option<BlockNumberFor<T>>,
) -> Result<(Vec<LeafOf<T, I>>, primitives::LeafProof<HashOf<T, I>>), primitives::Error> {
) -> 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(|| <frame_system::Pallet<T>>::block_number());
Expand All @@ -393,7 +386,7 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> {
// we need to translate the block_numbers into leaf indices.
let leaf_indices = block_numbers
.iter()
.map(|block_num| -> Result<LeafIndex, primitives::Error> {
.map(|block_num| -> Result<LeafIndex, Error> {
Self::block_num_to_leaf_index(*block_num)
})
.collect::<Result<Vec<LeafIndex>, _>>()?;
Expand All @@ -410,29 +403,30 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> {
/// or the proof is invalid.
pub fn verify_leaves(
leaves: Vec<LeafOf<T, I>>,
proof: primitives::LeafProof<HashOf<T, I>>,
) -> Result<(), primitives::Error> {
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(primitives::Error::Verify
.log_debug("The proof has incorrect number of leaves or proof items."))
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(primitives::Error::Verify.log_debug("The proof is incorrect."))
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<primitives::AncestryProof<HashOf<T, I>>, Error> {
) -> 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(|| <frame_system::Pallet<T>>::block_number());
Expand All @@ -445,16 +439,15 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> {
}

#[cfg(feature = "runtime-benchmarks")]
pub fn generate_mock_ancestry_proof() -> Result<primitives::AncestryProof<HashOf<T, I>>, Error>
{
pub fn generate_mock_ancestry_proof() -> Result<AncestryProof<HashOf<T, I>>, Error> {
let leaf_count = Self::block_num_to_leaf_count(<frame_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 verify_ancestry_proof(
root: HashOf<T, I>,
ancestry_proof: primitives::AncestryProof<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)
}
Expand Down
Loading

0 comments on commit 569ce71

Please sign in to comment.