From 389a16a4beac3dde674b4f9d43c1a633d7333fa5 Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Thu, 27 Aug 2026 18:25:04 +0100 Subject: [PATCH] Reject native multisig transaction simulation Reject multisig keys and signature data during gas simulation while leaving normal transaction execution and single-signature simulation unchanged. Part of SIP-3 --- CHANGELOG.md | 1 + sei-cosmos/x/auth/ante/sigverify.go | 16 +++++++++ sei-cosmos/x/auth/ante/sigverify_test.go | 44 ++++++++++++++++++++++++ 3 files changed, 61 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a9db8140f7..a6d1be2f49 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,6 +36,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ * [#3990](https://github.com/sei-protocol/sei-chain/pull/3990) Freeze mode is limited to full nodes and disables transaction and evidence submission, mempool gossip, and state sync from startup while preserving query RPC and mempool-backed reads. Frozen and Autobahn nodes no longer advertise the unused mempool P2P channel. ### Upgrade guide +* **Native multisig simulation.** Cosmos transaction simulation now rejects native multisig transactions. Single-signature Cosmos transactions and EVM JSON-RPC simulation are unaffected. * **IBC core removal.** Removes the retired IBC core source, protobufs, light clients, CLI, and simulation support. Retired IBC stores remain mounted but are omitted from `export-genesis`; preserve the state database or use v6.6 freeze nodes for historical IBC data. * **IBC transfer removal.** Removes ICS-20 execution, module APIs, CLI commands, CosmWasm transfer messages, transfer codecs, and transfer keeper integration, including the IBC EVM precompile's keeper injection. The transfer store and module account remain materialized for state compatibility. Transfer queries, historical transfer transaction decoding, and pre-v6.7 IBC precompile tracing must be served by v6.6 freeze nodes; v6.7 nodes do not provide them. * **IBC query removal.** Removes the IBC core gRPC, REST, Protobuf, raw ABCI store, and native CosmWasm query APIs, along with CLI query commands. Historical IBC queries must be served by v6.6 freeze nodes. diff --git a/sei-cosmos/x/auth/ante/sigverify.go b/sei-cosmos/x/auth/ante/sigverify.go index 73b07ae871..77b2ed4bd5 100644 --- a/sei-cosmos/x/auth/ante/sigverify.go +++ b/sei-cosmos/x/auth/ante/sigverify.go @@ -186,6 +186,10 @@ func (sgcd SigGasConsumeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simula Sequence: sig.Sequence, } + if err := rejectMultisigSimulation(simulate, sig); err != nil { + return ctx, err + } + err = sgcd.sigGasConsumer(ctx.GasMeter(), sig, params) if err != nil { return ctx, err @@ -195,6 +199,18 @@ func (sgcd SigGasConsumeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simula return next(ctx, tx, simulate) } +func rejectMultisigSimulation(simulate bool, sig signing.SignatureV2) error { + if !simulate { + return nil + } + _, hasMultisigKey := sig.PubKey.(multisig.PubKey) + _, hasMultisigData := sig.Data.(*signing.MultiSignatureData) + if hasMultisigKey || hasMultisigData { + return sdkerrors.Wrap(sdkerrors.ErrNotSupported, "multisig transaction simulation is not supported") + } + return nil +} + // Verify all signatures for a tx and return an error if any are invalid. Note, // the SigVerificationDecorator will not check signatures on ReCheck. // diff --git a/sei-cosmos/x/auth/ante/sigverify_test.go b/sei-cosmos/x/auth/ante/sigverify_test.go index 1aa7e5c289..82c4d33f4f 100644 --- a/sei-cosmos/x/auth/ante/sigverify_test.go +++ b/sei-cosmos/x/auth/ante/sigverify_test.go @@ -14,6 +14,7 @@ import ( "github.com/sei-protocol/sei-chain/sei-cosmos/crypto/types/multisig" "github.com/sei-protocol/sei-chain/sei-cosmos/testutil/testdata" sdk "github.com/sei-protocol/sei-chain/sei-cosmos/types" + sdkerrors "github.com/sei-protocol/sei-chain/sei-cosmos/types/errors" "github.com/sei-protocol/sei-chain/sei-cosmos/types/tx/signing" "github.com/sei-protocol/sei-chain/sei-cosmos/x/auth/ante" "github.com/sei-protocol/sei-chain/sei-cosmos/x/auth/legacy/legacytx" @@ -118,6 +119,49 @@ func (suite *AnteTestSuite) TestConsumeSignatureVerificationGas() { } } +func (suite *AnteTestSuite) TestSigGasConsumeDecoratorRejectsMultisigSimulation() { + suite.SetupTest(true) + + pubKeys, _ := generatePubKeysAndSignatures(2, []byte("sign bytes"), false) + multisigKey := kmultisig.NewLegacyAminoPubKey(2, pubKeys) + signer := sdk.AccAddress(multisigKey.Address()) + account := suite.app.AccountKeeper.NewAccountWithAddress(suite.ctx, signer) + suite.Require().NoError(account.SetPubKey(multisigKey)) + suite.app.AccountKeeper.SetAccount(suite.ctx, account) + + suite.txBuilder = suite.clientCtx.TxConfig.NewTxBuilder() + suite.Require().NoError(suite.txBuilder.SetMsgs(testdata.NewTestMsg(signer))) + suite.Require().NoError(suite.txBuilder.SetSignatures(signing.SignatureV2{ + PubKey: multisigKey, + Data: multisig.NewMultisig(len(pubKeys)), + })) + + consumerCalled := false + decorator := ante.NewSigGasConsumeDecorator( + suite.app.AccountKeeper, + func(sdk.GasMeter, signing.SignatureV2, types.Params) error { + consumerCalled = true + return nil + }, + ) + nextCalled := false + next := func(ctx sdk.Context, _ sdk.Tx, _ bool) (sdk.Context, error) { + nextCalled = true + return ctx, nil + } + + _, err := decorator.AnteHandle(suite.ctx, suite.txBuilder.GetTx(), true, next) + suite.Require().ErrorIs(err, sdkerrors.ErrNotSupported) + suite.Require().ErrorContains(err, "multisig transaction simulation is not supported") + suite.Require().False(consumerCalled) + suite.Require().False(nextCalled) + + _, err = decorator.AnteHandle(suite.ctx, suite.txBuilder.GetTx(), false, next) + suite.Require().NoError(err) + suite.Require().True(consumerCalled) + suite.Require().True(nextCalled) +} + func (suite *AnteTestSuite) TestSigVerification() { suite.SetupTest(true) // setup suite.txBuilder = suite.clientCtx.TxConfig.NewTxBuilder()