Skip to content

Commit

Permalink
test: add mock middleware to block app upgrades and provide test cove…
Browse files Browse the repository at this point in the history
…rage in core (#5406)
  • Loading branch information
damiannolan authored Jan 8, 2024
1 parent fadf8f2 commit f560430
Show file tree
Hide file tree
Showing 3 changed files with 329 additions and 0 deletions.
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

0 comments on commit f560430

Please sign in to comment.