diff --git a/CHANGELOG.md b/CHANGELOG.md index eb9f13fd013..82a0de6198f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -45,6 +45,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ * (core) [\#629](https://github.com/cosmos/ibc-go/pull/629) Removes the GetProofSpecs from the ClientState interface. This function was previously unused by core IBC. * (transfer) [\#517](https://github.com/cosmos/ibc-go/pull/517) Separates the ICS 26 callback functions from `AppModule` into a new type `IBCModule` for ICS 20 transfer. * (modules/core/02-client) [\#536](https://github.com/cosmos/ibc-go/pull/536) GetSelfConsensusState return type changed from bool to error. +* (channel) [\#644](https://github.com/cosmos/ibc-go/pull/644) Removes `CounterpartyHops` function from the ChannelKeeper. ### State Machine Breaking @@ -52,6 +53,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ * [\#383](https://github.com/cosmos/ibc-go/pull/383) Adds helper functions for merging and splitting middleware versions from the underlying app version. * (modules/core/05-port) [\#288](https://github.com/cosmos/ibc-go/issues/288) Making the 05-port keeper function IsBound public. The IsBound function checks if the provided portID is already binded to a module. +* (channel) [\#644](https://github.com/cosmos/ibc-go/pull/644) Adds `GetChannelConnection` to the ChannelKeeper. This function returns the connectionID and connection state associated with a channel. ### Features diff --git a/modules/apps/27-interchain-accounts/controller/keeper/handshake.go b/modules/apps/27-interchain-accounts/controller/keeper/handshake.go index c2c510e0e2c..66f0720f66c 100644 --- a/modules/apps/27-interchain-accounts/controller/keeper/handshake.go +++ b/modules/apps/27-interchain-accounts/controller/keeper/handshake.go @@ -105,24 +105,19 @@ func (k Keeper) OnChanCloseConfirm( // validateControllerPortParams asserts the provided connection sequence and counterparty connection sequence // match that of the associated connection stored in state func (k Keeper) validateControllerPortParams(ctx sdk.Context, channelID, portID string, connectionSeq, counterpartyConnectionSeq uint64) error { - channel, found := k.channelKeeper.GetChannel(ctx, portID, channelID) - if !found { - return sdkerrors.Wrapf(channeltypes.ErrChannelNotFound, "port ID %s channel ID %s", portID, channelID) - } - - counterpartyHops, found := k.channelKeeper.CounterpartyHops(ctx, channel) - if !found { - return sdkerrors.Wrap(connectiontypes.ErrConnectionNotFound, channel.ConnectionHops[0]) + connectionID, connection, err := k.channelKeeper.GetChannelConnection(ctx, portID, channelID) + if err != nil { + return err } - connSeq, err := connectiontypes.ParseConnectionSequence(channel.ConnectionHops[0]) + connSeq, err := connectiontypes.ParseConnectionSequence(connectionID) if err != nil { - return sdkerrors.Wrapf(err, "failed to parse connection sequence %s", channel.ConnectionHops[0]) + return sdkerrors.Wrapf(err, "failed to parse connection sequence %s", connectionID) } - counterpartyConnSeq, err := connectiontypes.ParseConnectionSequence(counterpartyHops[0]) + counterpartyConnSeq, err := connectiontypes.ParseConnectionSequence(connection.GetCounterparty().GetConnectionID()) if err != nil { - return sdkerrors.Wrapf(err, "failed to parse counterparty connection sequence %s", counterpartyHops[0]) + return sdkerrors.Wrapf(err, "failed to parse counterparty connection sequence %s", connection.GetCounterparty().GetConnectionID()) } if connSeq != connectionSeq { diff --git a/modules/apps/27-interchain-accounts/host/keeper/handshake.go b/modules/apps/27-interchain-accounts/host/keeper/handshake.go index a223020ee18..3c19c931122 100644 --- a/modules/apps/27-interchain-accounts/host/keeper/handshake.go +++ b/modules/apps/27-interchain-accounts/host/keeper/handshake.go @@ -105,24 +105,19 @@ func (k Keeper) OnChanCloseConfirm( // validateControllerPortParams asserts the provided connection sequence and counterparty connection sequence // match that of the associated connection stored in state func (k Keeper) validateControllerPortParams(ctx sdk.Context, channelID, portID string, connectionSeq, counterpartyConnectionSeq uint64) error { - channel, found := k.channelKeeper.GetChannel(ctx, portID, channelID) - if !found { - return sdkerrors.Wrapf(channeltypes.ErrChannelNotFound, "port ID %s channel ID %s", portID, channelID) - } - - counterpartyHops, found := k.channelKeeper.CounterpartyHops(ctx, channel) - if !found { - return sdkerrors.Wrap(connectiontypes.ErrConnectionNotFound, channel.ConnectionHops[0]) + connectionID, connection, err := k.channelKeeper.GetChannelConnection(ctx, portID, channelID) + if err != nil { + return err } - connSeq, err := connectiontypes.ParseConnectionSequence(channel.ConnectionHops[0]) + connSeq, err := connectiontypes.ParseConnectionSequence(connectionID) if err != nil { - return sdkerrors.Wrapf(err, "failed to parse connection sequence %s", channel.ConnectionHops[0]) + return sdkerrors.Wrapf(err, "failed to parse connection sequence %s", connectionID) } - counterpartyConnSeq, err := connectiontypes.ParseConnectionSequence(counterpartyHops[0]) + counterpartyConnSeq, err := connectiontypes.ParseConnectionSequence(connection.GetCounterparty().GetConnectionID()) if err != nil { - return sdkerrors.Wrapf(err, "failed to parse counterparty connection sequence %s", counterpartyHops[0]) + return sdkerrors.Wrapf(err, "failed to parse counterparty connection sequence %s", connection.GetCounterparty().GetConnectionID()) } if connSeq != connectionSeq { diff --git a/modules/apps/27-interchain-accounts/types/expected_keepers.go b/modules/apps/27-interchain-accounts/types/expected_keepers.go index 493debeec66..ba782e1d2f1 100644 --- a/modules/apps/27-interchain-accounts/types/expected_keepers.go +++ b/modules/apps/27-interchain-accounts/types/expected_keepers.go @@ -27,7 +27,7 @@ type ICS4Wrapper interface { type ChannelKeeper interface { GetChannel(ctx sdk.Context, srcPort, srcChan string) (channel channeltypes.Channel, found bool) GetNextSequenceSend(ctx sdk.Context, portID, channelID string) (uint64, bool) - CounterpartyHops(ctx sdk.Context, channel channeltypes.Channel) ([]string, bool) + GetChannelConnection(ctx sdk.Context, portID, channelID string) (string, ibcexported.ConnectionI, error) } // PortKeeper defines the expected IBC port keeper diff --git a/modules/core/04-channel/keeper/handshake.go b/modules/core/04-channel/keeper/handshake.go index 6b0b81df077..f1fa5eafcb6 100644 --- a/modules/core/04-channel/keeper/handshake.go +++ b/modules/core/04-channel/keeper/handshake.go @@ -5,6 +5,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" + connectiontypes "github.com/cosmos/ibc-go/v3/modules/core/03-connection/types" "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" porttypes "github.com/cosmos/ibc-go/v3/modules/core/05-port/types" @@ -12,27 +13,6 @@ import ( "github.com/cosmos/ibc-go/v3/modules/core/exported" ) -// CounterpartyHops returns the connection hops of the counterparty channel. -// The counterparty hops are stored in the inverse order as the channel's. -// NOTE: Since connectionHops only supports single connection channels for now, -// this function requires that connection hops only contain a single connection id -func (k Keeper) CounterpartyHops(ctx sdk.Context, ch types.Channel) ([]string, bool) { - // Return empty array if connection hops is more than one - // ConnectionHops length should be verified earlier - if len(ch.ConnectionHops) != 1 { - return []string{}, false - } - counterpartyHops := make([]string, 1) - hop := ch.ConnectionHops[0] - conn, found := k.connectionKeeper.GetConnection(ctx, hop) - if !found { - return []string{}, false - } - - counterpartyHops[0] = conn.GetCounterparty().GetConnectionID() - return counterpartyHops, true -} - // ChanOpenInit is called by a module to initiate a channel opening handshake with // a module on another chain. The counterparty channel identifier is validated to be // empty in msg validation. @@ -127,6 +107,11 @@ func (k Keeper) ChanOpenTry( channelID := previousChannelID + // connection hops only supports a single connection + if len(connectionHops) != 1 { + return "", nil, sdkerrors.Wrapf(types.ErrTooManyConnectionHops, "expected 1, got %d", len(connectionHops)) + } + // empty channel identifier indicates continuing a previous channel handshake if previousChannelID != "" { // channel identifier and connection hop length checked on msg.ValidateBasic() @@ -139,7 +124,7 @@ func (k Keeper) ChanOpenTry( if !(previousChannel.Ordering == order && previousChannel.Counterparty.PortId == counterparty.PortId && previousChannel.Counterparty.ChannelId == "" && - previousChannel.ConnectionHops[0] == connectionHops[0] && + previousChannel.ConnectionHops[0] == connectionHops[0] && // ChanOpenInit will only set a single connection hop previousChannel.Version == version) { return "", nil, sdkerrors.Wrap(types.ErrInvalidChannel, "channel fields mismatch previous channel fields") } @@ -189,12 +174,7 @@ func (k Keeper) ChanOpenTry( // NOTE: this step has been switched with the one below to reverse the connection // hops channel := types.NewChannel(types.TRYOPEN, order, counterparty, connectionHops, version) - - counterpartyHops, found := k.CounterpartyHops(ctx, channel) - if !found { - // should not reach here, connectionEnd was able to be retrieved above - panic("cannot find connection") - } + counterpartyHops := []string{connectionEnd.GetCounterparty().GetConnectionID()} // expectedCounterpaty is the counterparty of the counterparty's channel end // (i.e self) @@ -297,11 +277,7 @@ func (k Keeper) ChanOpenAck( ) } - counterpartyHops, found := k.CounterpartyHops(ctx, channel) - if !found { - // should not reach here, connectionEnd was able to be retrieved above - panic("cannot find connection") - } + counterpartyHops := []string{connectionEnd.GetCounterparty().GetConnectionID()} // counterparty of the counterparty channel end (i.e self) expectedCounterparty := types.NewCounterparty(portID, channelID) @@ -381,11 +357,7 @@ func (k Keeper) ChanOpenConfirm( ) } - counterpartyHops, found := k.CounterpartyHops(ctx, channel) - if !found { - // Should not reach here, connectionEnd was able to be retrieved above - panic("cannot find connection") - } + counterpartyHops := []string{connectionEnd.GetCounterparty().GetConnectionID()} counterparty := types.NewCounterparty(portID, channelID) expectedChannel := types.NewChannel( @@ -519,11 +491,7 @@ func (k Keeper) ChanCloseConfirm( ) } - counterpartyHops, found := k.CounterpartyHops(ctx, channel) - if !found { - // Should not reach here, connectionEnd was able to be retrieved above - panic("cannot find connection") - } + counterpartyHops := []string{connectionEnd.GetCounterparty().GetConnectionID()} counterparty := types.NewCounterparty(portID, channelID) expectedChannel := types.NewChannel( diff --git a/modules/core/04-channel/keeper/keeper.go b/modules/core/04-channel/keeper/keeper.go index 324d11e0bd2..e680b23f182 100644 --- a/modules/core/04-channel/keeper/keeper.go +++ b/modules/core/04-channel/keeper/keeper.go @@ -403,6 +403,23 @@ func (k Keeper) GetChannelClientState(ctx sdk.Context, portID, channelID string) return connection.ClientId, clientState, nil } +// GetChannelConnection returns the connection ID and state associated with the given port and channel identifier. +func (k Keeper) GetChannelConnection(ctx sdk.Context, portID, channelID string) (string, exported.ConnectionI, error) { + channel, found := k.GetChannel(ctx, portID, channelID) + if !found { + return "", nil, sdkerrors.Wrapf(types.ErrChannelNotFound, "port-id: %s, channel-id: %s", portID, channelID) + } + + connectionID := channel.ConnectionHops[0] + + connection, found := k.connectionKeeper.GetConnection(ctx, connectionID) + if !found { + return "", nil, sdkerrors.Wrapf(connectiontypes.ErrConnectionNotFound, "connection-id: %s", connectionID) + } + + return connectionID, connection, nil +} + // LookupModuleByChannel will return the IBCModule along with the capability associated with a given channel defined by its portID and channelID func (k Keeper) LookupModuleByChannel(ctx sdk.Context, portID, channelID string) (string, *capabilitytypes.Capability, error) { modules, cap, err := k.scopedKeeper.LookupModules(ctx, host.ChannelCapabilityPath(portID, channelID)) diff --git a/modules/core/04-channel/keeper/timeout.go b/modules/core/04-channel/keeper/timeout.go index f7f4ab4b96c..ae7039ef0b2 100644 --- a/modules/core/04-channel/keeper/timeout.go +++ b/modules/core/04-channel/keeper/timeout.go @@ -227,11 +227,7 @@ func (k Keeper) TimeoutOnClose( return sdkerrors.Wrapf(types.ErrInvalidPacket, "packet commitment bytes are not equal: got (%v), expected (%v)", commitment, packetCommitment) } - counterpartyHops, found := k.CounterpartyHops(ctx, channel) - if !found { - // Should not reach here, connectionEnd was able to be retrieved above - panic("cannot find connection") - } + counterpartyHops := []string{connectionEnd.GetCounterparty().GetConnectionID()} counterparty := types.NewCounterparty(packet.GetSourcePort(), packet.GetSourceChannel()) expectedChannel := types.NewChannel(