Extract mod merkle
This commit is contained in:
parent
dbcaee9cb3
commit
215ec5f0a7
2 changed files with 117 additions and 113 deletions
|
@ -18,24 +18,17 @@ use std::error::Error;
|
||||||
|
|
||||||
use clap::{Parser, Subcommand};
|
use clap::{Parser, Subcommand};
|
||||||
use cosmrs::AccountId;
|
use cosmrs::AccountId;
|
||||||
use ibc_proto::{
|
|
||||||
ibc::core::commitment::v1::MerkleProof as RawMerkleProof, ibc::core::commitment::v1::MerkleRoot,
|
|
||||||
};
|
|
||||||
use ibc_relayer_types::{
|
use ibc_relayer_types::{
|
||||||
core::ics23_commitment::commitment::CommitmentRoot,
|
core::ics23_commitment::commitment::CommitmentRoot, core::ics23_commitment::specs::ProofSpecs,
|
||||||
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::block::Height;
|
use tendermint::block::Height;
|
||||||
use tendermint::merkle::proof::ProofOps as TendermintProof;
|
|
||||||
use tendermint::AppHash;
|
use tendermint::AppHash;
|
||||||
use tendermint_rpc::endpoint::abci_query::AbciQuery;
|
use tendermint_rpc::endpoint::abci_query::AbciQuery;
|
||||||
use tendermint_rpc::endpoint::status::Response;
|
use tendermint_rpc::endpoint::status::Response;
|
||||||
use tendermint_rpc::{client::HttpClient as TmRpcClient, Client, HttpClientUrl};
|
use tendermint_rpc::{client::HttpClient as TmRpcClient, Client, HttpClientUrl};
|
||||||
|
|
||||||
|
mod merkle;
|
||||||
|
|
||||||
#[derive(Debug, Parser)]
|
#[derive(Debug, Parser)]
|
||||||
#[command(author, version, about, long_about = None)]
|
#[command(author, version, about, long_about = None)]
|
||||||
struct Cli {
|
struct Cli {
|
||||||
|
@ -137,7 +130,7 @@ fn encode_length(namespace: &[u8]) -> [u8; 2] {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn verify_proof(latest_app_hash: AppHash, result: AbciQuery) -> Result<Vec<u8>, Box<dyn Error>> {
|
fn verify_proof(latest_app_hash: AppHash, result: AbciQuery) -> Result<Vec<u8>, Box<dyn Error>> {
|
||||||
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 root = CommitmentRoot::from_bytes(latest_app_hash.as_bytes());
|
||||||
let prefixed_key = vec!["wasm".to_string().into_bytes(), result.key];
|
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<Vec<u8>,
|
||||||
|
|
||||||
Ok(result.value)
|
Ok(result.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copied from hermes and patched to allow non-string keys
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
|
||||||
struct MerkleProof {
|
|
||||||
proofs: Vec<CommitmentProof>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Convert to ics23::CommitmentProof
|
|
||||||
impl From<RawMerkleProof> for MerkleProof {
|
|
||||||
fn from(proof: RawMerkleProof) -> Self {
|
|
||||||
Self {
|
|
||||||
proofs: proof.proofs,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<MerkleProof> for RawMerkleProof {
|
|
||||||
fn from(proof: MerkleProof) -> Self {
|
|
||||||
Self {
|
|
||||||
proofs: proof.proofs,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MerkleProof {
|
|
||||||
pub fn verify_membership(
|
|
||||||
&self,
|
|
||||||
specs: &ProofSpecs,
|
|
||||||
root: MerkleRoot,
|
|
||||||
keys: Vec<Vec<u8>>,
|
|
||||||
value: Vec<u8>,
|
|
||||||
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::<ProofSpec>::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::<ics23::HostFunctionsManager>(existence_proof)
|
|
||||||
.map_err(|_| ProofError::invalid_merkle_proof())?;
|
|
||||||
|
|
||||||
if !verify_membership::<ics23::HostFunctionsManager>(
|
|
||||||
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<MerkleProof, 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(MerkleProof::from(RawMerkleProof { proofs }))
|
|
||||||
}
|
|
||||||
|
|
113
utils/cw-prover/src/merkle.rs
Normal file
113
utils/cw-prover/src/merkle.rs
Normal file
|
@ -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<CommitmentProof>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert to ics23::CommitmentProof
|
||||||
|
impl From<RawMerkleProof> for MerkleProof {
|
||||||
|
fn from(proof: RawMerkleProof) -> Self {
|
||||||
|
Self {
|
||||||
|
proofs: proof.proofs,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<MerkleProof> for RawMerkleProof {
|
||||||
|
fn from(proof: MerkleProof) -> Self {
|
||||||
|
Self {
|
||||||
|
proofs: proof.proofs,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MerkleProof {
|
||||||
|
pub fn verify_membership(
|
||||||
|
&self,
|
||||||
|
specs: &ProofSpecs,
|
||||||
|
root: MerkleRoot,
|
||||||
|
keys: Vec<Vec<u8>>,
|
||||||
|
value: Vec<u8>,
|
||||||
|
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::<ProofSpec>::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::<ics23::HostFunctionsManager>(existence_proof)
|
||||||
|
.map_err(|_| ProofError::invalid_merkle_proof())?;
|
||||||
|
|
||||||
|
if !verify_membership::<ics23::HostFunctionsManager>(
|
||||||
|
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<MerkleProof, 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(MerkleProof::from(RawMerkleProof { proofs }))
|
||||||
|
}
|
Loading…
Reference in a new issue