From 66ea43598fe7ae6b460c8bbc0e680b23d86e30bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?colin=20axn=C3=A9r?= <25233464+colin-axner@users.noreply.github.com> Date: Tue, 21 May 2024 11:28:16 +0200 Subject: [PATCH 1/2] perf: minimize logic on rechecktx for recvpacket (#6280) * perf: minimize logic on rechecktx for recvpacket * refactor: rework layout for recvpacket rechecktx * test: add tests for 04-channel rechecktx func * test: add tests for core ante handler * chore: add comment explaining is rechecktx usage * linter appeasement * chore: add changelog entry * Update modules/core/ante/ante.go Co-authored-by: Carlos Rodriguez * imp: use cached ctx for consistency * refactor: change added test to use expected errors * lint --------- Co-authored-by: Carlos Rodriguez (cherry picked from commit 56ae97d806339cd83cb346502bae20775a2b7ca7) # Conflicts: # modules/core/04-channel/keeper/packet.go # modules/core/ante/ante.go --- CHANGELOG.md | 1 + modules/core/04-channel/keeper/ante.go | 24 ++++++ modules/core/04-channel/keeper/ante_test.go | 64 ++++++++++++++ modules/core/04-channel/keeper/packet.go | 39 +++++++++ modules/core/ante/ante.go | 60 ++++++++++++- modules/core/ante/ante_test.go | 94 ++++++++++++++++++++- 6 files changed, 280 insertions(+), 2 deletions(-) create mode 100644 modules/core/04-channel/keeper/ante.go create mode 100644 modules/core/04-channel/keeper/ante_test.go diff --git a/CHANGELOG.md b/CHANGELOG.md index f3db8c09cc0..d66487636b1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -45,6 +45,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ ### Improvements * (core/ante) [\#6302](https://github.com/cosmos/ibc-go/pull/6302) Performance: Skip app callbacks during RecvPacket execution in checkTx within the redundant relay ante handler. +* (core/ante) [\#6280](https://github.com/cosmos/ibc-go/pull/6280) Performance: Skip redundant proof checking in RecvPacket execution in reCheckTx within the redundant relay ante handler. ### Features diff --git a/modules/core/04-channel/keeper/ante.go b/modules/core/04-channel/keeper/ante.go new file mode 100644 index 00000000000..ac387569c68 --- /dev/null +++ b/modules/core/04-channel/keeper/ante.go @@ -0,0 +1,24 @@ +package keeper + +import ( + errorsmod "cosmossdk.io/errors" + + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/cosmos/ibc-go/v8/modules/core/04-channel/types" +) + +// RecvPacketReCheckTx applies replay protection ensuring that when relay messages are +// re-executed in ReCheckTx, we can appropriately filter out redundant relay transactions. +func (k *Keeper) RecvPacketReCheckTx(ctx sdk.Context, packet types.Packet) error { + channel, found := k.GetChannel(ctx, packet.GetDestPort(), packet.GetDestChannel()) + if !found { + return errorsmod.Wrap(types.ErrChannelNotFound, packet.GetDestChannel()) + } + + if err := k.applyReplayProtection(ctx, packet, channel); err != nil { + return err + } + + return nil +} diff --git a/modules/core/04-channel/keeper/ante_test.go b/modules/core/04-channel/keeper/ante_test.go new file mode 100644 index 00000000000..4bc62ff2ae6 --- /dev/null +++ b/modules/core/04-channel/keeper/ante_test.go @@ -0,0 +1,64 @@ +package keeper_test + +import ( + "github.com/cosmos/ibc-go/v8/modules/core/04-channel/types" + ibctesting "github.com/cosmos/ibc-go/v8/testing" +) + +func (suite *KeeperTestSuite) TestRecvPacketReCheckTx() { + var ( + path *ibctesting.Path + packet types.Packet + ) + + testCases := []struct { + name string + malleate func() + expError error + }{ + { + "success", + func() {}, + nil, + }, + { + "channel not found", + func() { + packet.DestinationPort = "invalid-port" //nolint:goconst + }, + types.ErrChannelNotFound, + }, + { + "redundant relay", + func() { + err := suite.chainB.App.GetIBCKeeper().ChannelKeeper.RecvPacketReCheckTx(suite.chainB.GetContext(), packet) + suite.Require().NoError(err) + }, + types.ErrNoOpMsg, + }, + } + + for _, tc := range testCases { + tc := tc + suite.Run(tc.name, func() { + suite.SetupTest() // reset + path = ibctesting.NewPath(suite.chainA, suite.chainB) + 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) + + tc.malleate() + + err = suite.chainB.App.GetIBCKeeper().ChannelKeeper.RecvPacketReCheckTx(suite.chainB.GetContext(), packet) + + expPass := tc.expError == nil + if expPass { + suite.Require().NoError(err) + } else { + suite.Require().ErrorIs(err, tc.expError) + } + }) + } +} diff --git a/modules/core/04-channel/keeper/packet.go b/modules/core/04-channel/keeper/packet.go index 69a3f77aa11..2200902603f 100644 --- a/modules/core/04-channel/keeper/packet.go +++ b/modules/core/04-channel/keeper/packet.go @@ -201,7 +201,43 @@ func (k Keeper) RecvPacket( packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence(), commitment, ); err != nil { +<<<<<<< HEAD return sdkerrors.Wrap(err, "couldn't verify counterparty packet commitment") +======= + return errorsmod.Wrap(err, "couldn't verify counterparty packet commitment") + } + + if err := k.applyReplayProtection(ctx, packet, channel); err != nil { + return err + } + + // log that a packet has been received & executed + k.Logger(ctx).Info( + "packet received", + "sequence", strconv.FormatUint(packet.GetSequence(), 10), + "src_port", packet.GetSourcePort(), + "src_channel", packet.GetSourceChannel(), + "dst_port", packet.GetDestPort(), + "dst_channel", packet.GetDestChannel(), + ) + + // emit an event that the relayer can query for + emitRecvPacketEvent(ctx, packet, channel) + + return nil +} + +// applyReplayProtection ensures a packet has not already been received +// and performs the necessary state changes to ensure it cannot be received again. +func (k *Keeper) applyReplayProtection(ctx sdk.Context, packet types.Packet, channel types.Channel) error { + // 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. + recvStartSequence, _ := k.GetRecvStartSequence(ctx, packet.GetDestPort(), packet.GetDestChannel()) + if packet.GetSequence() < recvStartSequence { + return errorsmod.Wrap(types.ErrPacketReceived, "packet already processed in previous channel upgrade") +>>>>>>> 56ae97d8 (perf: minimize logic on rechecktx for recvpacket (#6280)) } switch channel.Ordering { @@ -257,6 +293,7 @@ func (k Keeper) RecvPacket( } +<<<<<<< HEAD // log that a packet has been received & executed k.Logger(ctx).Info( "packet received", @@ -270,6 +307,8 @@ func (k Keeper) RecvPacket( // emit an event that the relayer can query for EmitRecvPacketEvent(ctx, packet, channel) +======= +>>>>>>> 56ae97d8 (perf: minimize logic on rechecktx for recvpacket (#6280)) return nil } diff --git a/modules/core/ante/ante.go b/modules/core/ante/ante.go index a4d852ecaa1..da465a16465 100644 --- a/modules/core/ante/ante.go +++ b/modules/core/ante/ante.go @@ -36,10 +36,14 @@ func (rrd RedundantRelayDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simula err error ) // when we are in ReCheckTx mode, ctx.IsCheckTx() will also return true - // there we must start the if statement on ctx.IsReCheckTx() to correctly + // therefore we must start the if statement on ctx.IsReCheckTx() to correctly // determine which mode we are in if ctx.IsReCheckTx() { +<<<<<<< HEAD response, err = rrd.k.RecvPacket(sdk.WrapSDKContext(ctx), msg) +======= + response, err = rrd.recvPacketReCheckTx(ctx, msg) +>>>>>>> 56ae97d8 (perf: minimize logic on rechecktx for recvpacket (#6280)) } else { response, err = rrd.recvPacketCheckTx(ctx, msg) } @@ -129,3 +133,57 @@ func (rrd RedundantRelayDecorator) recvPacketCheckTx(ctx sdk.Context, msg *chann return &channeltypes.MsgRecvPacketResponse{Result: channeltypes.SUCCESS}, nil } +<<<<<<< HEAD +======= + +// recvPacketReCheckTx runs a subset of ibc recv packet logic to be used specifically within the RedundantRelayDecorator AnteHandler. +// It only performs core IBC receiving logic and skips any application logic. +func (rrd RedundantRelayDecorator) recvPacketReCheckTx(ctx sdk.Context, msg *channeltypes.MsgRecvPacket) (*channeltypes.MsgRecvPacketResponse, error) { + // If the packet was already received, perform a no-op + // Use a cached context to prevent accidental state changes + cacheCtx, writeFn := ctx.CacheContext() + err := rrd.k.ChannelKeeper.RecvPacketReCheckTx(cacheCtx, msg.Packet) + + switch err { + case nil: + writeFn() + case channeltypes.ErrNoOpMsg: + return &channeltypes.MsgRecvPacketResponse{Result: channeltypes.NOOP}, nil + default: + return nil, errorsmod.Wrap(err, "receive packet verification failed") + } + + return &channeltypes.MsgRecvPacketResponse{Result: channeltypes.SUCCESS}, nil +} + +// updateClientCheckTx runs a subset of ibc client update logic to be used specifically within the RedundantRelayDecorator AnteHandler. +// The following function performs ibc client message verification for CheckTx only and state updates in both CheckTx and ReCheckTx. +// Note that misbehaviour checks are omitted. +func (rrd RedundantRelayDecorator) updateClientCheckTx(ctx sdk.Context, msg *clienttypes.MsgUpdateClient) error { + clientMsg, err := clienttypes.UnpackClientMessage(msg.ClientMessage) + if err != nil { + return err + } + + if status := rrd.k.ClientKeeper.GetClientStatus(ctx, msg.ClientId); status != exported.Active { + return errorsmod.Wrapf(clienttypes.ErrClientNotActive, "cannot update client (%s) with status %s", msg.ClientId, status) + } + + clientModule, found := rrd.k.ClientKeeper.Route(msg.ClientId) + if !found { + return errorsmod.Wrap(clienttypes.ErrRouteNotFound, msg.ClientId) + } + + if !ctx.IsReCheckTx() { + if err := clientModule.VerifyClientMessage(ctx, msg.ClientId, clientMsg); err != nil { + return err + } + } + + heights := clientModule.UpdateState(ctx, msg.ClientId, clientMsg) + + ctx.Logger().With("module", "x/"+exported.ModuleName).Debug("ante ibc client update", "consensusHeights", heights) + + return nil +} +>>>>>>> 56ae97d8 (perf: minimize logic on rechecktx for recvpacket (#6280)) diff --git a/modules/core/ante/ante_test.go b/modules/core/ante/ante_test.go index b51857acea4..1ba0015a965 100644 --- a/modules/core/ante/ante_test.go +++ b/modules/core/ante/ante_test.go @@ -174,7 +174,7 @@ func (suite *AnteTestSuite) createUpdateClientMessage() sdk.Msg { return msg } -func (suite *AnteTestSuite) TestAnteDecorator() { +func (suite *AnteTestSuite) TestAnteDecoratorCheckTx() { testCases := []struct { name string malleate func(suite *AnteTestSuite) []sdk.Msg @@ -497,3 +497,95 @@ func (suite *AnteTestSuite) TestAnteDecorator() { }) } } + +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 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, packet channeltypes.Packet, relayer sdk.AccAddress, + ) exported.Acknowledgement { + panic(fmt.Errorf("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, + }, + } + + for _, tc := range testCases { + tc := tc + + 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") + } + }) + } +} From 5e99c80e24565f94be13c6ab99892b55adc0875d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Colin=20Axn=C3=A9r?= <25233464+colin-axner@users.noreply.github.com> Date: Tue, 21 May 2024 12:05:42 +0200 Subject: [PATCH 2/2] fix: merge conflicts --- modules/core/04-channel/keeper/ante.go | 2 +- modules/core/04-channel/keeper/ante_test.go | 6 ++-- modules/core/04-channel/keeper/packet.go | 33 ++--------------- modules/core/ante/ante.go | 40 +-------------------- modules/core/ante/ante_test.go | 14 -------- 5 files changed, 7 insertions(+), 88 deletions(-) diff --git a/modules/core/04-channel/keeper/ante.go b/modules/core/04-channel/keeper/ante.go index ac387569c68..b996f623b33 100644 --- a/modules/core/04-channel/keeper/ante.go +++ b/modules/core/04-channel/keeper/ante.go @@ -5,7 +5,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/ibc-go/v8/modules/core/04-channel/types" + "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" ) // RecvPacketReCheckTx applies replay protection ensuring that when relay messages are diff --git a/modules/core/04-channel/keeper/ante_test.go b/modules/core/04-channel/keeper/ante_test.go index 4bc62ff2ae6..e391443bed1 100644 --- a/modules/core/04-channel/keeper/ante_test.go +++ b/modules/core/04-channel/keeper/ante_test.go @@ -1,8 +1,8 @@ package keeper_test import ( - "github.com/cosmos/ibc-go/v8/modules/core/04-channel/types" - ibctesting "github.com/cosmos/ibc-go/v8/testing" + "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + ibctesting "github.com/cosmos/ibc-go/v7/testing" ) func (suite *KeeperTestSuite) TestRecvPacketReCheckTx() { @@ -43,7 +43,7 @@ func (suite *KeeperTestSuite) TestRecvPacketReCheckTx() { suite.Run(tc.name, func() { suite.SetupTest() // reset path = ibctesting.NewPath(suite.chainA, suite.chainB) - path.Setup() + suite.coordinator.Setup(path) sequence, err := path.EndpointA.SendPacket(defaultTimeoutHeight, disabledTimeoutTimestamp, ibctesting.MockPacketData) suite.Require().NoError(err) diff --git a/modules/core/04-channel/keeper/packet.go b/modules/core/04-channel/keeper/packet.go index 2200902603f..827f324d9ac 100644 --- a/modules/core/04-channel/keeper/packet.go +++ b/modules/core/04-channel/keeper/packet.go @@ -201,10 +201,7 @@ func (k Keeper) RecvPacket( packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence(), commitment, ); err != nil { -<<<<<<< HEAD return sdkerrors.Wrap(err, "couldn't verify counterparty packet commitment") -======= - return errorsmod.Wrap(err, "couldn't verify counterparty packet commitment") } if err := k.applyReplayProtection(ctx, packet, channel); err != nil { @@ -222,24 +219,14 @@ func (k Keeper) RecvPacket( ) // emit an event that the relayer can query for - emitRecvPacketEvent(ctx, packet, channel) + EmitRecvPacketEvent(ctx, packet, channel) return nil } // applyReplayProtection ensures a packet has not already been received // and performs the necessary state changes to ensure it cannot be received again. -func (k *Keeper) applyReplayProtection(ctx sdk.Context, packet types.Packet, channel types.Channel) error { - // 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. - recvStartSequence, _ := k.GetRecvStartSequence(ctx, packet.GetDestPort(), packet.GetDestChannel()) - if packet.GetSequence() < recvStartSequence { - return errorsmod.Wrap(types.ErrPacketReceived, "packet already processed in previous channel upgrade") ->>>>>>> 56ae97d8 (perf: minimize logic on rechecktx for recvpacket (#6280)) - } - +func (k *Keeper) applyReplayProtection(ctx sdk.Context, packet exported.PacketI, channel types.Channel) error { switch channel.Ordering { case types.UNORDERED: // check if the packet receipt has been received already for unordered channels @@ -293,22 +280,6 @@ func (k *Keeper) applyReplayProtection(ctx sdk.Context, packet types.Packet, cha } -<<<<<<< HEAD - // log that a packet has been received & executed - k.Logger(ctx).Info( - "packet received", - "sequence", strconv.FormatUint(packet.GetSequence(), 10), - "src_port", packet.GetSourcePort(), - "src_channel", packet.GetSourceChannel(), - "dst_port", packet.GetDestPort(), - "dst_channel", packet.GetDestChannel(), - ) - - // emit an event that the relayer can query for - EmitRecvPacketEvent(ctx, packet, channel) - -======= ->>>>>>> 56ae97d8 (perf: minimize logic on rechecktx for recvpacket (#6280)) return nil } diff --git a/modules/core/ante/ante.go b/modules/core/ante/ante.go index da465a16465..0f2855d3017 100644 --- a/modules/core/ante/ante.go +++ b/modules/core/ante/ante.go @@ -39,11 +39,7 @@ func (rrd RedundantRelayDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simula // therefore we must start the if statement on ctx.IsReCheckTx() to correctly // determine which mode we are in if ctx.IsReCheckTx() { -<<<<<<< HEAD - response, err = rrd.k.RecvPacket(sdk.WrapSDKContext(ctx), msg) -======= response, err = rrd.recvPacketReCheckTx(ctx, msg) ->>>>>>> 56ae97d8 (perf: minimize logic on rechecktx for recvpacket (#6280)) } else { response, err = rrd.recvPacketCheckTx(ctx, msg) } @@ -133,8 +129,6 @@ func (rrd RedundantRelayDecorator) recvPacketCheckTx(ctx sdk.Context, msg *chann return &channeltypes.MsgRecvPacketResponse{Result: channeltypes.SUCCESS}, nil } -<<<<<<< HEAD -======= // recvPacketReCheckTx runs a subset of ibc recv packet logic to be used specifically within the RedundantRelayDecorator AnteHandler. // It only performs core IBC receiving logic and skips any application logic. @@ -150,40 +144,8 @@ func (rrd RedundantRelayDecorator) recvPacketReCheckTx(ctx sdk.Context, msg *cha case channeltypes.ErrNoOpMsg: return &channeltypes.MsgRecvPacketResponse{Result: channeltypes.NOOP}, nil default: - return nil, errorsmod.Wrap(err, "receive packet verification failed") + return nil, sdkerrors.Wrap(err, "receive packet verification failed") } return &channeltypes.MsgRecvPacketResponse{Result: channeltypes.SUCCESS}, nil } - -// updateClientCheckTx runs a subset of ibc client update logic to be used specifically within the RedundantRelayDecorator AnteHandler. -// The following function performs ibc client message verification for CheckTx only and state updates in both CheckTx and ReCheckTx. -// Note that misbehaviour checks are omitted. -func (rrd RedundantRelayDecorator) updateClientCheckTx(ctx sdk.Context, msg *clienttypes.MsgUpdateClient) error { - clientMsg, err := clienttypes.UnpackClientMessage(msg.ClientMessage) - if err != nil { - return err - } - - if status := rrd.k.ClientKeeper.GetClientStatus(ctx, msg.ClientId); status != exported.Active { - return errorsmod.Wrapf(clienttypes.ErrClientNotActive, "cannot update client (%s) with status %s", msg.ClientId, status) - } - - clientModule, found := rrd.k.ClientKeeper.Route(msg.ClientId) - if !found { - return errorsmod.Wrap(clienttypes.ErrRouteNotFound, msg.ClientId) - } - - if !ctx.IsReCheckTx() { - if err := clientModule.VerifyClientMessage(ctx, msg.ClientId, clientMsg); err != nil { - return err - } - } - - heights := clientModule.UpdateState(ctx, msg.ClientId, clientMsg) - - ctx.Logger().With("module", "x/"+exported.ModuleName).Debug("ante ibc client update", "consensusHeights", heights) - - return nil -} ->>>>>>> 56ae97d8 (perf: minimize logic on rechecktx for recvpacket (#6280)) diff --git a/modules/core/ante/ante_test.go b/modules/core/ante/ante_test.go index 1ba0015a965..fd280b6a333 100644 --- a/modules/core/ante/ante_test.go +++ b/modules/core/ante/ante_test.go @@ -531,20 +531,6 @@ func (suite *AnteTestSuite) TestAnteDecoratorReCheckTx() { }, 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, packet channeltypes.Packet, relayer sdk.AccAddress, - ) exported.Acknowledgement { - panic(fmt.Errorf("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 {