From 337b60efc6e7760e8bd19654a226453899bafe16 Mon Sep 17 00:00:00 2001 From: david Date: Tue, 11 Jan 2022 14:03:23 +0100 Subject: [PATCH 01/24] updated mmr rpc api with functions for batch generation of proof --- bin/node/runtime/src/lib.rs | 26 ++++++ frame/merkle-mountain-range/rpc/src/lib.rs | 64 +++++++++++++++ frame/merkle-mountain-range/src/lib.rs | 65 +++++++++++++++ frame/merkle-mountain-range/src/mmr/mmr.rs | 87 ++++++++++++++++++++- primitives/merkle-mountain-range/src/lib.rs | 30 +++++++ 5 files changed, 271 insertions(+), 1 deletion(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index f073482d887bd..2fff661e85b92 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -1823,6 +1823,32 @@ impl_runtime_apis! { fn mmr_root() -> Result { Ok(Mmr::mmr_root()) } + + fn generate_batch_proof(leaf_indices: Vec) + -> Result<(Vec<(mmr::EncodableOpaqueLeaf, pallet_mmr::primitives::LeafIndex)>, mmr::BatchProof), mmr::Error> + { + Mmr::generate_batch_proof(leaf_indices) + .map(|(leaves, proof)| (leaves.into_iter().map(|(pos, leaf)| (mmr::EncodableOpaqueLeaf::from_leaf(&leaf), pos)).collect(), proof)) + } + + fn verify_batch_proof(leaves: Vec, proof: mmr::BatchProof) + -> Result<(), mmr::Error> + { + let leaf = leaves.into_iter().map(|leaf| + leaf.into_opaque_leaf() + .try_decode() + .ok_or(mmr::Error::Verify)).collect::>, mmr::Error>()?; + Mmr::verify_leaves(leaf, proof) + } + + fn verify_batch_proof_stateless( + root: mmr::Hash, + leaves: Vec, + proof: mmr::BatchProof + ) -> Result<(), mmr::Error> { + let nodes = leaves.into_iter().map(|leaf|mmr::DataOrHash::Data(leaf.into_opaque_leaf())).collect(); + pallet_mmr::verify_leaves_proof::(root, nodes, proof) + } } impl sp_session::SessionKeys for Runtime { diff --git a/frame/merkle-mountain-range/rpc/src/lib.rs b/frame/merkle-mountain-range/rpc/src/lib.rs index 99359bfea8eb6..111de06b323ac 100644 --- a/frame/merkle-mountain-range/rpc/src/lib.rs +++ b/frame/merkle-mountain-range/rpc/src/lib.rs @@ -57,6 +57,33 @@ impl LeafProof { } } +/// Retrieved MMR leaf and its proof. +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] +pub struct LeafBatchProof { + /// Block hash the proof was generated for. + pub block_hash: BlockHash, + /// SCALE-encoded vector of leaf index and leaf data `(LeafData, LeafIndex)`. + pub leaves: Bytes, + /// SCALE-encoded proof data. See [pallet_mmr_primitives::BatchProof]. + pub proof: Bytes, +} + +impl LeafBatchProof { + /// Create new `LeafBatchProof` from a given vector of concrete `leaf` and `proof`. + pub fn new( + block_hash: BlockHash, + leaves: Vec<(Leaf, LeafIndex)>, + proof: BatchProof, + ) -> Self + where + Leaf: Encode, + MmrHash: Encode, + { + Self { block_hash, leaves: Bytes(leaves.encode()), proof: Bytes(proof.encode()) } + } +} + /// MMR RPC methods. #[rpc] pub trait MmrApi { @@ -74,6 +101,21 @@ pub trait MmrApi { leaf_index: LeafIndex, at: Option, ) -> Result>; + + /// Generate MMR proof for the given leaf indices. + /// + /// This method calls into a runtime with MMR pallet included and attempts to generate + /// MMR proof for a set of leaves at the given `leaf_indices`. + /// Optionally, a block hash at which the runtime should be queried can be specified. + /// + /// Returns the leaves and a proof for these leaves (compact encoding, i.e. hash of + /// the leaves). Both parameters are SCALE-encoded. + #[rpc(name = "mmr_generateBatchProof")] + fn generate_batch_proof( + &self, + leaf_indices: Vec, + at: Option, + ) -> Result>; } /// An implementation of MMR specific RPC methods. @@ -117,6 +159,28 @@ where Ok(LeafProof::new(block_hash, leaf, proof)) } + + fn generate_batch_proof( + &self, + leaf_indices: Vec, + at: Option<::Hash>, + ) -> Result::Hash>> { + let api = self.client.runtime_api(); + let block_hash = at.unwrap_or_else(|| + // If the block hash is not supplied assume the best block. + self.client.info().best_hash); + + let (leaves, proof) = api + .generate_batch_proof_with_context( + &BlockId::hash(block_hash), + sp_core::ExecutionContext::OffchainCall(None), + leaf_indices, + ) + .map_err(runtime_error_into_rpc_error)? + .map_err(mmr_error_into_rpc_error)?; + + Ok(LeafBatchProof::new(block_hash, leaves, proof)) + } } const RUNTIME_ERROR: i64 = 8000; diff --git a/frame/merkle-mountain-range/src/lib.rs b/frame/merkle-mountain-range/src/lib.rs index 855eb0a7436dc..7795219c9e362 100644 --- a/frame/merkle-mountain-range/src/lib.rs +++ b/frame/merkle-mountain-range/src/lib.rs @@ -251,6 +251,29 @@ where } } +/// Stateless MMR proof verification for batch of leaves. +/// +/// This function can be used to verify received MMR proof (`proof`) +/// for given leaf data (`leaf`) against a known MMR root hash (`root`). +/// +/// The verification does not require any storage access. +pub fn verify_leaves_proof( + root: H::Output, + leaves: Vec>, + proof: primitives::BatchProof, +) -> Result<(), primitives::Error> +where + H: traits::Hash, + L: primitives::FullLeaf, +{ + let is_valid = mmr::verify_leaves_proof::(root, leaves, proof)?; + if is_valid { + Ok(()) + } else { + Err(primitives::Error::Verify.log_debug(("The proof is incorrect.", root))) + } +} + impl, I: 'static> Pallet { fn offchain_key(pos: NodeIndex) -> sp_std::prelude::Vec { (T::INDEXING_PREFIX, pos).encode() @@ -269,6 +292,22 @@ impl, I: 'static> Pallet { mmr.generate_proof(leaf_index) } + /// Generate a MMR proof for the given `leaf_indices`. + /// + /// 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_batch_proof( + leaf_indices: Vec, + ) -> Result< + (Vec<(LeafOf, NodeIndex)>, primitives::BatchProof<>::Hash>), + primitives::Error, + > { + let mmr: ModuleMmr = mmr::Mmr::new(Self::mmr_leaves()); + mmr.generate_batch_proof(leaf_indices) + } + /// Verify MMR proof for given `leaf`. /// /// This method is safe to use within the runtime code. @@ -300,4 +339,30 @@ impl, I: 'static> Pallet { pub fn mmr_root() -> >::Hash { Self::mmr_root_hash() } + /// 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>, + proof: primitives::BatchProof<>::Hash>, + ) -> Result<(), primitives::Error> { + if proof.leaf_count > Self::mmr_leaves() || + proof.leaf_count == 0 || + proof.items.len() as u32 > mmr::utils::NodesUtils::new(proof.leaf_count).depth() + { + return Err(primitives::Error::Verify + .log_debug("The proof has incorrect number of leaves or proof items.")) + } + + let mmr: ModuleMmr = 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.")) + } + } } diff --git a/frame/merkle-mountain-range/src/mmr/mmr.rs b/frame/merkle-mountain-range/src/mmr/mmr.rs index a1516ee8607f4..fdddf248e408d 100644 --- a/frame/merkle-mountain-range/src/mmr/mmr.rs +++ b/frame/merkle-mountain-range/src/mmr/mmr.rs @@ -25,7 +25,7 @@ use crate::{ Config, HashingOf, }; #[cfg(not(feature = "std"))] -use sp_std::vec; +use sp_std::{prelude::Vec, vec}; /// Stateless verification of the leaf proof. pub fn verify_leaf_proof( @@ -48,6 +48,33 @@ where .map_err(|e| Error::Verify.log_debug(e)) } +/// Stateless verification of the proof for a batch of leaves. +pub fn verify_leaves_proof( + root: H::Output, + leaves: Vec>, + proof: primitives::BatchProof, +) -> Result +where + H: sp_runtime::traits::Hash, + L: primitives::FullLeaf, +{ + let size = NodesUtils::new(proof.leaf_count).size(); + + let leaves_and_position = proof + .leaf_indices + .into_iter() + .map(|index| mmr_lib::leaf_index_to_pos(index)) + .zip(leaves.into_iter()) + .collect(); + + let p = mmr_lib::MerkleProof::, Hasher>::new( + size, + proof.items.into_iter().map(Node::Hash).collect(), + ); + p.verify(Node::Hash(root), leaves_and_position) + .map_err(|e| Error::Verify.log_debug(e)) +} + /// A wrapper around a MMR library to expose limited functionality. /// /// Available functions depend on the storage kind ([Runtime](crate::mmr::storage::RuntimeStorage) @@ -92,6 +119,28 @@ where .map_err(|e| Error::Verify.log_debug(e)) } + /// Verify proof of a single leaf. + /// Note, the leaves should be sorted such that the order of leaves is equivalent to the order + /// of leaf indices in the proof + pub fn verify_leaves_proof( + &self, + leaves: Vec, + proof: primitives::BatchProof<>::Hash>, + ) -> Result { + let p = mmr_lib::MerkleProof::, Hasher, L>>::new( + self.mmr.mmr_size(), + proof.items.into_iter().map(Node::Hash).collect(), + ); + let leaves_and_position = 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_and_position).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 { @@ -159,4 +208,40 @@ where }) .map(|p| (leaf, p)) } + + /// 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_batch_proof( + &self, + leaf_indices: Vec, + ) -> Result<(Vec<(L, NodeIndex)>, primitives::BatchProof<>::Hash>), Error> { + let positions = leaf_indices + .iter() + .map(|index| mmr_lib::leaf_index_to_pos(*index)) + .collect::>(); + let store = >::default(); + let leaves = positions + .iter() + .map(|pos| match mmr_lib::MMRStore::get_elem(&store, *pos) { + Ok(Some(Node::Data(leaf))) => Ok(leaf), + e => Err(Error::LeafNotFound.log_debug(e)), + }) + .collect::, Error>>()? + .into_iter() + .zip(leaf_indices.iter().cloned()) + .collect::>(); + + let leaf_count = self.leaves; + self.mmr + .gen_proof(positions) + .map_err(|e| Error::GenerateProof.log_error(e)) + .map(|p| primitives::BatchProof { + leaf_indices, + leaf_count, + items: p.proof_items().iter().map(|x| x.hash()).collect(), + }) + .map(|p| (leaves, p)) + } } diff --git a/primitives/merkle-mountain-range/src/lib.rs b/primitives/merkle-mountain-range/src/lib.rs index a27536b8d35b7..bc2bd2af651fb 100644 --- a/primitives/merkle-mountain-range/src/lib.rs +++ b/primitives/merkle-mountain-range/src/lib.rs @@ -351,6 +351,17 @@ impl_leaf_data_for_tuple!(A:0, B:1, C:2); impl_leaf_data_for_tuple!(A:0, B:1, C:2, D:3); impl_leaf_data_for_tuple!(A:0, B:1, C:2, D:3, E:4); +/// A MMR proof data for a group of leaves. +#[derive(codec::Encode, codec::Decode, RuntimeDebug, Clone, PartialEq, Eq)] +pub struct BatchProof { + /// The indices of the leaves the proof is for. + 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). + pub items: Vec, +} + /// Merkle Mountain Range operation error. #[derive(RuntimeDebug, codec::Encode, codec::Decode, PartialEq, Eq)] pub enum Error { @@ -417,6 +428,25 @@ sp_api::decl_runtime_apis! { /// Return the on-chain MMR root hash. fn mmr_root() -> Result; + /// Generate MMR proof for a series of leaves under given indices. + fn generate_batch_proof(leaf_indices: Vec) -> Result<(Vec<(EncodableOpaqueLeaf, LeafIndex)>, BatchProof), Error>; + + /// Verify MMR proof against on-chain MMR for a batch of leaves. + /// + /// Note this function will use on-chain MMR root hash and check if the proof + /// matches the hash. + /// Leaves must be sorted in same order as the leaf indices in the proof + fn verify_batch_proof(leaves: Vec, proof: BatchProof) -> Result<(), Error>; + + /// Verify MMR proof against given root hash or a batch of leaves. + /// + /// Note this function does not require any on-chain storage - the + /// proof is verified against given MMR root hash. + /// + /// The leaf data is expected to be encoded in it's compact form. + /// Leaves must be sorted in same order as the leaf indices in the proof + fn verify_batch_proof_stateless(root: Hash, leaves: Vec, proof: BatchProof) + -> Result<(), Error>; } } From a01ac915aa843e7739b149817f9ec5ecd85abb9d Mon Sep 17 00:00:00 2001 From: david Date: Tue, 11 Jan 2022 14:19:25 +0100 Subject: [PATCH 02/24] update code comments --- frame/merkle-mountain-range/src/mmr/mmr.rs | 7 +++++-- frame/merkle-mountain-range/src/mmr/mod.rs | 2 +- primitives/merkle-mountain-range/src/lib.rs | 7 ++++--- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/frame/merkle-mountain-range/src/mmr/mmr.rs b/frame/merkle-mountain-range/src/mmr/mmr.rs index fdddf248e408d..6fc1c2a033c20 100644 --- a/frame/merkle-mountain-range/src/mmr/mmr.rs +++ b/frame/merkle-mountain-range/src/mmr/mmr.rs @@ -49,6 +49,8 @@ where } /// Stateless verification of the proof for a batch of leaves. +/// Note, the leaves should be sorted such that each corresponding leafs and leaf indices have the +/// same position in both the `leaves` vector and the `leaf_indices` vector contained in the proof pub fn verify_leaves_proof( root: H::Output, leaves: Vec>, @@ -120,8 +122,9 @@ where } /// Verify proof of a single leaf. - /// Note, the leaves should be sorted such that the order of leaves is equivalent to the order - /// of leaf indices in the proof + /// Note, the leaves should be sorted such that each corresponding leafs and leaf indices have + /// the same position in both the `leaves` vector and the `leaf_indices` vector contained in the + /// proof pub fn verify_leaves_proof( &self, leaves: Vec, diff --git a/frame/merkle-mountain-range/src/mmr/mod.rs b/frame/merkle-mountain-range/src/mmr/mod.rs index 1cb4e8535b991..931a121882f38 100644 --- a/frame/merkle-mountain-range/src/mmr/mod.rs +++ b/frame/merkle-mountain-range/src/mmr/mod.rs @@ -22,7 +22,7 @@ pub mod utils; use sp_mmr_primitives::{DataOrHash, FullLeaf}; use sp_runtime::traits; -pub use self::mmr::{verify_leaf_proof, Mmr}; +pub use self::mmr::{verify_leaf_proof, verify_leaves_proof, Mmr}; /// Node type for runtime `T`. pub type NodeOf = Node<>::Hashing, L>; diff --git a/primitives/merkle-mountain-range/src/lib.rs b/primitives/merkle-mountain-range/src/lib.rs index bc2bd2af651fb..2730d32efdd06 100644 --- a/primitives/merkle-mountain-range/src/lib.rs +++ b/primitives/merkle-mountain-range/src/lib.rs @@ -435,7 +435,8 @@ sp_api::decl_runtime_apis! { /// /// Note this function will use on-chain MMR root hash and check if the proof /// matches the hash. - /// Leaves must be sorted in same order as the leaf indices in the proof + /// Note, the leaves should be sorted such that corresponding leaf and leaf indices have the same position + /// in both the `leaves` vector and the `leaf_indices` vector contained in the proof fn verify_batch_proof(leaves: Vec, proof: BatchProof) -> Result<(), Error>; /// Verify MMR proof against given root hash or a batch of leaves. @@ -443,8 +444,8 @@ sp_api::decl_runtime_apis! { /// Note this function does not require any on-chain storage - the /// proof is verified against given MMR root hash. /// - /// The leaf data is expected to be encoded in it's compact form. - /// Leaves must be sorted in same order as the leaf indices in the proof + /// Note, the leaves should be sorted such that each corresponding leafs and leaf indices have the same position + /// in both the `leaves` vector and the `leaf_indices` vector contained in the proof fn verify_batch_proof_stateless(root: Hash, leaves: Vec, proof: BatchProof) -> Result<(), Error>; } From 23083c00a10d0e3fcd5113a286582e5fe9846799 Mon Sep 17 00:00:00 2001 From: david Date: Tue, 11 Jan 2022 14:31:57 +0100 Subject: [PATCH 03/24] fix build errors --- bin/node/runtime/src/lib.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 2fff661e85b92..516fbc69b9987 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -1828,17 +1828,17 @@ impl_runtime_apis! { -> Result<(Vec<(mmr::EncodableOpaqueLeaf, pallet_mmr::primitives::LeafIndex)>, mmr::BatchProof), mmr::Error> { Mmr::generate_batch_proof(leaf_indices) - .map(|(leaves, proof)| (leaves.into_iter().map(|(pos, leaf)| (mmr::EncodableOpaqueLeaf::from_leaf(&leaf), pos)).collect(), proof)) + .map(|(leaves, proof)| (leaves.into_iter().map(|(leaf, pos)| (mmr::EncodableOpaqueLeaf::from_leaf(&leaf), pos)).collect(), proof)) } fn verify_batch_proof(leaves: Vec, proof: mmr::BatchProof) -> Result<(), mmr::Error> { - let leaf = leaves.into_iter().map(|leaf| + let leaves = leaves.into_iter().map(|leaf| leaf.into_opaque_leaf() .try_decode() - .ok_or(mmr::Error::Verify)).collect::>, mmr::Error>()?; - Mmr::verify_leaves(leaf, proof) + .ok_or(mmr::Error::Verify)).collect::, mmr::Error>>()?; + Mmr::verify_leaves(leaves, proof) } fn verify_batch_proof_stateless( From 9140db9a644884e9fdbf9926e18a6a98f85ee41b Mon Sep 17 00:00:00 2001 From: david Date: Tue, 11 Jan 2022 14:56:10 +0100 Subject: [PATCH 04/24] added tests to mmr-rpc --- frame/merkle-mountain-range/rpc/src/lib.rs | 48 ++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/frame/merkle-mountain-range/rpc/src/lib.rs b/frame/merkle-mountain-range/rpc/src/lib.rs index 111de06b323ac..bc58a2489873f 100644 --- a/frame/merkle-mountain-range/rpc/src/lib.rs +++ b/frame/merkle-mountain-range/rpc/src/lib.rs @@ -243,6 +243,27 @@ mod tests { ); } + #[test] + fn should_serialize_leaf_batch_proof() { + // given + let leaf = vec![1_u8, 2, 3, 4]; + let proof = BatchProof { + leaf_indices: vec![1], + leaf_count: 9, + items: vec![H256::repeat_byte(1), H256::repeat_byte(2)], + }; + + let leaf_proof = LeafBatchProof::new(H256::repeat_byte(0), vec![(leaf, 1)], proof); + + // when + let actual = serde_json::to_string(&leaf_proof).unwrap(); + // then + assert_eq!( + actual, + r#"{"blockHash":"0x0000000000000000000000000000000000000000000000000000000000000000","leaves":"0x0410010203040100000000000000","proof":"0x04010000000000000009000000000000000801010101010101010101010101010101010101010101010101010101010101010202020202020202020202020202020202020202020202020202020202020202"}"# + ); + } + #[test] fn should_deserialize_leaf_proof() { // given @@ -269,4 +290,31 @@ mod tests { // then assert_eq!(actual, expected); } + + #[test] + fn should_deserialize_leaf_batch_proof() { + // given + let expected = LeafBatchProof { + block_hash: H256::repeat_byte(0), + leaves: Bytes(vec![(vec![1_u8, 2, 3, 4], 1)].encode()), + proof: Bytes( + BatchProof { + leaf_indices: vec![1], + leaf_count: 9, + items: vec![H256::repeat_byte(1), H256::repeat_byte(2)], + } + .encode(), + ), + }; + + // when + let actual: LeafBatchProof = serde_json::from_str(r#"{ + "blockHash":"0x0000000000000000000000000000000000000000000000000000000000000000", + "leaves":"0x04100102030401000000", + "proof":"0x04010000000000000009000000000000000801010101010101010101010101010101010101010101010101010101010101010202020202020202020202020202020202020202020202020202020202020202" + }"#).unwrap(); + + // then + assert_eq!(actual, expected); + } } From 55469f0a1bf0b64952a7f3978352cc957b5bbe7d Mon Sep 17 00:00:00 2001 From: david Date: Tue, 11 Jan 2022 15:44:43 +0100 Subject: [PATCH 05/24] add tests to pallet-mmr --- frame/merkle-mountain-range/src/tests.rs | 97 +++++++++++++++++++++++- 1 file changed, 96 insertions(+), 1 deletion(-) diff --git a/frame/merkle-mountain-range/src/tests.rs b/frame/merkle-mountain-range/src/tests.rs index 70d1395aa94d5..771afb1689ab6 100644 --- a/frame/merkle-mountain-range/src/tests.rs +++ b/frame/merkle-mountain-range/src/tests.rs @@ -23,7 +23,7 @@ use sp_core::{ offchain::{testing::TestOffchainExt, OffchainDbExt, OffchainWorkerExt}, H256, }; -use sp_mmr_primitives::{Compact, Proof}; +use sp_mmr_primitives::{Compact, Proof, BatchProof}; pub(crate) fn new_test_ext() -> sp_io::TestExternalities { frame_system::GenesisConfig::default().build_storage::().unwrap().into() @@ -276,6 +276,37 @@ fn should_generate_proofs_correctly() { }); } +#[test] +fn should_generate_batch_proof_correctly() { + let _ = env_logger::try_init(); + let mut ext = new_test_ext(); + // given + ext.execute_with(|| init_chain(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 all leaves + let (leaves, proof) = crate::Pallet::::generate_batch_proof(vec![0, 4, 5]).unwrap(); + + // then + assert_eq!( + proof, + BatchProof { + leaf_indices: vec![0, 4, 5], + leaf_count: 7, + items: vec![ + hex("ad4cbc033833612ccd4626d5f023b9dfc50a35e838514dd1f3c86f8506728705"), + hex("cb24f4614ad5b2a5430344c99545b421d9af83c46fd632d70a332200884b4d46"), + hex("611c2174c6164952a66d985cfe1ec1a623794393e3acff96b136d198f37a648c"), + ], + } + ); + }); +} + #[test] fn should_verify() { let _ = env_logger::try_init(); @@ -301,6 +332,37 @@ fn should_verify() { }); } +#[test] +fn should_verify_batch_proof() { + let _ = env_logger::try_init(); + + // Start off with chain initialisation and storing indexing data off-chain + // (MMR Leafs) + let mut ext = new_test_ext(); + ext.execute_with(|| init_chain(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::::generate_batch_proof(vec![0, 4, 5]).unwrap() + }); + + ext.execute_with(|| { + init_chain(7); + // then + assert_eq!( + crate::Pallet::::verify_leaves( + leaves.into_iter().map(|(leaf, ..)| leaf).collect(), + proof + ), + Ok(()) + ); + }); +} + #[test] fn verification_should_be_stateless() { let _ = env_logger::try_init(); @@ -328,6 +390,39 @@ fn verification_should_be_stateless() { ); } +#[test] +fn should_verify_batch_proof_statelessly() { + let _ = env_logger::try_init(); + + // Start off with chain initialisation and storing indexing data off-chain + // (MMR Leafs) + let mut ext = new_test_ext(); + ext.execute_with(|| init_chain(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::::generate_batch_proof(vec![0, 4, 5]).unwrap() + }); + let root = ext.execute_with(|| crate::Pallet::::mmr_root_hash()); + + // Verify proof without relying on any on-chain data. + assert_eq!( + crate::verify_leaves_proof::<::Hashing, _>( + root, + leaves + .into_iter() + .map(|(leaf, ..)| crate::primitives::DataOrHash::Data(leaf)) + .collect(), + proof + ), + Ok(()) + ); +} + #[test] fn should_verify_on_the_next_block_since_there_is_no_pruning_yet() { let _ = env_logger::try_init(); From d3078cd8fb2f336503c0b49e848659dead545bd3 Mon Sep 17 00:00:00 2001 From: david Date: Tue, 11 Jan 2022 15:57:13 +0100 Subject: [PATCH 06/24] update comments --- frame/merkle-mountain-range/src/mmr/mmr.rs | 9 +++++---- primitives/merkle-mountain-range/src/lib.rs | 8 ++++---- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/frame/merkle-mountain-range/src/mmr/mmr.rs b/frame/merkle-mountain-range/src/mmr/mmr.rs index 6fc1c2a033c20..9a978f2d3f99c 100644 --- a/frame/merkle-mountain-range/src/mmr/mmr.rs +++ b/frame/merkle-mountain-range/src/mmr/mmr.rs @@ -49,8 +49,9 @@ where } /// Stateless verification of the proof for a batch of leaves. -/// Note, the leaves should be sorted such that each corresponding leafs and leaf indices have the -/// same position in both the `leaves` vector and the `leaf_indices` vector contained in the proof +/// 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::BatchProof] pub fn verify_leaves_proof( root: H::Output, leaves: Vec>, @@ -122,9 +123,9 @@ where } /// Verify proof of a single leaf. - /// Note, the leaves should be sorted such that each corresponding leafs and leaf indices have + /// 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 - /// proof + /// [primitives::BatchProof] pub fn verify_leaves_proof( &self, leaves: Vec, diff --git a/primitives/merkle-mountain-range/src/lib.rs b/primitives/merkle-mountain-range/src/lib.rs index 2730d32efdd06..984f235f8b7b9 100644 --- a/primitives/merkle-mountain-range/src/lib.rs +++ b/primitives/merkle-mountain-range/src/lib.rs @@ -435,8 +435,8 @@ sp_api::decl_runtime_apis! { /// /// Note this function will use on-chain MMR root hash and check if the proof /// matches the hash. - /// Note, the leaves should be sorted such that corresponding leaf and leaf indices have the same position - /// in both the `leaves` vector and the `leaf_indices` vector contained in the proof + /// 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 [BatchProof] fn verify_batch_proof(leaves: Vec, proof: BatchProof) -> Result<(), Error>; /// Verify MMR proof against given root hash or a batch of leaves. @@ -444,8 +444,8 @@ sp_api::decl_runtime_apis! { /// Note this function does not require any on-chain storage - the /// proof is verified against given MMR root hash. /// - /// Note, the leaves should be sorted such that each corresponding leafs and leaf indices have the same position - /// in both the `leaves` vector and the `leaf_indices` vector contained in the proof + /// 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 [BatchProof] fn verify_batch_proof_stateless(root: Hash, leaves: Vec, proof: BatchProof) -> Result<(), Error>; } From 518408e8f452364e240bcfbdf158876642b7bc4d Mon Sep 17 00:00:00 2001 From: david Date: Wed, 12 Jan 2022 10:01:41 +0100 Subject: [PATCH 07/24] minor comment fix --- frame/merkle-mountain-range/rpc/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/merkle-mountain-range/rpc/src/lib.rs b/frame/merkle-mountain-range/rpc/src/lib.rs index bc58a2489873f..75b02a3c14df1 100644 --- a/frame/merkle-mountain-range/rpc/src/lib.rs +++ b/frame/merkle-mountain-range/rpc/src/lib.rs @@ -70,7 +70,7 @@ pub struct LeafBatchProof { } impl LeafBatchProof { - /// Create new `LeafBatchProof` from a given vector of concrete `leaf` and `proof`. + /// Create new `LeafBatchProof` from a given vector of ([Leaf], [LeafIndex]) and a [BatchProof]. pub fn new( block_hash: BlockHash, leaves: Vec<(Leaf, LeafIndex)>, From ed3dc1554907482a8f08679997ca21fcef826eaf Mon Sep 17 00:00:00 2001 From: david Date: Wed, 12 Jan 2022 10:11:57 +0100 Subject: [PATCH 08/24] remove unused variables --- frame/merkle-mountain-range/src/tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/merkle-mountain-range/src/tests.rs b/frame/merkle-mountain-range/src/tests.rs index 771afb1689ab6..9abdd2ac5dfad 100644 --- a/frame/merkle-mountain-range/src/tests.rs +++ b/frame/merkle-mountain-range/src/tests.rs @@ -289,7 +289,7 @@ fn should_generate_batch_proof_correctly() { register_offchain_ext(&mut ext); ext.execute_with(|| { // when generate proofs for all leaves - let (leaves, proof) = crate::Pallet::::generate_batch_proof(vec![0, 4, 5]).unwrap(); + let (.., proof) = crate::Pallet::::generate_batch_proof(vec![0, 4, 5]).unwrap(); // then assert_eq!( From 41a96c84128aaffe53a7d92c8ab85041e1f1a6e3 Mon Sep 17 00:00:00 2001 From: david Date: Wed, 12 Jan 2022 10:30:31 +0100 Subject: [PATCH 09/24] fix rust doc errors --- frame/merkle-mountain-range/rpc/src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/frame/merkle-mountain-range/rpc/src/lib.rs b/frame/merkle-mountain-range/rpc/src/lib.rs index 75b02a3c14df1..cab1155c2834a 100644 --- a/frame/merkle-mountain-range/rpc/src/lib.rs +++ b/frame/merkle-mountain-range/rpc/src/lib.rs @@ -70,7 +70,8 @@ pub struct LeafBatchProof { } impl LeafBatchProof { - /// Create new `LeafBatchProof` from a given vector of ([Leaf], [LeafIndex]) and a [BatchProof]. + /// Create new `LeafBatchProof` from a given vector of (`Leaf`, + /// [pallet_mmr_primitives::LeafIndex]) and a [pallet_mmr_primitives::BatchProof]. pub fn new( block_hash: BlockHash, leaves: Vec<(Leaf, LeafIndex)>, From 1a4ce0a55c84fb7a2671be578cda27fcfb1f34fb Mon Sep 17 00:00:00 2001 From: David Salami Date: Wed, 13 Apr 2022 14:26:07 +0000 Subject: [PATCH 10/24] refactor mmr runtime api --- bin/node/runtime/src/lib.rs | 2 +- frame/merkle-mountain-range/rpc/Cargo.toml | 2 +- frame/merkle-mountain-range/rpc/src/lib.rs | 33 +++++----- frame/merkle-mountain-range/src/lib.rs | 66 +------------------ frame/merkle-mountain-range/src/mmr/mmr.rs | 70 +-------------------- frame/merkle-mountain-range/src/mmr/mod.rs | 2 +- frame/merkle-mountain-range/src/tests.rs | 46 +++++++------- primitives/merkle-mountain-range/src/lib.rs | 23 ++----- 8 files changed, 53 insertions(+), 191 deletions(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 516fbc69b9987..dcc8c2e535915 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -1823,7 +1823,7 @@ impl_runtime_apis! { fn mmr_root() -> Result { Ok(Mmr::mmr_root()) } - + fn generate_batch_proof(leaf_indices: Vec) -> Result<(Vec<(mmr::EncodableOpaqueLeaf, pallet_mmr::primitives::LeafIndex)>, mmr::BatchProof), mmr::Error> { diff --git a/frame/merkle-mountain-range/rpc/Cargo.toml b/frame/merkle-mountain-range/rpc/Cargo.toml index 94c895ea91517..359ee88a9c485 100644 --- a/frame/merkle-mountain-range/rpc/Cargo.toml +++ b/frame/merkle-mountain-range/rpc/Cargo.toml @@ -22,7 +22,7 @@ serde = { version = "1.0.136", features = ["derive"] } sp-api = { version = "4.0.0-dev", path = "../../../primitives/api" } sp-blockchain = { version = "4.0.0-dev", path = "../../../primitives/blockchain" } sp-core = { version = "6.0.0", path = "../../../primitives/core" } -sp-mmr-primitives = { version = "4.0.0-dev", default-features = false, path = "../../../primitives/merkle-mountain-range" } +sp-mmr-primitives = { version = "4.0.0-dev", path = "../../../primitives/merkle-mountain-range" } sp-runtime = { version = "6.0.0", path = "../../../primitives/runtime" } [dev-dependencies] diff --git a/frame/merkle-mountain-range/rpc/src/lib.rs b/frame/merkle-mountain-range/rpc/src/lib.rs index cab1155c2834a..c1bef40852b72 100644 --- a/frame/merkle-mountain-range/rpc/src/lib.rs +++ b/frame/merkle-mountain-range/rpc/src/lib.rs @@ -29,10 +29,10 @@ use serde::{Deserialize, Serialize}; use sp_api::ProvideRuntimeApi; use sp_blockchain::HeaderBackend; use sp_core::Bytes; -use sp_mmr_primitives::{Error as MmrError, LeafIndex, Proof}; +use sp_mmr_primitives::{Error as MmrError, LeafIndex}; use sp_runtime::{generic::BlockId, traits::Block as BlockT}; -pub use sp_mmr_primitives::MmrApi as MmrRuntimeApi; +pub use sp_mmr_primitives::{BatchProof, MmrApi as MmrRuntimeApi}; /// Retrieved MMR leaf and its proof. #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] @@ -42,13 +42,13 @@ pub struct LeafProof { pub block_hash: BlockHash, /// SCALE-encoded leaf data. pub leaf: Bytes, - /// SCALE-encoded proof data. See [sp_mmr_primitives::Proof]. + /// SCALE-encoded proof data. See [sp_mmr_primitives::BatchProof]. pub proof: Bytes, } impl LeafProof { /// Create new `LeafProof` from given concrete `leaf` and `proof`. - pub fn new(block_hash: BlockHash, leaf: Leaf, proof: Proof) -> Self + pub fn new(block_hash: BlockHash, leaf: Leaf, proof: BatchProof) -> Self where Leaf: Encode, MmrHash: Encode, @@ -63,7 +63,7 @@ impl LeafProof { pub struct LeafBatchProof { /// Block hash the proof was generated for. pub block_hash: BlockHash, - /// SCALE-encoded vector of leaf index and leaf data `(LeafData, LeafIndex)`. + /// SCALE-encoded vector of leaf data `LeafData`. pub leaves: Bytes, /// SCALE-encoded proof data. See [pallet_mmr_primitives::BatchProof]. pub proof: Bytes, @@ -74,7 +74,7 @@ impl LeafBatchProof { /// [pallet_mmr_primitives::LeafIndex]) and a [pallet_mmr_primitives::BatchProof]. pub fn new( block_hash: BlockHash, - leaves: Vec<(Leaf, LeafIndex)>, + leaves: Vec, proof: BatchProof, ) -> Self where @@ -226,8 +226,8 @@ mod tests { fn should_serialize_leaf_proof() { // given let leaf = vec![1_u8, 2, 3, 4]; - let proof = Proof { - leaf_index: 1, + let proof = BatchProof { + leaf_indices: vec![1], leaf_count: 9, items: vec![H256::repeat_byte(1), H256::repeat_byte(2)], }; @@ -240,7 +240,7 @@ mod tests { // then assert_eq!( actual, - r#"{"blockHash":"0x0000000000000000000000000000000000000000000000000000000000000000","leaf":"0x1001020304","proof":"0x010000000000000009000000000000000801010101010101010101010101010101010101010101010101010101010101010202020202020202020202020202020202020202020202020202020202020202"}"# + r#"{"blockHash":"0x0000000000000000000000000000000000000000000000000000000000000000","leaf":"0x1001020304","proof":"0x04010000000000000009000000000000000801010101010101010101010101010101010101010101010101010101010101010202020202020202020202020202020202020202020202020202020202020202"}"# ); } @@ -254,14 +254,15 @@ mod tests { items: vec![H256::repeat_byte(1), H256::repeat_byte(2)], }; - let leaf_proof = LeafBatchProof::new(H256::repeat_byte(0), vec![(leaf, 1)], proof); + let leaf_proof = LeafBatchProof::new(H256::repeat_byte(0), vec![leaf], proof); // when let actual = serde_json::to_string(&leaf_proof).unwrap(); + // then assert_eq!( actual, - r#"{"blockHash":"0x0000000000000000000000000000000000000000000000000000000000000000","leaves":"0x0410010203040100000000000000","proof":"0x04010000000000000009000000000000000801010101010101010101010101010101010101010101010101010101010101010202020202020202020202020202020202020202020202020202020202020202"}"# + r#"{"blockHash":"0x0000000000000000000000000000000000000000000000000000000000000000","leaves":"0x041001020304","proof":"0x04010000000000000009000000000000000801010101010101010101010101010101010101010101010101010101010101010202020202020202020202020202020202020202020202020202020202020202"}"# ); } @@ -272,8 +273,8 @@ mod tests { block_hash: H256::repeat_byte(0), leaf: Bytes(vec![1_u8, 2, 3, 4].encode()), proof: Bytes( - Proof { - leaf_index: 1, + BatchProof { + leaf_indices: vec![1], leaf_count: 9, items: vec![H256::repeat_byte(1), H256::repeat_byte(2)], } @@ -285,7 +286,7 @@ mod tests { let actual: LeafProof = serde_json::from_str(r#"{ "blockHash":"0x0000000000000000000000000000000000000000000000000000000000000000", "leaf":"0x1001020304", - "proof":"0x010000000000000009000000000000000801010101010101010101010101010101010101010101010101010101010101010202020202020202020202020202020202020202020202020202020202020202" + "proof":"0x04010000000000000009000000000000000801010101010101010101010101010101010101010101010101010101010101010202020202020202020202020202020202020202020202020202020202020202" }"#).unwrap(); // then @@ -297,7 +298,7 @@ mod tests { // given let expected = LeafBatchProof { block_hash: H256::repeat_byte(0), - leaves: Bytes(vec![(vec![1_u8, 2, 3, 4], 1)].encode()), + leaves: Bytes(vec![vec![1_u8, 2, 3, 4]].encode()), proof: Bytes( BatchProof { leaf_indices: vec![1], @@ -311,7 +312,7 @@ mod tests { // when let actual: LeafBatchProof = serde_json::from_str(r#"{ "blockHash":"0x0000000000000000000000000000000000000000000000000000000000000000", - "leaves":"0x04100102030401000000", + "leaves":"0x041001020304", "proof":"0x04010000000000000009000000000000000801010101010101010101010101010101010101010101010101010101010101010202020202020202020202020202020202020202020202020202020202020202" }"#).unwrap(); diff --git a/frame/merkle-mountain-range/src/lib.rs b/frame/merkle-mountain-range/src/lib.rs index 7795219c9e362..8c8ffbb17a905 100644 --- a/frame/merkle-mountain-range/src/lib.rs +++ b/frame/merkle-mountain-range/src/lib.rs @@ -228,29 +228,6 @@ type LeafOf = <>::LeafData as primitives::LeafDataProvider> /// Hashing used for the pallet. pub(crate) type HashingOf = >::Hashing; -/// Stateless MMR proof verification. -/// -/// This function can be used to verify received MMR proof (`proof`) -/// for given leaf data (`leaf`) against a known MMR root hash (`root`). -/// -/// The verification does not require any storage access. -pub fn verify_leaf_proof( - root: H::Output, - leaf: mmr::Node, - proof: primitives::Proof, -) -> Result<(), primitives::Error> -where - H: traits::Hash, - L: primitives::FullLeaf, -{ - let is_valid = mmr::verify_leaf_proof::(root, leaf, proof)?; - if is_valid { - Ok(()) - } else { - Err(primitives::Error::Verify.log_debug(("The proof is incorrect.", root))) - } -} - /// Stateless MMR proof verification for batch of leaves. /// /// This function can be used to verify received MMR proof (`proof`) @@ -278,20 +255,6 @@ impl, I: 'static> Pallet { fn offchain_key(pos: NodeIndex) -> sp_std::prelude::Vec { (T::INDEXING_PREFIX, pos).encode() } - - /// Generate a MMR proof for the given `leaf_index`. - /// - /// 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( - leaf_index: LeafIndex, - ) -> Result<(LeafOf, primitives::Proof<>::Hash>), primitives::Error> { - let mmr: ModuleMmr = mmr::Mmr::new(Self::mmr_leaves()); - mmr.generate_proof(leaf_index) - } - /// Generate a MMR proof for the given `leaf_indices`. /// /// Note this method can only be used from an off-chain context @@ -301,40 +264,13 @@ impl, I: 'static> Pallet { pub fn generate_batch_proof( leaf_indices: Vec, ) -> Result< - (Vec<(LeafOf, NodeIndex)>, primitives::BatchProof<>::Hash>), + (Vec>, primitives::BatchProof<>::Hash>), primitives::Error, > { let mmr: ModuleMmr = mmr::Mmr::new(Self::mmr_leaves()); mmr.generate_batch_proof(leaf_indices) } - /// Verify MMR proof for given `leaf`. - /// - /// 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_leaf( - leaf: LeafOf, - proof: primitives::Proof<>::Hash>, - ) -> Result<(), primitives::Error> { - if proof.leaf_count > Self::mmr_leaves() || - proof.leaf_count == 0 || - proof.items.len() as u32 > mmr::utils::NodesUtils::new(proof.leaf_count).depth() - { - return Err(primitives::Error::Verify - .log_debug("The proof has incorrect number of leaves or proof items.")) - } - - let mmr: ModuleMmr = mmr::Mmr::new(proof.leaf_count); - let is_valid = mmr.verify_leaf_proof(leaf, proof)?; - if is_valid { - Ok(()) - } else { - Err(primitives::Error::Verify.log_debug("The proof is incorrect.")) - } - } - /// Return the on-chain MMR root hash. pub fn mmr_root() -> >::Hash { Self::mmr_root_hash() diff --git a/frame/merkle-mountain-range/src/mmr/mmr.rs b/frame/merkle-mountain-range/src/mmr/mmr.rs index 9a978f2d3f99c..ab5e8a742cec0 100644 --- a/frame/merkle-mountain-range/src/mmr/mmr.rs +++ b/frame/merkle-mountain-range/src/mmr/mmr.rs @@ -27,27 +27,6 @@ use crate::{ #[cfg(not(feature = "std"))] use sp_std::{prelude::Vec, vec}; -/// Stateless verification of the leaf proof. -pub fn verify_leaf_proof( - root: H::Output, - leaf: Node, - proof: primitives::Proof, -) -> Result -where - H: sp_runtime::traits::Hash, - L: primitives::FullLeaf, -{ - let size = NodesUtils::new(proof.leaf_count).size(); - let leaf_position = mmr_lib::leaf_index_to_pos(proof.leaf_index); - - let p = mmr_lib::MerkleProof::, Hasher>::new( - size, - proof.items.into_iter().map(Node::Hash).collect(), - ); - p.verify(Node::Hash(root), vec![(leaf_position, leaf)]) - .map_err(|e| Error::Verify.log_debug(e)) -} - /// 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 @@ -106,22 +85,6 @@ where Self { mmr: mmr_lib::MMR::new(size, Default::default()), leaves } } - /// Verify proof of a single leaf. - pub fn verify_leaf_proof( - &self, - leaf: L, - proof: primitives::Proof<>::Hash>, - ) -> Result { - let p = mmr_lib::MerkleProof::, Hasher, L>>::new( - self.mmr.mmr_size(), - proof.items.into_iter().map(Node::Hash).collect(), - ); - let position = mmr_lib::leaf_index_to_pos(proof.leaf_index); - let root = self.mmr.get_root().map_err(|e| Error::GetRoot.log_error(e))?; - p.verify(root, vec![(position, Node::Data(leaf))]) - .map_err(|e| Error::Verify.log_debug(e)) - } - /// Verify proof of a single leaf. /// 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 @@ -187,32 +150,6 @@ where I: 'static, L: primitives::FullLeaf + codec::Decode, { - /// Generate a proof for given leaf index. - /// - /// 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_index: NodeIndex, - ) -> Result<(L, primitives::Proof<>::Hash>), Error> { - let position = mmr_lib::leaf_index_to_pos(leaf_index); - let store = >::default(); - let leaf = match mmr_lib::MMRStore::get_elem(&store, position) { - Ok(Some(Node::Data(leaf))) => leaf, - e => return Err(Error::LeafNotFound.log_debug(e)), - }; - let leaf_count = self.leaves; - self.mmr - .gen_proof(vec![position]) - .map_err(|e| Error::GenerateProof.log_error(e)) - .map(|p| primitives::Proof { - leaf_index, - leaf_count, - items: p.proof_items().iter().map(|x| x.hash()).collect(), - }) - .map(|p| (leaf, p)) - } - /// Generate a proof for given leaf indices. /// /// Proof generation requires all the nodes (or their hashes) to be available in the storage. @@ -220,7 +157,7 @@ where pub fn generate_batch_proof( &self, leaf_indices: Vec, - ) -> Result<(Vec<(L, NodeIndex)>, primitives::BatchProof<>::Hash>), Error> { + ) -> Result<(Vec, primitives::BatchProof<>::Hash>), Error> { let positions = leaf_indices .iter() .map(|index| mmr_lib::leaf_index_to_pos(*index)) @@ -232,10 +169,7 @@ where Ok(Some(Node::Data(leaf))) => Ok(leaf), e => Err(Error::LeafNotFound.log_debug(e)), }) - .collect::, Error>>()? - .into_iter() - .zip(leaf_indices.iter().cloned()) - .collect::>(); + .collect::, Error>>()?; let leaf_count = self.leaves; self.mmr diff --git a/frame/merkle-mountain-range/src/mmr/mod.rs b/frame/merkle-mountain-range/src/mmr/mod.rs index 931a121882f38..04fdfa199e72b 100644 --- a/frame/merkle-mountain-range/src/mmr/mod.rs +++ b/frame/merkle-mountain-range/src/mmr/mod.rs @@ -22,7 +22,7 @@ pub mod utils; use sp_mmr_primitives::{DataOrHash, FullLeaf}; use sp_runtime::traits; -pub use self::mmr::{verify_leaf_proof, verify_leaves_proof, Mmr}; +pub use self::mmr::{verify_leaves_proof, Mmr}; /// Node type for runtime `T`. pub type NodeOf = Node<>::Hashing, L>; diff --git a/frame/merkle-mountain-range/src/tests.rs b/frame/merkle-mountain-range/src/tests.rs index 9abdd2ac5dfad..b1dcd6f6e7d2e 100644 --- a/frame/merkle-mountain-range/src/tests.rs +++ b/frame/merkle-mountain-range/src/tests.rs @@ -23,7 +23,7 @@ use sp_core::{ offchain::{testing::TestOffchainExt, OffchainDbExt, OffchainWorkerExt}, H256, }; -use sp_mmr_primitives::{Compact, Proof, BatchProof}; +use sp_mmr_primitives::{BatchProof, Compact}; pub(crate) fn new_test_ext() -> sp_io::TestExternalities { frame_system::GenesisConfig::default().build_storage::().unwrap().into() @@ -225,16 +225,18 @@ fn should_generate_proofs_correctly() { // when generate proofs for all leaves let proofs = (0_u64..crate::NumberOfLeaves::::get()) .into_iter() - .map(|leaf_index| crate::Pallet::::generate_proof(leaf_index).unwrap()) + .map(|leaf_index| { + crate::Pallet::::generate_batch_proof(vec![leaf_index]).unwrap() + }) .collect::>(); // then assert_eq!( proofs[0], ( - Compact::new(((0, H256::repeat_byte(1)).into(), LeafData::new(1).into(),)), - Proof { - leaf_index: 0, + vec![Compact::new(((0, H256::repeat_byte(1)).into(), LeafData::new(1).into(),))], + BatchProof { + leaf_indices: vec![0], leaf_count: 7, items: vec![ hex("ad4cbc033833612ccd4626d5f023b9dfc50a35e838514dd1f3c86f8506728705"), @@ -247,9 +249,9 @@ fn should_generate_proofs_correctly() { assert_eq!( proofs[4], ( - Compact::new(((4, H256::repeat_byte(5)).into(), LeafData::new(5).into(),)), - Proof { - leaf_index: 4, + vec![Compact::new(((4, H256::repeat_byte(5)).into(), LeafData::new(5).into(),))], + BatchProof { + leaf_indices: vec![4], leaf_count: 7, items: vec![ hex("ae88a0825da50e953e7a359c55fe13c8015e48d03d301b8bdfc9193874da9252"), @@ -262,9 +264,9 @@ fn should_generate_proofs_correctly() { assert_eq!( proofs[6], ( - Compact::new(((6, H256::repeat_byte(7)).into(), LeafData::new(7).into(),)), - Proof { - leaf_index: 6, + vec![Compact::new(((6, H256::repeat_byte(7)).into(), LeafData::new(7).into(),))], + BatchProof { + leaf_indices: vec![6], leaf_count: 7, items: vec![ hex("ae88a0825da50e953e7a359c55fe13c8015e48d03d301b8bdfc9193874da9252"), @@ -320,15 +322,15 @@ fn should_verify() { // Try to generate proof now. This requires the offchain extensions to be present // to retrieve full leaf data. register_offchain_ext(&mut ext); - let (leaf, proof5) = ext.execute_with(|| { + let (leaves, proof5) = ext.execute_with(|| { // when - crate::Pallet::::generate_proof(5).unwrap() + crate::Pallet::::generate_batch_proof(vec![5]).unwrap() }); ext.execute_with(|| { init_chain(7); // then - assert_eq!(crate::Pallet::::verify_leaf(leaf, proof5), Ok(())); + assert_eq!(crate::Pallet::::verify_leaves(leaves, proof5), Ok(())); }); } @@ -355,7 +357,7 @@ fn should_verify_batch_proof() { // then assert_eq!( crate::Pallet::::verify_leaves( - leaves.into_iter().map(|(leaf, ..)| leaf).collect(), + leaves.into_iter().map(|leaf| leaf).collect(), proof ), Ok(()) @@ -376,16 +378,16 @@ fn verification_should_be_stateless() { // Try to generate proof now. This requires the offchain extensions to be present // to retrieve full leaf data. register_offchain_ext(&mut ext); - let (leaf, proof5) = ext.execute_with(|| { + let (leaves, proof5) = ext.execute_with(|| { // when - crate::Pallet::::generate_proof(5).unwrap() + crate::Pallet::::generate_batch_proof(vec![5]).unwrap() }); let root = ext.execute_with(|| crate::Pallet::::mmr_root_hash()); // Verify proof without relying on any on-chain data. - let leaf = crate::primitives::DataOrHash::Data(leaf); + let leaf = crate::primitives::DataOrHash::Data(leaves[0].clone()); assert_eq!( - crate::verify_leaf_proof::<::Hashing, _>(root, leaf, proof5), + crate::verify_leaves_proof::<::Hashing, _>(root, vec![leaf], proof5), Ok(()) ); } @@ -415,7 +417,7 @@ fn should_verify_batch_proof_statelessly() { root, leaves .into_iter() - .map(|(leaf, ..)| crate::primitives::DataOrHash::Data(leaf)) + .map(|leaf| crate::primitives::DataOrHash::Data(leaf)) .collect(), proof ), @@ -435,10 +437,10 @@ fn should_verify_on_the_next_block_since_there_is_no_pruning_yet() { ext.execute_with(|| { // when - let (leaf, proof5) = crate::Pallet::::generate_proof(5).unwrap(); + let (leaves, proof5) = crate::Pallet::::generate_batch_proof(vec![5]).unwrap(); new_block(); // then - assert_eq!(crate::Pallet::::verify_leaf(leaf, proof5), Ok(())); + assert_eq!(crate::Pallet::::verify_leaves(leaves, proof5), Ok(())); }); } diff --git a/primitives/merkle-mountain-range/src/lib.rs b/primitives/merkle-mountain-range/src/lib.rs index 984f235f8b7b9..a23f04d62d167 100644 --- a/primitives/merkle-mountain-range/src/lib.rs +++ b/primitives/merkle-mountain-range/src/lib.rs @@ -68,17 +68,6 @@ impl OnNewRoot for () { fn on_new_root(_root: &Hash) {} } -/// A MMR proof data for one of the leaves. -#[derive(codec::Encode, codec::Decode, RuntimeDebug, Clone, PartialEq, Eq)] -pub struct Proof { - /// The index of the leaf the proof is for. - pub leaf_index: LeafIndex, - /// 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). - pub items: Vec, -} - /// A full leaf content stored in the offchain-db. pub trait FullLeaf: Clone + PartialEq + fmt::Debug { /// Encode the leaf either in it's full or compact form. @@ -408,14 +397,14 @@ sp_api::decl_runtime_apis! { /// API to interact with MMR pallet. pub trait MmrApi { /// Generate MMR proof for a leaf under given index. - fn generate_proof(leaf_index: LeafIndex) -> Result<(EncodableOpaqueLeaf, Proof), Error>; + fn generate_proof(leaf_index: LeafIndex) -> Result<(EncodableOpaqueLeaf, BatchProof), Error>; /// Verify MMR proof against on-chain MMR. /// /// Note this function will use on-chain MMR root hash and check if the proof /// matches the hash. /// See [Self::verify_proof_stateless] for a stateless verifier. - fn verify_proof(leaf: EncodableOpaqueLeaf, proof: Proof) -> Result<(), Error>; + fn verify_proof(leaf: EncodableOpaqueLeaf, proof: BatchProof) -> Result<(), Error>; /// Verify MMR proof against given root hash. /// @@ -423,7 +412,7 @@ sp_api::decl_runtime_apis! { /// proof is verified against given MMR root hash. /// /// The leaf data is expected to be encoded in it's compact form. - fn verify_proof_stateless(root: Hash, leaf: EncodableOpaqueLeaf, proof: Proof) + fn verify_proof_stateless(root: Hash, leaf: EncodableOpaqueLeaf, proof: BatchProof) -> Result<(), Error>; /// Return the on-chain MMR root hash. @@ -465,13 +454,13 @@ mod tests { type Test = DataOrHash; type TestCompact = Compact; - type TestProof = Proof<::Output>; + type TestProof = BatchProof<::Output>; #[test] fn should_encode_decode_proof() { // given - let proof: TestProof = Proof { - leaf_index: 5, + let proof: TestProof = BatchProof { + leaf_indices: vec![5], leaf_count: 10, items: vec![ hex("c3e7ba6b511162fead58f2c8b5764ce869ed1118011ac37392522ed16720bbcd"), From cb75408c7b34290713af2614bafae8e93908d51e Mon Sep 17 00:00:00 2001 From: David Salami Date: Wed, 13 Apr 2022 14:39:44 +0000 Subject: [PATCH 11/24] fix tests --- client/beefy/src/tests.rs | 24 +++++++++++++++++---- frame/merkle-mountain-range/src/lib.rs | 9 ++++---- frame/merkle-mountain-range/src/mmr/mmr.rs | 7 +++--- primitives/merkle-mountain-range/src/lib.rs | 2 +- 4 files changed, 30 insertions(+), 12 deletions(-) diff --git a/client/beefy/src/tests.rs b/client/beefy/src/tests.rs index e568daba8e112..469d9c131016e 100644 --- a/client/beefy/src/tests.rs +++ b/client/beefy/src/tests.rs @@ -39,7 +39,7 @@ use beefy_primitives::{ crypto::AuthorityId, BeefyApi, ConsensusLog, MmrRootHash, ValidatorSet, BEEFY_ENGINE_ID, KEY_TYPE as BeefyKeyType, }; -use sp_mmr_primitives::{EncodableOpaqueLeaf, Error as MmrError, LeafIndex, MmrApi, Proof}; +use sp_mmr_primitives::{BatchProof, EncodableOpaqueLeaf, Error as MmrError, LeafIndex, MmrApi}; use sp_api::{ApiRef, ProvideRuntimeApi}; use sp_consensus::BlockOrigin; @@ -239,11 +239,11 @@ macro_rules! create_test_api { impl MmrApi for RuntimeApi { fn generate_proof(_leaf_index: LeafIndex) - -> Result<(EncodableOpaqueLeaf, Proof), MmrError> { + -> Result<(EncodableOpaqueLeaf, BatchProof), MmrError> { unimplemented!() } - fn verify_proof(_leaf: EncodableOpaqueLeaf, _proof: Proof) + fn verify_proof(_leaf: EncodableOpaqueLeaf, _proof: BatchProof) -> Result<(), MmrError> { unimplemented!() } @@ -251,7 +251,7 @@ macro_rules! create_test_api { fn verify_proof_stateless( _root: MmrRootHash, _leaf: EncodableOpaqueLeaf, - _proof: Proof + _proof: BatchProof ) -> Result<(), MmrError> { unimplemented!() } @@ -259,6 +259,22 @@ macro_rules! create_test_api { fn mmr_root() -> Result { Ok($mmr_root) } + + fn generate_batch_proof(_leaf_indices: Vec) -> Result<(Vec, BatchProof), MmrError> { + unimplemented!() + } + + fn verify_batch_proof(_leaves: Vec, _proof: BatchProof) -> Result<(), MmrError> { + unimplemented!() + } + + fn verify_batch_proof_stateless( + _root: MmrRootHash, + _leaves: Vec, + _proof: BatchProof + ) -> Result<(), MmrError> { + unimplemented!() + } } } } diff --git a/frame/merkle-mountain-range/src/lib.rs b/frame/merkle-mountain-range/src/lib.rs index 8c8ffbb17a905..64e22d24735c4 100644 --- a/frame/merkle-mountain-range/src/lib.rs +++ b/frame/merkle-mountain-range/src/lib.rs @@ -230,10 +230,11 @@ pub(crate) type HashingOf = >::Hashing; /// Stateless MMR proof verification for batch of leaves. /// -/// This function can be used to verify received MMR proof (`proof`) -/// for given leaf data (`leaf`) against a known MMR root hash (`root`). -/// -/// The verification does not require any storage access. +/// This function can be used to verify received MMR [primitives::BatchProof] (`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::BatchProof]. pub fn verify_leaves_proof( root: H::Output, leaves: Vec>, diff --git a/frame/merkle-mountain-range/src/mmr/mmr.rs b/frame/merkle-mountain-range/src/mmr/mmr.rs index ab5e8a742cec0..2b6bdc4cf867c 100644 --- a/frame/merkle-mountain-range/src/mmr/mmr.rs +++ b/frame/merkle-mountain-range/src/mmr/mmr.rs @@ -85,7 +85,7 @@ where Self { mmr: mmr_lib::MMR::new(size, Default::default()), leaves } } - /// Verify proof of a single leaf. + /// 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::BatchProof] @@ -98,14 +98,15 @@ where self.mmr.mmr_size(), proof.items.into_iter().map(Node::Hash).collect(), ); - let leaves_and_position = proof + 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_and_position).map_err(|e| Error::Verify.log_debug(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). diff --git a/primitives/merkle-mountain-range/src/lib.rs b/primitives/merkle-mountain-range/src/lib.rs index a23f04d62d167..c749ea8ba832a 100644 --- a/primitives/merkle-mountain-range/src/lib.rs +++ b/primitives/merkle-mountain-range/src/lib.rs @@ -418,7 +418,7 @@ sp_api::decl_runtime_apis! { /// Return the on-chain MMR root hash. fn mmr_root() -> Result; /// Generate MMR proof for a series of leaves under given indices. - fn generate_batch_proof(leaf_indices: Vec) -> Result<(Vec<(EncodableOpaqueLeaf, LeafIndex)>, BatchProof), Error>; + fn generate_batch_proof(leaf_indices: Vec) -> Result<(Vec, BatchProof), Error>; /// Verify MMR proof against on-chain MMR for a batch of leaves. /// From bd2f8c99facac3fc4bc08634548146c6ca2e2224 Mon Sep 17 00:00:00 2001 From: David Salami Date: Wed, 13 Apr 2022 14:43:01 +0000 Subject: [PATCH 12/24] minor fix --- frame/merkle-mountain-range/src/mmr/mmr.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frame/merkle-mountain-range/src/mmr/mmr.rs b/frame/merkle-mountain-range/src/mmr/mmr.rs index 2b6bdc4cf867c..62cebab9ebfa7 100644 --- a/frame/merkle-mountain-range/src/mmr/mmr.rs +++ b/frame/merkle-mountain-range/src/mmr/mmr.rs @@ -42,7 +42,7 @@ where { let size = NodesUtils::new(proof.leaf_count).size(); - let leaves_and_position = proof + let leaves_and_position_data = proof .leaf_indices .into_iter() .map(|index| mmr_lib::leaf_index_to_pos(index)) @@ -53,7 +53,7 @@ where size, proof.items.into_iter().map(Node::Hash).collect(), ); - p.verify(Node::Hash(root), leaves_and_position) + p.verify(Node::Hash(root), leaves_and_position_data) .map_err(|e| Error::Verify.log_debug(e)) } From b09072ec5ea5f16a4906195ecc80a30dfb4e1fea Mon Sep 17 00:00:00 2001 From: David Salami Date: Wed, 13 Apr 2022 15:29:17 +0000 Subject: [PATCH 13/24] minor fix --- frame/merkle-mountain-range/src/lib.rs | 1 + frame/merkle-mountain-range/src/mmr/mmr.rs | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frame/merkle-mountain-range/src/lib.rs b/frame/merkle-mountain-range/src/lib.rs index 64e22d24735c4..46b9cd9723865 100644 --- a/frame/merkle-mountain-range/src/lib.rs +++ b/frame/merkle-mountain-range/src/lib.rs @@ -71,6 +71,7 @@ mod tests; pub use pallet::*; pub use sp_mmr_primitives::{self as primitives, Error, LeafDataProvider, LeafIndex, NodeIndex}; +use sp_std::prelude::*; /// 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 diff --git a/frame/merkle-mountain-range/src/mmr/mmr.rs b/frame/merkle-mountain-range/src/mmr/mmr.rs index 62cebab9ebfa7..5469ec7ec30e2 100644 --- a/frame/merkle-mountain-range/src/mmr/mmr.rs +++ b/frame/merkle-mountain-range/src/mmr/mmr.rs @@ -24,8 +24,7 @@ use crate::{ primitives::{self, Error, NodeIndex}, Config, HashingOf, }; -#[cfg(not(feature = "std"))] -use sp_std::{prelude::Vec, vec}; +use sp_std::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 From 67587cf8f1d8c69728e4362e8af724665e77ef95 Mon Sep 17 00:00:00 2001 From: David Salami Date: Wed, 13 Apr 2022 16:26:49 +0000 Subject: [PATCH 14/24] fix node-runtime --- bin/node/runtime/src/lib.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index dcc8c2e535915..62c666307d66d 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -1795,29 +1795,29 @@ impl_runtime_apis! { mmr::Hash, > for Runtime { fn generate_proof(leaf_index: pallet_mmr::primitives::LeafIndex) - -> Result<(mmr::EncodableOpaqueLeaf, mmr::Proof), mmr::Error> + -> Result<(mmr::EncodableOpaqueLeaf, mmr::BatchProof), mmr::Error> { - Mmr::generate_proof(leaf_index) - .map(|(leaf, proof)| (mmr::EncodableOpaqueLeaf::from_leaf(&leaf), proof)) + Mmr::generate_batch_proof(vec![leaf_index]) + .map(|(leaves, proof)| (mmr::EncodableOpaqueLeaf::from_leaf(&leaves[0]), proof)) } - fn verify_proof(leaf: mmr::EncodableOpaqueLeaf, proof: mmr::Proof) + fn verify_proof(leaf: mmr::EncodableOpaqueLeaf, proof: mmr::BatchProof) -> Result<(), mmr::Error> { let leaf: mmr::Leaf = leaf .into_opaque_leaf() .try_decode() .ok_or(mmr::Error::Verify)?; - Mmr::verify_leaf(leaf, proof) + Mmr::verify_leaves(vec![leaf], proof) } fn verify_proof_stateless( root: mmr::Hash, leaf: mmr::EncodableOpaqueLeaf, - proof: mmr::Proof + proof: mmr::BatchProof ) -> Result<(), mmr::Error> { let node = mmr::DataOrHash::Data(leaf.into_opaque_leaf()); - pallet_mmr::verify_leaf_proof::(root, node, proof) + pallet_mmr::verify_leaves_proof::(root, vec![node], proof) } fn mmr_root() -> Result { @@ -1825,10 +1825,10 @@ impl_runtime_apis! { } fn generate_batch_proof(leaf_indices: Vec) - -> Result<(Vec<(mmr::EncodableOpaqueLeaf, pallet_mmr::primitives::LeafIndex)>, mmr::BatchProof), mmr::Error> + -> Result<(Vec, mmr::BatchProof), mmr::Error> { Mmr::generate_batch_proof(leaf_indices) - .map(|(leaves, proof)| (leaves.into_iter().map(|(leaf, pos)| (mmr::EncodableOpaqueLeaf::from_leaf(&leaf), pos)).collect(), proof)) + .map(|(leaves, proof)| (leaves.into_iter().map(|leaf| mmr::EncodableOpaqueLeaf::from_leaf(&leaf)).collect(), proof)) } fn verify_batch_proof(leaves: Vec, proof: mmr::BatchProof) From 8f1897870a432df16231ef8e627c4826b2ae330b Mon Sep 17 00:00:00 2001 From: David Salami Date: Fri, 15 Apr 2022 08:25:31 +0000 Subject: [PATCH 15/24] revert to initial api --- client/beefy/src/tests.rs | 10 +++--- frame/merkle-mountain-range/rpc/src/lib.rs | 28 +++++++-------- frame/merkle-mountain-range/src/tests.rs | 8 +---- primitives/merkle-mountain-range/src/lib.rs | 40 +++++++++++++++++---- 4 files changed, 55 insertions(+), 31 deletions(-) diff --git a/client/beefy/src/tests.rs b/client/beefy/src/tests.rs index 469d9c131016e..13ea6d0831ebb 100644 --- a/client/beefy/src/tests.rs +++ b/client/beefy/src/tests.rs @@ -39,7 +39,9 @@ use beefy_primitives::{ crypto::AuthorityId, BeefyApi, ConsensusLog, MmrRootHash, ValidatorSet, BEEFY_ENGINE_ID, KEY_TYPE as BeefyKeyType, }; -use sp_mmr_primitives::{BatchProof, EncodableOpaqueLeaf, Error as MmrError, LeafIndex, MmrApi}; +use sp_mmr_primitives::{ + BatchProof, EncodableOpaqueLeaf, Error as MmrError, LeafIndex, MmrApi, Proof, +}; use sp_api::{ApiRef, ProvideRuntimeApi}; use sp_consensus::BlockOrigin; @@ -239,11 +241,11 @@ macro_rules! create_test_api { impl MmrApi for RuntimeApi { fn generate_proof(_leaf_index: LeafIndex) - -> Result<(EncodableOpaqueLeaf, BatchProof), MmrError> { + -> Result<(EncodableOpaqueLeaf, Proof), MmrError> { unimplemented!() } - fn verify_proof(_leaf: EncodableOpaqueLeaf, _proof: BatchProof) + fn verify_proof(_leaf: EncodableOpaqueLeaf, _proof: Proof) -> Result<(), MmrError> { unimplemented!() } @@ -251,7 +253,7 @@ macro_rules! create_test_api { fn verify_proof_stateless( _root: MmrRootHash, _leaf: EncodableOpaqueLeaf, - _proof: BatchProof + _proof: Proof ) -> Result<(), MmrError> { unimplemented!() } diff --git a/frame/merkle-mountain-range/rpc/src/lib.rs b/frame/merkle-mountain-range/rpc/src/lib.rs index c1bef40852b72..86467bb855c89 100644 --- a/frame/merkle-mountain-range/rpc/src/lib.rs +++ b/frame/merkle-mountain-range/rpc/src/lib.rs @@ -29,10 +29,10 @@ use serde::{Deserialize, Serialize}; use sp_api::ProvideRuntimeApi; use sp_blockchain::HeaderBackend; use sp_core::Bytes; -use sp_mmr_primitives::{Error as MmrError, LeafIndex}; +use sp_mmr_primitives::{BatchProof, Error as MmrError, LeafIndex, Proof}; use sp_runtime::{generic::BlockId, traits::Block as BlockT}; -pub use sp_mmr_primitives::{BatchProof, MmrApi as MmrRuntimeApi}; +pub use sp_mmr_primitives::MmrApi as MmrRuntimeApi; /// Retrieved MMR leaf and its proof. #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] @@ -42,13 +42,13 @@ pub struct LeafProof { pub block_hash: BlockHash, /// SCALE-encoded leaf data. pub leaf: Bytes, - /// SCALE-encoded proof data. See [sp_mmr_primitives::BatchProof]. + /// SCALE-encoded proof data. See [sp_mmr_primitives::Proof]. pub proof: Bytes, } impl LeafProof { /// Create new `LeafProof` from given concrete `leaf` and `proof`. - pub fn new(block_hash: BlockHash, leaf: Leaf, proof: BatchProof) -> Self + pub fn new(block_hash: BlockHash, leaf: Leaf, proof: Proof) -> Self where Leaf: Encode, MmrHash: Encode, @@ -63,15 +63,15 @@ impl LeafProof { pub struct LeafBatchProof { /// Block hash the proof was generated for. pub block_hash: BlockHash, - /// SCALE-encoded vector of leaf data `LeafData`. + /// SCALE-encoded vector of `LeafData`. pub leaves: Bytes, - /// SCALE-encoded proof data. See [pallet_mmr_primitives::BatchProof]. + /// SCALE-encoded proof data. See [sp_mmr_primitives::BatchProof]. pub proof: Bytes, } impl LeafBatchProof { - /// Create new `LeafBatchProof` from a given vector of (`Leaf`, - /// [pallet_mmr_primitives::LeafIndex]) and a [pallet_mmr_primitives::BatchProof]. + /// Create new `LeafBatchProof` from a given vector of `Leaf` and a + /// [sp_mmr_primitives::BatchProof]. pub fn new( block_hash: BlockHash, leaves: Vec, @@ -226,8 +226,8 @@ mod tests { fn should_serialize_leaf_proof() { // given let leaf = vec![1_u8, 2, 3, 4]; - let proof = BatchProof { - leaf_indices: vec![1], + let proof = Proof { + leaf_index: 1, leaf_count: 9, items: vec![H256::repeat_byte(1), H256::repeat_byte(2)], }; @@ -240,7 +240,7 @@ mod tests { // then assert_eq!( actual, - r#"{"blockHash":"0x0000000000000000000000000000000000000000000000000000000000000000","leaf":"0x1001020304","proof":"0x04010000000000000009000000000000000801010101010101010101010101010101010101010101010101010101010101010202020202020202020202020202020202020202020202020202020202020202"}"# + r#"{"blockHash":"0x0000000000000000000000000000000000000000000000000000000000000000","leaf":"0x1001020304","proof":"0x010000000000000009000000000000000801010101010101010101010101010101010101010101010101010101010101010202020202020202020202020202020202020202020202020202020202020202"}"# ); } @@ -273,8 +273,8 @@ mod tests { block_hash: H256::repeat_byte(0), leaf: Bytes(vec![1_u8, 2, 3, 4].encode()), proof: Bytes( - BatchProof { - leaf_indices: vec![1], + Proof { + leaf_index: 1, leaf_count: 9, items: vec![H256::repeat_byte(1), H256::repeat_byte(2)], } @@ -286,7 +286,7 @@ mod tests { let actual: LeafProof = serde_json::from_str(r#"{ "blockHash":"0x0000000000000000000000000000000000000000000000000000000000000000", "leaf":"0x1001020304", - "proof":"0x04010000000000000009000000000000000801010101010101010101010101010101010101010101010101010101010101010202020202020202020202020202020202020202020202020202020202020202" + "proof":"0x010000000000000009000000000000000801010101010101010101010101010101010101010101010101010101010101010202020202020202020202020202020202020202020202020202020202020202" }"#).unwrap(); // then diff --git a/frame/merkle-mountain-range/src/tests.rs b/frame/merkle-mountain-range/src/tests.rs index b1dcd6f6e7d2e..d025910a9ee5c 100644 --- a/frame/merkle-mountain-range/src/tests.rs +++ b/frame/merkle-mountain-range/src/tests.rs @@ -355,13 +355,7 @@ fn should_verify_batch_proof() { ext.execute_with(|| { init_chain(7); // then - assert_eq!( - crate::Pallet::::verify_leaves( - leaves.into_iter().map(|leaf| leaf).collect(), - proof - ), - Ok(()) - ); + assert_eq!(crate::Pallet::::verify_leaves(leaves, proof), Ok(())); }); } diff --git a/primitives/merkle-mountain-range/src/lib.rs b/primitives/merkle-mountain-range/src/lib.rs index c749ea8ba832a..53704164c8c22 100644 --- a/primitives/merkle-mountain-range/src/lib.rs +++ b/primitives/merkle-mountain-range/src/lib.rs @@ -68,6 +68,17 @@ impl OnNewRoot for () { fn on_new_root(_root: &Hash) {} } +/// A MMR proof data for one of the leaves. +#[derive(codec::Encode, codec::Decode, RuntimeDebug, Clone, PartialEq, Eq)] +pub struct Proof { + /// The index of the leaf the proof is for. + pub leaf_index: LeafIndex, + /// 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). + pub items: Vec, +} + /// A full leaf content stored in the offchain-db. pub trait FullLeaf: Clone + PartialEq + fmt::Debug { /// Encode the leaf either in it's full or compact form. @@ -351,6 +362,23 @@ pub struct BatchProof { pub items: Vec, } +impl TryFrom> for Proof { + type Error = &'static str; + fn try_from(proof: BatchProof) -> Result { + if proof.leaf_indices.len() > 1 { + return Err("BatchProof can only be converted to Proof for single leaf") + } + Ok(Proof { + leaf_index: proof + .leaf_indices + .get(0) + .ok_or("BatchProof can only be converted to Proof for single leaf")? + .clone(), + leaf_count: proof.leaf_count, + items: proof.items, + }) + } +} /// Merkle Mountain Range operation error. #[derive(RuntimeDebug, codec::Encode, codec::Decode, PartialEq, Eq)] pub enum Error { @@ -397,14 +425,14 @@ sp_api::decl_runtime_apis! { /// API to interact with MMR pallet. pub trait MmrApi { /// Generate MMR proof for a leaf under given index. - fn generate_proof(leaf_index: LeafIndex) -> Result<(EncodableOpaqueLeaf, BatchProof), Error>; + fn generate_proof(leaf_index: LeafIndex) -> Result<(EncodableOpaqueLeaf, Proof), Error>; /// Verify MMR proof against on-chain MMR. /// /// Note this function will use on-chain MMR root hash and check if the proof /// matches the hash. /// See [Self::verify_proof_stateless] for a stateless verifier. - fn verify_proof(leaf: EncodableOpaqueLeaf, proof: BatchProof) -> Result<(), Error>; + fn verify_proof(leaf: EncodableOpaqueLeaf, proof: Proof) -> Result<(), Error>; /// Verify MMR proof against given root hash. /// @@ -412,7 +440,7 @@ sp_api::decl_runtime_apis! { /// proof is verified against given MMR root hash. /// /// The leaf data is expected to be encoded in it's compact form. - fn verify_proof_stateless(root: Hash, leaf: EncodableOpaqueLeaf, proof: BatchProof) + fn verify_proof_stateless(root: Hash, leaf: EncodableOpaqueLeaf, proof: Proof) -> Result<(), Error>; /// Return the on-chain MMR root hash. @@ -454,13 +482,13 @@ mod tests { type Test = DataOrHash; type TestCompact = Compact; - type TestProof = BatchProof<::Output>; + type TestProof = Proof<::Output>; #[test] fn should_encode_decode_proof() { // given - let proof: TestProof = BatchProof { - leaf_indices: vec![5], + let proof: TestProof = Proof { + leaf_index: 5, leaf_count: 10, items: vec![ hex("c3e7ba6b511162fead58f2c8b5764ce869ed1118011ac37392522ed16720bbcd"), From 000492c45f8f7ca50d8af5e65ef1cafd5105978e Mon Sep 17 00:00:00 2001 From: David Salami Date: Fri, 15 Apr 2022 08:33:19 +0000 Subject: [PATCH 16/24] impl from proof fot batchproof --- primitives/merkle-mountain-range/src/lib.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/primitives/merkle-mountain-range/src/lib.rs b/primitives/merkle-mountain-range/src/lib.rs index 53704164c8c22..4b28586621067 100644 --- a/primitives/merkle-mountain-range/src/lib.rs +++ b/primitives/merkle-mountain-range/src/lib.rs @@ -25,6 +25,7 @@ use sp_runtime::traits; use sp_std::fmt; #[cfg(not(feature = "std"))] use sp_std::prelude::Vec; +use sp_std::vec; /// A type to describe node position in the MMR (node index). pub type NodeIndex = u64; @@ -379,6 +380,16 @@ impl TryFrom> for Proof { }) } } + +impl From> for BatchProof { + fn from(proof: Proof) -> Self { + BatchProof { + leaf_indices: vec![proof.leaf_index], + leaf_count: proof.leaf_count, + items: proof.items, + } + } +} /// Merkle Mountain Range operation error. #[derive(RuntimeDebug, codec::Encode, codec::Decode, PartialEq, Eq)] pub enum Error { From c862af0c4f628612712d1dad15d90a206ff7fba0 Mon Sep 17 00:00:00 2001 From: David Salami Date: Fri, 15 Apr 2022 08:52:12 +0000 Subject: [PATCH 17/24] minor fix --- bin/node/runtime/src/lib.rs | 12 ++++++------ primitives/merkle-mountain-range/src/lib.rs | 19 ++++++------------- 2 files changed, 12 insertions(+), 19 deletions(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 62c666307d66d..717154133ca2c 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -1795,29 +1795,29 @@ impl_runtime_apis! { mmr::Hash, > for Runtime { fn generate_proof(leaf_index: pallet_mmr::primitives::LeafIndex) - -> Result<(mmr::EncodableOpaqueLeaf, mmr::BatchProof), mmr::Error> + -> Result<(mmr::EncodableOpaqueLeaf, mmr::Proof), mmr::Error> { Mmr::generate_batch_proof(vec![leaf_index]) - .map(|(leaves, proof)| (mmr::EncodableOpaqueLeaf::from_leaf(&leaves[0]), proof)) + .map(|(leaves, proof)| (mmr::EncodableOpaqueLeaf::from_leaf(&leaves[0]), proof.into())) } - fn verify_proof(leaf: mmr::EncodableOpaqueLeaf, proof: mmr::BatchProof) + fn verify_proof(leaf: mmr::EncodableOpaqueLeaf, proof: mmr::Proof) -> Result<(), mmr::Error> { let leaf: mmr::Leaf = leaf .into_opaque_leaf() .try_decode() .ok_or(mmr::Error::Verify)?; - Mmr::verify_leaves(vec![leaf], proof) + Mmr::verify_leaves(vec![leaf], proof.into()) } fn verify_proof_stateless( root: mmr::Hash, leaf: mmr::EncodableOpaqueLeaf, - proof: mmr::BatchProof + proof: mmr::Proof ) -> Result<(), mmr::Error> { let node = mmr::DataOrHash::Data(leaf.into_opaque_leaf()); - pallet_mmr::verify_leaves_proof::(root, vec![node], proof) + pallet_mmr::verify_leaves_proof::(root, vec![node], proof.into()) } fn mmr_root() -> Result { diff --git a/primitives/merkle-mountain-range/src/lib.rs b/primitives/merkle-mountain-range/src/lib.rs index 4b28586621067..a826c46f34983 100644 --- a/primitives/merkle-mountain-range/src/lib.rs +++ b/primitives/merkle-mountain-range/src/lib.rs @@ -363,21 +363,14 @@ pub struct BatchProof { pub items: Vec, } -impl TryFrom> for Proof { - type Error = &'static str; - fn try_from(proof: BatchProof) -> Result { - if proof.leaf_indices.len() > 1 { - return Err("BatchProof can only be converted to Proof for single leaf") - } - Ok(Proof { - leaf_index: proof - .leaf_indices - .get(0) - .ok_or("BatchProof can only be converted to Proof for single leaf")? - .clone(), +impl From> for Proof { + /// Will panic if leaf indices is less than 1 + fn from(proof: BatchProof) -> Self { + Proof { + leaf_index: proof.leaf_indices[0], leaf_count: proof.leaf_count, items: proof.items, - }) + } } } From fdada3315a27e05c1c3d60d88d7350626d0ab153 Mon Sep 17 00:00:00 2001 From: David Salami Date: Fri, 15 Apr 2022 09:02:15 +0000 Subject: [PATCH 18/24] minor fix --- primitives/merkle-mountain-range/src/lib.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/primitives/merkle-mountain-range/src/lib.rs b/primitives/merkle-mountain-range/src/lib.rs index a826c46f34983..47ed6ce92954e 100644 --- a/primitives/merkle-mountain-range/src/lib.rs +++ b/primitives/merkle-mountain-range/src/lib.rs @@ -22,10 +22,9 @@ use sp_debug_derive::RuntimeDebug; use sp_runtime::traits; -use sp_std::fmt; #[cfg(not(feature = "std"))] use sp_std::prelude::Vec; -use sp_std::vec; +use sp_std::{fmt, vec}; /// A type to describe node position in the MMR (node index). pub type NodeIndex = u64; From 9674732f85ed0796d4caf685d457bd408a20002b Mon Sep 17 00:00:00 2001 From: David Salami Date: Mon, 18 Apr 2022 13:14:16 +0100 Subject: [PATCH 19/24] use explicit functions to convert btw batch proof and single proof --- bin/node/runtime/src/lib.rs | 6 +++--- frame/merkle-mountain-range/rpc/src/lib.rs | 2 +- frame/merkle-mountain-range/src/lib.rs | 1 + primitives/merkle-mountain-range/src/lib.rs | 12 +++++++----- 4 files changed, 12 insertions(+), 9 deletions(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 717154133ca2c..ef474ff76efc5 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -1798,7 +1798,7 @@ impl_runtime_apis! { -> Result<(mmr::EncodableOpaqueLeaf, mmr::Proof), mmr::Error> { Mmr::generate_batch_proof(vec![leaf_index]) - .map(|(leaves, proof)| (mmr::EncodableOpaqueLeaf::from_leaf(&leaves[0]), proof.into())) + .map(|(leaves, proof)| (mmr::EncodableOpaqueLeaf::from_leaf(&leaves[0]), mmr::BatchProof::into_single_leaf_proof(proof))) } fn verify_proof(leaf: mmr::EncodableOpaqueLeaf, proof: mmr::Proof) @@ -1808,7 +1808,7 @@ impl_runtime_apis! { .into_opaque_leaf() .try_decode() .ok_or(mmr::Error::Verify)?; - Mmr::verify_leaves(vec![leaf], proof.into()) + Mmr::verify_leaves(vec![leaf], mmr::Proof::into_batch_proof(proof)) } fn verify_proof_stateless( @@ -1817,7 +1817,7 @@ impl_runtime_apis! { proof: mmr::Proof ) -> Result<(), mmr::Error> { let node = mmr::DataOrHash::Data(leaf.into_opaque_leaf()); - pallet_mmr::verify_leaves_proof::(root, vec![node], proof.into()) + pallet_mmr::verify_leaves_proof::(root, vec![node], mmr::Proof::into_batch_proof(proof)) } fn mmr_root() -> Result { diff --git a/frame/merkle-mountain-range/rpc/src/lib.rs b/frame/merkle-mountain-range/rpc/src/lib.rs index 86467bb855c89..3b1848561ff41 100644 --- a/frame/merkle-mountain-range/rpc/src/lib.rs +++ b/frame/merkle-mountain-range/rpc/src/lib.rs @@ -57,7 +57,7 @@ impl LeafProof { } } -/// Retrieved MMR leaf and its proof. +/// Retrieved MMR leaves and their proof. #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct LeafBatchProof { diff --git a/frame/merkle-mountain-range/src/lib.rs b/frame/merkle-mountain-range/src/lib.rs index 46b9cd9723865..d6cf3240692fc 100644 --- a/frame/merkle-mountain-range/src/lib.rs +++ b/frame/merkle-mountain-range/src/lib.rs @@ -277,6 +277,7 @@ impl, I: 'static> Pallet { pub fn mmr_root() -> >::Hash { Self::mmr_root_hash() } + /// Verify MMR proof for given `leaves`. /// /// This method is safe to use within the runtime code. diff --git a/primitives/merkle-mountain-range/src/lib.rs b/primitives/merkle-mountain-range/src/lib.rs index 47ed6ce92954e..82596e308b91c 100644 --- a/primitives/merkle-mountain-range/src/lib.rs +++ b/primitives/merkle-mountain-range/src/lib.rs @@ -362,9 +362,9 @@ pub struct BatchProof { pub items: Vec, } -impl From> for Proof { - /// Will panic if leaf indices is less than 1 - fn from(proof: BatchProof) -> Self { +impl BatchProof { + /// Will panic if leaf indices length is less than 1 + pub fn into_single_leaf_proof(proof: BatchProof) -> Proof { Proof { leaf_index: proof.leaf_indices[0], leaf_count: proof.leaf_count, @@ -373,8 +373,9 @@ impl From> for Proof { } } -impl From> for BatchProof { - fn from(proof: Proof) -> Self { +impl Proof { + /// Converts a single leaf proof into a batch proof + pub fn into_batch_proof(proof: Proof) -> BatchProof { BatchProof { leaf_indices: vec![proof.leaf_index], leaf_count: proof.leaf_count, @@ -448,6 +449,7 @@ sp_api::decl_runtime_apis! { /// Return the on-chain MMR root hash. fn mmr_root() -> Result; + /// Generate MMR proof for a series of leaves under given indices. fn generate_batch_proof(leaf_indices: Vec) -> Result<(Vec, BatchProof), Error>; From 4eb167dafa8fc2f60af57a1c60e40e77f2ee7539 Mon Sep 17 00:00:00 2001 From: David Salami Date: Mon, 18 Apr 2022 13:50:05 +0100 Subject: [PATCH 20/24] minor fix --- frame/merkle-mountain-range/rpc/src/lib.rs | 2 ++ frame/merkle-mountain-range/src/mmr/mmr.rs | 9 +++++++++ 2 files changed, 11 insertions(+) diff --git a/frame/merkle-mountain-range/rpc/src/lib.rs b/frame/merkle-mountain-range/rpc/src/lib.rs index 3b1848561ff41..be1a74450d1f4 100644 --- a/frame/merkle-mountain-range/rpc/src/lib.rs +++ b/frame/merkle-mountain-range/rpc/src/lib.rs @@ -111,6 +111,8 @@ pub trait MmrApi { /// /// Returns the leaves and a proof for these leaves (compact encoding, i.e. hash of /// the leaves). Both parameters are SCALE-encoded. + /// The order of entries in the `leaves` field of the returned struct + /// is the same as the order of the entries in `leaf_indices` supplied #[rpc(name = "mmr_generateBatchProof")] fn generate_batch_proof( &self, diff --git a/frame/merkle-mountain-range/src/mmr/mmr.rs b/frame/merkle-mountain-range/src/mmr/mmr.rs index 5469ec7ec30e2..44e684c1bdcac 100644 --- a/frame/merkle-mountain-range/src/mmr/mmr.rs +++ b/frame/merkle-mountain-range/src/mmr/mmr.rs @@ -41,6 +41,10 @@ where { 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() @@ -97,6 +101,11 @@ where 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() From f1a9860a8f2001e0f46ab97d596cc29f911461ea Mon Sep 17 00:00:00 2001 From: David Salami Date: Wed, 20 Apr 2022 13:54:50 +0100 Subject: [PATCH 21/24] add new variant to mmr error --- primitives/merkle-mountain-range/src/lib.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/primitives/merkle-mountain-range/src/lib.rs b/primitives/merkle-mountain-range/src/lib.rs index 82596e308b91c..7555ba81bf91e 100644 --- a/primitives/merkle-mountain-range/src/lib.rs +++ b/primitives/merkle-mountain-range/src/lib.rs @@ -398,6 +398,8 @@ pub enum Error { Verify, /// Leaf not found in the storage. LeafNotFound, + /// Mmr Pallet not included in runtime + PalletNotIncluded } impl Error { From be0f200fcf66d26b40601e81f306d0479e2f9249 Mon Sep 17 00:00:00 2001 From: David Salami Date: Wed, 20 Apr 2022 13:58:24 +0100 Subject: [PATCH 22/24] fmt --- primitives/merkle-mountain-range/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/primitives/merkle-mountain-range/src/lib.rs b/primitives/merkle-mountain-range/src/lib.rs index 7555ba81bf91e..18ba572a2fe9f 100644 --- a/primitives/merkle-mountain-range/src/lib.rs +++ b/primitives/merkle-mountain-range/src/lib.rs @@ -399,7 +399,7 @@ pub enum Error { /// Leaf not found in the storage. LeafNotFound, /// Mmr Pallet not included in runtime - PalletNotIncluded + PalletNotIncluded, } impl Error { From c099c7e89ccad6ae4439cf83019f992557ecaff3 Mon Sep 17 00:00:00 2001 From: David Salami Date: Tue, 3 May 2022 16:34:17 +0100 Subject: [PATCH 23/24] update conversion to single leaf proof --- bin/node/runtime/src/lib.rs | 9 +++++++-- primitives/merkle-mountain-range/src/lib.rs | 12 +++++++----- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 003db9ac4e598..e43b4669e0ac7 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -1832,8 +1832,13 @@ impl_runtime_apis! { fn generate_proof(leaf_index: pallet_mmr::primitives::LeafIndex) -> Result<(mmr::EncodableOpaqueLeaf, mmr::Proof), mmr::Error> { - Mmr::generate_batch_proof(vec![leaf_index]) - .map(|(leaves, proof)| (mmr::EncodableOpaqueLeaf::from_leaf(&leaves[0]), mmr::BatchProof::into_single_leaf_proof(proof))) + let (leaf, proof) = Mmr::generate_batch_proof(vec![leaf_index]) + .map(|(leaves, proof)| (mmr::EncodableOpaqueLeaf::from_leaf(&leaves[0]), mmr::BatchProof::into_single_leaf_proof(proof)))?; + if let Ok(proof) = proof { + Ok((leaf, proof)) + } else { + Err(mmr::Error::InvalidLeafIndex) + } } fn verify_proof(leaf: mmr::EncodableOpaqueLeaf, proof: mmr::Proof) diff --git a/primitives/merkle-mountain-range/src/lib.rs b/primitives/merkle-mountain-range/src/lib.rs index 752831af8058f..5a339d069062c 100644 --- a/primitives/merkle-mountain-range/src/lib.rs +++ b/primitives/merkle-mountain-range/src/lib.rs @@ -363,13 +363,13 @@ pub struct BatchProof { } impl BatchProof { - /// Will panic if leaf indices length is less than 1 - pub fn into_single_leaf_proof(proof: BatchProof) -> Proof { - Proof { - leaf_index: proof.leaf_indices[0], + /// Converts batch proof to single leaf proof + pub fn into_single_leaf_proof(proof: BatchProof) -> Result, Error> { + Ok(Proof { + leaf_index: *proof.leaf_indices.get(0).ok_or(Error::InvalidLeafIndex)?, leaf_count: proof.leaf_count, items: proof.items, - } + }) } } @@ -400,6 +400,8 @@ pub enum Error { LeafNotFound, /// Mmr Pallet not included in runtime PalletNotIncluded, + /// Cannot find the requested leaf index + InvalidLeafIndex, } impl Error { From e0f00fa99ebcfd0477c41f6111f77f77abe65144 Mon Sep 17 00:00:00 2001 From: Adrian Catangiu Date: Wed, 4 May 2022 12:06:03 +0300 Subject: [PATCH 24/24] fix style nit --- bin/node/runtime/src/lib.rs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index e43b4669e0ac7..660ba7ab86229 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -1832,13 +1832,12 @@ impl_runtime_apis! { fn generate_proof(leaf_index: pallet_mmr::primitives::LeafIndex) -> Result<(mmr::EncodableOpaqueLeaf, mmr::Proof), mmr::Error> { - let (leaf, proof) = Mmr::generate_batch_proof(vec![leaf_index]) - .map(|(leaves, proof)| (mmr::EncodableOpaqueLeaf::from_leaf(&leaves[0]), mmr::BatchProof::into_single_leaf_proof(proof)))?; - if let Ok(proof) = proof { - Ok((leaf, proof)) - } else { - Err(mmr::Error::InvalidLeafIndex) - } + Mmr::generate_batch_proof(vec![leaf_index]).and_then(|(leaves, proof)| + Ok(( + mmr::EncodableOpaqueLeaf::from_leaf(&leaves[0]), + mmr::BatchProof::into_single_leaf_proof(proof)? + )) + ) } fn verify_proof(leaf: mmr::EncodableOpaqueLeaf, proof: mmr::Proof)