From f4b73bd1823181b2236d1da50dc21f6f7d9d9ec6 Mon Sep 17 00:00:00 2001 From: Serban Iorga Date: Mon, 13 May 2024 11:42:26 +0300 Subject: [PATCH] Add generate and verify logic for `AncestryProof` (#4430) Extracting the logic for generating and verifying ancestry proofs from this PR: https://github.com/paritytech/polkadot-sdk/pull/1903 with small adjustments @Lederstrumpf I added you as a co-author to each commit. Please let me know if you want me to also associate an e-mail address with your name --- Cargo.lock | 13 ++- bridges/primitives/beefy/src/lib.rs | 2 +- polkadot/node/service/src/fake_runtime_api.rs | 6 +- polkadot/runtime/rococo/src/lib.rs | 6 +- polkadot/runtime/test-runtime/src/lib.rs | 6 +- polkadot/runtime/westend/src/lib.rs | 6 +- substrate/bin/node/runtime/src/lib.rs | 6 +- .../merkle-mountain-range/rpc/src/lib.rs | 16 +-- .../merkle-mountain-range/src/test_utils.rs | 6 +- .../frame/merkle-mountain-range/src/lib.rs | 48 ++++++-- .../merkle-mountain-range/src/mmr/mmr.rs | 85 ++++++++++++-- .../merkle-mountain-range/src/mmr/storage.rs | 18 ++- .../frame/merkle-mountain-range/src/tests.rs | 104 ++++++++++-------- .../merkle-mountain-range/Cargo.toml | 2 +- .../merkle-mountain-range/src/lib.rs | 32 ++++-- 15 files changed, 251 insertions(+), 105 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2c13fcc0e32d..d787d2fe08c2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2602,6 +2602,15 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "ckb-merkle-mountain-range" +version = "0.6.0" +source = "git+https://github.com/paritytech/merkle-mountain-range.git?branch=master#537f0e3f67c5adf7afff0800bbb81f02f17570a1" +dependencies = [ + "cfg-if", + "itertools 0.10.5", +] + [[package]] name = "clang-sys" version = "1.6.1" @@ -9759,7 +9768,7 @@ dependencies = [ "bp-beefy", "bp-runtime", "bp-test-utils", - "ckb-merkle-mountain-range", + "ckb-merkle-mountain-range 0.5.2", "frame-support", "frame-system", "log", @@ -19545,7 +19554,7 @@ name = "sp-mmr-primitives" version = "26.0.0" dependencies = [ "array-bytes", - "ckb-merkle-mountain-range", + "ckb-merkle-mountain-range 0.6.0", "log", "parity-scale-codec", "scale-info", diff --git a/bridges/primitives/beefy/src/lib.rs b/bridges/primitives/beefy/src/lib.rs index 0441781e79a6..2494706818ef 100644 --- a/bridges/primitives/beefy/src/lib.rs +++ b/bridges/primitives/beefy/src/lib.rs @@ -22,7 +22,7 @@ pub use binary_merkle_tree::merkle_root; pub use pallet_beefy_mmr::BeefyEcdsaToEthereum; pub use pallet_mmr::{ - primitives::{DataOrHash as MmrDataOrHash, Proof as MmrProof}, + primitives::{DataOrHash as MmrDataOrHash, LeafProof as MmrProof}, verify_leaves_proof as verify_mmr_leaves_proof, }; pub use sp_consensus_beefy::{ diff --git a/polkadot/node/service/src/fake_runtime_api.rs b/polkadot/node/service/src/fake_runtime_api.rs index 5c889552a6ae..03c4836020d9 100644 --- a/polkadot/node/service/src/fake_runtime_api.rs +++ b/polkadot/node/service/src/fake_runtime_api.rs @@ -272,11 +272,11 @@ sp_api::impl_runtime_apis! { fn generate_proof( _: Vec, _: Option, - ) -> Result<(Vec, sp_mmr_primitives::Proof), sp_mmr_primitives::Error> { + ) -> Result<(Vec, sp_mmr_primitives::LeafProof), sp_mmr_primitives::Error> { unimplemented!() } - fn verify_proof(_: Vec, _: sp_mmr_primitives::Proof) + fn verify_proof(_: Vec, _: sp_mmr_primitives::LeafProof) -> Result<(), sp_mmr_primitives::Error> { unimplemented!() @@ -285,7 +285,7 @@ sp_api::impl_runtime_apis! { fn verify_proof_stateless( _: Hash, _: Vec, - _: sp_mmr_primitives::Proof + _: sp_mmr_primitives::LeafProof ) -> Result<(), sp_mmr_primitives::Error> { unimplemented!() } diff --git a/polkadot/runtime/rococo/src/lib.rs b/polkadot/runtime/rococo/src/lib.rs index 22e6183e5946..c22d5c39b233 100644 --- a/polkadot/runtime/rococo/src/lib.rs +++ b/polkadot/runtime/rococo/src/lib.rs @@ -2134,7 +2134,7 @@ sp_api::impl_runtime_apis! { fn generate_proof( block_numbers: Vec, best_known_block_number: Option, - ) -> Result<(Vec, mmr::Proof), mmr::Error> { + ) -> Result<(Vec, mmr::LeafProof), mmr::Error> { Mmr::generate_proof(block_numbers, best_known_block_number).map( |(leaves, proof)| { ( @@ -2148,7 +2148,7 @@ sp_api::impl_runtime_apis! { ) } - fn verify_proof(leaves: Vec, proof: mmr::Proof) + fn verify_proof(leaves: Vec, proof: mmr::LeafProof) -> Result<(), mmr::Error> { let leaves = leaves.into_iter().map(|leaf| @@ -2161,7 +2161,7 @@ sp_api::impl_runtime_apis! { fn verify_proof_stateless( root: mmr::Hash, leaves: Vec, - proof: mmr::Proof + proof: mmr::LeafProof ) -> 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) diff --git a/polkadot/runtime/test-runtime/src/lib.rs b/polkadot/runtime/test-runtime/src/lib.rs index 0509ba382b2e..9eb0fcca6678 100644 --- a/polkadot/runtime/test-runtime/src/lib.rs +++ b/polkadot/runtime/test-runtime/src/lib.rs @@ -1044,11 +1044,11 @@ sp_api::impl_runtime_apis! { fn generate_proof( _block_numbers: Vec, _best_known_block_number: Option, - ) -> Result<(Vec, mmr::Proof), mmr::Error> { + ) -> Result<(Vec, mmr::LeafProof), mmr::Error> { Err(mmr::Error::PalletNotIncluded) } - fn verify_proof(_leaves: Vec, _proof: mmr::Proof) + fn verify_proof(_leaves: Vec, _proof: mmr::LeafProof) -> Result<(), mmr::Error> { Err(mmr::Error::PalletNotIncluded) @@ -1057,7 +1057,7 @@ sp_api::impl_runtime_apis! { fn verify_proof_stateless( _root: Hash, _leaves: Vec, - _proof: mmr::Proof + _proof: mmr::LeafProof ) -> Result<(), mmr::Error> { Err(mmr::Error::PalletNotIncluded) } diff --git a/polkadot/runtime/westend/src/lib.rs b/polkadot/runtime/westend/src/lib.rs index cae12ab49c02..b62c6d08201c 100644 --- a/polkadot/runtime/westend/src/lib.rs +++ b/polkadot/runtime/westend/src/lib.rs @@ -2013,7 +2013,7 @@ sp_api::impl_runtime_apis! { fn generate_proof( block_numbers: Vec, best_known_block_number: Option, - ) -> Result<(Vec, mmr::Proof), mmr::Error> { + ) -> Result<(Vec, mmr::LeafProof), mmr::Error> { Mmr::generate_proof(block_numbers, best_known_block_number).map( |(leaves, proof)| { ( @@ -2027,7 +2027,7 @@ sp_api::impl_runtime_apis! { ) } - fn verify_proof(leaves: Vec, proof: mmr::Proof) + fn verify_proof(leaves: Vec, proof: mmr::LeafProof) -> Result<(), mmr::Error> { let leaves = leaves.into_iter().map(|leaf| @@ -2040,7 +2040,7 @@ sp_api::impl_runtime_apis! { fn verify_proof_stateless( root: mmr::Hash, leaves: Vec, - proof: mmr::Proof + proof: mmr::LeafProof ) -> 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) diff --git a/substrate/bin/node/runtime/src/lib.rs b/substrate/bin/node/runtime/src/lib.rs index 18b0d0c31a4d..017ee9100f9e 100644 --- a/substrate/bin/node/runtime/src/lib.rs +++ b/substrate/bin/node/runtime/src/lib.rs @@ -3094,7 +3094,7 @@ impl_runtime_apis! { fn generate_proof( block_numbers: Vec, best_known_block_number: Option, - ) -> Result<(Vec, mmr::Proof), mmr::Error> { + ) -> Result<(Vec, mmr::LeafProof), mmr::Error> { Mmr::generate_proof(block_numbers, best_known_block_number).map( |(leaves, proof)| { ( @@ -3108,7 +3108,7 @@ impl_runtime_apis! { ) } - fn verify_proof(leaves: Vec, proof: mmr::Proof) + fn verify_proof(leaves: Vec, proof: mmr::LeafProof) -> Result<(), mmr::Error> { let leaves = leaves.into_iter().map(|leaf| @@ -3121,7 +3121,7 @@ impl_runtime_apis! { fn verify_proof_stateless( root: mmr::Hash, leaves: Vec, - proof: mmr::Proof + proof: mmr::LeafProof ) -> 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) diff --git a/substrate/client/merkle-mountain-range/rpc/src/lib.rs b/substrate/client/merkle-mountain-range/rpc/src/lib.rs index b4da9848de54..41e73a5b8d75 100644 --- a/substrate/client/merkle-mountain-range/rpc/src/lib.rs +++ b/substrate/client/merkle-mountain-range/rpc/src/lib.rs @@ -36,7 +36,7 @@ use sp_core::{ offchain::{storage::OffchainDb, OffchainDbExt, OffchainStorage}, Bytes, }; -use sp_mmr_primitives::{Error as MmrError, Proof}; +use sp_mmr_primitives::{Error as MmrError, LeafProof}; use sp_runtime::traits::{Block as BlockT, NumberFor}; pub use sp_mmr_primitives::MmrApi as MmrRuntimeApi; @@ -52,17 +52,17 @@ pub struct LeavesProof { pub block_hash: BlockHash, /// SCALE-encoded vector of `LeafData`. pub leaves: Bytes, - /// SCALE-encoded proof data. See [sp_mmr_primitives::Proof]. + /// SCALE-encoded proof data. See [sp_mmr_primitives::LeafProof]. pub proof: Bytes, } impl LeavesProof { /// Create new `LeavesProof` from a given vector of `Leaf` and a - /// [sp_mmr_primitives::Proof]. + /// [sp_mmr_primitives::LeafProof]. pub fn new( block_hash: BlockHash, leaves: Vec, - proof: Proof, + proof: LeafProof, ) -> Self where Leaf: Encode, @@ -258,7 +258,7 @@ mod tests { fn should_serialize_leaf_proof() { // given let leaf = vec![1_u8, 2, 3, 4]; - let proof = Proof { + let proof = LeafProof { leaf_indices: vec![1], leaf_count: 9, items: vec![H256::repeat_byte(1), H256::repeat_byte(2)], @@ -281,7 +281,7 @@ mod tests { // given let leaf_a = vec![1_u8, 2, 3, 4]; let leaf_b = vec![2_u8, 2, 3, 4]; - let proof = Proof { + let proof = LeafProof { leaf_indices: vec![1, 2], leaf_count: 9, items: vec![H256::repeat_byte(1), H256::repeat_byte(2)], @@ -306,7 +306,7 @@ mod tests { block_hash: H256::repeat_byte(0), leaves: Bytes(vec![vec![1_u8, 2, 3, 4]].encode()), proof: Bytes( - Proof { + LeafProof { leaf_indices: vec![1], leaf_count: 9, items: vec![H256::repeat_byte(1), H256::repeat_byte(2)], @@ -333,7 +333,7 @@ mod tests { block_hash: H256::repeat_byte(0), leaves: Bytes(vec![vec![1_u8, 2, 3, 4], vec![2_u8, 2, 3, 4]].encode()), proof: Bytes( - Proof { + LeafProof { leaf_indices: vec![1, 2], leaf_count: 9, items: vec![H256::repeat_byte(1), H256::repeat_byte(2)], diff --git a/substrate/client/merkle-mountain-range/src/test_utils.rs b/substrate/client/merkle-mountain-range/src/test_utils.rs index 5775b4cfe67c..fcf9fa25b593 100644 --- a/substrate/client/merkle-mountain-range/src/test_utils.rs +++ b/substrate/client/merkle-mountain-range/src/test_utils.rs @@ -309,11 +309,11 @@ sp_api::mock_impl_runtime_apis! { &self, _block_numbers: Vec, _best_known_block_number: Option, - ) -> Result<(Vec, mmr::Proof), mmr::Error> { + ) -> Result<(Vec, mmr::LeafProof), mmr::Error> { Err(mmr::Error::PalletNotIncluded) } - fn verify_proof(_leaves: Vec, _proof: mmr::Proof) + fn verify_proof(_leaves: Vec, _proof: mmr::LeafProof) -> Result<(), mmr::Error> { Err(mmr::Error::PalletNotIncluded) @@ -322,7 +322,7 @@ sp_api::mock_impl_runtime_apis! { fn verify_proof_stateless( _root: MmrHash, _leaves: Vec, - _proof: mmr::Proof + _proof: mmr::LeafProof ) -> Result<(), mmr::Error> { Err(mmr::Error::PalletNotIncluded) } diff --git a/substrate/frame/merkle-mountain-range/src/lib.rs b/substrate/frame/merkle-mountain-range/src/lib.rs index e2b40974579e..a86443f2e011 100644 --- a/substrate/frame/merkle-mountain-range/src/lib.rs +++ b/substrate/frame/merkle-mountain-range/src/lib.rs @@ -260,15 +260,15 @@ pub mod pallet { /// Stateless MMR proof verification for batch of leaves. /// -/// This function can be used to verify received MMR [primitives::Proof] (`proof`) +/// This function can be used to verify received MMR [primitives::LeafProof] (`proof`) /// for given leaves set (`leaves`) against a known MMR root hash (`root`). /// Note, the leaves should be sorted such that corresponding leaves and leaf indices have the /// same position in both the `leaves` vector and the `leaf_indices` vector contained in the -/// [primitives::Proof]. +/// [primitives::LeafProof]. pub fn verify_leaves_proof( root: H::Output, leaves: Vec>, - proof: primitives::Proof, + proof: primitives::LeafProof, ) -> Result<(), primitives::Error> where H: traits::Hash, @@ -342,7 +342,7 @@ impl, I: 'static> Pallet { pub fn generate_proof( block_numbers: Vec>, best_known_block_number: Option>, - ) -> Result<(Vec>, primitives::Proof>), primitives::Error> { + ) -> Result<(Vec>, primitives::LeafProof>), primitives::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()); @@ -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. @@ -375,7 +370,7 @@ impl, I: 'static> Pallet { /// or the proof is invalid. pub fn verify_leaves( leaves: Vec>, - proof: primitives::Proof>, + proof: primitives::LeafProof>, ) -> Result<(), primitives::Error> { if proof.leaf_count > NumberOfLeaves::::get() || proof.leaf_count == 0 || @@ -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 aeb3e7ea6641..5efc172d1e93 100644 --- a/substrate/frame/merkle-mountain-range/src/mmr/mmr.rs +++ b/substrate/frame/merkle-mountain-range/src/mmr/mmr.rs @@ -23,17 +23,17 @@ use crate::{ primitives::{self, Error, NodeIndex}, Config, HashOf, HashingOf, }; -use sp_mmr_primitives::{mmr_lib, 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. /// 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::Proof] +/// [primitives::LeafProof] pub fn verify_leaves_proof( root: H::Output, leaves: Vec>, - proof: primitives::Proof, + proof: primitives::LeafProof, ) -> Result where H: sp_runtime::traits::Hash, @@ -69,7 +69,8 @@ where T: Config, I: 'static, L: primitives::FullLeaf, - Storage: mmr_lib::MMRStore>, + Storage: + MMRStoreReadOps> + mmr_lib::MMRStoreWriteOps>, { mmr: mmr_lib::MMR, Hasher, L>, Storage>, leaves: NodeIndex, @@ -80,7 +81,8 @@ where T: Config, I: 'static, L: primitives::FullLeaf, - Storage: mmr_lib::MMRStore>, + Storage: + MMRStoreReadOps> + mmr_lib::MMRStoreWriteOps>, { /// Create a pointer to an existing MMR with given number of leaves. pub fn new(leaves: NodeIndex) -> Self { @@ -91,11 +93,11 @@ where /// 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::Proof] + /// [primitives::LeafProof] pub fn verify_leaves_proof( &self, leaves: Vec, - proof: primitives::Proof>, + proof: primitives::LeafProof>, ) -> Result { let p = mmr_lib::MerkleProof::, Hasher, L>>::new( self.mmr.mmr_size(), @@ -117,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 { @@ -145,7 +185,7 @@ where /// Commit the changes to underlying storage, return current number of leaves and /// calculate the new MMR's root hash. - pub fn finalize(self) -> Result<(NodeIndex, HashOf), Error> { + pub fn finalize(mut self) -> Result<(NodeIndex, HashOf), Error> { let root = self.mmr.get_root().map_err(|e| Error::GetRoot.log_error(e))?; self.mmr.commit().map_err(|e| Error::Commit.log_error(e))?; Ok((self.leaves, root.hash())) @@ -166,7 +206,7 @@ where pub fn generate_proof( &self, leaf_indices: Vec, - ) -> Result<(Vec, primitives::Proof>), Error> { + ) -> Result<(Vec, primitives::LeafProof>), Error> { let positions = leaf_indices .iter() .map(|index| mmr_lib::leaf_index_to_pos(*index)) @@ -174,7 +214,7 @@ where let store = >::default(); let leaves = positions .iter() - .map(|pos| match mmr_lib::MMRStore::get_elem(&store, *pos) { + .map(|pos| match store.get_elem(*pos) { Ok(Some(Node::Data(leaf))) => Ok(leaf), e => Err(Error::LeafNotFound.log_debug(e)), }) @@ -184,11 +224,34 @@ where self.mmr .gen_proof(positions) .map_err(|e| Error::GenerateProof.log_error(e)) - .map(|p| primitives::Proof { + .map(|p| primitives::LeafProof { leaf_indices, leaf_count, items: p.proof_items().iter().map(|x| x.hash()).collect(), }) .map(|p| (leaves, p)) } + + pub fn generate_ancestry_proof( + &self, + prev_leaf_count: LeafIndex, + ) -> Result>, 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/mmr/storage.rs b/substrate/frame/merkle-mountain-range/src/mmr/storage.rs index f2acc35a137f..6848b8f1b990 100644 --- a/substrate/frame/merkle-mountain-range/src/mmr/storage.rs +++ b/substrate/frame/merkle-mountain-range/src/mmr/storage.rs @@ -60,7 +60,7 @@ impl Default for Storage { } } -impl mmr_lib::MMRStore> for Storage +impl mmr_lib::MMRStoreReadOps> for Storage where T: Config, I: 'static, @@ -98,13 +98,20 @@ where Ok(sp_io::offchain::local_storage_get(StorageKind::PERSISTENT, &temp_key) .and_then(|v| codec::Decode::decode(&mut &*v).ok())) } +} +impl mmr_lib::MMRStoreWriteOps> for Storage +where + T: Config, + I: 'static, + L: primitives::FullLeaf + codec::Decode, +{ fn append(&mut self, _: NodeIndex, _: Vec>) -> mmr_lib::Result<()> { panic!("MMR must not be altered in the off-chain context.") } } -impl mmr_lib::MMRStore> for Storage +impl mmr_lib::MMRStoreReadOps> for Storage where T: Config, I: 'static, @@ -113,7 +120,14 @@ where fn get_elem(&self, pos: NodeIndex) -> mmr_lib::Result>> { Ok(Nodes::::get(pos).map(Node::Hash)) } +} +impl mmr_lib::MMRStoreWriteOps> for Storage +where + T: Config, + I: 'static, + L: primitives::FullLeaf, +{ fn append(&mut self, pos: NodeIndex, elems: Vec>) -> mmr_lib::Result<()> { if elems.is_empty() { return Ok(()) diff --git a/substrate/frame/merkle-mountain-range/src/tests.rs b/substrate/frame/merkle-mountain-range/src/tests.rs index 88de7511c9f2..f8cfcb4e2c28 100644 --- a/substrate/frame/merkle-mountain-range/src/tests.rs +++ b/substrate/frame/merkle-mountain-range/src/tests.rs @@ -22,7 +22,7 @@ use sp_core::{ offchain::{testing::TestOffchainExt, OffchainDbExt, OffchainWorkerExt}, H256, }; -use sp_mmr_primitives::{mmr_lib::helper, utils, Compact, Proof}; +use sp_mmr_primitives::{mmr_lib::helper, utils, Compact, LeafProof}; use sp_runtime::BuildStorage; pub(crate) fn new_test_ext() -> sp_io::TestExternalities { @@ -283,7 +283,7 @@ fn should_generate_proofs_correctly() { proofs[0], ( vec![Compact::new(((0, H256::repeat_byte(1)).into(), LeafData::new(1).into(),))], - Proof { + LeafProof { leaf_indices: vec![0], leaf_count: 7, items: vec![ @@ -298,7 +298,7 @@ fn should_generate_proofs_correctly() { historical_proofs[0][0], ( vec![Compact::new(((0, H256::repeat_byte(1)).into(), LeafData::new(1).into(),))], - Proof { leaf_indices: vec![0], leaf_count: 1, items: vec![] } + LeafProof { leaf_indices: vec![0], leaf_count: 1, items: vec![] } ) ); @@ -314,7 +314,7 @@ fn should_generate_proofs_correctly() { proofs[2], ( vec![Compact::new(((2, H256::repeat_byte(3)).into(), LeafData::new(3).into(),))], - Proof { + LeafProof { leaf_indices: vec![2], leaf_count: 7, items: vec![ @@ -334,7 +334,7 @@ fn should_generate_proofs_correctly() { historical_proofs[2][0], ( vec![Compact::new(((2, H256::repeat_byte(3)).into(), LeafData::new(3).into(),))], - Proof { + LeafProof { leaf_indices: vec![2], leaf_count: 3, items: vec![hex( @@ -354,7 +354,7 @@ fn should_generate_proofs_correctly() { historical_proofs[2][2], ( vec![Compact::new(((2, H256::repeat_byte(3)).into(), LeafData::new(3).into(),))], - Proof { + LeafProof { leaf_indices: vec![2], leaf_count: 5, items: vec![ @@ -372,7 +372,7 @@ fn should_generate_proofs_correctly() { ( // NOTE: the leaf index is equivalent to the block number(in this case 5) - 1 vec![Compact::new(((4, H256::repeat_byte(5)).into(), LeafData::new(5).into(),))], - Proof { + LeafProof { leaf_indices: vec![4], leaf_count: 7, items: vec![ @@ -387,7 +387,7 @@ fn should_generate_proofs_correctly() { historical_proofs[4][0], ( vec![Compact::new(((4, H256::repeat_byte(5)).into(), LeafData::new(5).into(),))], - Proof { + LeafProof { leaf_indices: vec![4], leaf_count: 5, items: vec![hex( @@ -402,7 +402,7 @@ fn should_generate_proofs_correctly() { proofs[6], ( vec![Compact::new(((6, H256::repeat_byte(7)).into(), LeafData::new(7).into(),))], - Proof { + LeafProof { leaf_indices: vec![6], leaf_count: 7, items: vec![ @@ -433,7 +433,7 @@ fn should_generate_batch_proof_correctly() { // then assert_eq!( proof, - Proof { + LeafProof { // the leaf indices are equivalent to the above specified block numbers - 1. leaf_indices: vec![0, 4, 5], leaf_count: 7, @@ -451,7 +451,7 @@ fn should_generate_batch_proof_correctly() { // then assert_eq!( historical_proof, - Proof { + LeafProof { leaf_indices: vec![0, 4, 5], leaf_count: 6, items: vec![ @@ -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/Cargo.toml b/substrate/primitives/merkle-mountain-range/Cargo.toml index b97cef138ed4..65d8bd79e5af 100644 --- a/substrate/primitives/merkle-mountain-range/Cargo.toml +++ b/substrate/primitives/merkle-mountain-range/Cargo.toml @@ -18,7 +18,7 @@ targets = ["x86_64-unknown-linux-gnu"] codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false } scale-info = { version = "2.11.1", default-features = false, features = ["derive"] } log = { workspace = true } -mmr-lib = { package = "ckb-merkle-mountain-range", version = "0.5.2", default-features = false } +mmr-lib = { package = "ckb-merkle-mountain-range", git = "https://github.com/paritytech/merkle-mountain-range.git", branch = "master", default-features = false } serde = { features = ["alloc", "derive"], optional = true, workspace = true } sp-api = { path = "../api", default-features = false } sp-core = { path = "../core", default-features = false } diff --git a/substrate/primitives/merkle-mountain-range/src/lib.rs b/substrate/primitives/merkle-mountain-range/src/lib.rs index c76d66bb08ea..3740047e0278 100644 --- a/substrate/primitives/merkle-mountain-range/src/lib.rs +++ b/substrate/primitives/merkle-mountain-range/src/lib.rs @@ -352,15 +352,29 @@ impl_leaf_data_for_tuple!(A:0, B:1, C:2, D:3, E:4); /// An MMR proof data for a group of leaves. #[derive(codec::Encode, codec::Decode, RuntimeDebug, Clone, PartialEq, Eq, TypeInfo)] -pub struct Proof { +pub struct LeafProof { /// 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). + /// 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)] @@ -437,14 +451,14 @@ sp_api::decl_runtime_apis! { fn generate_proof( block_numbers: Vec, best_known_block_number: Option - ) -> Result<(Vec, Proof), Error>; + ) -> Result<(Vec, LeafProof), 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. /// 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] - fn verify_proof(leaves: Vec, proof: Proof) -> Result<(), Error>; + /// same position in both the `leaves` vector and the `leaf_indices` vector contained in the [LeafProof] + fn verify_proof(leaves: Vec, proof: LeafProof) -> Result<(), Error>; /// Verify MMR proof against given root hash for a batch of leaves. /// @@ -452,8 +466,8 @@ sp_api::decl_runtime_apis! { /// proof is verified against given MMR root hash. /// /// 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] - fn verify_proof_stateless(root: Hash, leaves: Vec, proof: Proof) + /// same position in both the `leaves` vector and the `leaf_indices` vector contained in the [LeafProof] + fn verify_proof_stateless(root: Hash, leaves: Vec, proof: LeafProof) -> Result<(), Error>; } } @@ -472,12 +486,12 @@ mod tests { type Test = DataOrHash; type TestCompact = Compact; - type TestProof = Proof<::Output>; + type TestProof = LeafProof<::Output>; #[test] fn should_encode_decode_proof() { // given - let proof: TestProof = Proof { + let proof: TestProof = LeafProof { leaf_indices: vec![5], leaf_count: 10, items: vec![