From 3abb5630b40c914a7dc758200076d7828a030254 Mon Sep 17 00:00:00 2001 From: Adi Seredinschi Date: Thu, 12 Nov 2020 11:20:10 +0100 Subject: [PATCH] ICS 4 Domain Types for channel handshakes (#391) * First step towards #315 * Domain type & tests for chan_open_init. * Fix FMT * WIP ChannelOpenTry Domain type & tests * Re-adding ConnectionMsgType (accidentally removed). * Done with OpenTry msg * Finished ChanOpenAck domain type + tests; also fmt fix. * Finished ChanOpenConfirm message * ChanCloseInit done * FMT fix * Removed unnecessary derives. Close Confirm done * Rename packet into RecvPacket. Almost done. * Updated changelog --- CHANGELOG.md | 5 +- modules/src/ics04_channel/channel.rs | 60 ++-- modules/src/ics04_channel/error.rs | 9 + modules/src/ics04_channel/msgs.rs | 2 +- .../src/ics04_channel/msgs/acknowledgement.rs | 36 ++ .../ics04_channel/msgs/chan_close_confirm.rs | 173 +++++++--- .../src/ics04_channel/msgs/chan_close_init.rs | 124 +++++-- .../src/ics04_channel/msgs/chan_open_ack.rs | 251 ++++++++++---- .../ics04_channel/msgs/chan_open_confirm.rs | 185 ++++++---- .../src/ics04_channel/msgs/chan_open_init.rs | 175 +++++----- .../src/ics04_channel/msgs/chan_open_try.rs | 321 +++++++++++------- modules/src/ics04_channel/msgs/packet.rs | 65 ---- modules/src/ics04_channel/msgs/recv_packet.rs | 112 ++++++ modules/src/ics04_channel/msgs/timeout.rs | 36 ++ modules/src/ics04_channel/packet.rs | 1 + 15 files changed, 1050 insertions(+), 505 deletions(-) delete mode 100644 modules/src/ics04_channel/msgs/packet.rs create mode 100644 modules/src/ics04_channel/msgs/recv_packet.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index ab99e05d28..14660526fa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,9 @@ Special thanks to external contributors for this release: @CharlyCst ([#347]). - [changelog] Added "unreleased" section in `CHANGELOG.MD` to help streamline releases ([#274]) - [relayer] Integrate relayer spike into relayer crate ([#335]) -- [modules] Implement flexible connection id selection ([#332]) +- [modules] + - Implement flexible connection id selection ([#332]) + - ICS 4 Domain Types for channel handshakes ([#315]) - [relayer] Implement `query_header_at_height` via plain RPC queries (no light client verification) ([#336]) - [relayer-cli] - Merge light clients config in relayer config and add commands to add/remove light clients ([#348]) @@ -19,6 +21,7 @@ Special thanks to external contributors for this release: @CharlyCst ([#347]). - [ibc-proto] Refactor and allow specifying a commit at which the Cosmos SDK should be checked out ([#366]) [#274]: https://github.com/informalsystems/ibc-rs/issues/274 +[#315]: https://github.com/informalsystems/ibc-rs/issues/315 [#332]: https://github.com/informalsystems/ibc-rs/issues/332 [#335]: https://github.com/informalsystems/ibc-rs/pulls/335 [#336]: https://github.com/informalsystems/ibc-rs/issues/336 diff --git a/modules/src/ics04_channel/channel.rs b/modules/src/ics04_channel/channel.rs index 4d09415082..587c801b93 100644 --- a/modules/src/ics04_channel/channel.rs +++ b/modules/src/ics04_channel/channel.rs @@ -2,11 +2,11 @@ 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 anomaly::fail; use std::convert::TryFrom; use std::str::FromStr; -use tendermint_proto::DomainType; #[derive(Clone, Debug, PartialEq)] pub struct ChannelEnd { @@ -50,9 +50,7 @@ impl TryFrom for ChannelEnd { .collect::, _>>() .map_err(|e| Kind::IdentifierError.context(e))?; - // This field is supposed to be opaque to the core IBC protocol. Empty - // version is allowed by the specification (cf. ICS 004). No explicit validation necessary. - let version = value.version; + let version = validate_version(value.version)?; let mut channel_end = ChannelEnd::new(chan_ordering, remote, connection_hops, version); channel_end.set_state(chan_state); @@ -241,37 +239,55 @@ impl State { } } +/// Version validation, specific for channel (ICS4) opening handshake protocol. +/// This field is supposed to be opaque to the core IBC protocol. No explicit validation necessary, +/// and empty version is currently allowed by the specification (cf. ICS 004, v1). +pub fn validate_version(version: String) -> Result { + Ok(version) +} + +#[cfg(test)] +pub mod test_util { + use ibc_proto::ibc::core::channel::v1::Channel as RawChannel; + use ibc_proto::ibc::core::channel::v1::Counterparty as RawCounterparty; + + /// Returns a dummy `RawCounterparty`, for testing only! + pub fn get_dummy_raw_counterparty() -> RawCounterparty { + RawCounterparty { + port_id: "0123456789".into(), + channel_id: "0987654321".into(), + } + } + + /// Returns a dummy `RawChannel`, for testing only! + pub fn get_dummy_raw_channel_end() -> RawChannel { + RawChannel { + state: 0, + ordering: 0, + counterparty: Some(get_dummy_raw_counterparty()), + connection_hops: vec![], + version: "".to_string(), // The version is not validated. + } + } +} + #[cfg(test)] mod tests { use std::str::FromStr; + use crate::ics04_channel::channel::test_util::get_dummy_raw_channel_end; use crate::ics04_channel::channel::ChannelEnd; use ibc_proto::ibc::core::channel::v1::Channel as RawChannel; - use ibc_proto::ibc::core::channel::v1::Counterparty as RawCounterparty; use std::convert::TryFrom; #[test] fn channel_end_try_from_raw() { + let raw_channel_end = get_dummy_raw_channel_end(); + let empty_raw_channel_end = RawChannel { - state: 0, - ordering: 0, counterparty: None, - connection_hops: vec![], - version: "".to_string(), - }; - - let cparty = RawCounterparty { - port_id: "0123456789".into(), - channel_id: "0987654321".into(), - }; - - let raw_channel_end = RawChannel { - state: 0, - ordering: 0, - counterparty: Some(cparty), - connection_hops: vec![], - version: "".to_string(), // The version is not validated. + ..raw_channel_end.clone() }; struct Test { diff --git a/modules/src/ics04_channel/error.rs b/modules/src/ics04_channel/error.rs index d4c68e6fb3..2f5abb2d8f 100644 --- a/modules/src/ics04_channel/error.rs +++ b/modules/src/ics04_channel/error.rs @@ -20,9 +20,15 @@ pub enum Kind { #[error("invalid version")] InvalidVersion, + #[error("invalid signer address")] + InvalidSigner, + #[error("invalid proof")] InvalidProof, + #[error("invalid proof: missing height")] + MissingHeight, + #[error("invalid packet")] InvalidPacket, @@ -31,6 +37,9 @@ pub enum Kind { #[error("missing counterparty")] MissingCounterparty, + + #[error("missing channel end")] + MissingChannel, } impl Kind { diff --git a/modules/src/ics04_channel/msgs.rs b/modules/src/ics04_channel/msgs.rs index 55edabd830..0e47108563 100644 --- a/modules/src/ics04_channel/msgs.rs +++ b/modules/src/ics04_channel/msgs.rs @@ -13,5 +13,5 @@ pub mod chan_close_init; // Packet specific messages. pub mod acknowledgement; -pub mod packet; +pub mod recv_packet; pub mod timeout; diff --git a/modules/src/ics04_channel/msgs/acknowledgement.rs b/modules/src/ics04_channel/msgs/acknowledgement.rs index d640384c2b..8fad1d04d2 100644 --- a/modules/src/ics04_channel/msgs/acknowledgement.rs +++ b/modules/src/ics04_channel/msgs/acknowledgement.rs @@ -1,9 +1,14 @@ +use crate::address::{account_to_string, string_to_account}; use crate::ics04_channel::error::{Error, Kind}; use crate::ics04_channel::packet::Packet; use crate::ics23_commitment::commitment::CommitmentProof; 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 std::convert::TryFrom; /// Message type for the `MsgAcknowledgement` message. const TYPE_MSG_ACKNOWLEDGEMENT: &str = "ics04/opaque"; @@ -64,3 +69,34 @@ impl Msg for MsgAcknowledgement { vec![self.signer] } } + +impl DomainType for MsgAcknowledgement {} + +#[allow(unreachable_code)] +impl TryFrom for MsgAcknowledgement { + type Error = anomaly::Error; + + fn try_from(raw_msg: RawMsgAcknowledgement) -> Result { + let signer = + string_to_account(raw_msg.signer).map_err(|e| Kind::InvalidSigner.context(e))?; + + Ok(MsgAcknowledgement { + packet: Packet, + acknowledgement: vec![], + signer, + proofs: todo!(), + }) + } +} + +impl From for RawMsgAcknowledgement { + fn from(domain_msg: MsgAcknowledgement) -> Self { + RawMsgAcknowledgement { + packet: None, + acknowledgement: vec![], + proof: vec![], + signer: account_to_string(domain_msg.signer).unwrap(), + proof_height: None, + } + } +} diff --git a/modules/src/ics04_channel/msgs/chan_close_confirm.rs b/modules/src/ics04_channel/msgs/chan_close_confirm.rs index 8a2305a045..f1021a654a 100644 --- a/modules/src/ics04_channel/msgs/chan_close_confirm.rs +++ b/modules/src/ics04_channel/msgs/chan_close_confirm.rs @@ -1,9 +1,14 @@ +use crate::address::{account_to_string, string_to_account}; use crate::ics04_channel::error::{Error, Kind}; use crate::ics23_commitment::commitment::CommitmentProof; use crate::ics24_host::identifier::{ChannelId, PortId}; 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 std::convert::{TryFrom, TryInto}; /// Message type for the `MsgChannelCloseConfirm` message. const TYPE_MSG_CHANNEL_CLOSE_CONFIRM: &str = "channel_close_confirm"; @@ -21,7 +26,9 @@ pub struct MsgChannelCloseConfirm { } impl MsgChannelCloseConfirm { - pub fn new( + // todo: Constructor not used yet. + #[allow(dead_code)] + fn new( port_id: String, channel_id: String, proof_init: CommitmentProof, @@ -64,110 +71,162 @@ impl Msg for MsgChannelCloseConfirm { } } -#[cfg(test)] -mod tests { - use crate::ics04_channel::msgs::chan_close_confirm::MsgChannelCloseConfirm; - use crate::ics23_commitment::commitment::CommitmentProof; - use crate::test_utils::get_dummy_proof; - use crate::Height; - use std::str::FromStr; - use tendermint::account::Id as AccountId; +impl DomainType for MsgChannelCloseConfirm {} - #[test] - fn parse_channel_close_confirm_msg() { - let id_hex = "0CDA3F47EF3C4906693B170EF650EB968C5F4B2C"; - let acc = AccountId::from_str(id_hex).unwrap(); - - #[derive(Clone, Debug, PartialEq)] - struct CloseConfirmParams { - port_id: String, - channel_id: String, - proof_init: CommitmentProof, - proof_height: Height, +impl TryFrom for MsgChannelCloseConfirm { + type Error = anomaly::Error; + + fn try_from(raw_msg: RawMsgChannelCloseConfirm) -> Result { + let signer = + string_to_account(raw_msg.signer).map_err(|e| Kind::InvalidSigner.context(e))?; + + let proofs = Proofs::new( + raw_msg.proof_init.into(), + None, + None, + raw_msg + .proof_height + .ok_or_else(|| Kind::MissingHeight)? + .try_into() + .map_err(|e| Kind::InvalidProof.context(e))?, + ) + .map_err(|e| Kind::InvalidProof.context(e))?; + + Ok(MsgChannelCloseConfirm { + port_id: raw_msg + .port_id + .parse() + .map_err(|e| Kind::IdentifierError.context(e))?, + channel_id: raw_msg + .channel_id + .parse() + .map_err(|e| Kind::IdentifierError.context(e))?, + proofs, + signer, + }) + } +} + +impl From for RawMsgChannelCloseConfirm { + fn from(domain_msg: MsgChannelCloseConfirm) -> Self { + RawMsgChannelCloseConfirm { + port_id: domain_msg.port_id.to_string(), + channel_id: domain_msg.channel_id.to_string(), + proof_init: domain_msg.proofs.object_proof().clone().into(), + proof_height: Some(domain_msg.proofs.height().into()), + signer: account_to_string(domain_msg.signer).unwrap(), } + } +} + +#[cfg(test)] +pub mod test_util { + use ibc_proto::ibc::core::channel::v1::MsgChannelCloseConfirm as RawMsgChannelCloseConfirm; - let default_params = CloseConfirmParams { + use crate::test_utils::{get_dummy_bech32_account, get_dummy_proof}; + use ibc_proto::ibc::core::client::v1::Height; + + /// Returns a dummy `RawMsgChannelCloseConfirm`, for testing only! + pub fn get_dummy_raw_msg_chan_close_confirm(proof_height: u64) -> RawMsgChannelCloseConfirm { + RawMsgChannelCloseConfirm { port_id: "port".to_string(), channel_id: "testchannel".to_string(), - proof_init: get_dummy_proof().into(), - proof_height: Height { - version_number: 0, - version_height: 10, - }, - }; + proof_init: get_dummy_proof(), + proof_height: Some(Height { + version_number: 1, + version_height: proof_height, + }), + signer: get_dummy_bech32_account(), + } + } +} + +#[cfg(test)] +mod tests { + use ibc_proto::ibc::core::channel::v1::MsgChannelCloseConfirm as RawMsgChannelCloseConfirm; + + use crate::ics04_channel::msgs::chan_close_confirm::test_util::get_dummy_raw_msg_chan_close_confirm; + use crate::ics04_channel::msgs::chan_close_confirm::MsgChannelCloseConfirm; + use ibc_proto::ibc::core::client::v1::Height; + use std::convert::TryFrom; + #[test] + fn parse_channel_close_confirm_msg() { struct Test { name: String, - params: CloseConfirmParams, + raw: RawMsgChannelCloseConfirm, want_pass: bool, } + let proof_height = 10; + let default_raw_msg = get_dummy_raw_msg_chan_close_confirm(proof_height); + let tests: Vec = vec![ Test { name: "Good parameters".to_string(), - params: default_params.clone(), + raw: default_raw_msg.clone(), want_pass: true, }, Test { name: "Correct port".to_string(), - params: CloseConfirmParams { + raw: RawMsgChannelCloseConfirm { port_id: "p34".to_string(), - ..default_params.clone() + ..default_raw_msg.clone() }, want_pass: true, }, Test { name: "Bad port, name too short".to_string(), - params: CloseConfirmParams { + raw: RawMsgChannelCloseConfirm { port_id: "p".to_string(), - ..default_params.clone() + ..default_raw_msg.clone() }, want_pass: false, }, Test { name: "Bad port, name too long".to_string(), - params: CloseConfirmParams { + raw: RawMsgChannelCloseConfirm { port_id: "abcdefghijklmnsdfasdfasdfasdfasdgafgadsfasdfasdfasdasfdasdfsadfopqrstu" .to_string(), - ..default_params.clone() + ..default_raw_msg.clone() }, want_pass: false, }, Test { name: "Correct channel identifier".to_string(), - params: CloseConfirmParams { + raw: RawMsgChannelCloseConfirm { channel_id: "channelid34".to_string(), - ..default_params.clone() + ..default_raw_msg.clone() }, want_pass: true, }, Test { name: "Bad channel, name too short".to_string(), - params: CloseConfirmParams { + raw: RawMsgChannelCloseConfirm { channel_id: "chshort".to_string(), - ..default_params.clone() + ..default_raw_msg.clone() }, want_pass: false, }, Test { name: "Bad channel, name too long".to_string(), - params: CloseConfirmParams { + raw: RawMsgChannelCloseConfirm { channel_id: "abcdefghiasdfadsfasdfgdfsadfasdasdfasdasdfasddsfasdfasdjklmnopqrstu" .to_string(), - ..default_params.clone() + ..default_raw_msg.clone() }, want_pass: false, }, Test { name: "Bad proof height, height = 0".to_string(), - params: CloseConfirmParams { - proof_height: Height { + raw: RawMsgChannelCloseConfirm { + proof_height: Some(Height { version_number: 0, version_height: 0, - }, - ..default_params + }), + ..default_raw_msg }, want_pass: false, }, @@ -176,24 +235,26 @@ mod tests { .collect(); for test in tests { - let p = test.params.clone(); - - let msg = MsgChannelCloseConfirm::new( - p.port_id, - p.channel_id, - p.proof_init, - p.proof_height, - acc, - ); + let msg = MsgChannelCloseConfirm::try_from(test.raw.clone()); assert_eq!( test.want_pass, msg.is_ok(), - "MsgChanCloseConfirm::new failed for test {}, \nmsg {:?} with error {:?}", + "MsgChanCloseConfirm::try_from raw failed for test {}, \nraw msg {:?} with error {:?}", test.name, - test.params.clone(), + test.raw, msg.err(), ); } } + + #[test] + fn to_and_from() { + let raw = get_dummy_raw_msg_chan_close_confirm(19); + let msg = MsgChannelCloseConfirm::try_from(raw.clone()).unwrap(); + let raw_back = RawMsgChannelCloseConfirm::from(msg.clone()); + let msg_back = MsgChannelCloseConfirm::try_from(raw_back.clone()).unwrap(); + assert_eq!(raw, raw_back); + assert_eq!(msg, msg_back); + } } diff --git a/modules/src/ics04_channel/msgs/chan_close_init.rs b/modules/src/ics04_channel/msgs/chan_close_init.rs index ccd7c7a7b8..b4d4bbf77f 100644 --- a/modules/src/ics04_channel/msgs/chan_close_init.rs +++ b/modules/src/ics04_channel/msgs/chan_close_init.rs @@ -1,8 +1,13 @@ +use crate::address::{account_to_string, string_to_account}; use crate::ics04_channel::error::{Error, Kind}; use crate::ics24_host::identifier::{ChannelId, PortId}; 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 std::convert::TryFrom; /// Message type for the `MsgChannelCloseInit` message. const TYPE_MSG_CHANNEL_CLOSE_INIT: &str = "channel_close_init"; @@ -18,7 +23,9 @@ pub struct MsgChannelCloseInit { } impl MsgChannelCloseInit { - pub fn new( + // todo: Constructor not used yet. + #[allow(dead_code)] + fn new( port_id: String, channel_id: String, signer: AccountId, @@ -57,85 +64,124 @@ impl Msg for MsgChannelCloseInit { } } -#[cfg(test)] -mod tests { - use crate::ics04_channel::msgs::chan_close_init::MsgChannelCloseInit; - use std::str::FromStr; - use tendermint::account::Id as AccountId; +impl DomainType for MsgChannelCloseInit {} - #[test] - fn parse_channel_close_init_msg() { - let id_hex = "0CDA3F47EF3C4906693B170EF650EB968C5F4B2C"; - let acc = AccountId::from_str(id_hex).unwrap(); +impl TryFrom for MsgChannelCloseInit { + type Error = anomaly::Error; + + fn try_from(raw_msg: RawMsgChannelCloseInit) -> Result { + let signer = + string_to_account(raw_msg.signer).map_err(|e| Kind::InvalidSigner.context(e))?; + + Ok(MsgChannelCloseInit { + port_id: raw_msg + .port_id + .parse() + .map_err(|e| Kind::IdentifierError.context(e))?, + channel_id: raw_msg + .channel_id + .parse() + .map_err(|e| Kind::IdentifierError.context(e))?, + signer, + }) + } +} - #[derive(Clone, Debug, PartialEq)] - struct CloseInitParams { - port_id: String, - channel_id: String, +impl From for RawMsgChannelCloseInit { + fn from(domain_msg: MsgChannelCloseInit) -> Self { + RawMsgChannelCloseInit { + port_id: domain_msg.port_id.to_string(), + channel_id: domain_msg.channel_id.to_string(), + signer: account_to_string(domain_msg.signer).unwrap(), } + } +} - let default_params = CloseInitParams { +#[cfg(test)] +pub mod test_util { + use ibc_proto::ibc::core::channel::v1::MsgChannelCloseInit as RawMsgChannelCloseInit; + + use crate::test_utils::get_dummy_bech32_account; + + /// Returns a dummy `RawMsgChannelCloseInit`, for testing only! + pub fn get_dummy_raw_msg_chan_close_init() -> RawMsgChannelCloseInit { + RawMsgChannelCloseInit { port_id: "port".to_string(), channel_id: "testchannel".to_string(), - }; + signer: get_dummy_bech32_account(), + } + } +} + +#[cfg(test)] +mod tests { + use ibc_proto::ibc::core::channel::v1::MsgChannelCloseInit as RawMsgChannelCloseInit; + use crate::ics04_channel::msgs::chan_close_init::test_util::get_dummy_raw_msg_chan_close_init; + use crate::ics04_channel::msgs::chan_close_init::MsgChannelCloseInit; + use std::convert::TryFrom; + + #[test] + fn parse_channel_close_init_msg() { struct Test { name: String, - params: CloseInitParams, + raw: RawMsgChannelCloseInit, want_pass: bool, } + let default_raw_msg = get_dummy_raw_msg_chan_close_init(); + let tests: Vec = vec![ Test { name: "Good parameters".to_string(), - params: default_params.clone(), + raw: default_raw_msg.clone(), want_pass: true, }, Test { name: "Correct port".to_string(), - params: CloseInitParams { + raw: RawMsgChannelCloseInit { port_id: "p34".to_string(), - ..default_params.clone() + ..default_raw_msg.clone() }, want_pass: true, }, Test { name: "Bad port, name too short".to_string(), - params: CloseInitParams { + raw: RawMsgChannelCloseInit { port_id: "p".to_string(), - ..default_params.clone() + ..default_raw_msg.clone() }, want_pass: false, }, Test { name: "Bad port, name too long".to_string(), - params: CloseInitParams { + raw: RawMsgChannelCloseInit { port_id: "abcdefsdfasdfasdfasdfasdfasdfadsfasdgafsgadfasdfasdfasdfsdfasdfaghijklmnopqrstu".to_string(), - ..default_params.clone() + ..default_raw_msg.clone() }, want_pass: false, }, Test { name: "Correct channel identifier".to_string(), - params: CloseInitParams { + raw: RawMsgChannelCloseInit { channel_id: "channelid34".to_string(), - ..default_params.clone() + ..default_raw_msg.clone() }, want_pass: true, }, Test { name: "Bad channel, name too short".to_string(), - params: CloseInitParams { + raw: RawMsgChannelCloseInit { channel_id: "chshort".to_string(), - ..default_params.clone() + ..default_raw_msg.clone() }, want_pass: false, }, Test { name: "Bad channel, name too long".to_string(), - params: CloseInitParams { + raw: RawMsgChannelCloseInit { channel_id: "abcdeasdfasdfasdfasdfasdfasdfasdfasdfdgasdfasdfasdfghijklmnopqrstu".to_string(), - ..default_params + ..default_raw_msg }, want_pass: false, }, @@ -144,18 +190,26 @@ mod tests { .collect(); for test in tests { - let p = test.params.clone(); - - let msg = MsgChannelCloseInit::new(p.port_id, p.channel_id, acc); + let msg = MsgChannelCloseInit::try_from(test.raw.clone()); assert_eq!( test.want_pass, msg.is_ok(), - "MsgChanCloseInit::new failed for test {}, \nmsg {:?} with error {:?}", + "MsgChanCloseInit::try_from failed for test {}, \nmsg {:?} with error {:?}", test.name, - test.params.clone(), + test.raw, msg.err(), ); } } + + #[test] + fn to_and_from() { + let raw = get_dummy_raw_msg_chan_close_init(); + let msg = MsgChannelCloseInit::try_from(raw.clone()).unwrap(); + let raw_back = RawMsgChannelCloseInit::from(msg.clone()); + let msg_back = MsgChannelCloseInit::try_from(raw_back.clone()).unwrap(); + assert_eq!(raw, raw_back); + assert_eq!(msg, msg_back); + } } diff --git a/modules/src/ics04_channel/msgs/chan_open_ack.rs b/modules/src/ics04_channel/msgs/chan_open_ack.rs index dba0111f17..501294db63 100644 --- a/modules/src/ics04_channel/msgs/chan_open_ack.rs +++ b/modules/src/ics04_channel/msgs/chan_open_ack.rs @@ -1,9 +1,15 @@ +use crate::address::{account_to_string, string_to_account}; +use crate::ics04_channel::channel::validate_version; use crate::ics04_channel::error::{Error, Kind}; use crate::ics23_commitment::commitment::CommitmentProof; use crate::ics24_host::identifier::{ChannelId, PortId}; 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 std::convert::{TryFrom, TryInto}; /// Message type for the `MsgChannelOpenAck` message. const TYPE_MSG_CHANNEL_OPEN_ACK: &str = "channel_open_ack"; @@ -15,13 +21,16 @@ const TYPE_MSG_CHANNEL_OPEN_ACK: &str = "channel_open_ack"; pub struct MsgChannelOpenAck { port_id: PortId, channel_id: ChannelId, + counterparty_channel_id: ChannelId, counterparty_version: String, proofs: Proofs, signer: AccountId, } impl MsgChannelOpenAck { - pub fn new( + #[allow(dead_code, unreachable_code, unused_variables)] + // TODO: Not used (yet). Also missing `counterparty_channel_id` value. + fn new( port_id: String, channel_id: String, counterparty_version: String, @@ -36,7 +45,9 @@ impl MsgChannelOpenAck { channel_id: channel_id .parse() .map_err(|e| Kind::IdentifierError.context(e))?, - counterparty_version, + counterparty_channel_id: todo!(), + counterparty_version: validate_version(counterparty_version) + .map_err(|e| Kind::InvalidVersion.context(e))?, proofs: Proofs::new(proof_try, None, None, proofs_height) .map_err(|e| Kind::InvalidProof.context(e))?, signer, @@ -66,108 +77,223 @@ impl Msg for MsgChannelOpenAck { } } -#[cfg(test)] -mod tests { - use crate::ics04_channel::msgs::chan_open_ack::MsgChannelOpenAck; - use crate::ics23_commitment::commitment::CommitmentProof; - use crate::test_utils::get_dummy_proof; - use crate::Height; - use std::str::FromStr; - use tendermint::account::Id as AccountId; +impl DomainType for MsgChannelOpenAck {} - #[test] - fn parse_channel_open_ack_msg() { - let id_hex = "0CDA3F47EF3C4906693B170EF650EB968C5F4B2C"; - let acc = AccountId::from_str(id_hex).unwrap(); - - #[derive(Clone, Debug, PartialEq)] - struct OpenAckParams { - port_id: String, - channel_id: String, - counterparty_version: String, - proof_try: CommitmentProof, - proof_height: Height, +impl TryFrom for MsgChannelOpenAck { + type Error = anomaly::Error; + + fn try_from(raw_msg: RawMsgChannelOpenAck) -> Result { + let signer = + string_to_account(raw_msg.signer).map_err(|e| Kind::InvalidSigner.context(e))?; + + let proofs = Proofs::new( + raw_msg.proof_try.into(), + None, + None, + raw_msg + .proof_height + .ok_or_else(|| Kind::MissingHeight)? + .try_into() + .map_err(|e| Kind::InvalidProof.context(e))?, + ) + .map_err(|e| Kind::InvalidProof.context(e))?; + + Ok(MsgChannelOpenAck { + port_id: raw_msg + .port_id + .parse() + .map_err(|e| Kind::IdentifierError.context(e))?, + channel_id: raw_msg + .channel_id + .parse() + .map_err(|e| Kind::IdentifierError.context(e))?, + counterparty_channel_id: raw_msg + .counterparty_channel_id + .parse() + .map_err(|e| Kind::IdentifierError.context(e))?, + counterparty_version: validate_version(raw_msg.counterparty_version)?, + proofs, + signer, + }) + } +} + +impl From for RawMsgChannelOpenAck { + fn from(domain_msg: MsgChannelOpenAck) -> Self { + RawMsgChannelOpenAck { + port_id: domain_msg.port_id.to_string(), + channel_id: domain_msg.channel_id.to_string(), + counterparty_channel_id: domain_msg.counterparty_channel_id.to_string(), + counterparty_version: domain_msg.counterparty_version.to_string(), + proof_try: domain_msg.proofs.object_proof().clone().into(), + proof_height: Some(domain_msg.proofs.height().into()), + signer: account_to_string(domain_msg.signer).unwrap(), } + } +} - let default_params = OpenAckParams { +#[cfg(test)] +pub mod test_util { + use ibc_proto::ibc::core::channel::v1::MsgChannelOpenAck as RawMsgChannelOpenAck; + + use crate::test_utils::{get_dummy_bech32_account, get_dummy_proof}; + use ibc_proto::ibc::core::client::v1::Height; + + /// Returns a dummy `RawMsgChannelOpenAck`, for testing only! + pub fn get_dummy_raw_msg_chan_open_ack(proof_height: u64) -> RawMsgChannelOpenAck { + RawMsgChannelOpenAck { port_id: "port".to_string(), channel_id: "testchannel".to_string(), - counterparty_version: "1.0".to_string(), - proof_try: get_dummy_proof().into(), - proof_height: Height { - version_number: 0, - version_height: 10, - }, - }; + counterparty_channel_id: "cpartychannel".to_string(), + counterparty_version: "v1".to_string(), + proof_try: get_dummy_proof(), + proof_height: Some(Height { + version_number: 1, + version_height: proof_height, + }), + signer: get_dummy_bech32_account(), + } + } +} + +#[cfg(test)] +mod tests { + use ibc_proto::ibc::core::channel::v1::MsgChannelOpenAck as RawMsgChannelOpenAck; + + use crate::ics04_channel::msgs::chan_open_ack::test_util::get_dummy_raw_msg_chan_open_ack; + use crate::ics04_channel::msgs::chan_open_ack::MsgChannelOpenAck; + use ibc_proto::ibc::core::client::v1::Height; + use std::convert::TryFrom; + #[test] + fn parse_channel_open_ack_msg() { struct Test { name: String, - params: OpenAckParams, + raw: RawMsgChannelOpenAck, want_pass: bool, } + let proof_height = 20; + let default_raw_msg = get_dummy_raw_msg_chan_open_ack(proof_height); + let tests: Vec = vec![ Test { name: "Good parameters".to_string(), - params: default_params.clone(), + raw: default_raw_msg.clone(), want_pass: true, }, Test { name: "Correct port identifier".to_string(), - params: OpenAckParams { + raw: RawMsgChannelOpenAck { port_id: "p34".to_string(), - ..default_params.clone() + ..default_raw_msg.clone() }, want_pass: true, }, Test { name: "Bad port, name too short".to_string(), - params: OpenAckParams { + raw: RawMsgChannelOpenAck { port_id: "p".to_string(), - ..default_params.clone() + ..default_raw_msg.clone() }, want_pass: false, }, Test { name: "Bad port, name too long".to_string(), - params: OpenAckParams { + raw: RawMsgChannelOpenAck { port_id: "abcdezdfDfsdfgfddsfsfdsdfdfvxcvzxcvsgdfsdfwefwvsdfdsfdasgagadgsadgsdffghijklmnopqrstu".to_string(), - ..default_params.clone() + ..default_raw_msg.clone() }, want_pass: false, }, Test { name: "Correct channel identifier".to_string(), - params: OpenAckParams { + raw: RawMsgChannelOpenAck { channel_id: "channelid34".to_string(), - ..default_params.clone() + ..default_raw_msg.clone() }, want_pass: true, }, Test { name: "Bad channel, name too short".to_string(), - params: OpenAckParams { + raw: RawMsgChannelOpenAck { channel_id: "chshort".to_string(), - ..default_params.clone() + ..default_raw_msg.clone() }, want_pass: false, }, Test { name: "Bad channel, name too long".to_string(), - params: OpenAckParams { + raw: RawMsgChannelOpenAck { channel_id: "abcdefghsdfasdfasfdasfdwewefsdfasdfasdfasdfasdfasdfsfdijklmnopqrstu".to_string(), - ..default_params.clone() + ..default_raw_msg.clone() + }, + want_pass: false, + }, + Test { + name: "[Counterparty] Correct channel identifier".to_string(), + raw: RawMsgChannelOpenAck { + counterparty_channel_id: "channelid34".to_string(), + ..default_raw_msg.clone() + }, + want_pass: true, + }, + Test { + name: "[Counterparty] Bad channel, name too short".to_string(), + raw: RawMsgChannelOpenAck { + counterparty_channel_id: "chshort".to_string(), + ..default_raw_msg.clone() }, want_pass: false, }, + Test { + name: "[Counterparty] Bad channel, name too long".to_string(), + raw: RawMsgChannelOpenAck { + counterparty_channel_id: "abcdefghsdfasdfasfdasfdwewefsdfasdfasdfasdfasdfasdfsfdijklmnopqrstu".to_string(), + ..default_raw_msg.clone() + }, + want_pass: false, + }, + Test { + name: "Empty counterparty version (allowed)".to_string(), + raw: RawMsgChannelOpenAck { + counterparty_version: " ".to_string(), + ..default_raw_msg.clone() + }, + want_pass: true, + }, + Test { + name: "Arbitrary counterparty version (allowed)".to_string(), + raw: RawMsgChannelOpenAck { + counterparty_version: "v1.1.23-alpha".to_string(), + ..default_raw_msg.clone() + }, + want_pass: true, + }, Test { name: "Bad proof height, height = 0".to_string(), - params: OpenAckParams { - proof_height: Height { + raw: RawMsgChannelOpenAck { + proof_height: Some(Height { version_number: 0, version_height: 0, - }, - ..default_params + }), + ..default_raw_msg.clone() + }, + want_pass: false, + }, + Test { + name: "Missing proof height".to_string(), + raw: RawMsgChannelOpenAck { + proof_height: None, + ..default_raw_msg.clone() + }, + want_pass: false, + }, + Test { + name: "Missing proof try (object proof)".to_string(), + raw: RawMsgChannelOpenAck { + proof_try: vec![], + ..default_raw_msg }, want_pass: false, }, @@ -176,25 +302,26 @@ mod tests { .collect(); for test in tests { - let p = test.params.clone(); - - let msg = MsgChannelOpenAck::new( - p.port_id, - p.channel_id, - p.counterparty_version, - p.proof_try, - p.proof_height, - acc, - ); + let res_msg = MsgChannelOpenAck::try_from(test.raw.clone()); assert_eq!( test.want_pass, - msg.is_ok(), - "MsgChanOpenAck::new failed for test {}, \nmsg {:?} with error {:?}", + res_msg.is_ok(), + "MsgChanOpenAck::try_from raw failed for test {}, \nraw msg {:?} with error {:?}", test.name, - test.params.clone(), - msg.err(), + test.raw, + res_msg.err(), ); } } + + #[test] + fn to_and_from() { + let raw = get_dummy_raw_msg_chan_open_ack(100); + let msg = MsgChannelOpenAck::try_from(raw.clone()).unwrap(); + let raw_back = RawMsgChannelOpenAck::from(msg.clone()); + let msg_back = MsgChannelOpenAck::try_from(raw_back.clone()).unwrap(); + assert_eq!(raw, raw_back); + assert_eq!(msg, msg_back); + } } diff --git a/modules/src/ics04_channel/msgs/chan_open_confirm.rs b/modules/src/ics04_channel/msgs/chan_open_confirm.rs index 852016d217..c5d35ec14b 100644 --- a/modules/src/ics04_channel/msgs/chan_open_confirm.rs +++ b/modules/src/ics04_channel/msgs/chan_open_confirm.rs @@ -1,9 +1,14 @@ +use crate::address::{account_to_string, string_to_account}; use crate::ics04_channel::error::{Error, Kind}; use crate::ics23_commitment::commitment::CommitmentProof; use crate::ics24_host::identifier::{ChannelId, PortId}; 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 std::convert::{TryFrom, TryInto}; /// Message type for the `MsgChannelOpenConfirm` message. const TYPE_MSG_CHANNEL_OPEN_CONFIRM: &str = "channel_open_confirm"; @@ -21,7 +26,9 @@ pub struct MsgChannelOpenConfirm { } impl MsgChannelOpenConfirm { - pub fn new( + #[allow(dead_code)] + // TODO: Not in use (yet), hence private. + fn new( port_id: String, channel_id: String, proof_ack: CommitmentProof, @@ -64,106 +71,166 @@ impl Msg for MsgChannelOpenConfirm { } } -#[cfg(test)] -mod tests { - use crate::ics04_channel::msgs::chan_open_confirm::MsgChannelOpenConfirm; - use crate::ics23_commitment::commitment::CommitmentProof; - use crate::test_utils::get_dummy_proof; - use crate::Height; - use std::str::FromStr; - use tendermint::account::Id as AccountId; +impl DomainType for MsgChannelOpenConfirm {} - #[test] - fn parse_channel_open_confirm_msg() { - let id_hex = "0CDA3F47EF3C4906693B170EF650EB968C5F4B2C"; - let acc = AccountId::from_str(id_hex).unwrap(); - - #[derive(Clone, Debug, PartialEq)] - struct OpenConfirmParams { - port_id: String, - channel_id: String, - proof_ack: CommitmentProof, - proof_height: Height, +impl TryFrom for MsgChannelOpenConfirm { + type Error = anomaly::Error; + + fn try_from(raw_msg: RawMsgChannelOpenConfirm) -> Result { + let signer = + string_to_account(raw_msg.signer).map_err(|e| Kind::InvalidSigner.context(e))?; + + let proofs = Proofs::new( + raw_msg.proof_ack.into(), + None, + None, + raw_msg + .proof_height + .ok_or_else(|| Kind::MissingHeight)? + .try_into() + .map_err(|e| Kind::InvalidProof.context(e))?, + ) + .map_err(|e| Kind::InvalidProof.context(e))?; + + Ok(MsgChannelOpenConfirm { + port_id: raw_msg + .port_id + .parse() + .map_err(|e| Kind::IdentifierError.context(e))?, + channel_id: raw_msg + .channel_id + .parse() + .map_err(|e| Kind::IdentifierError.context(e))?, + proofs, + signer, + }) + } +} + +impl From for RawMsgChannelOpenConfirm { + fn from(domain_msg: MsgChannelOpenConfirm) -> Self { + RawMsgChannelOpenConfirm { + port_id: domain_msg.port_id.to_string(), + channel_id: domain_msg.channel_id.to_string(), + proof_ack: domain_msg.proofs.object_proof().clone().into(), + proof_height: Some(domain_msg.proofs.height().into()), + signer: account_to_string(domain_msg.signer).unwrap(), } + } +} + +#[cfg(test)] +pub mod test_util { + use ibc_proto::ibc::core::channel::v1::MsgChannelOpenConfirm as RawMsgChannelOpenConfirm; + + use crate::test_utils::{get_dummy_bech32_account, get_dummy_proof}; + use ibc_proto::ibc::core::client::v1::Height; - let default_params = OpenConfirmParams { + /// Returns a dummy `RawMsgChannelOpenConfirm`, for testing only! + pub fn get_dummy_raw_msg_chan_open_confirm(proof_height: u64) -> RawMsgChannelOpenConfirm { + RawMsgChannelOpenConfirm { port_id: "port".to_string(), channel_id: "testchannel".to_string(), - proof_ack: get_dummy_proof().into(), - proof_height: Height { - version_number: 0, - version_height: 10, - }, - }; + proof_ack: get_dummy_proof(), + proof_height: Some(Height { + version_number: 1, + version_height: proof_height, + }), + signer: get_dummy_bech32_account(), + } + } +} + +#[cfg(test)] +mod tests { + use ibc_proto::ibc::core::channel::v1::MsgChannelOpenConfirm as RawMsgChannelOpenConfirm; + + use crate::ics04_channel::msgs::chan_open_confirm::test_util::get_dummy_raw_msg_chan_open_confirm; + use crate::ics04_channel::msgs::chan_open_confirm::MsgChannelOpenConfirm; + use ibc_proto::ibc::core::client::v1::Height; + use std::convert::TryFrom; + #[test] + fn parse_channel_open_confirm_msg() { struct Test { name: String, - params: OpenConfirmParams, + raw: RawMsgChannelOpenConfirm, want_pass: bool, } + let proof_height = 78; + let default_raw_msg = get_dummy_raw_msg_chan_open_confirm(proof_height); + let tests: Vec = vec![ Test { name: "Good parameters".to_string(), - params: default_params.clone(), + raw: default_raw_msg.clone(), want_pass: true, }, Test { name: "Correct port".to_string(), - params: OpenConfirmParams { + raw: RawMsgChannelOpenConfirm { port_id: "p34".to_string(), - ..default_params.clone() + ..default_raw_msg.clone() }, want_pass: true, }, Test { name: "Bad port, name too short".to_string(), - params: OpenConfirmParams { + raw: RawMsgChannelOpenConfirm { port_id: "p".to_string(), - ..default_params.clone() + ..default_raw_msg.clone() }, want_pass: false, }, Test { name: "Bad port, name too long".to_string(), - params: OpenConfirmParams { + raw: RawMsgChannelOpenConfirm { port_id: "abcdesdfasdsdffasdfasdfasfasdgasdfgasdfasdfasdfasdfasdfasdffghijklmnopqrstu".to_string(), - ..default_params.clone() + ..default_raw_msg.clone() }, want_pass: false, }, Test { name: "Correct channel identifier".to_string(), - params: OpenConfirmParams { + raw: RawMsgChannelOpenConfirm { channel_id: "channelid34".to_string(), - ..default_params.clone() + ..default_raw_msg.clone() }, want_pass: true, }, Test { name: "Bad channel, name too short".to_string(), - params: OpenConfirmParams { + raw: RawMsgChannelOpenConfirm { channel_id: "chshort".to_string(), - ..default_params.clone() + ..default_raw_msg.clone() }, want_pass: false, }, Test { name: "Bad channel, name too long".to_string(), - params: OpenConfirmParams { + raw: RawMsgChannelOpenConfirm { channel_id: "abcdefghijklmnoasdfasdfasdfasdfasdgsdghasdfasdfasdfasdfadsfasgdasdfasdfasfdpqrstu".to_string(), - ..default_params.clone() + ..default_raw_msg.clone() }, want_pass: false, }, Test { name: "Bad proof height, height = 0".to_string(), - params: OpenConfirmParams { - proof_height: Height { + raw: RawMsgChannelOpenConfirm { + proof_height: Some(Height { version_number: 0, version_height: 0, - }, - ..default_params + }), + ..default_raw_msg.clone() + }, + want_pass: false, + }, + Test { + name: "Missing object proof".to_string(), + raw: RawMsgChannelOpenConfirm { + proof_ack: vec![], + ..default_raw_msg }, want_pass: false, }, @@ -172,24 +239,26 @@ mod tests { .collect(); for test in tests { - let p = test.params.clone(); - - let msg = MsgChannelOpenConfirm::new( - p.port_id, - p.channel_id, - p.proof_ack, - p.proof_height, - acc, - ); + let res_msg = MsgChannelOpenConfirm::try_from(test.raw.clone()); assert_eq!( test.want_pass, - msg.is_ok(), - "MsgChanOpenConfirm::new failed for test {}, \nmsg {:?} with error {:?}", + res_msg.is_ok(), + "MsgChanOpenConfirm::try_from failed for test {}, \nraw msg {:?} with error {:?}", test.name, - test.params.clone(), - msg.err(), + test.raw, + res_msg.err(), ); } } + + #[test] + fn to_and_from() { + let raw = get_dummy_raw_msg_chan_open_confirm(19); + let msg = MsgChannelOpenConfirm::try_from(raw.clone()).unwrap(); + let raw_back = RawMsgChannelOpenConfirm::from(msg.clone()); + let msg_back = MsgChannelOpenConfirm::try_from(raw_back.clone()).unwrap(); + assert_eq!(raw, raw_back); + assert_eq!(msg, msg_back); + } } diff --git a/modules/src/ics04_channel/msgs/chan_open_init.rs b/modules/src/ics04_channel/msgs/chan_open_init.rs index 65d9f9a777..13aadc2c11 100644 --- a/modules/src/ics04_channel/msgs/chan_open_init.rs +++ b/modules/src/ics04_channel/msgs/chan_open_init.rs @@ -1,12 +1,14 @@ -#![allow(clippy::too_many_arguments)] - +use crate::address::{account_to_string, string_to_account}; use crate::ics04_channel::channel::{ChannelEnd, Counterparty, Order}; use crate::ics04_channel::error::{Error, Kind}; use crate::ics24_host::identifier::{ChannelId, ConnectionId, 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 std::convert::{TryFrom, TryInto}; use std::str::FromStr; /// Message type for the `MsgChannelOpenInit` message. @@ -24,7 +26,11 @@ pub struct MsgChannelOpenInit { } impl MsgChannelOpenInit { - pub fn new( + // 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, @@ -78,96 +84,106 @@ impl Msg for MsgChannelOpenInit { } } -#[cfg(test)] -mod tests { - use crate::ics04_channel::channel::Order; - use crate::ics04_channel::msgs::chan_open_init::MsgChannelOpenInit; - use std::str::FromStr; - use tendermint::account::Id as AccountId; +impl DomainType for MsgChannelOpenInit {} - #[test] - fn parse_channel_open_init_msg() { - let id_hex = "0CDA3F47EF3C4906693B170EF650EB968C5F4B2C"; - let acc = AccountId::from_str(id_hex).unwrap(); - - #[derive(Clone, Debug, PartialEq)] - struct OpenInitParams { - port_id: String, - channel_id: String, - version: String, - order: i32, - connection_hops: Vec, - counterparty_port_id: String, - counterparty_channel_id: String, +impl TryFrom for MsgChannelOpenInit { + type Error = anomaly::Error; + + fn try_from(raw_msg: RawMsgChannelOpenInit) -> Result { + let signer = + string_to_account(raw_msg.signer).map_err(|e| Kind::InvalidSigner.context(e))?; + + Ok(MsgChannelOpenInit { + port_id: raw_msg + .port_id + .parse() + .map_err(|e| Kind::IdentifierError.context(e))?, + channel_id: raw_msg + .channel_id + .parse() + .map_err(|e| Kind::IdentifierError.context(e))?, + channel: raw_msg + .channel + .ok_or_else(|| Kind::MissingChannel)? + .try_into()?, + signer, + }) + } +} + +impl From for RawMsgChannelOpenInit { + fn from(domain_msg: MsgChannelOpenInit) -> Self { + RawMsgChannelOpenInit { + port_id: domain_msg.port_id.to_string(), + channel_id: domain_msg.channel_id.to_string(), + channel: Some(domain_msg.channel.into()), + signer: account_to_string(domain_msg.signer).unwrap(), } + } +} - let default_params = OpenInitParams { +#[cfg(test)] +pub mod test_util { + use ibc_proto::ibc::core::channel::v1::MsgChannelOpenInit as RawMsgChannelOpenInit; + + use crate::ics04_channel::channel::test_util::get_dummy_raw_channel_end; + use crate::test_utils::get_dummy_bech32_account; + + /// Returns a dummy `RawMsgChannelOpenInit`, for testing only! + pub fn get_dummy_raw_msg_chan_open_init() -> RawMsgChannelOpenInit { + RawMsgChannelOpenInit { port_id: "port".to_string(), channel_id: "testchannel".to_string(), - version: "1.0".to_string(), - order: Order::from_str("ORDERED").unwrap() as i32, - connection_hops: vec!["connectionhop".to_string()].into_iter().collect(), - counterparty_port_id: "destport".to_string(), - counterparty_channel_id: "testdestchannel".to_string(), - }; + channel: Some(get_dummy_raw_channel_end()), + signer: get_dummy_bech32_account(), + } + } +} + +#[cfg(test)] +mod tests { + use crate::ics04_channel::msgs::chan_open_init::test_util::get_dummy_raw_msg_chan_open_init; + use crate::ics04_channel::msgs::chan_open_init::MsgChannelOpenInit; + use ibc_proto::ibc::core::channel::v1::MsgChannelOpenInit as RawMsgChannelOpenInit; + use std::convert::TryFrom; + #[test] + fn channel_open_init_from_raw() { struct Test { name: String, - params: OpenInitParams, + raw: RawMsgChannelOpenInit, want_pass: bool, } + let default_raw_msg = get_dummy_raw_msg_chan_open_init(); + let tests: Vec = vec![ Test { name: "Good parameters".to_string(), - params: default_params.clone(), - want_pass: true, - }, - Test { - name: "Correct port identifier".to_string(), - params: OpenInitParams { - port_id: "p34".to_string(), - ..default_params.clone() - }, + raw: default_raw_msg.clone(), want_pass: true, }, Test { name: "Incorrect port identifier, slash (separator) prohibited".to_string(), - params: OpenInitParams { + raw: RawMsgChannelOpenInit { port_id: "p34/".to_string(), - ..default_params.clone() + ..default_raw_msg.clone() }, want_pass: false, }, Test { name: "Bad channel, name too short".to_string(), - params: OpenInitParams { + raw: RawMsgChannelOpenInit { channel_id: "chshort".to_string(), - ..default_params.clone() + ..default_raw_msg.clone() }, want_pass: false, }, Test { - name: "Bad version".to_string(), - params: OpenInitParams { - version: " . ".to_string(), - ..default_params.clone() - }, - want_pass: true, // verified in validate_basic() - }, - Test { - name: "Bad order".to_string(), - params: OpenInitParams { - order: 99, - ..default_params.clone() - }, - want_pass: false, - }, - Test { - name: "Bad connection hops (conn id too short, must be 10 chars)".to_string(), - params: OpenInitParams { - connection_hops: vec!["conn124".to_string()].into_iter().collect(), - ..default_params + name: "Missing channel".to_string(), + raw: RawMsgChannelOpenInit { + channel: None, + ..default_raw_msg }, want_pass: false, }, @@ -176,27 +192,26 @@ mod tests { .collect(); for test in tests { - let p = test.params.clone(); - - let msg = MsgChannelOpenInit::new( - p.port_id, - p.channel_id, - p.version, - p.order, - p.connection_hops, - p.counterparty_port_id, - p.counterparty_channel_id, - acc, - ); + let res_msg = MsgChannelOpenInit::try_from(test.raw.clone()); assert_eq!( test.want_pass, - msg.is_ok(), - "MsgChanOpenInit::new failed for test {}, \nmsg {:?} with error {:?}", + res_msg.is_ok(), + "MsgChanOpenInit::try_from failed for test {}, \nraw msg {:?} with error {:?}", test.name, - test.params.clone(), - msg.err(), + test.raw, + res_msg.err(), ); } } + + #[test] + fn to_and_from() { + let raw = get_dummy_raw_msg_chan_open_init(); + let msg = MsgChannelOpenInit::try_from(raw.clone()).unwrap(); + let raw_back = RawMsgChannelOpenInit::from(msg.clone()); + let msg_back = MsgChannelOpenInit::try_from(raw_back.clone()).unwrap(); + assert_eq!(raw, raw_back); + assert_eq!(msg, msg_back); + } } diff --git a/modules/src/ics04_channel/msgs/chan_open_try.rs b/modules/src/ics04_channel/msgs/chan_open_try.rs index afa2567681..d22e4775d0 100644 --- a/modules/src/ics04_channel/msgs/chan_open_try.rs +++ b/modules/src/ics04_channel/msgs/chan_open_try.rs @@ -1,12 +1,15 @@ -#![allow(clippy::too_many_arguments)] -use crate::ics04_channel::channel::{ChannelEnd, Counterparty, Order}; +use crate::address::{account_to_string, string_to_account}; +use crate::ics04_channel::channel::{validate_version, ChannelEnd, Counterparty, Order}; 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 ibc_proto::ibc::core::channel::v1::MsgChannelOpenTry as RawMsgChannelOpenTry; use tendermint::account::Id as AccountId; +use tendermint_proto::DomainType; +use std::convert::{TryFrom, TryInto}; use std::str::FromStr; /// Message type for the `MsgChannelOpenTry` message. @@ -18,7 +21,8 @@ const TYPE_MSG_CHANNEL_OPEN_TRY: &str = "channel_open_try"; #[derive(Clone, Debug, PartialEq)] pub struct MsgChannelOpenTry { port_id: PortId, - channel_id: ChannelId, + channel_id: ChannelId, // Labeled `desired_channel_id` in raw types. + counterparty_chosen_channel_id: Option, channel: ChannelEnd, counterparty_version: String, proofs: Proofs, @@ -26,7 +30,9 @@ pub struct MsgChannelOpenTry { } impl MsgChannelOpenTry { - pub fn new( + #[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, @@ -44,6 +50,9 @@ impl MsgChannelOpenTry { .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() @@ -51,14 +60,16 @@ impl MsgChannelOpenTry { 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))?, - channel_version, + version, ), - counterparty_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, @@ -86,210 +97,270 @@ impl Msg for MsgChannelOpenTry { } } -#[cfg(test)] -mod tests { - use crate::ics04_channel::channel::Order; - use crate::ics04_channel::msgs::chan_open_try::MsgChannelOpenTry; - use crate::ics23_commitment::commitment::CommitmentProof; - use crate::test_utils::get_dummy_proof; - use crate::Height; - use std::str::FromStr; - use tendermint::account::Id as AccountId; +impl DomainType for MsgChannelOpenTry {} - #[test] - fn parse_channel_open_try_msg() { - let id_hex = "0CDA3F47EF3C4906693B170EF650EB968C5F4B2C"; - let acc = AccountId::from_str(id_hex).unwrap(); +impl TryFrom for MsgChannelOpenTry { + type Error = anomaly::Error; + + fn try_from(raw_msg: RawMsgChannelOpenTry) -> Result { + let signer = + string_to_account(raw_msg.signer).map_err(|e| Kind::InvalidSigner.context(e))?; + + let proofs = Proofs::new( + raw_msg.proof_init.into(), + None, + None, + raw_msg + .proof_height + .ok_or_else(|| Kind::MissingHeight)? + .try_into() + .map_err(|e| Kind::InvalidProof.context(e))?, + ) + .map_err(|e| Kind::InvalidProof.context(e))?; + + let counterparty_chosen_channel_id = Some(raw_msg.counterparty_chosen_channel_id) + .filter(|x| !x.is_empty()) + .map(|v| FromStr::from_str(v.as_str())) + .transpose() + .map_err(|e| Kind::IdentifierError.context(e))?; + + Ok(MsgChannelOpenTry { + port_id: raw_msg + .port_id + .parse() + .map_err(|e| Kind::IdentifierError.context(e))?, + channel_id: raw_msg + .desired_channel_id + .parse() + .map_err(|e| Kind::IdentifierError.context(e))?, + counterparty_chosen_channel_id, + channel: raw_msg + .channel + .ok_or_else(|| Kind::MissingChannel)? + .try_into()?, + counterparty_version: validate_version(raw_msg.counterparty_version)?, + proofs, + signer, + }) + } +} - #[derive(Clone, Debug, PartialEq)] - struct OpenTryParams { - 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, - proof_height: Height, +impl From for RawMsgChannelOpenTry { + fn from(domain_msg: MsgChannelOpenTry) -> Self { + RawMsgChannelOpenTry { + port_id: domain_msg.port_id.to_string(), + desired_channel_id: domain_msg.channel_id.to_string(), + counterparty_chosen_channel_id: domain_msg + .counterparty_chosen_channel_id + .map_or_else(|| "".to_string(), |id| id.to_string()), + channel: Some(domain_msg.channel.into()), + counterparty_version: domain_msg.counterparty_version, + proof_init: domain_msg.proofs.object_proof().clone().into(), + proof_height: Some(domain_msg.proofs.height().into()), + signer: account_to_string(domain_msg.signer).unwrap(), } + } +} + +#[cfg(test)] +pub mod test_util { + use ibc_proto::ibc::core::channel::v1::MsgChannelOpenTry as RawMsgChannelOpenTry; + + use crate::ics04_channel::channel::test_util::get_dummy_raw_channel_end; + use crate::test_utils::{get_dummy_bech32_account, get_dummy_proof}; + use ibc_proto::ibc::core::client::v1::Height; - let default_params = OpenTryParams { + /// Returns a dummy `RawMsgChannelOpenTry`, for testing only! + pub fn get_dummy_raw_msg_chan_open_try(proof_height: u64) -> RawMsgChannelOpenTry { + RawMsgChannelOpenTry { port_id: "port".to_string(), - channel_id: "testchannel".to_string(), - channel_version: "1.0".to_string(), - order: Order::from_str("ORDERED").unwrap() as i32, - connection_hops: vec!["connectionhop".to_string()].into_iter().collect(), - counterparty_port_id: "destport".to_string(), - counterparty_channel_id: "testdestchannel".to_string(), - counterparty_version: "1.0".to_string(), - proof_init: get_dummy_proof().into(), - proof_height: Height { - version_number: 0, - version_height: 10, - }, - }; + desired_channel_id: "testchannel".to_string(), + counterparty_chosen_channel_id: "".to_string(), + channel: Some(get_dummy_raw_channel_end()), + counterparty_version: "".to_string(), + proof_init: get_dummy_proof(), + proof_height: Some(Height { + version_number: 1, + version_height: proof_height, + }), + signer: get_dummy_bech32_account(), + } + } +} + +#[cfg(test)] +mod tests { + use crate::ics04_channel::msgs::chan_open_try::test_util::get_dummy_raw_msg_chan_open_try; + use crate::ics04_channel::msgs::chan_open_try::MsgChannelOpenTry; + use ibc_proto::ibc::core::channel::v1::MsgChannelOpenTry as RawMsgChannelOpenTry; + use ibc_proto::ibc::core::client::v1::Height; + use std::convert::TryFrom; + #[test] + fn channel_open_try_from_raw() { struct Test { name: String, - params: OpenTryParams, + raw: RawMsgChannelOpenTry, want_pass: bool, } + let proof_height = 10; + let default_raw_msg = get_dummy_raw_msg_chan_open_try(proof_height); + let tests: Vec = vec![ Test { name: "Good parameters".to_string(), - params: default_params.clone(), + raw: default_raw_msg.clone(), want_pass: true, }, Test { name: "Correct port".to_string(), - params: OpenTryParams { + raw: RawMsgChannelOpenTry { port_id: "p34".to_string(), - ..default_params.clone() + ..default_raw_msg.clone() }, want_pass: true, }, Test { name: "Bad port, name too short".to_string(), - params: OpenTryParams { + raw: RawMsgChannelOpenTry { port_id: "p".to_string(), - ..default_params.clone() + ..default_raw_msg.clone() }, want_pass: false, }, Test { name: "Bad port, name too long".to_string(), - params: OpenTryParams { + raw: RawMsgChannelOpenTry { port_id: "abcdefghijasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfadgasgasdfasdfasdfasdfaklmnopqrstu".to_string(), - ..default_params.clone() + ..default_raw_msg.clone() }, want_pass: false, }, Test { name: "Correct channel identifier".to_string(), - params: OpenTryParams { - channel_id: "channelid34".to_string(), - ..default_params.clone() + raw: RawMsgChannelOpenTry { + desired_channel_id: "channelid34".to_string(), + ..default_raw_msg.clone() }, want_pass: true, }, Test { name: "Bad channel, name too short".to_string(), - params: OpenTryParams { - channel_id: "chshort".to_string(), - ..default_params.clone() + raw: RawMsgChannelOpenTry { + desired_channel_id: "chshort".to_string(), + ..default_raw_msg.clone() }, want_pass: false, }, Test { name: "Bad channel, name too long".to_string(), - params: OpenTryParams { - channel_id: "abcdefghijkasdfasdfasdfasgdasdgasdfasdfadflmnoasdasdasdfasdfasdfasdfadadgadgadsfpqrstu".to_string(), - ..default_params.clone() + raw: RawMsgChannelOpenTry { + desired_channel_id: "abcdefghijkasdfasdfasdfasgdasdgasdfasdfadflmnoasdasdasdfasdfasdfasdfadadgadgadsfpqrstu".to_string(), + ..default_raw_msg.clone() }, want_pass: false, }, Test { - name: "Bad proof height, height = 0".to_string(), - params: OpenTryParams { - proof_height: Height { - version_number: 0, - version_height: 0, - }, - ..default_params.clone() + name: "Empty counterparty chosen channel id (valid choice)".to_string(), + raw: RawMsgChannelOpenTry { + counterparty_chosen_channel_id: "".to_string(), + ..default_raw_msg.clone() + }, + want_pass: true, + }, + Test { + name: "[Counterparty] Correct channel identifier".to_string(), + raw: RawMsgChannelOpenTry { + counterparty_chosen_channel_id: "cpartyid34".to_string(), + ..default_raw_msg.clone() + }, + want_pass: true, + }, + Test { + name: "[Counterparty] Bad channel, name too short".to_string(), + raw: RawMsgChannelOpenTry { + counterparty_chosen_channel_id: "cparty".to_string(), + ..default_raw_msg.clone() }, want_pass: false, }, Test { - name: "Bad order".to_string(), - params: OpenTryParams { - order: 99, - ..default_params.clone() + name: "[Counterparty] Bad channel, name too long".to_string(), + raw: RawMsgChannelOpenTry { + counterparty_chosen_channel_id: "cpartydefghijkasdfasdfasdfasgdasdgasdfasdfadflmnoasdasdasdfasdfasdfasdfadadgadgadsfpqrstu".to_string(), + ..default_raw_msg.clone() }, want_pass: false, }, Test { - name: "Correct connection hops (connection id)".to_string(), - params: OpenTryParams { - connection_hops: vec!["connection124".to_string()].into_iter().collect(), - ..default_params.clone() + name: "Empty counterparty version (valid choice)".to_string(), + raw: RawMsgChannelOpenTry { + counterparty_version: " ".to_string(), + ..default_raw_msg.clone() }, want_pass: true, }, Test { - name: "Bad connection hops, connection id too long".to_string(), - params: OpenTryParams { - connection_hops: vec!["abcdefghadvvxvczxcvzxvxvzxvcsddsfsdsdfasdfasfasdasdgasdfasdfasdfadsfasdfijklmnopqrstu".to_string()] - .into_iter() - .collect(), - ..default_params.clone() + name: "Arbitrary counterparty version (valid choice)".to_string(), + raw: RawMsgChannelOpenTry { + counterparty_version: "anyversion".to_string(), + ..default_raw_msg.clone() }, - want_pass: false, + want_pass: true, }, Test { - name: "Bad connection hops, connection id too short".to_string(), - params: OpenTryParams { - connection_hops: vec!["connid".to_string()].into_iter().collect(), - ..default_params.clone() + name: "Bad proof height, height = 0".to_string(), + raw: RawMsgChannelOpenTry { + proof_height: Some(Height { + version_number: 0, + version_height: 0, + }), + ..default_raw_msg.clone() }, want_pass: false, }, - // Currently failing because we don't validate connection hops - // Test { - // name: "Bad connection hops, more than 1".to_string(), - // params: OpenTryParams { - // connection_hops: vec!["connectionhop".to_string(), "connectionhopnext".to_string()].into_iter().collect(), - // ..default_params.clone() - // }, - // want_pass: false, - // }, Test { - name: "Bad counterparty port, name too long".to_string(), - params: OpenTryParams { - counterparty_port_id: "abcdefgaszdsfgasdasdvsdfasdfasdfdfasdfasdfadsgasdfasdfasdfasdfasdfasdfhijklmnopqrstu".to_string(), - ..default_params.clone() + name: "Missing proof height".to_string(), + raw: RawMsgChannelOpenTry { + proof_height: None, + ..default_raw_msg.clone() }, want_pass: false, }, Test { - name: "Correct counterparty channel identifier".to_string(), - params: OpenTryParams { - counterparty_channel_id: "channelid34".to_string(), - ..default_params + name: "Missing proof init (object proof)".to_string(), + raw: RawMsgChannelOpenTry { + proof_init: vec![], + ..default_raw_msg }, - want_pass: true, + want_pass: false, }, ] .into_iter() .collect(); for test in tests { - let p = test.params.clone(); - - let msg = MsgChannelOpenTry::new( - p.port_id, - p.channel_id, - p.channel_version, - p.order, - p.connection_hops, - p.counterparty_port_id, - p.counterparty_channel_id, - p.counterparty_version, - p.proof_init, - p.proof_height, - acc, - ); + let res_msg = MsgChannelOpenTry::try_from(test.raw.clone()); assert_eq!( test.want_pass, - msg.is_ok(), - "MsgChanOpenTry::new failed for test {}, \nmsg {:?} with error {:?}", + res_msg.is_ok(), + "MsgChanOpenTry::try_from failed for test {}, \nraw msg {:?} with error {:?}", test.name, - test.params.clone(), - msg.err(), + test.raw, + res_msg.err(), ); } } + + #[test] + fn to_and_from() { + let raw = get_dummy_raw_msg_chan_open_try(10); + let msg = MsgChannelOpenTry::try_from(raw.clone()).unwrap(); + let raw_back = RawMsgChannelOpenTry::from(msg.clone()); + let msg_back = MsgChannelOpenTry::try_from(raw_back.clone()).unwrap(); + assert_eq!(raw, raw_back); + assert_eq!(msg, msg_back); + } } diff --git a/modules/src/ics04_channel/msgs/packet.rs b/modules/src/ics04_channel/msgs/packet.rs deleted file mode 100644 index 1e3b77f2f8..0000000000 --- a/modules/src/ics04_channel/msgs/packet.rs +++ /dev/null @@ -1,65 +0,0 @@ -use crate::ics04_channel::error::{Error, Kind}; -use crate::ics04_channel::packet::Packet; -use crate::ics23_commitment::commitment::CommitmentProof; -use crate::{proofs::Proofs, tx_msg::Msg, Height}; - -use tendermint::account::Id as AccountId; - -/// Message type for `MsgPacket`. -const TYPE_MSG_PACKET: &str = "ics04/opaque"; - -/// -/// Message definition for the packet wrapper domain type (`OpaquePacket` datagram). -/// -#[derive(Clone, Debug, PartialEq)] -pub struct MsgPacket { - packet: Packet, - proofs: Proofs, - signer: AccountId, -} - -impl MsgPacket { - pub fn new( - packet: Packet, - proof: CommitmentProof, - proof_height: Height, - signer: AccountId, - ) -> Result { - Ok(Self { - packet: packet - .validate() - .map_err(|e| Kind::InvalidPacket.context(e))?, - proofs: Proofs::new(proof, None, None, proof_height) - .map_err(|e| Kind::InvalidProof.context(e))?, - signer, - }) - } - - // returns the base64-encoded bytes used for the - // data field when signing the packet - pub fn get_data_bytes() -> Vec { - todo!() - } -} - -impl Msg for MsgPacket { - type ValidationError = Error; - - fn route(&self) -> String { - crate::keys::ROUTER_KEY.to_string() - } - - fn get_type(&self) -> String { - TYPE_MSG_PACKET.to_string() - } - - fn validate_basic(&self) -> Result<(), Self::ValidationError> { - // Nothing to validate - // All the validation is performed on creation - Ok(()) - } - - fn get_signers(&self) -> Vec { - vec![self.signer] - } -} diff --git a/modules/src/ics04_channel/msgs/recv_packet.rs b/modules/src/ics04_channel/msgs/recv_packet.rs new file mode 100644 index 0000000000..57515976be --- /dev/null +++ b/modules/src/ics04_channel/msgs/recv_packet.rs @@ -0,0 +1,112 @@ +use crate::address::{account_to_string, string_to_account}; +use crate::ics04_channel::error::{Error, Kind}; +use crate::ics04_channel::packet::Packet; +use crate::ics23_commitment::commitment::CommitmentProof; +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; + +/// Message type for `MsgPacket`. +const TYPE_MSG_PACKET: &str = "ics04/opaque"; + +/// +/// Message definition for the packet wrapper domain type (`OpaquePacket` datagram). +/// This whole module is WIP: https://github.com/informalsystems/ibc-rs/issues/95. +/// +#[derive(Clone, Debug, PartialEq)] +pub struct MsgRecvPacket { + packet: Packet, + proofs: Proofs, + signer: AccountId, +} + +impl MsgRecvPacket { + pub fn new( + packet: Packet, + proof: CommitmentProof, + proof_height: Height, + signer: AccountId, + ) -> Result { + Ok(Self { + packet: packet + .validate() + .map_err(|e| Kind::InvalidPacket.context(e))?, + proofs: Proofs::new(proof, None, None, proof_height) + .map_err(|e| Kind::InvalidProof.context(e))?, + signer, + }) + } + + // returns the base64-encoded bytes used for the + // data field when signing the packet + pub fn get_data_bytes() -> Vec { + todo!() + } +} + +impl Msg for MsgRecvPacket { + type ValidationError = Error; + + fn route(&self) -> String { + crate::keys::ROUTER_KEY.to_string() + } + + fn get_type(&self) -> String { + TYPE_MSG_PACKET.to_string() + } + + fn validate_basic(&self) -> Result<(), Self::ValidationError> { + // Nothing to validate + // All the validation is performed on creation + Ok(()) + } + + fn get_signers(&self) -> Vec { + vec![self.signer] + } +} + +impl DomainType for MsgRecvPacket {} + +impl TryFrom for MsgRecvPacket { + type Error = anomaly::Error; + + // This depends on Packet domain type (not implemented yet). + #[allow(unreachable_code, unused_variables)] + fn try_from(raw_msg: RawMsgRecvPacket) -> Result { + let signer = + string_to_account(raw_msg.signer).map_err(|e| Kind::InvalidSigner.context(e))?; + + let proofs = Proofs::new( + raw_msg.proof.into(), + None, + None, + raw_msg + .proof_height + .ok_or_else(|| Kind::MissingHeight)? + .try_into() + .map_err(|e| Kind::InvalidProof.context(e))?, + ) + .map_err(|e| Kind::InvalidProof.context(e))?; + + Ok(MsgRecvPacket { + packet: todo!(), + proofs, + signer, + }) + } +} + +impl From for RawMsgRecvPacket { + fn from(domain_msg: MsgRecvPacket) -> Self { + RawMsgRecvPacket { + packet: None, // TODO: Should be: `Some(domain_msg.packet.into())`. + proof: domain_msg.proofs.object_proof().clone().into(), + proof_height: Some(domain_msg.proofs.height().into()), + signer: account_to_string(domain_msg.signer).unwrap(), + } + } +} diff --git a/modules/src/ics04_channel/msgs/timeout.rs b/modules/src/ics04_channel/msgs/timeout.rs index ac91049ccd..9e4a237d2e 100644 --- a/modules/src/ics04_channel/msgs/timeout.rs +++ b/modules/src/ics04_channel/msgs/timeout.rs @@ -1,9 +1,14 @@ +use crate::address::{account_to_string, string_to_account}; use crate::ics04_channel::error::{Error, Kind}; use crate::ics04_channel::packet::Packet; use crate::ics23_commitment::commitment::CommitmentProof; 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 std::convert::TryFrom; /// Message type for the `MsgTimeout` message. const TYPE_MSG_TIMEOUT: &str = "ics04/timeout"; @@ -60,3 +65,34 @@ impl Msg for MsgTimeout { vec![self.signer] } } + +impl DomainType for MsgTimeout {} + +#[allow(unreachable_code, unused_variables)] +impl TryFrom for MsgTimeout { + type Error = anomaly::Error; + + fn try_from(raw_msg: RawMsgTimeout) -> Result { + let signer = + string_to_account(raw_msg.signer).map_err(|e| Kind::InvalidSigner.context(e))?; + + Ok(MsgTimeout { + packet: todo!(), + next_sequence_recv: None, + signer, + proofs: todo!(), + }) + } +} + +impl From for RawMsgTimeout { + fn from(domain_msg: MsgTimeout) -> Self { + RawMsgTimeout { + packet: None, + proof: vec![], + proof_height: None, + next_sequence_recv: 0, + signer: account_to_string(domain_msg.signer).unwrap(), + } + } +} diff --git a/modules/src/ics04_channel/packet.rs b/modules/src/ics04_channel/packet.rs index 52964159cd..c2d6dcc369 100644 --- a/modules/src/ics04_channel/packet.rs +++ b/modules/src/ics04_channel/packet.rs @@ -2,6 +2,7 @@ use crate::ics04_channel::error::Error; // TODO: Packet needs to be implemented // This is only a workaround for MsgPacket for now! +// https://github.com/informalsystems/ibc-rs/issues/95 #[derive(Clone, Debug, PartialEq)] pub struct Packet;