From 215ec5f0a7f2137186ea2dc1711536fc323cf9b0 Mon Sep 17 00:00:00 2001 From: hu55a1n1 Date: Fri, 22 Dec 2023 02:35:42 -0800 Subject: [PATCH] Extract mod merkle --- utils/cw-prover/src/main.rs | 117 ++-------------------------------- utils/cw-prover/src/merkle.rs | 113 ++++++++++++++++++++++++++++++++ 2 files changed, 117 insertions(+), 113 deletions(-) create mode 100644 utils/cw-prover/src/merkle.rs diff --git a/utils/cw-prover/src/main.rs b/utils/cw-prover/src/main.rs index 27aa920..1233276 100644 --- a/utils/cw-prover/src/main.rs +++ b/utils/cw-prover/src/main.rs @@ -18,24 +18,17 @@ use std::error::Error; use clap::{Parser, Subcommand}; use cosmrs::AccountId; -use ibc_proto::{ - ibc::core::commitment::v1::MerkleProof as RawMerkleProof, ibc::core::commitment::v1::MerkleRoot, -}; use ibc_relayer_types::{ - core::ics23_commitment::commitment::CommitmentRoot, - core::ics23_commitment::error::Error as ProofError, core::ics23_commitment::specs::ProofSpecs, -}; -use ics23::{ - calculate_existence_root, commitment_proof::Proof, verify_membership, CommitmentProof, - ProofSpec, + core::ics23_commitment::commitment::CommitmentRoot, core::ics23_commitment::specs::ProofSpecs, }; use tendermint::block::Height; -use tendermint::merkle::proof::ProofOps as TendermintProof; use tendermint::AppHash; use tendermint_rpc::endpoint::abci_query::AbciQuery; use tendermint_rpc::endpoint::status::Response; use tendermint_rpc::{client::HttpClient as TmRpcClient, Client, HttpClientUrl}; +mod merkle; + #[derive(Debug, Parser)] #[command(author, version, about, long_about = None)] struct Cli { @@ -137,7 +130,7 @@ fn encode_length(namespace: &[u8]) -> [u8; 2] { } fn verify_proof(latest_app_hash: AppHash, result: AbciQuery) -> Result, Box> { - let proof = convert_tm_to_ics_merkle_proof(&result.proof.expect("queried with proof"))?; + let proof = merkle::convert_tm_to_ics_merkle_proof(&result.proof.expect("queried with proof"))?; let root = CommitmentRoot::from_bytes(latest_app_hash.as_bytes()); let prefixed_key = vec!["wasm".to_string().into_bytes(), result.key]; @@ -151,105 +144,3 @@ fn verify_proof(latest_app_hash: AppHash, result: AbciQuery) -> Result, Ok(result.value) } - -// Copied from hermes and patched to allow non-string keys -#[derive(Clone, Debug, PartialEq)] -struct MerkleProof { - proofs: Vec, -} - -/// Convert to ics23::CommitmentProof -impl From for MerkleProof { - fn from(proof: RawMerkleProof) -> Self { - Self { - proofs: proof.proofs, - } - } -} - -impl From for RawMerkleProof { - fn from(proof: MerkleProof) -> Self { - Self { - proofs: proof.proofs, - } - } -} - -impl MerkleProof { - pub fn verify_membership( - &self, - specs: &ProofSpecs, - root: MerkleRoot, - keys: Vec>, - value: Vec, - start_index: usize, - ) -> Result<(), ProofError> { - // validate arguments - if self.proofs.is_empty() { - return Err(ProofError::empty_merkle_proof()); - } - if root.hash.is_empty() { - return Err(ProofError::empty_merkle_root()); - } - let num = self.proofs.len(); - let ics23_specs = Vec::::from(specs.clone()); - if ics23_specs.len() != num { - return Err(ProofError::number_of_specs_mismatch()); - } - if keys.len() != num { - return Err(ProofError::number_of_keys_mismatch()); - } - if value.is_empty() { - return Err(ProofError::empty_verified_value()); - } - - let mut subroot = value.clone(); - let mut value = value; - - // keys are represented from root-to-leaf - for ((proof, spec), key) in self - .proofs - .iter() - .zip(ics23_specs.iter()) - .zip(keys.iter().rev()) - .skip(start_index) - { - match &proof.proof { - Some(Proof::Exist(existence_proof)) => { - subroot = - calculate_existence_root::(existence_proof) - .map_err(|_| ProofError::invalid_merkle_proof())?; - - if !verify_membership::( - proof, spec, &subroot, key, &value, - ) { - return Err(ProofError::verification_failure()); - } - value = subroot.clone(); - } - _ => return Err(ProofError::invalid_merkle_proof()), - } - } - - if root.hash != subroot { - return Err(ProofError::verification_failure()); - } - - Ok(()) - } -} - -fn convert_tm_to_ics_merkle_proof(tm_proof: &TendermintProof) -> Result { - let mut proofs = Vec::new(); - - for op in &tm_proof.ops { - let mut parsed = CommitmentProof { proof: None }; - - prost::Message::merge(&mut parsed, op.data.as_slice()) - .map_err(ProofError::commitment_proof_decoding_failed)?; - - proofs.push(parsed); - } - - Ok(MerkleProof::from(RawMerkleProof { proofs })) -} diff --git a/utils/cw-prover/src/merkle.rs b/utils/cw-prover/src/merkle.rs new file mode 100644 index 0000000..35c67e7 --- /dev/null +++ b/utils/cw-prover/src/merkle.rs @@ -0,0 +1,113 @@ +use ibc_proto::ibc::core::commitment::v1::{MerkleProof as RawMerkleProof, MerkleRoot}; +use ibc_relayer_types::{ + core::ics23_commitment::error::Error as ProofError, core::ics23_commitment::specs::ProofSpecs, +}; +use ics23::{ + calculate_existence_root, commitment_proof::Proof, verify_membership, CommitmentProof, + ProofSpec, +}; +use tendermint::merkle::proof::ProofOps as TendermintProof; + +// Copied from hermes and patched to allow non-string keys +#[derive(Clone, Debug, PartialEq)] +pub struct MerkleProof { + proofs: Vec, +} + +/// Convert to ics23::CommitmentProof +impl From for MerkleProof { + fn from(proof: RawMerkleProof) -> Self { + Self { + proofs: proof.proofs, + } + } +} + +impl From for RawMerkleProof { + fn from(proof: MerkleProof) -> Self { + Self { + proofs: proof.proofs, + } + } +} + +impl MerkleProof { + pub fn verify_membership( + &self, + specs: &ProofSpecs, + root: MerkleRoot, + keys: Vec>, + value: Vec, + start_index: usize, + ) -> Result<(), ProofError> { + // validate arguments + if self.proofs.is_empty() { + return Err(ProofError::empty_merkle_proof()); + } + if root.hash.is_empty() { + return Err(ProofError::empty_merkle_root()); + } + let num = self.proofs.len(); + let ics23_specs = Vec::::from(specs.clone()); + if ics23_specs.len() != num { + return Err(ProofError::number_of_specs_mismatch()); + } + if keys.len() != num { + return Err(ProofError::number_of_keys_mismatch()); + } + if value.is_empty() { + return Err(ProofError::empty_verified_value()); + } + + let mut subroot = value.clone(); + let mut value = value; + + // keys are represented from root-to-leaf + for ((proof, spec), key) in self + .proofs + .iter() + .zip(ics23_specs.iter()) + .zip(keys.iter().rev()) + .skip(start_index) + { + match &proof.proof { + Some(Proof::Exist(existence_proof)) => { + subroot = + calculate_existence_root::(existence_proof) + .map_err(|_| ProofError::invalid_merkle_proof())?; + + if !verify_membership::( + proof, spec, &subroot, key, &value, + ) { + return Err(ProofError::verification_failure()); + } + value = subroot.clone(); + } + _ => return Err(ProofError::invalid_merkle_proof()), + } + } + + if root.hash != subroot { + return Err(ProofError::verification_failure()); + } + + Ok(()) + } +} + +pub fn convert_tm_to_ics_merkle_proof( + tm_proof: &TendermintProof, +) -> Result { + let mut proofs = Vec::new(); + + for op in &tm_proof.ops { + let mut parsed = CommitmentProof { proof: None }; + + prost::Message::merge(&mut parsed, op.data.as_slice()) + .map_err(ProofError::commitment_proof_decoding_failed)?; + + proofs.push(parsed); + } + + Ok(MerkleProof::from(RawMerkleProof { proofs })) +}