diff --git a/modules/core/04-channel/keeper/packet.go b/modules/core/04-channel/keeper/packet.go index ee73ce54d5f..5832eba7c80 100644 --- a/modules/core/04-channel/keeper/packet.go +++ b/modules/core/04-channel/keeper/packet.go @@ -118,8 +118,8 @@ func (k Keeper) RecvPacket( return errorsmod.Wrap(types.ErrChannelNotFound, packet.GetDestChannel()) } - if !slices.Contains([]types.State{types.OPEN, types.FLUSHING}, channel.State) { - return errorsmod.Wrapf(types.ErrInvalidChannelState, "expected channel state to be one of [%s, %s], but got %s", types.OPEN, types.FLUSHING, channel.State) + if !slices.Contains([]types.State{types.OPEN, types.FLUSHING, types.FLUSHCOMPLETE}, channel.State) { + return errorsmod.Wrapf(types.ErrInvalidChannelState, "expected channel state to be one of [%s, %s, %s], but got %s", types.OPEN, types.FLUSHING, types.FLUSHCOMPLETE, channel.State) } // If counterpartyUpgrade is stored we need to ensure that the @@ -300,11 +300,8 @@ func (k Keeper) WriteAcknowledgement( return errorsmod.Wrap(types.ErrChannelNotFound, packet.GetDestChannel()) } - if !channel.IsOpen() { - return errorsmod.Wrapf( - types.ErrInvalidChannelState, - "channel state is not OPEN (got %s)", channel.State.String(), - ) + if !slices.Contains([]types.State{types.OPEN, types.FLUSHING, types.FLUSHCOMPLETE}, channel.State) { + return errorsmod.Wrapf(types.ErrInvalidChannelState, "expected one of [%s, %s, %s], got %s", types.OPEN, types.FLUSHING, types.FLUSHCOMPLETE, channel.State) } // Authenticate capability to ensure caller has authority to receive packet on this channel diff --git a/modules/core/04-channel/keeper/packet_test.go b/modules/core/04-channel/keeper/packet_test.go index 9fc510c0900..1af25835f24 100644 --- a/modules/core/04-channel/keeper/packet_test.go +++ b/modules/core/04-channel/keeper/packet_test.go @@ -323,6 +323,38 @@ func (suite *KeeperTestSuite) TestRecvPacket() { }, nil, }, + { + "success UNORDERED channel in FLUSHING", + func() { + // setup uses an UNORDERED channel + suite.coordinator.Setup(path) + channel := path.EndpointB.GetChannel() + channel.State = types.FLUSHING + path.EndpointB.SetChannel(channel) + + sequence, err := path.EndpointA.SendPacket(defaultTimeoutHeight, disabledTimeoutTimestamp, ibctesting.MockPacketData) + suite.Require().NoError(err) + packet = types.NewPacket(ibctesting.MockPacketData, sequence, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, defaultTimeoutHeight, disabledTimeoutTimestamp) + channelCap = suite.chainB.GetChannelCapability(path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID) + }, + nil, + }, + { + "success UNORDERED channel in FLUSHCOMPLETE", + func() { + // setup uses an UNORDERED channel + suite.coordinator.Setup(path) + channel := path.EndpointB.GetChannel() + channel.State = types.FLUSHCOMPLETE + path.EndpointB.SetChannel(channel) + + sequence, err := path.EndpointA.SendPacket(defaultTimeoutHeight, disabledTimeoutTimestamp, ibctesting.MockPacketData) + suite.Require().NoError(err) + packet = types.NewPacket(ibctesting.MockPacketData, sequence, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, defaultTimeoutHeight, disabledTimeoutTimestamp) + channelCap = suite.chainB.GetChannelCapability(path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID) + }, + nil, + }, { "success with out of order packet: UNORDERED channel", func() { @@ -395,21 +427,6 @@ func (suite *KeeperTestSuite) TestRecvPacket() { }, types.ErrInvalidPacket, }, - { - "failure while upgrading channel, channel in flush complete state", - func() { - suite.coordinator.Setup(path) - sequence, err := path.EndpointA.SendPacket(defaultTimeoutHeight, disabledTimeoutTimestamp, ibctesting.MockPacketData) - suite.Require().NoError(err) - packet = types.NewPacket(ibctesting.MockPacketData, sequence, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, defaultTimeoutHeight, disabledTimeoutTimestamp) - channelCap = suite.chainB.GetChannelCapability(path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID) - - channel := path.EndpointB.GetChannel() - channel.State = types.FLUSHCOMPLETE - path.EndpointB.SetChannel(channel) - }, - types.ErrInvalidChannelState, - }, { "packet already relayed ORDERED channel (no-op)", func() { @@ -702,6 +719,32 @@ func (suite *KeeperTestSuite) TestWriteAcknowledgement() { }, true, }, + { + "success: channel flushing", + func() { + suite.coordinator.Setup(path) + packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, defaultTimeoutHeight, disabledTimeoutTimestamp) + ack = ibcmock.MockAcknowledgement + + err := path.EndpointB.SetChannelState(types.FLUSHING) + suite.Require().NoError(err) + channelCap = suite.chainB.GetChannelCapability(path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID) + }, + true, + }, + { + "success: channel flush complete", + func() { + suite.coordinator.Setup(path) + packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, defaultTimeoutHeight, disabledTimeoutTimestamp) + ack = ibcmock.MockAcknowledgement + + err := path.EndpointB.SetChannelState(types.FLUSHCOMPLETE) + suite.Require().NoError(err) + channelCap = suite.chainB.GetChannelCapability(path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID) + }, + true, + }, {"channel not found", func() { // use wrong channel naming suite.coordinator.Setup(path)