Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

pallet-mmr: move offchain logic to client-side gadget #12753

Merged
merged 15 commits into from
Nov 29, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions bin/node/runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2064,6 +2064,10 @@ impl_runtime_apis! {
Ok(Mmr::mmr_root())
}

fn num_mmr_blocks() -> Result<BlockNumber, mmr::Error> {
Mmr::num_mmr_blocks()
}

fn generate_proof(
block_numbers: Vec<BlockNumber>,
best_known_block_number: Option<BlockNumber>,
Expand Down
2 changes: 1 addition & 1 deletion frame/merkle-mountain-range/rpc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ fn mmr_error_into_rpc_error(err: MmrError) -> CallError {
MmrError::LeafNotFound => 1,
MmrError::GenerateProof => 2,
MmrError::Verify => 3,
MmrError::BlockNumToLeafIndex => 4,
MmrError::InvalidNumericOp => 4,
MmrError::InvalidBestKnownBlock => 5,
_ => 0,
};
Expand Down
38 changes: 17 additions & 21 deletions frame/merkle-mountain-range/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@

use frame_support::{log, traits::Get, weights::Weight};
use sp_runtime::{
traits::{self, CheckedSub, One, Saturating},
traits::{self, One, Saturating},
SaturatedConversion,
};

Expand All @@ -72,6 +72,7 @@ mod mock;
mod tests;

pub use pallet::*;
use sp_mmr_primitives::utils;
pub use sp_mmr_primitives::{
self as primitives, utils::NodesUtils, Error, LeafDataProvider, LeafIndex, NodeIndex,
};
Expand Down Expand Up @@ -360,30 +361,25 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> {
.saturating_add(leaf_index.saturated_into())
}

/// Get the number of MMR blocks in the chain.
pub fn num_mmr_blocks() -> Result<T::BlockNumber, Error> {
Self::mmr_leaves().try_into().map_err(|_| {
Error::InvalidNumericOp
.log_debug("The number of leaves couldn't be converted to a block number.")
})
}
acatangiu marked this conversation as resolved.
Show resolved Hide resolved

/// Convert a block number into a leaf index.
fn block_num_to_leaf_index(block_num: T::BlockNumber) -> Result<LeafIndex, primitives::Error>
fn block_num_to_leaf_index(block_num: T::BlockNumber) -> Result<LeafIndex, Error>
where
T: frame_system::Config,
{
// leaf_idx = (leaves_count - 1) - (current_block_num - block_num);
let best_block_num = <frame_system::Pallet<T>>::block_number();
let blocks_diff = best_block_num.checked_sub(&block_num).ok_or_else(|| {
primitives::Error::BlockNumToLeafIndex
.log_debug("The provided block_number is greater than the best block number.")
})?;
let blocks_diff_as_leaf_idx = blocks_diff.try_into().map_err(|_| {
primitives::Error::BlockNumToLeafIndex
.log_debug("The `blocks_diff` couldn't be converted to `LeafIndex`.")
})?;

let leaf_idx = Self::mmr_leaves()
.checked_sub(1)
.and_then(|last_leaf_idx| last_leaf_idx.checked_sub(blocks_diff_as_leaf_idx))
.ok_or_else(|| {
primitives::Error::BlockNumToLeafIndex
.log_debug("There aren't enough leaves in the chain.")
})?;
Ok(leaf_idx)
let first_mmr_block = utils::first_mmr_block_num::<T::Header>(
<frame_system::Pallet<T>>::block_number(),
Self::num_mmr_blocks()?,
)?;

utils::block_num_to_leaf_index::<T::Header>(block_num, first_mmr_block)
}

/// Generate an MMR proof for the given `block_numbers`.
Expand Down
9 changes: 3 additions & 6 deletions frame/merkle-mountain-range/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -963,21 +963,18 @@ fn does_not_panic_when_generating_historical_proofs() {
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::BlockNumToLeafIndex),
);
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::BlockNumToLeafIndex),
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::BlockNumToLeafIndex),
Err(Error::LeafNotFound),
);
});
}
7 changes: 5 additions & 2 deletions primitives/merkle-mountain-range/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -361,8 +361,8 @@ pub struct Proof<Hash> {
#[derive(RuntimeDebug, codec::Encode, codec::Decode, PartialEq, Eq)]
pub enum Error {
/// Error during translation of a block number into a leaf index.
#[cfg_attr(feature = "std", error("Error translation block number into leaf index"))]
BlockNumToLeafIndex,
#[cfg_attr(feature = "std", error("Error performing numeric op"))]
InvalidNumericOp,
/// Error while pushing new node.
#[cfg_attr(feature = "std", error("Error pushing new node"))]
Push,
Expand Down Expand Up @@ -423,6 +423,9 @@ sp_api::decl_runtime_apis! {
/// Return the on-chain MMR root hash.
fn mmr_root() -> Result<Hash, Error>;

/// Return the number of MMR blocks in the chain.
fn num_mmr_blocks() -> Result<BlockNumber, Error>;

/// Generate MMR proof for a series of block numbers. If `best_known_block_number = Some(n)`,
/// use historical MMR state at given block height `n`. Else, use current MMR state.
fn generate_proof(
Expand Down
33 changes: 31 additions & 2 deletions primitives/merkle-mountain-range/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,40 @@
use codec::Encode;
use mmr_lib::helper;

use sp_runtime::traits::Header;
use sp_runtime::traits::{CheckedAdd, CheckedSub, Header, One};
#[cfg(not(feature = "std"))]
use sp_std::prelude::Vec;

use crate::{LeafIndex, NodeIndex};
use crate::{Error, LeafIndex, NodeIndex};

/// Get the first block with MMR.
pub fn first_mmr_block_num<H: Header>(
best_block_num: H::Number,
num_mmr_blocks: H::Number,
) -> Result<H::Number, Error> {
best_block_num
.checked_sub(&num_mmr_blocks)
.and_then(|last_non_mmr_block| last_non_mmr_block.checked_add(&One::one()))
.ok_or_else(|| {
Error::InvalidNumericOp
.log_debug("The best block should be greater than the number of mmr blocks.")
})
}

/// Convert a block number into a leaf index.
pub fn block_num_to_leaf_index<H: Header>(
block_num: H::Number,
first_mmr_block_num: H::Number,
) -> Result<LeafIndex, Error> {
let leaf_idx = block_num.checked_sub(&first_mmr_block_num).ok_or_else(|| {
Error::InvalidNumericOp
.log_debug("The provided block should be greater than the first mmr block.")
})?;

leaf_idx.try_into().map_err(|_| {
Error::InvalidNumericOp.log_debug("Couldn't convert the leaf index to `LeafIndex`.")
})
}

/// MMR nodes & size -related utilities.
pub struct NodesUtils {
Expand Down