From 40a044429a30c3fa104ca146a5b42bf6497672cd Mon Sep 17 00:00:00 2001 From: hu55a1n1 Date: Sat, 30 Dec 2023 04:50:47 -0800 Subject: [PATCH] Reorg --- utils/cw-prover/src/main.rs | 31 +- utils/cw-prover/src/merkle.rs | 304 -------------------- utils/cw-prover/src/proof/cw.rs | 70 +++++ utils/cw-prover/src/proof/key.rs | 39 +++ utils/cw-prover/src/proof/mod.rs | 34 +++ utils/cw-prover/src/proof/prefix.rs | 9 + utils/cw-prover/src/proof/verifier/cw.rs | 39 +++ utils/cw-prover/src/proof/verifier/ics23.rs | 62 ++++ utils/cw-prover/src/proof/verifier/mod.rs | 29 ++ utils/cw-prover/src/proof/verifier/multi.rs | 47 +++ 10 files changed, 350 insertions(+), 314 deletions(-) delete mode 100644 utils/cw-prover/src/merkle.rs create mode 100644 utils/cw-prover/src/proof/cw.rs create mode 100644 utils/cw-prover/src/proof/key.rs create mode 100644 utils/cw-prover/src/proof/mod.rs create mode 100644 utils/cw-prover/src/proof/prefix.rs create mode 100644 utils/cw-prover/src/proof/verifier/cw.rs create mode 100644 utils/cw-prover/src/proof/verifier/ics23.rs create mode 100644 utils/cw-prover/src/proof/verifier/mod.rs create mode 100644 utils/cw-prover/src/proof/verifier/multi.rs diff --git a/utils/cw-prover/src/main.rs b/utils/cw-prover/src/main.rs index fedec86..47abeda 100644 --- a/utils/cw-prover/src/main.rs +++ b/utils/cw-prover/src/main.rs @@ -14,7 +14,7 @@ unused_qualifications )] -mod merkle; +mod proof; use std::error::Error; use std::fmt::Debug; @@ -30,7 +30,7 @@ use tendermint_rpc::endpoint::abci_query::AbciQuery; use tendermint_rpc::endpoint::status::Response; use tendermint_rpc::{client::HttpClient as TmRpcClient, Client, HttpClientUrl}; -use crate::merkle::{CwProof, RawQueryProof}; +use crate::proof::{cw::RawCwProof, Proof}; #[derive(Debug, Parser)] #[command(author, version, about, long_about = None)] @@ -91,7 +91,7 @@ async fn main() -> Result<(), Box> { .abci_query(Some(path), data, Some(proof_height), true) .await?; - let proof: RawQueryProof = result.clone().try_into().expect("todo"); + let proof: RawCwProof = result.clone().try_into().expect("todo"); proof.verify(latest_app_hash.clone().into())?; println!("{}", String::from_utf8(result.value.clone())?); @@ -153,8 +153,9 @@ fn write_proof_to_file(proof_file: PathBuf, output: AbciQuery) -> Result<(), Box #[cfg(test)] mod tests { + use crate::proof::cw::RawCwProof; + use crate::proof::Proof; use tendermint_rpc::endpoint::abci_query::AbciQuery; - use crate::merkle::{CwProof, RawQueryProof}; #[test] fn test_query_item() { @@ -184,11 +185,16 @@ mod tests { } "#; - let abci_query: AbciQuery = serde_json::from_str(abci_query_response).expect("deserialization failure for hardcoded response"); - let proof: RawQueryProof = abci_query.try_into().expect("hardcoded response does not include proof"); + let abci_query: AbciQuery = serde_json::from_str(abci_query_response) + .expect("deserialization failure for hardcoded response"); + let proof: RawCwProof = abci_query + .try_into() + .expect("hardcoded response does not include proof"); let root = "25a8b485e0ff095f7b60a1aab837d65756c9a4cdc216bae7ba9c59b3fb28fbec"; - proof.verify(hex::decode(root).expect("invalid hex")).expect(""); + proof + .verify(hex::decode(root).expect("invalid hex")) + .expect(""); } #[test] @@ -219,10 +225,15 @@ mod tests { } "#; - let abci_query: AbciQuery = serde_json::from_str(abci_query_response).expect("deserialization failure for hardcoded response"); - let proof: RawQueryProof = abci_query.try_into().expect("hardcoded response does not include proof"); + let abci_query: AbciQuery = serde_json::from_str(abci_query_response) + .expect("deserialization failure for hardcoded response"); + let proof: RawCwProof = abci_query + .try_into() + .expect("hardcoded response does not include proof"); let root = "632612de75657f50bbb769157bf0ef8dd417409b367b0204bbda4529ab2b2d4f"; - proof.verify(hex::decode(root).expect("invalid hex")).expect(""); + proof + .verify(hex::decode(root).expect("invalid hex")) + .expect(""); } } diff --git a/utils/cw-prover/src/merkle.rs b/utils/cw-prover/src/merkle.rs deleted file mode 100644 index f6a9e65..0000000 --- a/utils/cw-prover/src/merkle.rs +++ /dev/null @@ -1,304 +0,0 @@ -use core::fmt::Debug; -use core::marker::PhantomData; - -use ibc_relayer_types::core::ics23_commitment::error::Error as ProofError; -use ics23::{ - calculate_existence_root, commitment_proof::Proof, verify_membership, CommitmentProof, - ProofSpec, -}; -use tendermint::merkle::proof::ProofOps; -use tendermint_rpc::endpoint::abci_query::AbciQuery; - -// Copied from hermes -pub fn convert_tm_to_ics_merkle_proof( - tm_proof: &ProofOps, -) -> Result, ProofError> { - 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(proofs) -} - -trait Verifier { - type Proof; - type Root: Eq; - type Key; - type Value; - type Error; - - fn verify( - &self, - proof: &Self::Proof, - key: &Self::Key, - value: &Self::Value, - ) -> Result; - - fn verify_against_root( - &self, - proof: &Self::Proof, - key: &Self::Key, - value: &Self::Value, - root: &Self::Root, - ) -> Result { - let found_root = self.verify(proof, key, value)?; - Ok(root == &found_root) - } -} - -#[derive(Clone, Debug)] -struct Ics23MembershipVerifier { - spec: ProofSpec, - _phantom: PhantomData<(K, V)>, -} - -impl Ics23MembershipVerifier { - fn new(spec: ProofSpec) -> Self { - Self { - spec, - _phantom: Default::default(), - } - } -} - -impl Verifier for Ics23MembershipVerifier -where - K: AsRef<[u8]>, - V: AsRef<[u8]>, -{ - type Proof = CommitmentProof; - type Root = Vec; - type Key = K; - type Value = V; - type Error = ProofError; - - fn verify( - &self, - commitment_proof: &Self::Proof, - key: &Self::Key, - value: &Self::Value, - ) -> Result { - if value.as_ref().is_empty() { - return Err(ProofError::empty_verified_value()); - } - - let Some(Proof::Exist(existence_proof)) = &commitment_proof.proof else { - return Err(ProofError::invalid_merkle_proof()); - }; - - let root = calculate_existence_root::(existence_proof) - .map_err(|_| ProofError::invalid_merkle_proof())?; - - if !verify_membership::( - commitment_proof, - &self.spec, - &root, - key.as_ref(), - value.as_ref(), - ) { - return Err(ProofError::verification_failure()); - } - - Ok(root) - } -} - -#[derive(Clone, Debug)] -struct MultiVerifier { - verifiers: [V; N], -} - -impl MultiVerifier { - fn new(verifiers: [V; N]) -> Self { - assert!(N > 0, "cannot create empty multi-verifiers"); - Self { verifiers } - } -} - -impl Verifier for MultiVerifier -where - V: Verifier, - V::Value: Clone, - V::Root: Into + Clone, -{ - type Proof = [V::Proof; N]; - type Root = V::Root; - type Key = [V::Key; N]; - type Value = V::Value; - type Error = V::Error; - - fn verify( - &self, - proofs: &Self::Proof, - keys: &Self::Key, - value: &Self::Value, - ) -> Result { - let mut root = None; - let mut value = value.clone(); - - for (idx, verifier) in self.verifiers.iter().enumerate() { - let proof = &proofs[idx]; - let key = &keys[N - idx - 1]; - let sub_root = verifier.verify(proof, key, &value)?; - - value = sub_root.clone().into(); - root = Some(sub_root); - } - - Ok(root.expect("MultiVerifier cannot be instantiated with 0 verifiers")) - } -} - -#[derive(Clone, Debug)] -pub struct CwVerifier(MultiVerifier, Vec>, 2>); - -impl CwVerifier { - pub fn new() -> Self { - let mv = MultiVerifier::new([ - Ics23MembershipVerifier::new(ics23::iavl_spec()), - Ics23MembershipVerifier::new(ics23::tendermint_spec()), - ]); - Self(mv) - } - - pub fn verify( - &self, - proofs: &[CommitmentProof; 2], - root: &Vec, - keys: &[Vec; 2], - value: &Vec, - ) -> Result<(), ProofError> { - if root.is_empty() { - return Err(ProofError::empty_merkle_root()); - } - - let verified = self.0.verify_against_root(proofs, keys, value, root)?; - if !verified { - return Err(ProofError::verification_failure()); - } - Ok(()) - } -} - -pub trait CwProof { - type Key; - type Value; - type Proof; - - fn verify(self, root: Vec) -> Result<(), ProofError>; -} - -trait IntoKeys { - fn into_keys(self) -> Vec>; -} - -struct PrefixedKey { - key: K, - prefix: PhantomData

, -} - -impl PrefixedKey { - fn new(key: K) -> Self { - Self { - key, - prefix: PhantomData, - } - } -} - -impl IntoKeys for PrefixedKey -where - K: Into>, - P: ConstPrefix, -{ - fn into_keys(self) -> Vec> { - vec![P::PREFIX.to_string().into_bytes(), self.key.into()] - } -} - -struct PrefixWasm; - -trait ConstPrefix { - const PREFIX: &'static str; -} - -impl ConstPrefix for PrefixWasm { - const PREFIX: &'static str = "wasm"; -} - -impl IntoKeys for K -where - K: Into>, -{ - fn into_keys(self) -> Vec> { - vec![self.into()] - } -} - -pub struct QueryProof { - proof: ProofOps, - key: PrefixedKey, - value: V, -} - -pub type RawQueryProof = QueryProof, Vec>; - -#[derive(Clone, Debug)] -pub struct ErrorWithoutProof; - -impl TryFrom for RawQueryProof { - type Error = ErrorWithoutProof; - - fn try_from(query: AbciQuery) -> Result { - let AbciQuery { - key, value, proof, .. - } = query; - let Some(proof) = proof else { - return Err(ErrorWithoutProof); - }; - - Ok(Self { - proof, - key: PrefixedKey::new(key), - value, - }) - } -} - -impl CwProof for QueryProof -where - K: Into>, - V: Into>, -{ - type Key = K; - type Value = V; - type Proof = ProofOps; - - fn verify(self, root: Vec) -> Result<(), ProofError> { - fn as_array_2(v: Vec) -> Result<[T; 2], ProofError> { - let boxed_slice = v.into_boxed_slice(); - let boxed_array: Box<[T; 2]> = boxed_slice.try_into().expect("todo"); - Ok(*boxed_array) - } - - let Self { proof, key, value } = self; - let proofs = convert_tm_to_ics_merkle_proof(&proof)?; - - let cw_verifier = CwVerifier::new(); - cw_verifier.verify( - &as_array_2(proofs)?, - &root, - &as_array_2(key.into_keys())?, - &value.into(), - )?; - - Ok(()) - } -} diff --git a/utils/cw-prover/src/proof/cw.rs b/utils/cw-prover/src/proof/cw.rs new file mode 100644 index 0000000..ea29a9b --- /dev/null +++ b/utils/cw-prover/src/proof/cw.rs @@ -0,0 +1,70 @@ +use crate::proof; +use crate::proof::key::{IntoKeys, PrefixedKey}; +use crate::proof::prefix::PrefixWasm; +use crate::proof::verifier::cw::CwVerifier; +use crate::proof::Proof; +use ibc_relayer_types::core::ics23_commitment::error::Error as ProofError; +use std::fmt::Debug; +use tendermint::merkle::proof::ProofOps; +use tendermint_rpc::endpoint::abci_query::AbciQuery; + +pub type RawCwProof = CwProof, Vec>; + +pub struct CwProof { + proof: ProofOps, + key: PrefixedKey, + value: V, +} + +#[derive(Clone, Debug)] +pub struct ErrorWithoutProof; + +impl TryFrom for RawCwProof { + type Error = ErrorWithoutProof; + + fn try_from(query: AbciQuery) -> Result { + let AbciQuery { + key, value, proof, .. + } = query; + let Some(proof) = proof else { + return Err(ErrorWithoutProof); + }; + + Ok(Self { + proof, + key: PrefixedKey::new(key), + value, + }) + } +} + +impl Proof for CwProof +where + K: Into>, + V: Into>, +{ + type Key = K; + type Value = V; + type ProofOps = ProofOps; + + fn verify(self, root: Vec) -> Result<(), ProofError> { + fn into_array_of_size_2(v: Vec) -> Result<[T; 2], ProofError> { + let boxed_slice = v.into_boxed_slice(); + let boxed_array: Box<[T; 2]> = boxed_slice.try_into().expect("todo"); + Ok(*boxed_array) + } + + let Self { proof, key, value } = self; + let proofs = proof::convert_tm_to_ics_merkle_proof(&proof)?; + + let cw_verifier = CwVerifier::new(); + cw_verifier.verify( + &into_array_of_size_2(proofs)?, + &root, + &into_array_of_size_2(key.into_keys())?, + &value.into(), + )?; + + Ok(()) + } +} diff --git a/utils/cw-prover/src/proof/key.rs b/utils/cw-prover/src/proof/key.rs new file mode 100644 index 0000000..05fd71f --- /dev/null +++ b/utils/cw-prover/src/proof/key.rs @@ -0,0 +1,39 @@ +use crate::proof::prefix::ConstPrefix; +use std::marker::PhantomData; + +pub trait IntoKeys { + fn into_keys(self) -> Vec>; +} + +impl IntoKeys for K +where + K: Into>, +{ + fn into_keys(self) -> Vec> { + vec![self.into()] + } +} + +pub struct PrefixedKey { + key: K, + prefix: PhantomData

, +} + +impl PrefixedKey { + pub fn new(key: K) -> Self { + Self { + key, + prefix: PhantomData, + } + } +} + +impl IntoKeys for PrefixedKey +where + K: Into>, + P: ConstPrefix, +{ + fn into_keys(self) -> Vec> { + vec![P::PREFIX.to_string().into_bytes(), self.key.into()] + } +} diff --git a/utils/cw-prover/src/proof/mod.rs b/utils/cw-prover/src/proof/mod.rs new file mode 100644 index 0000000..8fffb26 --- /dev/null +++ b/utils/cw-prover/src/proof/mod.rs @@ -0,0 +1,34 @@ +use ibc_relayer_types::core::ics23_commitment::error::Error as ProofError; +use ics23::CommitmentProof; +use tendermint::merkle::proof::ProofOps; + +pub mod cw; +mod key; +mod prefix; +mod verifier; + +// Copied from hermes +pub fn convert_tm_to_ics_merkle_proof( + tm_proof: &ProofOps, +) -> Result, ProofError> { + 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(proofs) +} + +pub trait Proof { + type Key; + type Value; + type ProofOps; + + fn verify(self, root: Vec) -> Result<(), ProofError>; +} diff --git a/utils/cw-prover/src/proof/prefix.rs b/utils/cw-prover/src/proof/prefix.rs new file mode 100644 index 0000000..62db0df --- /dev/null +++ b/utils/cw-prover/src/proof/prefix.rs @@ -0,0 +1,9 @@ +pub struct PrefixWasm; + +pub trait ConstPrefix { + const PREFIX: &'static str; +} + +impl ConstPrefix for PrefixWasm { + const PREFIX: &'static str = "wasm"; +} diff --git a/utils/cw-prover/src/proof/verifier/cw.rs b/utils/cw-prover/src/proof/verifier/cw.rs new file mode 100644 index 0000000..241644d --- /dev/null +++ b/utils/cw-prover/src/proof/verifier/cw.rs @@ -0,0 +1,39 @@ +use ibc_relayer_types::core::ics23_commitment::error::Error as ProofError; +use ics23::CommitmentProof; + +use crate::{ + proof::verifier::ics23::Ics23MembershipVerifier, proof::verifier::multi::MultiVerifier, + proof::verifier::Verifier, +}; + +#[derive(Clone, Debug)] +pub struct CwVerifier(MultiVerifier, Vec>, 2>); + +impl CwVerifier { + pub fn new() -> Self { + let mv = MultiVerifier::new([ + Ics23MembershipVerifier::new(ics23::iavl_spec()), + Ics23MembershipVerifier::new(ics23::tendermint_spec()), + ]); + Self(mv) + } + + pub fn verify( + &self, + proofs: &[CommitmentProof; 2], + root: &Vec, + keys: &[Vec; 2], + value: &Vec, + ) -> Result<(), ProofError> { + if root.is_empty() { + return Err(ProofError::empty_merkle_root()); + } + + let verified = self.0.verify_against_root(proofs, keys, value, root)?; + if !verified { + return Err(ProofError::verification_failure()); + } + + Ok(()) + } +} diff --git a/utils/cw-prover/src/proof/verifier/ics23.rs b/utils/cw-prover/src/proof/verifier/ics23.rs new file mode 100644 index 0000000..db60946 --- /dev/null +++ b/utils/cw-prover/src/proof/verifier/ics23.rs @@ -0,0 +1,62 @@ +use crate::proof::verifier::Verifier; +use ibc_relayer_types::core::ics23_commitment::error::Error as ProofError; +use ics23::commitment_proof::Proof; +use ics23::{calculate_existence_root, verify_membership, CommitmentProof, ProofSpec}; +use std::marker::PhantomData; + +#[derive(Clone, Debug)] +pub struct Ics23MembershipVerifier { + spec: ProofSpec, + _phantom: PhantomData<(K, V)>, +} + +impl Ics23MembershipVerifier { + pub fn new(spec: ProofSpec) -> Self { + Self { + spec, + _phantom: Default::default(), + } + } +} + +impl Verifier for Ics23MembershipVerifier +where + K: AsRef<[u8]>, + V: AsRef<[u8]>, +{ + type Proof = CommitmentProof; + type Root = Vec; + type Key = K; + type Value = V; + type Error = ProofError; + + fn verify( + &self, + commitment_proof: &Self::Proof, + key: &Self::Key, + value: &Self::Value, + ) -> Result { + if value.as_ref().is_empty() { + return Err(ProofError::empty_verified_value()); + } + + let Some(Proof::Exist(existence_proof)) = &commitment_proof.proof else { + return Err(ProofError::invalid_merkle_proof()); + }; + + let root = calculate_existence_root::(existence_proof) + .map_err(|_| ProofError::invalid_merkle_proof())?; + + if !verify_membership::( + commitment_proof, + &self.spec, + &root, + key.as_ref(), + value.as_ref(), + ) { + return Err(ProofError::verification_failure()); + } + + Ok(root) + } +} diff --git a/utils/cw-prover/src/proof/verifier/mod.rs b/utils/cw-prover/src/proof/verifier/mod.rs new file mode 100644 index 0000000..1ce6bc6 --- /dev/null +++ b/utils/cw-prover/src/proof/verifier/mod.rs @@ -0,0 +1,29 @@ +pub mod cw; +pub mod ics23; +pub mod multi; + +trait Verifier { + type Proof; + type Root: Eq; + type Key; + type Value; + type Error; + + fn verify( + &self, + proof: &Self::Proof, + key: &Self::Key, + value: &Self::Value, + ) -> Result; + + fn verify_against_root( + &self, + proof: &Self::Proof, + key: &Self::Key, + value: &Self::Value, + root: &Self::Root, + ) -> Result { + let found_root = self.verify(proof, key, value)?; + Ok(root == &found_root) + } +} diff --git a/utils/cw-prover/src/proof/verifier/multi.rs b/utils/cw-prover/src/proof/verifier/multi.rs new file mode 100644 index 0000000..7f165bc --- /dev/null +++ b/utils/cw-prover/src/proof/verifier/multi.rs @@ -0,0 +1,47 @@ +use crate::proof::verifier::Verifier; + +#[derive(Clone, Debug)] +pub struct MultiVerifier { + verifiers: [V; N], +} + +impl MultiVerifier { + pub fn new(verifiers: [V; N]) -> Self { + assert!(N > 0, "cannot create empty multi-verifiers"); + Self { verifiers } + } +} + +impl Verifier for MultiVerifier +where + V: Verifier, + V::Value: Clone, + V::Root: Into + Clone, +{ + type Proof = [V::Proof; N]; + type Root = V::Root; + type Key = [V::Key; N]; + type Value = V::Value; + type Error = V::Error; + + fn verify( + &self, + proofs: &Self::Proof, + keys: &Self::Key, + value: &Self::Value, + ) -> Result { + let mut root = None; + let mut value = value.clone(); + + for (idx, verifier) in self.verifiers.iter().enumerate() { + let proof = &proofs[idx]; + let key = &keys[N - idx - 1]; + let sub_root = verifier.verify(proof, key, &value)?; + + value = sub_root.clone().into(); + root = Some(sub_root); + } + + Ok(root.expect("MultiVerifier cannot be instantiated with 0 verifiers")) + } +}