package ante_test import ( "errors" "testing" "time" "github.com/stretchr/testify/require" testifysuite "github.com/stretchr/testify/suite" codectypes "git.cw.tr/mukan-network/mukan-sdk/codec/types" sdk "git.cw.tr/mukan-network/mukan-sdk/types" clienttypes "git.cw.tr/mukan-network/mukan-ibc/modules/core/02-client/types" channeltypes "git.cw.tr/mukan-network/mukan-ibc/modules/core/04-channel/types" channeltypesv2 "git.cw.tr/mukan-network/mukan-ibc/modules/core/04-channel/v2/types" commitmenttypes "git.cw.tr/mukan-network/mukan-ibc/modules/core/23-commitment/types" host "git.cw.tr/mukan-network/mukan-ibc/modules/core/24-host" hostv2 "git.cw.tr/mukan-network/mukan-ibc/modules/core/24-host/v2" "git.cw.tr/mukan-network/mukan-ibc/modules/core/ante" "git.cw.tr/mukan-network/mukan-ibc/modules/core/exported" ibctm "git.cw.tr/mukan-network/mukan-ibc/modules/light-clients/07-tendermint" ibctesting "git.cw.tr/mukan-network/mukan-ibc/testing" "git.cw.tr/mukan-network/mukan-ibc/testing/mock/v2" ) type AnteTestSuite struct { testifysuite.Suite coordinator *ibctesting.Coordinator // testing chains used for convenience and readability chainA *ibctesting.TestChain chainB *ibctesting.TestChain path *ibctesting.Path } // SetupTest creates a coordinator with 2 test chains. func (suite *AnteTestSuite) SetupTest() { suite.coordinator = ibctesting.NewCoordinator(suite.T(), 2) suite.chainA = suite.coordinator.GetChain(ibctesting.GetChainID(1)) suite.chainB = suite.coordinator.GetChain(ibctesting.GetChainID(2)) // commit some blocks so that QueryProof returns valid proof (cannot return valid query if height <= 1) suite.coordinator.CommitNBlocks(suite.chainA, 2) suite.coordinator.CommitNBlocks(suite.chainB, 2) suite.path = ibctesting.NewPath(suite.chainA, suite.chainB) suite.path.Setup() } // TestAnteTestSuite runs all the tests within this package. func TestAnteTestSuite(t *testing.T) { testifysuite.Run(t, new(AnteTestSuite)) } // createRecvPacketMessage creates a RecvPacket message for a packet sent from chain A to chain B. func (suite *AnteTestSuite) createRecvPacketMessage(isRedundant bool) *channeltypes.MsgRecvPacket { sequence, err := suite.path.EndpointA.SendPacket(clienttypes.NewHeight(2, 0), 0, ibctesting.MockPacketData) suite.Require().NoError(err) packet := channeltypes.NewPacket(ibctesting.MockPacketData, sequence, suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, suite.path.EndpointB.ChannelConfig.PortID, suite.path.EndpointB.ChannelID, clienttypes.NewHeight(2, 0), 0) if isRedundant { err = suite.path.EndpointB.RecvPacket(packet) suite.Require().NoError(err) } err = suite.path.EndpointB.UpdateClient() suite.Require().NoError(err) packetKey := host.PacketCommitmentKey(packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence()) proof, proofHeight := suite.chainA.QueryProof(packetKey) 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.GetTimeoutTimestampSecs(), 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.SourceClient, 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) suite.Require().NoError(err) packet := channeltypes.NewPacket(ibctesting.MockPacketData, sequence, suite.path.EndpointB.ChannelConfig.PortID, suite.path.EndpointB.ChannelID, suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, clienttypes.NewHeight(2, 0), 0) err = suite.path.EndpointA.RecvPacket(packet) suite.Require().NoError(err) if isRedundant { err = suite.path.EndpointB.AcknowledgePacket(packet, ibctesting.MockAcknowledgement) suite.Require().NoError(err) } packetKey := host.PacketAcknowledgementKey(packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence()) proof, proofHeight := suite.chainA.QueryProof(packetKey) 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.GetTimeoutTimestampSecs(), mock.NewMockPayload(mock.ModuleNameA, mock.ModuleNameB)) suite.Require().NoError(err) err = suite.path.EndpointA.MsgRecvPacket(packet) suite.Require().NoError(err) ack := channeltypesv2.Acknowledgement{AppAcknowledgements: [][]byte{mock.MockRecvPacketResult.Acknowledgement}} if isRedundant { err = suite.path.EndpointB.MsgAcknowledgePacket(packet, ack) suite.Require().NoError(err) } packetKey := hostv2.PacketAcknowledgementKey(packet.DestinationClient, 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() timeoutHeight := clienttypes.NewHeight(height.GetRevisionNumber(), height.GetRevisionHeight()+1) sequence, err := suite.path.EndpointB.SendPacket(timeoutHeight, 0, ibctesting.MockPacketData) suite.Require().NoError(err) suite.coordinator.CommitNBlocks(suite.chainA, 3) err = suite.path.EndpointB.UpdateClient() suite.Require().NoError(err) packet := channeltypes.NewPacket(ibctesting.MockPacketData, sequence, suite.path.EndpointB.ChannelConfig.PortID, suite.path.EndpointB.ChannelID, suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, timeoutHeight, 0) if isRedundant { err = suite.path.EndpointB.TimeoutPacket(packet) suite.Require().NoError(err) } packetKey := host.PacketReceiptKey(packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence()) proof, proofHeight := suite.chainA.QueryProof(packetKey) 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().Add(time.Second).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.SourceClient, 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() timeoutHeight := clienttypes.NewHeight(height.GetRevisionNumber(), height.GetRevisionHeight()+1) sequence, err := suite.path.EndpointB.SendPacket(timeoutHeight, 0, ibctesting.MockPacketData) suite.Require().NoError(err) suite.path.EndpointA.UpdateChannel(func(channel *channeltypes.Channel) { channel.State = channeltypes.CLOSED }) packet := channeltypes.NewPacket(ibctesting.MockPacketData, sequence, suite.path.EndpointB.ChannelConfig.PortID, suite.path.EndpointB.ChannelID, suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, timeoutHeight, 0) if isRedundant { err = suite.path.EndpointB.TimeoutOnClose(packet) suite.Require().NoError(err) } packetKey := host.PacketReceiptKey(packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence()) proof, proofHeight := suite.chainA.QueryProof(packetKey) channelKey := host.ChannelKey(packet.GetDestPort(), packet.GetDestChannel()) closedProof, _ := suite.chainA.QueryProof(channelKey) return channeltypes.NewMsgTimeoutOnClose(packet, 1, proof, closedProof, proofHeight, suite.path.EndpointA.Chain.SenderAccount.GetAddress().String()) } func (suite *AnteTestSuite) createUpdateClientMessage() sdk.Msg { endpoint := suite.path.EndpointB // ensure counterparty has committed state endpoint.Chain.Coordinator.CommitBlock(endpoint.Counterparty.Chain) var header exported.ClientMessage switch endpoint.ClientConfig.GetClientType() { case exported.Tendermint: trustedHeight := endpoint.GetClientLatestHeight() header, _ = endpoint.Counterparty.Chain.IBCClientHeader(endpoint.Counterparty.Chain.LatestCommittedHeader, trustedHeight.(clienttypes.Height)) default: } msg, err := clienttypes.NewMsgUpdateClient( endpoint.ClientID, header, endpoint.Chain.SenderAccount.GetAddress().String(), ) require.NoError(endpoint.Chain.TB, err) return msg } func (suite *AnteTestSuite) TestAnteDecoratorCheckTx() { testCases := []struct { name string malleate func(suite *AnteTestSuite) []sdk.Msg expError error }{ { "success on one new RecvPacket message", func(suite *AnteTestSuite) []sdk.Msg { // the RecvPacket message has not been submitted to the chain yet, so it will succeed return []sdk.Msg{suite.createRecvPacketMessage(false)} }, 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 { // the Acknowledgement message has not been submitted to the chain yet, so it will succeed return []sdk.Msg{suite.createAcknowledgementMessage(false)} }, 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 { // the Timeout message has not been submitted to the chain yet, so it will succeed return []sdk.Msg{suite.createTimeoutMessage(false)} }, 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 { // the TimeoutOnClose message has not been submitted to the chain yet, so it will succeed return []sdk.Msg{suite.createTimeoutOnCloseMessage(false)} }, nil, }, { "success on three new messages of each type", func(suite *AnteTestSuite) []sdk.Msg { var msgs []sdk.Msg // none of the messages of each type has been submitted to the chain yet, // the first message is succeed and the next two of each type will be rejected // because they are redundant. // from A to B for i := 1; i <= 3; i++ { msgs = append(msgs, suite.createRecvPacketMessage(false)) } // from B to A for i := 1; i <= 9; i++ { switch { case i >= 1 && i <= 3: msgs = append(msgs, suite.createAcknowledgementMessage(false)) case i >= 4 && i <= 6: msgs = append(msgs, suite.createTimeoutMessage(false)) case i >= 7 && i <= 9: msgs = append(msgs, suite.createTimeoutOnCloseMessage(false)) } } return msgs }, nil, }, { "success on three redundant messages of RecvPacket, Acknowledgement and TimeoutOnClose, and one new Timeout message", func(suite *AnteTestSuite) []sdk.Msg { var msgs []sdk.Msg // we pass three messages of RecvPacket, Acknowledgement and TimeoutOnClose that // are all redundant (i.e. those messages have already been submitted and // processed by the chain). But these messages will not be rejected because the // Timeout message is new. // from A to B for i := 1; i <= 3; i++ { msgs = append(msgs, suite.createRecvPacketMessage(true)) } // from B to A for i := 1; i <= 7; i++ { switch { case i >= 1 && i <= 3: msgs = append(msgs, suite.createAcknowledgementMessage(true)) case i == 4: msgs = append(msgs, suite.createTimeoutMessage(false)) case i >= 5 && i <= 7: msgs = append(msgs, suite.createTimeoutOnCloseMessage(true)) } } return msgs }, nil, }, { "success on one new message and two redundant messages of each type", func(suite *AnteTestSuite) []sdk.Msg { var msgs []sdk.Msg // For each type there is a new message and two messages that are redundant // (i.e. they have been already submitted and processed by the chain). But all // the redundant messages will not be rejected because there is a new message // of each type. // from A to B for i := 1; i <= 3; i++ { msgs = append(msgs, suite.createRecvPacketMessage(i != 2)) } // from B to A for i := 1; i <= 9; i++ { switch { case i >= 1 && i <= 3: msgs = append(msgs, suite.createAcknowledgementMessage(i != 2)) case i >= 4 && i <= 6: msgs = append(msgs, suite.createTimeoutMessage(i != 5)) case i >= 7 && i <= 9: msgs = append(msgs, suite.createTimeoutOnCloseMessage(i != 8)) } } return msgs }, nil, }, { "success on one new UpdateClient message", func(suite *AnteTestSuite) []sdk.Msg { return []sdk.Msg{suite.createUpdateClientMessage()} }, nil, }, { "success on three new UpdateClient messages", func(suite *AnteTestSuite) []sdk.Msg { return []sdk.Msg{suite.createUpdateClientMessage(), suite.createUpdateClientMessage(), suite.createUpdateClientMessage()} }, nil, }, { "success on three new Updateclient messages and one new RecvPacket message", func(suite *AnteTestSuite) []sdk.Msg { return []sdk.Msg{ suite.createUpdateClientMessage(), suite.createUpdateClientMessage(), suite.createUpdateClientMessage(), suite.createRecvPacketMessage(false), } }, nil, }, { "success on three redundant RecvPacket messages and one SubmitMisbehaviour message", func(suite *AnteTestSuite) []sdk.Msg { msgs := []sdk.Msg{suite.createUpdateClientMessage()} for i := 1; i <= 3; i++ { msgs = append(msgs, suite.createRecvPacketMessage(true)) } // append non packet and update message to msgs to ensure multimsg tx should pass msgs = append(msgs, &clienttypes.MsgSubmitMisbehaviour{}) //nolint:staticcheck // we're using the deprecated message for testing return msgs }, nil, }, { "success on app callback error, app callbacks are skipped for performance", func(suite *AnteTestSuite) []sdk.Msg { suite.chainB.GetSimApp().IBCMockModule.IBCApp.OnRecvPacket = func( ctx sdk.Context, channelVersion string, packet channeltypes.Packet, relayer sdk.AccAddress, ) exported.Acknowledgement { panic(errors.New("failed OnRecvPacket mock callback")) } // the RecvPacket message has not been submitted to the chain yet, so it will succeed return []sdk.Msg{suite.createRecvPacketMessage(false)} }, nil, }, { "no success on one redundant RecvPacket message", func(suite *AnteTestSuite) []sdk.Msg { return []sdk.Msg{suite.createRecvPacketMessage(true)} }, 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 { var msgs []sdk.Msg // from A to B for i := 1; i <= 3; i++ { msgs = append(msgs, suite.createRecvPacketMessage(true)) } // from B to A for i := 1; i <= 9; i++ { switch { case i >= 1 && i <= 3: msgs = append(msgs, suite.createAcknowledgementMessage(true)) case i >= 4 && i <= 6: msgs = append(msgs, suite.createTimeoutMessage(true)) case i >= 7 && i <= 9: msgs = append(msgs, suite.createTimeoutOnCloseMessage(true)) } } return msgs }, channeltypes.ErrRedundantTx, }, { "no success on one new UpdateClient message and three redundant RecvPacket messages", func(suite *AnteTestSuite) []sdk.Msg { msgs := []sdk.Msg{suite.createUpdateClientMessage()} for i := 1; i <= 3; i++ { msgs = append(msgs, suite.createRecvPacketMessage(true)) } return msgs }, channeltypes.ErrRedundantTx, }, { "no success on one new UpdateClient message: invalid client identifier", func(suite *AnteTestSuite) []sdk.Msg { clientMsg, err := codectypes.NewAnyWithValue(&ibctm.Header{}) suite.Require().NoError(err) msgs := []sdk.Msg{&clienttypes.MsgUpdateClient{ClientId: ibctesting.InvalidID, ClientMessage: clientMsg}} return msgs }, clienttypes.ErrClientNotActive, }, { "no success on one new UpdateClient message: client module not found", func(suite *AnteTestSuite) []sdk.Msg { clientMsg, err := codectypes.NewAnyWithValue(&ibctm.Header{}) suite.Require().NoError(err) msgs := []sdk.Msg{&clienttypes.MsgUpdateClient{ClientId: clienttypes.FormatClientIdentifier("08-wasm", 1), ClientMessage: clientMsg}} return msgs }, clienttypes.ErrClientNotActive, }, { "no success on one new UpdateClient message: no consensus state for trusted height", func(suite *AnteTestSuite) []sdk.Msg { clientMsg, err := codectypes.NewAnyWithValue(&ibctm.Header{TrustedHeight: clienttypes.NewHeight(1, 10000)}) suite.Require().NoError(err) msgs := []sdk.Msg{&clienttypes.MsgUpdateClient{ClientId: suite.path.EndpointA.ClientID, ClientMessage: clientMsg}} return msgs }, clienttypes.ErrConsensusStateNotFound, }, { "no success on three new UpdateClient messages and three redundant messages of each type", func(suite *AnteTestSuite) []sdk.Msg { msgs := []sdk.Msg{suite.createUpdateClientMessage(), suite.createUpdateClientMessage(), suite.createUpdateClientMessage()} // from A to B for i := 1; i <= 3; i++ { msgs = append(msgs, suite.createRecvPacketMessage(true)) } // from B to A for i := 1; i <= 9; i++ { switch { case i >= 1 && i <= 3: msgs = append(msgs, suite.createAcknowledgementMessage(true)) case i >= 4 && i <= 6: msgs = append(msgs, suite.createTimeoutMessage(true)) case i >= 7 && i <= 9: msgs = append(msgs, suite.createTimeoutOnCloseMessage(true)) } } return msgs }, channeltypes.ErrRedundantTx, }, { "no success on one new message and one invalid message", func(suite *AnteTestSuite) []sdk.Msg { packet := channeltypes.NewPacket(ibctesting.MockPacketData, 2, suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, suite.path.EndpointB.ChannelConfig.PortID, suite.path.EndpointB.ChannelID, clienttypes.NewHeight(2, 0), 0) return []sdk.Msg{ suite.createRecvPacketMessage(false), channeltypes.NewMsgRecvPacket(packet, []byte("proof"), clienttypes.NewHeight(1, 1), "signer"), } }, commitmenttypes.ErrInvalidProof, }, { "no success on one new message and one redundant message in the same block", func(suite *AnteTestSuite) []sdk.Msg { msg := suite.createRecvPacketMessage(false) // We want to be able to run check tx with the non-redundant message without // committing it to a block, so that the when check tx runs with the redundant // message they are both in the same block k := suite.chainB.App.GetIBCKeeper() decorator := ante.NewRedundantRelayDecorator(k) checkCtx := suite.chainB.GetContext().WithIsCheckTx(true) next := func(ctx sdk.Context, tx sdk.Tx, simulate bool) (newCtx sdk.Context, err error) { return ctx, nil } txBuilder := suite.chainB.TxConfig.NewTxBuilder() err := txBuilder.SetMsgs([]sdk.Msg{msg}...) suite.Require().NoError(err) tx := txBuilder.GetTx() _, err = decorator.AnteHandle(checkCtx, tx, false, next) suite.Require().NoError(err) return []sdk.Msg{msg} }, channeltypes.ErrRedundantTx, }, } for _, tc := range testCases { suite.Run(tc.name, func() { // reset suite suite.SetupTest() k := suite.chainB.App.GetIBCKeeper() decorator := ante.NewRedundantRelayDecorator(k) msgs := tc.malleate(suite) deliverCtx := suite.chainB.GetContext().WithIsCheckTx(false) checkCtx := suite.chainB.GetContext().WithIsCheckTx(true) // create multimsg tx txBuilder := suite.chainB.TxConfig.NewTxBuilder() err := txBuilder.SetMsgs(msgs...) suite.Require().NoError(err) tx := txBuilder.GetTx() next := func(ctx sdk.Context, tx sdk.Tx, simulate bool) (newCtx sdk.Context, err error) { return ctx, nil } _, err = decorator.AnteHandle(deliverCtx, tx, false, next) suite.Require().NoError(err, "antedecorator should not error on DeliverTx") _, err = decorator.AnteHandle(checkCtx, tx, false, next) if tc.expError == nil { suite.Require().NoError(err, "non-strict decorator did not pass as expected") } else { suite.Require().ErrorIs(err, tc.expError, "non-strict antehandler did not return error as expected") } }) } } func (suite *AnteTestSuite) TestAnteDecoratorReCheckTx() { testCases := []struct { name string malleate func(suite *AnteTestSuite) []sdk.Msg expError error }{ { "success on one new RecvPacket message", func(suite *AnteTestSuite) []sdk.Msg { // the RecvPacket message has not been submitted to the chain yet, so it will succeed return []sdk.Msg{suite.createRecvPacketMessage(false)} }, 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 { return []sdk.Msg{ suite.createRecvPacketMessage(true), suite.createRecvPacketMessage(false), } }, nil, }, { "success on invalid proof (proof checks occur in checkTx)", func(suite *AnteTestSuite) []sdk.Msg { msg := suite.createRecvPacketMessage(false) msg.ProofCommitment = []byte("invalid-proof") return []sdk.Msg{msg} }, nil, }, { "success on app callback error, app callbacks are skipped for performance", func(suite *AnteTestSuite) []sdk.Msg { suite.chainB.GetSimApp().IBCMockModule.IBCApp.OnRecvPacket = func( ctx sdk.Context, channelVersion string, packet channeltypes.Packet, relayer sdk.AccAddress, ) exported.Acknowledgement { panic(errors.New("failed OnRecvPacket mock callback")) } // the RecvPacket message has not been submitted to the chain yet, so it will succeed return []sdk.Msg{suite.createRecvPacketMessage(false)} }, nil, }, { "no success on one redundant RecvPacket message", func(suite *AnteTestSuite) []sdk.Msg { return []sdk.Msg{suite.createRecvPacketMessage(true)} }, 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 { suite.Run(tc.name, func() { // reset suite suite.SetupTest() k := suite.chainB.App.GetIBCKeeper() decorator := ante.NewRedundantRelayDecorator(k) msgs := tc.malleate(suite) deliverCtx := suite.chainB.GetContext().WithIsCheckTx(false) reCheckCtx := suite.chainB.GetContext().WithIsReCheckTx(true) // create multimsg tx txBuilder := suite.chainB.TxConfig.NewTxBuilder() err := txBuilder.SetMsgs(msgs...) suite.Require().NoError(err) tx := txBuilder.GetTx() next := func(ctx sdk.Context, tx sdk.Tx, simulate bool) (newCtx sdk.Context, err error) { return ctx, nil } _, err = decorator.AnteHandle(deliverCtx, tx, false, next) suite.Require().NoError(err, "antedecorator should not error on DeliverTx") _, err = decorator.AnteHandle(reCheckCtx, tx, false, next) if tc.expError == nil { suite.Require().NoError(err, "non-strict decorator did not pass as expected") } else { suite.Require().ErrorIs(err, tc.expError, "non-strict antehandler did not return error as expected") } }) } }