diff --git a/substrate/frame/merkle-mountain-range/src/lib.rs b/substrate/frame/merkle-mountain-range/src/lib.rs index 7d02b66affaf..a86443f2e011 100644 --- a/substrate/frame/merkle-mountain-range/src/lib.rs +++ b/substrate/frame/merkle-mountain-range/src/lib.rs @@ -362,11 +362,6 @@ impl, I: 'static> Pallet { mmr.generate_proof(leaf_indices) } - /// Return the on-chain MMR root hash. - pub fn mmr_root() -> HashOf { - RootHash::::get() - } - /// Verify MMR proof for given `leaves`. /// /// This method is safe to use within the runtime code. @@ -393,4 +388,37 @@ impl, I: 'static> Pallet { Err(primitives::Error::Verify.log_debug("The proof is incorrect.")) } } + + pub fn generate_ancestry_proof( + prev_block_number: BlockNumberFor, + best_known_block_number: Option>, + ) -> Result>, 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(|| >::block_number()); + + let leaf_count = Self::block_num_to_leaf_index(best_known_block_number)?.saturating_add(1); + let prev_leaf_count = Self::block_num_to_leaf_index(prev_block_number)?.saturating_add(1); + + let mmr: ModuleMmr = mmr::Mmr::new(leaf_count); + mmr.generate_ancestry_proof(prev_leaf_count) + } + + pub fn verify_ancestry_proof( + ancestry_proof: primitives::AncestryProof>, + ) -> Result<(), Error> { + let mmr: ModuleMmr = + mmr::Mmr::new(ancestry_proof.leaf_count); + let is_valid = mmr.verify_ancestry_proof(ancestry_proof)?; + if is_valid { + Ok(()) + } else { + Err(Error::Verify.log_debug("The ancestry proof is incorrect.")) + } + } + + /// Return the on-chain MMR root hash. + pub fn mmr_root() -> HashOf { + RootHash::::get() + } } diff --git a/substrate/frame/merkle-mountain-range/src/mmr/mmr.rs b/substrate/frame/merkle-mountain-range/src/mmr/mmr.rs index 7fc50a943f91..5efc172d1e93 100644 --- a/substrate/frame/merkle-mountain-range/src/mmr/mmr.rs +++ b/substrate/frame/merkle-mountain-range/src/mmr/mmr.rs @@ -23,7 +23,7 @@ use crate::{ primitives::{self, Error, NodeIndex}, Config, HashOf, HashingOf, }; -use sp_mmr_primitives::{mmr_lib, mmr_lib::MMRStoreReadOps, utils::NodesUtils}; +use sp_mmr_primitives::{mmr_lib, mmr_lib::MMRStoreReadOps, utils::NodesUtils, LeafIndex}; use sp_std::prelude::*; /// Stateless verification of the proof for a batch of leaves. @@ -119,6 +119,44 @@ where .map_err(|e| Error::Verify.log_debug(e)) } + pub fn verify_ancestry_proof( + &self, + ancestry_proof: primitives::AncestryProof>, + ) -> Result { + let prev_peaks_proof = + mmr_lib::NodeMerkleProof::, Hasher, L>>::new( + self.mmr.mmr_size(), + ancestry_proof + .items + .into_iter() + .map(|(index, hash)| (index, Node::Hash(hash))) + .collect(), + ); + + let raw_ancestry_proof = mmr_lib::AncestryProof::< + NodeOf, + Hasher, L>, + > { + prev_peaks: ancestry_proof + .prev_peaks + .into_iter() + .map(|hash| Node::Hash(hash)) + .collect(), + prev_size: mmr_lib::helper::leaf_index_to_mmr_size(ancestry_proof.prev_leaf_count - 1), + proof: prev_peaks_proof, + }; + + let prev_root = mmr_lib::ancestry_proof::bagging_peaks_hashes::< + NodeOf, + Hasher, L>, + >(raw_ancestry_proof.prev_peaks.clone()) + .map_err(|e| Error::Verify.log_debug(e))?; + let root = self.mmr.get_root().map_err(|e| Error::GetRoot.log_error(e))?; + raw_ancestry_proof + .verify_ancestor(root, prev_root) + .map_err(|e| Error::Verify.log_debug(e)) + } + /// Return the internal size of the MMR (number of nodes). #[cfg(test)] pub fn size(&self) -> NodeIndex { @@ -193,4 +231,27 @@ where }) .map(|p| (leaves, p)) } + + pub fn generate_ancestry_proof( + &self, + prev_leaf_count: LeafIndex, + ) -> Result>, 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(primitives::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 + .proof + .proof_items() + .iter() + .map(|(index, item)| (*index, item.hash())) + .collect(), + }) + } } diff --git a/substrate/frame/merkle-mountain-range/src/tests.rs b/substrate/frame/merkle-mountain-range/src/tests.rs index e6c4086c71ea..f8cfcb4e2c28 100644 --- a/substrate/frame/merkle-mountain-range/src/tests.rs +++ b/substrate/frame/merkle-mountain-range/src/tests.rs @@ -516,43 +516,40 @@ fn should_verify() { }); } -#[test] -fn should_verify_batch_proofs() { - fn generate_and_verify_batch_proof( - ext: &mut sp_io::TestExternalities, - block_numbers: &Vec, - blocks_to_add: usize, - ) { - let (leaves, proof) = ext.execute_with(|| { - crate::Pallet::::generate_proof(block_numbers.to_vec(), None).unwrap() - }); +fn generate_and_verify_batch_proof( + ext: &mut sp_io::TestExternalities, + block_numbers: &Vec, + blocks_to_add: usize, +) { + let (leaves, proof) = ext.execute_with(|| { + crate::Pallet::::generate_proof(block_numbers.to_vec(), None).unwrap() + }); - let max_block_number = ext.execute_with(|| frame_system::Pallet::::block_number()); - let min_block_number = block_numbers.iter().max().unwrap(); + let max_block_number = ext.execute_with(|| frame_system::Pallet::::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::::generate_proof(block_numbers.to_vec(), Some(best_block)) - .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::::generate_proof(block_numbers.to_vec(), Some(best_block)) + .unwrap() }) - .collect::>(); - - ext.execute_with(|| { - add_blocks(blocks_to_add); - // then - assert_eq!(crate::Pallet::::verify_leaves(leaves, proof), Ok(())); - historical_proofs.iter().for_each(|(leaves, proof)| { - assert_eq!( - crate::Pallet::::verify_leaves(leaves.clone(), proof.clone()), - Ok(()) - ); - }); }) - } + .collect::>(); + + ext.execute_with(|| { + add_blocks(blocks_to_add); + // then + assert_eq!(crate::Pallet::::verify_leaves(leaves, proof), Ok(())); + historical_proofs.iter().for_each(|(leaves, proof)| { + assert_eq!(crate::Pallet::::verify_leaves(leaves.clone(), proof.clone()), Ok(())); + }); + }) +} +#[test] +fn should_verify_batch_proofs() { let _ = env_logger::try_init(); use itertools::Itertools; @@ -790,3 +787,24 @@ fn does_not_panic_when_generating_historical_proofs() { ); }); } + +#[test] +fn generating_and_verifying_ancestry_proofs_works_correctly() { + let _ = env_logger::try_init(); + let mut ext = new_test_ext(); + ext.execute_with(|| add_blocks(500)); + ext.persist_offchain_overlay(); + register_offchain_ext(&mut ext); + + ext.execute_with(|| { + // Check that generating and verifying ancestry proofs works correctly + // for each previous block + for prev_block_number in 1..501 { + let proof = Pallet::::generate_ancestry_proof(prev_block_number, None).unwrap(); + Pallet::::verify_ancestry_proof(proof).unwrap(); + } + + // Check that we can't generate ancestry proofs for a future block. + assert_eq!(Pallet::::generate_ancestry_proof(501, None), Err(Error::GenerateProof)); + }); +} diff --git a/substrate/primitives/merkle-mountain-range/src/lib.rs b/substrate/primitives/merkle-mountain-range/src/lib.rs index 5d98bcd66ef9..3740047e0278 100644 --- a/substrate/primitives/merkle-mountain-range/src/lib.rs +++ b/substrate/primitives/merkle-mountain-range/src/lib.rs @@ -357,10 +357,24 @@ pub struct LeafProof { pub leaf_indices: Vec, /// Number of leaves in MMR, when the proof was generated. pub leaf_count: NodeIndex, - /// Proof elements (hashes of siblings of inner nodes on the path to the leaf). + /// Proof elements (hashes of siblings of inner nodes on the path to the leafs). pub items: Vec, } +/// An MMR ancestry proof for a prior mmr root. +#[derive(codec::Encode, codec::Decode, RuntimeDebug, Clone, PartialEq, Eq, TypeInfo)] +pub struct AncestryProof { + /// Peaks of the ancestor's mmr + pub prev_peaks: Vec, + /// Number of leaves in the ancestor's MMR. + pub prev_leaf_count: u64, + /// Number of leaves in MMR, when the proof was generated. + pub leaf_count: NodeIndex, + /// Proof elements + /// (positions and hashes of siblings of inner nodes on the path to the previous peaks). + pub items: Vec<(u64, Hash)>, +} + /// Merkle Mountain Range operation error. #[cfg_attr(feature = "std", derive(thiserror::Error))] #[derive(RuntimeDebug, codec::Encode, codec::Decode, PartialEq, Eq, TypeInfo)]