From a6da808575fe403ab2bd7f6cd009896cdc6fd71a Mon Sep 17 00:00:00 2001
From: Robert Hambrock
Date: Wed, 9 Nov 2022 15:41:32 +0100
Subject: [PATCH] Consolidate and deduplicate MMR API methods (#12530)
* histor. batch proof: make best block arg optional
* correct testing range
* make generate_batch_proof stub for historical
* merge generate_{historical_}batch_proof functions
* merge generate_{batch_}proof functions
* merge verify_{batch_}proof functions
* merge verify_{batch_}proof_stateless functions
* remove {Leaf}Proof
Not utilized by API anymore, so superfluous.
Removal consistent with prior changes to just use "batch" proof API.
* rename BatchProof->Proof
no need to qualify if only one universal proof type.
* cleanup
* expose verify_proof rpc api
* document verify_proof
* expose verify_proof_stateless rpc api
* add optional BlockHash to mmr_root rpc api
* fixup! expose verify_proof rpc api
* fix documentation phrasing
Co-authored-by: Adrian Catangiu
* documentation grammar
Co-authored-by: Adrian Catangiu
* define mmr error msgs together with error enum
Co-authored-by: Serban Iorga
* fixup! define mmr error msgs together with error enum
* map decoding errors to CallError::InvalidParams
Co-authored-by: Serban Iorga
* fixup! map decoding errors to CallError::InvalidParams
Co-authored-by: Adrian Catangiu
Co-authored-by: parity-processbot <>
Co-authored-by: Serban Iorga
---
Cargo.lock | 2 +
bin/node/runtime/src/lib.rs | 58 +---
client/beefy/src/tests.rs | 36 +--
frame/beefy-mmr/primitives/src/lib.rs | 2 +-
frame/merkle-mountain-range/rpc/Cargo.toml | 1 +
frame/merkle-mountain-range/rpc/src/lib.rs | 287 +++++++++---------
frame/merkle-mountain-range/src/lib.rs | 50 ++-
frame/merkle-mountain-range/src/mmr/mmr.rs | 16 +-
.../merkle-mountain-range/src/mmr/storage.rs | 2 +-
frame/merkle-mountain-range/src/tests.rs | 82 +++--
primitives/beefy/src/mmr.rs | 2 +-
primitives/merkle-mountain-range/Cargo.toml | 1 +
primitives/merkle-mountain-range/src/lib.rs | 97 ++----
13 files changed, 249 insertions(+), 387 deletions(-)
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"),