From ad84e61b297bca0b6e07e7fae0be860aeadef084 Mon Sep 17 00:00:00 2001 From: David Salami <31099392+Wizdave97@users.noreply.github.com> Date: Sat, 7 Oct 2023 16:51:55 +0100 Subject: [PATCH] Return leaf index in mmr proofs (#42) --- .gitignore | 1 + parachain/modules/ismp/pallet/src/lib.rs | 103 +++++++++++------- parachain/modules/ismp/pallet/src/mmr/mmr.rs | 8 +- .../modules/ismp/pallet/src/primitives.rs | 4 +- parachain/modules/ismp/pallet/src/tests.rs | 2 +- parachain/modules/ismp/rpc/src/lib.rs | 78 ++++++++----- parachain/modules/ismp/runtime-api/src/lib.rs | 12 +- parachain/runtime/src/lib.rs | 16 +-- 8 files changed, 134 insertions(+), 90 deletions(-) diff --git a/.gitignore b/.gitignore index d8df91a9d..3a01158e2 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ **/zombienet/zombienet /pallet-ismp/evm/solidity/out /pallet-ismp/evm/solidity/cache +.env diff --git a/parachain/modules/ismp/pallet/src/lib.rs b/parachain/modules/ismp/pallet/src/lib.rs index 55768392e..8e5c828f2 100644 --- a/parachain/modules/ismp/pallet/src/lib.rs +++ b/parachain/modules/ismp/pallet/src/lib.rs @@ -459,11 +459,11 @@ impl Pallet { /// all the leaves to be present. /// It may return an error or panic if used incorrectly. pub fn generate_proof( - leaf_indices: Vec, + leaf_positions: Vec, ) -> Result<(Vec, primitives::Proof), primitives::Error> { let leaves_count = NumberOfLeaves::::get(); let mmr = Mmr::::new(leaves_count); - mmr.generate_proof(leaf_indices) + mmr.generate_proof(leaf_positions) } /// Provides a way to handle messages. @@ -579,7 +579,7 @@ pub struct RequestResponseLog { impl Pallet { /// Returns the offchain key for a request leaf index - pub fn request_leaf_index_offchain_key( + pub fn request_leaf_pos_and_index_offchain_key( source_chain: StateMachine, dest_chain: StateMachine, nonce: u64, @@ -588,7 +588,7 @@ impl Pallet { } /// Returns the offchain key for a response leaf index - pub fn response_leaf_index_offchain_key( + pub fn response_leaf_pos_and_index_offchain_key( source_chain: StateMachine, dest_chain: StateMachine, nonce: u64, @@ -596,14 +596,17 @@ impl Pallet { (T::INDEXING_PREFIX, "responses_leaf_indices", source_chain, dest_chain, nonce).encode() } - /// Stores the leaf index or the given key - pub fn store_leaf_index_offchain(key: Vec, leaf_index: LeafIndex) { - sp_io::offchain_index::set(&key, &leaf_index.encode()); + /// Stores the position and leaf index or the given key + pub fn store_leaf_position_and_index_offchain( + key: Vec, + leaf_pos_and_index: (LeafIndex, LeafIndex), + ) { + sp_io::offchain_index::set(&key, &leaf_pos_and_index.encode()); } /// Gets the request from the offchain storage - pub fn get_request(leaf_index: LeafIndex) -> Option { - let key = Pallet::::offchain_key(leaf_index); + pub fn get_request(leaf_position: LeafIndex) -> Option { + let key = Pallet::::offchain_key(leaf_position); if let Some(elem) = sp_io::offchain::local_storage_get(StorageKind::PERSISTENT, &key) { let data_or_hash = DataOrHash::decode(&mut &*elem).ok()?; return match data_or_hash { @@ -618,8 +621,8 @@ impl Pallet { } /// Gets the response from the offchain storage - pub fn get_response(leaf_index: LeafIndex) -> Option { - let key = Pallet::::offchain_key(leaf_index); + pub fn get_response(leaf_position: LeafIndex) -> Option { + let key = Pallet::::offchain_key(leaf_position); if let Some(elem) = sp_io::offchain::local_storage_get(StorageKind::PERSISTENT, &key) { let data_or_hash = DataOrHash::decode(&mut &*elem).ok()?; return match data_or_hash { @@ -633,20 +636,20 @@ impl Pallet { None } - /// Gets the leaf index for a request or response from the offchain storage - pub fn get_leaf_index( + /// Gets the positon and leaf index for a request or response from the offchain storage + pub fn get_position_and_leaf_index( source_chain: StateMachine, dest_chain: StateMachine, nonce: u64, is_req: bool, - ) -> Option { + ) -> Option<(LeafIndex, LeafIndex)> { let key = if is_req { - Self::request_leaf_index_offchain_key(source_chain, dest_chain, nonce) + Self::request_leaf_pos_and_index_offchain_key(source_chain, dest_chain, nonce) } else { - Self::response_leaf_index_offchain_key(source_chain, dest_chain, nonce) + Self::response_leaf_pos_and_index_offchain_key(source_chain, dest_chain, nonce) }; if let Some(elem) = sp_io::offchain::local_storage_get(StorageKind::PERSISTENT, &key) { - return LeafIndex::decode(&mut &*elem).ok() + return <(LeafIndex, LeafIndex)>::decode(&mut &*elem).ok() } None } @@ -655,9 +658,13 @@ impl Pallet { pub fn pending_get_requests() -> Vec { RequestCommitments::::iter() .filter_map(|(key, query)| { - let leaf_index = - Self::get_leaf_index(query.source_chain, query.dest_chain, query.nonce, true)?; - let req = Self::get_request(leaf_index)?; + let (position, _) = Self::get_position_and_leaf_index( + query.source_chain, + query.dest_chain, + query.nonce, + true, + )?; + let req = Self::get_request(position)?; (req.is_type_get() && !ResponseReceipts::::contains_key(key)) .then(|| req.get_request().ok()) .flatten() @@ -690,61 +697,75 @@ impl Pallet { Some(LatestStateMachineHeight::::get(id)) } - /// Get Request Leaf Indices - pub fn get_request_leaf_indices(leaf_queries: Vec) -> Vec { + /// Get Request Leaf positions and Indices + pub fn get_request_leaf_indices( + leaf_queries: Vec, + ) -> Vec<(LeafIndex, LeafIndex)> { leaf_queries .into_iter() .filter_map(|query| { - Self::get_leaf_index(query.source_chain, query.dest_chain, query.nonce, true) + Self::get_position_and_leaf_index( + query.source_chain, + query.dest_chain, + query.nonce, + true, + ) }) .collect() } - /// Get Response Leaf Indices - pub fn get_response_leaf_indices(leaf_queries: Vec) -> Vec { + /// Get Response Leaf positions and Indices + pub fn get_response_leaf_indices( + leaf_queries: Vec, + ) -> Vec<(LeafIndex, LeafIndex)> { leaf_queries .into_iter() .filter_map(|query| { - Self::get_leaf_index(query.source_chain, query.dest_chain, query.nonce, false) + Self::get_position_and_leaf_index( + query.source_chain, + query.dest_chain, + query.nonce, + false, + ) }) .collect() } /// Get actual requests - pub fn get_requests(leaf_indices: Vec) -> Vec { - leaf_indices + pub fn get_requests(leaf_positions: Vec) -> Vec { + leaf_positions .into_iter() - .filter_map(|leaf_index| Self::get_request(leaf_index)) + .filter_map(|leaf_pos| Self::get_request(leaf_pos)) .collect() } /// Get actual requests - pub fn get_responses(leaf_indices: Vec) -> Vec { - leaf_indices + pub fn get_responses(leaf_positions: Vec) -> Vec { + leaf_positions .into_iter() - .filter_map(|leaf_index| Self::get_response(leaf_index)) + .filter_map(|leaf_pos| Self::get_response(leaf_pos)) .collect() } - /// Insert a leaf into the mmr - pub(crate) fn mmr_push(leaf: Leaf) -> Option { + /// Insert a leaf into the mmr and return the position and leaf index + pub(crate) fn mmr_push(leaf: Leaf) -> Option<(NodeIndex, NodeIndex)> { let offchain_key = match &leaf { - Leaf::Request(req) => Pallet::::request_leaf_index_offchain_key( + Leaf::Request(req) => Pallet::::request_leaf_pos_and_index_offchain_key( req.source_chain(), req.dest_chain(), req.nonce(), ), - Leaf::Response(res) => Pallet::::response_leaf_index_offchain_key( - res.dest_chain(), + Leaf::Response(res) => Pallet::::response_leaf_pos_and_index_offchain_key( res.source_chain(), + res.dest_chain(), res.nonce(), ), }; let leaves = Self::number_of_leaves(); let mmr: Mmr = Mmr::new(leaves); - let pos = mmr.push(leaf)?; - Pallet::::store_leaf_index_offchain(offchain_key, pos); - Some(pos) + let (pos, leaf_index) = mmr.push(leaf)?; + Pallet::::store_leaf_position_and_index_offchain(offchain_key, (pos, leaf_index)); + Some((pos, leaf_index)) } } @@ -774,7 +795,7 @@ impl Pallet { NumberOfLeaves::::put(num_leaves) } - /// Returns the offchain key for an index + /// Returns the offchain key for a position in the mmr fn offchain_key(pos: NodeIndex) -> Vec { (T::INDEXING_PREFIX, "leaves", pos).encode() } diff --git a/parachain/modules/ismp/pallet/src/mmr/mmr.rs b/parachain/modules/ismp/pallet/src/mmr/mmr.rs index 9c7828814..15d6c71bd 100644 --- a/parachain/modules/ismp/pallet/src/mmr/mmr.rs +++ b/parachain/modules/ismp/pallet/src/mmr/mmr.rs @@ -58,13 +58,13 @@ where { /// Push another item to the MMR and commit /// - /// Returns number of leaves and the element position (index) in the MMR. - pub fn push(mut self, leaf: Leaf) -> Option { + /// Returns the element position (index) and number of leaves in the MMR. + pub fn push(mut self, leaf: Leaf) -> Option<(NodeIndex, NodeIndex)> { let position = self.mmr.push(DataOrHash::Data(leaf)).map_err(|_| Error::Push).ok()?; let num_leaves = self.leaves + 1; self.leaves = num_leaves; self.mmr.commit().ok()?; - Some(position) + Some((position, num_leaves)) } /// Calculate the new MMR's root hash. @@ -101,7 +101,7 @@ where .gen_proof(positions.clone()) .map_err(|_| Error::GenerateProof) .map(|p| Proof { - leaf_indices: positions, + leaf_positions: positions, leaf_count, items: p.proof_items().iter().map(|x| x.hash::>()).collect(), }) diff --git a/parachain/modules/ismp/pallet/src/primitives.rs b/parachain/modules/ismp/pallet/src/primitives.rs index b256769ab..05e21662b 100644 --- a/parachain/modules/ismp/pallet/src/primitives.rs +++ b/parachain/modules/ismp/pallet/src/primitives.rs @@ -31,8 +31,8 @@ use sp_std::prelude::*; /// An MMR proof data for a group of leaves. #[derive(codec::Encode, codec::Decode, RuntimeDebug, Clone, PartialEq, Eq, TypeInfo)] pub struct Proof { - /// The indices of the leaves the proof is for. - pub leaf_indices: Vec, + /// The positions of the leaves the proof is for. + pub leaf_positions: Vec, /// Number of leaves in MMR, when the proof was generated. pub leaf_count: NodeIndex, /// Proof elements (hashes of siblings of inner nodes on the path to the leaf). diff --git a/parachain/modules/ismp/pallet/src/tests.rs b/parachain/modules/ismp/pallet/src/tests.rs index 4378d0c97..a9c1b34dc 100644 --- a/parachain/modules/ismp/pallet/src/tests.rs +++ b/parachain/modules/ismp/pallet/src/tests.rs @@ -81,7 +81,7 @@ fn push_leaves(range: Range) -> Vec { let leaf = Leaf::Request(request); let pos = Pallet::::mmr_push(leaf.clone()).unwrap(); - positions.push(pos) + positions.push(pos.0) } positions diff --git a/parachain/modules/ismp/rpc/src/lib.rs b/parachain/modules/ismp/rpc/src/lib.rs index 2f9474fe6..427e097a8 100644 --- a/parachain/modules/ismp/rpc/src/lib.rs +++ b/parachain/modules/ismp/rpc/src/lib.rs @@ -27,7 +27,7 @@ use codec::Encode; use ismp::{ consensus::{ConsensusClientId, StateMachineId}, events::{ChallengePeriodStarted, Event, StateMachineUpdated}, - mmr::{Leaf, LeafIndex}, + mmr::{Leaf, LeafIndex, NodeIndex}, router::{Get, Request, Response}, LeafIndexQuery, }; @@ -59,13 +59,22 @@ impl Display for BlockNumberOrHash { } } +/// An MMR proof data for a group of leaves. +#[derive(codec::Encode, codec::Decode, Clone, PartialEq, Eq)] +pub struct MmrProof { + /// The positions and leaf indices the proof is for. + pub leaf_positions_and_indices: Vec<(LeafIndex, 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, +} + /// Contains a scale encoded Mmr Proof or Trie proof #[derive(Serialize, Deserialize)] pub struct Proof { - /// Scale encoded `pallet_ismp::primitives::Proof` or state trie proof `Vec>` + /// Scale encoded `MmrProof` or state trie proof `Vec>` pub proof: Vec, - /// Optional scale encoded `Vec` for mmr proof - pub leaves: Option>, /// Height at which proof was recovered pub height: u32, } @@ -173,15 +182,16 @@ where let mut api = self.client.runtime_api(); api.register_extension(OffchainDbExt::new(self.offchain_db.clone())); let at = self.client.info().best_hash; - let request_indices: Vec = + let request_pos_and_indices: Vec<(LeafIndex, LeafIndex)> = api.get_request_leaf_indices(at, query).map_err(|e| { runtime_error_into_rpc_error(format!( "Error fetching request leaf indices, {:?}", e )) })?; + let request_positions = request_pos_and_indices.into_iter().map(|(pos, _)| pos).collect(); - api.get_requests(at, request_indices) + api.get_requests(at, request_positions) .map_err(|_| runtime_error_into_rpc_error("Error fetching requests")) } @@ -189,11 +199,11 @@ where let mut api = self.client.runtime_api(); api.register_extension(OffchainDbExt::new(self.offchain_db.clone())); let at = self.client.info().best_hash; - let response_indices: Vec = api + let response_pos_and_indices: Vec<(LeafIndex, LeafIndex)> = api .get_response_leaf_indices(at, query) .map_err(|_| runtime_error_into_rpc_error("Error fetching response leaf indices"))?; - - api.get_responses(at, response_indices) + let response_positions = response_pos_and_indices.into_iter().map(|(pos, _)| pos).collect(); + api.get_responses(at, response_positions) .map_err(|_| runtime_error_into_rpc_error("Error fetching responses")) } @@ -206,15 +216,20 @@ where .ok() .flatten() .ok_or_else(|| runtime_error_into_rpc_error("invalid block height provided"))?; - let request_indices: Vec = api + let request_pos_and_indices: Vec<(LeafIndex, LeafIndex)> = api .get_request_leaf_indices(at, query) .map_err(|_| runtime_error_into_rpc_error("Error fetching response leaf indices"))?; - - let (leaves, proof): (Vec, pallet_ismp::primitives::Proof) = api - .generate_proof(at, request_indices) + let request_positions = request_pos_and_indices.iter().map(|(pos, _)| *pos).collect(); + let (_, proof): (Vec, pallet_ismp::primitives::Proof) = api + .generate_proof(at, request_positions) .map_err(|_| runtime_error_into_rpc_error("Error calling runtime api"))? .map_err(|_| runtime_error_into_rpc_error("Error generating mmr proof"))?; - Ok(Proof { proof: proof.encode(), leaves: Some(leaves.encode()), height }) + let proof = MmrProof { + leaf_positions_and_indices: request_pos_and_indices, + leaf_count: proof.leaf_count, + items: proof.items, + }; + Ok(Proof { proof: proof.encode(), height }) } fn query_responses_mmr_proof(&self, height: u32, query: Vec) -> Result { @@ -226,15 +241,20 @@ where .ok() .flatten() .ok_or_else(|| runtime_error_into_rpc_error("invalid block height provided"))?; - let response_indices: Vec = api + let response_pos_and_indices: Vec<(LeafIndex, LeafIndex)> = api .get_response_leaf_indices(at, query) .map_err(|_| runtime_error_into_rpc_error("Error fetching response leaf indices"))?; - - let (leaves, proof): (Vec, pallet_ismp::primitives::Proof) = api - .generate_proof(at, response_indices) + let response_positions = response_pos_and_indices.iter().map(|(pos, _)| *pos).collect(); + let (_, proof): (Vec, pallet_ismp::primitives::Proof) = api + .generate_proof(at, response_positions) .map_err(|_| runtime_error_into_rpc_error("Error calling runtime api"))? .map_err(|_| runtime_error_into_rpc_error("Error generating mmr proof"))?; - Ok(Proof { proof: proof.encode(), leaves: Some(leaves.encode()), height }) + let proof = MmrProof { + leaf_positions_and_indices: response_pos_and_indices, + leaf_count: proof.leaf_count, + items: proof.items, + }; + Ok(Proof { proof: proof.encode(), height }) } fn query_state_proof(&self, height: u32, keys: Vec>) -> Result { @@ -246,7 +266,7 @@ where .read_proof(at, &mut keys.iter().map(|key| key.as_slice())) .map(|proof| proof.into_iter_nodes().collect()) .map_err(|_| runtime_error_into_rpc_error("Error reading state proof"))?; - Ok(Proof { proof: proof.encode(), leaves: None, height }) + Ok(Proof { proof: proof.encode(), height }) } fn query_consensus_state( @@ -316,8 +336,8 @@ where })?, }; - let mut request_indices = vec![]; - let mut response_indices = vec![]; + let mut request_positions = vec![]; + let mut response_positions = vec![]; let mut temp: Vec = api .block_events(at) .map_err(|e| { @@ -332,9 +352,10 @@ where } => { let query = LeafIndexQuery { source_chain, dest_chain, nonce: request_nonce }; - let indices: Vec = + let positions_and_indices: Vec<(LeafIndex, LeafIndex)> = api.get_request_leaf_indices(at, vec![query]).ok()?; - request_indices.extend_from_slice(&indices); + let positions = positions_and_indices.into_iter().map(|(pos, _)| pos); + request_positions.extend(positions); None }, pallet_ismp::events::Event::Response { @@ -344,9 +365,10 @@ where } => { let query = LeafIndexQuery { source_chain, dest_chain, nonce: request_nonce }; - let indices: Vec = + let positions_and_indices: Vec<(LeafIndex, LeafIndex)> = api.get_response_leaf_indices(at, vec![query]).ok()?; - response_indices.extend_from_slice(&indices); + let positions = positions_and_indices.into_iter().map(|(pos, _)| pos); + response_positions.extend(positions); None }, pallet_ismp::events::Event::ChallengePeriodStarted { @@ -367,7 +389,7 @@ where .collect(); let request_events = api - .get_requests(at, request_indices) + .get_requests(at, request_positions) .map_err(|_| runtime_error_into_rpc_error("Error fetching requests"))? .into_iter() .map(|req| match req { @@ -376,7 +398,7 @@ where }); let response_events = api - .get_responses(at, response_indices) + .get_responses(at, response_positions) .map_err(|_| runtime_error_into_rpc_error("Error fetching response"))? .into_iter() .filter_map(|res| match res { diff --git a/parachain/modules/ismp/runtime-api/src/lib.rs b/parachain/modules/ismp/runtime-api/src/lib.rs index 6cae224b7..b7192db4d 100644 --- a/parachain/modules/ismp/runtime-api/src/lib.rs +++ b/parachain/modules/ismp/runtime-api/src/lib.rs @@ -42,7 +42,7 @@ sp_api::decl_runtime_apis! { /// Generate a proof for the provided leaf indices fn generate_proof( - leaf_indices: Vec + leaf_positions: Vec ) -> Result<(Vec, Proof), Error>; /// Fetch all ISMP events @@ -64,18 +64,18 @@ sp_api::decl_runtime_apis! { fn latest_messaging_height(id: StateMachineId) -> Option; /// Get Request Leaf Indices - fn get_request_leaf_indices(leaf_queries: Vec) -> Vec; + fn get_request_leaf_indices(leaf_queries: Vec) -> Vec<(LeafIndex, LeafIndex)>; /// Get Response Leaf Indices - fn get_response_leaf_indices(leaf_queries: Vec) -> Vec; + fn get_response_leaf_indices(leaf_queries: Vec) -> Vec<(LeafIndex, LeafIndex)>; /// Get actual requests - fn get_requests(leaf_indices: Vec) -> Vec; + fn get_requests(leaf_positions: Vec) -> Vec; /// Fetch all Get requests that have received no response fn pending_get_requests() -> Vec; - /// Get actual requests - fn get_responses(leaf_indices: Vec) -> Vec; + /// Get actual responses + fn get_responses(leaf_positions: Vec) -> Vec; } } diff --git a/parachain/runtime/src/lib.rs b/parachain/runtime/src/lib.rs index 4bfc8b2b3..70ec217d6 100644 --- a/parachain/runtime/src/lib.rs +++ b/parachain/runtime/src/lib.rs @@ -703,9 +703,9 @@ impl_runtime_apis! { /// Generate a proof for the provided leaf indices fn generate_proof( - leaf_indices: Vec + leaf_positions: Vec ) -> Result<(Vec, Proof<::Hash>), pallet_ismp::primitives::Error> { - Ismp::generate_proof(leaf_indices) + Ismp::generate_proof(leaf_positions) } /// Fetch all ISMP events @@ -744,23 +744,23 @@ impl_runtime_apis! { } /// Get Request Leaf Indices - fn get_request_leaf_indices(leaf_queries: Vec) -> Vec { + fn get_request_leaf_indices(leaf_queries: Vec) -> Vec<(LeafIndex, LeafIndex)> { Ismp::get_request_leaf_indices(leaf_queries) } /// Get Response Leaf Indices - fn get_response_leaf_indices(leaf_queries: Vec) -> Vec { + fn get_response_leaf_indices(leaf_queries: Vec) -> Vec<(LeafIndex, LeafIndex)> { Ismp::get_response_leaf_indices(leaf_queries) } /// Get actual requests - fn get_requests(leaf_indices: Vec) -> Vec { - Ismp::get_requests(leaf_indices) + fn get_requests(leaf_positions: Vec) -> Vec { + Ismp::get_requests(leaf_positions) } /// Get actual requests - fn get_responses(leaf_indices: Vec) -> Vec { - Ismp::get_responses(leaf_indices) + fn get_responses(leaf_positions: Vec) -> Vec { + Ismp::get_responses(leaf_positions) } fn pending_get_requests() -> Vec<::ismp::router::Get> {