Update MMR Runtime API with functionality to generate MMR proof for a series of leaf indices (#10635)

* updated mmr rpc api with functions for batch generation of proof

* update code comments

* fix build errors

* added tests to mmr-rpc

* add tests to pallet-mmr

* update comments

* minor comment fix

* remove unused variables

* fix rust doc errors

* refactor mmr runtime api

* fix tests

* minor fix

* minor fix

* fix node-runtime

* revert to initial api

* impl from proof fot batchproof

* minor fix

* minor fix

* use explicit functions to convert btw batch proof and single proof

* minor fix

* add new variant to mmr error

* fmt

* update conversion to single leaf proof

* fix style nit

Co-authored-by: Adrian Catangiu <adrian@parity.io>
This commit is contained in:
Web3 Smith
2022-05-04 11:40:11 +01:00
committed by GitHub
parent 82adb65f1b
commit fd45676d11
9 changed files with 429 additions and 81 deletions
@@ -29,7 +29,7 @@ use serde::{Deserialize, Serialize};
use sp_api::ProvideRuntimeApi;
use sp_blockchain::HeaderBackend;
use sp_core::Bytes;
use sp_mmr_primitives::{Error as MmrError, LeafIndex, Proof};
use sp_mmr_primitives::{BatchProof, Error as MmrError, LeafIndex, Proof};
use sp_runtime::{generic::BlockId, traits::Block as BlockT};
pub use sp_mmr_primitives::MmrApi as MmrRuntimeApi;
@@ -57,6 +57,34 @@ impl<BlockHash> LeafProof<BlockHash> {
}
}
/// Retrieved MMR leaves and their proof.
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct LeafBatchProof<BlockHash> {
/// 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].
pub proof: Bytes,
}
impl<BlockHash> LeafBatchProof<BlockHash> {
/// Create new `LeafBatchProof` from a given vector of `Leaf` and a
/// [sp_mmr_primitives::BatchProof].
pub fn new<Leaf, MmrHash>(
block_hash: BlockHash,
leaves: Vec<Leaf>,
proof: BatchProof<MmrHash>,
) -> Self
where
Leaf: Encode,
MmrHash: Encode,
{
Self { block_hash, leaves: Bytes(leaves.encode()), proof: Bytes(proof.encode()) }
}
}
/// MMR RPC methods.
#[rpc]
pub trait MmrApi<BlockHash> {
@@ -74,6 +102,23 @@ pub trait MmrApi<BlockHash> {
leaf_index: LeafIndex,
at: Option<BlockHash>,
) -> Result<LeafProof<BlockHash>>;
/// Generate MMR proof for the given leaf indices.
///
/// This method calls into a runtime with MMR pallet included and attempts to generate
/// MMR proof for a set of leaves at the given `leaf_indices`.
/// Optionally, a block hash at which the runtime should be queried can be specified.
///
/// Returns the leaves and a proof for these leaves (compact encoding, i.e. hash of
/// the leaves). Both parameters are SCALE-encoded.
/// The order of entries in the `leaves` field of the returned struct
/// is the same as the order of the entries in `leaf_indices` supplied
#[rpc(name = "mmr_generateBatchProof")]
fn generate_batch_proof(
&self,
leaf_indices: Vec<LeafIndex>,
at: Option<BlockHash>,
) -> Result<LeafBatchProof<BlockHash>>;
}
/// An implementation of MMR specific RPC methods.
@@ -117,6 +162,28 @@ where
Ok(LeafProof::new(block_hash, leaf, proof))
}
fn generate_batch_proof(
&self,
leaf_indices: Vec<LeafIndex>,
at: Option<<Block as BlockT>::Hash>,
) -> Result<LeafBatchProof<<Block as BlockT>::Hash>> {
let api = self.client.runtime_api();
let block_hash = at.unwrap_or_else(||
// If the block hash is not supplied assume the best block.
self.client.info().best_hash);
let (leaves, proof) = api
.generate_batch_proof_with_context(
&BlockId::hash(block_hash),
sp_core::ExecutionContext::OffchainCall(None),
leaf_indices,
)
.map_err(runtime_error_into_rpc_error)?
.map_err(mmr_error_into_rpc_error)?;
Ok(LeafBatchProof::new(block_hash, leaves, proof))
}
}
const RUNTIME_ERROR: i64 = 8000;
@@ -179,6 +246,28 @@ mod tests {
);
}
#[test]
fn should_serialize_leaf_batch_proof() {
// given
let leaf = vec![1_u8, 2, 3, 4];
let proof = BatchProof {
leaf_indices: vec![1],
leaf_count: 9,
items: vec![H256::repeat_byte(1), H256::repeat_byte(2)],
};
let leaf_proof = LeafBatchProof::new(H256::repeat_byte(0), vec![leaf], proof);
// when
let actual = serde_json::to_string(&leaf_proof).unwrap();
// then
assert_eq!(
actual,
r#"{"blockHash":"0x0000000000000000000000000000000000000000000000000000000000000000","leaves":"0x041001020304","proof":"0x04010000000000000009000000000000000801010101010101010101010101010101010101010101010101010101010101010202020202020202020202020202020202020202020202020202020202020202"}"#
);
}
#[test]
fn should_deserialize_leaf_proof() {
// given
@@ -205,4 +294,31 @@ mod tests {
// then
assert_eq!(actual, expected);
}
#[test]
fn should_deserialize_leaf_batch_proof() {
// given
let expected = LeafBatchProof {
block_hash: H256::repeat_byte(0),
leaves: Bytes(vec![vec![1_u8, 2, 3, 4]].encode()),
proof: Bytes(
BatchProof {
leaf_indices: vec![1],
leaf_count: 9,
items: vec![H256::repeat_byte(1), H256::repeat_byte(2)],
}
.encode(),
),
};
// when
let actual: LeafBatchProof<H256> = serde_json::from_str(r#"{
"blockHash":"0x0000000000000000000000000000000000000000000000000000000000000000",
"leaves":"0x041001020304",
"proof":"0x04010000000000000009000000000000000801010101010101010101010101010101010101010101010101010101010101010202020202020202020202020202020202020202020202020202020202020202"
}"#).unwrap();
// then
assert_eq!(actual, expected);
}
}