diff --git a/.github/workflows/e2e-upgrade.yaml b/.github/workflows/e2e-upgrade.yaml index c7c5e6bbf77..a0e45973a85 100644 --- a/.github/workflows/e2e-upgrade.yaml +++ b/.github/workflows/e2e-upgrade.yaml @@ -52,6 +52,11 @@ jobs: upgrade-plan: v10, test: TestV8ToV10ChainUpgrade_Localhost }, + { + tag: v10.3.0, + upgrade-plan: v11, + test: TestV10ToV11ChainUpgrade + }, ] steps: - uses: actions/checkout@v5 diff --git a/e2e/ci-e2e-config.yaml b/e2e/ci-e2e-config.yaml index aa71d998543..717f1645928 100644 --- a/e2e/ci-e2e-config.yaml +++ b/e2e/ci-e2e-config.yaml @@ -77,5 +77,7 @@ upgrades: tag: "v8.1.0" - planName: "v10" tag: "v10.3.0" + - planName: "v11" + tag: "v11.0.0" - planName: "ibcwasm-v8" tag: "v8.0.0-e2e-upgrade" diff --git a/e2e/tests/upgrades/upgrade_test.go b/e2e/tests/upgrades/upgrade_test.go index 233300e478e..ec02f096b3e 100644 --- a/e2e/tests/upgrades/upgrade_test.go +++ b/e2e/tests/upgrades/upgrade_test.go @@ -989,6 +989,101 @@ func (s *UpgradeTestSuite) TestV8ToV10ChainUpgrade_Localhost() { }) } +// TestV10ToV11ChainUpgrade will test that an upgrade from a v10 ibc-go binary to a v11 ibc-go binary is successful +// and that the v11 migration properly migrates the channel store to support IBC v2 for all OPEN UNORDERED channels. +func (s *UpgradeTestSuite) TestV10ToV11ChainUpgrade() { + t := s.T() + testCfg := testsuite.LoadConfig() + ctx := context.Background() + + testName := t.Name() + + chainA, chainB := s.GetChains() + + s.CreatePaths(ibc.DefaultClientOpts(), s.TransferChannelOptions(), testName) + relayer := s.GetRelayerForTest(testName) + channelA := s.GetChannelBetweenChains(testName, chainA, chainB) + + chainADenom := chainA.Config().Denom + chainBDenom := chainB.Config().Denom + + chainAWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) + chainAAddress := chainAWallet.FormattedAddress() + + chainBWallet := s.CreateUserOnChainB(ctx, testvalues.StartingTokenAmount) + chainBAddress := chainBWallet.FormattedAddress() + + chainAIBCToken := testsuite.GetIBCToken(chainBDenom, channelA.PortID, channelA.ChannelID) + chainBIBCToken := testsuite.GetIBCToken(chainADenom, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID) + + s.Require().NoError(test.WaitForBlocks(ctx, 1, chainA, chainB), "failed to wait for blocks") + + t.Run("start relayer", func(t *testing.T) { + s.StartRelayer(relayer, testName) + }) + + t.Run("transfer native tokens from chainA to chainB", func(t *testing.T) { + transferTxResp := s.Transfer(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, testvalues.DefaultTransferAmount(chainADenom), chainAAddress, chainBAddress, s.GetTimeoutHeight(ctx, chainB), 0, "") + s.AssertTxSuccess(transferTxResp) + + s.AssertPacketRelayed(ctx, chainA, channelA.PortID, channelA.ChannelID, 1) + + actualBalance, err := query.Balance(ctx, chainB, chainBAddress, chainBIBCToken.IBCDenom()) + s.Require().NoError(err) + + expected := testvalues.IBCTransferAmount + s.Require().Equal(expected, actualBalance.Int64()) + }) + + t.Run("transfer native tokens from chainB to chainA", func(t *testing.T) { + transferTxResp := s.Transfer(ctx, chainB, chainBWallet, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID, testvalues.DefaultTransferAmount(chainBDenom), chainBAddress, chainAAddress, s.GetTimeoutHeight(ctx, chainA.(*cosmos.CosmosChain)), 0, "") + s.AssertTxSuccess(transferTxResp) + + s.AssertPacketRelayed(ctx, chainA, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID, 1) + + actualBalance, err := query.Balance(ctx, chainA, chainAAddress, chainAIBCToken.IBCDenom()) + s.Require().NoError(err) + + expected := testvalues.IBCTransferAmount + s.Require().Equal(expected, actualBalance.Int64()) + }) + + s.Require().NoError(test.WaitForBlocks(ctx, 5, chainA), "failed to wait for blocks") + + t.Run("stop relayer", func(t *testing.T) { + s.StopRelayer(ctx, relayer) + }) + + t.Run("upgrade chain", func(t *testing.T) { + govProposalWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) + s.UpgradeChain(ctx, chainA.(*cosmos.CosmosChain), govProposalWallet, testCfg.GetUpgradeConfig().PlanName, testCfg.ChainConfigs[0].Tag, testCfg.GetUpgradeConfig().Tag) + }) + + t.Run("start relayer", func(t *testing.T) { + s.StartRelayer(relayer, testName) + }) + + t.Run("query denoms after upgrade", func(t *testing.T) { + resp, err := query.TransferDenoms(ctx, chainA) + s.Require().NoError(err) + s.Require().Len(resp.Denoms, 1) + s.Require().Equal(chainAIBCToken, resp.Denoms[0]) + }) + + t.Run("IBC token transfer from chainA to chainB, to make sure the upgrade did not break the packet flow", func(t *testing.T) { + transferTxResp := s.Transfer(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, testvalues.DefaultTransferAmount(chainADenom), chainAAddress, chainBAddress, s.GetTimeoutHeight(ctx, chainB), 0, "") + s.AssertTxSuccess(transferTxResp) + + s.AssertPacketRelayed(ctx, chainA, channelA.PortID, channelA.ChannelID, 2) + + actualBalance, err := chainB.GetBalance(ctx, chainBAddress, chainBIBCToken.IBCDenom()) + s.Require().NoError(err) + + expected := testvalues.IBCTransferAmount * 2 + s.Require().Equal(expected, actualBalance.Int64()) + }) +} + // ClientState queries the current ClientState by clientID func (*UpgradeTestSuite) ClientState(ctx context.Context, chain ibc.Chain, clientID string) (*clienttypes.QueryClientStateResponse, error) { res, err := query.GRPCQuery[clienttypes.QueryClientStateResponse](ctx, chain, &clienttypes.QueryClientStateRequest{ClientId: clientID}) diff --git a/simapp/upgrades.go b/simapp/upgrades.go index 1f28e067571..e5b36c58bb2 100644 --- a/simapp/upgrades.go +++ b/simapp/upgrades.go @@ -4,6 +4,8 @@ import ( storetypes "cosmossdk.io/store/types" circuittypes "cosmossdk.io/x/circuit/types" upgradetypes "cosmossdk.io/x/upgrade/types" + packetforwardtypes "github.com/cosmos/ibc-go/v10/modules/apps/packet-forward-middleware/types" + ratelimitingtypes "github.com/cosmos/ibc-go/v10/modules/apps/rate-limiting/types" "github.com/cosmos/ibc-go/simapp/upgrades" ) @@ -34,6 +36,15 @@ func (app *SimApp) registerUpgradeHandlers() { ), ) + app.UpgradeKeeper.SetUpgradeHandler( + upgrades.V11, + upgrades.CreateV11UpgradeHandler( + app.ModuleManager, + app.configurator, + app, + ), + ) + upgradeInfo, err := app.UpgradeKeeper.ReadUpgradeInfoFromDisk() if err != nil { panic(err) @@ -48,4 +59,16 @@ func (app *SimApp) registerUpgradeHandlers() { // configure store loader that checks if version == upgradeHeight and applies store upgrades app.SetStoreLoader(upgradetypes.UpgradeStoreLoader(upgradeInfo.Height, &storeUpgrades)) } + + if upgradeInfo.Name == upgrades.V11 && !app.UpgradeKeeper.IsSkipHeight(upgradeInfo.Height) { + storeUpgrades := storetypes.StoreUpgrades{ + Added: []string{ + packetforwardtypes.ModuleName, + ratelimitingtypes.ModuleName, + }, + } + // configure store loader that checks if version == upgradeHeight and applies store upgrades + app.SetStoreLoader(upgradetypes.UpgradeStoreLoader(upgradeInfo.Height, &storeUpgrades)) + } + } diff --git a/simapp/upgrades/upgrades.go b/simapp/upgrades/upgrades.go index a9f7efc0818..7180481c862 100644 --- a/simapp/upgrades/upgrades.go +++ b/simapp/upgrades/upgrades.go @@ -2,10 +2,18 @@ package upgrades import ( "context" + "fmt" + storetypes "cosmossdk.io/store/types" upgradetypes "cosmossdk.io/x/upgrade/types" + "github.com/cosmos/cosmos-sdk/runtime" + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/module" + v11 "github.com/cosmos/ibc-go/v10/modules/core/04-channel/migrations/v11" + ibcexported "github.com/cosmos/ibc-go/v10/modules/core/exported" + ibckeeper "github.com/cosmos/ibc-go/v10/modules/core/keeper" ) const ( @@ -15,6 +23,8 @@ const ( V8_1 = "v8.1" // V10 defines the upgrade name for the ibc-go/v10 upgrade handler. V10 = "v10" + // V11 defines the upgrade name for the ibc-go/v11 upgrade handler. + V11 = "v11" ) // CreateDefaultUpgradeHandler creates an upgrade handler which can be used for regular upgrade tests @@ -27,3 +37,51 @@ func CreateDefaultUpgradeHandler( return mm.RunMigrations(ctx, configurator, vm) } } + +// CreateV11UpgradeHandler creates an upgrade handler for v11 that includes IBC sequence migration +func CreateV11UpgradeHandler( + mm *module.Manager, + configurator module.Configurator, + app interface{}, +) upgradetypes.UpgradeHandler { + return func(ctx context.Context, _ upgradetypes.Plan, vm module.VersionMap) (module.VersionMap, error) { + // Run module migrations + vm, err := mm.RunMigrations(ctx, configurator, vm) + if err != nil { + return vm, fmt.Errorf("failed to run module migrations: %w", err) + } + + // Perform IBC v11 migration + if err := performIBCV11Migration(ctx, app); err != nil { + return vm, fmt.Errorf("failed to perform IBC v11 migration: %w", err) + } + + return vm, nil + } +} + +// performIBCV11Migration performs the IBC v11 sequence migration +func performIBCV11Migration(ctx context.Context, app interface{}) error { + // Type assertion to get the app methods we need + simApp, ok := app.(interface { + GetIBCKeeper() *ibckeeper.Keeper + AppCodec() codec.Codec + GetKey(moduleName string) *storetypes.KVStoreKey + }) + if !ok { + return fmt.Errorf("invalid app type for IBC migration") + } + + // Get the store service + storeService := runtime.NewKVStoreService(simApp.GetKey(ibcexported.StoreKey)) + + // Try to perform the migration + err := v11.MigrateStore( + sdk.UnwrapSDKContext(ctx), + storeService, + simApp.AppCodec(), + simApp.GetIBCKeeper(), + ) + + return err +}