diff --git a/packages/shared/src/types/error.rs b/packages/shared/src/types/error.rs index 7fafc46..067fb2b 100644 --- a/packages/shared/src/types/error.rs +++ b/packages/shared/src/types/error.rs @@ -47,6 +47,10 @@ pub enum ContractError { "invalid timeout timestamp: current {current}, timestamp {timestamp} (seconds since epoch)" )] InvalidTimeoutTimestamp { current: u64, timestamp: u64 }, + #[error("invalid timeout block: current {current}, block {block}")] + InvalidTimeoutBlock { current: u64, block: u64 }, + #[error("invalid revision number: current {current}, revision {revision}")] + InvalidRevisionNumber { current: u64, revision: u64 }, #[error("empty timestamp")] EmptyTimestamp, #[error("packet already commited: key: {:02x?}", key)] @@ -104,6 +108,20 @@ impl ContractError { Self::InvalidTimeoutTimestamp { current, timestamp } } + /// Returns a new [`ContractError::InvalidTimeoutBlock`] with the given current and block + /// values. + #[must_use] + pub const fn invalid_timeout_block(current: u64, block: u64) -> Self { + Self::InvalidTimeoutBlock { current, block } + } + + /// Returns a new [`ContractError::InvalidRevisionNumber`] with the given current and revision + /// values. + #[must_use] + pub const fn invalid_revision_number(current: u64, revision: u64) -> Self { + Self::InvalidRevisionNumber { current, revision } + } + /// Returns a new [`ContractError::PacketAlreadyCommited`] with the given key. #[must_use] pub const fn packet_already_commited(key: Vec) -> Self { diff --git a/packages/shared/src/utils/timeout.rs b/packages/shared/src/utils/timeout.rs index 9711f37..08dab3b 100644 --- a/packages/shared/src/utils/timeout.rs +++ b/packages/shared/src/utils/timeout.rs @@ -1,6 +1,9 @@ //! This module contains helper functions for working with [`cosmwasm_std::IbcTimeout`]. +use std::str::FromStr; + use cosmwasm_std::{Env, IbcTimeout}; +use ibc_core_host::types::identifiers::ChainId; use crate::types::error::ContractError; @@ -9,9 +12,23 @@ use crate::types::error::ContractError; /// # Errors /// Returns an error if the timeout is not a timestamp or if the timestamp is in the past. pub fn validate(env: &Env, timeout: &IbcTimeout) -> Result<(), ContractError> { - if timeout.block().is_some() { - return Err(ContractError::InvalidTimeoutHeight); - } + timeout.block().map_or(Ok(()), |b| { + if env.block.height >= b.height { + return Err(ContractError::invalid_timeout_block( + env.block.height, + b.height, + )); + } + + if ChainId::from_str(&env.block.chain_id)?.revision_number() > b.revision { + return Err(ContractError::invalid_revision_number( + env.block.height, + b.revision, + )); + } + + Ok(()) + })?; timeout .timestamp() .ok_or(ContractError::EmptyTimestamp)