Skip to content

Commit

Permalink
Adding GetChannelsWithPortPrefix function to 04-channel (#2304)
Browse files Browse the repository at this point in the history
(cherry picked from commit ce5aba8)

# Conflicts:
#	CHANGELOG.md
  • Loading branch information
chatton authored and mergify[bot] committed Sep 29, 2022
1 parent 0207728 commit b95a87f
Show file tree
Hide file tree
Showing 7 changed files with 142 additions and 12 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,11 @@ Ref: https://keepachangelog.com/en/1.0.0/
* (apps/27-interchain-accounts) [\#2146](https://github.com/cosmos/ibc-go/pull/2146) ICS27 controller now claims the channel capability passed via ibc core, and passes `nil` to the underlying app callback. The channel capability arg in `SendTx` is now ignored and looked up internally.
* (apps/27-interchain-accounts) [\#2177](https://github.com/cosmos/ibc-go/pull/2177) Adding `IsMiddlewareEnabled` flag to interchain accounts `ActiveChannel` genesis type.
* (apps/27-interchain-accounts) [\#2140](https://github.com/cosmos/ibc-go/pull/2140) Adding migration handler to ICS27 `controller` submodule to assert ownership of channel capabilities and set middleware enabled flag for existing channels. The ICS27 module consensus version has been bumped from 1 to 2.
<<<<<<< HEAD
=======
* (core/04-channel) [\#2304](https://github.com/cosmos/ibc-go/pull/2304) Adding `GetAllChannelsWithPortPrefix` function which filters channels based on a provided port prefix.
* (apps/27-interchain-accounts) [\#2290](https://github.com/cosmos/ibc-go/pull/2290) Changed `DefaultParams` function in `host` submodule to allow all messages by default. Defined a constant named `AllowAllHostMsgs` for `host` module to keep wildcard "*" string which allows all messages.
>>>>>>> ce5aba8 (Adding GetChannelsWithPortPrefix function to 04-channel (#2304))
* (apps/27-interchain-accounts) [\#2248](https://github.com/cosmos/ibc-go/pull/2248) Adding call to underlying app in `OnChanCloseConfirm` callback of the controller submodule and adding relevant unit tests.
* (apps/27-interchain-accounts) [\#2251](https://github.com/cosmos/ibc-go/pull/2251) Adding `msgServer` struct to controller submodule that embeds the `Keeper` struct.
* (apps/27-interchain-accounts) [\#2290](https://github.com/cosmos/ibc-go/pull/2290) Changed `DefaultParams` function in `host` submodule to allow all messages by default. Defined a constant named `AllowAllHostMsgs` for `host` module to keep wildcard "*" string which allows all messages.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import (
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types"

"github.com/cosmos/ibc-go/v6/modules/core/23-commitment/types"
controllertypes "github.com/cosmos/ibc-go/v6/modules/apps/27-interchain-accounts/controller/types"
icatypes "github.com/cosmos/ibc-go/v6/modules/apps/27-interchain-accounts/types"
host "github.com/cosmos/ibc-go/v6/modules/core/24-host"
)

Expand All @@ -23,19 +24,20 @@ func NewMigrator(keeper *Keeper) Migrator {
// are owned by the controller submodule and ibc.
func (m Migrator) AssertChannelCapabilityMigrations(ctx sdk.Context) error {
if m.keeper != nil {
for _, ch := range m.keeper.GetAllActiveChannels(ctx) {
filteredChannels := m.keeper.channelKeeper.GetAllChannelsWithPortPrefix(ctx, icatypes.PortPrefix)
for _, ch := range filteredChannels {
name := host.ChannelCapabilityPath(ch.PortId, ch.ChannelId)
capacity, found := m.keeper.scopedKeeper.GetCapability(ctx, name)
capability, found := m.keeper.scopedKeeper.GetCapability(ctx, name)
if !found {
return sdkerrors.Wrapf(capabilitytypes.ErrCapabilityNotFound, "failed to find capability: %s", name)
}

isAuthenticated := m.keeper.scopedKeeper.AuthenticateCapability(ctx, capacity, name)
isAuthenticated := m.keeper.scopedKeeper.AuthenticateCapability(ctx, capability, name)
if !isAuthenticated {
return sdkerrors.Wrapf(capabilitytypes.ErrCapabilityNotOwned, "expected capability owner: %s", types.SubModuleName)
return sdkerrors.Wrapf(capabilitytypes.ErrCapabilityNotOwned, "expected capability owner: %s", controllertypes.SubModuleName)
}

m.keeper.SetMiddlewareEnabled(ctx, ch.PortId, ch.ConnectionId)
m.keeper.SetMiddlewareEnabled(ctx, ch.PortId, ch.ConnectionHops[0])
}
}
return nil
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
package keeper_test

import (
"fmt"

"github.com/cosmos/ibc-go/v6/modules/apps/27-interchain-accounts/controller/keeper"
icatypes "github.com/cosmos/ibc-go/v6/modules/apps/27-interchain-accounts/types"
channeltypes "github.com/cosmos/ibc-go/v6/modules/core/04-channel/types"
ibctesting "github.com/cosmos/ibc-go/v6/testing"
)

Expand All @@ -16,15 +20,23 @@ func (suite *KeeperTestSuite) TestAssertChannelCapabilityMigrations() {
func() {},
true,
},
{
"channel with different port is filtered out",
func() {
portIDWithOutPrefix := ibctesting.MockPort
suite.chainA.GetSimApp().IBCKeeper.ChannelKeeper.SetChannel(suite.chainA.GetContext(), portIDWithOutPrefix, ibctesting.FirstChannelID, channeltypes.Channel{
ConnectionHops: []string{ibctesting.FirstConnectionID},
})
},
true,
},
{
"capability not found",
func() {
suite.chainA.GetSimApp().ICAControllerKeeper.SetActiveChannelID(
suite.chainA.GetContext(),
ibctesting.FirstConnectionID,
ibctesting.MockPort,
ibctesting.FirstChannelID,
)
portIDWithPrefix := fmt.Sprintf("%s%s", icatypes.PortPrefix, "port-without-capability")
suite.chainA.GetSimApp().IBCKeeper.ChannelKeeper.SetChannel(suite.chainA.GetContext(), portIDWithPrefix, ibctesting.FirstChannelID, channeltypes.Channel{
ConnectionHops: []string{ibctesting.FirstConnectionID},
})
},
false,
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ type ChannelKeeper interface {
GetChannel(ctx sdk.Context, srcPort, srcChan string) (channel channeltypes.Channel, found bool)
GetNextSequenceSend(ctx sdk.Context, portID, channelID string) (uint64, bool)
GetConnection(ctx sdk.Context, connectionID string) (ibcexported.ConnectionI, error)
GetAllChannelsWithPortPrefix(ctx sdk.Context, portPrefix string) []channeltypes.IdentifiedChannel
}

// PortKeeper defines the expected IBC port keeper
Expand Down
22 changes: 22 additions & 0 deletions modules/core/04-channel/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,28 @@ func (k Keeper) IterateChannels(ctx sdk.Context, cb func(types.IdentifiedChannel
}
}

// GetAllChannelsWithPortPrefix returns all channels with the specified port prefix. If an empty prefix is provided
// all channels will be returned.
func (k Keeper) GetAllChannelsWithPortPrefix(ctx sdk.Context, portPrefix string) []types.IdentifiedChannel {
if strings.TrimSpace(portPrefix) == "" {
return k.GetAllChannels(ctx)
}
store := ctx.KVStore(k.storeKey)
iterator := sdk.KVStorePrefixIterator(store, types.FilteredPortPrefix(portPrefix))
defer iterator.Close()

var filteredChannels []types.IdentifiedChannel
for ; iterator.Valid(); iterator.Next() {
var channel types.Channel
k.cdc.MustUnmarshal(iterator.Value(), &channel)

portID, channelID := host.MustParseChannelPath(string(iterator.Key()))
identifiedChannel := types.NewIdentifiedChannel(portID, channelID, channel)
filteredChannels = append(filteredChannels, identifiedChannel)
}
return filteredChannels
}

// GetAllChannels returns all stored Channel objects.
func (k Keeper) GetAllChannels(ctx sdk.Context) (channels []types.IdentifiedChannel) {
k.IterateChannels(ctx, func(channel types.IdentifiedChannel) bool {
Expand Down
83 changes: 83 additions & 0 deletions modules/core/04-channel/keeper/keeper_test.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package keeper_test

import (
"reflect"
"testing"

"github.com/stretchr/testify/suite"

transfertypes "github.com/cosmos/ibc-go/v6/modules/apps/transfer/types"
"github.com/cosmos/ibc-go/v6/modules/core/04-channel/types"
ibctesting "github.com/cosmos/ibc-go/v6/testing"
ibcmock "github.com/cosmos/ibc-go/v6/testing/mock"
Expand Down Expand Up @@ -81,6 +83,87 @@ func (suite *KeeperTestSuite) TestGetAppVersion() {
suite.Require().Equal(ibcmock.Version, channelVersion)
}

// TestGetAllChannelsWithPortPrefix verifies ports are filtered correctly using a port prefix.
func (suite *KeeperTestSuite) TestGetAllChannelsWithPortPrefix() {

const (
secondChannelID = "channel-1"
differentChannelPortID = "different-portid"
)

allChannels := []types.IdentifiedChannel{
types.NewIdentifiedChannel(transfertypes.PortID, ibctesting.FirstChannelID, types.Channel{}),
types.NewIdentifiedChannel(differentChannelPortID, secondChannelID, types.Channel{}),
}

tests := []struct {
name string
prefix string
allChannels []types.IdentifiedChannel
expectedChannels []types.IdentifiedChannel
}{
{
name: "transfer channel is retrieved with prefix",
prefix: "tra",
allChannels: allChannels,
expectedChannels: []types.IdentifiedChannel{types.NewIdentifiedChannel(transfertypes.TypeMsgTransfer, ibctesting.FirstChannelID, types.Channel{})},
},
{
name: "matches port with full name as prefix",
prefix: transfertypes.TypeMsgTransfer,
allChannels: allChannels,
expectedChannels: []types.IdentifiedChannel{types.NewIdentifiedChannel(transfertypes.TypeMsgTransfer, ibctesting.FirstChannelID, types.Channel{})},
},
{
name: "no ports match prefix",
prefix: "wont-match-anything",
allChannels: allChannels,
expectedChannels: nil,
},
{
name: "empty prefix matches everything",
prefix: "",
allChannels: allChannels,
expectedChannels: allChannels,
},
}

for _, tc := range tests {
tc := tc
suite.Run(tc.name, func() {
suite.SetupTest()

for _, ch := range tc.allChannels {
suite.chainA.GetSimApp().GetIBCKeeper().ChannelKeeper.SetChannel(suite.chainA.GetContext(), ch.PortId, ch.ChannelId, types.Channel{})
}

ctxA := suite.chainA.GetContext()

actualChannels := suite.chainA.GetSimApp().GetIBCKeeper().ChannelKeeper.GetAllChannelsWithPortPrefix(ctxA, tc.prefix)

suite.Require().True(containsAll(tc.expectedChannels, actualChannels))
})
}
}

// containsAll verifies if all elements in the expected slice exist in the actual slice
// independent of order.
func containsAll(expected, actual []types.IdentifiedChannel) bool {
for _, expectedChannel := range expected {
foundMatch := false
for _, actualChannel := range actual {
if reflect.DeepEqual(actualChannel, expectedChannel) {
foundMatch = true
break
}
}
if !foundMatch {
return false
}
}
return true
}

// TestGetAllChannels creates multiple channels on chain A through various connections
// and tests their retrieval. 2 channels are on connA0 and 1 channel is on connA1
func (suite KeeperTestSuite) TestGetAllChannels() {
Expand Down
5 changes: 5 additions & 0 deletions modules/core/04-channel/types/keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,8 @@ func ParseChannelSequence(channelID string) (uint64, error) {

return sequence, nil
}

// FilteredPortPrefix returns the prefix key for the given port prefix.
func FilteredPortPrefix(portPrefix string) []byte {
return []byte(fmt.Sprintf("%s/%s/%s", host.KeyChannelEndPrefix, host.KeyPortPrefix, portPrefix))
}

0 comments on commit b95a87f

Please sign in to comment.