Skip to content

Commit

Permalink
refactor: allow using custom path for light client proof verification
Browse files Browse the repository at this point in the history
  • Loading branch information
Farhad-Shabani committed Jul 10, 2024
1 parent 7ff41b3 commit 188e3db
Show file tree
Hide file tree
Showing 26 changed files with 380 additions and 264 deletions.
11 changes: 2 additions & 9 deletions ibc-clients/cw-context/src/handlers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,20 +103,13 @@ where
SudoMsg::VerifyUpgradeAndUpdateState(msg) => {
let msg = VerifyUpgradeAndUpdateStateMsg::try_from(msg)?;

let client_cons_state_path = ClientConsensusStatePath::new(
client_id.clone(),
client_state.latest_height().revision_number(),
client_state.latest_height().revision_height(),
);

let consensus_state = self.consensus_state(&client_cons_state_path)?;

client_state.verify_upgrade_client(
self,
client_id.clone(),

Check warning on line 108 in ibc-clients/cw-context/src/handlers.rs

View check run for this annotation

Codecov / codecov/patch

ibc-clients/cw-context/src/handlers.rs#L107-L108

Added lines #L107 - L108 were not covered by tests
msg.upgrade_client_state.clone(),
msg.upgrade_consensus_state.clone(),
msg.proof_upgrade_client,
msg.proof_upgrade_consensus_state,
consensus_state.root(),
)?;

client_state.update_state_on_upgrade(
Expand Down
44 changes: 24 additions & 20 deletions ibc-clients/cw-context/src/types/msgs.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
//! Defines the messages sent to the CosmWasm contract by the 08-wasm proxy
//! light client.
use std::str::FromStr;

use cosmwasm_schema::{cw_serde, QueryResponses};
use ibc_client_wasm_types::serializer::Base64;
use ibc_client_wasm_types::Bytes;
use ibc_core::client::types::proto::v1::Height as RawHeight;
use ibc_core::client::types::Height;
use ibc_core::commitment_types::commitment::{CommitmentPrefix, CommitmentProofBytes};
use ibc_core::host::types::path::Path;
use ibc_core::commitment_types::merkle::MerklePath;
use ibc_core::host::types::path::PathBytes;
use ibc_core::primitives::proto::Any;
use prost::Message;

Expand Down Expand Up @@ -133,11 +132,6 @@ impl TryFrom<VerifyUpgradeAndUpdateStateMsgRaw> for VerifyUpgradeAndUpdateStateM
}
}

#[cw_serde]
pub struct MerklePath {
pub key_path: Vec<String>,
}

#[cw_serde]
pub struct VerifyMembershipMsgRaw {
#[schemars(with = "String")]
Expand All @@ -155,7 +149,7 @@ pub struct VerifyMembershipMsgRaw {
pub struct VerifyMembershipMsg {
pub prefix: CommitmentPrefix,
pub proof: CommitmentProofBytes,
pub path: Path,
pub path: PathBytes,
pub value: Vec<u8>,
pub height: Height,
pub delay_block_period: u64,
Expand All @@ -167,17 +161,22 @@ impl TryFrom<VerifyMembershipMsgRaw> for VerifyMembershipMsg {

fn try_from(mut raw: VerifyMembershipMsgRaw) -> Result<Self, Self::Error> {
let proof = CommitmentProofBytes::try_from(raw.proof)?;
let prefix = raw.path.key_path.remove(0).into_bytes();
let path_str = raw.path.key_path.join("");
let path = Path::from_str(&path_str)?;
let prefix = raw.path.key_path.remove(0);

Check warning on line 164 in ibc-clients/cw-context/src/types/msgs.rs

View check run for this annotation

Codecov / codecov/patch

ibc-clients/cw-context/src/types/msgs.rs#L164

Added line #L164 was not covered by tests
let height = Height::try_from(raw.height)?;

let path_bytes: Vec<u8> = raw
.path
.key_path
.into_iter()
.flat_map(|p| p.into_vec())
.collect();

Check warning on line 173 in ibc-clients/cw-context/src/types/msgs.rs

View check run for this annotation

Codecov / codecov/patch

ibc-clients/cw-context/src/types/msgs.rs#L167-L173

Added lines #L167 - L173 were not covered by tests
Ok(Self {
proof,
path,
path: path_bytes.into(),

Check warning on line 176 in ibc-clients/cw-context/src/types/msgs.rs

View check run for this annotation

Codecov / codecov/patch

ibc-clients/cw-context/src/types/msgs.rs#L176

Added line #L176 was not covered by tests
value: raw.value,
height,
prefix: CommitmentPrefix::try_from(prefix)?,
prefix: CommitmentPrefix::try_from(prefix.into_vec())?,

Check warning on line 179 in ibc-clients/cw-context/src/types/msgs.rs

View check run for this annotation

Codecov / codecov/patch

ibc-clients/cw-context/src/types/msgs.rs#L179

Added line #L179 was not covered by tests
delay_block_period: raw.delay_block_period,
delay_time_period: raw.delay_time_period,
})
Expand All @@ -198,7 +197,7 @@ pub struct VerifyNonMembershipMsgRaw {
pub struct VerifyNonMembershipMsg {
pub prefix: CommitmentPrefix,
pub proof: CommitmentProofBytes,
pub path: Path,
pub path: PathBytes,
pub height: Height,
pub delay_block_period: u64,
pub delay_time_period: u64,
Expand All @@ -209,15 +208,20 @@ impl TryFrom<VerifyNonMembershipMsgRaw> for VerifyNonMembershipMsg {

fn try_from(mut raw: VerifyNonMembershipMsgRaw) -> Result<Self, Self::Error> {
let proof = CommitmentProofBytes::try_from(raw.proof)?;
let prefix = raw.path.key_path.remove(0).into_bytes();
let path_str = raw.path.key_path.join("");
let path = Path::from_str(&path_str)?;
let prefix = raw.path.key_path.remove(0);

Check warning on line 211 in ibc-clients/cw-context/src/types/msgs.rs

View check run for this annotation

Codecov / codecov/patch

ibc-clients/cw-context/src/types/msgs.rs#L211

Added line #L211 was not covered by tests
let height = raw.height.try_into()?;

let path_bytes: Vec<u8> = raw
.path
.key_path
.into_iter()
.flat_map(|p| p.into_vec())
.collect();

Check warning on line 219 in ibc-clients/cw-context/src/types/msgs.rs

View check run for this annotation

Codecov / codecov/patch

ibc-clients/cw-context/src/types/msgs.rs#L214-L219

Added lines #L214 - L219 were not covered by tests
Ok(Self {
proof,
path,
path: path_bytes.into(),

Check warning on line 222 in ibc-clients/cw-context/src/types/msgs.rs

View check run for this annotation

Codecov / codecov/patch

ibc-clients/cw-context/src/types/msgs.rs#L222

Added line #L222 was not covered by tests
height,
prefix: CommitmentPrefix::try_from(prefix)?,
prefix: CommitmentPrefix::try_from(prefix.into_vec())?,

Check warning on line 224 in ibc-clients/cw-context/src/types/msgs.rs

View check run for this annotation

Codecov / codecov/patch

ibc-clients/cw-context/src/types/msgs.rs#L224

Added line #L224 was not covered by tests
delay_block_period: raw.delay_block_period,
delay_time_period: raw.delay_time_period,
})
Expand Down
112 changes: 9 additions & 103 deletions ibc-clients/ics07-tendermint/src/client_state/common.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
use ibc_client_tendermint_types::{client_type as tm_client_type, ClientState as ClientStateType};
use ibc_core_client::context::client_state::ClientStateCommon;
use ibc_core_client::context::consensus_state::ConsensusState;
use ibc_core_client::types::error::{ClientError, UpgradeClientError};
use ibc_core_client::types::error::ClientError;
use ibc_core_client::types::Height;
use ibc_core_commitment_types::commitment::{
CommitmentPrefix, CommitmentProofBytes, CommitmentRoot,
};
use ibc_core_commitment_types::merkle::{apply_prefix, MerkleProof};
use ibc_core_commitment_types::merkle::{MerklePath, MerkleProof};
use ibc_core_commitment_types::proto::ics23::{HostFunctionsManager, HostFunctionsProvider};
use ibc_core_commitment_types::specs::ProofSpecs;
use ibc_core_host::types::identifiers::ClientType;
use ibc_core_host::types::path::{Path, UpgradeClientPath};
use ibc_core_host::types::path::PathBytes;
use ibc_primitives::prelude::*;
use ibc_primitives::proto::Any;
use ibc_primitives::ToVec;

use super::ClientState;
use crate::consensus_state::ConsensusState as TmConsensusState;
Expand All @@ -35,30 +34,12 @@ impl ClientStateCommon for ClientState {
validate_proof_height(self.inner(), proof_height)
}

fn verify_upgrade_client(
&self,
upgraded_client_state: Any,
upgraded_consensus_state: Any,
proof_upgrade_client: CommitmentProofBytes,
proof_upgrade_consensus_state: CommitmentProofBytes,
root: &CommitmentRoot,
) -> Result<(), ClientError> {
verify_upgrade_client::<HostFunctionsManager>(
self.inner(),
upgraded_client_state,
upgraded_consensus_state,
proof_upgrade_client,
proof_upgrade_consensus_state,
root,
)
}

fn verify_membership(
&self,
prefix: &CommitmentPrefix,
proof: &CommitmentProofBytes,
root: &CommitmentRoot,
path: Path,
path: PathBytes,
value: Vec<u8>,
) -> Result<(), ClientError> {
verify_membership::<HostFunctionsManager>(
Expand All @@ -76,7 +57,7 @@ impl ClientStateCommon for ClientState {
prefix: &CommitmentPrefix,
proof: &CommitmentProofBytes,
root: &CommitmentRoot,
path: Path,
path: PathBytes,
) -> Result<(), ClientError> {
verify_non_membership::<HostFunctionsManager>(
&self.inner().proof_specs,
Expand Down Expand Up @@ -128,81 +109,6 @@ pub fn validate_proof_height(
Ok(())
}

/// Perform client-specific verifications and check all data in the new
/// client state to be the same across all valid Tendermint clients for the
/// new chain.
///
/// You can learn more about how to upgrade IBC-connected SDK chains in
/// [this](https://ibc.cosmos.network/main/ibc/upgrades/quick-guide.html)
/// guide.
///
/// Note that this function is typically implemented as part of the
/// [`ClientStateCommon`] trait, but has been made a standalone function
/// in order to make the ClientState APIs more flexible.
pub fn verify_upgrade_client<H: HostFunctionsProvider>(
client_state: &ClientStateType,
upgraded_client_state: Any,
upgraded_consensus_state: Any,
proof_upgrade_client: CommitmentProofBytes,
proof_upgrade_consensus_state: CommitmentProofBytes,
root: &CommitmentRoot,
) -> Result<(), ClientError> {
// Make sure that the client type is of Tendermint type `ClientState`
let upgraded_tm_client_state = ClientState::try_from(upgraded_client_state.clone())?;

// Make sure that the consensus type is of Tendermint type `ConsensusState`
TmConsensusState::try_from(upgraded_consensus_state.clone())?;

let latest_height = client_state.latest_height;
let upgraded_tm_client_state_height = upgraded_tm_client_state.latest_height();

// Make sure the latest height of the current client is not greater then
// the upgrade height This condition checks both the revision number and
// the height
if latest_height >= upgraded_tm_client_state_height {
Err(UpgradeClientError::LowUpgradeHeight {
upgraded_height: latest_height,
client_height: upgraded_tm_client_state_height,
})?
}

// Check to see if the upgrade path is set
let mut upgrade_path = client_state.upgrade_path.clone();

if upgrade_path.pop().is_none() {
return Err(ClientError::ClientSpecific {
description: "cannot upgrade client as no upgrade path has been set".to_string(),
});
};

let upgrade_path_prefix = CommitmentPrefix::try_from(upgrade_path[0].clone().into_bytes())
.map_err(ClientError::InvalidCommitmentProof)?;

let last_height = latest_height.revision_height();

// Verify the proof of the upgraded client state
verify_membership::<H>(
&client_state.proof_specs,
&upgrade_path_prefix,
&proof_upgrade_client,
root,
Path::UpgradeClient(UpgradeClientPath::UpgradedClientState(last_height)),
upgraded_client_state.to_vec(),
)?;

// Verify the proof of the upgraded consensus state
verify_membership::<H>(
&client_state.proof_specs,
&upgrade_path_prefix,
&proof_upgrade_consensus_state,
root,
Path::UpgradeClient(UpgradeClientPath::UpgradedClientConsensusState(last_height)),
upgraded_consensus_state.to_vec(),
)?;

Ok(())
}

/// Verify membership of the given value against the client's merkle proof.
///
/// Note that this function is typically implemented as part of the
Expand All @@ -213,10 +119,10 @@ pub fn verify_membership<H: HostFunctionsProvider>(
prefix: &CommitmentPrefix,
proof: &CommitmentProofBytes,
root: &CommitmentRoot,
path: Path,
path: PathBytes,
value: Vec<u8>,
) -> Result<(), ClientError> {
let merkle_path = apply_prefix(prefix, vec![path.to_string()]);
let merkle_path = MerklePath::new(vec![prefix.clone().into_vec().into(), path]);
let merkle_proof = MerkleProof::try_from(proof).map_err(ClientError::InvalidCommitmentProof)?;

merkle_proof
Expand All @@ -234,9 +140,9 @@ pub fn verify_non_membership<H: HostFunctionsProvider>(
prefix: &CommitmentPrefix,
proof: &CommitmentProofBytes,
root: &CommitmentRoot,
path: Path,
path: PathBytes,
) -> Result<(), ClientError> {
let merkle_path = apply_prefix(prefix, vec![path.to_string()]);
let merkle_path = MerklePath::new(vec![prefix.clone().into_vec().into(), path]);
let merkle_proof = MerkleProof::try_from(proof).map_err(ClientError::InvalidCommitmentProof)?;

merkle_proof
Expand Down
Loading

0 comments on commit 188e3db

Please sign in to comment.