diff --git a/CHANGELOG.md b/CHANGELOG.md index d809dee177..9eb182ccc4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,11 +14,13 @@ Special thanks to external contributors for this release: @CharlyCst ([#347]). - ICS 4 Domain Types for channel handshakes ([#315]) - [relayer] - Implement `query_header_at_height` via plain RPC queries (no light client verification) ([#336]) - - Implement the relayer logic for connection handshake message ([#358], [#359], [#360]) + - Implement the relayer logic for connection handshake messages ([#358], [#359], [#360]) + - Implement the relayer logic for channel handshake messages ([#371], [#372], [#373], [#374]) - [relayer-cli] - Merge light clients config in relayer config and add commands to add/remove light clients ([#348]) - CLI for client update message ([#277]) - - Implement the relayer CLI for connection handshake message ([#358], [#359], [#360]) + - Implement the relayer CLI for connection handshake messages ([#358], [#359], [#360]) + - Implement the relayer CLI for channel handshake messages ([#371], [#372], [#373], [#374]) - [proto-compiler] - Refactor and allow specifying a commit at which the Cosmos SDK should be checked out ([#366]) - Add a `--tag` option to the `clone-sdk` command to check out a tag instead of a commit ([#369]) @@ -36,6 +38,11 @@ Special thanks to external contributors for this release: @CharlyCst ([#347]). [#366]: https://github.com/informalsystems/ibc-rs/issues/366 [#368]: https://github.com/informalsystems/ibc-rs/issues/368 [#369]: https://github.com/informalsystems/ibc-rs/pulls/369 +[#371]: https://github.com/informalsystems/ibc-rs/issues/371 +[#372]: https://github.com/informalsystems/ibc-rs/issues/372 +[#373]: https://github.com/informalsystems/ibc-rs/issues/373 +[#374]: https://github.com/informalsystems/ibc-rs/issues/374 + [proto-compiler]: https://github.com/informalsystems/ibc-rs/tree/master/proto-compiler ### IMPROVEMENTS diff --git a/Cargo.toml b/Cargo.toml index 0001bad76b..85d053d133 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,4 +9,4 @@ members = [ exclude = [ "proto-compiler" -] +] \ No newline at end of file diff --git a/modules/Cargo.toml b/modules/Cargo.toml index e25f3a7b48..a99983232e 100644 --- a/modules/Cargo.toml +++ b/modules/Cargo.toml @@ -33,17 +33,17 @@ regex = "1" bech32 = "0.7.2" [dependencies.tendermint] -version = "0.17.0-rc2" +version = "=0.17.0-rc3" [dependencies.tendermint-rpc] -version = "0.17.0-rc2" +version = "=0.17.0-rc3" features = ["http-client", "websocket-client"] [dependencies.tendermint-light-client] -version = "0.17.0-rc2" +version = "=0.17.0-rc3" [dependencies.tendermint-proto] -version = "0.17.0-rc2" +version = "=0.17.0-rc3" [dev-dependencies] tokio = { version = "0.2", features = ["macros"] } diff --git a/modules/src/ics02_client/client_def.rs b/modules/src/ics02_client/client_def.rs index 032449e324..e953149fa9 100644 --- a/modules/src/ics02_client/client_def.rs +++ b/modules/src/ics02_client/client_def.rs @@ -1,7 +1,7 @@ use prost_types::Any; use std::convert::TryFrom; -use tendermint_proto::DomainType; +use tendermint_proto::Protobuf; use crate::downcast; use crate::ics02_client::client_type::ClientType; @@ -119,7 +119,7 @@ impl Header for AnyHeader { } } -impl DomainType for AnyHeader {} +impl Protobuf for AnyHeader {} impl TryFrom for AnyHeader { type Error = Error; @@ -185,7 +185,7 @@ impl AnyClientState { } } -impl DomainType for AnyClientState {} +impl Protobuf for AnyClientState {} impl TryFrom for AnyClientState { type Error = Error; @@ -267,7 +267,7 @@ impl AnyConsensusState { } } -impl DomainType for AnyConsensusState {} +impl Protobuf for AnyConsensusState {} impl TryFrom for AnyConsensusState { type Error = Error; diff --git a/modules/src/ics02_client/height.rs b/modules/src/ics02_client/height.rs index 205abd36cf..8d4bd57bd5 100644 --- a/modules/src/ics02_client/height.rs +++ b/modules/src/ics02_client/height.rs @@ -1,6 +1,6 @@ use std::{cmp::Ordering, convert::TryFrom}; -use tendermint_proto::DomainType; +use tendermint_proto::Protobuf; use crate::ics02_client::error::{Error, Kind}; use ibc_proto::ibc::core::client::v1::Height as RawHeight; @@ -87,7 +87,7 @@ impl Ord for Height { } } -impl DomainType for Height {} +impl Protobuf for Height {} impl TryFrom for Height { type Error = anomaly::Error; diff --git a/modules/src/ics02_client/msgs/create_client.rs b/modules/src/ics02_client/msgs/create_client.rs index b877c1856c..5c6b915988 100644 --- a/modules/src/ics02_client/msgs/create_client.rs +++ b/modules/src/ics02_client/msgs/create_client.rs @@ -8,7 +8,7 @@ use std::convert::TryFrom; use std::str::FromStr; use tendermint::account::Id as AccountId; -use tendermint_proto::DomainType; +use tendermint_proto::Protobuf; use ibc_proto::ibc::core::client::v1::MsgCreateClient as RawMsgCreateClient; @@ -87,7 +87,7 @@ impl Msg for MsgCreateAnyClient { } } -impl DomainType for MsgCreateAnyClient {} +impl Protobuf for MsgCreateAnyClient {} impl TryFrom for MsgCreateAnyClient { type Error = Error; diff --git a/modules/src/ics02_client/msgs/update_client.rs b/modules/src/ics02_client/msgs/update_client.rs index 9530d85dc9..a5400baf24 100644 --- a/modules/src/ics02_client/msgs/update_client.rs +++ b/modules/src/ics02_client/msgs/update_client.rs @@ -7,7 +7,7 @@ use std::convert::TryFrom; use tendermint::account::Id as AccountId; -use tendermint_proto::DomainType; +use tendermint_proto::Protobuf; use ibc_proto::ibc::core::client::v1::MsgUpdateClient as RawMsgUpdateClient; @@ -62,7 +62,7 @@ impl Msg for MsgUpdateAnyClient { } } -impl DomainType for MsgUpdateAnyClient {} +impl Protobuf for MsgUpdateAnyClient {} impl TryFrom for MsgUpdateAnyClient { type Error = Error; diff --git a/modules/src/ics02_client/raw.rs b/modules/src/ics02_client/raw.rs index 2705e28487..0fc4988b95 100644 --- a/modules/src/ics02_client/raw.rs +++ b/modules/src/ics02_client/raw.rs @@ -2,7 +2,7 @@ use crate::ics03_connection::error::Kind; use crate::ics24_host::identifier::ConnectionId; use std::convert::TryFrom; use std::str::FromStr; -use tendermint_proto::DomainType; +use tendermint_proto::Protobuf; //TODO: This might need to be migrated to ibc-proto crate. But ClientConnections (as array of strings) // might not be part of an official proto file @@ -15,7 +15,7 @@ pub struct RawClientConnections { #[derive(Clone, Debug)] pub struct ConnectionIds(pub Vec); -impl DomainType for ConnectionIds {} +impl Protobuf for ConnectionIds {} impl TryFrom for ConnectionIds { type Error = anomaly::Error; diff --git a/modules/src/ics03_connection/connection.rs b/modules/src/ics03_connection/connection.rs index c4e8d19c96..cbf882f497 100644 --- a/modules/src/ics03_connection/connection.rs +++ b/modules/src/ics03_connection/connection.rs @@ -4,7 +4,7 @@ use std::str::FromStr; use ibc_proto::ibc::core::connection::v1::{ ConnectionEnd as RawConnectionEnd, Counterparty as RawCounterparty, }; -use tendermint_proto::DomainType; +use tendermint_proto::Protobuf; use crate::ics03_connection::error::{Error, Kind}; use crate::ics03_connection::version::validate_versions; @@ -20,7 +20,7 @@ pub struct ConnectionEnd { versions: Vec, } -impl DomainType for ConnectionEnd {} +impl Protobuf for ConnectionEnd {} impl TryFrom for ConnectionEnd { type Error = anomaly::Error; diff --git a/modules/src/ics03_connection/msgs/conn_open_ack.rs b/modules/src/ics03_connection/msgs/conn_open_ack.rs index ab79ed3ede..31069a85c4 100644 --- a/modules/src/ics03_connection/msgs/conn_open_ack.rs +++ b/modules/src/ics03_connection/msgs/conn_open_ack.rs @@ -2,7 +2,7 @@ use std::convert::{TryFrom, TryInto}; use std::str::FromStr; use ibc_proto::ibc::core::connection::v1::MsgConnectionOpenAck as RawMsgConnectionOpenAck; -use tendermint_proto::DomainType; +use tendermint_proto::Protobuf; use tendermint::account::Id as AccountId; @@ -90,7 +90,7 @@ impl Msg for MsgConnectionOpenAck { } } -impl DomainType for MsgConnectionOpenAck {} +impl Protobuf for MsgConnectionOpenAck {} impl TryFrom for MsgConnectionOpenAck { type Error = anomaly::Error; diff --git a/modules/src/ics03_connection/msgs/conn_open_confirm.rs b/modules/src/ics03_connection/msgs/conn_open_confirm.rs index 19eab21213..3f0420eed4 100644 --- a/modules/src/ics03_connection/msgs/conn_open_confirm.rs +++ b/modules/src/ics03_connection/msgs/conn_open_confirm.rs @@ -1,7 +1,7 @@ use std::convert::{TryFrom, TryInto}; use ibc_proto::ibc::core::connection::v1::MsgConnectionOpenConfirm as RawMsgConnectionOpenConfirm; -use tendermint_proto::DomainType; +use tendermint_proto::Protobuf; use tendermint::account::Id as AccountId; @@ -59,7 +59,7 @@ impl Msg for MsgConnectionOpenConfirm { } } -impl DomainType for MsgConnectionOpenConfirm {} +impl Protobuf for MsgConnectionOpenConfirm {} impl TryFrom for MsgConnectionOpenConfirm { type Error = anomaly::Error; diff --git a/modules/src/ics03_connection/msgs/conn_open_init.rs b/modules/src/ics03_connection/msgs/conn_open_init.rs index fbacad0a4f..9f90ca259d 100644 --- a/modules/src/ics03_connection/msgs/conn_open_init.rs +++ b/modules/src/ics03_connection/msgs/conn_open_init.rs @@ -1,7 +1,7 @@ use std::convert::{TryFrom, TryInto}; use ibc_proto::ibc::core::connection::v1::MsgConnectionOpenInit as RawMsgConnectionOpenInit; -use tendermint_proto::DomainType; +use tendermint_proto::Protobuf; use tendermint::account::Id as AccountId; @@ -75,7 +75,7 @@ impl Msg for MsgConnectionOpenInit { } } -impl DomainType for MsgConnectionOpenInit {} +impl Protobuf for MsgConnectionOpenInit {} impl TryFrom for MsgConnectionOpenInit { type Error = anomaly::Error; diff --git a/modules/src/ics03_connection/msgs/conn_open_try.rs b/modules/src/ics03_connection/msgs/conn_open_try.rs index 49381b2332..e9c4636cea 100644 --- a/modules/src/ics03_connection/msgs/conn_open_try.rs +++ b/modules/src/ics03_connection/msgs/conn_open_try.rs @@ -2,7 +2,7 @@ use std::convert::{TryFrom, TryInto}; use std::str::FromStr; use ibc_proto::ibc::core::connection::v1::MsgConnectionOpenTry as RawMsgConnectionOpenTry; -use tendermint_proto::DomainType; +use tendermint_proto::Protobuf; use tendermint::account::Id as AccountId; @@ -107,7 +107,7 @@ impl Msg for MsgConnectionOpenTry { } } -impl DomainType for MsgConnectionOpenTry {} +impl Protobuf for MsgConnectionOpenTry {} impl TryFrom for MsgConnectionOpenTry { type Error = Error; diff --git a/modules/src/ics03_connection/version.rs b/modules/src/ics03_connection/version.rs index 6c8e9c7a60..7371d8c5a5 100644 --- a/modules/src/ics03_connection/version.rs +++ b/modules/src/ics03_connection/version.rs @@ -1,7 +1,7 @@ use std::convert::TryFrom; use ibc_proto::ibc::core::connection::v1::Version as RawVersion; -use tendermint_proto::DomainType; +use tendermint_proto::Protobuf; use crate::ics03_connection::error::{Error, Kind}; use std::str::FromStr; @@ -14,7 +14,7 @@ struct Version { features: Vec, } -impl DomainType for Version {} +impl Protobuf for Version {} impl TryFrom for Version { type Error = anomaly::Error; diff --git a/modules/src/ics04_channel/channel.rs b/modules/src/ics04_channel/channel.rs index 587c801b93..5810982faa 100644 --- a/modules/src/ics04_channel/channel.rs +++ b/modules/src/ics04_channel/channel.rs @@ -2,10 +2,12 @@ use crate::ics04_channel::error::{self, Error, Kind}; use crate::ics24_host::identifier::{ChannelId, ConnectionId, PortId}; use ibc_proto::ibc::core::channel::v1::Channel as RawChannel; -use tendermint_proto::DomainType; +use ibc_proto::ibc::core::channel::v1::Counterparty as RawCounterparty; + +use tendermint_proto::Protobuf; use anomaly::fail; -use std::convert::TryFrom; +use std::convert::{TryFrom, TryInto}; use std::str::FromStr; #[derive(Clone, Debug, PartialEq)] @@ -17,7 +19,7 @@ pub struct ChannelEnd { version: String, } -impl DomainType for ChannelEnd {} +impl Protobuf for ChannelEnd {} impl TryFrom for ChannelEnd { type Error = anomaly::Error; @@ -28,19 +30,11 @@ impl TryFrom for ChannelEnd { let chan_state = State::from_i32(value.state)?; - // Pop out the Counterparty from the Option. - let counterparty = match value.counterparty { - Some(cp) => cp, - None => return Err(Kind::MissingCounterparty.into()), - }; - // Assemble the 'remote' attribute of the Channel, which represents the Counterparty. - let remote = Counterparty { - port_id: PortId::from_str(counterparty.port_id.as_str()) - .map_err(|e| Kind::IdentifierError.context(e))?, - channel_id: ChannelId::from_str(counterparty.channel_id.as_str()) - .map_err(|e| Kind::IdentifierError.context(e))?, - }; + let remote = value + .counterparty + .ok_or_else(|| Kind::MissingCounterparty)? + .try_into()?; // Parse each item in connection_hops into a ConnectionId. let connection_hops = value @@ -52,10 +46,13 @@ impl TryFrom for ChannelEnd { let version = validate_version(value.version)?; - let mut channel_end = ChannelEnd::new(chan_ordering, remote, connection_hops, version); - channel_end.set_state(chan_state); - - Ok(channel_end) + Ok(ChannelEnd::new( + chan_state, + chan_ordering, + remote, + connection_hops, + version, + )) } } @@ -63,11 +60,8 @@ impl From for RawChannel { fn from(value: ChannelEnd) -> Self { RawChannel { state: value.state.clone() as i32, - ordering: value.ordering.clone() as i32, - counterparty: Some(ibc_proto::ibc::core::channel::v1::Counterparty { - port_id: value.counterparty().port_id.to_string(), - channel_id: value.counterparty().channel_id.to_string(), - }), + ordering: value.ordering as i32, + counterparty: Some(value.counterparty().into()), connection_hops: value .connection_hops .iter() @@ -81,13 +75,14 @@ impl From for RawChannel { impl ChannelEnd { /// Creates a new ChannelEnd in state Uninitialized and other fields parametrized. pub fn new( + state: State, ordering: Order, remote: Counterparty, connection_hops: Vec, version: String, ) -> Self { Self { - state: State::Uninitialized, + state, ordering, remote, connection_hops, @@ -135,28 +130,24 @@ impl ChannelEnd { #[derive(Clone, Debug, PartialEq)] pub struct Counterparty { - port_id: PortId, - channel_id: ChannelId, + pub port_id: PortId, + pub channel_id: Option, } impl Counterparty { - pub fn new(port_id: String, channel_id: String) -> Result { - Ok(Self { - port_id: port_id - .parse() - .map_err(|e| Kind::IdentifierError.context(e))?, - channel_id: channel_id - .parse() - .map_err(|e| Kind::IdentifierError.context(e))?, - }) + pub fn new(port_id: PortId, channel_id: Option) -> Self { + Self { + port_id, + channel_id, + } } - pub fn port_id(&self) -> String { - self.port_id.as_str().into() + pub fn port_id(&self) -> &PortId { + &self.port_id } - pub fn channel_id(&self) -> String { - self.channel_id.as_str().into() + pub fn channel_id(&self) -> Option<&ChannelId> { + self.channel_id.as_ref() } pub fn validate_basic(&self) -> Result<(), Error> { @@ -164,13 +155,51 @@ impl Counterparty { } } -#[derive(Clone, Debug, PartialEq)] +impl Protobuf for Counterparty {} + +impl TryFrom for Counterparty { + type Error = anomaly::Error; + + fn try_from(value: RawCounterparty) -> Result { + let channel_id = Some(value.channel_id) + .filter(|x| !x.is_empty()) + .map(|v| FromStr::from_str(v.as_str())) + .transpose() + .map_err(|e| Kind::IdentifierError.context(e))?; + Ok(Counterparty::new( + value + .port_id + .parse() + .map_err(|e| Kind::IdentifierError.context(e))?, + channel_id, + )) + } +} + +impl From for RawCounterparty { + fn from(value: Counterparty) -> Self { + RawCounterparty { + port_id: value.port_id.as_str().to_string(), + channel_id: value + .channel_id + .map_or_else(|| "".to_string(), |v| v.as_str().to_string()), + } + } +} + +#[derive(Clone, Copy, Debug, PartialEq)] pub enum Order { None = 0, Unordered, Ordered, } +impl Default for Order { + fn default() -> Self { + Order::Unordered + } +} + impl Order { /// Yields the Order as a string pub fn as_string(&self) -> &'static str { diff --git a/modules/src/ics04_channel/msgs/acknowledgement.rs b/modules/src/ics04_channel/msgs/acknowledgement.rs index 8fad1d04d2..c97a12366e 100644 --- a/modules/src/ics04_channel/msgs/acknowledgement.rs +++ b/modules/src/ics04_channel/msgs/acknowledgement.rs @@ -6,7 +6,7 @@ use crate::{proofs::Proofs, tx_msg::Msg, Height}; use ibc_proto::ibc::core::channel::v1::MsgAcknowledgement as RawMsgAcknowledgement; use tendermint::account::Id as AccountId; -use tendermint_proto::DomainType; +use tendermint_proto::Protobuf; use std::convert::TryFrom; @@ -70,7 +70,7 @@ impl Msg for MsgAcknowledgement { } } -impl DomainType for MsgAcknowledgement {} +impl Protobuf for MsgAcknowledgement {} #[allow(unreachable_code)] impl TryFrom for MsgAcknowledgement { diff --git a/modules/src/ics04_channel/msgs/chan_close_confirm.rs b/modules/src/ics04_channel/msgs/chan_close_confirm.rs index f1021a654a..8b72a1d09c 100644 --- a/modules/src/ics04_channel/msgs/chan_close_confirm.rs +++ b/modules/src/ics04_channel/msgs/chan_close_confirm.rs @@ -6,7 +6,7 @@ use crate::{proofs::Proofs, tx_msg::Msg, Height}; use ibc_proto::ibc::core::channel::v1::MsgChannelCloseConfirm as RawMsgChannelCloseConfirm; use tendermint::account::Id as AccountId; -use tendermint_proto::DomainType; +use tendermint_proto::Protobuf; use std::convert::{TryFrom, TryInto}; @@ -71,7 +71,7 @@ impl Msg for MsgChannelCloseConfirm { } } -impl DomainType for MsgChannelCloseConfirm {} +impl Protobuf for MsgChannelCloseConfirm {} impl TryFrom for MsgChannelCloseConfirm { type Error = anomaly::Error; diff --git a/modules/src/ics04_channel/msgs/chan_close_init.rs b/modules/src/ics04_channel/msgs/chan_close_init.rs index b4d4bbf77f..7e5d70585c 100644 --- a/modules/src/ics04_channel/msgs/chan_close_init.rs +++ b/modules/src/ics04_channel/msgs/chan_close_init.rs @@ -5,7 +5,7 @@ use crate::tx_msg::Msg; use ibc_proto::ibc::core::channel::v1::MsgChannelCloseInit as RawMsgChannelCloseInit; use tendermint::account::Id as AccountId; -use tendermint_proto::DomainType; +use tendermint_proto::Protobuf; use std::convert::TryFrom; @@ -64,7 +64,7 @@ impl Msg for MsgChannelCloseInit { } } -impl DomainType for MsgChannelCloseInit {} +impl Protobuf for MsgChannelCloseInit {} impl TryFrom for MsgChannelCloseInit { type Error = anomaly::Error; diff --git a/modules/src/ics04_channel/msgs/chan_open_ack.rs b/modules/src/ics04_channel/msgs/chan_open_ack.rs index 501294db63..2e1eb879dd 100644 --- a/modules/src/ics04_channel/msgs/chan_open_ack.rs +++ b/modules/src/ics04_channel/msgs/chan_open_ack.rs @@ -7,7 +7,7 @@ use crate::{proofs::Proofs, tx_msg::Msg, Height}; use ibc_proto::ibc::core::channel::v1::MsgChannelOpenAck as RawMsgChannelOpenAck; use tendermint::account::Id as AccountId; -use tendermint_proto::DomainType; +use tendermint_proto::Protobuf; use std::convert::{TryFrom, TryInto}; @@ -19,12 +19,12 @@ const TYPE_MSG_CHANNEL_OPEN_ACK: &str = "channel_open_ack"; /// #[derive(Clone, Debug, PartialEq)] pub struct MsgChannelOpenAck { - port_id: PortId, - channel_id: ChannelId, - counterparty_channel_id: ChannelId, - counterparty_version: String, - proofs: Proofs, - signer: AccountId, + pub port_id: PortId, + pub channel_id: ChannelId, + pub counterparty_channel_id: ChannelId, + pub counterparty_version: String, + pub proofs: Proofs, + pub signer: AccountId, } impl MsgChannelOpenAck { @@ -71,13 +71,16 @@ impl Msg for MsgChannelOpenAck { // All the validation is performed on creation Ok(()) } + fn type_url(&self) -> String { + "/ibc.core.channel.v1.MsgChannelOpenAck".to_string() + } fn get_signers(&self) -> Vec { vec![self.signer] } } -impl DomainType for MsgChannelOpenAck {} +impl Protobuf for MsgChannelOpenAck {} impl TryFrom for MsgChannelOpenAck { type Error = anomaly::Error; diff --git a/modules/src/ics04_channel/msgs/chan_open_confirm.rs b/modules/src/ics04_channel/msgs/chan_open_confirm.rs index c5d35ec14b..841e4438fe 100644 --- a/modules/src/ics04_channel/msgs/chan_open_confirm.rs +++ b/modules/src/ics04_channel/msgs/chan_open_confirm.rs @@ -6,7 +6,7 @@ use crate::{proofs::Proofs, tx_msg::Msg, Height}; use ibc_proto::ibc::core::channel::v1::MsgChannelOpenConfirm as RawMsgChannelOpenConfirm; use tendermint::account::Id as AccountId; -use tendermint_proto::DomainType; +use tendermint_proto::Protobuf; use std::convert::{TryFrom, TryInto}; @@ -19,10 +19,10 @@ const TYPE_MSG_CHANNEL_OPEN_CONFIRM: &str = "channel_open_confirm"; /// #[derive(Clone, Debug, PartialEq)] pub struct MsgChannelOpenConfirm { - port_id: PortId, - channel_id: ChannelId, - proofs: Proofs, - signer: AccountId, + pub port_id: PortId, + pub channel_id: ChannelId, + pub proofs: Proofs, + pub signer: AccountId, } impl MsgChannelOpenConfirm { @@ -66,12 +66,16 @@ impl Msg for MsgChannelOpenConfirm { Ok(()) } + fn type_url(&self) -> String { + "/ibc.core.channel.v1.MsgChannelOpenConfirm".to_string() + } + fn get_signers(&self) -> Vec { vec![self.signer] } } -impl DomainType for MsgChannelOpenConfirm {} +impl Protobuf for MsgChannelOpenConfirm {} impl TryFrom for MsgChannelOpenConfirm { type Error = anomaly::Error; diff --git a/modules/src/ics04_channel/msgs/chan_open_init.rs b/modules/src/ics04_channel/msgs/chan_open_init.rs index 13aadc2c11..072490091f 100644 --- a/modules/src/ics04_channel/msgs/chan_open_init.rs +++ b/modules/src/ics04_channel/msgs/chan_open_init.rs @@ -1,15 +1,14 @@ use crate::address::{account_to_string, string_to_account}; -use crate::ics04_channel::channel::{ChannelEnd, Counterparty, Order}; +use crate::ics04_channel::channel::ChannelEnd; use crate::ics04_channel::error::{Error, Kind}; -use crate::ics24_host::identifier::{ChannelId, ConnectionId, PortId}; +use crate::ics24_host::identifier::{ChannelId, PortId}; use crate::tx_msg::Msg; use ibc_proto::ibc::core::channel::v1::MsgChannelOpenInit as RawMsgChannelOpenInit; use tendermint::account::Id as AccountId; -use tendermint_proto::DomainType; +use tendermint_proto::Protobuf; use std::convert::{TryFrom, TryInto}; -use std::str::FromStr; /// Message type for the `MsgChannelOpenInit` message. const TYPE_MSG_CHANNEL_OPEN_INIT: &str = "channel_open_init"; @@ -19,49 +18,10 @@ const TYPE_MSG_CHANNEL_OPEN_INIT: &str = "channel_open_init"; /// #[derive(Clone, Debug, PartialEq)] pub struct MsgChannelOpenInit { - port_id: PortId, - channel_id: ChannelId, - channel: ChannelEnd, - signer: AccountId, -} - -impl MsgChannelOpenInit { - // TODO: Constructors for domain types are in flux. - // Relayer will need this constructor. Validation here should be identical to `try_from` method. - // https://github.com/informalsystems/ibc-rs/issues/219 - #[allow(dead_code, clippy::too_many_arguments)] - fn new( - port_id: String, - channel_id: String, - version: String, - order: i32, - connection_hops: Vec, - counterparty_port_id: String, - counterparty_channel_id: String, - signer: AccountId, - ) -> Result { - let connection_hops: Result, _> = connection_hops - .into_iter() - .map(|s| ConnectionId::from_str(s.as_str())) - .collect(); - - Ok(Self { - port_id: port_id - .parse() - .map_err(|e| Kind::IdentifierError.context(e))?, - channel_id: channel_id - .parse() - .map_err(|e| Kind::IdentifierError.context(e))?, - channel: ChannelEnd::new( - Order::from_i32(order)?, - Counterparty::new(counterparty_port_id, counterparty_channel_id) - .map_err(|e| Kind::IdentifierError.context(e))?, - connection_hops.map_err(|e| Kind::IdentifierError.context(e))?, - version, - ), - signer, - }) - } + pub port_id: PortId, + pub channel_id: ChannelId, + pub channel: ChannelEnd, + pub signer: AccountId, } impl Msg for MsgChannelOpenInit { @@ -79,12 +39,16 @@ impl Msg for MsgChannelOpenInit { self.channel.validate_basic() } + fn type_url(&self) -> String { + "/ibc.core.channel.v1.MsgChannelOpenInit".to_string() + } + fn get_signers(&self) -> Vec { vec![self.signer] } } -impl DomainType for MsgChannelOpenInit {} +impl Protobuf for MsgChannelOpenInit {} impl TryFrom for MsgChannelOpenInit { type Error = anomaly::Error; diff --git a/modules/src/ics04_channel/msgs/chan_open_try.rs b/modules/src/ics04_channel/msgs/chan_open_try.rs index d22e4775d0..e091ad0327 100644 --- a/modules/src/ics04_channel/msgs/chan_open_try.rs +++ b/modules/src/ics04_channel/msgs/chan_open_try.rs @@ -1,13 +1,12 @@ use crate::address::{account_to_string, string_to_account}; -use crate::ics04_channel::channel::{validate_version, ChannelEnd, Counterparty, Order}; +use crate::ics04_channel::channel::{validate_version, ChannelEnd}; use crate::ics04_channel::error::{Error, Kind}; -use crate::ics23_commitment::commitment::CommitmentProof; -use crate::ics24_host::identifier::{ChannelId, ConnectionId, PortId}; -use crate::{proofs::Proofs, tx_msg::Msg, Height}; +use crate::ics24_host::identifier::{ChannelId, PortId}; +use crate::{proofs::Proofs, tx_msg::Msg}; use ibc_proto::ibc::core::channel::v1::MsgChannelOpenTry as RawMsgChannelOpenTry; use tendermint::account::Id as AccountId; -use tendermint_proto::DomainType; +use tendermint_proto::Protobuf; use std::convert::{TryFrom, TryInto}; use std::str::FromStr; @@ -20,61 +19,13 @@ const TYPE_MSG_CHANNEL_OPEN_TRY: &str = "channel_open_try"; /// #[derive(Clone, Debug, PartialEq)] pub struct MsgChannelOpenTry { - port_id: PortId, - channel_id: ChannelId, // Labeled `desired_channel_id` in raw types. - counterparty_chosen_channel_id: Option, - channel: ChannelEnd, - counterparty_version: String, - proofs: Proofs, - signer: AccountId, -} - -impl MsgChannelOpenTry { - #[allow(dead_code, clippy::too_many_arguments)] - // TODO: Not used (yet). Also missing `counterparty_chosen_channel_id` value. - fn new( - port_id: String, - channel_id: String, - channel_version: String, - order: i32, - connection_hops: Vec, - counterparty_port_id: String, - counterparty_channel_id: String, - counterparty_version: String, - proof_init: CommitmentProof, - proofs_height: Height, - signer: AccountId, - ) -> Result { - let connection_hops: Result, _> = connection_hops - .into_iter() - .map(|s| ConnectionId::from_str(s.as_str())) - .collect(); - - let version = - validate_version(channel_version).map_err(|e| Kind::InvalidVersion.context(e))?; - - Ok(Self { - port_id: port_id - .parse() - .map_err(|e| Kind::IdentifierError.context(e))?, - channel_id: channel_id - .parse() - .map_err(|e| Kind::IdentifierError.context(e))?, - counterparty_chosen_channel_id: None, - channel: ChannelEnd::new( - Order::from_i32(order)?, - Counterparty::new(counterparty_port_id, counterparty_channel_id) - .map_err(|e| Kind::IdentifierError.context(e))?, - connection_hops.map_err(|e| Kind::IdentifierError.context(e))?, - version, - ), - counterparty_version: validate_version(counterparty_version) - .map_err(|e| Kind::InvalidVersion.context(e))?, - proofs: Proofs::new(proof_init, None, None, proofs_height) - .map_err(|e| Kind::InvalidProof.context(e))?, - signer, - }) - } + pub port_id: PortId, + pub channel_id: ChannelId, // Labeled `desired_channel_id` in raw types. + pub counterparty_chosen_channel_id: Option, + pub channel: ChannelEnd, + pub counterparty_version: String, + pub proofs: Proofs, + pub signer: AccountId, } impl Msg for MsgChannelOpenTry { @@ -92,12 +43,16 @@ impl Msg for MsgChannelOpenTry { self.channel.validate_basic() } + fn type_url(&self) -> String { + "/ibc.core.channel.v1.MsgChannelOpenTry".to_string() + } + fn get_signers(&self) -> Vec { vec![self.signer] } } -impl DomainType for MsgChannelOpenTry {} +impl Protobuf for MsgChannelOpenTry {} impl TryFrom for MsgChannelOpenTry { type Error = anomaly::Error; diff --git a/modules/src/ics04_channel/msgs/recv_packet.rs b/modules/src/ics04_channel/msgs/recv_packet.rs index 57515976be..f7e40a94f8 100644 --- a/modules/src/ics04_channel/msgs/recv_packet.rs +++ b/modules/src/ics04_channel/msgs/recv_packet.rs @@ -7,7 +7,7 @@ use crate::{proofs::Proofs, tx_msg::Msg, Height}; use ibc_proto::ibc::core::channel::v1::MsgRecvPacket as RawMsgRecvPacket; use std::convert::{TryFrom, TryInto}; use tendermint::account::Id as AccountId; -use tendermint_proto::DomainType; +use tendermint_proto::Protobuf; /// Message type for `MsgPacket`. const TYPE_MSG_PACKET: &str = "ics04/opaque"; @@ -69,7 +69,7 @@ impl Msg for MsgRecvPacket { } } -impl DomainType for MsgRecvPacket {} +impl Protobuf for MsgRecvPacket {} impl TryFrom for MsgRecvPacket { type Error = anomaly::Error; diff --git a/modules/src/ics04_channel/msgs/timeout.rs b/modules/src/ics04_channel/msgs/timeout.rs index 9e4a237d2e..e9d156b7d4 100644 --- a/modules/src/ics04_channel/msgs/timeout.rs +++ b/modules/src/ics04_channel/msgs/timeout.rs @@ -6,7 +6,7 @@ use crate::{proofs::Proofs, tx_msg::Msg, Height}; use ibc_proto::ibc::core::channel::v1::MsgTimeout as RawMsgTimeout; use tendermint::account::Id as AccountId; -use tendermint_proto::DomainType; +use tendermint_proto::Protobuf; use std::convert::TryFrom; @@ -66,7 +66,7 @@ impl Msg for MsgTimeout { } } -impl DomainType for MsgTimeout {} +impl Protobuf for MsgTimeout {} #[allow(unreachable_code, unused_variables)] impl TryFrom for MsgTimeout { diff --git a/modules/src/ics07_tendermint/client_state.rs b/modules/src/ics07_tendermint/client_state.rs index a4779e04af..7c8cf62c7d 100644 --- a/modules/src/ics07_tendermint/client_state.rs +++ b/modules/src/ics07_tendermint/client_state.rs @@ -4,7 +4,7 @@ use std::time::Duration; use ibc_proto::ibc::lightclients::tendermint::v1::{ClientState as RawClientState, Fraction}; use tendermint::consensus::Params; use tendermint_light_client::types::TrustThreshold; -use tendermint_proto::DomainType; +use tendermint_proto::Protobuf; use crate::Height; @@ -28,7 +28,7 @@ pub struct ClientState { pub allow_update_after_misbehaviour: bool, } -impl DomainType for ClientState {} +impl Protobuf for ClientState {} impl ClientState { #[allow(clippy::too_many_arguments)] diff --git a/modules/src/ics07_tendermint/consensus_state.rs b/modules/src/ics07_tendermint/consensus_state.rs index e46f88713a..2489b02935 100644 --- a/modules/src/ics07_tendermint/consensus_state.rs +++ b/modules/src/ics07_tendermint/consensus_state.rs @@ -5,7 +5,7 @@ use ibc_proto::ibc::lightclients::tendermint::v1::ConsensusState as RawConsensus use tendermint::time::Time; use tendermint::Hash; -use tendermint_proto::DomainType; +use tendermint_proto::Protobuf; use crate::ics02_client::client_type::ClientType; use crate::ics07_tendermint::error::{Error, Kind}; @@ -43,7 +43,7 @@ impl crate::ics02_client::state::ConsensusState for ConsensusState { } } -impl DomainType for ConsensusState {} +impl Protobuf for ConsensusState {} impl TryFrom for ConsensusState { type Error = Error; diff --git a/modules/src/ics07_tendermint/header.rs b/modules/src/ics07_tendermint/header.rs index 6f29343dd2..0f34afa4d5 100644 --- a/modules/src/ics07_tendermint/header.rs +++ b/modules/src/ics07_tendermint/header.rs @@ -2,7 +2,7 @@ use std::convert::{TryFrom, TryInto}; use tendermint::block::signed_header::SignedHeader; use tendermint::validator::Set as ValidatorSet; -use tendermint_proto::DomainType; +use tendermint_proto::Protobuf; use ibc_proto::ibc::lightclients::tendermint::v1::Header as RawHeader; @@ -33,7 +33,7 @@ impl crate::ics02_client::header::Header for Header { } } -impl DomainType for Header {} +impl Protobuf for Header {} impl TryFrom for Header { type Error = Error; diff --git a/modules/src/ics24_host/identifier.rs b/modules/src/ics24_host/identifier.rs index efe899c0d8..dc7c354c9f 100644 --- a/modules/src/ics24_host/identifier.rs +++ b/modules/src/ics24_host/identifier.rs @@ -103,7 +103,7 @@ impl FromStr for ClientId { impl Default for ClientId { fn default() -> Self { - "defaultclientid".to_string().parse().unwrap() + "defaultClient".to_string().parse().unwrap() } } @@ -153,7 +153,7 @@ impl FromStr for ConnectionId { impl Default for ConnectionId { fn default() -> Self { - "defaultconnectionid".to_string().parse().unwrap() + "defaultConnection".to_string().parse().unwrap() } } /// Equality check against string literal (satisfies &ConnectionId == &str). @@ -200,6 +200,12 @@ impl FromStr for PortId { } } +impl Default for PortId { + fn default() -> Self { + "defaultPort".to_string().parse().unwrap() + } +} + #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)] pub struct ChannelId(String); @@ -229,3 +235,9 @@ impl FromStr for ChannelId { validate_channel_identifier(s).map(|_| Self(s.to_string())) } } + +impl Default for ChannelId { + fn default() -> Self { + "defaultChannel".to_string().parse().unwrap() + } +} diff --git a/modules/src/mock_client/header.rs b/modules/src/mock_client/header.rs index a1d930fa7c..e21c3daee3 100644 --- a/modules/src/mock_client/header.rs +++ b/modules/src/mock_client/header.rs @@ -1,7 +1,7 @@ use std::convert::{TryFrom, TryInto}; use ibc_proto::ibc::mock::Header as RawMockHeader; -use tendermint_proto::DomainType; +use tendermint_proto::Protobuf; use crate::ics02_client::client_def::{AnyConsensusState, AnyHeader}; use crate::ics02_client::client_type::ClientType; @@ -13,7 +13,7 @@ use crate::Height; #[derive(Copy, Clone, Default, Debug, PartialEq, Eq)] pub struct MockHeader(pub Height); -impl DomainType for MockHeader {} +impl Protobuf for MockHeader {} impl TryFrom for MockHeader { type Error = Error; diff --git a/modules/src/mock_client/state.rs b/modules/src/mock_client/state.rs index 3cddc8a9e7..139d2e5ea3 100644 --- a/modules/src/mock_client/state.rs +++ b/modules/src/mock_client/state.rs @@ -2,7 +2,7 @@ use std::collections::HashMap; use std::convert::{TryFrom, TryInto}; -use tendermint_proto::DomainType; +use tendermint_proto::Protobuf; use ibc_proto::ibc::mock::ClientState as RawMockClientState; use ibc_proto::ibc::mock::ConsensusState as RawMockConsensusState; @@ -35,7 +35,7 @@ pub struct MockClientRecord { #[derive(Copy, Clone, Default, Debug, PartialEq, Eq)] pub struct MockClientState(pub MockHeader); -impl DomainType for MockClientState {} +impl Protobuf for MockClientState {} impl MockClientState { pub fn latest_height(&self) -> Height { @@ -115,7 +115,7 @@ impl From for MockClientState { #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub struct MockConsensusState(pub MockHeader); -impl DomainType for MockConsensusState {} +impl Protobuf for MockConsensusState {} impl TryFrom for MockConsensusState { type Error = Error; diff --git a/proto/Cargo.toml b/proto/Cargo.toml index 30959a8be6..bc7cb07746 100644 --- a/proto/Cargo.toml +++ b/proto/Cargo.toml @@ -26,6 +26,4 @@ thiserror = "1.0" tonic = "0.3.1" [dependencies.tendermint-proto] -version = "0.17.0-rc2" - - +version = "=0.17.0-rc3" diff --git a/relayer-cli/Cargo.toml b/relayer-cli/Cargo.toml index 546b8e981a..72abc0b171 100644 --- a/relayer-cli/Cargo.toml +++ b/relayer-cli/Cargo.toml @@ -26,23 +26,20 @@ prost-types = { version = "0.6.1" } hex = "0.4" [dependencies.tendermint-proto] -version = "0.17.0-rc2" +version = "=0.17.0-rc3" [dependencies.tendermint] -version = "0.17.0-rc2" +version = "=0.17.0-rc3" [dependencies.tendermint-rpc] -version = "0.17.0-rc2" +version = "=0.17.0-rc3" features = ["http-client", "websocket-client"] [dependencies.tendermint-light-client] -version = "0.17.0-rc2" +version = "=0.17.0-rc3" [dependencies.abscissa_core] version = "0.5.2" -# optional: use `gimli` to capture backtraces -# see https://github.com/rust-lang/backtrace-rs/issues/189 -# features = ["gimli-backtrace"] [dev-dependencies] abscissa_core = { version = "0.5.2", features = ["testing"] } diff --git a/relayer-cli/src/commands/query/channel.rs b/relayer-cli/src/commands/query/channel.rs index aee3d1ca09..ece521bbb0 100644 --- a/relayer-cli/src/commands/query/channel.rs +++ b/relayer-cli/src/commands/query/channel.rs @@ -12,7 +12,7 @@ use crate::error::{Error, Kind}; use ibc::ics24_host::error::ValidationError; use relayer::chain::{Chain, CosmosSDKChain}; use tendermint::chain::Id as ChainId; -use tendermint_proto::DomainType; +use tendermint_proto::Protobuf; #[derive(Clone, Command, Debug, Options)] pub struct QueryChannelEndCmd { diff --git a/relayer-cli/src/commands/query/client.rs b/relayer-cli/src/commands/query/client.rs index 20ec1dc53d..6f7223d162 100644 --- a/relayer-cli/src/commands/query/client.rs +++ b/relayer-cli/src/commands/query/client.rs @@ -12,7 +12,7 @@ use ibc::ics24_host::Path::{ClientConnections, ClientConsensusState, ClientState use relayer::chain::Chain; use relayer::chain::CosmosSDKChain; use tendermint::chain::Id as ChainId; -use tendermint_proto::DomainType; +use tendermint_proto::Protobuf; use std::convert::TryInto; diff --git a/relayer-cli/src/commands/tx.rs b/relayer-cli/src/commands/tx.rs index 376ddd687e..8fbafc40c8 100644 --- a/relayer-cli/src/commands/tx.rs +++ b/relayer-cli/src/commands/tx.rs @@ -3,6 +3,7 @@ use abscissa_core::{Command, Help, Options, Runnable}; use crate::commands::tx::client::{TxCreateClientCmd, TxUpdateClientCmd}; +mod channel; mod client; mod connection; @@ -47,4 +48,20 @@ pub enum TxRawCommands { /// The `tx raw conn-confirm` subcommand #[options(help = "tx raw conn-confirm")] ConnConfirm(connection::TxRawConnConfirmCmd), + + /// The `tx raw chan-init` subcommand + #[options(help = "tx raw chan-init")] + ChanInit(channel::TxRawChanInitCmd), + + /// The `tx raw chan-try` subcommand + #[options(help = "tx raw chan-try")] + ChanTry(channel::TxRawChanTryCmd), + + /// The `tx raw chan-ack` subcommand + #[options(help = "tx raw chan-ack")] + ChanAck(channel::TxRawChanAckCmd), + + /// The `tx raw chan-confirm` subcommand + #[options(help = "tx raw chan-confirm")] + ChanConfirm(channel::TxRawChanConfirmCmd), } diff --git a/relayer-cli/src/commands/tx/channel.rs b/relayer-cli/src/commands/tx/channel.rs new file mode 100644 index 0000000000..ae43bcecec --- /dev/null +++ b/relayer-cli/src/commands/tx/channel.rs @@ -0,0 +1,389 @@ +use crate::prelude::*; + +use abscissa_core::{Command, Options, Runnable}; +use ibc::ics04_channel::channel::Order; +use ibc::ics24_host::identifier::{ChannelId, ConnectionId, PortId}; + +use relayer::config::Config; + +use crate::error::{Error, Kind}; +use relayer::tx::channel::{ + build_chan_ack_and_send, build_chan_confirm_and_send, build_chan_init_and_send, + build_chan_try_and_send, ChannelOpenInitOptions, ChannelOpenOptions, +}; + +#[derive(Clone, Command, Debug, Options)] +pub struct TxRawChanInitCmd { + #[options(free, help = "identifier of the destination chain")] + dest_chain_id: String, + + #[options(free, help = "identifier of the source chain")] + src_chain_id: String, + + #[options(free, help = "identifier of the destination connection")] + dest_connection_id: ConnectionId, + + #[options(free, help = "identifier of the destination port")] + dest_port_id: PortId, + + #[options(free, help = "identifier of the source port")] + src_port_id: PortId, + + #[options(free, help = "identifier of the destination channel")] + dest_channel_id: ChannelId, + + #[options(help = "identifier of the source channel", short = "s")] + src_channel_id: Option, + + #[options(help = "the channel order", short = "o")] + ordering: Order, + + #[options( + help = "json key file for the signer, must include mnemonic", + short = "k" + )] + seed_file: String, +} + +impl TxRawChanInitCmd { + fn validate_options(&self, config: &Config) -> Result { + let dest_chain_config = config + .chains + .iter() + .find(|c| c.id == self.dest_chain_id.parse().unwrap()) + .ok_or_else(|| "missing destination chain configuration".to_string())?; + + let src_chain_config = config + .chains + .iter() + .find(|c| c.id == self.src_chain_id.parse().unwrap()) + .ok_or_else(|| "missing src chain configuration".to_string())?; + + let signer_seed = std::fs::read_to_string(&self.seed_file).map_err(|e| { + anomaly::Context::new("invalid signer seed file", Some(e.into())).to_string() + })?; + + let opts = ChannelOpenInitOptions { + dest_chain_config: dest_chain_config.clone(), + src_chain_config: src_chain_config.clone(), + + dest_connection_id: self.dest_connection_id.clone(), + + dest_port_id: self.dest_port_id.clone(), + src_port_id: self.src_port_id.clone(), + + dest_channel_id: self.dest_channel_id.clone(), + src_channel_id: self.src_channel_id.clone(), + + ordering: self.ordering, + signer_seed, + }; + + Ok(opts) + } +} + +impl Runnable for TxRawChanInitCmd { + fn run(&self) { + let config = app_config(); + + let opts = match self.validate_options(&config) { + Err(err) => { + status_err!("invalid options: {}", err); + return; + } + Ok(result) => result, + }; + status_info!("Message", "{:?}", opts); + + let res: Result = + build_chan_init_and_send(&opts).map_err(|e| Kind::Tx.context(e).into()); + + match res { + Ok(receipt) => status_info!("channel init, result: ", "{:?}", receipt), + Err(e) => status_info!("channel init failed, error: ", "{}", e), + } + } +} + +#[derive(Clone, Command, Debug, Options)] +pub struct TxRawChanTryCmd { + #[options(free, help = "identifier of the destination chain")] + dest_chain_id: String, + + #[options(free, help = "identifier of the source chain")] + src_chain_id: String, + + #[options(free, help = "identifier of the destination connection")] + dest_connection_id: ConnectionId, + + #[options(free, help = "identifier of the destination port")] + dest_port_id: PortId, + + #[options(free, help = "identifier of the source port")] + src_port_id: PortId, + + #[options(free, help = "identifier of the destination channel")] + dest_channel_id: ChannelId, + + #[options(free, help = "identifier of the source channel")] + src_channel_id: ChannelId, + + #[options(help = "the channel order", short = "o")] + ordering: Order, + + #[options( + help = "json key file for the signer, must include mnemonic", + short = "k" + )] + seed_file: String, +} + +impl TxRawChanTryCmd { + fn validate_options(&self, config: &Config) -> Result { + let dest_chain_config = config + .chains + .iter() + .find(|c| c.id == self.dest_chain_id.parse().unwrap()) + .ok_or_else(|| "missing destination chain configuration".to_string())?; + + let src_chain_config = config + .chains + .iter() + .find(|c| c.id == self.src_chain_id.parse().unwrap()) + .ok_or_else(|| "missing src chain configuration".to_string())?; + + let signer_seed = std::fs::read_to_string(&self.seed_file).map_err(|e| { + anomaly::Context::new("invalid signer seed file", Some(e.into())).to_string() + })?; + + let opts = ChannelOpenOptions { + dest_chain_config: dest_chain_config.clone(), + src_chain_config: src_chain_config.clone(), + + dest_connection_id: self.dest_connection_id.clone(), + + dest_port_id: self.dest_port_id.clone(), + src_port_id: self.src_port_id.clone(), + + dest_channel_id: self.dest_channel_id.clone(), + src_channel_id: self.src_channel_id.clone(), + + ordering: self.ordering, + signer_seed, + }; + + Ok(opts) + } +} + +impl Runnable for TxRawChanTryCmd { + fn run(&self) { + let config = app_config(); + + let opts = match self.validate_options(&config) { + Err(err) => { + status_err!("invalid options: {}", err); + return; + } + Ok(result) => result, + }; + status_info!("Message", "{:?}", opts); + + let res: Result = + build_chan_try_and_send(&opts).map_err(|e| Kind::Tx.context(e).into()); + + match res { + Ok(receipt) => status_info!("channel try, result: ", "{:?}", receipt), + Err(e) => status_info!("channel try failed, error: ", "{}", e), + } + } +} + +#[derive(Clone, Command, Debug, Options)] +pub struct TxRawChanAckCmd { + #[options(free, help = "identifier of the destination chain")] + dest_chain_id: String, + + #[options(free, help = "identifier of the source chain")] + src_chain_id: String, + + #[options(free, help = "identifier of the destination connection")] + dest_connection_id: ConnectionId, + + #[options(free, help = "identifier of the destination port")] + dest_port_id: PortId, + + #[options(free, help = "identifier of the source port")] + src_port_id: PortId, + + #[options(free, help = "identifier of the destination channel")] + dest_channel_id: ChannelId, + + #[options(free, help = "identifier of the source channel")] + src_channel_id: ChannelId, + + #[options(help = "the channel order", short = "o")] + ordering: Order, + + #[options( + help = "json key file for the signer, must include mnemonic", + short = "k" + )] + seed_file: String, +} + +impl TxRawChanAckCmd { + fn validate_options(&self, config: &Config) -> Result { + let dest_chain_config = config + .chains + .iter() + .find(|c| c.id == self.dest_chain_id.parse().unwrap()) + .ok_or_else(|| "missing destination chain configuration".to_string())?; + + let src_chain_config = config + .chains + .iter() + .find(|c| c.id == self.src_chain_id.parse().unwrap()) + .ok_or_else(|| "missing src chain configuration".to_string())?; + + let signer_seed = std::fs::read_to_string(&self.seed_file).map_err(|e| { + anomaly::Context::new("invalid signer seed file", Some(e.into())).to_string() + })?; + + let opts = ChannelOpenOptions { + dest_chain_config: dest_chain_config.clone(), + src_chain_config: src_chain_config.clone(), + + dest_connection_id: self.dest_connection_id.clone(), + + dest_port_id: self.dest_port_id.clone(), + src_port_id: self.src_port_id.clone(), + + dest_channel_id: self.dest_channel_id.clone(), + src_channel_id: self.src_channel_id.clone(), + + ordering: self.ordering, + signer_seed, + }; + + Ok(opts) + } +} + +impl Runnable for TxRawChanAckCmd { + fn run(&self) { + let config = app_config(); + + let opts = match self.validate_options(&config) { + Err(err) => { + status_err!("invalid options: {}", err); + return; + } + Ok(result) => result, + }; + status_info!("Message", "{:?}", opts); + + let res: Result = + build_chan_ack_and_send(&opts).map_err(|e| Kind::Tx.context(e).into()); + + match res { + Ok(receipt) => status_info!("channel ack, result: ", "{:?}", receipt), + Err(e) => status_info!("channel ack failed, error: ", "{}", e), + } + } +} + +#[derive(Clone, Command, Debug, Options)] +pub struct TxRawChanConfirmCmd { + #[options(free, help = "identifier of the destination chain")] + dest_chain_id: String, + + #[options(free, help = "identifier of the source chain")] + src_chain_id: String, + + #[options(free, help = "identifier of the destination connection")] + dest_connection_id: ConnectionId, + + #[options(free, help = "identifier of the destination port")] + dest_port_id: PortId, + + #[options(free, help = "identifier of the source port")] + src_port_id: PortId, + + #[options(free, help = "identifier of the destination channel")] + dest_channel_id: ChannelId, + + #[options(free, help = "identifier of the source channel")] + src_channel_id: ChannelId, + + #[options(help = "the channel order", short = "o")] + ordering: Order, + + #[options( + help = "json key file for the signer, must include mnemonic", + short = "k" + )] + seed_file: String, +} + +impl TxRawChanConfirmCmd { + fn validate_options(&self, config: &Config) -> Result { + let dest_chain_config = config + .chains + .iter() + .find(|c| c.id == self.dest_chain_id.parse().unwrap()) + .ok_or_else(|| "missing destination chain configuration".to_string())?; + + let src_chain_config = config + .chains + .iter() + .find(|c| c.id == self.src_chain_id.parse().unwrap()) + .ok_or_else(|| "missing src chain configuration".to_string())?; + + let signer_seed = std::fs::read_to_string(&self.seed_file).map_err(|e| { + anomaly::Context::new("invalid signer seed file", Some(e.into())).to_string() + })?; + + let opts = ChannelOpenOptions { + dest_chain_config: dest_chain_config.clone(), + src_chain_config: src_chain_config.clone(), + + dest_connection_id: self.dest_connection_id.clone(), + + dest_port_id: self.dest_port_id.clone(), + src_port_id: self.src_port_id.clone(), + + dest_channel_id: self.dest_channel_id.clone(), + src_channel_id: self.src_channel_id.clone(), + + ordering: self.ordering, + signer_seed, + }; + + Ok(opts) + } +} + +impl Runnable for TxRawChanConfirmCmd { + fn run(&self) { + let config = app_config(); + + let opts = match self.validate_options(&config) { + Err(err) => { + status_err!("invalid options: {}", err); + return; + } + Ok(result) => result, + }; + status_info!("Message", "{:?}", opts); + + let res: Result = + build_chan_confirm_and_send(&opts).map_err(|e| Kind::Tx.context(e).into()); + + match res { + Ok(receipt) => status_info!("channel confirm, result: ", "{:?}", receipt), + Err(e) => status_info!("channel confirm failed, error: ", "{}", e), + } + } +} diff --git a/relayer-cli/src/commands/tx/connection.rs b/relayer-cli/src/commands/tx/connection.rs index c889960988..414af0baa3 100644 --- a/relayer-cli/src/commands/tx/connection.rs +++ b/relayer-cli/src/commands/tx/connection.rs @@ -29,7 +29,7 @@ pub struct TxRawConnInitCmd { #[options(free, help = "identifier of the destination connection")] dest_connection_id: ConnectionId, - #[options(help = "identifier of the source connection", short = "d")] + #[options(help = "identifier of the source connection", short = "s")] src_connection_id: Option, #[options( diff --git a/relayer-cli/tests/integration.rs b/relayer-cli/tests/integration.rs index b486a29592..0706b6ec30 100644 --- a/relayer-cli/tests/integration.rs +++ b/relayer-cli/tests/integration.rs @@ -18,7 +18,7 @@ use ibc::ics24_host::Path::{ChannelEnds, ClientConnections}; use relayer::chain::{Chain, CosmosSDKChain}; use relayer::config::{default, ChainConfig, Config}; use tendermint::net::Address; -use tendermint_proto::DomainType; +use tendermint_proto::Protobuf; use std::convert::TryInto; use std::str::FromStr; @@ -100,8 +100,11 @@ fn query_channel_id() { assert_eq!(query.state(), &ChannelState::Init); assert_eq!(query.ordering(), &Order::Ordered); - assert_eq!(query.counterparty().port_id(), "secondport"); - assert_eq!(query.counterparty().channel_id(), "secondchannel"); + assert_eq!(query.counterparty().port_id().as_str(), "secondport"); + assert_eq!( + query.counterparty().channel_id().unwrap().as_str(), + "secondchannel" + ); assert_eq!(query.connection_hops()[0].as_str(), "connectionidatob"); assert_eq!(query.version(), "1.0"); } diff --git a/relayer/Cargo.toml b/relayer/Cargo.toml index 71475cfa94..b060c44432 100644 --- a/relayer/Cargo.toml +++ b/relayer/Cargo.toml @@ -37,16 +37,16 @@ bech32 = "0.7.2" tonic = "0.3.1" [dependencies.tendermint] -version = "0.17.0-rc2" +version = "=0.17.0-rc3" [dependencies.tendermint-rpc] -version = "0.17.0-rc2" +version = "=0.17.0-rc3" features = ["http-client", "websocket-client"] [dependencies.tendermint-light-client] -version = "0.17.0-rc2" +version = "=0.17.0-rc3" [dependencies.tendermint-proto] -version = "0.17.0-rc2" +version = "=0.17.0-rc3" [dev-dependencies] diff --git a/relayer/src/chain.rs b/relayer/src/chain.rs index 226186c98a..cc91a19cb5 100644 --- a/relayer/src/chain.rs +++ b/relayer/src/chain.rs @@ -5,7 +5,7 @@ use anomaly::fail; use prost_types::Any; use serde::{de::DeserializeOwned, Serialize}; -use tendermint_proto::DomainType; +use tendermint_proto::Protobuf; // TODO - tendermint deps should not be here //use tendermint::account::Id as AccountId; @@ -26,7 +26,7 @@ use ibc::ics03_connection::msgs::conn_open_init::MsgConnectionOpenInit; use ibc::ics03_connection::msgs::conn_open_try::MsgConnectionOpenTry; use ibc::ics03_connection::version::get_compatible_versions; use ibc::ics23_commitment::commitment::{CommitmentPrefix, CommitmentProof}; -use ibc::ics24_host::identifier::{ClientId, ConnectionId}; +use ibc::ics24_host::identifier::{ChannelId, ClientId, ConnectionId, PortId}; use ibc::ics24_host::Path; use ibc::ics24_host::Path::ClientConsensusState as ClientConsensusPath; use ibc::proofs::{ConsensusProof, Proofs}; @@ -40,7 +40,10 @@ use crate::tx::connection::{ConnectionMsgType, ConnectionOpenInitOptions, Connec use crate::util::block_on; pub(crate) mod cosmos; +use crate::tx::channel::ChannelMsgType; pub use cosmos::CosmosSDKChain; +use ibc::ics04_channel::channel::ChannelEnd; + pub mod handle; pub mod runtime; @@ -149,6 +152,24 @@ pub trait Chain { Ok((connection_end, res.proof)) } + fn proven_channel( + &self, + port_id: &PortId, + channel_id: &ChannelId, + height: ICSHeight, + ) -> Result<(ChannelEnd, MerkleProof), Error> { + let res = self + .ics_query( + Path::ChannelEnds(port_id.clone(), channel_id.clone()), + height, + true, + ) + .map_err(|e| Kind::Query.context(e))?; + let channel_end = ChannelEnd::decode_vec(&res.value).map_err(|e| Kind::Query.context(e))?; + + Ok((channel_end, res.proof)) + } + fn proven_client_consensus( &self, client_id: &ClientId, @@ -203,6 +224,22 @@ pub trait Chain { })?) } + fn query_channel( + &self, + port_id: &PortId, + channel_id: &ChannelId, + height: ICSHeight, + ) -> Result { + Ok(self + .ics_query( + Path::ChannelEnds(port_id.clone(), channel_id.clone()), + height, + false, + ) + .map_err(|e| Kind::Query.context(e)) + .and_then(|v| ChannelEnd::decode_vec(&v.value).map_err(|e| Kind::Query.context(e)))?) + } + /// Builds the required proofs and the client state for connection handshake messages. /// The proofs and client state must be obtained from queries at same height with value /// `height - 1` @@ -266,4 +303,33 @@ pub trait Chain { .map_err(|_| Kind::MalformedProof)?, )) } + + fn module_version(&self, port_id: &PortId) -> String { + // TODO - query the chain, currently hardcoded + if port_id.as_str() == "transfer" { + "ics20-1".to_string() + } else { + "".to_string() + } + } + + /// Builds the proof for channel handshake messages. + /// The proof must be obtained from queries at height `height - 1` + fn build_channel_proofs( + &self, + port_id: &PortId, + channel_id: &ChannelId, + height: ICSHeight, + ) -> Result { + // Set the height of the queries at height - 1 + let query_height = height + .decrement() + .map_err(|e| Kind::InvalidHeight.context(e))?; + + // Collect all proofs as required + let channel_proof = + CommitmentProof::from(self.proven_channel(port_id, channel_id, query_height)?.1); + + Ok(Proofs::new(channel_proof, None, None, height).map_err(|_| Kind::MalformedProof)?) + } } diff --git a/relayer/src/chain/cosmos.rs b/relayer/src/chain/cosmos.rs index de13eee614..e5c81d5947 100644 --- a/relayer/src/chain/cosmos.rs +++ b/relayer/src/chain/cosmos.rs @@ -11,7 +11,7 @@ use bitcoin::hashes::hex::ToHex; use k256::ecdsa::{SigningKey, VerifyKey}; use tendermint_proto::crypto::ProofOps; -use tendermint_proto::DomainType; +use tendermint_proto::Protobuf; use tendermint_rpc::endpoint::abci_query::AbciQuery; use tendermint_rpc::endpoint::broadcast; diff --git a/relayer/src/error.rs b/relayer/src/error.rs index c874f508d6..7230139c00 100644 --- a/relayer/src/error.rs +++ b/relayer/src/error.rs @@ -1,7 +1,7 @@ //! This module defines the various errors that be raised in the relayer. use anomaly::{BoxError, Context}; -use ibc::ics24_host::identifier::{ClientId, ConnectionId}; +use ibc::ics24_host::identifier::{ChannelId, ClientId, ConnectionId}; use thiserror::Error; /// An error that can be raised by the relayer. @@ -78,6 +78,22 @@ pub enum Kind { #[error("Failed to build conn open confirm {0}: {1}")] ConnOpenConfirm(ConnectionId, String), + /// Channel open init failure + #[error("Failed to build channel open init {0}: {1}")] + ChanOpenInit(ChannelId, String), + + /// Channel open try failure + #[error("Failed to build channel open try {0}: {1}")] + ChanOpenTry(ChannelId, String), + + /// Channel open ack failure + #[error("Failed to build channel open ack {0}: {1}")] + ChanOpenAck(ChannelId, String), + + /// Channel open confirm failure + #[error("Failed to build channel open confirm {0}: {1}")] + ChanOpenConfirm(ChannelId, String), + /// A message transaction failure #[error("Message transaction failure: {0}")] MessageTransaction(String), diff --git a/relayer/src/event_monitor.rs b/relayer/src/event_monitor.rs index 70d6f4619b..d3c7a70056 100644 --- a/relayer/src/event_monitor.rs +++ b/relayer/src/event_monitor.rs @@ -2,7 +2,6 @@ use ibc::events::IBCEvent; use tendermint::{chain, net, Error as TMError}; use tendermint_rpc::{ query::EventType, query::Query, Subscription, SubscriptionClient, WebSocketClient, - WebSocketClientDriver, }; use tokio::stream::StreamExt; use tokio::sync::mpsc::Sender; diff --git a/relayer/src/tx.rs b/relayer/src/tx.rs index 771d38780b..210a35c8ac 100644 --- a/relayer/src/tx.rs +++ b/relayer/src/tx.rs @@ -1,2 +1,3 @@ +pub mod channel; pub mod client; pub mod connection; diff --git a/relayer/src/tx/channel.rs b/relayer/src/tx/channel.rs new file mode 100644 index 0000000000..1e93dbde5f --- /dev/null +++ b/relayer/src/tx/channel.rs @@ -0,0 +1,449 @@ +use prost_types::Any; + +use ibc_proto::ibc::core::channel::v1::MsgChannelOpenAck as RawMsgChannelOpenAck; +use ibc_proto::ibc::core::channel::v1::MsgChannelOpenConfirm as RawMsgChannelOpenConfirm; +use ibc_proto::ibc::core::channel::v1::MsgChannelOpenInit as RawMsgChannelOpenInit; +use ibc_proto::ibc::core::channel::v1::MsgChannelOpenTry as RawMsgChannelOpenTry; +use ibc_proto::ibc::core::client::v1::MsgUpdateClient as RawMsgUpdateClient; + +use ibc::ics04_channel::channel::{ChannelEnd, Counterparty, Order, State}; +use ibc::ics04_channel::msgs::chan_open_ack::MsgChannelOpenAck; +use ibc::ics04_channel::msgs::chan_open_confirm::MsgChannelOpenConfirm; +use ibc::ics04_channel::msgs::chan_open_init::MsgChannelOpenInit; +use ibc::ics04_channel::msgs::chan_open_try::MsgChannelOpenTry; +use ibc::ics24_host::identifier::{ChannelId, ClientId, ConnectionId, PortId}; +use ibc::tx_msg::Msg; +use ibc::Height as ICSHeight; + +use crate::chain::{Chain, CosmosSDKChain}; +use crate::config::ChainConfig; +use crate::error::{Error, Kind}; +use crate::tx::client::build_update_client; + +/// Enumeration of proof carrying ICS4 message, helper for relayer. +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum ChannelMsgType { + OpenTry, + OpenAck, + OpenConfirm, +} + +#[derive(Clone, Debug)] +pub struct ChannelOpenInitOptions { + pub dest_chain_config: ChainConfig, + pub src_chain_config: ChainConfig, + pub dest_connection_id: ConnectionId, + pub dest_port_id: PortId, + pub src_port_id: PortId, + pub dest_channel_id: ChannelId, + pub src_channel_id: Option, + pub ordering: Order, + pub signer_seed: String, +} + +pub fn build_chan_init( + dest_chain: &mut CosmosSDKChain, + src_chain: &CosmosSDKChain, + opts: &ChannelOpenInitOptions, +) -> Result, Error> { + // Check that the destination chain will accept the message, i.e. it does not have the channel + if dest_chain + .query_channel( + &opts.dest_port_id, + &opts.dest_channel_id, + ICSHeight::default(), + ) + .is_ok() + { + return Err(Kind::ChanOpenInit( + opts.dest_channel_id.clone(), + "channel already exist".into(), + ) + .into()); + } + + // Get the signer from key seed file + let (_, signer) = dest_chain.key_and_signer(&opts.signer_seed)?; + + let counterparty = Counterparty::new(opts.src_port_id.clone(), opts.src_channel_id.clone()); + + let channel = ChannelEnd::new( + State::Init, + opts.ordering, + counterparty, + vec![opts.dest_connection_id.clone()], + dest_chain.module_version(&opts.dest_port_id), + ); + + // Build the domain type message + let new_msg = MsgChannelOpenInit { + port_id: opts.dest_port_id.clone(), + channel_id: opts.dest_channel_id.clone(), + channel, + signer, + }; + + Ok(vec![new_msg.to_any::()]) +} + +pub fn build_chan_init_and_send(opts: &ChannelOpenInitOptions) -> Result { + // Get the source and destination chains. + let src_chain = &CosmosSDKChain::from_config(opts.clone().src_chain_config)?; + let dest_chain = &mut CosmosSDKChain::from_config(opts.clone().dest_chain_config)?; + + let new_msgs = build_chan_init(dest_chain, src_chain, opts)?; + let (key, _) = dest_chain.key_and_signer(&opts.signer_seed)?; + + Ok(dest_chain.send(new_msgs, key, "".to_string(), 0)?) +} + +#[derive(Clone, Debug)] +pub struct ChannelOpenOptions { + pub dest_chain_config: ChainConfig, + pub src_chain_config: ChainConfig, + pub dest_port_id: PortId, + pub src_port_id: PortId, + pub dest_channel_id: ChannelId, + pub src_channel_id: ChannelId, + pub dest_connection_id: ConnectionId, + pub ordering: Order, + pub signer_seed: String, +} + +fn check_destination_channel_state( + channel_id: ChannelId, + existing_channel: ChannelEnd, + expected_channel: ChannelEnd, +) -> Result<(), Error> { + let good_connection_hops = + existing_channel.connection_hops() == expected_channel.connection_hops(); + + let good_state = + existing_channel.state().clone() as u32 <= expected_channel.state().clone() as u32; + + let good_channel_ids = existing_channel.counterparty().channel_id().is_none() + || existing_channel.counterparty().channel_id() + == expected_channel.counterparty().channel_id(); + + // TODO check versions + + if good_state && good_connection_hops && good_channel_ids { + Ok(()) + } else { + Err(Kind::ChanOpenTry( + channel_id, + "channel already exist in an incompatible state".into(), + ) + .into()) + } +} + +/// Retrieves the channel from destination and compares against the expected channel +/// built from the message type (`msg_type`) and options (`opts`). +/// If the expected and the destination channels are compatible, it returns the expected channel +fn validated_expected_channel( + dest_chain: &mut CosmosSDKChain, + src_chain: &CosmosSDKChain, + msg_type: ChannelMsgType, + opts: &ChannelOpenOptions, +) -> Result { + // If there is a channel present on the destination chain, it should look like this: + let counterparty = Counterparty::new( + opts.src_port_id.clone(), + Option::from(opts.src_channel_id.clone()), + ); + + // The highest expected state, depends on the message type: + let highest_state = match msg_type { + ChannelMsgType::OpenTry => State::Init, + ChannelMsgType::OpenAck => State::TryOpen, + ChannelMsgType::OpenConfirm => State::TryOpen, + }; + + let dest_expected_channel = ChannelEnd::new( + highest_state, + opts.ordering, + counterparty, + vec![opts.dest_connection_id.clone()], + dest_chain.module_version(&opts.dest_port_id), + ); + + // Retrieve existing channel if any + let dest_channel = dest_chain.query_channel( + &opts.dest_port_id, + &opts.dest_channel_id, + ICSHeight::default(), + ); + + // Check if a connection is expected to exist on destination chain + if msg_type == ChannelMsgType::OpenTry { + // TODO - check typed Err, or make query_channel return Option + // It is ok if there is no channel for Try Tx + if dest_channel.is_err() { + return Ok(dest_expected_channel); + } + } else { + // A channel must exist on destination chain for Ack and Confirm Tx-es to succeed + if dest_channel.is_err() { + return Err(Kind::ChanOpenTry( + opts.src_channel_id.clone(), + "missing channel on source chain".to_string(), + ) + .into()); + } + } + + check_destination_channel_state( + opts.dest_channel_id.clone(), + dest_channel?, + dest_expected_channel.clone(), + )?; + + Ok(dest_expected_channel) +} + +pub fn build_chan_try( + dest_chain: &mut CosmosSDKChain, + src_chain: &CosmosSDKChain, + opts: &ChannelOpenOptions, +) -> Result, Error> { + // Check that the destination chain will accept the message, i.e. it does not have the channel + let dest_expected_channel = + validated_expected_channel(dest_chain, src_chain, ChannelMsgType::OpenTry, opts).map_err( + |e| { + Kind::ChanOpenTry( + opts.src_channel_id.clone(), + "try options inconsistent with existing channel on destination chain" + .to_string(), + ) + .context(e) + }, + )?; + + let src_channel = src_chain + .query_channel( + &opts.src_port_id, + &opts.src_channel_id, + ICSHeight::default(), + ) + .map_err(|e| { + Kind::ChanOpenTry( + opts.dest_channel_id.clone(), + "channel does not exist on source".into(), + ) + .context(e) + })?; + + // Retrieve the connection + let dest_connection = + dest_chain.query_connection(&opts.dest_connection_id.clone(), ICSHeight::default())?; + + let ics_target_height = src_chain.query_latest_height()?; + + // Build message to update client on destination + let mut msgs = build_update_client( + dest_chain, + src_chain, + dest_connection.client_id().clone(), + ics_target_height, + &opts.signer_seed, + )?; + + let counterparty = + Counterparty::new(opts.src_port_id.clone(), Some(opts.src_channel_id.clone())); + + let channel = ChannelEnd::new( + State::Init, + opts.ordering, + counterparty, + vec![opts.dest_connection_id.clone()], + dest_chain.module_version(&opts.dest_port_id), + ); + + // Build the domain type message + let new_msg = MsgChannelOpenTry { + port_id: opts.dest_port_id.clone(), + channel_id: opts.dest_channel_id.clone(), + counterparty_chosen_channel_id: src_channel.counterparty().channel_id, + channel, + counterparty_version: src_chain.module_version(&opts.src_port_id), + proofs: src_chain.build_channel_proofs( + &opts.src_port_id, + &opts.src_channel_id, + ics_target_height, + )?, + signer: dest_chain.key_and_signer(&opts.signer_seed)?.1, + }; + + let mut new_msgs = vec![new_msg.to_any::()]; + + msgs.append(&mut new_msgs); + + Ok(msgs) +} + +pub fn build_chan_try_and_send(opts: &ChannelOpenOptions) -> Result { + // Get the source and destination chains. + let src_chain = &CosmosSDKChain::from_config(opts.clone().src_chain_config)?; + let dest_chain = &mut CosmosSDKChain::from_config(opts.clone().dest_chain_config)?; + + let new_msgs = build_chan_try(dest_chain, src_chain, opts)?; + let (key, _) = dest_chain.key_and_signer(&opts.signer_seed)?; + + Ok(dest_chain.send(new_msgs, key, "".to_string(), 0)?) +} + +pub fn build_chan_ack( + dest_chain: &mut CosmosSDKChain, + src_chain: &CosmosSDKChain, + opts: &ChannelOpenOptions, +) -> Result, Error> { + // Check that the destination chain will accept the message + let dest_expected_channel = + validated_expected_channel(dest_chain, src_chain, ChannelMsgType::OpenAck, opts).map_err( + |e| { + Kind::ChanOpenAck( + opts.src_channel_id.clone(), + "ack options inconsistent with existing channel on destination chain" + .to_string(), + ) + .context(e) + }, + )?; + + let src_channel = src_chain + .query_channel( + &opts.src_port_id, + &opts.src_channel_id, + ICSHeight::default(), + ) + .map_err(|e| { + Kind::ChanOpenAck( + opts.dest_channel_id.clone(), + "channel does not exist on source".into(), + ) + .context(e) + })?; + + // Retrieve the connection + let dest_connection = + dest_chain.query_connection(&opts.dest_connection_id.clone(), ICSHeight::default())?; + + let ics_target_height = src_chain.query_latest_height()?; + + // Build message to update client on destination + let mut msgs = build_update_client( + dest_chain, + src_chain, + dest_connection.client_id().clone(), + ics_target_height, + &opts.signer_seed, + )?; + + // Build the domain type message + let new_msg = MsgChannelOpenAck { + port_id: opts.dest_port_id.clone(), + channel_id: opts.dest_channel_id.clone(), + counterparty_channel_id: opts.src_channel_id.clone(), + counterparty_version: src_chain.module_version(&opts.dest_port_id), + proofs: src_chain.build_channel_proofs( + &opts.src_port_id, + &opts.src_channel_id, + ics_target_height, + )?, + signer: dest_chain.key_and_signer(&opts.signer_seed)?.1, + }; + + let mut new_msgs = vec![new_msg.to_any::()]; + + msgs.append(&mut new_msgs); + + Ok(msgs) +} + +pub fn build_chan_ack_and_send(opts: &ChannelOpenOptions) -> Result { + // Get the source and destination chains. + let src_chain = &CosmosSDKChain::from_config(opts.clone().src_chain_config)?; + let dest_chain = &mut CosmosSDKChain::from_config(opts.clone().dest_chain_config)?; + + let new_msgs = build_chan_ack(dest_chain, src_chain, opts)?; + let (key, _) = dest_chain.key_and_signer(&opts.signer_seed)?; + + Ok(dest_chain.send(new_msgs, key, "".to_string(), 0)?) +} + +pub fn build_chan_confirm( + dest_chain: &mut CosmosSDKChain, + src_chain: &CosmosSDKChain, + opts: &ChannelOpenOptions, +) -> Result, Error> { + // Check that the destination chain will accept the message + let dest_expected_channel = + validated_expected_channel(dest_chain, src_chain, ChannelMsgType::OpenConfirm, opts) + .map_err(|e| { + Kind::ChanOpenConfirm( + opts.src_channel_id.clone(), + "confirm options inconsistent with existing channel on destination chain" + .to_string(), + ) + .context(e) + })?; + + let src_channel = src_chain + .query_channel( + &opts.src_port_id, + &opts.src_channel_id, + ICSHeight::default(), + ) + .map_err(|e| { + Kind::ChanOpenConfirm( + opts.dest_channel_id.clone(), + "channel does not exist on source".into(), + ) + .context(e) + })?; + + // Retrieve the connection + let dest_connection = + dest_chain.query_connection(&opts.dest_connection_id.clone(), ICSHeight::default())?; + + let ics_target_height = src_chain.query_latest_height()?; + + // Build message to update client on destination + let mut msgs = build_update_client( + dest_chain, + src_chain, + dest_connection.client_id().clone(), + ics_target_height, + &opts.signer_seed, + )?; + + // Build the domain type message + let new_msg = MsgChannelOpenConfirm { + port_id: opts.dest_port_id.clone(), + channel_id: opts.dest_channel_id.clone(), + proofs: src_chain.build_channel_proofs( + &opts.src_port_id, + &opts.src_channel_id, + ics_target_height, + )?, + signer: dest_chain.key_and_signer(&opts.signer_seed)?.1, + }; + + let mut new_msgs = vec![new_msg.to_any::()]; + + msgs.append(&mut new_msgs); + + Ok(msgs) +} + +pub fn build_chan_confirm_and_send(opts: &ChannelOpenOptions) -> Result { + // Get the source and destination chains. + let src_chain = &CosmosSDKChain::from_config(opts.clone().src_chain_config)?; + let dest_chain = &mut CosmosSDKChain::from_config(opts.clone().dest_chain_config)?; + + let new_msgs = build_chan_confirm(dest_chain, src_chain, opts)?; + let (key, _) = dest_chain.key_and_signer(&opts.signer_seed)?; + + Ok(dest_chain.send(new_msgs, key, "".to_string(), 0)?) +} diff --git a/relayer/src/tx/client.rs b/relayer/src/tx/client.rs index e8992cf422..6706924950 100644 --- a/relayer/src/tx/client.rs +++ b/relayer/src/tx/client.rs @@ -7,7 +7,7 @@ use prost_types::Any; use tendermint::account::Id as AccountId; use tendermint_light_client::types::TrustThreshold; -use tendermint_proto::DomainType; +use tendermint_proto::Protobuf; use ibc_proto::ibc::core::client::v1::MsgCreateClient as RawMsgCreateClient; use ibc_proto::ibc::core::client::v1::MsgUpdateClient as RawMsgUpdateClient;