diff --git a/modules/core/04-channel/keeper/packet.go b/modules/core/04-channel/keeper/packet.go index d4019b32f0e..523fef04dcd 100644 --- a/modules/core/04-channel/keeper/packet.go +++ b/modules/core/04-channel/keeper/packet.go @@ -305,6 +305,16 @@ func (k Keeper) WriteAcknowledgement( ) } + // REPLAY PROTECTION: The recvStartSequence will prevent historical proofs from allowing replay + // attacks on packets processed in previous lifecycles of a channel. After a successful channel + // upgrade all packets under the recvStartSequence will have been processed and thus should be + // rejected. Any asynchronous acknowledgement writes from packets processed in a previous lifecycle of a channel + // will also be rejected. + recvStartSequence, _ := k.GetRecvStartSequence(ctx, packet.GetDestPort(), packet.GetDestChannel()) + if packet.GetSequence() < recvStartSequence { + return errorsmod.Wrap(types.ErrPacketReceived, "packet already processed in previous channel upgrade") + } + // NOTE: IBC app modules might have written the acknowledgement synchronously on // the OnRecvPacket callback so we need to check if the acknowledgement is already // set on the store and return an error if so. diff --git a/modules/core/04-channel/keeper/packet_test.go b/modules/core/04-channel/keeper/packet_test.go index 929bf8b4e89..b165375b3f3 100644 --- a/modules/core/04-channel/keeper/packet_test.go +++ b/modules/core/04-channel/keeper/packet_test.go @@ -777,6 +777,22 @@ func (suite *KeeperTestSuite) TestWriteAcknowledgement() { }, false, }, + { + "packet already received", + func() { + path.Setup() + + 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) + ack = ibcmock.MockAcknowledgement + + // set recv seq start to indicate packet was processed in previous upgrade + suite.chainB.App.GetIBCKeeper().ChannelKeeper.SetRecvStartSequence(suite.chainB.GetContext(), path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, sequence+1) + }, + false, + }, } for i, tc := range testCases { tc := tc