diff --git a/modules/core/ante/ante.go b/modules/core/ante/ante.go index 243a25f7442..1798df63566 100644 --- a/modules/core/ante/ante.go +++ b/modules/core/ante/ante.go @@ -7,6 +7,7 @@ import ( clienttypes "github.com/cosmos/ibc-go/v9/modules/core/02-client/types" channeltypes "github.com/cosmos/ibc-go/v9/modules/core/04-channel/types" + channeltypesv2 "github.com/cosmos/ibc-go/v9/modules/core/04-channel/v2/types" "github.com/cosmos/ibc-go/v9/modules/core/exported" "github.com/cosmos/ibc-go/v9/modules/core/keeper" ) @@ -89,6 +90,36 @@ func (rrd RedundantRelayDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simula return ctx, err } + case *channeltypesv2.MsgTimeout: + response, err := rrd.k.ChannelKeeperV2.Timeout(ctx, msg) + if err != nil { + return ctx, err + } + + if response.Result == channeltypes.NOOP { + redundancies++ + } + packetMsgs++ + case *channeltypesv2.MsgAcknowledgement: + response, err := rrd.k.ChannelKeeperV2.Acknowledgement(ctx, msg) + if err != nil { + return ctx, err + } + + if response.Result == channeltypes.NOOP { + redundancies++ + } + packetMsgs++ + case *channeltypesv2.MsgRecvPacket: + response, err := rrd.k.ChannelKeeperV2.RecvPacket(ctx, msg) + if err != nil { + return ctx, err + } + + if response.Result == channeltypes.NOOP { + redundancies++ + } + packetMsgs++ default: // if the multiMsg tx has a msg that is not a packet msg or update msg, then we will not return error // regardless of if all packet messages are redundant. This ensures that non-packet messages get processed diff --git a/modules/core/ante/ante_test.go b/modules/core/ante/ante_test.go index 1543e5803f4..5fb941c6532 100644 --- a/modules/core/ante/ante_test.go +++ b/modules/core/ante/ante_test.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "testing" + "time" "github.com/stretchr/testify/require" testifysuite "github.com/stretchr/testify/suite" @@ -13,12 +14,15 @@ import ( clienttypes "github.com/cosmos/ibc-go/v9/modules/core/02-client/types" channeltypes "github.com/cosmos/ibc-go/v9/modules/core/04-channel/types" + channeltypesv2 "github.com/cosmos/ibc-go/v9/modules/core/04-channel/v2/types" commitmenttypes "github.com/cosmos/ibc-go/v9/modules/core/23-commitment/types" host "github.com/cosmos/ibc-go/v9/modules/core/24-host" + hostv2 "github.com/cosmos/ibc-go/v9/modules/core/24-host/v2" "github.com/cosmos/ibc-go/v9/modules/core/ante" "github.com/cosmos/ibc-go/v9/modules/core/exported" ibctm "github.com/cosmos/ibc-go/v9/modules/light-clients/07-tendermint" ibctesting "github.com/cosmos/ibc-go/v9/testing" + "github.com/cosmos/ibc-go/v9/testing/mock/v2" ) type AnteTestSuite struct { @@ -74,6 +78,25 @@ func (suite *AnteTestSuite) createRecvPacketMessage(isRedundant bool) *channelty return channeltypes.NewMsgRecvPacket(packet, proof, proofHeight, suite.path.EndpointA.Chain.SenderAccount.GetAddress().String()) } +// createRecvPacketMessageV2 creates a V2 RecvPacket message for a packet sent from chain A to chain B. +func (suite *AnteTestSuite) createRecvPacketMessageV2(isRedundant bool) *channeltypesv2.MsgRecvPacket { + packet, err := suite.path.EndpointA.MsgSendPacket(suite.chainA.GetTimeoutTimestamp(), mock.NewMockPayload(mock.ModuleNameA, mock.ModuleNameB)) + suite.Require().NoError(err) + + if isRedundant { + err = suite.path.EndpointB.MsgRecvPacket(packet) + suite.Require().NoError(err) + } + + err = suite.path.EndpointB.UpdateClient() + suite.Require().NoError(err) + + packetKey := hostv2.PacketCommitmentKey(packet.SourceChannel, packet.Sequence) + proof, proofHeight := suite.chainA.QueryProof(packetKey) + + return channeltypesv2.NewMsgRecvPacket(packet, proof, proofHeight, suite.path.EndpointA.Chain.SenderAccount.GetAddress().String()) +} + // createAcknowledgementMessage creates an Acknowledgement message for a packet sent from chain B to chain A. func (suite *AnteTestSuite) createAcknowledgementMessage(isRedundant bool) sdk.Msg { sequence, err := suite.path.EndpointB.SendPacket(clienttypes.NewHeight(2, 0), 0, ibctesting.MockPacketData) @@ -97,6 +120,33 @@ func (suite *AnteTestSuite) createAcknowledgementMessage(isRedundant bool) sdk.M return channeltypes.NewMsgAcknowledgement(packet, ibctesting.MockAcknowledgement, proof, proofHeight, suite.path.EndpointA.Chain.SenderAccount.GetAddress().String()) } +// createAcknowledgementMessageV2 creates a V2 Acknowledgement message for a packet sent from chain B to chain A. +func (suite *AnteTestSuite) createAcknowledgementMessageV2(isRedundant bool) *channeltypesv2.MsgAcknowledgement { + packet, err := suite.path.EndpointB.MsgSendPacket(suite.chainB.GetTimeoutTimestamp(), mock.NewMockPayload(mock.ModuleNameA, mock.ModuleNameB)) + suite.Require().NoError(err) + + err = suite.path.EndpointA.MsgRecvPacket(packet) + suite.Require().NoError(err) + + ack := channeltypesv2.Acknowledgement{ + AcknowledgementResults: []channeltypesv2.AcknowledgementResult{ + { + AppName: mock.ModuleNameB, + RecvPacketResult: mock.MockRecvPacketResult, + }, + }, + } + if isRedundant { + err = suite.path.EndpointB.MsgAcknowledgePacket(packet, ack) + suite.Require().NoError(err) + } + + packetKey := hostv2.PacketAcknowledgementKey(packet.DestinationChannel, packet.Sequence) + proof, proofHeight := suite.chainA.QueryProof(packetKey) + + return channeltypesv2.NewMsgAcknowledgement(packet, ack, proof, proofHeight, suite.path.EndpointA.Chain.SenderAccount.GetAddress().String()) +} + // createTimeoutMessage creates an Timeout message for a packet sent from chain B to chain A. func (suite *AnteTestSuite) createTimeoutMessage(isRedundant bool) sdk.Msg { height := suite.chainA.LatestCommittedHeader.GetHeight() @@ -126,6 +176,27 @@ func (suite *AnteTestSuite) createTimeoutMessage(isRedundant bool) sdk.Msg { return channeltypes.NewMsgTimeout(packet, sequence, proof, proofHeight, suite.path.EndpointA.Chain.SenderAccount.GetAddress().String()) } +// createTimeoutMessageV2 creates a V2 Timeout message for a packet sent from chain B to chain A. +func (suite *AnteTestSuite) createTimeoutMessageV2(isRedundant bool) *channeltypesv2.MsgTimeout { + timeoutTimestamp := uint64(suite.chainB.GetContext().BlockTime().Unix()) + packet, err := suite.path.EndpointB.MsgSendPacket(timeoutTimestamp, mock.NewMockPayload(mock.ModuleNameA, mock.ModuleNameB)) + suite.Require().NoError(err) + + suite.coordinator.IncrementTimeBy(time.Hour) + err = suite.path.EndpointB.UpdateClient() + suite.Require().NoError(err) + + if isRedundant { + err = suite.path.EndpointB.MsgTimeoutPacket(packet) + suite.Require().NoError(err) + } + + packetKey := hostv2.PacketReceiptKey(packet.SourceChannel, packet.Sequence) + proof, proofHeight := suite.chainA.QueryProof(packetKey) + + return channeltypesv2.NewMsgTimeout(packet, proof, proofHeight, suite.path.EndpointA.Chain.SenderAccount.GetAddress().String()) +} + // createTimeoutOnCloseMessage creates an TimeoutOnClose message for a packet sent from chain B to chain A. func (suite *AnteTestSuite) createTimeoutOnCloseMessage(isRedundant bool) sdk.Msg { height := suite.chainA.LatestCommittedHeader.GetHeight() @@ -193,6 +264,15 @@ func (suite *AnteTestSuite) TestAnteDecoratorCheckTx() { }, nil, }, + { + "success on one new V2 RecvPacket message", + func(suite *AnteTestSuite) []sdk.Msg { + suite.path.SetupV2() + // the RecvPacket message has not been submitted to the chain yet, so it will succeed + return []sdk.Msg{suite.createRecvPacketMessageV2(false)} + }, + nil, + }, { "success on one new Acknowledgement message", func(suite *AnteTestSuite) []sdk.Msg { @@ -201,6 +281,15 @@ func (suite *AnteTestSuite) TestAnteDecoratorCheckTx() { }, nil, }, + { + "success on one new V2 Acknowledgement message", + func(suite *AnteTestSuite) []sdk.Msg { + suite.path.SetupV2() + // the Acknowledgement message has not been submitted to the chain yet, so it will succeed + return []sdk.Msg{suite.createAcknowledgementMessageV2(false)} + }, + nil, + }, { "success on one new Timeout message", func(suite *AnteTestSuite) []sdk.Msg { @@ -209,6 +298,15 @@ func (suite *AnteTestSuite) TestAnteDecoratorCheckTx() { }, nil, }, + { + "success on one new Timeout V2 message", + func(suite *AnteTestSuite) []sdk.Msg { + suite.path.SetupV2() + // the Timeout message has not been submitted to the chain yet, so it will succeed + return []sdk.Msg{suite.createTimeoutMessageV2(false)} + }, + nil, + }, { "success on one new TimeoutOnClose message", func(suite *AnteTestSuite) []sdk.Msg { @@ -368,6 +466,14 @@ func (suite *AnteTestSuite) TestAnteDecoratorCheckTx() { }, channeltypes.ErrRedundantTx, }, + { + "no success on one redundant V2 RecvPacket message", + func(suite *AnteTestSuite) []sdk.Msg { + suite.path.SetupV2() + return []sdk.Msg{suite.createRecvPacketMessageV2(true)} + }, + channeltypes.ErrRedundantTx, + }, { "no success on three redundant messages of each type", func(suite *AnteTestSuite) []sdk.Msg { @@ -555,6 +661,15 @@ func (suite *AnteTestSuite) TestAnteDecoratorReCheckTx() { }, nil, }, + { + "success on one new V2 RecvPacket message", + func(suite *AnteTestSuite) []sdk.Msg { + suite.path.SetupV2() + // the RecvPacket message has not been submitted to the chain yet, so it will succeed + return []sdk.Msg{suite.createRecvPacketMessageV2(false)} + }, + nil, + }, { "success on one redundant and one new RecvPacket message", func(suite *AnteTestSuite) []sdk.Msg { @@ -595,6 +710,14 @@ func (suite *AnteTestSuite) TestAnteDecoratorReCheckTx() { }, channeltypes.ErrRedundantTx, }, + { + "no success on one redundant V2 RecvPacket message", + func(suite *AnteTestSuite) []sdk.Msg { + suite.path.SetupV2() + return []sdk.Msg{suite.createRecvPacketMessageV2(true)} + }, + channeltypes.ErrRedundantTx, + }, } for _, tc := range testCases {