Skip to content

Commit

Permalink
Merge branch 'main' into dependabot/cargo/rstest-0.22
Browse files Browse the repository at this point in the history
  • Loading branch information
seanchen1991 authored Aug 7, 2024
2 parents e3a8722 + 7ea5cae commit ada940d
Show file tree
Hide file tree
Showing 18 changed files with 413 additions and 144 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
- [ibc-client-cw] Rename `path` to `merkle_path` in `cw-context` for verify
(non)membership ([\#1291](https://github.com/cosmos/ibc-rs/issues/1291))
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
- [ibc-core-client] Prevent expired client creations by ensuring that consensus
state's timestamp is within the trusting period.
- ([\#1088](https://github.com/cosmos/ibc-rs/issues/1088))
26 changes: 13 additions & 13 deletions cosmwasm/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,24 @@ contracts. Currently, the available packages are:

## IBC Clients

- [ibc-client-cw](./cw-context): Provides utilities and a generic `Context` object
- [ibc-client-cw](./ibc-clients/cw-context): Provides utilities and a generic `Context` object
to streamline the implementation of any ibc-rs-powered light clients as
CosmWasm contracts.
- To utilize the CosmWasm contracts developed with this library, hosting
environments must support the CosmWasm module and be using the version of
`ibc-go` that supports the `08-wasm` proxy light client.

> [!CAUTION]
> The `ibc-client-cw` is currently in development and should not be
deployed for production use. Users are advised to exercise caution and test
thoroughly in non-production environments.
> [!CAUTION]
> The `ibc-client-cw` is currently in development and should not be
deployed for production use. Users are advised to exercise caution and test
thoroughly in non-production environments.

- [ibc-client-tendermint-cw](./../cosmwasm/ibc-clients/ics07-tendermint): CosmWasm Contract
- [ibc-client-tendermint-cw](./ibc-clients/ics07-tendermint): CosmWasm Contract

> [!TIP]
> The pre-compiled CosmWasm contract for `ibc-client-tendermint-cw` is available
> as Github workflow artifacts at
> [_Actions_](https://github.com/cosmos/ibc-rs/actions/workflows/upload-cw-clients.yaml)
> tab. They can be downloaded
> [during a Github workflow](https://github.com/cosmos/ibc-rs/blob/1098f252c04152812f026520e28e323f3bc0507e/.github/workflows/upload-cw-clients.yaml#L87-L96)
> using `actions/download-artifact@v4` action.
> [!TIP]
> The pre-compiled CosmWasm contract for `ibc-client-tendermint-cw` is available
> as Github workflow artifacts at
> [_Actions_](https://github.com/cosmos/ibc-rs/actions/workflows/upload-cw-clients.yaml)
> tab. They can be downloaded
> [during a Github workflow](https://github.com/cosmos/ibc-rs/blob/1098f252c04152812f026520e28e323f3bc0507e/.github/workflows/upload-cw-clients.yaml#L87-L96)
> using `actions/download-artifact@v4` action.
14 changes: 7 additions & 7 deletions cosmwasm/ibc-clients/cw-context/src/types/msgs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ pub struct MerklePath {
#[cw_serde]
pub struct VerifyMembershipMsgRaw {
pub proof: Binary,
pub path: MerklePath,
pub merkle_path: MerklePath,
pub value: Binary,
pub height: RawHeight,
pub delay_block_period: u64,
Expand All @@ -144,8 +144,8 @@ impl TryFrom<VerifyMembershipMsgRaw> for VerifyMembershipMsg {

fn try_from(mut raw: VerifyMembershipMsgRaw) -> Result<Self, Self::Error> {
let proof = CommitmentProofBytes::try_from(raw.proof.to_vec())?;
let prefix = CommitmentPrefix::from_bytes(raw.path.key_path.remove(0));
let path = PathBytes::flatten(raw.path.key_path);
let prefix = CommitmentPrefix::from_bytes(raw.merkle_path.key_path.remove(0));
let path = PathBytes::flatten(raw.merkle_path.key_path);
let height = Height::try_from(raw.height)?;

Ok(Self {
Expand All @@ -163,7 +163,7 @@ impl TryFrom<VerifyMembershipMsgRaw> for VerifyMembershipMsg {
#[cw_serde]
pub struct VerifyNonMembershipMsgRaw {
pub proof: Binary,
pub path: MerklePath,
pub merkle_path: MerklePath,
pub height: RawHeight,
pub delay_block_period: u64,
pub delay_time_period: u64,
Expand All @@ -183,8 +183,8 @@ impl TryFrom<VerifyNonMembershipMsgRaw> for VerifyNonMembershipMsg {

fn try_from(mut raw: VerifyNonMembershipMsgRaw) -> Result<Self, Self::Error> {
let proof = CommitmentProofBytes::try_from(raw.proof.to_vec())?;
let prefix = CommitmentPrefix::from_bytes(raw.path.key_path.remove(0));
let path = PathBytes::flatten(raw.path.key_path);
let prefix = CommitmentPrefix::from_bytes(raw.merkle_path.key_path.remove(0));
let path = PathBytes::flatten(raw.merkle_path.key_path);
let height = raw.height.try_into()?;

Ok(Self {
Expand Down Expand Up @@ -277,7 +277,7 @@ mod test {
"delay_time_period":0,
"delay_block_period":0,
"proof":"CuECCt4CChhjb25uZWN0aW9ucy9jb25uZWN0aW9uLTASWgoPMDctdGVuZGVybWludC0wEiMKATESDU9SREVSX09SREVSRUQSD09SREVSX1VOT1JERVJFRBgCIiAKCTA4LXdhc20tMBIMY29ubmVjdGlvbi0wGgUKA2liYxoLCAEYASABKgMAAkgiKQgBEiUCBHAg3HTYmBAMxlr6u0mv6wCpm3ur2WQc7A3Af6aV7Ye0Fe0gIisIARIEBAZwIBohIHXEkQ9RIH08ZZYBIP6THxOOJiRmjXWGn1G4RCWT3V6rIisIARIEBgxwIBohIEUjGWV7YLPEzdFVLAb0lv4VvP7A+l1TqFkjpx1kDKAPIikIARIlCBhwILWsAKEot+2MknVyn5zcS0qsqVhRj4AHpgDx7fNPbfhtICIpCAESJQxAcCCzyYMGE+CdCltudr1ddHvCJrqv3kl/i7YnMLx3XWJt/yAK/AEK+QEKA2liYxIg2nvqL76rejXXGlX6ng/UKrbw+72C8uKKgM2vP0JKj1QaCQgBGAEgASoBACIlCAESIQEGuZwNgRn/HtvL4WXQ8ZM327wIDmd8iOV6oq52fr8PDyInCAESAQEaIKplBAbqDXbjndQ9LqapHj/aockI/CGnymjl5izIEVY5IiUIARIhAdt4G8DCLINAaaJnhUMIzv74AV3zZiugAyyZ/lWYRv+cIiUIARIhAf+sohoEV+uWeKThAPEbqCUivWT4H8KNT7Giw9//LsyvIicIARIBARogNHO4HC5KxPCwBdQGgBCscVtEKw+YSn2pnf654Y3Oxik=",
"path":{"key_path":["aWJj","Y29ubmVjdGlvbnMvY29ubmVjdGlvbi0w"]},
"merkle_path":{"key_path":["aWJj","Y29ubmVjdGlvbnMvY29ubmVjdGlvbi0w"]},
"value":"Cg8wNy10ZW5kZXJtaW50LTASIwoBMRINT1JERVJfT1JERVJFRBIPT1JERVJfVU5PUkRFUkVEGAIiIAoJMDgtd2FzbS0wEgxjb25uZWN0aW9uLTAaBQoDaWJj"
}
}"#;
Expand Down
60 changes: 55 additions & 5 deletions ibc-clients/ics07-tendermint/src/client_state/common.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
use core::time::Duration;

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::Height;
use ibc_core_client::types::{Height, Status};
use ibc_core_commitment_types::commitment::{
CommitmentPrefix, CommitmentProofBytes, CommitmentRoot,
};
Expand All @@ -14,14 +16,22 @@ use ibc_core_host::types::identifiers::ClientType;
use ibc_core_host::types::path::{Path, PathBytes, UpgradeClientPath};
use ibc_primitives::prelude::*;
use ibc_primitives::proto::Any;
use ibc_primitives::ToVec;
use ibc_primitives::{Timestamp, ToVec};

use super::ClientState;
use crate::consensus_state::ConsensusState as TmConsensusState;

impl ClientStateCommon for ClientState {
fn verify_consensus_state(&self, consensus_state: Any) -> Result<(), ClientError> {
verify_consensus_state(consensus_state)
fn verify_consensus_state(
&self,
consensus_state: Any,
host_timestamp: &Timestamp,
) -> Result<(), ClientError> {
verify_consensus_state(
consensus_state,
host_timestamp,
self.inner().trusting_period,
)
}

fn client_type(&self) -> ClientType {
Expand Down Expand Up @@ -111,7 +121,11 @@ impl ClientStateCommon for ClientState {
/// 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_consensus_state(consensus_state: Any) -> Result<(), ClientError> {
pub fn verify_consensus_state(
consensus_state: Any,
host_timestamp: &Timestamp,
trusting_period: Duration,
) -> Result<(), ClientError> {
let tm_consensus_state = TmConsensusState::try_from(consensus_state)?;

if tm_consensus_state.root().is_empty() {
Expand All @@ -120,9 +134,45 @@ pub fn verify_consensus_state(consensus_state: Any) -> Result<(), ClientError> {
});
};

if consensus_state_status(&tm_consensus_state, host_timestamp, trusting_period)?.is_expired() {
return Err(ClientError::ClientNotActive {
status: Status::Expired,
});
}

Ok(())
}

/// Determines the `Status`, whether it is `Active` or `Expired`, of a consensus
/// state, using its timestamp, the host's timestamp, and the trusting period.
pub fn consensus_state_status<CS: ConsensusState>(
consensus_state: &CS,
host_timestamp: &Timestamp,
trusting_period: Duration,
) -> Result<Status, ClientError> {
if !host_timestamp.is_set() {
return Err(ClientError::Other {
description: "host timestamp is none".into(),
});
}

// Note: if the `duration_since()` is `None`, indicating that the latest
// consensus state is in the future, then we don't consider the client
// to be expired.
if let Some(elapsed_since_latest_consensus_state) =
host_timestamp.duration_since(&consensus_state.timestamp())
{
// Note: The equality is considered as expired to stay consistent with
// the check in tendermint-rs, where a header at `trusted_header_time +
// trusting_period` is considered expired.
if elapsed_since_latest_consensus_state >= trusting_period {
return Ok(Status::Expired);
}
}

Ok(Status::Active)
}

/// Validate the given proof height against the client state's latest height, returning
/// an error if the proof height is greater than the latest height of the client state.
///
Expand Down
22 changes: 10 additions & 12 deletions ibc-clients/ics07-tendermint/src/client_state/validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@ use tendermint::crypto::Sha256 as Sha256Trait;
use tendermint::merkle::MerkleHash;
use tendermint_light_client_verifier::{ProdVerifier, Verifier};

use super::{check_for_misbehaviour_on_misbehavior, check_for_misbehaviour_on_update, ClientState};
use super::{
check_for_misbehaviour_on_misbehavior, check_for_misbehaviour_on_update,
consensus_state_status, ClientState,
};
use crate::client_state::{verify_header, verify_misbehaviour};

impl<V> ClientStateValidation<V> for ClientState
Expand Down Expand Up @@ -218,18 +221,13 @@ where
// to be expired.
let now = ctx.host_timestamp()?;

if let Some(elapsed_since_latest_consensus_state) =
now.duration_since(&latest_consensus_state.timestamp().into())
{
// Note: The equality is considered as expired to stay consistent with
// the check in tendermint-rs, where a header at `trusted_header_time +
// trusting_period` is considered expired.
if elapsed_since_latest_consensus_state >= client_state.trusting_period {
return Ok(Status::Expired);
}
}
let status = consensus_state_status(
&latest_consensus_state.into(),
&now,
client_state.trusting_period,
)?;

Ok(Status::Active)
Ok(status)
}

/// Check that the subject and substitute client states match as part of
Expand Down
7 changes: 6 additions & 1 deletion ibc-core/ics02-client/context/src/client_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use ibc_core_host_types::identifiers::{ClientId, ClientType};
use ibc_core_host_types::path::{Path, PathBytes};
use ibc_primitives::prelude::*;
use ibc_primitives::proto::Any;
use ibc_primitives::Timestamp;

use crate::context::{ClientExecutionContext, ClientValidationContext};
use crate::Convertible;
Expand All @@ -22,7 +23,11 @@ pub trait ClientStateCommon: Convertible<Any> {
///
/// Notably, an implementation should verify that it can properly
/// deserialize the object into the expected format.
fn verify_consensus_state(&self, consensus_state: Any) -> Result<(), ClientError>;
fn verify_consensus_state(
&self,
consensus_state: Any,
host_timestamp: &Timestamp,
) -> Result<(), ClientError>;

/// Type of client associated with this state (eg. Tendermint)
fn client_type(&self) -> ClientType;
Expand Down
4 changes: 3 additions & 1 deletion ibc-core/ics02-client/src/handler/create_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,9 @@ where
.into());
};

client_state.verify_consensus_state(consensus_state)?;
let host_timestamp = ctx.host_timestamp()?;

client_state.verify_consensus_state(consensus_state, &host_timestamp)?;

if client_val_ctx.client_state(&client_id).is_ok() {
return Err(ClientError::ClientStateAlreadyExists { client_id }.into());
Expand Down
5 changes: 3 additions & 2 deletions ibc-derive/src/client_state/traits/client_state_common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ pub(crate) fn impl_ClientStateCommon(
let verify_consensus_state_impl = delegate_call_in_match(
client_state_enum_name,
enum_variants.iter(),
quote! { verify_consensus_state(cs, consensus_state) },
quote! { verify_consensus_state(cs, consensus_state, &host_timestamp) },
imports,
);
let client_type_impl = delegate_call_in_match(
Expand Down Expand Up @@ -82,12 +82,13 @@ pub(crate) fn impl_ClientStateCommon(
let ClientType = imports.client_type();
let ClientError = imports.client_error();
let Height = imports.height();
let Timestamp = imports.timestamp();
let Path = imports.path();
let PathBytes = imports.path_bytes();

quote! {
impl #ClientStateCommon for #HostClientState {
fn verify_consensus_state(&self, consensus_state: #Any) -> Result<(), #ClientError> {
fn verify_consensus_state(&self, consensus_state: #Any, host_timestamp: &#Timestamp) -> Result<(), #ClientError> {
match self {
#(#verify_consensus_state_impl),*
}
Expand Down
64 changes: 64 additions & 0 deletions ibc-testkit/src/data/json/valid_signed_header.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
{
"header": {
"version": {
"block": "0",
"app": "0"
},
"chain_id": "test-chain-1",
"height": "20",
"time": "2030-11-02T15:04:10Z",
"last_block_id": {
"hash": "15F15EF50BDE2018F4B129A827F90C18222C757770C8295EB8EE7BF50E761BC0",
"part_set_header": {
"total": 1,
"hash": "077E16D720F9AA656EBFD7F3FB31A4A35E1F2F4EBEBB123642BED45535D88AD5"
}
},
"last_commit_hash": "D5439DD65D45EF1E51412691BCF2F6741D48AC1325572E08D48BD72F80669E70",
"data_hash": "",
"validators_hash": "26952B5D784A1564D167DF98D2D37376B5E77771928256D25E6FF9AE3AD11564",
"next_validators_hash": "26952B5D784A1564D167DF98D2D37376B5E77771928256D25E6FF9AE3AD11564",
"consensus_hash": "048091BC7DDC283F77BFBF91D73C44DA58C3DF8A9CBC867405D8B7F3DAADA22F",
"app_hash": "6170705F68617368",
"last_results_hash": "",
"evidence_hash": "",
"proposer_address": "026CC7B6F3E62F789DBECEC59766888B5464737D"
},
"commit": {
"height": "20",
"round": 1,
"block_id": {
"hash": "5E87BD3A35C62D06138273453AF49C7728E4F8FB4CAFB0784F4816D5052AA349",
"part_set_header": {
"total": 1,
"hash": "DC797E9C450AE5FD0D8000E31672BE3EE97B6C0A3BD69239187F75C00C39D72B"
}
},
"signatures": [
{
"block_id_flag": 3,
"validator_address": "01F527D77D3FFCC4FCFF2DDC2952EEA5414F2A33",
"timestamp": "2019-11-02T15:04:15Z",
"signature": "SMBYJYr7yHbqmScg7jUhSGyis/gOMvwkO4mlEmKt9NKJ+aZgETFgIIFnExCju0VidMyy9mUwMQ/b+EoVL1NwAQ=="
},
{
"block_id_flag": 2,
"validator_address": "026CC7B6F3E62F789DBECEC59766888B5464737D",
"timestamp": "2019-11-02T15:04:15Z",
"signature": "Bn8cV8WlqGjezZcFgQey0ikBshIHw56jpuzWssO2/0HOpMC1Mr2mJ5mxEd4mpsQ4PwenpGtrLJv11PFK6aJLBw=="
},
{
"block_id_flag": 2,
"validator_address": "03A238BCAF7D1626DFE8A4AFB9448D00B7A3D2E2",
"timestamp": "2019-11-02T15:04:15Z",
"signature": "mhgrDo+fV5GaS34CZn//zgNx24H21Ila9io5HPgxeEWbG24eGLp0GAAlGPryT9bdv5LXPUq8wZpUHxAcyhvZAQ=="
},
{
"block_id_flag": 2,
"validator_address": "03EC0413849A3311A5341E7A69D6C544E9A30310",
"timestamp": "2019-11-02T15:04:15Z",
"signature": "+g5Rhm8Utshm0SN2gxKeRrqsJyUO32BxDqTVu0wHzaJz0cFJWZxA2JaYxaf6EzuQULkwp+oh6u3xboy/e5wHAg=="
}
]
}
}
20 changes: 16 additions & 4 deletions ibc-testkit/src/fixtures/clients/tendermint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,12 +112,24 @@ impl ClientStateConfig {
}

#[cfg(feature = "serde")]
pub fn dummy_tendermint_header() -> tendermint::block::Header {
pub fn dummy_valid_tendermint_header() -> tendermint::block::Header {
use tendermint::block::signed_header::SignedHeader;

serde_json::from_str::<SignedHeader>(include_str!(concat!(
env!("CARGO_MANIFEST_DIR"),
"/src/data/json/signed_header.json"
"/src/data/json/valid_signed_header.json"
)))
.expect("Never fails")
.header
}

#[cfg(feature = "serde")]
pub fn dummy_expired_tendermint_header() -> tendermint::block::Header {
use tendermint::block::signed_header::SignedHeader;

serde_json::from_str::<SignedHeader>(include_str!(concat!(
env!("CARGO_MANIFEST_DIR"),
"/src/data/json/expired_signed_header.json"
)))
.expect("Never fails")
.header
Expand Down Expand Up @@ -147,7 +159,7 @@ pub fn dummy_ics07_header() -> Header {
// Build a SignedHeader from a JSON file.
let shdr = serde_json::from_str::<SignedHeader>(include_str!(concat!(
env!("CARGO_MANIFEST_DIR"),
"/src/data/json/signed_header.json"
"/src/data/json/valid_signed_header.json"
)))
.expect("Never fails");

Expand Down Expand Up @@ -222,7 +234,7 @@ mod tests {
#[test]
fn tm_client_state_from_header_healthy() {
// check client state creation path from a tendermint header
let tm_header = dummy_tendermint_header();
let tm_header = dummy_valid_tendermint_header();
let tm_client_state_from_header = dummy_tm_client_state_from_header(tm_header);
let any_from_header = Any::from(tm_client_state_from_header.clone());
let tm_client_state_from_any = ClientStateType::try_from(any_from_header);
Expand Down
Loading

0 comments on commit ada940d

Please sign in to comment.