Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test: add mock middleware to block app upgrades and provide test coverage in core (backport #5406) #5544

Merged
merged 1 commit into from
Jan 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
124 changes: 124 additions & 0 deletions modules/core/keeper/msg_server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
clienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types"
connectiontypes "github.com/cosmos/ibc-go/v8/modules/core/03-connection/types"
channeltypes "github.com/cosmos/ibc-go/v8/modules/core/04-channel/types"
porttypes "github.com/cosmos/ibc-go/v8/modules/core/05-port/types"
commitmenttypes "github.com/cosmos/ibc-go/v8/modules/core/23-commitment/types"
host "github.com/cosmos/ibc-go/v8/modules/core/24-host"
ibcerrors "github.com/cosmos/ibc-go/v8/modules/core/errors"
Expand Down Expand Up @@ -903,6 +904,27 @@ func (suite *KeeperTestSuite) TestChannelUpgradeInit() {
suite.Require().Nil(res)
},
},
{
"ibc application does not implement the UpgradeableModule interface",
func() {
path = ibctesting.NewPath(suite.chainA, suite.chainB)
path.EndpointA.ChannelConfig.PortID = ibcmock.MockBlockUpgrade
path.EndpointB.ChannelConfig.PortID = ibcmock.MockBlockUpgrade

suite.coordinator.Setup(path)

msg = channeltypes.NewMsgChannelUpgradeInit(
path.EndpointA.ChannelConfig.PortID,
path.EndpointA.ChannelID,
path.EndpointA.GetProposedUpgrade().Fields,
path.EndpointA.Chain.GetSimApp().IBCKeeper.GetAuthority(),
)
},
func(res *channeltypes.MsgChannelUpgradeInitResponse, err error) {
suite.Require().ErrorIs(err, porttypes.ErrInvalidRoute)
suite.Require().Nil(res)
},
},
}

for _, tc := range cases {
Expand Down Expand Up @@ -982,6 +1004,23 @@ func (suite *KeeperTestSuite) TestChannelUpgradeTry() {
suite.Require().Equal(uint64(99), errorReceipt.Sequence)
},
},
{
"ibc application does not implement the UpgradeableModule interface",
func() {
path = ibctesting.NewPath(suite.chainA, suite.chainB)
path.EndpointA.ChannelConfig.PortID = ibcmock.MockBlockUpgrade
path.EndpointB.ChannelConfig.PortID = ibcmock.MockBlockUpgrade

suite.coordinator.Setup(path)

msg.PortId = path.EndpointB.ChannelConfig.PortID
msg.ChannelId = path.EndpointB.ChannelID
},
func(res *channeltypes.MsgChannelUpgradeTryResponse, err error) {
suite.Require().ErrorIs(err, porttypes.ErrInvalidRoute)
suite.Require().Nil(res)
},
},
}

for _, tc := range cases {
Expand Down Expand Up @@ -1150,6 +1189,23 @@ func (suite *KeeperTestSuite) TestChannelUpgradeAck() {
suite.Require().False(store.Has([]byte("foo")))
},
},
{
"ibc application does not implement the UpgradeableModule interface",
func() {
path = ibctesting.NewPath(suite.chainA, suite.chainB)
path.EndpointA.ChannelConfig.PortID = ibcmock.MockBlockUpgrade
path.EndpointB.ChannelConfig.PortID = ibcmock.MockBlockUpgrade

suite.coordinator.Setup(path)

msg.PortId = path.EndpointB.ChannelConfig.PortID
msg.ChannelId = path.EndpointB.ChannelID
},
func(res *channeltypes.MsgChannelUpgradeAckResponse, err error) {
suite.Require().ErrorIs(err, porttypes.ErrInvalidRoute)
suite.Require().Nil(res)
},
},
}

for _, tc := range cases {
Expand Down Expand Up @@ -1355,6 +1411,23 @@ func (suite *KeeperTestSuite) TestChannelUpgradeConfirm() {
suite.Require().Equal(uint64(1), errorReceipt.Sequence)
},
},
{
"ibc application does not implement the UpgradeableModule interface",
func() {
path = ibctesting.NewPath(suite.chainA, suite.chainB)
path.EndpointA.ChannelConfig.PortID = ibcmock.MockBlockUpgrade
path.EndpointB.ChannelConfig.PortID = ibcmock.MockBlockUpgrade

suite.coordinator.Setup(path)

msg.PortId = path.EndpointB.ChannelConfig.PortID
msg.ChannelId = path.EndpointB.ChannelID
},
func(res *channeltypes.MsgChannelUpgradeConfirmResponse, err error) {
suite.Require().ErrorIs(err, porttypes.ErrInvalidRoute)
suite.Require().Nil(res)
},
},
}

for _, tc := range cases {
Expand Down Expand Up @@ -1455,6 +1528,23 @@ func (suite *KeeperTestSuite) TestChannelUpgradeOpen() {
suite.Require().ErrorIs(err, channeltypes.ErrInvalidChannelState)
},
},
{
"ibc application does not implement the UpgradeableModule interface",
func() {
path = ibctesting.NewPath(suite.chainA, suite.chainB)
path.EndpointA.ChannelConfig.PortID = ibcmock.MockBlockUpgrade
path.EndpointB.ChannelConfig.PortID = ibcmock.MockBlockUpgrade

suite.coordinator.Setup(path)

msg.PortId = path.EndpointB.ChannelConfig.PortID
msg.ChannelId = path.EndpointB.ChannelID
},
func(res *channeltypes.MsgChannelUpgradeOpenResponse, err error) {
suite.Require().ErrorIs(err, porttypes.ErrInvalidRoute)
suite.Require().Nil(res)
},
},
}

for _, tc := range cases {
Expand Down Expand Up @@ -1652,6 +1742,23 @@ func (suite *KeeperTestSuite) TestChannelUpgradeCancel() {
suite.Require().Equal(uint64(1), channel.UpgradeSequence)
},
},
{
"ibc application does not implement the UpgradeableModule interface",
func() {
path = ibctesting.NewPath(suite.chainA, suite.chainB)
path.EndpointA.ChannelConfig.PortID = ibcmock.MockBlockUpgrade
path.EndpointB.ChannelConfig.PortID = ibcmock.MockBlockUpgrade

suite.coordinator.Setup(path)

msg.PortId = path.EndpointB.ChannelConfig.PortID
msg.ChannelId = path.EndpointB.ChannelID
},
func(res *channeltypes.MsgChannelUpgradeCancelResponse, err error) {
suite.Require().ErrorIs(err, porttypes.ErrInvalidRoute)
suite.Require().Nil(res)
},
},
}
for _, tc := range cases {
tc := tc
Expand Down Expand Up @@ -1793,6 +1900,23 @@ func (suite *KeeperTestSuite) TestChannelUpgradeTimeout() {
suite.Require().True(found, "channel upgrade should not be nil")
},
},
{
"ibc application does not implement the UpgradeableModule interface",
func() {
path = ibctesting.NewPath(suite.chainA, suite.chainB)
path.EndpointA.ChannelConfig.PortID = ibcmock.MockBlockUpgrade
path.EndpointB.ChannelConfig.PortID = ibcmock.MockBlockUpgrade

suite.coordinator.Setup(path)

msg.PortId = path.EndpointB.ChannelConfig.PortID
msg.ChannelId = path.EndpointB.ChannelID
},
func(res *channeltypes.MsgChannelUpgradeTimeoutResponse, err error) {
suite.Require().ErrorIs(err, porttypes.ErrInvalidRoute)
suite.Require().Nil(res)
},
},
}

for _, tc := range cases {
Expand Down
197 changes: 197 additions & 0 deletions testing/mock/middleware.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
package mock

import (
"bytes"
"strings"

sdk "github.com/cosmos/cosmos-sdk/types"

capabilitytypes "github.com/cosmos/ibc-go/modules/capability/types"
clienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types"
channeltypes "github.com/cosmos/ibc-go/v8/modules/core/04-channel/types"
porttypes "github.com/cosmos/ibc-go/v8/modules/core/05-port/types"
host "github.com/cosmos/ibc-go/v8/modules/core/24-host"
"github.com/cosmos/ibc-go/v8/modules/core/exported"
)

const (
MockBlockUpgrade = "mockblockupgrade"
)

var _ porttypes.Middleware = (*BlockUpgradeMiddleware)(nil)

// BlockUpgradeMiddleware does not implement the UpgradeableModule interface
type BlockUpgradeMiddleware struct {
appModule *AppModule
IBCApp *IBCApp // base application of an IBC middleware stack
}

// NewIBCModule creates a new IBCModule given the underlying mock IBC application and scopedKeeper.
func NewBlockUpgradeMiddleware(appModule *AppModule, app *IBCApp) BlockUpgradeMiddleware {
appModule.ibcApps = append(appModule.ibcApps, app)
return BlockUpgradeMiddleware{
appModule: appModule,
IBCApp: app,
}
}

// OnChanOpenInit implements the IBCModule interface.
func (im BlockUpgradeMiddleware) OnChanOpenInit(
ctx sdk.Context, order channeltypes.Order, connectionHops []string, portID string,
channelID string, chanCap *capabilitytypes.Capability, counterparty channeltypes.Counterparty, version string,
) (string, error) {
if strings.TrimSpace(version) == "" {
version = Version
}

if im.IBCApp.OnChanOpenInit != nil {
return im.IBCApp.OnChanOpenInit(ctx, order, connectionHops, portID, channelID, chanCap, counterparty, version)
}

if chanCap != nil {
// Claim channel capability passed back by IBC module
if err := im.IBCApp.ScopedKeeper.ClaimCapability(ctx, chanCap, host.ChannelCapabilityPath(portID, channelID)); err != nil {
return "", err
}
}

return version, nil
}

// OnChanOpenTry implements the IBCModule interface.
func (im BlockUpgradeMiddleware) OnChanOpenTry(
ctx sdk.Context, order channeltypes.Order, connectionHops []string, portID string,
channelID string, chanCap *capabilitytypes.Capability, counterparty channeltypes.Counterparty, counterpartyVersion string,
) (version string, err error) {
if im.IBCApp.OnChanOpenTry != nil {
return im.IBCApp.OnChanOpenTry(ctx, order, connectionHops, portID, channelID, chanCap, counterparty, counterpartyVersion)
}

if chanCap != nil {
// Claim channel capability passed back by IBC module
if err := im.IBCApp.ScopedKeeper.ClaimCapability(ctx, chanCap, host.ChannelCapabilityPath(portID, channelID)); err != nil {
return "", err
}
}

return Version, nil
}

// OnChanOpenAck implements the IBCModule interface.
func (im BlockUpgradeMiddleware) OnChanOpenAck(ctx sdk.Context, portID string, channelID string, counterpartyChannelID string, counterpartyVersion string) error {
if im.IBCApp.OnChanOpenAck != nil {
return im.IBCApp.OnChanOpenAck(ctx, portID, channelID, counterpartyChannelID, counterpartyVersion)
}

return nil
}

// OnChanOpenConfirm implements the IBCModule interface.
func (im BlockUpgradeMiddleware) OnChanOpenConfirm(ctx sdk.Context, portID, channelID string) error {
if im.IBCApp.OnChanOpenConfirm != nil {
return im.IBCApp.OnChanOpenConfirm(ctx, portID, channelID)
}

return nil
}

// OnChanCloseInit implements the IBCModule interface.
func (im BlockUpgradeMiddleware) OnChanCloseInit(ctx sdk.Context, portID, channelID string) error {
if im.IBCApp.OnChanCloseInit != nil {
return im.IBCApp.OnChanCloseInit(ctx, portID, channelID)
}

return nil
}

// OnChanCloseConfirm implements the IBCModule interface.
func (im BlockUpgradeMiddleware) OnChanCloseConfirm(ctx sdk.Context, portID, channelID string) error {
if im.IBCApp.OnChanCloseConfirm != nil {
return im.IBCApp.OnChanCloseConfirm(ctx, portID, channelID)
}

return nil
}

// OnRecvPacket implements the IBCModule interface.
func (im BlockUpgradeMiddleware) OnRecvPacket(ctx sdk.Context, packet channeltypes.Packet, relayer sdk.AccAddress) exported.Acknowledgement {
if im.IBCApp.OnRecvPacket != nil {
return im.IBCApp.OnRecvPacket(ctx, packet, relayer)
}

// set state by claiming capability to check if revert happens return
capName := GetMockRecvCanaryCapabilityName(packet)
if _, err := im.IBCApp.ScopedKeeper.NewCapability(ctx, capName); err != nil {
// application callback called twice on same packet sequence
// must never occur
panic(err)
}

if bytes.Equal(MockPacketData, packet.GetData()) {
return MockAcknowledgement
} else if bytes.Equal(MockAsyncPacketData, packet.GetData()) {
return nil
}

return MockFailAcknowledgement
}

// OnAcknowledgementPacket implements the IBCModule interface.
func (im BlockUpgradeMiddleware) OnAcknowledgementPacket(ctx sdk.Context, packet channeltypes.Packet, acknowledgement []byte, relayer sdk.AccAddress) error {
if im.IBCApp.OnAcknowledgementPacket != nil {
return im.IBCApp.OnAcknowledgementPacket(ctx, packet, acknowledgement, relayer)
}

capName := GetMockAckCanaryCapabilityName(packet)
if _, err := im.IBCApp.ScopedKeeper.NewCapability(ctx, capName); err != nil {
// application callback called twice on same packet sequence
// must never occur
panic(err)
}

return nil
}

// OnTimeoutPacket implements the IBCModule interface.
func (im BlockUpgradeMiddleware) OnTimeoutPacket(ctx sdk.Context, packet channeltypes.Packet, relayer sdk.AccAddress) error {
if im.IBCApp.OnTimeoutPacket != nil {
return im.IBCApp.OnTimeoutPacket(ctx, packet, relayer)
}

capName := GetMockTimeoutCanaryCapabilityName(packet)
if _, err := im.IBCApp.ScopedKeeper.NewCapability(ctx, capName); err != nil {
// application callback called twice on same packet sequence
// must never occur
panic(err)
}

return nil
}

// SendPacket implements the ICS4 Wrapper interface
func (BlockUpgradeMiddleware) SendPacket(
ctx sdk.Context,
chanCap *capabilitytypes.Capability,
sourcePort string,
sourceChannel string,
timeoutHeight clienttypes.Height,
timeoutTimestamp uint64,
data []byte,
) (uint64, error) {
return 0, nil
}

// WriteAcknowledgement implements the ICS4 Wrapper interface
func (BlockUpgradeMiddleware) WriteAcknowledgement(
ctx sdk.Context,
chanCap *capabilitytypes.Capability,
packet exported.PacketI,
ack exported.Acknowledgement,
) error {
return nil
}

// GetAppVersion returns the application version of the underlying application
func (BlockUpgradeMiddleware) GetAppVersion(ctx sdk.Context, portID, channelID string) (string, bool) {
return Version, true
}
8 changes: 8 additions & 0 deletions testing/simapp/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,7 @@ func NewSimApp(
// NOTE: the IBC mock keeper and application module is used only for testing core IBC. Do
// not replicate if you do not need to test core IBC or light clients.
scopedIBCMockKeeper := app.CapabilityKeeper.ScopeToModule(ibcmock.ModuleName)
scopedIBCMockBlockUpgradeKeeper := app.CapabilityKeeper.ScopeToModule(ibcmock.MockBlockUpgrade)
scopedFeeMockKeeper := app.CapabilityKeeper.ScopeToModule(MockFeePort)
scopedICAMockKeeper := app.CapabilityKeeper.ScopeToModule(ibcmock.ModuleName + icacontrollertypes.SubModuleName)

Expand Down Expand Up @@ -489,6 +490,13 @@ func NewSimApp(
app.IBCMockModule = mockIBCModule
ibcRouter.AddRoute(ibcmock.ModuleName, mockIBCModule)

// Mock IBC app wrapped with a middleware which does not implement the UpgradeableModule interface.
// NOTE: this is used to test integration with apps which do not yet fulfill the UpgradeableModule interface and error when
// an upgrade is tried on the channel.
mockBlockUpgradeIBCModule := ibcmock.NewIBCModule(&mockModule, ibcmock.NewIBCApp(ibcmock.MockBlockUpgrade, scopedIBCMockBlockUpgradeKeeper))
mockBlockUpgradeMw := ibcmock.NewBlockUpgradeMiddleware(&mockModule, mockBlockUpgradeIBCModule.IBCApp)
ibcRouter.AddRoute(ibcmock.MockBlockUpgrade, mockBlockUpgradeMw)

// Create Transfer Stack
// SendPacket, since it is originating from the application to core IBC:
// transferKeeper.SendPacket -> fee.SendPacket -> channel.SendPacket
Expand Down
Loading