From ec4d4aab8d0e121a918c82de83eb302dfa5af6d3 Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Tue, 25 Aug 2026 19:14:35 +0100 Subject: [PATCH] Simplify retired IBC transaction rejection Replace keeper-backed IBC redundancy checks with a lightweight type-URL rejection before transactions enter the mempool. Remove obsolete IBC keeper threading from ante and CheckTx paths while retaining MsgServer tombstones, begin-block behavior, and mounted stores. --- app/abci_test.go | 16 ++++ app/ante.go | 7 +- app/ante/cosmos_checktx.go | 70 +---------------- app/ante_test.go | 1 - app/app.go | 2 - app/legacyabci/check_tx.go | 4 +- sei-ibc-go/modules/core/ante/ante.go | 95 +++++------------------ sei-ibc-go/modules/core/ante/ante_test.go | 50 ++++++++++++ sei-wasmd/app/ante.go | 7 +- sei-wasmd/app/app.go | 1 - 10 files changed, 91 insertions(+), 162 deletions(-) create mode 100644 sei-ibc-go/modules/core/ante/ante_test.go diff --git a/app/abci_test.go b/app/abci_test.go index f9cff66878..7d6a8e2f31 100644 --- a/app/abci_test.go +++ b/app/abci_test.go @@ -7,12 +7,28 @@ import ( "time" "github.com/sei-protocol/sei-chain/app/migration" + sdk "github.com/sei-protocol/sei-chain/sei-cosmos/types" + channeltypes "github.com/sei-protocol/sei-chain/sei-ibc-go/modules/core/04-channel/types" ibccoretypes "github.com/sei-protocol/sei-chain/sei-ibc-go/modules/core/types" abci "github.com/sei-protocol/sei-chain/sei-tendermint/abci/types" tmproto "github.com/sei-protocol/sei-chain/sei-tendermint/proto/tendermint/types" "github.com/stretchr/testify/require" ) +func TestIBCMessageRouterReturnsDeprecatedError(t *testing.T) { + testApp := Setup(t, false, false, false) + msg := &channeltypes.MsgChannelCloseInit{ + PortId: "transfer", + ChannelId: "channel-0", + Signer: sdk.AccAddress("test-address-0000000").String(), + } + handler := testApp.MsgServiceRouter().Handler(msg) + require.NotNil(t, handler) + + _, err := handler(testApp.NewContext(false, tmproto.Header{}), msg) + require.ErrorIs(t, err, ibccoretypes.ErrIBCDeprecated) +} + // TestMigrationSubspaceRegistered verifies the generic "migration" params // subspace is wired with its key table so governance can edit // NumKeysToMigratePerBlock via a ParameterChangeProposal. diff --git a/app/ante.go b/app/ante.go index 2952a9cc1f..9e238e5c19 100644 --- a/app/ante.go +++ b/app/ante.go @@ -9,7 +9,6 @@ import ( paramskeeper "github.com/sei-protocol/sei-chain/sei-cosmos/x/params/keeper" upgradekeeper "github.com/sei-protocol/sei-chain/sei-cosmos/x/upgrade/keeper" ibcante "github.com/sei-protocol/sei-chain/sei-ibc-go/modules/core/ante" - ibckeeper "github.com/sei-protocol/sei-chain/sei-ibc-go/modules/core/keeper" wasm "github.com/sei-protocol/sei-chain/sei-wasmd/x/wasm" wasmkeeper "github.com/sei-protocol/sei-chain/sei-wasmd/x/wasm/keeper" wasmtypes "github.com/sei-protocol/sei-chain/sei-wasmd/x/wasm/types" @@ -17,12 +16,10 @@ import ( evmkeeper "github.com/sei-protocol/sei-chain/x/evm/keeper" ) -// HandlerOptions extend the SDK's AnteHandler options by requiring the IBC -// channel keeper. +// HandlerOptions extends the SDK's AnteHandler options with application keepers and configuration. type HandlerOptions struct { ante.HandlerOptions - IBCKeeper *ibckeeper.Keeper WasmConfig *wasmtypes.WasmConfig WasmKeeper *wasm.Keeper EVMKeeper *evmkeeper.Keeper @@ -88,7 +85,7 @@ func NewAnteHandler(options HandlerOptions) (sdk.AnteHandler, sdk.AnteHandler, e ante.NewIncrementSequenceDecorator(options.AccountKeeper), evmante.NewEVMAddressDecorator(options.EVMKeeper, options.EVMKeeper.AccountKeeper()), antedecorators.NewAuthzNestedMessageDecorator(), - ibcante.NewAnteDecorator(options.IBCKeeper), + ibcante.NewAnteDecorator(), } anteHandler := sdk.ChainAnteDecorators(anteDecorators...) diff --git a/app/ante/cosmos_checktx.go b/app/ante/cosmos_checktx.go index df0198b364..cc834e2fb5 100644 --- a/app/ante/cosmos_checktx.go +++ b/app/ante/cosmos_checktx.go @@ -25,9 +25,7 @@ import ( "github.com/sei-protocol/sei-chain/sei-cosmos/x/authz" bankkeeper "github.com/sei-protocol/sei-chain/sei-cosmos/x/bank/keeper" paramskeeper "github.com/sei-protocol/sei-chain/sei-cosmos/x/params/keeper" - clienttypes "github.com/sei-protocol/sei-chain/sei-ibc-go/modules/core/02-client/types" - channeltypes "github.com/sei-protocol/sei-chain/sei-ibc-go/modules/core/04-channel/types" - ibckeeper "github.com/sei-protocol/sei-chain/sei-ibc-go/modules/core/keeper" + ibcante "github.com/sei-protocol/sei-chain/sei-ibc-go/modules/core/ante" tmproto "github.com/sei-protocol/sei-chain/sei-tendermint/proto/tendermint/types" "github.com/sei-protocol/sei-chain/utils/helpers" evmkeeper "github.com/sei-protocol/sei-chain/x/evm/keeper" @@ -70,7 +68,6 @@ func CosmosCheckTxAnte( ek *evmkeeper.Keeper, accountKeeper authkeeper.AccountKeeper, bankKeeper bankkeeper.Keeper, - ibcKeeper *ibckeeper.Keeper, ) (returnCtx sdk.Context, returnErr error) { // Auth params are needed for stateless checks before SetGasMeter installs the // tx meter. Read them on a throwaway meter so this early lookup does not @@ -113,7 +110,7 @@ func CosmosCheckTxAnte( } ctx = DecoratePriority(ctx, priority) - return ctx, CheckMessage(ctx, tx, ibcKeeper) + return ctx, ibcante.RejectMessages(tx) } func HandleOutofGas(recoveredErr any, gasLimit uint64, gasConsumed uint64) error { @@ -569,69 +566,6 @@ func UpdateSigners(ctx sdk.Context, tx sdk.Tx, accountKeeper authkeeper.AccountK return events, nil } -func CheckMessage(ctx sdk.Context, tx sdk.Tx, ibcKeeper *ibckeeper.Keeper) error { - // keep track of total packet messages and number of redundancies across `RecvPacket`, `AcknowledgePacket`, and `TimeoutPacket/OnClose` - redundancies := 0 - packetMsgs := 0 - for _, m := range tx.GetMsgs() { - switch msg := m.(type) { - case *channeltypes.MsgRecvPacket: - response, err := ibcKeeper.RecvPacket(sdk.WrapSDKContext(ctx), msg) - if err != nil { - return err - } - if response.Result == channeltypes.NOOP { - redundancies += 1 - } - packetMsgs += 1 - - case *channeltypes.MsgAcknowledgement: - response, err := ibcKeeper.Acknowledgement(sdk.WrapSDKContext(ctx), msg) - if err != nil { - return err - } - if response.Result == channeltypes.NOOP { - redundancies += 1 - } - packetMsgs += 1 - - case *channeltypes.MsgTimeout: - response, err := ibcKeeper.Timeout(sdk.WrapSDKContext(ctx), msg) - if err != nil { - return err - } - if response.Result == channeltypes.NOOP { - redundancies += 1 - } - packetMsgs += 1 - - case *channeltypes.MsgTimeoutOnClose: - response, err := ibcKeeper.TimeoutOnClose(sdk.WrapSDKContext(ctx), msg) - if err != nil { - return err - } - if response.Result == channeltypes.NOOP { - redundancies += 1 - } - packetMsgs += 1 - - case *clienttypes.MsgUpdateClient: - _, err := ibcKeeper.UpdateClient(sdk.WrapSDKContext(ctx), msg) - if err != nil { - return err - } - - } - } - - // only return error if all packet messages are redundant - if redundancies == packetMsgs && packetMsgs > 0 { - return channeltypes.ErrRedundantTx - } - - return nil -} - func CheckAuthzContainsEvm(authzMsg *authz.MsgExec, nestedLvl int) (bool, error) { if nestedLvl >= maxNestedMsgs { return false, errors.New("permission denied, more nested msgs than permitted") diff --git a/app/ante_test.go b/app/ante_test.go index c1877f779c..68a7265fa4 100644 --- a/app/ante_test.go +++ b/app/ante_test.go @@ -81,7 +81,6 @@ func (suite *AnteTestSuite) SetupTest(isCheckTx bool) { SigGasConsumer: ante.DefaultSigVerificationGasConsumer, // BatchVerifier: app.batchVerifier, }, - IBCKeeper: suite.App.IBCKeeper, WasmConfig: &wasmConfig, WasmKeeper: &suite.App.WasmKeeper, TracingInfo: tracingInfo, diff --git a/app/app.go b/app/app.go index 49ae3fd09d..e12a4bc0a4 100644 --- a/app/app.go +++ b/app/app.go @@ -883,7 +883,6 @@ func New( app.CheckTxKeepers = legacyabci.CheckTxKeepers{ AccountKeeper: app.AccountKeeper, BankKeeper: app.BankKeeper, - IBCKeeper: app.IBCKeeper, EvmKeeper: &app.EvmKeeper, ParamsKeeper: app.ParamsKeeper, UpgradeKeeper: &app.UpgradeKeeper, @@ -975,7 +974,6 @@ func New( SigGasConsumer: ante.DefaultSigVerificationGasConsumer, // BatchVerifier: app.batchVerifier, }, - IBCKeeper: app.IBCKeeper, TXCounterStoreKey: keys[wasm.StoreKey], WasmConfig: &wasmConfig, WasmKeeper: &app.WasmKeeper, diff --git a/app/legacyabci/check_tx.go b/app/legacyabci/check_tx.go index bfba59eee7..0c4a8da2e5 100644 --- a/app/legacyabci/check_tx.go +++ b/app/legacyabci/check_tx.go @@ -21,7 +21,6 @@ import ( bankkeeper "github.com/sei-protocol/sei-chain/sei-cosmos/x/bank/keeper" paramskeeper "github.com/sei-protocol/sei-chain/sei-cosmos/x/params/keeper" upgradekeeper "github.com/sei-protocol/sei-chain/sei-cosmos/x/upgrade/keeper" - ibckeeper "github.com/sei-protocol/sei-chain/sei-ibc-go/modules/core/keeper" otelmetric "go.opentelemetry.io/otel/metric" ) @@ -30,7 +29,6 @@ var defaultRecoveryMiddleware = newDefaultRecoveryMiddleware() type CheckTxKeepers struct { AccountKeeper authkeeper.AccountKeeper BankKeeper bankkeeper.Keeper - IBCKeeper *ibckeeper.Keeper EvmKeeper *evmkeeper.Keeper ParamsKeeper paramskeeper.Keeper UpgradeKeeper *upgradekeeper.Keeper @@ -102,7 +100,7 @@ func CheckTx( } else if isEVM { newCtx, err = ante.EvmCheckTxAnte(anteCtx, tx, keepers.UpgradeKeeper, keepers.EvmKeeper) } else { - newCtx, err = ante.CosmosCheckTxAnte(anteCtx, txConfig, tx, keepers.ParamsKeeper, keepers.EvmKeeper, keepers.AccountKeeper, keepers.BankKeeper, keepers.IBCKeeper) + newCtx, err = ante.CosmosCheckTxAnte(anteCtx, txConfig, tx, keepers.ParamsKeeper, keepers.EvmKeeper, keepers.AccountKeeper, keepers.BankKeeper) } if !newCtx.IsZero() { ctx = newCtx diff --git a/sei-ibc-go/modules/core/ante/ante.go b/sei-ibc-go/modules/core/ante/ante.go index 823c3ac98b..8270fb5e62 100644 --- a/sei-ibc-go/modules/core/ante/ante.go +++ b/sei-ibc-go/modules/core/ante/ante.go @@ -1,92 +1,33 @@ package ante import ( + "strings" + sdk "github.com/sei-protocol/sei-chain/sei-cosmos/types" - clienttypes "github.com/sei-protocol/sei-chain/sei-ibc-go/modules/core/02-client/types" - channeltypes "github.com/sei-protocol/sei-chain/sei-ibc-go/modules/core/04-channel/types" - "github.com/sei-protocol/sei-chain/sei-ibc-go/modules/core/keeper" + coretypes "github.com/sei-protocol/sei-chain/sei-ibc-go/modules/core/types" ) -type AnteDecorator struct { - k *keeper.Keeper -} +// AnteDecorator rejects retired IBC messages before they enter the mempool. +type AnteDecorator struct{} -func NewAnteDecorator(k *keeper.Keeper) AnteDecorator { - return AnteDecorator{k: k} +func NewAnteDecorator() AnteDecorator { + return AnteDecorator{} } -// AnteDecorator returns an error if a multiMsg tx only contains packet messages (Recv, Ack, Timeout) and additional update messages -// and all packet messages are redundant. If the transaction is just a single UpdateClient message, or the multimsg transaction -// contains some other message type, then the antedecorator returns no error and continues processing to ensure these transactions -// are included. This will ensure that relayers do not waste fees on multiMsg transactions when another relayer has already submitted -// all packets, by rejecting the tx at the mempool layer. -func (ad AnteDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (sdk.Context, error) { - // do not run redundancy check on DeliverTx or simulate - if (ctx.IsCheckTx() || ctx.IsReCheckTx()) && !simulate { - // keep track of total packet messages and number of redundancies across `RecvPacket`, `AcknowledgePacket`, and `TimeoutPacket/OnClose` - redundancies := 0 - packetMsgs := 0 - for _, m := range tx.GetMsgs() { - switch msg := m.(type) { - case *channeltypes.MsgRecvPacket: - response, err := ad.k.RecvPacket(sdk.WrapSDKContext(ctx), msg) - if err != nil { - return ctx, err - } - if response.Result == channeltypes.NOOP { - redundancies += 1 - } - packetMsgs += 1 - - case *channeltypes.MsgAcknowledgement: - response, err := ad.k.Acknowledgement(sdk.WrapSDKContext(ctx), msg) - if err != nil { - return ctx, err - } - if response.Result == channeltypes.NOOP { - redundancies += 1 - } - packetMsgs += 1 - - case *channeltypes.MsgTimeout: - response, err := ad.k.Timeout(sdk.WrapSDKContext(ctx), msg) - if err != nil { - return ctx, err - } - if response.Result == channeltypes.NOOP { - redundancies += 1 - } - packetMsgs += 1 - - case *channeltypes.MsgTimeoutOnClose: - response, err := ad.k.TimeoutOnClose(sdk.WrapSDKContext(ctx), msg) - if err != nil { - return ctx, err - } - if response.Result == channeltypes.NOOP { - redundancies += 1 - } - packetMsgs += 1 - - case *clienttypes.MsgUpdateClient: - _, err := ad.k.UpdateClient(sdk.WrapSDKContext(ctx), msg) - if err != nil { - return ctx, err - } - - 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 - // even if they get batched with redundant packet messages. - return next(ctx, tx, simulate) - } +// RejectMessages returns the retirement error when a transaction contains an IBC message. +func RejectMessages(tx sdk.Tx) error { + for _, msg := range tx.GetMsgs() { + if strings.HasPrefix(sdk.MsgTypeURL(msg), "/ibc.") { + return coretypes.ErrIBCDeprecated } + } + return nil +} - // only return error if all packet messages are redundant - if redundancies == packetMsgs && packetMsgs > 0 { - return ctx, channeltypes.ErrRedundantTx - } +func (AnteDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (sdk.Context, error) { + if err := RejectMessages(tx); err != nil { + return ctx, err } return next(ctx, tx, simulate) } diff --git a/sei-ibc-go/modules/core/ante/ante_test.go b/sei-ibc-go/modules/core/ante/ante_test.go new file mode 100644 index 0000000000..ab3cd1dbd6 --- /dev/null +++ b/sei-ibc-go/modules/core/ante/ante_test.go @@ -0,0 +1,50 @@ +package ante + +import ( + "testing" + + "github.com/stretchr/testify/require" + + sdk "github.com/sei-protocol/sei-chain/sei-cosmos/types" + banktypes "github.com/sei-protocol/sei-chain/sei-cosmos/x/bank/types" + channeltypes "github.com/sei-protocol/sei-chain/sei-ibc-go/modules/core/04-channel/types" + coretypes "github.com/sei-protocol/sei-chain/sei-ibc-go/modules/core/types" +) + +type testTx struct { + msgs []sdk.Msg +} + +func (tx testTx) GetMsgs() []sdk.Msg { return tx.msgs } +func (testTx) ValidateBasic() error { return nil } +func (testTx) GetGasEstimate() uint64 { return 0 } + +func TestAnteDecorator(t *testing.T) { + decorator := NewAnteDecorator() + + t.Run("rejects IBC messages", func(t *testing.T) { + nextCalled := false + _, err := decorator.AnteHandle(sdk.Context{}, testTx{msgs: []sdk.Msg{ + &channeltypes.MsgChannelCloseInit{}, + }}, false, func(ctx sdk.Context, _ sdk.Tx, _ bool) (sdk.Context, error) { + nextCalled = true + return ctx, nil + }) + + require.ErrorIs(t, err, coretypes.ErrIBCDeprecated) + require.False(t, nextCalled) + }) + + t.Run("passes non-IBC messages", func(t *testing.T) { + nextCalled := false + _, err := decorator.AnteHandle(sdk.Context{}, testTx{msgs: []sdk.Msg{ + &banktypes.MsgSend{}, + }}, false, func(ctx sdk.Context, _ sdk.Tx, _ bool) (sdk.Context, error) { + nextCalled = true + return ctx, nil + }) + + require.NoError(t, err) + require.True(t, nextCalled) + }) +} diff --git a/sei-wasmd/app/ante.go b/sei-wasmd/app/ante.go index f150817fb3..4d711aae1e 100644 --- a/sei-wasmd/app/ante.go +++ b/sei-wasmd/app/ante.go @@ -5,19 +5,16 @@ import ( sdkerrors "github.com/sei-protocol/sei-chain/sei-cosmos/types/errors" "github.com/sei-protocol/sei-chain/sei-cosmos/x/auth/ante" ibcante "github.com/sei-protocol/sei-chain/sei-ibc-go/modules/core/ante" - "github.com/sei-protocol/sei-chain/sei-ibc-go/modules/core/keeper" paramskeeper "github.com/sei-protocol/sei-chain/sei-cosmos/x/params/keeper" wasmkeeper "github.com/sei-protocol/sei-chain/sei-wasmd/x/wasm/keeper" wasmTypes "github.com/sei-protocol/sei-chain/sei-wasmd/x/wasm/types" ) -// HandlerOptions extend the SDK's AnteHandler options by requiring the IBC -// channel keeper. +// HandlerOptions extends the SDK's AnteHandler options with application configuration. type HandlerOptions struct { ante.HandlerOptions - IBCKeeper *keeper.Keeper WasmConfig *wasmTypes.WasmConfig TXCounterStoreKey sdk.StoreKey } @@ -57,7 +54,7 @@ func NewAnteHandler(options HandlerOptions) (sdk.AnteHandler, error) { ante.NewSigGasConsumeDecorator(options.AccountKeeper, options.SigGasConsumer), ante.NewSigVerificationDecorator(options.AccountKeeper, options.SignModeHandler), ante.NewIncrementSequenceDecorator(options.AccountKeeper), - ibcante.NewAnteDecorator(options.IBCKeeper), + ibcante.NewAnteDecorator(), } anteHandler := sdk.ChainAnteDecorators(anteDecorators...) diff --git a/sei-wasmd/app/app.go b/sei-wasmd/app/app.go index 47001e9d1f..3ae120534e 100644 --- a/sei-wasmd/app/app.go +++ b/sei-wasmd/app/app.go @@ -487,7 +487,6 @@ func NewWasmApp( SignModeHandler: encodingConfig.TxConfig.SignModeHandler(), SigGasConsumer: ante.DefaultSigVerificationGasConsumer, }, - IBCKeeper: app.ibcKeeper, WasmConfig: &wasmConfig, TXCounterStoreKey: keys[wasm.StoreKey], },