diff --git a/CHANGELOG.md b/CHANGELOG.md index 2aae0efca69..675fecc4595 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -50,6 +50,8 @@ Ref: https://keepachangelog.com/en/1.0.0/ ### Bug Fixes +* (core/04-channel) [\#6935](https://github.com/cosmos/ibc-go/pull/6935) Check upgrade compatibility in `ChanUpgradeConfirm`. + ## [v8.3.2](https://github.com/cosmos/ibc-go/releases/tag/v8.3.2) - 2024-06-20 ### Dependencies diff --git a/modules/core/04-channel/keeper/upgrade.go b/modules/core/04-channel/keeper/upgrade.go index 1dae7f7a862..03f74e8673c 100644 --- a/modules/core/04-channel/keeper/upgrade.go +++ b/modules/core/04-channel/keeper/upgrade.go @@ -448,6 +448,21 @@ func (k Keeper) ChanUpgradeConfirm( return errorsmod.Wrap(err, "failed to verify counterparty upgrade") } + // if we have cancelled our upgrade after performing UpgradeInit, + // UpgradeTry or UpgradeAck, the lack of a stored upgrade will prevent + // us from continuing the upgrade handshake + upgrade, found := k.GetUpgrade(ctx, portID, channelID) + if !found { + return errorsmod.Wrapf(types.ErrUpgradeNotFound, "failed to retrieve channel upgrade: port ID (%s) channel ID (%s)", portID, channelID) + } + + // in the crossing-hello case it is possible that both chains execute the + // INIT, TRY and CONFIRM steps without any of them executing ACK, therefore + // we also need to check that the upgrades are compatible on this step + if err := k.checkForUpgradeCompatibility(ctx, upgrade.Fields, counterpartyUpgrade.Fields); err != nil { + return types.NewUpgradeError(channel.UpgradeSequence, err) + } + timeout := counterpartyUpgrade.Timeout selfHeight, selfTimestamp := clienttypes.GetSelfHeight(ctx), uint64(ctx.BlockTime().UnixNano()) diff --git a/modules/core/04-channel/keeper/upgrade_test.go b/modules/core/04-channel/keeper/upgrade_test.go index a67ffa12682..86edcfa7606 100644 --- a/modules/core/04-channel/keeper/upgrade_test.go +++ b/modules/core/04-channel/keeper/upgrade_test.go @@ -1124,6 +1124,27 @@ func (suite *KeeperTestSuite) TestChanUpgradeConfirm() { }, types.NewUpgradeError(1, types.ErrTimeoutElapsed), }, + { + "upgrade not found", + func() { + path.EndpointB.Chain.DeleteKey(host.ChannelUpgradeKey(path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID)) + }, + types.ErrUpgradeNotFound, + }, + { + "upgrades are not compatible", + func() { + // the expected upgrade version is mock-version-v2 + counterpartyUpgrade.Fields.Version = fmt.Sprintf("%s-v3", mock.Version) + path.EndpointA.SetChannelUpgrade(counterpartyUpgrade) + + suite.coordinator.CommitBlock(suite.chainA) + + err := path.EndpointB.UpdateClient() + suite.Require().NoError(err) + }, + types.NewUpgradeError(1, types.ErrIncompatibleCounterpartyUpgrade), + }, } for _, tc := range testCases {