diff --git a/.github/workflows/integration.yaml b/.github/workflows/integration.yaml index 85659b5eae..89912a5d17 100644 --- a/.github/workflows/integration.yaml +++ b/.github/workflows/integration.yaml @@ -425,7 +425,7 @@ jobs: fail-fast: false matrix: chain: - - package: ibc-go-v7-channel-upgrade + - package: ibc-go-v8-channel-upgrade-simapp command: simd account_prefix: cosmos steps: diff --git a/Cargo.lock b/Cargo.lock index d9378af793..9b6e5ded21 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1372,8 +1372,7 @@ dependencies = [ [[package]] name = "ibc-proto" version = "0.37.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "725fd83c94e9859fd967cd706996679799171eba940740f071f0046fb6f6e031" +source = "git+https://github.com/cosmos/ibc-proto-rs.git?branch=romac/channel-upgrade-only#f2745837ae445d431e8e48e5fd0bef14b2cf4585" dependencies = [ "base64 0.21.4", "bytes", @@ -4185,8 +4184,3 @@ dependencies = [ "quote", "syn 2.0.38", ] - -[[patch.unused]] -name = "ibc-proto" -version = "0.33.0" -source = "git+https://github.com/cosmos/ibc-proto-rs.git?branch=romac/channel-upgrade-only#a3cad9102eeae2d356054b2a5c55298213f9405d" diff --git a/crates/relayer-types/src/core/ics04_channel/channel.rs b/crates/relayer-types/src/core/ics04_channel/channel.rs index 2cc4705398..f30f6714c0 100644 --- a/crates/relayer-types/src/core/ics04_channel/channel.rs +++ b/crates/relayer-types/src/core/ics04_channel/channel.rs @@ -7,12 +7,12 @@ use ibc_proto::protobuf::Protobuf; use serde::{Deserialize, Serialize}; use ibc_proto::ibc::core::channel::v1::{ - Channel as RawChannel, Counterparty as RawCounterparty, FlushStatus as RawFlushStatus, + Channel as RawChannel, Counterparty as RawCounterparty, IdentifiedChannel as RawIdentifiedChannel, }; use crate::core::ics04_channel::packet::Sequence; -use crate::core::ics04_channel::{error::Error, flush_status::FlushStatus, version::Version}; +use crate::core::ics04_channel::{error::Error, version::Version}; use crate::core::ics24_host::identifier::{ChannelId, ConnectionId, PortId}; #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] @@ -45,7 +45,6 @@ impl TryFrom for IdentifiedChannelEnd { connection_hops: value.connection_hops, version: value.version, upgrade_sequence: 0, // FIXME: proto IdentifiedChannel does not have this field, should we default to 0 ? - flush_status: 0, }; Ok(IdentifiedChannelEnd { @@ -59,7 +58,7 @@ impl TryFrom for IdentifiedChannelEnd { impl From for RawIdentifiedChannel { fn from(value: IdentifiedChannelEnd) -> Self { RawIdentifiedChannel { - state: value.channel_end.state as i32, + state: value.channel_end.state.as_i32(), ordering: value.channel_end.ordering as i32, counterparty: Some(value.channel_end.counterparty().clone().into()), connection_hops: value @@ -83,7 +82,6 @@ pub struct ChannelEnd { pub connection_hops: Vec, pub version: Version, pub upgraded_sequence: Sequence, - pub flush_status: FlushStatus, } impl Display for ChannelEnd { @@ -105,7 +103,6 @@ impl Default for ChannelEnd { connection_hops: Vec::new(), version: Version::default(), upgraded_sequence: Sequence::from(0), // The value of 0 indicates the channel has never been upgraded - flush_status: FlushStatus::NotinflushUnspecified, } } } @@ -140,8 +137,6 @@ impl TryFrom for ChannelEnd { let version = value.version.into(); - let flush_status = FlushStatus::try_from(value.flush_status)?; - Ok(ChannelEnd::new( chan_state, chan_ordering, @@ -149,7 +144,6 @@ impl TryFrom for ChannelEnd { connection_hops, version, Sequence::from(value.upgrade_sequence), - flush_status, )) } } @@ -157,7 +151,7 @@ impl TryFrom for ChannelEnd { impl From for RawChannel { fn from(value: ChannelEnd) -> Self { RawChannel { - state: value.state as i32, + state: value.state.as_i32(), ordering: value.ordering as i32, counterparty: Some(value.counterparty().clone().into()), connection_hops: value @@ -167,7 +161,6 @@ impl From for RawChannel { .collect(), version: value.version.to_string(), upgrade_sequence: value.upgraded_sequence.into(), - flush_status: RawFlushStatus::from(value.flush_status).into(), } } } @@ -181,7 +174,6 @@ impl ChannelEnd { connection_hops: Vec, version: Version, upgraded_sequence: Sequence, - flush_status: FlushStatus, ) -> Self { Self { state, @@ -190,7 +182,6 @@ impl ChannelEnd { connection_hops, version, upgraded_sequence, - flush_status, } } @@ -207,19 +198,17 @@ impl ChannelEnd { self.remote.channel_id = Some(c); } - /// Returns `true` if this `ChannelEnd` is in state [`State::Open`]. + /// Returns `true` if this `ChannelEnd` is in state [`State::Open`] + /// [`State::Open(UpgradeState::Upgrading)`] is only used in the channel upgrade + /// handshake so this method matches with [`State::Open(UpgradeState::NotUpgrading)`]. pub fn is_open(&self) -> bool { - self.state_matches(&State::Open) + self.state_matches(&State::Open(UpgradeState::NotUpgrading)) } pub fn state(&self) -> &State { &self.state } - pub fn flush_status(&self) -> &FlushStatus { - &self.flush_status - } - pub fn ordering(&self) -> &Ordering { &self.ordering } @@ -251,11 +240,6 @@ impl ChannelEnd { self.state() == other } - /// Helper function to compare the flush status of this end with another flush status. - pub fn flush_status_matches(&self, other: &FlushStatus) -> bool { - self.flush_status() == other - } - /// Helper function to compare the order of this end with another order. pub fn order_matches(&self, other: &Ordering) -> bool { self.ordering() == other @@ -397,6 +381,15 @@ impl FromStr for Ordering { } } +/// This enum is used to differentiate if a channel is being upgraded when +/// a `UpgradeInitChannel` or a `UpgradeOpenChannel` is received. +/// See `handshake_step` method in `crates/relayer/src/channel.rs`. +#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum UpgradeState { + Upgrading, + NotUpgrading, +} + /// The possible state variants that a channel can exhibit. /// /// These are encoded with integer discriminants so that there is @@ -407,34 +400,25 @@ impl FromStr for Ordering { #[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub enum State { /// Default state - Uninitialized = 0, + Uninitialized, /// A channel has just started the opening handshake. - Init = 1, + Init, /// A channel has acknowledged the handshake step on the counterparty chain. - TryOpen = 2, + TryOpen, /// A channel has completed the handshake step. Open channels are ready to /// send and receive packets. - Open = 3, + /// During some steps of channel upgrades, the state is still in Open. The + /// `UpgradeState` is used to differentiate these states during the upgrade + /// handshake. + /// + Open(UpgradeState), /// A channel has been closed and can no longer be used to send or receive /// packets. - Closed = 4, + Closed, /// A channel has just accepted the upgrade handshake attempt and is flushing in-flight packets. - Flushing = 5, + Flushing, /// A channel has just completed flushing any in-flight packets. - Flushcomplete = 6, - /// A channel has just started the upgrade handshake. The chain that is - /// proposing the upgrade should set the channel state from OPEN to INITUPGRADE. - InitUpgrade = 7, - /// A channel has acknowledged the upgrade handshake step on the counterparty chain. - /// The counterparty chain that accepts the upgrade should set the channel state from - /// OPEN to TRYUPGRADE. - /// The counterparty chain blocks new packets until the channel upgrade is done or cancelled. - TryUpgrade = 8, - /// A channel has confirmed the upgrade handshake step on the counterparty chain. - /// The chain that confirmed the upgrade should set the channel state from - /// INITUPGRADE to ACKUPGRADE. - /// The chain blocks new packets until the channel upgrade is done or cancelled. - AckUpgrade = 9, + Flushcomplete, } impl State { @@ -444,13 +428,10 @@ impl State { Self::Uninitialized => "UNINITIALIZED", Self::Init => "INIT", Self::TryOpen => "TRYOPEN", - Self::Open => "OPEN", + Self::Open(_) => "OPEN", Self::Closed => "CLOSED", Self::Flushing => "FLUSHING", Self::Flushcomplete => "FLUSHCOMPLETE", - Self::InitUpgrade => "INITUPGRADE", - Self::TryUpgrade => "TRYUPGRADE", - Self::AckUpgrade => "ACKUPGRADE", } } @@ -460,20 +441,30 @@ impl State { 0 => Ok(Self::Uninitialized), 1 => Ok(Self::Init), 2 => Ok(Self::TryOpen), - 3 => Ok(Self::Open), + 3 => Ok(Self::Open(UpgradeState::NotUpgrading)), 4 => Ok(Self::Closed), 5 => Ok(Self::Flushing), 6 => Ok(Self::Flushcomplete), - 7 => Ok(Self::InitUpgrade), - 8 => Ok(Self::TryUpgrade), - 9 => Ok(Self::AckUpgrade), _ => Err(Error::unknown_state(s)), } } + // Parses the State out from a i32. + pub fn as_i32(&self) -> i32 { + match self { + State::Uninitialized => 0, + State::Init => 1, + State::TryOpen => 2, + State::Open(_) => 3, + State::Closed => 4, + State::Flushing => 5, + State::Flushcomplete => 6, + } + } + /// Returns whether or not this channel state is `Open`. pub fn is_open(self) -> bool { - self == State::Open + self == State::Open(UpgradeState::NotUpgrading) } /// Returns whether or not this channel state is `Closed`. @@ -483,6 +474,7 @@ impl State { /// Returns whether or not the channel with this state /// has progressed less or the same than the argument. + /// This only takes into account the open channel handshake. /// /// # Example /// ```rust,ignore @@ -498,31 +490,23 @@ impl State { Init => !matches!(other, Uninitialized), TryOpen => !matches!(other, Uninitialized | Init), - Open => !matches!(other, Uninitialized | Init | TryOpen), + Open(UpgradeState::NotUpgrading) => !matches!(other, Uninitialized | Init | TryOpen), + _ => false, + } + } - Flushing => !matches!(other, Uninitialized | Init | TryOpen | Open), - Flushcomplete => !matches!(other, Uninitialized | Init | TryOpen | Open | Flushing), - InitUpgrade => !matches!( - other, - Uninitialized | Init | TryOpen | Open | Flushing | Flushcomplete - ), - TryUpgrade => !matches!( - other, - Uninitialized | Init | TryOpen | Open | Flushing | Flushcomplete | InitUpgrade - ), - AckUpgrade => !matches!( + /// Returns whether or not the channel with this state is + /// being upgraded. + pub fn is_upgrading(self, other: Self) -> bool { + use State::*; + + match self { + Open(UpgradeState::NotUpgrading) => matches!( other, - Uninitialized - | Init - | TryOpen - | Open - | Flushing - | Flushcomplete - | InitUpgrade - | TryUpgrade + Open(UpgradeState::Upgrading) | Flushing | Flushcomplete ), - - Closed => false, + Open(UpgradeState::Upgrading) | Flushing | Flushcomplete => true, + _ => false, } } } @@ -559,7 +543,6 @@ pub mod test_util { connection_hops: vec![ConnectionId::default().to_string()], version: "ics20".to_string(), // The version is not validated. upgrade_sequence: 0, // The value of 0 indicates the channel has never been upgraded - flush_status: 0, } } } @@ -700,4 +683,119 @@ mod tests { } } } + + #[test] + fn less_or_equal_progress_uninitialized() { + use crate::core::ics04_channel::channel::State; + use crate::core::ics04_channel::channel::UpgradeState; + + let higher_or_equal_states = vec![ + State::Uninitialized, + State::Init, + State::TryOpen, + State::Open(UpgradeState::NotUpgrading), + State::Open(UpgradeState::Upgrading), + State::Closed, + State::Flushing, + State::Flushcomplete, + ]; + for state in higher_or_equal_states { + assert!(State::Uninitialized.less_or_equal_progress(state)) + } + } + + #[test] + fn less_or_equal_progress_init() { + use crate::core::ics04_channel::channel::State; + use crate::core::ics04_channel::channel::UpgradeState; + + let lower_states = vec![State::Uninitialized]; + let higher_or_equal_states = vec![ + State::Init, + State::TryOpen, + State::Open(UpgradeState::NotUpgrading), + State::Open(UpgradeState::Upgrading), + State::Closed, + State::Flushing, + State::Flushcomplete, + ]; + for state in lower_states { + assert!(!State::Init.less_or_equal_progress(state)); + } + for state in higher_or_equal_states { + assert!(State::Init.less_or_equal_progress(state)) + } + } + + #[test] + fn less_or_equal_progress_tryopen() { + use crate::core::ics04_channel::channel::State; + use crate::core::ics04_channel::channel::UpgradeState; + + let lower_states = vec![State::Uninitialized, State::Init]; + let higher_or_equal_states = vec![ + State::TryOpen, + State::Open(UpgradeState::NotUpgrading), + State::Open(UpgradeState::Upgrading), + State::Closed, + State::Flushing, + State::Flushcomplete, + ]; + for state in lower_states { + assert!(!State::TryOpen.less_or_equal_progress(state)); + } + for state in higher_or_equal_states { + assert!(State::TryOpen.less_or_equal_progress(state)) + } + } + + #[test] + fn less_or_equal_progress_open_not_upgrading() { + use crate::core::ics04_channel::channel::State; + use crate::core::ics04_channel::channel::UpgradeState; + + let lower_states = vec![State::Uninitialized, State::Init, State::TryOpen]; + let higher_or_equal_states = vec![ + State::Open(UpgradeState::NotUpgrading), + State::Open(UpgradeState::Upgrading), + State::Closed, + State::Flushing, + State::Flushcomplete, + ]; + for state in lower_states { + assert!(!State::Open(UpgradeState::NotUpgrading).less_or_equal_progress(state)); + } + for state in higher_or_equal_states { + assert!(State::Open(UpgradeState::NotUpgrading).less_or_equal_progress(state)) + } + } + + #[test] + fn less_or_equal_progress_upgrading_states() { + use crate::core::ics04_channel::channel::State; + use crate::core::ics04_channel::channel::UpgradeState; + + let states = [ + State::Uninitialized, + State::Init, + State::TryOpen, + State::Open(UpgradeState::NotUpgrading), + State::Open(UpgradeState::Upgrading), + State::Closed, + State::Flushing, + State::Flushcomplete, + ]; + + let upgrading_states = vec![ + State::Open(UpgradeState::Upgrading), + State::Closed, + State::Flushing, + State::Flushcomplete, + ]; + for upgrade_state in upgrading_states { + for state in states.iter() { + assert!(!upgrade_state.less_or_equal_progress(*state)); + } + } + } } diff --git a/crates/relayer-types/src/core/ics04_channel/events.rs b/crates/relayer-types/src/core/ics04_channel/events.rs index e231089d49..0e466e17d2 100644 --- a/crates/relayer-types/src/core/ics04_channel/events.rs +++ b/crates/relayer-types/src/core/ics04_channel/events.rs @@ -7,7 +7,6 @@ use tendermint::abci; use crate::core::ics04_channel::channel::Ordering; use crate::core::ics04_channel::error::Error; -use crate::core::ics04_channel::flush_status::FlushStatus; use crate::core::ics04_channel::packet::Packet; use crate::core::ics04_channel::packet::Sequence; use crate::core::ics04_channel::version::Version; @@ -146,10 +145,16 @@ pub struct UpgradeAttributes { pub upgrade_version: Version, pub upgrade_sequence: Sequence, pub upgrade_ordering: Ordering, - pub channel_flush_status: FlushStatus, } -impl UpgradeAttributes {} +impl UpgradeAttributes { + pub fn port_id(&self) -> &PortId { + &self.port_id + } + pub fn channel_id(&self) -> &ChannelId { + &self.channel_id + } +} impl Display for UpgradeAttributes { fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { @@ -573,7 +578,6 @@ pub struct UpgradeInit { pub upgrade_version: Version, pub upgrade_sequence: Sequence, pub upgrade_ordering: Ordering, - pub channel_flush_status: FlushStatus, } impl Display for UpgradeInit { @@ -588,8 +592,8 @@ impl Display for UpgradeInit { } write!( f, - "], upgrade_version: {}, upgrade_sequence: {}, upgrade_ordering: {}, channel_flush_status: {} }}", - self.upgrade_version, self.upgrade_sequence, self.upgrade_ordering, self.channel_flush_status + "], upgrade_version: {}, upgrade_sequence: {}, upgrade_ordering: {} }}", + self.upgrade_version, self.upgrade_sequence, self.upgrade_ordering ) } } @@ -605,7 +609,6 @@ impl From for UpgradeAttributes { upgrade_version: ev.upgrade_version, upgrade_sequence: ev.upgrade_sequence, upgrade_ordering: ev.upgrade_ordering, - channel_flush_status: ev.channel_flush_status, } } } @@ -651,7 +654,6 @@ impl TryFrom for UpgradeInit { upgrade_version: attrs.upgrade_version, upgrade_sequence: attrs.upgrade_sequence, upgrade_ordering: attrs.upgrade_ordering, - channel_flush_status: attrs.channel_flush_status, }) } } @@ -678,7 +680,6 @@ pub struct UpgradeTry { pub upgrade_version: Version, pub upgrade_sequence: Sequence, pub upgrade_ordering: Ordering, - pub channel_flush_status: FlushStatus, } impl Display for UpgradeTry { @@ -693,8 +694,8 @@ impl Display for UpgradeTry { } write!( f, - "], upgrade_version: {}, upgrade_sequence: {}, upgrade_ordering: {}, channel_flush_status: {} }}", - self.upgrade_version, self.upgrade_sequence, self.upgrade_ordering, self.channel_flush_status + "], upgrade_version: {}, upgrade_sequence: {}, upgrade_ordering: {} }}", + self.upgrade_version, self.upgrade_sequence, self.upgrade_ordering ) } } @@ -710,7 +711,6 @@ impl From for UpgradeAttributes { upgrade_version: ev.upgrade_version, upgrade_sequence: ev.upgrade_sequence, upgrade_ordering: ev.upgrade_ordering, - channel_flush_status: ev.channel_flush_status, } } } @@ -756,7 +756,6 @@ impl TryFrom for UpgradeTry { upgrade_version: attrs.upgrade_version, upgrade_sequence: attrs.upgrade_sequence, upgrade_ordering: attrs.upgrade_ordering, - channel_flush_status: attrs.channel_flush_status, }) } } @@ -783,7 +782,6 @@ pub struct UpgradeAck { pub upgrade_version: Version, pub upgrade_sequence: Sequence, pub upgrade_ordering: Ordering, - pub channel_flush_status: FlushStatus, } impl Display for UpgradeAck { @@ -798,8 +796,8 @@ impl Display for UpgradeAck { } write!( f, - "], upgrade_version: {}, upgrade_sequence: {}, upgrade_ordering: {}, channel_flush_status: {} }}", - self.upgrade_version, self.upgrade_sequence, self.upgrade_ordering, self.channel_flush_status + "], upgrade_version: {}, upgrade_sequence: {}, upgrade_ordering: {} }}", + self.upgrade_version, self.upgrade_sequence, self.upgrade_ordering ) } } @@ -815,7 +813,6 @@ impl From for UpgradeAttributes { upgrade_version: ev.upgrade_version, upgrade_sequence: ev.upgrade_sequence, upgrade_ordering: ev.upgrade_ordering, - channel_flush_status: ev.channel_flush_status, } } } @@ -861,7 +858,6 @@ impl TryFrom for UpgradeAck { upgrade_version: attrs.upgrade_version, upgrade_sequence: attrs.upgrade_sequence, upgrade_ordering: attrs.upgrade_ordering, - channel_flush_status: attrs.channel_flush_status, }) } } @@ -888,7 +884,6 @@ pub struct UpgradeConfirm { pub upgrade_version: Version, pub upgrade_sequence: Sequence, pub upgrade_ordering: Ordering, - pub channel_flush_status: FlushStatus, } impl Display for UpgradeConfirm { @@ -903,8 +898,8 @@ impl Display for UpgradeConfirm { } write!( f, - "], upgrade_version: {}, upgrade_sequence: {}, upgrade_ordering: {}, channel_flush_status: {} }}", - self.upgrade_version, self.upgrade_sequence, self.upgrade_ordering, self.channel_flush_status + "], upgrade_version: {}, upgrade_sequence: {}, upgrade_ordering: {} }}", + self.upgrade_version, self.upgrade_sequence, self.upgrade_ordering ) } } @@ -920,7 +915,6 @@ impl From for UpgradeAttributes { upgrade_version: ev.upgrade_version, upgrade_sequence: ev.upgrade_sequence, upgrade_ordering: ev.upgrade_ordering, - channel_flush_status: ev.channel_flush_status, } } } @@ -966,7 +960,6 @@ impl TryFrom for UpgradeConfirm { upgrade_version: attrs.upgrade_version, upgrade_sequence: attrs.upgrade_sequence, upgrade_ordering: attrs.upgrade_ordering, - channel_flush_status: attrs.channel_flush_status, }) } } @@ -993,7 +986,6 @@ pub struct UpgradeOpen { pub upgrade_version: Version, pub upgrade_sequence: Sequence, pub upgrade_ordering: Ordering, - pub channel_flush_status: FlushStatus, } impl Display for UpgradeOpen { @@ -1008,8 +1000,8 @@ impl Display for UpgradeOpen { } write!( f, - "], upgrade_version: {}, upgrade_sequence: {}, upgrade_ordering: {}, channel_flush_status: {} }}", - self.upgrade_version, self.upgrade_sequence, self.upgrade_ordering, self.channel_flush_status + "], upgrade_version: {}, upgrade_sequence: {}, upgrade_ordering: {} }}", + self.upgrade_version, self.upgrade_sequence, self.upgrade_ordering ) } } @@ -1025,7 +1017,6 @@ impl From for UpgradeAttributes { upgrade_version: ev.upgrade_version, upgrade_sequence: ev.upgrade_sequence, upgrade_ordering: ev.upgrade_ordering, - channel_flush_status: ev.channel_flush_status, } } } @@ -1071,7 +1062,6 @@ impl TryFrom for UpgradeOpen { upgrade_version: attrs.upgrade_version, upgrade_sequence: attrs.upgrade_sequence, upgrade_ordering: attrs.upgrade_ordering, - channel_flush_status: attrs.channel_flush_status, }) } } diff --git a/crates/relayer-types/src/core/ics04_channel/flush_status.rs b/crates/relayer-types/src/core/ics04_channel/flush_status.rs deleted file mode 100644 index 80cfb172d3..0000000000 --- a/crates/relayer-types/src/core/ics04_channel/flush_status.rs +++ /dev/null @@ -1,73 +0,0 @@ -use std::fmt::Display; -use std::fmt::Error as FmtError; -use std::fmt::Formatter; -use std::str::FromStr; - -use serde::Deserialize; -use serde::Serialize; - -use ibc_proto::ibc::core::channel::v1::FlushStatus as RawFlushStatus; - -use crate::core::ics04_channel::error::Error; - -#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)] -pub enum FlushStatus { - #[default] - NotinflushUnspecified = 0, - Flushing = 1, - Flushcomplete = 2, -} - -impl Display for FlushStatus { - fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { - match self { - FlushStatus::NotinflushUnspecified => write!(f, "FLUSH_STATUS_NOTINFLUSH_UNSPECIFIED"), - FlushStatus::Flushing => write!(f, "FLUSH_STATUS_FLUSHING"), - FlushStatus::Flushcomplete => write!(f, "FLUSH_STATUS_FLUSHCOMPLETE"), - } - } -} - -impl FromStr for FlushStatus { - type Err = Error; - - fn from_str(s: &str) -> Result { - match s.to_lowercase().trim_start_matches("flush_status_") { - "notinflush_unspecified" => Ok(Self::NotinflushUnspecified), - "flushing" => Ok(Self::Flushing), - "flushcomplete" => Ok(Self::Flushcomplete), - _ => Err(Error::unknown_flush_status_type(s.to_string())), - } - } -} - -impl TryFrom for FlushStatus { - type Error = Error; - - fn try_from(value: RawFlushStatus) -> Result { - value.try_into() - } -} - -impl From for RawFlushStatus { - fn from(value: FlushStatus) -> Self { - match value { - FlushStatus::NotinflushUnspecified => RawFlushStatus::NotinflushUnspecified, - FlushStatus::Flushing => RawFlushStatus::Flushing, - FlushStatus::Flushcomplete => RawFlushStatus::Flushcomplete, - } - } -} - -impl TryFrom for FlushStatus { - type Error = Error; - - fn try_from(value: i32) -> Result { - match value { - 0 => Ok(FlushStatus::NotinflushUnspecified), - 1 => Ok(FlushStatus::Flushing), - 2 => Ok(FlushStatus::Flushcomplete), - _ => Err(Error::unknown_flush_status(value)), - } - } -} diff --git a/crates/relayer-types/src/core/ics04_channel/mod.rs b/crates/relayer-types/src/core/ics04_channel/mod.rs index b3abac8a4b..018a0608de 100644 --- a/crates/relayer-types/src/core/ics04_channel/mod.rs +++ b/crates/relayer-types/src/core/ics04_channel/mod.rs @@ -5,7 +5,6 @@ pub mod channel; pub mod commitment; pub mod error; pub mod events; -pub mod flush_status; pub mod msgs; pub mod packet; pub mod packet_id; diff --git a/crates/relayer-types/src/core/ics04_channel/msgs/chan_upgrade_confirm.rs b/crates/relayer-types/src/core/ics04_channel/msgs/chan_upgrade_confirm.rs index fb3c0ed1eb..3ae7856a13 100644 --- a/crates/relayer-types/src/core/ics04_channel/msgs/chan_upgrade_confirm.rs +++ b/crates/relayer-types/src/core/ics04_channel/msgs/chan_upgrade_confirm.rs @@ -108,7 +108,7 @@ impl From for RawMsgChannelUpgradeConfirm { RawMsgChannelUpgradeConfirm { port_id: domain_msg.port_id.to_string(), channel_id: domain_msg.channel_id.to_string(), - counterparty_channel_state: domain_msg.counterparty_channel_state as i32, + counterparty_channel_state: domain_msg.counterparty_channel_state.as_i32(), counterparty_upgrade: Some(domain_msg.counterparty_upgrade.into()), proof_upgrade: domain_msg.proof_upgrade.into(), proof_channel: domain_msg.proof_channel.into(), diff --git a/crates/relayer-types/src/core/ics04_channel/msgs/chan_upgrade_open.rs b/crates/relayer-types/src/core/ics04_channel/msgs/chan_upgrade_open.rs index 841a97ee45..a33ba54851 100644 --- a/crates/relayer-types/src/core/ics04_channel/msgs/chan_upgrade_open.rs +++ b/crates/relayer-types/src/core/ics04_channel/msgs/chan_upgrade_open.rs @@ -90,7 +90,7 @@ impl From for RawMsgChannelUpgradeOpen { RawMsgChannelUpgradeOpen { port_id: domain_msg.port_id.to_string(), channel_id: domain_msg.channel_id.to_string(), - counterparty_channel_state: domain_msg.counterparty_channel_state as i32, + counterparty_channel_state: domain_msg.counterparty_channel_state.as_i32(), proof_channel: domain_msg.proof_channel.into(), proof_height: Some(domain_msg.proof_height.into()), signer: domain_msg.signer.to_string(), @@ -111,7 +111,7 @@ pub mod test_util { RawMsgChannelUpgradeOpen { port_id: PortId::default().to_string(), channel_id: ChannelId::default().to_string(), - counterparty_channel_state: 7, // AckUpgrade + counterparty_channel_state: 6, // Flushcomplete proof_channel: get_dummy_proof(), proof_height: Some(RawHeight { revision_number: 1, diff --git a/crates/relayer-types/src/events.rs b/crates/relayer-types/src/events.rs index 5a36c572a1..caff7a421a 100644 --- a/crates/relayer-types/src/events.rs +++ b/crates/relayer-types/src/events.rs @@ -19,8 +19,8 @@ use crate::core::ics03_connection::error as connection_error; use crate::core::ics03_connection::events as ConnectionEvents; use crate::core::ics03_connection::events::Attributes as ConnectionAttributes; use crate::core::ics04_channel::error as channel_error; -use crate::core::ics04_channel::events as ChannelEvents; use crate::core::ics04_channel::events::Attributes as ChannelAttributes; +use crate::core::ics04_channel::events::{self as ChannelEvents, UpgradeAttributes}; use crate::core::ics04_channel::packet::Packet; use crate::core::ics24_host::error::ValidationError; use crate::timestamp::ParseTimestampError; @@ -450,6 +450,17 @@ impl IbcEvent { } } + pub fn channel_upgrade_attributes(self) -> Option { + match self { + IbcEvent::UpgradeInitChannel(ev) => Some(ev.into()), + IbcEvent::UpgradeTryChannel(ev) => Some(ev.into()), + IbcEvent::UpgradeAckChannel(ev) => Some(ev.into()), + IbcEvent::UpgradeConfirmChannel(ev) => Some(ev.into()), + IbcEvent::UpgradeOpenChannel(ev) => Some(ev.into()), + _ => None, + } + } + pub fn connection_attributes(&self) -> Option<&ConnectionAttributes> { match self { IbcEvent::OpenInitConnection(ev) => Some(ev.attributes()), diff --git a/crates/relayer/src/chain/counterparty.rs b/crates/relayer/src/chain/counterparty.rs index ac34d84f40..f1fcc7cd14 100644 --- a/crates/relayer/src/chain/counterparty.rs +++ b/crates/relayer/src/chain/counterparty.rs @@ -295,7 +295,11 @@ pub fn channel_on_destination( channel_id: remote_channel_id.clone(), height: QueryHeight::Latest, }, - IncludeProof::No, + // IncludeProof::Yes forces a new query when the CachingChainHandle + // is used. + // TODO: Pass the BaseChainHandle instead of the CachingChainHandle + // to the channel worker to avoid querying for a Proof . + IncludeProof::Yes, ) .map(|(c, _)| IdentifiedChannelEnd { port_id: channel.channel_end.counterparty().port_id().clone(), diff --git a/crates/relayer/src/channel.rs b/crates/relayer/src/channel.rs index 318fc593cb..8c46816278 100644 --- a/crates/relayer/src/channel.rs +++ b/crates/relayer/src/channel.rs @@ -1,6 +1,5 @@ pub use error::ChannelError; use ibc_proto::ibc::core::channel::v1::QueryUpgradeRequest; -use ibc_relayer_types::core::ics04_channel::flush_status::FlushStatus; use ibc_relayer_types::core::ics04_channel::msgs::chan_upgrade_ack::MsgChannelUpgradeAck; use ibc_relayer_types::core::ics04_channel::msgs::chan_upgrade_confirm::MsgChannelUpgradeConfirm; use ibc_relayer_types::core::ics04_channel::msgs::chan_upgrade_open::MsgChannelUpgradeOpen; @@ -15,7 +14,7 @@ use serde::Serialize; use tracing::{debug, error, info, warn}; use ibc_relayer_types::core::ics04_channel::channel::{ - ChannelEnd, Counterparty, IdentifiedChannelEnd, Ordering, State, + ChannelEnd, Counterparty, IdentifiedChannelEnd, Ordering, State, UpgradeState, }; use ibc_relayer_types::core::ics04_channel::msgs::chan_close_confirm::MsgChannelCloseConfirm; use ibc_relayer_types::core::ics04_channel::msgs::chan_close_init::MsgChannelCloseInit; @@ -309,7 +308,11 @@ impl Channel { channel_id: channel.src_channel_id.clone(), height: QueryHeight::Specific(height), }, - IncludeProof::No, + // IncludeProof::Yes forces a new query when the CachingChainHandle + // is used. + // TODO: Pass the BaseChainHandle instead of the CachingChainHandle + // to the channel worker to avoid querying for a Proof . + IncludeProof::Yes, ) .map_err(ChannelError::relayer)?; @@ -673,7 +676,7 @@ impl Channel { } // send the Confirm message to chain B (destination) - (State::Open, State::TryOpen) => { + (State::Open(UpgradeState::NotUpgrading), State::TryOpen) => { self.build_chan_open_confirm_and_send().map_err(|e| { error!("failed ChanOpenConfirm {}: {}", self.b_side, e); e @@ -681,7 +684,7 @@ impl Channel { } // send the Confirm message to chain A (source) - (State::TryOpen, State::Open) => { + (State::TryOpen, State::Open(UpgradeState::NotUpgrading)) => { self.flipped() .build_chan_open_confirm_and_send() .map_err(|e| { @@ -690,7 +693,7 @@ impl Channel { })?; } - (State::Open, State::Open) => { + (State::Open(UpgradeState::NotUpgrading), State::Open(UpgradeState::NotUpgrading)) => { info!("channel handshake already finished for {}", self); return Ok(()); } @@ -767,17 +770,33 @@ impl Channel { (State::Init, State::Init) => Some(self.build_chan_open_try_and_send()?), (State::TryOpen, State::Init) => Some(self.build_chan_open_ack_and_send()?), (State::TryOpen, State::TryOpen) => Some(self.build_chan_open_ack_and_send()?), - (State::Open, State::TryOpen) => Some(self.build_chan_open_confirm_and_send()?), - (State::Open, State::Open) => return Ok((None, Next::Abort)), + (State::Open(UpgradeState::NotUpgrading), State::TryOpen) => { + Some(self.build_chan_open_confirm_and_send()?) + } + (State::Open(UpgradeState::NotUpgrading), State::Open(_)) => { + return Ok((None, Next::Abort)) + } // If the counterparty state is already Open but current state is TryOpen, // return anyway as the final step is to be done by the counterparty worker. - (State::TryOpen, State::Open) => return Ok((None, Next::Abort)), + (State::TryOpen, State::Open(_)) => return Ok((None, Next::Abort)), // Close handshake steps (State::Closed, State::Closed) => return Ok((None, Next::Abort)), (State::Closed, _) => Some(self.build_chan_close_confirm_and_send()?), + // Channel Upgrade handshake steps + (State::Open(UpgradeState::Upgrading), State::Open(_)) => { + Some(self.build_chan_upgrade_try_and_send()?) + } + (State::Flushing, State::Open(_)) => Some(self.build_chan_upgrade_ack_and_send()?), + (State::Flushcomplete, State::Flushing) => { + Some(self.build_chan_upgrade_confirm_and_send()?) + } + (State::Open(_), State::Flushcomplete) => { + Some(self.build_chan_upgrade_open_and_send()?) + } + _ => None, }; @@ -786,7 +805,9 @@ impl Channel { match event { Some(IbcEvent::OpenConfirmChannel(_)) | Some(IbcEvent::OpenAckChannel(_)) - | Some(IbcEvent::CloseConfirmChannel(_)) => Ok((event, Next::Abort)), + | Some(IbcEvent::CloseConfirmChannel(_)) + | Some(IbcEvent::UpgradeConfirmChannel(_)) + | Some(IbcEvent::UpgradeOpenChannel(_)) => Ok((event, Next::Abort)), _ => Ok((event, Next::Continue)), } } @@ -817,9 +838,14 @@ impl Channel { let state = match event { IbcEvent::OpenInitChannel(_) => State::Init, IbcEvent::OpenTryChannel(_) => State::TryOpen, - IbcEvent::OpenAckChannel(_) => State::Open, - IbcEvent::OpenConfirmChannel(_) => State::Open, + IbcEvent::OpenAckChannel(_) => State::Open(UpgradeState::NotUpgrading), + IbcEvent::OpenConfirmChannel(_) => State::Open(UpgradeState::NotUpgrading), IbcEvent::CloseInitChannel(_) => State::Closed, + IbcEvent::UpgradeInitChannel(_) => State::Open(UpgradeState::Upgrading), + IbcEvent::UpgradeTryChannel(_) => State::Flushing, + IbcEvent::UpgradeAckChannel(_) => State::Flushcomplete, + IbcEvent::UpgradeConfirmChannel(_) => State::Flushcomplete, + IbcEvent::UpgradeOpenChannel(_) => State::Open(UpgradeState::Upgrading), _ => State::Uninitialized, }; @@ -871,7 +897,6 @@ impl Channel { vec![self.dst_connection_id().clone()], version, Sequence::from(0), - FlushStatus::NotinflushUnspecified, ); // Build the domain type message @@ -941,7 +966,7 @@ impl Channel { let highest_state = match msg_type { ChannelMsgType::OpenAck => State::TryOpen, ChannelMsgType::OpenConfirm => State::TryOpen, - ChannelMsgType::CloseConfirm => State::Open, + ChannelMsgType::CloseConfirm => State::Open(UpgradeState::NotUpgrading), _ => State::Uninitialized, }; @@ -952,7 +977,6 @@ impl Channel { vec![self.dst_connection_id().clone()], Version::empty(), Sequence::from(0), - FlushStatus::NotinflushUnspecified, // UPGRADE TODO check ); // Retrieve existing channel @@ -1045,7 +1069,6 @@ impl Channel { vec![self.dst_connection_id().clone()], version, Sequence::from(0), - FlushStatus::NotinflushUnspecified, // UPGRADE TODO check ); // Get signer @@ -1508,9 +1531,9 @@ impl Channel { ) .map_err(|e| ChannelError::query(self.dst_chain().id(), e))?; - if channel_end.state != State::Open { + if channel_end.state != State::Open(UpgradeState::NotUpgrading) { return Err(ChannelError::invalid_channel_upgrade_state( - State::Open.to_string(), + State::Open(UpgradeState::NotUpgrading).to_string(), channel_end.state.to_string(), )); } @@ -1687,9 +1710,9 @@ impl Channel { )); } - if channel_end.state != State::Open { + if channel_end.state != State::Open(UpgradeState::NotUpgrading) { return Err(ChannelError::invalid_channel_upgrade_state( - State::Open.to_string(), + State::Open(UpgradeState::NotUpgrading).to_string(), channel_end.state.to_string(), )); } @@ -2108,7 +2131,9 @@ fn check_destination_channel_state( existing_channel.connection_hops() == expected_channel.connection_hops(); // TODO: Refactor into a method - let good_state = *existing_channel.state() as u32 <= *expected_channel.state() as u32; + let good_state = existing_channel + .state() + .less_or_equal_progress(*expected_channel.state()); let good_channel_port_ids = existing_channel.counterparty().channel_id().is_none() || existing_channel.counterparty().channel_id() == expected_channel.counterparty().channel_id() diff --git a/crates/relayer/src/connection.rs b/crates/relayer/src/connection.rs index 341049446b..52326ad937 100644 --- a/crates/relayer/src/connection.rs +++ b/crates/relayer/src/connection.rs @@ -1365,7 +1365,9 @@ fn check_destination_connection_state( && existing_connection.counterparty().client_id() == expected_connection.counterparty().client_id(); - let good_state = *existing_connection.state() as u32 <= *expected_connection.state() as u32; + let good_state = existing_connection + .state() + .less_or_equal_progress(*expected_connection.state()); let good_connection_ids = existing_connection.counterparty().connection_id().is_none() || existing_connection.counterparty().connection_id() diff --git a/crates/relayer/src/event.rs b/crates/relayer/src/event.rs index 181c452c7a..a1771e56ff 100644 --- a/crates/relayer/src/event.rs +++ b/crates/relayer/src/event.rs @@ -5,6 +5,10 @@ use tendermint::abci::Event as AbciEvent; use ibc_relayer_types::{ applications::ics29_fee::events::{DistributeFeePacket, IncentivizedPacket}, applications::ics31_icq::events::CrossChainQueryPacket, + core::ics03_connection::{ + error::Error as ConnectionError, + events::{self as connection_events, Attributes as ConnectionAttributes}, + }, core::ics04_channel::{ error::Error as ChannelError, events::{ @@ -24,13 +28,6 @@ use ibc_relayer_types::{ ics04_channel::{channel::Ordering, packet::Sequence, version::Version}, ics24_host::identifier::ConnectionId, }, - core::{ - ics03_connection::{ - error::Error as ConnectionError, - events::{self as connection_events, Attributes as ConnectionAttributes}, - }, - ics04_channel::flush_status::FlushStatus, - }, events::{Error as IbcEventError, IbcEvent, IbcEventType}, Height, }; @@ -516,9 +513,6 @@ fn channel_upgrade_extract_attributes_from_tx( channel_events::UPGRADE_ORDERING => { attr.upgrade_ordering = Ordering::from_str(value)?; } - channel_events::CHANNEL_FLUSH_STATUS => { - attr.channel_flush_status = FlushStatus::from_str(value)?; - } _ => {} } } diff --git a/crates/relayer/src/event/source/websocket/extract.rs b/crates/relayer/src/event/source/websocket/extract.rs index ad8db55b6a..9530cd8122 100644 --- a/crates/relayer/src/event/source/websocket/extract.rs +++ b/crates/relayer/src/event/source/websocket/extract.rs @@ -235,6 +235,11 @@ fn event_is_type_channel(ev: &IbcEvent) -> bool { | IbcEvent::OpenConfirmChannel(_) | IbcEvent::CloseInitChannel(_) | IbcEvent::CloseConfirmChannel(_) + | IbcEvent::UpgradeInitChannel(_) + | IbcEvent::UpgradeTryChannel(_) + | IbcEvent::UpgradeAckChannel(_) + | IbcEvent::UpgradeConfirmChannel(_) + | IbcEvent::UpgradeOpenChannel(_) | IbcEvent::SendPacket(_) | IbcEvent::ReceivePacket(_) | IbcEvent::WriteAcknowledgement(_) diff --git a/crates/relayer/src/link.rs b/crates/relayer/src/link.rs index 5e9716aee4..1ed7dc7df3 100644 --- a/crates/relayer/src/link.rs +++ b/crates/relayer/src/link.rs @@ -1,6 +1,6 @@ use ibc_relayer_types::core::{ ics03_connection::connection::State as ConnectionState, - ics04_channel::channel::State as ChannelState, + ics04_channel::channel::{State as ChannelState, UpgradeState}, ics24_host::identifier::{ChannelId, PortChannelId, PortId}, }; use tracing::info; @@ -77,7 +77,7 @@ impl Link { ) })?; - if !a_channel.state_matches(&ChannelState::Open) + if !a_channel.state_matches(&ChannelState::Open(UpgradeState::NotUpgrading)) && !a_channel.state_matches(&ChannelState::Closed) { return Err(LinkError::invalid_channel_state( diff --git a/crates/relayer/src/object.rs b/crates/relayer/src/object.rs index 9f7349734d..eea5e2f60c 100644 --- a/crates/relayer/src/object.rs +++ b/crates/relayer/src/object.rs @@ -1,6 +1,7 @@ use std::fmt::Display; use flex_error::define_error; +use ibc_relayer_types::core::ics04_channel::events::UpgradeAttributes; use serde::{Deserialize, Serialize}; use ibc_relayer_types::applications::ics29_fee::events::IncentivizedPacket; @@ -460,6 +461,38 @@ impl Object { .into()) } + pub fn channel_from_chan_upgrade_events( + attributes: &UpgradeAttributes, + src_chain: &impl ChainHandle, + allow_non_open_connection: bool, + ) -> Result { + let channel_id = attributes.channel_id(); + + let port_id = attributes.port_id(); + + let dst_chain_id = if allow_non_open_connection { + // Get the destination chain allowing for the possibility that the connection is not yet open. + // This is to support the optimistic channel handshake by allowing the channel worker to get + // the channel events while the connection is being established. + // The channel worker will eventually finish the channel handshake via the retry mechanism. + channel_connection_client_no_checks(src_chain, port_id, channel_id) + .map(|c| c.client.client_state.chain_id()) + .map_err(ObjectError::supervisor)? + } else { + channel_connection_client(src_chain, port_id, channel_id) + .map(|c| c.client.client_state.chain_id()) + .map_err(ObjectError::supervisor)? + }; + + Ok(Channel { + dst_chain_id, + src_chain_id: src_chain.id(), + src_channel_id: channel_id.clone(), + src_port_id: port_id.clone(), + } + .into()) + } + /// Build the object associated with the given [`SendPacket`] event. pub fn for_send_packet( e: &SendPacket, diff --git a/crates/relayer/src/supervisor.rs b/crates/relayer/src/supervisor.rs index 8a28d07605..37c42acbfb 100644 --- a/crates/relayer/src/supervisor.rs +++ b/crates/relayer/src/supervisor.rs @@ -539,6 +539,30 @@ pub fn collect_events( || Object::client_from_chan_open_events(&attributes, src_chain).ok(), ); } + IbcEvent::UpgradeInitChannel(..) + | IbcEvent::UpgradeTryChannel(..) + | IbcEvent::UpgradeAckChannel(..) + | IbcEvent::UpgradeOpenChannel(..) => { + collect_event( + &mut collected, + event_with_height.clone(), + mode.channels.enabled, + || { + event_with_height + .event + .clone() + .channel_upgrade_attributes() + .and_then(|attr| { + Object::channel_from_chan_upgrade_events( + &attr, + src_chain, + mode.connections.enabled, + ) + .ok() + }) + }, + ); + } IbcEvent::SendPacket(ref packet) => { collect_event( &mut collected, diff --git a/crates/relayer/src/supervisor/spawn.rs b/crates/relayer/src/supervisor/spawn.rs index c7ffd2a240..eb5499b272 100644 --- a/crates/relayer/src/supervisor/spawn.rs +++ b/crates/relayer/src/supervisor/spawn.rs @@ -315,6 +315,8 @@ impl<'a, Chain: ChainHandle> SpawnContext<'a, Chain> { let open_handshake = chan_state_dst.less_or_equal_progress(ChannelState::TryOpen) && chan_state_dst.less_or_equal_progress(chan_state_src); + let upgrade_handshake = chan_state_dst.is_upgrading(chan_state_src); + // Determine if close handshake is required, i.e. if channel state on source is `Closed`, // and on destination channel state is not `Closed, and there are no pending packets. // If there are pending packets on destination then we let the packet worker clear the @@ -322,7 +324,7 @@ impl<'a, Chain: ChainHandle> SpawnContext<'a, Chain> { let close_handshake = chan_state_src.is_closed() && !chan_state_dst.is_closed() && !has_packets(); - if open_handshake || close_handshake { + if open_handshake || upgrade_handshake || close_handshake { // create worker for channel handshake that will advance the counterparty state let channel_object = Object::Channel(Channel { dst_chain_id: counterparty_chain.id(), diff --git a/crates/relayer/src/worker/channel.rs b/crates/relayer/src/worker/channel.rs index a6204a3df4..84a84e233b 100644 --- a/crates/relayer/src/worker/channel.rs +++ b/crates/relayer/src/worker/channel.rs @@ -1,5 +1,6 @@ use core::time::Duration; use crossbeam_channel::Receiver; +use ibc_relayer_types::events::IbcEventType; use tracing::{debug, error_span}; use crate::channel::{channel_handshake_retry, Channel as RelayChannel}; @@ -49,19 +50,39 @@ pub fn spawn_channel_worker( complete_handshake_on_new_block = false; if let Some(event_with_height) = last_event { - retry_with_index( - channel_handshake_retry::default_strategy(max_block_times), - |index| match RelayChannel::restore_from_event( - chains.a.clone(), - chains.b.clone(), - event_with_height.event.clone(), - ) { - Ok(mut handshake_channel) => handshake_channel - .step_event(&event_with_height.event, index), - Err(_) => RetryResult::Retry(index), - }, - ) - .map_err(|e| TaskError::Fatal(RunError::retry(e))) + match event_with_height.event.event_type() { + IbcEventType::UpgradeInitChannel + | IbcEventType::UpgradeTryChannel + | IbcEventType::UpgradeAckChannel + | IbcEventType::UpgradeConfirmChannel + | IbcEventType::UpgradeOpenChannel => retry_with_index( + channel_handshake_retry::default_strategy(max_block_times), + |index| match RelayChannel::restore_from_state( + chains.a.clone(), + chains.b.clone(), + channel.clone(), + event_with_height.height, + ) { + Ok((mut handshake_channel, _)) => handshake_channel + .step_event(&event_with_height.event, index), + Err(_) => RetryResult::Retry(index), + }, + ) + .map_err(|e| TaskError::Fatal(RunError::retry(e))), + _ => retry_with_index( + channel_handshake_retry::default_strategy(max_block_times), + |index| match RelayChannel::restore_from_event( + chains.a.clone(), + chains.b.clone(), + event_with_height.event.clone(), + ) { + Ok(mut handshake_channel) => handshake_channel + .step_event(&event_with_height.event, index), + Err(_) => RetryResult::Retry(index), + }, + ) + .map_err(|e| TaskError::Fatal(RunError::retry(e))), + } } else { Ok(Next::Continue) } diff --git a/e2e/e2e/channel.py b/e2e/e2e/channel.py index 0036ae7fa3..37f3871aee 100644 --- a/e2e/e2e/channel.py +++ b/e2e/e2e/channel.py @@ -472,15 +472,17 @@ def handshake( split() a_chan_end = query_channel_end(c, side_a, port_id, a_chan_id) - if a_chan_end.state != 'Open': + expected_state = str({'Open': 'NotUpgrading'}) + if str(a_chan_end.state) != str({'Open': 'NotUpgrading'}): l.error( - f'Channel end with id {a_chan_id} on chain {side_a} is not in Open state, got: {a_chan_end.state}') + f"Channel end with id {a_chan_id} on chain {side_a} is not in {expected_state} state, got: {a_chan_end.state}") exit(1) b_chan_end = query_channel_end(c, side_b, port_id, b_chan_id) - if b_chan_end.state != 'Open': + print(b_chan_end.state) + if str(b_chan_end.state) != str({'Open': 'NotUpgrading'}): l.error( - f'Channel end with id {b_chan_id} on chain {side_b} is not in Open state, got: {b_chan_end.state}') + f'Channel end with id {b_chan_id} on chain {side_b} is not in {expected_state} state, got: {b_chan_end.state}') exit(1) a_chan_ends = query_channel_ends(c, side_a, port_id, a_chan_id) diff --git a/flake.lock b/flake.lock index 6c1750e11d..48b6fb9850 100644 --- a/flake.lock +++ b/flake.lock @@ -896,11 +896,11 @@ }, "nixpkgs_4": { "locked": { - "lastModified": 1696757521, - "narHash": "sha256-cfgtLNCBLFx2qOzRLI6DHfqTdfWI+UbvsKYa3b3fvaA=", + "lastModified": 1697009197, + "narHash": "sha256-viVRhBTFT8fPJTb1N3brQIpFZnttmwo3JVKNuWRVc3s=", "owner": "nixos", "repo": "nixpkgs", - "rev": "2646b294a146df2781b1ca49092450e8a32814e1", + "rev": "01441e14af5e29c9d27ace398e6dd0b293e25a54", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index bba0185745..1fa649d7d7 100644 --- a/flake.nix +++ b/flake.nix @@ -4,54 +4,54 @@ inputs = { nixpkgs.url = github:nixos/nixpkgs/nixpkgs-unstable; flake-utils.url = github:numtide/flake-utils; - cosmos-nix.url = github:informalsystems/cosmos.nix/ibc-go-channel-upgrade; + cosmos-nix.url = github:informalsystems/cosmos.nix; }; outputs = inputs: let - utils = inputs.flake-utils.lib; - in + utils = inputs.flake-utils.lib; + in utils.eachSystem - [ - "aarch64-linux" - "aarch64-darwin" - "x86_64-darwin" - "x86_64-linux" - ] + [ + "aarch64-linux" + "aarch64-darwin" + "x86_64-darwin" + "x86_64-linux" + ] (system: let - nixpkgs = import inputs.nixpkgs { - inherit system; - }; + nixpkgs = import inputs.nixpkgs { + inherit system; + }; - cosmos-nix = inputs.cosmos-nix.packages.${system}; + cosmos-nix = inputs.cosmos-nix.packages.${system}; in { - packages = { + packages = { inherit (cosmos-nix) gaia6-ordered gaia12 - osmosis - wasmd - ibc-go-v2-simapp - ibc-go-v3-simapp - ibc-go-v4-simapp - ibc-go-v5-simapp - ibc-go-v6-simapp - ibc-go-v7-simapp - ibc-go-v7-channel-upgrade - apalache - evmos - juno - stride - stride-no-admin - stride-consumer-no-admin - stride-consumer - migaloo - neutron - ; + osmosis + wasmd + ibc-go-v2-simapp + ibc-go-v3-simapp + ibc-go-v4-simapp + ibc-go-v5-simapp + ibc-go-v6-simapp + ibc-go-v7-simapp + ibc-go-v8-channel-upgrade-simapp + apalache + evmos + juno + stride + stride-no-admin + stride-consumer-no-admin + stride-consumer + migaloo + neutron + ; - python = nixpkgs.python3.withPackages (p: [ - p.toml - ]); - }; - }); + python = nixpkgs.python3.withPackages (p: [ + p.toml + ]); + }; + }); } diff --git a/guide/src/templates/commands/hermes/tx/chan-upgrade-try_1.md b/guide/src/templates/commands/hermes/tx/chan-upgrade-try_1.md index 57a55e65ea..a2f0e74436 100644 --- a/guide/src/templates/commands/hermes/tx/chan-upgrade-try_1.md +++ b/guide/src/templates/commands/hermes/tx/chan-upgrade-try_1.md @@ -1 +1 @@ -[[#BINARY hermes]][[#GLOBALOPTIONS]] tx chan-upgrade-try --dst-chain [[#DST_CHAIN_ID]] --src-chain [[#SRC_CHAIN_ID]] --dst-connection [[#DST_CONNECTION_ID]] --dst-port [[#DST_PORT_ID]] --src-port [[#SRC_PORT_ID]] --src-channel [[#SRC_CHANNEL_ID]] --dst-channel [[#DST_CHANNEL_ID]] \ No newline at end of file +[[#BINARY hermes]][[#GLOBALOPTIONS]] tx chan-upgrade-try --dst-chain [[#DST_CHAIN_ID]] --src-chain [[#SRC_CHAIN_ID]] --dst-connection [[#DST_CONNECTION_ID]] --dst-port [[#DST_PORT_ID]] --src-port [[#SRC_PORT_ID]] --src-channel [[#SRC_CHANNEL_ID]] --dst-channel [[#DST_CHANNEL_ID]] diff --git a/tools/integration-test/src/tests/channel_upgrade/ics29.rs b/tools/integration-test/src/tests/channel_upgrade/ics29.rs new file mode 100644 index 0000000000..cdcae198fa --- /dev/null +++ b/tools/integration-test/src/tests/channel_upgrade/ics29.rs @@ -0,0 +1,220 @@ +//! Tests channel upgrade fetures: +//! +//! - `ChannelUpgradeICS29` tests that only after the upgrade handshake is completed +//! and the channel version has been updated to ICS29 can Incentivized packets be +//! relayed. + +use ibc_relayer::chain::requests::{IncludeProof, QueryChannelRequest, QueryHeight}; +use ibc_relayer_types::core::ics04_channel::version::Version; +use ibc_test_framework::prelude::*; +use ibc_test_framework::relayer::channel::{ + assert_eventually_channel_established, assert_eventually_channel_upgrade_open, + ChannelUpgradableAttributes, +}; +use ibc_test_framework::util::random::random_u128_range; + +#[test] +fn test_channel_upgrade_ics29() -> Result<(), Error> { + run_binary_channel_test(&ChannelUpgradeICS29) +} + +pub struct ChannelUpgradeICS29; + +impl TestOverrides for ChannelUpgradeICS29 { + fn modify_relayer_config(&self, config: &mut Config) { + config.mode.channels.enabled = true; + config.mode.packets.auto_register_counterparty_payee = true; + config.mode.packets.clear_interval = 0; + config.mode.packets.clear_on_start = false; + + config.mode.clients.misbehaviour = false; + } +} + +impl BinaryChannelTest for ChannelUpgradeICS29 { + fn run( + &self, + _config: &TestConfig, + _relayer: RelayerDriver, + chains: ConnectedChains, + channels: ConnectedChannel, + ) -> Result<(), Error> { + info!("Check that channels are both in OPEN State"); + + assert_eventually_channel_established( + &chains.handle_b, + &chains.handle_a, + &channels.channel_id_b.as_ref(), + &channels.port_b.as_ref(), + )?; + + let channel_end_a = chains + .handle_a + .query_channel( + QueryChannelRequest { + port_id: channels.port_a.0.clone(), + channel_id: channels.channel_id_a.0.clone(), + height: QueryHeight::Latest, + }, + IncludeProof::No, + ) + .map(|(channel_end, _)| channel_end) + .map_err(|e| eyre!("Error querying ChannelEnd A: {e}"))?; + + let channel_end_b = chains + .handle_b + .query_channel( + QueryChannelRequest { + port_id: channels.port_b.0.clone(), + channel_id: channels.channel_id_b.0.clone(), + height: QueryHeight::Latest, + }, + IncludeProof::No, + ) + .map(|(channel_end, _)| channel_end) + .map_err(|e| eyre!("Error querying ChannelEnd B: {e}"))?; + + let chain_driver_a = chains.node_a.chain_driver(); + let chain_driver_b = chains.node_b.chain_driver(); + + let denom_a = chains.node_a.denom(); + + let port_a = channels.port_a.as_ref(); + let channel_id_a = channels.channel_id_a.as_ref(); + + let wallets_a = chains.node_a.wallets(); + let wallets_b = chains.node_b.wallets(); + + let relayer_a = wallets_a.relayer(); + + let user_a = wallets_a.user1(); + let user_b = wallets_b.user1(); + + let balance_a1 = chain_driver_a.query_balance(&user_a.address(), &denom_a)?; + + let relayer_balance_a = chain_driver_a.query_balance(&relayer_a.address(), &denom_a)?; + + let send_amount = random_u128_range(1000, 2000); + let receive_fee = random_u128_range(300, 400); + let ack_fee = random_u128_range(200, 300); + let timeout_fee = random_u128_range(100, 200); + + let total_sent = send_amount + receive_fee + ack_fee + timeout_fee; + + let balance_a2 = balance_a1 - total_sent; + + let ics29_transfer = chain_driver_a.ibc_token_transfer_with_fee( + &port_a, + &channel_id_a, + &user_a, + &user_b.address(), + &denom_a.with_amount(send_amount).as_ref(), + &denom_a.with_amount(receive_fee).as_ref(), + &denom_a.with_amount(ack_fee).as_ref(), + &denom_a.with_amount(timeout_fee).as_ref(), + Duration::from_secs(60), + ); + + assert!(ics29_transfer.is_err(), "{ics29_transfer:?}"); + + let old_ordering = channel_end_a.ordering; + let old_connection_hops_a = channel_end_a.connection_hops; + let old_connection_hops_b = channel_end_b.connection_hops; + + let channel = channels.channel; + let new_version = Version::ics20_with_fee(); + let new_ordering = None; + let new_connection_hops = None; + + let upgraded_attrs = ChannelUpgradableAttributes::new( + new_version.clone(), + new_version.clone(), + old_ordering, + old_connection_hops_a, + old_connection_hops_b, + ); + + info!("Will initialise upgrade handshake by sending the ChanUpgradeInit step..."); + + // Note: Initialising a channel upgrade this way, without requiring a + // signature or proof of authority to perform the channel upgrade, will + // eventually be removed. + // Only authority (gov module or other) will be able to trigger a channel upgrade. + // See: https://github.com/cosmos/ibc-go/issues/4186 + channel.flipped().build_chan_upgrade_init_and_send( + Some(new_version), + new_ordering, + new_connection_hops, + )?; + + info!("Check that the channel upgrade successfully upgraded the version..."); + + assert_eventually_channel_upgrade_open( + &chains.handle_a, + &chains.handle_b, + &channels.channel_id_a.as_ref(), + &channels.port_a.as_ref(), + &upgraded_attrs, + )?; + + // Since the channel has been updated to ICS29 version after the Hermes instance + // was started, the `auto_register_counterparty_payee` has not registered the + // counterparty payee. It is required to register it manually. + chain_driver_b.register_counterparty_payee( + &wallets_b.relayer(), + &relayer_a.address(), + &channels.channel_id_b.as_ref(), + &channels.port_b.as_ref(), + )?; + + { + let counterparty_payee = chain_driver_b.query_counterparty_payee( + &channels.channel_id_b.as_ref(), + &wallets_b.relayer().address(), + )?; + + assert_eq( + "counterparty address should match registered address", + &counterparty_payee, + &Some(relayer_a.address().cloned()), + )?; + } + + chain_driver_a.ibc_token_transfer_with_fee( + &port_a, + &channel_id_a, + &user_a, + &user_b.address(), + &denom_a.with_amount(send_amount).as_ref(), + &denom_a.with_amount(receive_fee).as_ref(), + &denom_a.with_amount(ack_fee).as_ref(), + &denom_a.with_amount(timeout_fee).as_ref(), + Duration::from_secs(60), + )?; + + let denom_b = derive_ibc_denom( + &channels.port_b.as_ref(), + &channels.channel_id_b.as_ref(), + &denom_a, + )?; + + chain_driver_a.assert_eventual_wallet_amount(&user_a.address(), &balance_a2.as_ref())?; + + chain_driver_b.assert_eventual_wallet_amount( + &user_b.address(), + &denom_b.with_amount(send_amount).as_ref(), + )?; + + chain_driver_a.assert_eventual_wallet_amount( + &user_a.address(), + &(balance_a2 + timeout_fee).as_ref(), + )?; + + chain_driver_a.assert_eventual_wallet_amount( + &relayer_a.address(), + &(relayer_balance_a + ack_fee + receive_fee).as_ref(), + )?; + + Ok(()) + } +} diff --git a/tools/integration-test/src/tests/channel_upgrade/manual_channel_upgrade.rs b/tools/integration-test/src/tests/channel_upgrade/manual_channel_upgrade.rs deleted file mode 100644 index db7d60c4f0..0000000000 --- a/tools/integration-test/src/tests/channel_upgrade/manual_channel_upgrade.rs +++ /dev/null @@ -1,183 +0,0 @@ -//! Tests the successful channel upgrade handshake: -//! -//! - `ChannelUpgradeManualHandshake` tests that after the channel can be upgraded -//! without relaying on the supervisor. This test manually calls the INIT, TRY, -//! ACK and CONFIRM steps. - -use ibc_relayer::chain::requests::{IncludeProof, QueryChannelRequest, QueryHeight}; -use ibc_relayer_types::core::ics04_channel::version::Version; -use ibc_test_framework::prelude::*; -use ibc_test_framework::relayer::channel::{ - assert_eventually_channel_established, assert_eventually_channel_upgrade_ack, - assert_eventually_channel_upgrade_confirm, assert_eventually_channel_upgrade_init, - assert_eventually_channel_upgrade_open, assert_eventually_channel_upgrade_try, - ChannelUpgradableAttributes, -}; - -#[test] -fn test_channel_upgrade_manual_handshake() -> Result<(), Error> { - run_binary_channel_test(&ChannelUpgradeManualHandshake) -} - -pub struct ChannelUpgradeManualHandshake; - -impl TestOverrides for ChannelUpgradeManualHandshake { - fn should_spawn_supervisor(&self) -> bool { - false - } -} - -impl BinaryChannelTest for ChannelUpgradeManualHandshake { - fn run( - &self, - _config: &TestConfig, - _relayer: RelayerDriver, - chains: ConnectedChains, - channels: ConnectedChannel, - ) -> Result<(), Error> { - info!("Check that channels are both in OPEN State"); - - assert_eventually_channel_established( - &chains.handle_b, - &chains.handle_a, - &channels.channel_id_b.as_ref(), - &channels.port_b.as_ref(), - )?; - - let channel_end_a = chains - .handle_a - .query_channel( - QueryChannelRequest { - port_id: channels.port_a.0.clone(), - channel_id: channels.channel_id_a.0.clone(), - height: QueryHeight::Latest, - }, - IncludeProof::No, - ) - .map(|(channel_end, _)| channel_end) - .map_err(|e| eyre!("Error querying ChannelEnd A: {e}"))?; - - let channel_end_b = chains - .handle_b - .query_channel( - QueryChannelRequest { - port_id: channels.port_b.0.clone(), - channel_id: channels.channel_id_b.0.clone(), - height: QueryHeight::Latest, - }, - IncludeProof::No, - ) - .map(|(channel_end, _)| channel_end) - .map_err(|e| eyre!("Error querying ChannelEnd B: {e}"))?; - - let old_version = channel_end_a.version; - let old_ordering = channel_end_a.ordering; - let old_connection_hops_a = channel_end_a.connection_hops; - let old_connection_hops_b = channel_end_b.connection_hops; - - let channel = channels.channel; - let new_version = Version::ics20_with_fee(); - let new_ordering = None; - let new_connection_hops = None; - - let old_attrs = ChannelUpgradableAttributes::new( - old_version.clone(), - old_version.clone(), - old_ordering, - old_connection_hops_a.clone(), - old_connection_hops_b.clone(), - ); - - let interm_attrs = ChannelUpgradableAttributes::new( - old_version, - new_version.clone(), - old_ordering, - old_connection_hops_a.clone(), - old_connection_hops_b.clone(), - ); - - let upgraded_attrs = ChannelUpgradableAttributes::new( - new_version.clone(), - new_version.clone(), - old_ordering, - old_connection_hops_a, - old_connection_hops_b, - ); - - info!("Will run ChanUpgradeInit step..."); - - channel.flipped().build_chan_upgrade_init_and_send( - Some(new_version), - new_ordering, - new_connection_hops, - )?; - - info!("Check that the step ChanUpgradeInit was correctly executed..."); - - assert_eventually_channel_upgrade_init( - &chains.handle_a, - &chains.handle_b, - &channels.channel_id_a.as_ref(), - &channels.port_a.as_ref(), - &old_attrs, - )?; - - info!("Will run ChanUpgradeTry step..."); - - channel.build_chan_upgrade_try_and_send()?; - - info!("Check that the step ChanUpgradeTry was correctly executed..."); - - assert_eventually_channel_upgrade_try( - &chains.handle_b, - &chains.handle_a, - &channels.channel_id_b.as_ref(), - &channels.port_b.as_ref(), - &old_attrs.flipped(), - )?; - - info!("Will run ChanUpgradeAck step..."); - - channel.flipped().build_chan_upgrade_ack_and_send()?; - - info!("Check that the step ChanUpgradeAck was correctly executed..."); - - assert_eventually_channel_upgrade_ack( - &chains.handle_a, - &chains.handle_b, - &channels.channel_id_a.as_ref(), - &channels.port_a.as_ref(), - &old_attrs, - )?; - - info!("Will run ChanUpgradeConfirm step..."); - - channel.build_chan_upgrade_confirm_and_send()?; - - info!("Check that the step ChanUpgradeConfirm was correctly executed..."); - - assert_eventually_channel_upgrade_confirm( - &chains.handle_b, - &chains.handle_a, - &channels.channel_id_b.as_ref(), - &channels.port_b.as_ref(), - &interm_attrs.flipped(), - )?; - - info!("Will run ChanUpgradeOpen step..."); - - channel.flipped().build_chan_upgrade_open_and_send()?; - - info!("Check that the ChanUpgradeOpen steps were correctly executed..."); - - assert_eventually_channel_upgrade_open( - &chains.handle_a, - &chains.handle_b, - &channels.channel_id_a.as_ref(), - &channels.port_a.as_ref(), - &upgraded_attrs, - )?; - - Ok(()) - } -} diff --git a/tools/integration-test/src/tests/channel_upgrade/mod.rs b/tools/integration-test/src/tests/channel_upgrade/mod.rs index f5fee49272..158224819b 100644 --- a/tools/integration-test/src/tests/channel_upgrade/mod.rs +++ b/tools/integration-test/src/tests/channel_upgrade/mod.rs @@ -1 +1,3 @@ -pub mod manual_channel_upgrade; +pub mod ics29; +pub mod upgrade_handshake; +pub mod upgrade_handshake_steps; diff --git a/tools/integration-test/src/tests/channel_upgrade/upgrade_handshake.rs b/tools/integration-test/src/tests/channel_upgrade/upgrade_handshake.rs new file mode 100644 index 0000000000..b0e100b422 --- /dev/null +++ b/tools/integration-test/src/tests/channel_upgrade/upgrade_handshake.rs @@ -0,0 +1,116 @@ +//! Tests channel upgrade: +//! +//! - `ChannelUpgradeHandshake` tests that after the upgrade handshake is completed +//! after initialising the upgrade with `build_chan_upgrade_init_and_send` without +//! any in-flight packets. + +use ibc_relayer::chain::requests::{IncludeProof, QueryChannelRequest, QueryHeight}; +use ibc_relayer_types::core::ics04_channel::version::Version; +use ibc_test_framework::prelude::*; +use ibc_test_framework::relayer::channel::{ + assert_eventually_channel_established, assert_eventually_channel_upgrade_open, + ChannelUpgradableAttributes, +}; + +#[test] +fn test_channel_upgrade_handshake() -> Result<(), Error> { + run_binary_channel_test(&ChannelUpgradeHandshake) +} + +pub struct ChannelUpgradeHandshake; + +impl TestOverrides for ChannelUpgradeHandshake { + fn modify_relayer_config(&self, config: &mut Config) { + config.mode.channels.enabled = true; + } +} + +impl BinaryChannelTest for ChannelUpgradeHandshake { + fn run( + &self, + _config: &TestConfig, + _relayer: RelayerDriver, + chains: ConnectedChains, + channels: ConnectedChannel, + ) -> Result<(), Error> { + info!("Check that channels are both in OPEN State"); + + assert_eventually_channel_established( + &chains.handle_b, + &chains.handle_a, + &channels.channel_id_b.as_ref(), + &channels.port_b.as_ref(), + )?; + + let channel_end_a = chains + .handle_a + .query_channel( + QueryChannelRequest { + port_id: channels.port_a.0.clone(), + channel_id: channels.channel_id_a.0.clone(), + height: QueryHeight::Latest, + }, + IncludeProof::No, + ) + .map(|(channel_end, _)| channel_end) + .map_err(|e| eyre!("Error querying ChannelEnd A: {e}"))?; + + let channel_end_b = chains + .handle_b + .query_channel( + QueryChannelRequest { + port_id: channels.port_b.0.clone(), + channel_id: channels.channel_id_b.0.clone(), + height: QueryHeight::Latest, + }, + IncludeProof::No, + ) + .map(|(channel_end, _)| channel_end) + .map_err(|e| eyre!("Error querying ChannelEnd B: {e}"))?; + + let old_ordering = channel_end_a.ordering; + let old_connection_hops_a = channel_end_a.connection_hops; + let old_connection_hops_b = channel_end_b.connection_hops; + + let channel = channels.channel; + let new_version = Version::ics20_with_fee(); + let new_ordering = None; + let new_connection_hops = None; + + let upgraded_attrs = ChannelUpgradableAttributes::new( + new_version.clone(), + new_version.clone(), + old_ordering, + old_connection_hops_a, + old_connection_hops_b, + ); + + info!("Will initialise upgrade handshake by sending the ChanUpgradeInit step..."); + + // Note: Initialising a channel upgrade this way, without requiring a + // signature or proof of authority to perform the channel upgrade, will + // eventually be removed. + // Only authority (gov module or other) will be able to trigger a channel upgrade. + // See: https://github.com/cosmos/ibc-go/issues/4186 + channel.flipped().build_chan_upgrade_init_and_send( + Some(new_version), + new_ordering, + new_connection_hops, + )?; + + info!("Check that the channel upgrade successfully upgraded the version..."); + + // This will assert that both channel ends are eventually + // in Open state, and that the fields targeted by the upgrade + // have been correctly updated. + assert_eventually_channel_upgrade_open( + &chains.handle_a, + &chains.handle_b, + &channels.channel_id_a.as_ref(), + &channels.port_a.as_ref(), + &upgraded_attrs, + )?; + + Ok(()) + } +} diff --git a/tools/integration-test/src/tests/channel_upgrade/upgrade_handshake_steps.rs b/tools/integration-test/src/tests/channel_upgrade/upgrade_handshake_steps.rs new file mode 100644 index 0000000000..6c76d57638 --- /dev/null +++ b/tools/integration-test/src/tests/channel_upgrade/upgrade_handshake_steps.rs @@ -0,0 +1,644 @@ +//! Tests channel upgrade: +//! +//! - `ChannelUpgradeManualHandshake` tests each step of the channel upgrade manually, +//! without relaying on the supervisor. +//! +//! - `ChannelUpgradeHandshakeFromTry` tests that the channel worker will finish the +//! upgrade handshake if the channel is being upgraded and is at the Try step. +//! +//! - `ChannelUpgradeHandshakeFromAck` tests that the channel worker will finish the +//! upgrade handshake if the channel is being upgraded and is at the Ack step. +//! +//! - `ChannelUpgradeHandshakeFromConfirm` tests that the channel worker will finish the +//! upgrade handshake if the channel is being upgraded and is at the Confirm step. + +use ibc_relayer::chain::requests::{IncludeProof, QueryChannelRequest, QueryHeight}; +use ibc_relayer_types::core::ics04_channel::version::Version; +use ibc_test_framework::prelude::*; +use ibc_test_framework::relayer::channel::{ + assert_eventually_channel_established, assert_eventually_channel_upgrade_ack, + assert_eventually_channel_upgrade_confirm, assert_eventually_channel_upgrade_init, + assert_eventually_channel_upgrade_open, assert_eventually_channel_upgrade_try, + ChannelUpgradableAttributes, +}; + +#[test] +fn test_channel_upgrade_manual_handshake() -> Result<(), Error> { + run_binary_channel_test(&ChannelUpgradeManualHandshake) +} + +#[test] +fn test_channel_upgrade_handshake_from_try() -> Result<(), Error> { + run_binary_channel_test(&ChannelUpgradeHandshakeFromTry) +} + +#[test] +fn test_channel_upgrade_handshake_from_ack() -> Result<(), Error> { + run_binary_channel_test(&ChannelUpgradeHandshakeFromAck) +} + +#[test] +fn test_channel_upgrade_handshake_from_confirm() -> Result<(), Error> { + run_binary_channel_test(&ChannelUpgradeHandshakeFromConfirm) +} + +pub struct ChannelUpgradeManualHandshake; + +impl TestOverrides for ChannelUpgradeManualHandshake { + fn should_spawn_supervisor(&self) -> bool { + false + } +} + +impl BinaryChannelTest for ChannelUpgradeManualHandshake { + fn run( + &self, + _config: &TestConfig, + _relayer: RelayerDriver, + chains: ConnectedChains, + channels: ConnectedChannel, + ) -> Result<(), Error> { + info!("Check that channels are both in OPEN State"); + + assert_eventually_channel_established( + &chains.handle_b, + &chains.handle_a, + &channels.channel_id_b.as_ref(), + &channels.port_b.as_ref(), + )?; + + let channel_end_a = chains + .handle_a + .query_channel( + QueryChannelRequest { + port_id: channels.port_a.0.clone(), + channel_id: channels.channel_id_a.0.clone(), + height: QueryHeight::Latest, + }, + IncludeProof::No, + ) + .map(|(channel_end, _)| channel_end) + .map_err(|e| eyre!("Error querying ChannelEnd A: {e}"))?; + + let channel_end_b = chains + .handle_b + .query_channel( + QueryChannelRequest { + port_id: channels.port_b.0.clone(), + channel_id: channels.channel_id_b.0.clone(), + height: QueryHeight::Latest, + }, + IncludeProof::No, + ) + .map(|(channel_end, _)| channel_end) + .map_err(|e| eyre!("Error querying ChannelEnd B: {e}"))?; + + let old_version = channel_end_a.version; + let old_ordering = channel_end_a.ordering; + let old_connection_hops_a = channel_end_a.connection_hops; + let old_connection_hops_b = channel_end_b.connection_hops; + + let channel = channels.channel; + let new_version = Version::ics20_with_fee(); + let new_ordering = None; + let new_connection_hops = None; + + let old_attrs = ChannelUpgradableAttributes::new( + old_version.clone(), + old_version.clone(), + old_ordering, + old_connection_hops_a.clone(), + old_connection_hops_b.clone(), + ); + + let interm_attrs = ChannelUpgradableAttributes::new( + old_version, + new_version.clone(), + old_ordering, + old_connection_hops_a.clone(), + old_connection_hops_b.clone(), + ); + + let upgraded_attrs = ChannelUpgradableAttributes::new( + new_version.clone(), + new_version.clone(), + old_ordering, + old_connection_hops_a, + old_connection_hops_b, + ); + + info!("Will run ChanUpgradeInit step..."); + + // Note: Initialising a channel upgrade this way, without requiring a + // signature or proof of authority to perform the channel upgrade, will + // eventually be removed. + // Only authority (gov module or other) will be able to trigger a channel upgrade. + // See: https://github.com/cosmos/ibc-go/issues/4186 + channel.flipped().build_chan_upgrade_init_and_send( + Some(new_version), + new_ordering, + new_connection_hops, + )?; + + info!("Check that the step ChanUpgradeInit was correctly executed..."); + + assert_eventually_channel_upgrade_init( + &chains.handle_a, + &chains.handle_b, + &channels.channel_id_a.as_ref(), + &channels.port_a.as_ref(), + &old_attrs, + )?; + + info!("Will run ChanUpgradeTry step..."); + + channel.build_chan_upgrade_try_and_send()?; + + info!("Check that the step ChanUpgradeTry was correctly executed..."); + + assert_eventually_channel_upgrade_try( + &chains.handle_b, + &chains.handle_a, + &channels.channel_id_b.as_ref(), + &channels.port_b.as_ref(), + &old_attrs.flipped(), + )?; + + info!("Will run ChanUpgradeAck step..."); + + channel.flipped().build_chan_upgrade_ack_and_send()?; + + info!("Check that the step ChanUpgradeAck was correctly executed..."); + + assert_eventually_channel_upgrade_ack( + &chains.handle_a, + &chains.handle_b, + &channels.channel_id_a.as_ref(), + &channels.port_a.as_ref(), + &old_attrs, + )?; + + info!("Will run ChanUpgradeConfirm step..."); + + channel.build_chan_upgrade_confirm_and_send()?; + + info!("Check that the step ChanUpgradeConfirm was correctly executed..."); + + assert_eventually_channel_upgrade_confirm( + &chains.handle_b, + &chains.handle_a, + &channels.channel_id_b.as_ref(), + &channels.port_b.as_ref(), + &interm_attrs.flipped(), + )?; + + info!("Will run ChanUpgradeOpen step..."); + + channel.flipped().build_chan_upgrade_open_and_send()?; + + info!("Check that the ChanUpgradeOpen steps were correctly executed..."); + + // This will assert that both channel ends are eventually + // in Open state, and that the fields targeted by the upgrade + // have been correctly updated. + assert_eventually_channel_upgrade_open( + &chains.handle_a, + &chains.handle_b, + &channels.channel_id_a.as_ref(), + &channels.port_a.as_ref(), + &upgraded_attrs, + )?; + + Ok(()) + } +} + +pub struct ChannelUpgradeHandshakeFromTry; + +impl TestOverrides for ChannelUpgradeHandshakeFromTry { + fn modify_relayer_config(&self, config: &mut Config) { + config.mode.channels.enabled = true; + } + + fn should_spawn_supervisor(&self) -> bool { + false + } +} + +impl BinaryChannelTest for ChannelUpgradeHandshakeFromTry { + fn run( + &self, + _config: &TestConfig, + relayer: RelayerDriver, + chains: ConnectedChains, + channels: ConnectedChannel, + ) -> Result<(), Error> { + info!("Check that channels are both in OPEN State"); + + assert_eventually_channel_established( + &chains.handle_b, + &chains.handle_a, + &channels.channel_id_b.as_ref(), + &channels.port_b.as_ref(), + )?; + + let channel_end_a = chains + .handle_a + .query_channel( + QueryChannelRequest { + port_id: channels.port_a.0.clone(), + channel_id: channels.channel_id_a.0.clone(), + height: QueryHeight::Latest, + }, + IncludeProof::No, + ) + .map(|(channel_end, _)| channel_end) + .map_err(|e| eyre!("Error querying ChannelEnd A: {e}"))?; + + let channel_end_b = chains + .handle_b + .query_channel( + QueryChannelRequest { + port_id: channels.port_b.0.clone(), + channel_id: channels.channel_id_b.0.clone(), + height: QueryHeight::Latest, + }, + IncludeProof::No, + ) + .map(|(channel_end, _)| channel_end) + .map_err(|e| eyre!("Error querying ChannelEnd B: {e}"))?; + + let old_version = channel_end_a.version; + let old_ordering = channel_end_a.ordering; + let old_connection_hops_a = channel_end_a.connection_hops; + let old_connection_hops_b = channel_end_b.connection_hops; + + let channel = channels.channel; + let new_version = Version::ics20_with_fee(); + let new_ordering = None; + let new_connection_hops = None; + + let old_attrs = ChannelUpgradableAttributes::new( + old_version.clone(), + old_version.clone(), + old_ordering, + old_connection_hops_a.clone(), + old_connection_hops_b.clone(), + ); + + let upgraded_attrs = ChannelUpgradableAttributes::new( + new_version.clone(), + new_version.clone(), + old_ordering, + old_connection_hops_a, + old_connection_hops_b, + ); + + info!("Will initialise upgrade handshake by sending the ChanUpgradeInit step..."); + + // Note: Initialising a channel upgrade this way, without requiring a + // signature or proof of authority to perform the channel upgrade, will + // eventually be removed. + // Only authority (gov module or other) will be able to trigger a channel upgrade. + // See: https://github.com/cosmos/ibc-go/issues/4186 + channel.flipped().build_chan_upgrade_init_and_send( + Some(new_version), + new_ordering, + new_connection_hops, + )?; + + info!("Will run ChanUpgradeTry step..."); + + channel.build_chan_upgrade_try_and_send()?; + + info!("Check that the step ChanUpgradeTry was correctly executed..."); + + assert_eventually_channel_upgrade_try( + &chains.handle_b, + &chains.handle_a, + &channels.channel_id_b.as_ref(), + &channels.port_b.as_ref(), + &old_attrs.flipped(), + )?; + + info!("Check that the channel upgrade successfully upgraded the version..."); + + relayer.with_supervisor(|| { + // This will assert that both channel ends are eventually + // in Open state, and that the fields targeted by the upgrade + // have been correctly updated. + assert_eventually_channel_upgrade_open( + &chains.handle_a, + &chains.handle_b, + &channels.channel_id_a.as_ref(), + &channels.port_a.as_ref(), + &upgraded_attrs, + )?; + + Ok(()) + }) + } +} + +pub struct ChannelUpgradeHandshakeFromAck; + +impl TestOverrides for ChannelUpgradeHandshakeFromAck { + fn modify_relayer_config(&self, config: &mut Config) { + config.mode.channels.enabled = true; + } + + fn should_spawn_supervisor(&self) -> bool { + false + } +} + +impl BinaryChannelTest for ChannelUpgradeHandshakeFromAck { + fn run( + &self, + _config: &TestConfig, + relayer: RelayerDriver, + chains: ConnectedChains, + channels: ConnectedChannel, + ) -> Result<(), Error> { + info!("Check that channels are both in OPEN State"); + + assert_eventually_channel_established( + &chains.handle_b, + &chains.handle_a, + &channels.channel_id_b.as_ref(), + &channels.port_b.as_ref(), + )?; + + let channel_end_a = chains + .handle_a + .query_channel( + QueryChannelRequest { + port_id: channels.port_a.0.clone(), + channel_id: channels.channel_id_a.0.clone(), + height: QueryHeight::Latest, + }, + IncludeProof::No, + ) + .map(|(channel_end, _)| channel_end) + .map_err(|e| eyre!("Error querying ChannelEnd A: {e}"))?; + + let channel_end_b = chains + .handle_b + .query_channel( + QueryChannelRequest { + port_id: channels.port_b.0.clone(), + channel_id: channels.channel_id_b.0.clone(), + height: QueryHeight::Latest, + }, + IncludeProof::No, + ) + .map(|(channel_end, _)| channel_end) + .map_err(|e| eyre!("Error querying ChannelEnd B: {e}"))?; + + let old_version = channel_end_a.version; + let old_ordering = channel_end_a.ordering; + let old_connection_hops_a = channel_end_a.connection_hops; + let old_connection_hops_b = channel_end_b.connection_hops; + + let channel = channels.channel; + let new_version = Version::ics20_with_fee(); + let new_ordering = None; + let new_connection_hops = None; + + let old_attrs = ChannelUpgradableAttributes::new( + old_version.clone(), + old_version.clone(), + old_ordering, + old_connection_hops_a.clone(), + old_connection_hops_b.clone(), + ); + + let upgraded_attrs = ChannelUpgradableAttributes::new( + new_version.clone(), + new_version.clone(), + old_ordering, + old_connection_hops_a, + old_connection_hops_b, + ); + + info!("Will initialise upgrade handshake by sending the ChanUpgradeInit step..."); + + // Note: Initialising a channel upgrade this way, without requiring a + // signature or proof of authority to perform the channel upgrade, will + // eventually be removed. + // Only authority (gov module or other) will be able to trigger a channel upgrade. + // See: https://github.com/cosmos/ibc-go/issues/4186 + channel.flipped().build_chan_upgrade_init_and_send( + Some(new_version), + new_ordering, + new_connection_hops, + )?; + + info!("Will run ChanUpgradeTry step..."); + + channel.build_chan_upgrade_try_and_send()?; + + info!("Check that the step ChanUpgradeTry was correctly executed..."); + + assert_eventually_channel_upgrade_try( + &chains.handle_b, + &chains.handle_a, + &channels.channel_id_b.as_ref(), + &channels.port_b.as_ref(), + &old_attrs.flipped(), + )?; + + info!("Will run ChanUpgradeAck step..."); + + channel.flipped().build_chan_upgrade_ack_and_send()?; + + info!("Check that the step ChanUpgradeAck was correctly executed..."); + + assert_eventually_channel_upgrade_ack( + &chains.handle_a, + &chains.handle_b, + &channels.channel_id_a.as_ref(), + &channels.port_a.as_ref(), + &old_attrs, + )?; + + info!("Check that the channel upgrade successfully upgraded the version..."); + + relayer.with_supervisor(|| { + // This will assert that both channel ends are eventually + // in Open state, and that the fields targeted by the upgrade + // have been correctly updated. + assert_eventually_channel_upgrade_open( + &chains.handle_a, + &chains.handle_b, + &channels.channel_id_a.as_ref(), + &channels.port_a.as_ref(), + &upgraded_attrs, + )?; + + Ok(()) + }) + } +} +pub struct ChannelUpgradeHandshakeFromConfirm; + +impl TestOverrides for ChannelUpgradeHandshakeFromConfirm { + fn modify_relayer_config(&self, config: &mut Config) { + config.mode.channels.enabled = true; + } + + fn should_spawn_supervisor(&self) -> bool { + false + } +} + +impl BinaryChannelTest for ChannelUpgradeHandshakeFromConfirm { + fn run( + &self, + _config: &TestConfig, + relayer: RelayerDriver, + chains: ConnectedChains, + channels: ConnectedChannel, + ) -> Result<(), Error> { + info!("Check that channels are both in OPEN State"); + + assert_eventually_channel_established( + &chains.handle_b, + &chains.handle_a, + &channels.channel_id_b.as_ref(), + &channels.port_b.as_ref(), + )?; + + let channel_end_a = chains + .handle_a + .query_channel( + QueryChannelRequest { + port_id: channels.port_a.0.clone(), + channel_id: channels.channel_id_a.0.clone(), + height: QueryHeight::Latest, + }, + IncludeProof::No, + ) + .map(|(channel_end, _)| channel_end) + .map_err(|e| eyre!("Error querying ChannelEnd A: {e}"))?; + + let channel_end_b = chains + .handle_b + .query_channel( + QueryChannelRequest { + port_id: channels.port_b.0.clone(), + channel_id: channels.channel_id_b.0.clone(), + height: QueryHeight::Latest, + }, + IncludeProof::No, + ) + .map(|(channel_end, _)| channel_end) + .map_err(|e| eyre!("Error querying ChannelEnd B: {e}"))?; + + let old_version = channel_end_a.version; + let old_ordering = channel_end_a.ordering; + let old_connection_hops_a = channel_end_a.connection_hops; + let old_connection_hops_b = channel_end_b.connection_hops; + + let channel = channels.channel; + let new_version = Version::ics20_with_fee(); + let new_ordering = None; + let new_connection_hops = None; + + let old_attrs = ChannelUpgradableAttributes::new( + old_version.clone(), + old_version.clone(), + old_ordering, + old_connection_hops_a.clone(), + old_connection_hops_b.clone(), + ); + + let interm_attrs = ChannelUpgradableAttributes::new( + old_version, + new_version.clone(), + old_ordering, + old_connection_hops_a.clone(), + old_connection_hops_b.clone(), + ); + + let upgraded_attrs = ChannelUpgradableAttributes::new( + new_version.clone(), + new_version.clone(), + old_ordering, + old_connection_hops_a, + old_connection_hops_b, + ); + + info!("Will initialise upgrade handshake by sending the ChanUpgradeInit step..."); + + // Note: Initialising a channel upgrade this way, without requiring a + // signature or proof of authority to perform the channel upgrade, will + // eventually be removed. + // Only authority (gov module or other) will be able to trigger a channel upgrade. + // See: https://github.com/cosmos/ibc-go/issues/4186 + channel.flipped().build_chan_upgrade_init_and_send( + Some(new_version), + new_ordering, + new_connection_hops, + )?; + + info!("Will run ChanUpgradeTry step..."); + + channel.build_chan_upgrade_try_and_send()?; + + info!("Check that the step ChanUpgradeTry was correctly executed..."); + + assert_eventually_channel_upgrade_try( + &chains.handle_b, + &chains.handle_a, + &channels.channel_id_b.as_ref(), + &channels.port_b.as_ref(), + &old_attrs.flipped(), + )?; + + info!("Will run ChanUpgradeAck step..."); + + channel.flipped().build_chan_upgrade_ack_and_send()?; + + info!("Check that the step ChanUpgradeAck was correctly executed..."); + + assert_eventually_channel_upgrade_ack( + &chains.handle_a, + &chains.handle_b, + &channels.channel_id_a.as_ref(), + &channels.port_a.as_ref(), + &old_attrs, + )?; + + info!("Will run ChanUpgradeConfirm step..."); + + channel.build_chan_upgrade_confirm_and_send()?; + + info!("Check that the step ChanUpgradeConfirm was correctly executed..."); + + assert_eventually_channel_upgrade_confirm( + &chains.handle_b, + &chains.handle_a, + &channels.channel_id_b.as_ref(), + &channels.port_b.as_ref(), + &interm_attrs.flipped(), + )?; + + info!("Check that the channel upgrade successfully upgraded the version..."); + + relayer.with_supervisor(|| { + // This will assert that both channel ends are eventually + // in Open state, and that the fields targeted by the upgrade + // have been correctly updated. + assert_eventually_channel_upgrade_open( + &chains.handle_a, + &chains.handle_b, + &channels.channel_id_a.as_ref(), + &channels.port_a.as_ref(), + &upgraded_attrs, + )?; + + Ok(()) + }) + } +} diff --git a/tools/test-framework/src/chain/cli/query.rs b/tools/test-framework/src/chain/cli/query.rs index 8029d23929..9bf90f81bf 100644 --- a/tools/test-framework/src/chain/cli/query.rs +++ b/tools/test-framework/src/chain/cli/query.rs @@ -14,7 +14,9 @@ pub fn query_balance( wallet_id: &str, denom: &str, ) -> Result { - let res = simple_exec( + // SDK v0.50 has removed the `--denom` flag from the `query bank balances` CLI. + // It also changed the JSON output. + match simple_exec( chain_id, command_path, &[ @@ -29,20 +31,64 @@ pub fn query_balance( "--output", "json", ], - )? - .stdout; + ) { + Ok(output) => { + let amount_str = json::from_str::(&output.stdout) + .map_err(handle_generic_error)? + .get("amount") + .ok_or_else(|| eyre!("expected amount field"))? + .as_str() + .ok_or_else(|| eyre!("expected string field"))? + .to_string(); - let amount_str = json::from_str::(&res) - .map_err(handle_generic_error)? - .get("amount") - .ok_or_else(|| eyre!("expected amount field"))? - .as_str() - .ok_or_else(|| eyre!("expected string field"))? - .to_string(); + let amount = Amount::from_str(&amount_str).map_err(handle_generic_error)?; + + Ok(amount) + } + Err(_) => { + let res = simple_exec( + chain_id, + command_path, + &[ + "--node", + rpc_listen_address, + "query", + "bank", + "balances", + wallet_id, + "--output", + "json", + ], + )? + .stdout; + let amounts_array = + json::from_str::(&res).map_err(handle_generic_error)?; - let amount = Amount::from_str(&amount_str).map_err(handle_generic_error)?; + let balances = amounts_array + .get("balances") + .ok_or_else(|| eyre!("expected balances field"))? + .as_array() + .ok_or_else(|| eyre!("expected array field"))?; - Ok(amount) + let amount_str = balances + .iter() + .find(|a| { + a.get("denom") + .ok_or_else(|| eyre!("expected denom field")) + .unwrap() + == denom + }) + .ok_or_else(|| eyre!("no entry with denom `{denom}` found"))? + .get("amount") + .ok_or_else(|| eyre!("expected amount field"))? + .as_str() + .ok_or_else(|| eyre!("expected amount to be in string format"))?; + + let amount = Amount::from_str(amount_str).map_err(handle_generic_error)?; + + Ok(amount) + } + } } /** diff --git a/tools/test-framework/src/relayer/channel.rs b/tools/test-framework/src/relayer/channel.rs index 42ed7f9670..637ec1f195 100644 --- a/tools/test-framework/src/relayer/channel.rs +++ b/tools/test-framework/src/relayer/channel.rs @@ -4,9 +4,8 @@ use ibc_relayer::chain::handle::ChainHandle; use ibc_relayer::chain::requests::{IncludeProof, QueryChannelRequest, QueryHeight}; use ibc_relayer::channel::{extract_channel_id, Channel, ChannelSide}; use ibc_relayer_types::core::ics04_channel::channel::{ - ChannelEnd, IdentifiedChannelEnd, Ordering, State as ChannelState, + ChannelEnd, IdentifiedChannelEnd, Ordering, State as ChannelState, UpgradeState, }; -use ibc_relayer_types::core::ics04_channel::flush_status::FlushStatus; use ibc_relayer_types::core::ics04_channel::version::Version; use ibc_relayer_types::core::ics24_host::identifier::ConnectionId; @@ -240,7 +239,10 @@ pub fn assert_eventually_channel_established( a_side_state: ChannelState, b_side_state: ChannelState, - a_side_flush_status: FlushStatus, - b_side_flush_status: FlushStatus, handle_a: &ChainA, handle_b: &ChainB, channel_id_a: &TaggedChannelIdRef, @@ -433,17 +426,6 @@ fn assert_channel_upgrade_state( ))); } - if !channel_end_a - .value() - .flush_status_matches(&a_side_flush_status) - { - return Err(Error::generic(eyre!( - "expected channel end A flush status to be `{}`, but is instead `{}`", - a_side_flush_status, - channel_end_a.value().flush_status() - ))); - } - if !channel_end_a .value() .version_matches(upgrade_attrs.version_a()) @@ -493,17 +475,6 @@ fn assert_channel_upgrade_state( ))); } - if !channel_end_b - .value() - .flush_status_matches(&b_side_flush_status) - { - return Err(Error::generic(eyre!( - "expected channel end B flush status to be `{}`, but is instead `{}`", - b_side_flush_status, - channel_end_b.value().flush_status() - ))); - } - if !channel_end_b .value() .version_matches(upgrade_attrs.version_b())