From 1d63ca95f9866b406cef5c90663f0ac2cd197aef Mon Sep 17 00:00:00 2001 From: Vitor Enes Date: Mon, 22 Feb 2021 14:26:41 +0100 Subject: [PATCH 1/8] Allow a conn open ack to succeed --- CHANGELOG.md | 4 +- modules/src/ics02_client/client_def.rs | 4 +- modules/src/ics03_connection/context.rs | 5 +-- modules/src/ics03_connection/error.rs | 2 +- .../ics03_connection/handler/conn_open_ack.rs | 41 +++++++++++++------ .../ics03_connection/handler/conn_open_try.rs | 7 ++-- .../src/ics03_connection/handler/verify.rs | 9 ++-- modules/src/ics07_tendermint/client_def.rs | 2 +- modules/src/mock/client_def.rs | 2 +- modules/src/mock/context.rs | 10 ++--- 10 files changed, 49 insertions(+), 37 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b3b2a8d154..009b80208e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ - Consistent identifier handling across ICS 02, 03 and 04 ([#622]) - [ibc-relayer] + - [nothing yet] - [ibc-relayer-cli] - [nothing yet] @@ -28,7 +29,7 @@ ### BUG FIXES - [ibc] - - [nothing yet] + - Fix overflow bug in ICS03 client consensus height verification method ([#685]) - [ibc-relayer] - [nothing yet] @@ -47,6 +48,7 @@ - [ibc-relayer-cli] - [nothing yet] +[#685]: https://github.com/informalsystems/ibc-rs/issues/685 [#689]: https://github.com/informalsystems/ibc-rs/issues/689 ## v0.1.1 diff --git a/modules/src/ics02_client/client_def.rs b/modules/src/ics02_client/client_def.rs index d2a64f944c..d4b613daa1 100644 --- a/modules/src/ics02_client/client_def.rs +++ b/modules/src/ics02_client/client_def.rs @@ -73,7 +73,7 @@ pub trait ClientDef: Clone { height: Height, prefix: &CommitmentPrefix, proof: &CommitmentProofBytes, - connection_id: &ConnectionId, + connection_id: Option<&ConnectionId>, expected_connection_end: &ConnectionEnd, ) -> Result<(), Box>; @@ -472,7 +472,7 @@ impl ClientDef for AnyClient { height: Height, prefix: &CommitmentPrefix, proof: &CommitmentProofBytes, - connection_id: &ConnectionId, + connection_id: Option<&ConnectionId>, expected_connection_end: &ConnectionEnd, ) -> Result<(), Box> { match self { diff --git a/modules/src/ics03_connection/context.rs b/modules/src/ics03_connection/context.rs index 37ba52c62b..ba35c6ab5b 100644 --- a/modules/src/ics03_connection/context.rs +++ b/modules/src/ics03_connection/context.rs @@ -22,9 +22,8 @@ pub trait ConnectionReader { /// Returns the current height of the local chain. fn host_current_height(&self) -> Height; - /// Returns the number of consensus state entries that the local chain maintains. The history - /// size determines the pruning window of the host chain. - fn host_chain_history_size(&self) -> usize; + /// Returns the oldest height available on the local chain. + fn host_oldest_height(&self) -> Height; /// Returns the prefix that the local chain uses in the KV store. fn commitment_prefix(&self) -> CommitmentPrefix; diff --git a/modules/src/ics03_connection/error.rs b/modules/src/ics03_connection/error.rs index af1a64ad5e..b9c4e7e773 100644 --- a/modules/src/ics03_connection/error.rs +++ b/modules/src/ics03_connection/error.rs @@ -23,7 +23,7 @@ pub enum Kind { #[error("consensus height claimed by the client on the other party is too advanced: {0} (host chain current height: {1})")] InvalidConsensusHeight(Height, Height), - #[error("consensus height claimed by the client on the other party falls outside of trusting period: {0} (host chain current height: {1})")] + #[error("consensus height claimed by the client on the other party has been pruned: {0} (host chain oldest height: {1})")] StaleConsensusHeight(Height, Height), #[error("identifier error")] diff --git a/modules/src/ics03_connection/handler/conn_open_ack.rs b/modules/src/ics03_connection/handler/conn_open_ack.rs index 218c4f78b4..0834e23d08 100644 --- a/modules/src/ics03_connection/handler/conn_open_ack.rs +++ b/modules/src/ics03_connection/handler/conn_open_ack.rs @@ -30,10 +30,19 @@ pub(crate) fn process( || old_conn_end.state_matches(&State::TryOpen) && old_conn_end.versions().get(0).eq(&Some(msg.version())); - // Check that if the msg's counterparty connection id is not empty then it matches - // the old connection's counterparty. - let counterparty_matches = msg.counterparty_connection_id().is_none() - || old_conn_end.counterparty().connection_id() == msg.counterparty_connection_id(); + // Check that if we have a counterparty connection id, then it + // matches the one in the message + let counterparty_matches = if let Some(counterparty_connection_id) = + old_conn_end.counterparty().connection_id() + { + // it's okay to unwrap as `msg.counterparty_connection_id` + // should always be set + // TODO: `msg.counterparty_connection_id` should simply be a + // `ConnectionId`, not an `Option` + msg.counterparty_connection_id.as_ref().unwrap() == counterparty_connection_id + } else { + true + }; if state_is_consistent && counterparty_matches { Ok(old_conn_end) @@ -111,7 +120,6 @@ mod tests { use crate::ics24_host::identifier::{ChainId, ClientId}; use crate::mock::context::MockContext; use crate::mock::host::HostType; - use crate::Height; #[test] fn conn_open_ack_msg_processing() { @@ -133,11 +141,13 @@ mod tests { // Parametrize the host chain to have a height at least as recent as the // the height of the proofs in the Ack msg. + let latest_height = proof_height.increment(); + let max_history_size = 5; let default_context = MockContext::new( - ChainId::new("mockgaia".to_string(), 1), + ChainId::new("mockgaia".to_string(), latest_height.revision_number), HostType::Mock, - 5, - Height::new(1, msg_ack.proofs().height().increment().revision_height), + max_history_size, + latest_height, ); // A connection end that will exercise the successful path. @@ -183,14 +193,14 @@ mod tests { ctx: default_context .clone() .with_client(&client_id, proof_height) - .with_connection(conn_id.clone(), default_conn_end.clone()), + .with_connection(conn_id.clone(), default_conn_end), msg: ConnectionMsg::ConnectionOpenAck(Box::new(msg_ack.clone())), want_pass: true, error_kind: None, }, Test { name: "Processing fails because the connection does not exist in the context".to_string(), - ctx: MockContext::default(), // Empty context + ctx: default_context.clone(), msg: ConnectionMsg::ConnectionOpenAck(Box::new(msg_ack.clone())), want_pass: false, error_kind: Some(Kind::UninitializedConnection(conn_id.clone())), @@ -220,10 +230,11 @@ mod tests { ctx: default_context .with_client(&client_id, proof_height) .with_connection(conn_id.clone(), conn_end_cparty), - msg: ConnectionMsg::ConnectionOpenAck(Box::new(msg_ack.clone())), + msg: ConnectionMsg::ConnectionOpenAck(Box::new(msg_ack)), want_pass: false, - error_kind: Some(Kind::ConnectionMismatch(conn_id.clone())) + error_kind: Some(Kind::ConnectionMismatch(conn_id)) }, + /* Test { name: "Processing fails due to MissingLocalConsensusState".to_string(), ctx: MockContext::default() @@ -233,6 +244,7 @@ mod tests { want_pass: false, error_kind: Some(Kind::MissingLocalConsensusState) }, + */ ]; for test in tests { @@ -273,7 +285,10 @@ mod tests { assert_eq!( &expected_kind, e.kind(), - "conn_open_ack: expected error kind mismatches thrown error kind" + "conn_open_ack: failed for test: {}\nexpected error kind: {:?}\nfound: {:?}", + test.name, + expected_kind, + e.kind() ) } } diff --git a/modules/src/ics03_connection/handler/conn_open_try.rs b/modules/src/ics03_connection/handler/conn_open_try.rs index 720e635d04..75bcadaec7 100644 --- a/modules/src/ics03_connection/handler/conn_open_try.rs +++ b/modules/src/ics03_connection/handler/conn_open_try.rs @@ -125,7 +125,6 @@ mod tests { use crate::events::IbcEvent; use crate::ics03_connection::connection::State; - use crate::ics03_connection::context::ConnectionReader; use crate::ics03_connection::handler::{dispatch, ConnectionResult}; use crate::ics03_connection::msgs::conn_open_try::test_util::get_dummy_raw_msg_conn_open_try; use crate::ics03_connection::msgs::conn_open_try::MsgConnectionOpenTry; @@ -145,13 +144,13 @@ mod tests { } let host_chain_height = Height::new(0, 35); + let max_history_size = 5; let context = MockContext::new( ChainId::new("mockgaia".to_string(), 0), HostType::Mock, - 5, + max_history_size, host_chain_height, ); - let pruning_window = context.host_chain_history_size() as u64; let client_consensus_state_height = 10; let msg_conn_try = MsgConnectionOpenTry::try_from(get_dummy_raw_msg_conn_open_try( @@ -167,7 +166,7 @@ mod tests { )) .unwrap(); let pruned_height = host_chain_height - .sub(pruning_window + 1) + .sub(max_history_size as u64 + 1) .unwrap() .revision_height; // The consensus proof targets a missing height (pruned) on destination chain. diff --git a/modules/src/ics03_connection/handler/verify.rs b/modules/src/ics03_connection/handler/verify.rs index c26c52679a..7b9c76a7fd 100644 --- a/modules/src/ics03_connection/handler/verify.rs +++ b/modules/src/ics03_connection/handler/verify.rs @@ -96,7 +96,7 @@ pub fn verify_connection_proof( proof_height, connection_end.counterparty().prefix(), proof, - &connection_end.counterparty().connection_id().unwrap(), + connection_end.counterparty().connection_id(), expected_conn, ) .map_err(|_| Kind::InvalidProof)?) @@ -197,12 +197,9 @@ pub fn check_client_consensus_height( return Err(Kind::InvalidConsensusHeight(claimed_height, ctx.host_current_height()).into()); } - let oldest_available_height = - ctx.host_current_height().revision_height - ctx.host_chain_history_size() as u64; - - if claimed_height.revision_height < oldest_available_height { + if claimed_height < ctx.host_oldest_height() { // Fail if the consensus height is too old (has been pruned). - return Err(Kind::StaleConsensusHeight(claimed_height, ctx.host_current_height()).into()); + return Err(Kind::StaleConsensusHeight(claimed_height, ctx.host_oldest_height()).into()); } // Height check is within normal bounds, check passes. diff --git a/modules/src/ics07_tendermint/client_def.rs b/modules/src/ics07_tendermint/client_def.rs index b11eba1405..fedccc7fe0 100644 --- a/modules/src/ics07_tendermint/client_def.rs +++ b/modules/src/ics07_tendermint/client_def.rs @@ -57,7 +57,7 @@ impl ClientDef for TendermintClient { _height: Height, _prefix: &CommitmentPrefix, _proof: &CommitmentProofBytes, - _connection_id: &ConnectionId, + _connection_id: Option<&ConnectionId>, _expected_connection_end: &ConnectionEnd, ) -> Result<(), Box> { todo!() diff --git a/modules/src/mock/client_def.rs b/modules/src/mock/client_def.rs index e7e13b6b33..ec31ff9caa 100644 --- a/modules/src/mock/client_def.rs +++ b/modules/src/mock/client_def.rs @@ -64,7 +64,7 @@ impl ClientDef for MockClient { _height: Height, _prefix: &CommitmentPrefix, _proof: &CommitmentProofBytes, - _connection_id: &ConnectionId, + _connection_id: Option<&ConnectionId>, _expected_connection_end: &ConnectionEnd, ) -> Result<(), Box> { Ok(()) diff --git a/modules/src/mock/context.rs b/modules/src/mock/context.rs index 33c5a0de22..b685e31d71 100644 --- a/modules/src/mock/context.rs +++ b/modules/src/mock/context.rs @@ -102,10 +102,10 @@ pub struct MockContext { impl Default for MockContext { fn default() -> Self { Self::new( - ChainId::new("mockgaia".to_string(), 1), + ChainId::new("mockgaia".to_string(), 0), HostType::Mock, 5, - Height::new(1, 5), + Height::new(0, 5), ) } } @@ -450,9 +450,9 @@ impl ConnectionReader for MockContext { self.latest_height } - /// Returns the number of consensus state historical entries for the local chain. - fn host_chain_history_size(&self) -> usize { - self.max_history_size + fn host_oldest_height(&self) -> Height { + // history must be non-empty, so `self.history[0]` is valid + self.history[0].height() } fn commitment_prefix(&self) -> CommitmentPrefix { From b889b7bf5980ddb822f732eb61b2b908a6027c5d Mon Sep 17 00:00:00 2001 From: Vitor Enes Date: Mon, 22 Feb 2021 14:31:53 +0100 Subject: [PATCH 2/8] Remove invalid test --- .../ics03_connection/handler/conn_open_ack.rs | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/modules/src/ics03_connection/handler/conn_open_ack.rs b/modules/src/ics03_connection/handler/conn_open_ack.rs index 0834e23d08..a65e63cdc0 100644 --- a/modules/src/ics03_connection/handler/conn_open_ack.rs +++ b/modules/src/ics03_connection/handler/conn_open_ack.rs @@ -177,16 +177,6 @@ mod tests { CommitmentPrefix::from(vec![]), // incorrect field )); - // A connection end with correct state & prefix, but incorrect counterparty; exercises - // unsuccessful processing path. - let mut conn_end_cparty = conn_end_open.clone(); - conn_end_cparty.set_state(State::Init); - conn_end_cparty.set_counterparty(Counterparty::new( - client_id.clone(), - None, // incorrect field - CommitmentPrefix::from(b"ibc".to_vec()), - )); - let tests: Vec = vec![ Test { name: "Successful processing of an Ack message".to_string(), @@ -225,15 +215,6 @@ mod tests { want_pass: false, error_kind: Some(Kind::ConsensusStateVerificationFailure(proof_height)) }, - Test { - name: "Processing fails due to mismatching counterparty conn id".to_string(), - ctx: default_context - .with_client(&client_id, proof_height) - .with_connection(conn_id.clone(), conn_end_cparty), - msg: ConnectionMsg::ConnectionOpenAck(Box::new(msg_ack)), - want_pass: false, - error_kind: Some(Kind::ConnectionMismatch(conn_id)) - }, /* Test { name: "Processing fails due to MissingLocalConsensusState".to_string(), From a173dcd87d7ef06ac5433f0c9edbbd6c2e135805 Mon Sep 17 00:00:00 2001 From: Vitor Enes Date: Mon, 22 Feb 2021 14:57:38 +0100 Subject: [PATCH 3/8] update CHANGELOG --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 009b80208e..60ea2a6bdb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,6 +30,7 @@ - [ibc] - Fix overflow bug in ICS03 client consensus height verification method ([#685]) + - Allow a conn open ack to succeed in the happy case ([#699]) - [ibc-relayer] - [nothing yet] @@ -49,7 +50,7 @@ - [nothing yet] [#685]: https://github.com/informalsystems/ibc-rs/issues/685 -[#689]: https://github.com/informalsystems/ibc-rs/issues/689 +[#699]: https://github.com/informalsystems/ibc-rs/issues/699 ## v0.1.1 *February 17, 2021* From d25cc7b248aeb7d2a9407614f7abd351add0a851 Mon Sep 17 00:00:00 2001 From: Vitor Enes Date: Mon, 22 Feb 2021 14:58:47 +0100 Subject: [PATCH 4/8] fix CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 60ea2a6bdb..22822d149a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -50,6 +50,7 @@ - [nothing yet] [#685]: https://github.com/informalsystems/ibc-rs/issues/685 +[#689]: https://github.com/informalsystems/ibc-rs/issues/689 [#699]: https://github.com/informalsystems/ibc-rs/issues/699 ## v0.1.1 From 731dcc92077bdedb1daf15c36de9b516076eaaa2 Mon Sep 17 00:00:00 2001 From: Vitor Enes Date: Mon, 22 Feb 2021 15:39:34 +0100 Subject: [PATCH 5/8] Fix clippy --- modules/src/ics03_connection/handler/conn_open_ack.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/modules/src/ics03_connection/handler/conn_open_ack.rs b/modules/src/ics03_connection/handler/conn_open_ack.rs index a65e63cdc0..fdc2a2bda2 100644 --- a/modules/src/ics03_connection/handler/conn_open_ack.rs +++ b/modules/src/ics03_connection/handler/conn_open_ack.rs @@ -208,10 +208,9 @@ mod tests { Test { name: "Processing fails: ConsensusStateVerificationFailure due to empty counterparty prefix".to_string(), ctx: default_context - .clone() .with_client(&client_id, proof_height) - .with_connection(conn_id.clone(), conn_end_prefix), - msg: ConnectionMsg::ConnectionOpenAck(Box::new(msg_ack.clone())), + .with_connection(conn_id, conn_end_prefix), + msg: ConnectionMsg::ConnectionOpenAck(Box::new(msg_ack)), want_pass: false, error_kind: Some(Kind::ConsensusStateVerificationFailure(proof_height)) }, From 53e2a08b0e0d6f1ef6d8677cc32896bdcb22f0ac Mon Sep 17 00:00:00 2001 From: Vitor Enes Date: Mon, 22 Feb 2021 17:54:45 +0100 Subject: [PATCH 6/8] Make MsgConnectionOpenAck.counterparty_connection_id not an Option --- .../ics03_connection/handler/conn_open_ack.rs | 18 ++++++--------- .../ics03_connection/msgs/conn_open_ack.rs | 22 +++++++------------ 2 files changed, 15 insertions(+), 25 deletions(-) diff --git a/modules/src/ics03_connection/handler/conn_open_ack.rs b/modules/src/ics03_connection/handler/conn_open_ack.rs index fdc2a2bda2..e4552a7962 100644 --- a/modules/src/ics03_connection/handler/conn_open_ack.rs +++ b/modules/src/ics03_connection/handler/conn_open_ack.rs @@ -30,16 +30,12 @@ pub(crate) fn process( || old_conn_end.state_matches(&State::TryOpen) && old_conn_end.versions().get(0).eq(&Some(msg.version())); - // Check that if we have a counterparty connection id, then it - // matches the one in the message + // Check that, if we have a counterparty connection id, then it + // matches the one in the message. let counterparty_matches = if let Some(counterparty_connection_id) = old_conn_end.counterparty().connection_id() { - // it's okay to unwrap as `msg.counterparty_connection_id` - // should always be set - // TODO: `msg.counterparty_connection_id` should simply be a - // `ConnectionId`, not an `Option` - msg.counterparty_connection_id.as_ref().unwrap() == counterparty_connection_id + &msg.counterparty_connection_id == counterparty_connection_id } else { true }; @@ -68,8 +64,8 @@ pub(crate) fn process( Counterparty::new( // The counterparty is the local chain. new_conn_end.client_id().clone(), // The local client identifier. - msg.counterparty_connection_id().cloned(), // This chain's connection id as known on counterparty. - ctx.commitment_prefix(), // Local commitment prefix. + Some(msg.counterparty_connection_id().clone()), // This chain's connection id as known on counterparty. + ctx.commitment_prefix(), // Local commitment prefix. ), vec![msg.version().clone()], new_conn_end.delay_period, @@ -156,7 +152,7 @@ mod tests { client_id.clone(), Counterparty::new( client_id.clone(), - msg_ack.counterparty_connection_id().cloned(), + Some(msg_ack.counterparty_connection_id().clone()), CommitmentPrefix::from(b"ibc".to_vec()), ), vec![msg_ack.version().clone()], @@ -173,7 +169,7 @@ mod tests { conn_end_prefix.set_state(State::Init); conn_end_prefix.set_counterparty(Counterparty::new( client_id.clone(), - msg_ack.counterparty_connection_id().cloned(), + Some(msg_ack.counterparty_connection_id().clone()), CommitmentPrefix::from(vec![]), // incorrect field )); diff --git a/modules/src/ics03_connection/msgs/conn_open_ack.rs b/modules/src/ics03_connection/msgs/conn_open_ack.rs index 1f0d57ac6d..670ed8dbe5 100644 --- a/modules/src/ics03_connection/msgs/conn_open_ack.rs +++ b/modules/src/ics03_connection/msgs/conn_open_ack.rs @@ -1,5 +1,4 @@ use std::convert::{TryFrom, TryInto}; -use std::str::FromStr; use tendermint::account::Id as AccountId; use tendermint_proto::Protobuf; @@ -22,7 +21,7 @@ pub const TYPE_URL: &str = "/ibc.core.connection.v1.MsgConnectionOpenAck"; #[derive(Clone, Debug, PartialEq, Eq)] pub struct MsgConnectionOpenAck { pub connection_id: ConnectionId, - pub counterparty_connection_id: Option, + pub counterparty_connection_id: ConnectionId, pub client_state: Option, pub proofs: Proofs, pub version: Version, @@ -36,8 +35,8 @@ impl MsgConnectionOpenAck { } /// Getter for accessing the counterparty's connection identifier from this message. - pub fn counterparty_connection_id(&self) -> Option<&ConnectionId> { - self.counterparty_connection_id.as_ref() + pub fn counterparty_connection_id(&self) -> &ConnectionId { + &self.counterparty_connection_id } /// Getter for accessing the client state. @@ -107,18 +106,15 @@ impl TryFrom for MsgConnectionOpenAck { .filter(|x| !x.is_empty()) .map(CommitmentProofBytes::from); - let counterparty_connection_id = Some(msg.counterparty_connection_id) - .filter(|x| !x.is_empty()) - .map(|v| FromStr::from_str(v.as_str())) - .transpose() - .map_err(|e| Kind::IdentifierError.context(e))?; - Ok(Self { connection_id: msg .connection_id .parse() .map_err(|e| Kind::IdentifierError.context(e))?, - counterparty_connection_id, + counterparty_connection_id: msg + .counterparty_connection_id + .parse() + .map_err(|e| Kind::IdentifierError.context(e))?, client_state: msg .client_state .map(AnyClientState::try_from) @@ -146,9 +142,7 @@ impl From for RawMsgConnectionOpenAck { fn from(ics_msg: MsgConnectionOpenAck) -> Self { RawMsgConnectionOpenAck { connection_id: ics_msg.connection_id.as_str().to_string(), - counterparty_connection_id: ics_msg - .counterparty_connection_id - .map_or_else(|| "".to_string(), |v| v.as_str().to_string()), + counterparty_connection_id: ics_msg.counterparty_connection_id.as_str().to_string(), client_state: ics_msg .client_state .map_or_else(|| None, |v| Some(v.into())), From dda8c6f943612d018b2d8b12563b7f204dd29705 Mon Sep 17 00:00:00 2001 From: Vitor Enes Date: Mon, 22 Feb 2021 18:02:25 +0100 Subject: [PATCH 7/8] Make MsgConnectionOpenAck.counterparty_connection_id not an Option --- relayer/src/connection.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/relayer/src/connection.rs b/relayer/src/connection.rs index 2b23a3e9a6..7e56bc8abc 100644 --- a/relayer/src/connection.rs +++ b/relayer/src/connection.rs @@ -566,7 +566,7 @@ impl Connection { let new_msg = MsgConnectionOpenAck { connection_id: self.dst_connection_id().clone(), - counterparty_connection_id: Option::from(self.src_connection_id().clone()), + counterparty_connection_id: self.src_connection_id().clone(), client_state, proofs, version: src_connection.versions()[0].clone(), From a5403a8d08b2e3b84f3b7b0a1e618f9e2832d153 Mon Sep 17 00:00:00 2001 From: Vitor Enes Date: Tue, 23 Feb 2021 12:28:05 +0100 Subject: [PATCH 8/8] update CHANGELOG --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 22822d149a..bcdf1e2e88 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,7 +41,7 @@ ### BREAKING CHANGES - [ibc] - - [nothing yet] + - `MsgConnectionOpenAck.counterparty_connection_id` is now a `ConnectionId` instead of an `Option`([#700]) - [ibc-relayer] - [nothing yet] @@ -52,6 +52,7 @@ [#685]: https://github.com/informalsystems/ibc-rs/issues/685 [#689]: https://github.com/informalsystems/ibc-rs/issues/689 [#699]: https://github.com/informalsystems/ibc-rs/issues/699 +[#700]: https://github.com/informalsystems/ibc-rs/pull/700 ## v0.1.1 *February 17, 2021*