diff --git a/Cargo.lock b/Cargo.lock index d65411de0125c..a959bc42def38 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5667,6 +5667,7 @@ dependencies = [ name = "pallet-mmr-rpc" version = "3.0.0" dependencies = [ + "anyhow", "jsonrpsee", "parity-scale-codec", "serde", @@ -9707,6 +9708,7 @@ dependencies = [ "sp-debug-derive", "sp-runtime", "sp-std", + "thiserror", ] [[package]] diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index d94a31422be8b..270386be2bbd3 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -2055,59 +2055,15 @@ impl_runtime_apis! { mmr::Hash, BlockNumber, > for Runtime { - fn generate_proof(block_number: BlockNumber) - -> Result<(mmr::EncodableOpaqueLeaf, mmr::Proof), mmr::Error> - { - Mmr::generate_batch_proof(vec![block_number]).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) - -> Result<(), mmr::Error> - { - let leaf: mmr::Leaf = leaf - .into_opaque_leaf() - .try_decode() - .ok_or(mmr::Error::Verify)?; - Mmr::verify_leaves(vec![leaf], mmr::Proof::into_batch_proof(proof)) - } - - fn verify_proof_stateless( - root: mmr::Hash, - leaf: mmr::EncodableOpaqueLeaf, - proof: mmr::Proof - ) -> Result<(), mmr::Error> { - let node = mmr::DataOrHash::Data(leaf.into_opaque_leaf()); - pallet_mmr::verify_leaves_proof::(root, vec![node], mmr::Proof::into_batch_proof(proof)) - } - fn mmr_root() -> Result { Ok(Mmr::mmr_root()) } - fn generate_batch_proof( - block_numbers: Vec, - ) -> Result<(Vec, mmr::BatchProof), mmr::Error> { - Mmr::generate_batch_proof(block_numbers).map(|(leaves, proof)| { - ( - leaves - .into_iter() - .map(|leaf| mmr::EncodableOpaqueLeaf::from_leaf(&leaf)) - .collect(), - proof, - ) - }) - } - - fn generate_historical_batch_proof( + fn generate_proof( block_numbers: Vec, - best_known_block_number: BlockNumber, - ) -> Result<(Vec, mmr::BatchProof), mmr::Error> { - Mmr::generate_historical_batch_proof(block_numbers, best_known_block_number).map( + best_known_block_number: Option, + ) -> Result<(Vec, mmr::Proof), mmr::Error> { + Mmr::generate_proof(block_numbers, best_known_block_number).map( |(leaves, proof)| { ( leaves @@ -2120,7 +2076,7 @@ impl_runtime_apis! { ) } - fn verify_batch_proof(leaves: Vec, proof: mmr::BatchProof) + fn verify_proof(leaves: Vec, proof: mmr::Proof) -> Result<(), mmr::Error> { let leaves = leaves.into_iter().map(|leaf| @@ -2130,10 +2086,10 @@ impl_runtime_apis! { Mmr::verify_leaves(leaves, proof) } - fn verify_batch_proof_stateless( + fn verify_proof_stateless( root: mmr::Hash, leaves: Vec, - proof: mmr::BatchProof + proof: mmr::Proof ) -> 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/client/beefy/src/tests.rs b/client/beefy/src/tests.rs index 2f0306209071e..1d5da4aaefba3 100644 --- a/client/beefy/src/tests.rs +++ b/client/beefy/src/tests.rs @@ -43,7 +43,7 @@ use beefy_primitives::{ KEY_TYPE as BeefyKeyType, }; use sc_network::{config::RequestResponseConfig, ProtocolName}; -use sp_mmr_primitives::{BatchProof, EncodableOpaqueLeaf, Error as MmrError, MmrApi, Proof}; +use sp_mmr_primitives::{EncodableOpaqueLeaf, Error as MmrError, MmrApi, Proof}; use sp_api::{ApiRef, ProvideRuntimeApi}; use sp_consensus::BlockOrigin; @@ -246,47 +246,25 @@ macro_rules! create_test_api { } impl MmrApi> for RuntimeApi { - fn generate_proof(_block_number: u64) - -> Result<(EncodableOpaqueLeaf, Proof), MmrError> { - unimplemented!() - } - - fn verify_proof(_leaf: EncodableOpaqueLeaf, _proof: Proof) - -> Result<(), MmrError> { - unimplemented!() - } - - fn verify_proof_stateless( - _root: MmrRootHash, - _leaf: EncodableOpaqueLeaf, - _proof: Proof - ) -> Result<(), MmrError> { - unimplemented!() - } - fn mmr_root() -> Result { Ok($mmr_root) } - fn generate_batch_proof(_block_numbers: Vec) -> Result<(Vec, BatchProof), MmrError> { - unimplemented!() - } - - fn generate_historical_batch_proof( + fn generate_proof( _block_numbers: Vec, - _best_known_block_number: u64 - ) -> Result<(Vec, BatchProof), MmrError> { + _best_known_block_number: Option + ) -> Result<(Vec, Proof), MmrError> { unimplemented!() } - fn verify_batch_proof(_leaves: Vec, _proof: BatchProof) -> Result<(), MmrError> { + fn verify_proof(_leaves: Vec, _proof: Proof) -> Result<(), MmrError> { unimplemented!() } - fn verify_batch_proof_stateless( + fn verify_proof_stateless( _root: MmrRootHash, _leaves: Vec, - _proof: BatchProof + _proof: Proof ) -> Result<(), MmrError> { unimplemented!() } diff --git a/frame/beefy-mmr/primitives/src/lib.rs b/frame/beefy-mmr/primitives/src/lib.rs index f56be8bcafe5b..f88fb89acaaab 100644 --- a/frame/beefy-mmr/primitives/src/lib.rs +++ b/frame/beefy-mmr/primitives/src/lib.rs @@ -29,7 +29,7 @@ //! Inner nodes are created by concatenating child hashes and hashing again. The implementation //! does not perform any sorting of the input data (leaves) nor when inner nodes are created. //! -//! If the number of leaves is not even, last leave (hash of) is promoted to the upper layer. +//! If the number of leaves is not even, last leaf (hash of) is promoted to the upper layer. pub use sp_runtime::traits::Keccak256; use sp_runtime::{app_crypto::sp_core, sp_std, traits::Hash as HashT}; diff --git a/frame/merkle-mountain-range/rpc/Cargo.toml b/frame/merkle-mountain-range/rpc/Cargo.toml index 3da6678a39bf2..eb2e1e8b53d9e 100644 --- a/frame/merkle-mountain-range/rpc/Cargo.toml +++ b/frame/merkle-mountain-range/rpc/Cargo.toml @@ -21,6 +21,7 @@ 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", path = "../../../primitives/merkle-mountain-range" } sp-runtime = { version = "6.0.0", path = "../../../primitives/runtime" } +anyhow = "1" [dev-dependencies] serde_json = "1.0.85" diff --git a/frame/merkle-mountain-range/rpc/src/lib.rs b/frame/merkle-mountain-range/rpc/src/lib.rs index ffc7ac2da56bf..8476d82f3e70d 100644 --- a/frame/merkle-mountain-range/rpc/src/lib.rs +++ b/frame/merkle-mountain-range/rpc/src/lib.rs @@ -22,7 +22,7 @@ use std::{marker::PhantomData, sync::Arc}; -use codec::{Codec, Encode}; +use codec::{Codec, Decode, Encode}; use jsonrpsee::{ core::{async_trait, RpcResult}, proc_macros::rpc, @@ -33,58 +33,33 @@ use serde::{Deserialize, Serialize}; use sp_api::{NumberFor, ProvideRuntimeApi}; use sp_blockchain::HeaderBackend; use sp_core::Bytes; -use sp_mmr_primitives::{BatchProof, Error as MmrError, Proof}; +use sp_mmr_primitives::{Error as MmrError, Proof}; use sp_runtime::{generic::BlockId, traits::Block as BlockT}; pub use sp_mmr_primitives::MmrApi as MmrRuntimeApi; const RUNTIME_ERROR: i32 = 8000; const MMR_ERROR: i32 = 8010; -const LEAF_NOT_FOUND_ERROR: i32 = MMR_ERROR + 1; -const GENERATE_PROOF_ERROR: i32 = MMR_ERROR + 2; - -/// Retrieved MMR leaf and its proof. -#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] -#[serde(rename_all = "camelCase")] -pub struct LeafProof { - /// Block hash the proof was generated for. - pub block_hash: BlockHash, - /// SCALE-encoded leaf data. - pub leaf: Bytes, - /// 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: Proof) -> Self - where - Leaf: Encode, - MmrHash: Encode, - { - Self { block_hash, leaf: Bytes(leaf.encode()), proof: Bytes(proof.encode()) } - } -} /// Retrieved MMR leaves and their proof. #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] #[serde(rename_all = "camelCase")] -pub struct LeafBatchProof { +pub struct LeavesProof { /// Block hash the proof was generated for. pub block_hash: BlockHash, /// SCALE-encoded vector of `LeafData`. pub leaves: Bytes, - /// SCALE-encoded proof data. See [sp_mmr_primitives::BatchProof]. + /// SCALE-encoded proof data. See [sp_mmr_primitives::Proof]. pub proof: Bytes, } -impl LeafBatchProof { - /// Create new `LeafBatchProof` from a given vector of `Leaf` and a - /// [sp_mmr_primitives::BatchProof]. +impl LeavesProof { + /// Create new `LeavesProof` from a given vector of `Leaf` and a + /// [sp_mmr_primitives::Proof]. pub fn new( block_hash: BlockHash, leaves: Vec, - proof: BatchProof, + proof: Proof, ) -> Self where Leaf: Encode, @@ -96,63 +71,59 @@ impl LeafBatchProof { /// MMR RPC methods. #[rpc(client, server)] -pub trait MmrApi { - /// Generate MMR proof for given block number. - /// - /// This method calls into a runtime with MMR pallet included and attempts to generate - /// MMR proof for a block with a specified `block_number`. - /// Optionally, a block hash at which the runtime should be queried can be specified. - /// - /// Returns the (full) leaf itself and a proof for this leaf (compact encoding, i.e. hash of - /// the leaf). Both parameters are SCALE-encoded. - #[method(name = "mmr_generateProof")] - fn generate_proof( - &self, - block_number: BlockNumber, - at: Option, - ) -> RpcResult>; +pub trait MmrApi { + /// Get the MMR root hash for the current best block. + #[method(name = "mmr_root")] + fn mmr_root(&self, at: Option) -> RpcResult; - /// Generate MMR proof for the given block numbers. + /// Generate an MMR proof for the given `block_numbers`. /// /// This method calls into a runtime with MMR pallet included and attempts to generate - /// MMR proof for a set of blocks with the specific `block_numbers`. - /// Optionally, a block hash at which the runtime should be queried can be specified. + /// an MMR proof for the set of blocks that have the given `block_numbers` with the MMR root at + /// `best_known_block_number`. `best_known_block_number` must be larger than all the + /// `block_numbers` for the function to succeed. + /// + /// Optionally via `at`, a block hash at which the runtime should be queried can be specified. + /// Optionally via `best_known_block_number`, the proof can be generated using the MMR's state + /// at a specific best block. Note that if `best_known_block_number` is provided, then also + /// specifying the block hash via `at` isn't super-useful here, unless you're generating proof + /// using non-finalized blocks where there are several competing forks. That's because MMR state + /// will be fixed to the state with `best_known_block_number`, which already points to + /// some historical block. /// - /// Returns the leaves and a proof for these leaves (compact encoding, i.e. hash of + /// Returns the (full) 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 `block_numbers` supplied - #[method(name = "mmr_generateBatchProof")] - fn generate_batch_proof( + #[method(name = "mmr_generateProof")] + fn generate_proof( &self, block_numbers: Vec, + best_known_block_number: Option, at: Option, - ) -> RpcResult>; + ) -> RpcResult>; - /// Generate a MMR proof for the given `block_numbers` given the `best_known_block_number`. + /// Verify an MMR `proof`. /// - /// This method calls into a runtime with MMR pallet included and attempts to generate - /// a MMR proof for the set of blocks that have the given `block_numbers` with MMR given the - /// `best_known_block_number`. `best_known_block_number` must be larger than all the - /// `block_numbers` for the function to succeed. + /// This method calls into a runtime with MMR pallet included and attempts to verify + /// an MMR proof. /// - /// Optionally, a block hash at which the runtime should be queried can be specified. - /// Note that specifying the block hash isn't super-useful here, unless you're generating - /// proof using non-finalized blocks where there are several competing forks. That's because - /// MMR state will be fixed to the state with `best_known_block_number`, which already points to - /// some historical block. + /// Returns `true` if the proof is valid, else returns the verification error. + #[method(name = "mmr_verifyProof")] + fn verify_proof(&self, proof: LeavesProof) -> RpcResult; + + /// Verify an MMR `proof` statelessly given an `mmr_root`. /// - /// 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 `block_numbers` supplied - #[method(name = "mmr_generateHistoricalBatchProof")] - fn generate_historical_batch_proof( + /// This method calls into a runtime with MMR pallet included and attempts to verify + /// an MMR proof against a provided MMR root. + /// + /// Returns `true` if the proof is valid, else returns the verification error. + #[method(name = "mmr_verifyProofStateless")] + fn verify_proof_stateless( &self, - block_numbers: Vec, - best_known_block_number: BlockNumber, - at: Option, - ) -> RpcResult>; + mmr_root: MmrHash, + proof: LeavesProof, + ) -> RpcResult; } /// MMR RPC methods. @@ -169,7 +140,7 @@ impl Mmr { } #[async_trait] -impl MmrApiServer<::Hash, NumberFor> +impl MmrApiServer<::Hash, NumberFor, MmrHash> for Mmr where Block: BlockT, @@ -177,89 +148,102 @@ where Client::Api: MmrRuntimeApi>, MmrHash: Codec + Send + Sync + 'static, { - fn generate_proof( - &self, - block_number: NumberFor, - at: Option<::Hash>, - ) -> RpcResult> { + fn mmr_root(&self, at: Option<::Hash>) -> RpcResult { + let block_hash = at.unwrap_or_else(|| + // If the block hash is not supplied assume the best block. + self.client.info().best_hash); let api = self.client.runtime_api(); - let block_hash = at.unwrap_or_else(|| self.client.info().best_hash); - - let (leaf, proof) = api - .generate_proof_with_context( - &BlockId::hash(block_hash), - sp_core::ExecutionContext::OffchainCall(None), - block_number, - ) + let mmr_root = api + .mmr_root(&BlockId::Hash(block_hash)) .map_err(runtime_error_into_rpc_error)? .map_err(mmr_error_into_rpc_error)?; - - Ok(LeafProof::new(block_hash, leaf, proof)) + Ok(mmr_root) } - fn generate_batch_proof( + fn generate_proof( &self, block_numbers: Vec>, + best_known_block_number: Option>, at: Option<::Hash>, - ) -> RpcResult::Hash>> { + ) -> RpcResult::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( + .generate_proof_with_context( &BlockId::hash(block_hash), sp_core::ExecutionContext::OffchainCall(None), block_numbers, + best_known_block_number, ) .map_err(runtime_error_into_rpc_error)? .map_err(mmr_error_into_rpc_error)?; - Ok(LeafBatchProof::new(block_hash, leaves, proof)) + Ok(LeavesProof::new(block_hash, leaves, proof)) + } + + fn verify_proof(&self, proof: LeavesProof<::Hash>) -> RpcResult { + let api = self.client.runtime_api(); + + let leaves = Decode::decode(&mut &proof.leaves.0[..]) + .map_err(|e| CallError::InvalidParams(anyhow::Error::new(e)))?; + + let decoded_proof = Decode::decode(&mut &proof.proof.0[..]) + .map_err(|e| CallError::InvalidParams(anyhow::Error::new(e)))?; + + api.verify_proof_with_context( + &BlockId::hash(proof.block_hash), + sp_core::ExecutionContext::OffchainCall(None), + leaves, + decoded_proof, + ) + .map_err(runtime_error_into_rpc_error)? + .map_err(mmr_error_into_rpc_error)?; + + Ok(true) } - fn generate_historical_batch_proof( + fn verify_proof_stateless( &self, - block_numbers: Vec>, - best_known_block_number: NumberFor, - at: Option<::Hash>, - ) -> RpcResult::Hash>> { + mmr_root: MmrHash, + proof: LeavesProof<::Hash>, + ) -> RpcResult { 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_historical_batch_proof_with_context( - &BlockId::hash(block_hash), - sp_core::ExecutionContext::OffchainCall(None), - block_numbers, - best_known_block_number, - ) - .map_err(runtime_error_into_rpc_error)? - .map_err(mmr_error_into_rpc_error)?; + let leaves = Decode::decode(&mut &proof.leaves.0[..]) + .map_err(|e| CallError::InvalidParams(anyhow::Error::new(e)))?; - Ok(LeafBatchProof::new(block_hash, leaves, proof)) + let decoded_proof = Decode::decode(&mut &proof.proof.0[..]) + .map_err(|e| CallError::InvalidParams(anyhow::Error::new(e)))?; + + api.verify_proof_stateless( + &BlockId::hash(proof.block_hash), + mmr_root, + leaves, + decoded_proof, + ) + .map_err(runtime_error_into_rpc_error)? + .map_err(mmr_error_into_rpc_error)?; + + Ok(true) } } -/// Converts a mmr-specific error into a [`CallError`]. +/// Converts an mmr-specific error into a [`CallError`]. fn mmr_error_into_rpc_error(err: MmrError) -> CallError { - let data = format!("{:?}", err); - match err { - MmrError::LeafNotFound => CallError::Custom(ErrorObject::owned( - LEAF_NOT_FOUND_ERROR, - "Leaf was not found", - Some(data), - )), - MmrError::GenerateProof => CallError::Custom(ErrorObject::owned( - GENERATE_PROOF_ERROR, - "Error while generating the proof", - Some(data), - )), - _ => CallError::Custom(ErrorObject::owned(MMR_ERROR, "Unexpected MMR error", Some(data))), - } + let error_code = MMR_ERROR + + match err { + MmrError::LeafNotFound => 1, + MmrError::GenerateProof => 2, + MmrError::Verify => 3, + MmrError::BlockNumToLeafIndex => 4, + MmrError::InvalidBestKnownBlock => 5, + _ => 0, + }; + + CallError::Custom(ErrorObject::owned(error_code, err.to_string(), Some(format!("{:?}", err)))) } /// Converts a runtime trap into a [`CallError`]. @@ -281,12 +265,12 @@ mod tests { // given let leaf = vec![1_u8, 2, 3, 4]; let proof = Proof { - leaf_index: 1, + leaf_indices: vec![1], leaf_count: 9, items: vec![H256::repeat_byte(1), H256::repeat_byte(2)], }; - let leaf_proof = LeafProof::new(H256::repeat_byte(0), leaf, proof); + let leaf_proof = LeavesProof::new(H256::repeat_byte(0), vec![leaf], proof); // when let actual = serde_json::to_string(&leaf_proof).unwrap(); @@ -294,21 +278,22 @@ mod tests { // then assert_eq!( actual, - r#"{"blockHash":"0x0000000000000000000000000000000000000000000000000000000000000000","leaf":"0x1001020304","proof":"0x010000000000000009000000000000000801010101010101010101010101010101010101010101010101010101010101010202020202020202020202020202020202020202020202020202020202020202"}"# + r#"{"blockHash":"0x0000000000000000000000000000000000000000000000000000000000000000","leaves":"0x041001020304","proof":"0x04010000000000000009000000000000000801010101010101010101010101010101010101010101010101010101010101010202020202020202020202020202020202020202020202020202020202020202"}"# ); } #[test] - fn should_serialize_leaf_batch_proof() { + fn should_serialize_leaves_proof() { // given - let leaf = vec![1_u8, 2, 3, 4]; - let proof = BatchProof { - leaf_indices: vec![1], + let leaf_a = vec![1_u8, 2, 3, 4]; + let leaf_b = vec![2_u8, 2, 3, 4]; + let proof = Proof { + leaf_indices: vec![1, 2], leaf_count: 9, items: vec![H256::repeat_byte(1), H256::repeat_byte(2)], }; - let leaf_proof = LeafBatchProof::new(H256::repeat_byte(0), vec![leaf], proof); + let leaf_proof = LeavesProof::new(H256::repeat_byte(0), vec![leaf_a, leaf_b], proof); // when let actual = serde_json::to_string(&leaf_proof).unwrap(); @@ -316,19 +301,19 @@ mod tests { // then assert_eq!( actual, - r#"{"blockHash":"0x0000000000000000000000000000000000000000000000000000000000000000","leaves":"0x041001020304","proof":"0x04010000000000000009000000000000000801010101010101010101010101010101010101010101010101010101010101010202020202020202020202020202020202020202020202020202020202020202"}"# + r#"{"blockHash":"0x0000000000000000000000000000000000000000000000000000000000000000","leaves":"0x0810010203041002020304","proof":"0x080100000000000000020000000000000009000000000000000801010101010101010101010101010101010101010101010101010101010101010202020202020202020202020202020202020202020202020202020202020202"}"# ); } #[test] fn should_deserialize_leaf_proof() { // given - let expected = LeafProof { + let expected = LeavesProof { block_hash: H256::repeat_byte(0), - leaf: Bytes(vec![1_u8, 2, 3, 4].encode()), + leaves: Bytes(vec![vec![1_u8, 2, 3, 4]].encode()), proof: Bytes( Proof { - leaf_index: 1, + leaf_indices: vec![1], leaf_count: 9, items: vec![H256::repeat_byte(1), H256::repeat_byte(2)], } @@ -337,10 +322,10 @@ mod tests { }; // when - let actual: LeafProof = serde_json::from_str(r#"{ + let actual: LeavesProof = serde_json::from_str(r#"{ "blockHash":"0x0000000000000000000000000000000000000000000000000000000000000000", - "leaf":"0x1001020304", - "proof":"0x010000000000000009000000000000000801010101010101010101010101010101010101010101010101010101010101010202020202020202020202020202020202020202020202020202020202020202" + "leaves":"0x041001020304", + "proof":"0x04010000000000000009000000000000000801010101010101010101010101010101010101010101010101010101010101010202020202020202020202020202020202020202020202020202020202020202" }"#).unwrap(); // then @@ -348,14 +333,14 @@ mod tests { } #[test] - fn should_deserialize_leaf_batch_proof() { + fn should_deserialize_leaves_proof() { // given - let expected = LeafBatchProof { + let expected = LeavesProof { block_hash: H256::repeat_byte(0), - leaves: Bytes(vec![vec![1_u8, 2, 3, 4]].encode()), + leaves: Bytes(vec![vec![1_u8, 2, 3, 4], vec![2_u8, 2, 3, 4]].encode()), proof: Bytes( - BatchProof { - leaf_indices: vec![1], + Proof { + leaf_indices: vec![1, 2], leaf_count: 9, items: vec![H256::repeat_byte(1), H256::repeat_byte(2)], } @@ -364,10 +349,10 @@ mod tests { }; // when - let actual: LeafBatchProof = serde_json::from_str(r#"{ + let actual: LeavesProof = serde_json::from_str(r#"{ "blockHash":"0x0000000000000000000000000000000000000000000000000000000000000000", - "leaves":"0x041001020304", - "proof":"0x04010000000000000009000000000000000801010101010101010101010101010101010101010101010101010101010101010202020202020202020202020202020202020202020202020202020202020202" + "leaves":"0x0810010203041002020304", + "proof":"0x080100000000000000020000000000000009000000000000000801010101010101010101010101010101010101010101010101010101010101010202020202020202020202020202020202020202020202020202020202020202" }"#).unwrap(); // then diff --git a/frame/merkle-mountain-range/src/lib.rs b/frame/merkle-mountain-range/src/lib.rs index 92ab8702b65c0..a2d42417ae5dc 100644 --- a/frame/merkle-mountain-range/src/lib.rs +++ b/frame/merkle-mountain-range/src/lib.rs @@ -22,7 +22,7 @@ //! Details on Merkle Mountain Ranges (MMRs) can be found here: //! //! -//! The MMR pallet constructs a MMR from leaf data obtained on every block from +//! The MMR pallet constructs an MMR from leaf data obtained on every block from //! `LeafDataProvider`. MMR nodes are stored both in: //! - on-chain storage - hashes only; not full leaf content) //! - off-chain storage - via Indexing API we push full leaf content (and all internal nodes as @@ -50,7 +50,7 @@ //! //! Secondary use case is to archive historical data, but still be able to retrieve them on-demand //! if needed. For instance if parent block hashes are stored in the MMR it's possible at any point -//! in time to provide a MMR proof about some past block hash, while this data can be safely pruned +//! in time to provide an MMR proof about some past block hash, while this data can be safely pruned //! from on-chain storage. //! //! NOTE This pallet is experimental and not proven to work in production. @@ -103,7 +103,7 @@ pub trait WeightInfo { fn on_initialize(peaks: NodeIndex) -> Weight; } -/// A MMR specific to the pallet. +/// An MMR specific to the pallet. type ModuleMmr = mmr::Mmr>; /// Leaf data. @@ -287,15 +287,15 @@ pub mod pallet { /// Stateless MMR proof verification for batch of leaves. /// -/// This function can be used to verify received MMR [primitives::BatchProof] (`proof`) +/// This function can be used to verify received MMR [primitives::Proof] (`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]. +/// [primitives::Proof]. pub fn verify_leaves_proof( root: H::Output, leaves: Vec>, - proof: primitives::BatchProof, + proof: primitives::Proof, ) -> Result<(), primitives::Error> where H: traits::Hash, @@ -381,37 +381,23 @@ impl, I: 'static> Pallet { Ok(leaf_idx) } - /// Generate a MMR proof for the given `block_numbers`. + /// Generate an MMR proof for the given `block_numbers`. + /// If `best_known_block_number = Some(n)`, this generates a historical proof for + /// the chain with head at height `n`. + /// Else it generates a proof for the MMR at the current block height. /// /// 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( + pub fn generate_proof( block_numbers: Vec, - ) -> Result< - (Vec>, primitives::BatchProof<>::Hash>), - primitives::Error, - > { - Self::generate_historical_batch_proof( - block_numbers, - >::block_number(), - ) - } + best_known_block_number: Option, + ) -> Result<(Vec>, primitives::Proof<>::Hash>), 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()); - /// Generate a MMR proof for the given `block_numbers` given the `best_known_block_number`. - /// - /// 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_historical_batch_proof( - block_numbers: Vec, - best_known_block_number: T::BlockNumber, - ) -> Result< - (Vec>, primitives::BatchProof<>::Hash>), - primitives::Error, - > { let leaves_count = Self::block_num_to_leaf_index(best_known_block_number)?.saturating_add(1); @@ -424,7 +410,7 @@ impl, I: 'static> Pallet { .collect::, _>>()?; let mmr: ModuleMmr = mmr::Mmr::new(leaves_count); - mmr.generate_batch_proof(leaf_indices) + mmr.generate_proof(leaf_indices) } /// Return the on-chain MMR root hash. @@ -440,7 +426,7 @@ impl, I: 'static> Pallet { /// or the proof is invalid. pub fn verify_leaves( leaves: Vec>, - proof: primitives::BatchProof<>::Hash>, + proof: primitives::Proof<>::Hash>, ) -> Result<(), primitives::Error> { if proof.leaf_count > Self::mmr_leaves() || proof.leaf_count == 0 || diff --git a/frame/merkle-mountain-range/src/mmr/mmr.rs b/frame/merkle-mountain-range/src/mmr/mmr.rs index 44e684c1bdcac..1f5a5bdae380b 100644 --- a/frame/merkle-mountain-range/src/mmr/mmr.rs +++ b/frame/merkle-mountain-range/src/mmr/mmr.rs @@ -29,11 +29,11 @@ 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::BatchProof] +/// [primitives::Proof] pub fn verify_leaves_proof( root: H::Output, leaves: Vec>, - proof: primitives::BatchProof, + proof: primitives::Proof, ) -> Result where H: sp_runtime::traits::Hash, @@ -60,7 +60,7 @@ where .map_err(|e| Error::Verify.log_debug(e)) } -/// A wrapper around a MMR library to expose limited functionality. +/// A wrapper around an MMR library to expose limited functionality. /// /// Available functions depend on the storage kind ([Runtime](crate::mmr::storage::RuntimeStorage) /// vs [Off-chain](crate::mmr::storage::OffchainStorage)). @@ -91,11 +91,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::BatchProof] + /// [primitives::Proof] pub fn verify_leaves_proof( &self, leaves: Vec, - proof: primitives::BatchProof<>::Hash>, + proof: primitives::Proof<>::Hash>, ) -> Result { let p = mmr_lib::MerkleProof::, Hasher, L>>::new( self.mmr.mmr_size(), @@ -163,10 +163,10 @@ where /// /// 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( + pub fn generate_proof( &self, leaf_indices: Vec, - ) -> Result<(Vec, primitives::BatchProof<>::Hash>), Error> { + ) -> Result<(Vec, primitives::Proof<>::Hash>), Error> { let positions = leaf_indices .iter() .map(|index| mmr_lib::leaf_index_to_pos(*index)) @@ -184,7 +184,7 @@ where self.mmr .gen_proof(positions) .map_err(|e| Error::GenerateProof.log_error(e)) - .map(|p| primitives::BatchProof { + .map(|p| primitives::Proof { leaf_indices, leaf_count, items: p.proof_items().iter().map(|x| x.hash()).collect(), diff --git a/frame/merkle-mountain-range/src/mmr/storage.rs b/frame/merkle-mountain-range/src/mmr/storage.rs index 870ce81226bd2..d16ca8cf1e5c8 100644 --- a/frame/merkle-mountain-range/src/mmr/storage.rs +++ b/frame/merkle-mountain-range/src/mmr/storage.rs @@ -15,7 +15,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! A MMR storage implementations. +//! An MMR storage implementation. use codec::Encode; use frame_support::log::{debug, error, trace}; diff --git a/frame/merkle-mountain-range/src/tests.rs b/frame/merkle-mountain-range/src/tests.rs index dbbdc12c8e1d5..e47f1b3b2e63a 100644 --- a/frame/merkle-mountain-range/src/tests.rs +++ b/frame/merkle-mountain-range/src/tests.rs @@ -27,7 +27,7 @@ use sp_core::{ offchain::{testing::TestOffchainExt, OffchainDbExt, OffchainWorkerExt}, H256, }; -use sp_mmr_primitives::{BatchProof, Compact}; +use sp_mmr_primitives::{Compact, Proof}; pub(crate) fn new_test_ext() -> sp_io::TestExternalities { frame_system::GenesisConfig::default().build_storage::().unwrap().into() @@ -236,18 +236,18 @@ fn should_generate_proofs_correctly() { // when generate proofs for all leaves. let proofs = (1_u64..=best_block_number) .into_iter() - .map(|block_num| crate::Pallet::::generate_batch_proof(vec![block_num]).unwrap()) + .map(|block_num| crate::Pallet::::generate_proof(vec![block_num], None).unwrap()) .collect::>(); // when generate historical proofs for all leaves let historical_proofs = (1_u64..best_block_number) .into_iter() .map(|block_num| { let mut proofs = vec![]; - for leaves_count in block_num..=num_blocks { + for historical_best_block in block_num..=num_blocks { proofs.push( - crate::Pallet::::generate_historical_batch_proof( + crate::Pallet::::generate_proof( vec![block_num], - leaves_count, + Some(historical_best_block), ) .unwrap(), ) @@ -261,7 +261,7 @@ fn should_generate_proofs_correctly() { proofs[0], ( vec![Compact::new(((0, H256::repeat_byte(1)).into(), LeafData::new(1).into(),))], - BatchProof { + Proof { leaf_indices: vec![0], leaf_count: 7, items: vec![ @@ -276,7 +276,7 @@ fn should_generate_proofs_correctly() { historical_proofs[0][0], ( vec![Compact::new(((0, H256::repeat_byte(1)).into(), LeafData::new(1).into(),))], - BatchProof { leaf_indices: vec![0], leaf_count: 1, items: vec![] } + Proof { leaf_indices: vec![0], leaf_count: 1, items: vec![] } ) ); @@ -292,7 +292,7 @@ fn should_generate_proofs_correctly() { proofs[2], ( vec![Compact::new(((2, H256::repeat_byte(3)).into(), LeafData::new(3).into(),))], - BatchProof { + Proof { leaf_indices: vec![2], leaf_count: 7, items: vec![ @@ -312,7 +312,7 @@ fn should_generate_proofs_correctly() { historical_proofs[2][0], ( vec![Compact::new(((2, H256::repeat_byte(3)).into(), LeafData::new(3).into(),))], - BatchProof { + Proof { leaf_indices: vec![2], leaf_count: 3, items: vec![hex( @@ -332,7 +332,7 @@ fn should_generate_proofs_correctly() { historical_proofs[2][2], ( vec![Compact::new(((2, H256::repeat_byte(3)).into(), LeafData::new(3).into(),))], - BatchProof { + Proof { leaf_indices: vec![2], leaf_count: 5, items: vec![ @@ -350,7 +350,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(),))], - BatchProof { + Proof { leaf_indices: vec![4], leaf_count: 7, items: vec![ @@ -365,7 +365,7 @@ fn should_generate_proofs_correctly() { historical_proofs[4][0], ( vec![Compact::new(((4, H256::repeat_byte(5)).into(), LeafData::new(5).into(),))], - BatchProof { + Proof { leaf_indices: vec![4], leaf_count: 5, items: vec![hex( @@ -380,7 +380,7 @@ fn should_generate_proofs_correctly() { proofs[6], ( vec![Compact::new(((6, H256::repeat_byte(7)).into(), LeafData::new(7).into(),))], - BatchProof { + Proof { leaf_indices: vec![6], leaf_count: 7, items: vec![ @@ -407,11 +407,11 @@ fn should_generate_batch_proof_correctly() { register_offchain_ext(&mut ext); ext.execute_with(|| { // when generate proofs for a batch of leaves - let (.., proof) = crate::Pallet::::generate_batch_proof(vec![1, 5, 6]).unwrap(); + let (.., proof) = crate::Pallet::::generate_proof(vec![1, 5, 6], None).unwrap(); // then assert_eq!( proof, - BatchProof { + Proof { // the leaf indices are equivalent to the above specified block numbers - 1. leaf_indices: vec![0, 4, 5], leaf_count: 7, @@ -425,11 +425,11 @@ fn should_generate_batch_proof_correctly() { // when generate historical proofs for a batch of leaves let (.., historical_proof) = - crate::Pallet::::generate_historical_batch_proof(vec![1, 5, 6], 6).unwrap(); + crate::Pallet::::generate_proof(vec![1, 5, 6], Some(6)).unwrap(); // then assert_eq!( historical_proof, - BatchProof { + Proof { leaf_indices: vec![0, 4, 5], leaf_count: 6, items: vec![ @@ -441,7 +441,7 @@ fn should_generate_batch_proof_correctly() { // when generate historical proofs for a batch of leaves let (.., historical_proof) = - crate::Pallet::::generate_historical_batch_proof(vec![1, 5, 6], 7).unwrap(); + crate::Pallet::::generate_proof(vec![1, 5, 6], None).unwrap(); // then assert_eq!(historical_proof, proof); }); @@ -462,15 +462,15 @@ fn should_verify() { register_offchain_ext(&mut ext); let (leaves, proof5) = ext.execute_with(|| { // when - crate::Pallet::::generate_batch_proof(vec![5]).unwrap() + crate::Pallet::::generate_proof(vec![5], None).unwrap() }); let (simple_historical_leaves, simple_historical_proof5) = ext.execute_with(|| { // when - crate::Pallet::::generate_historical_batch_proof(vec![5], 6).unwrap() + crate::Pallet::::generate_proof(vec![5], Some(6)).unwrap() }); let (advanced_historical_leaves, advanced_historical_proof5) = ext.execute_with(|| { // when - crate::Pallet::::generate_historical_batch_proof(vec![5], 7).unwrap() + crate::Pallet::::generate_proof(vec![5], Some(7)).unwrap() }); ext.execute_with(|| { @@ -502,22 +502,18 @@ fn should_verify_batch_proofs() { blocks_to_add: usize, ) { let (leaves, proof) = ext.execute_with(|| { - crate::Pallet::::generate_batch_proof(block_numbers.to_vec()).unwrap() + crate::Pallet::::generate_proof(block_numbers.to_vec(), None).unwrap() }); - let mmr_size = ext.execute_with(|| crate::Pallet::::mmr_leaves()); - let min_mmr_size = block_numbers.iter().max().unwrap() + 1; + let max_block_number = ext.execute_with(|| frame_system::Pallet::::block_number()); + let min_block_number = block_numbers.iter().max().unwrap(); - // generate historical proofs for all possible mmr sizes, - // lower bound being index of highest leaf to be proven - let historical_proofs = (min_mmr_size..=mmr_size) - .map(|mmr_size| { + // 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_historical_batch_proof( - block_numbers.to_vec(), - mmr_size, - ) - .unwrap() + crate::Pallet::::generate_proof(block_numbers.to_vec(), Some(best_block)) + .unwrap() }) }) .collect::>(); @@ -602,11 +598,11 @@ fn verification_should_be_stateless() { register_offchain_ext(&mut ext); let (leaves, proof5) = ext.execute_with(|| { // when - crate::Pallet::::generate_batch_proof(vec![5]).unwrap() + crate::Pallet::::generate_proof(vec![5], None).unwrap() }); let (_, historical_proof5) = ext.execute_with(|| { // when - crate::Pallet::::generate_historical_batch_proof(vec![5], 6).unwrap() + crate::Pallet::::generate_proof(vec![5], Some(6)).unwrap() }); // Verify proof without relying on any on-chain data. @@ -650,11 +646,11 @@ fn should_verify_batch_proof_statelessly() { register_offchain_ext(&mut ext); let (leaves, proof) = ext.execute_with(|| { // when - crate::Pallet::::generate_batch_proof(vec![1, 4, 5]).unwrap() + crate::Pallet::::generate_proof(vec![1, 4, 5], None).unwrap() }); let (historical_leaves, historical_proof) = ext.execute_with(|| { // when - crate::Pallet::::generate_historical_batch_proof(vec![1, 4, 5], 6).unwrap() + crate::Pallet::::generate_proof(vec![1, 4, 5], Some(6)).unwrap() }); // Verify proof without relying on any on-chain data. @@ -694,7 +690,7 @@ fn should_verify_on_the_next_block_since_there_is_no_pruning_yet() { ext.execute_with(|| { // when - let (leaves, proof5) = crate::Pallet::::generate_batch_proof(vec![5]).unwrap(); + let (leaves, proof5) = crate::Pallet::::generate_proof(vec![5], None).unwrap(); new_block(); // then @@ -928,7 +924,7 @@ fn should_verify_canonicalized() { // Generate proofs for some blocks. let (leaves, proofs) = - ext.execute_with(|| crate::Pallet::::generate_batch_proof(vec![1, 4, 5, 7]).unwrap()); + ext.execute_with(|| crate::Pallet::::generate_proof(vec![1, 4, 5, 7], None).unwrap()); // Verify all previously generated proofs. ext.execute_with(|| { assert_eq!(crate::Pallet::::verify_leaves(leaves, proofs), Ok(())); @@ -936,7 +932,7 @@ fn should_verify_canonicalized() { // Generate proofs for some new blocks. let (leaves, proofs) = ext.execute_with(|| { - crate::Pallet::::generate_batch_proof(vec![block_hash_size + 7]).unwrap() + crate::Pallet::::generate_proof(vec![block_hash_size + 7], None).unwrap() }); // Add some more blocks then verify all previously generated proofs. ext.execute_with(|| { @@ -960,19 +956,19 @@ fn does_not_panic_when_generating_historical_proofs() { ext.execute_with(|| { // when leaf index is invalid assert_eq!( - crate::Pallet::::generate_historical_batch_proof(vec![10], 7), + crate::Pallet::::generate_proof(vec![10], None), Err(Error::BlockNumToLeafIndex), ); // when leaves count is invalid assert_eq!( - crate::Pallet::::generate_historical_batch_proof(vec![3], 100), + crate::Pallet::::generate_proof(vec![3], Some(100)), Err(Error::BlockNumToLeafIndex), ); // when both leaf index and leaves count are invalid assert_eq!( - crate::Pallet::::generate_historical_batch_proof(vec![10], 100), + crate::Pallet::::generate_proof(vec![10], Some(100)), Err(Error::BlockNumToLeafIndex), ); }); diff --git a/primitives/beefy/src/mmr.rs b/primitives/beefy/src/mmr.rs index 78ceef0b6b396..549d2edbdf287 100644 --- a/primitives/beefy/src/mmr.rs +++ b/primitives/beefy/src/mmr.rs @@ -64,7 +64,7 @@ pub struct MmrLeaf { pub leaf_extra: ExtraData, } -/// A MMR leaf versioning scheme. +/// An MMR leaf versioning scheme. /// /// Version is a single byte that constist of two components: /// - `major` - 3 bits diff --git a/primitives/merkle-mountain-range/Cargo.toml b/primitives/merkle-mountain-range/Cargo.toml index 0be53132f3eec..e857974ba898c 100644 --- a/primitives/merkle-mountain-range/Cargo.toml +++ b/primitives/merkle-mountain-range/Cargo.toml @@ -21,6 +21,7 @@ sp-core = { version = "6.0.0", default-features = false, path = "../core" } sp-debug-derive = { version = "4.0.0", default-features = false, path = "../debug-derive" } sp-runtime = { version = "6.0.0", default-features = false, path = "../runtime" } sp-std = { version = "4.0.0", default-features = false, path = "../std" } +thiserror = "1.0" [dev-dependencies] array-bytes = "4.1" diff --git a/primitives/merkle-mountain-range/src/lib.rs b/primitives/merkle-mountain-range/src/lib.rs index 06bc1f4bffe84..d46cb73c3c5e8 100644 --- a/primitives/merkle-mountain-range/src/lib.rs +++ b/primitives/merkle-mountain-range/src/lib.rs @@ -23,9 +23,9 @@ use scale_info::TypeInfo; 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::{fmt, vec}; /// A type to describe node position in the MMR (node index). pub type NodeIndex = u64; @@ -69,17 +69,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, TypeInfo)] -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 its full or compact form. @@ -352,9 +341,9 @@ 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. +/// An MMR proof data for a group of leaves. #[derive(codec::Encode, codec::Decode, RuntimeDebug, Clone, PartialEq, Eq, TypeInfo)] -pub struct BatchProof { +pub struct Proof { /// The indices of the leaves the proof is for. pub leaf_indices: Vec, /// Number of leaves in MMR, when the proof was generated. @@ -363,49 +352,39 @@ pub struct BatchProof { pub items: Vec, } -impl BatchProof { - /// 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, - }) - } -} - -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, - items: proof.items, - } - } -} /// Merkle Mountain Range operation error. +#[cfg_attr(feature = "std", derive(thiserror::Error))] #[derive(RuntimeDebug, codec::Encode, codec::Decode, PartialEq, Eq)] pub enum Error { /// Error during translation of a block number into a leaf index. + #[cfg_attr(feature = "std", error("Error translation block number into leaf index"))] BlockNumToLeafIndex, /// Error while pushing new node. + #[cfg_attr(feature = "std", error("Error pushing new node"))] Push, /// Error getting the new root. + #[cfg_attr(feature = "std", error("Error getting new root"))] GetRoot, - /// Error commiting changes. + /// Error committing changes. + #[cfg_attr(feature = "std", error("Error committing changes"))] Commit, /// Error during proof generation. + #[cfg_attr(feature = "std", error("Error generating proof"))] GenerateProof, /// Proof verification error. + #[cfg_attr(feature = "std", error("Invalid proof"))] Verify, /// Leaf not found in the storage. + #[cfg_attr(feature = "std", error("Leaf was not found"))] LeafNotFound, /// Mmr Pallet not included in runtime + #[cfg_attr(feature = "std", error("MMR pallet not included in runtime"))] PalletNotIncluded, /// Cannot find the requested leaf index + #[cfg_attr(feature = "std", error("Requested leaf index invalid"))] InvalidLeafIndex, /// The provided best know block number is invalid. + #[cfg_attr(feature = "std", error("Provided best known block number invalid"))] InvalidBestKnownBlock, } @@ -437,53 +416,31 @@ impl Error { sp_api::decl_runtime_apis! { /// API to interact with MMR pallet. pub trait MmrApi { - /// Generate MMR proof for a block with a specified `block_number`. - fn generate_proof(block_number: BlockNumber) -> 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: Proof) -> Result<(), Error>; - - /// Verify MMR proof against given root hash. - /// - /// 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 its compact form. - fn verify_proof_stateless(root: Hash, leaf: EncodableOpaqueLeaf, proof: Proof) - -> Result<(), Error>; - /// Return the on-chain MMR root hash. fn mmr_root() -> Result; - /// Generate MMR proof for a series of blocks with the specified block numbers. - fn generate_batch_proof(block_numbers: Vec) -> Result<(Vec, BatchProof), Error>; - - /// Generate MMR proof for a series of `block_numbers`, given the `best_known_block_number`. - fn generate_historical_batch_proof( + /// Generate MMR proof for a series of block numbers. If `best_known_block_number = Some(n)`, + /// use historical MMR state at given block height `n`. Else, use current MMR state. + fn generate_proof( block_numbers: Vec, - best_known_block_number: BlockNumber - ) -> Result<(Vec, BatchProof), Error>; + best_known_block_number: Option + ) -> Result<(Vec, Proof), 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 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 [BatchProof] - fn verify_batch_proof(leaves: Vec, proof: BatchProof) -> Result<(), Error>; + /// 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>; - /// Verify MMR proof against given root hash or a batch of leaves. + /// Verify MMR proof against given root hash for a batch of leaves. /// /// 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 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) + /// 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) -> Result<(), Error>; } } @@ -508,7 +465,7 @@ mod tests { fn should_encode_decode_proof() { // given let proof: TestProof = Proof { - leaf_index: 5, + leaf_indices: vec![5], leaf_count: 10, items: vec![ hex("c3e7ba6b511162fead58f2c8b5764ce869ed1118011ac37392522ed16720bbcd"),