diff --git a/custom/auth/ante/ante.go b/custom/auth/ante/ante.go index 75400c61..edd69f0e 100644 --- a/custom/auth/ante/ante.go +++ b/custom/auth/ante/ante.go @@ -85,6 +85,7 @@ func NewAnteHandler(options HandlerOptions) (sdk.AnteHandler, error) { ante.NewValidateMemoDecorator(options.AccountKeeper), // SpammingPreventionDecorator prevents spamming oracle vote tx attempts at same height NewSpammingPreventionDecorator(options.OracleKeeper), + NewIBCTransferSpamPreventionDecorator(), // prevents spamming IBC transfer tx with long memo and receiver // MinInitialDepositDecorator prevents submitting governance proposal low initial deposit NewMinInitialDepositDecorator(options.GovKeeper, options.TreasuryKeeper), ante.NewConsumeGasForTxSizeDecorator(options.AccountKeeper), diff --git a/custom/auth/ante/ibc_spamming_prevention.go b/custom/auth/ante/ibc_spamming_prevention.go new file mode 100644 index 00000000..2a9f943a --- /dev/null +++ b/custom/auth/ante/ibc_spamming_prevention.go @@ -0,0 +1,45 @@ +package ante + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + ibctransfertypes "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" +) + +const ( + DefaultMaxMemoLength = 1024 // need 1024 to work with skip protocol + DefaultMaxReceiverLength = 128 +) + +const ModuleName = "ibcspamprevention" + +var ( + ErrReceiverTooLong = sdkerrors.Register(ModuleName, 11, "receiver too long") + ErrMemoTooLong = sdkerrors.Register(ModuleName, 12, "memo too long") +) + +type IBCTransferSpamPreventionDecorator struct{} + +func NewIBCTransferSpamPreventionDecorator() IBCTransferSpamPreventionDecorator { + return IBCTransferSpamPreventionDecorator{} +} + +// AnteHandle checks IBC transfer messages for potential spam characteristics +func (ispd IBCTransferSpamPreventionDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) { + if ctx.IsReCheckTx() { + return next(ctx, tx, simulate) + } + + for _, msg := range tx.GetMsgs() { + if ibcTransferMsg, ok := msg.(*ibctransfertypes.MsgTransfer); ok { + if len(ibcTransferMsg.Memo) > DefaultMaxMemoLength { + return ctx, ErrMemoTooLong + } + if len(ibcTransferMsg.Receiver) > DefaultMaxReceiverLength { + return ctx, ErrReceiverTooLong + } + } + } + + return next(ctx, tx, simulate) +} diff --git a/custom/auth/ante/ibc_spamming_prevention_test.go b/custom/auth/ante/ibc_spamming_prevention_test.go new file mode 100644 index 00000000..57dc7bbd --- /dev/null +++ b/custom/auth/ante/ibc_spamming_prevention_test.go @@ -0,0 +1,128 @@ +package ante_test + +import ( + "github.com/classic-terra/core/v3/custom/auth/ante" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + "github.com/cosmos/cosmos-sdk/testutil/testdata" + sdk "github.com/cosmos/cosmos-sdk/types" + ibctransfertypes "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" +) + +func (suite *AnteTestSuite) TestIBCTransferSpamPrevention() { + testCases := []struct { + name string + malleate func() (sdk.Tx, error) + expectErr error + }{ + { + "memo too long", + func() (sdk.Tx, error) { + suite.SetupTest(true) + suite.txBuilder = suite.clientCtx.TxConfig.NewTxBuilder() + + priv1, _, addr1 := testdata.KeyTestPubAddr() + _, _, addr2 := testdata.KeyTestPubAddr() + + msg := ibctransfertypes.NewMsgTransfer( + "transfer", + "channel-0", + sdk.NewCoin("uluna", sdk.NewInt(100000)), + addr1.String(), + addr2.String(), + clienttypes.NewHeight(1, 1000), + 0, + string(make([]byte, ante.DefaultMaxMemoLength+1)), // greater than max memo length + ) + + feeAmount := testdata.NewTestFeeAmount() + gasLimit := testdata.NewTestGasLimit() + suite.Require().NoError(suite.txBuilder.SetMsgs(msg)) + suite.txBuilder.SetFeeAmount(feeAmount) + suite.txBuilder.SetGasLimit(gasLimit) + + privs, accNums, accSeqs := []cryptotypes.PrivKey{priv1}, []uint64{0}, []uint64{0} + return suite.CreateTestTx(privs, accNums, accSeqs, suite.ctx.ChainID()) + }, + ante.ErrMemoTooLong, + }, + { + "receiver too long", + func() (sdk.Tx, error) { + suite.SetupTest(true) + suite.txBuilder = suite.clientCtx.TxConfig.NewTxBuilder() + + priv1, _, addr1 := testdata.KeyTestPubAddr() + + msg := ibctransfertypes.NewMsgTransfer( + "transfer", + "channel-0", + sdk.NewCoin("uluna", sdk.NewInt(100000)), + addr1.String(), + string(make([]byte, ante.DefaultMaxReceiverLength+1)), // greater than max receiver length + clienttypes.NewHeight(1, 1000), + 0, + "normal memo", + ) + + feeAmount := testdata.NewTestFeeAmount() + gasLimit := testdata.NewTestGasLimit() + suite.Require().NoError(suite.txBuilder.SetMsgs(msg)) + suite.txBuilder.SetFeeAmount(feeAmount) + suite.txBuilder.SetGasLimit(gasLimit) + + privs, accNums, accSeqs := []cryptotypes.PrivKey{priv1}, []uint64{0}, []uint64{0} + return suite.CreateTestTx(privs, accNums, accSeqs, suite.ctx.ChainID()) + }, + ante.ErrReceiverTooLong, + }, + { + "valid transfer", + func() (sdk.Tx, error) { + suite.SetupTest(true) + suite.txBuilder = suite.clientCtx.TxConfig.NewTxBuilder() + + priv1, _, addr1 := testdata.KeyTestPubAddr() + _, _, addr2 := testdata.KeyTestPubAddr() + + msg := ibctransfertypes.NewMsgTransfer( + "transfer", + "channel-0", + sdk.NewCoin("uluna", sdk.NewInt(100000)), + addr1.String(), + addr2.String(), + clienttypes.NewHeight(1, 1000), + 0, + "normal memo", + ) + + feeAmount := testdata.NewTestFeeAmount() + gasLimit := testdata.NewTestGasLimit() + suite.Require().NoError(suite.txBuilder.SetMsgs(msg)) + suite.txBuilder.SetFeeAmount(feeAmount) + suite.txBuilder.SetGasLimit(gasLimit) + + privs, accNums, accSeqs := []cryptotypes.PrivKey{priv1}, []uint64{0}, []uint64{0} + return suite.CreateTestTx(privs, accNums, accSeqs, suite.ctx.ChainID()) + }, + nil, + }, + } + + for _, tc := range testCases { + suite.Run(tc.name, func() { + decorator := ante.NewIBCTransferSpamPreventionDecorator() + antehandler := sdk.ChainAnteDecorators(decorator) + + tx, err := tc.malleate() + suite.Require().NoError(err) + + _, err = antehandler(suite.ctx.WithIsCheckTx(true), tx, false) + if tc.expectErr != nil { + suite.Require().Equal(tc.expectErr, err) + } else { + suite.Require().NoError(err) + } + }) + } +} diff --git a/go.mod b/go.mod index b9f84dcb..a8ca3f2d 100644 --- a/go.mod +++ b/go.mod @@ -15,7 +15,7 @@ require ( github.com/cosmos/cosmos-sdk v0.47.14 github.com/cosmos/gogoproto v1.7.0 github.com/cosmos/ibc-apps/modules/ibc-hooks/v7 v7.0.0-20240321032823-2733d24a1b99 - github.com/cosmos/ibc-go/v7 v7.4.0 + github.com/cosmos/ibc-go/v7 v7.4.1 github.com/gogo/protobuf v1.3.3 github.com/golang/protobuf v1.5.4 github.com/google/gofuzz v1.2.0 @@ -231,7 +231,6 @@ replace ( github.com/CosmWasm/wasmd => github.com/classic-terra/wasmd v0.46.0-classic.2 // use cometbft github.com/cometbft/cometbft-db => github.com/cometbft/cometbft-db v0.8.0 - github.com/cosmos/ibc-go/v7 => github.com/classic-terra/ibc-go/v7 v7.4.0-terra github.com/cosmos/ledger-cosmos-go => github.com/terra-money/ledger-terra-go v0.11.2 // replace goleveldb to optimized one github.com/syndtr/goleveldb => github.com/classic-terra/goleveldb v0.0.0-20230914223247-2b28f6655121 diff --git a/go.sum b/go.sum index 1046ea33..4f828211 100644 --- a/go.sum +++ b/go.sum @@ -348,8 +348,6 @@ github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6D github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= github.com/classic-terra/goleveldb v0.0.0-20230914223247-2b28f6655121 h1:fjpWDB0hm225wYg9vunyDyTH8ftd5xEUgINJKidj+Tw= github.com/classic-terra/goleveldb v0.0.0-20230914223247-2b28f6655121/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= -github.com/classic-terra/ibc-go/v7 v7.4.0-terra h1:hawaq62XKlxyc8xLyIcc6IujDDEbqDBU+2U15SF+hj8= -github.com/classic-terra/ibc-go/v7 v7.4.0-terra/go.mod h1:s0lxNkjVIqsb8AVltL0qhzxeLgOKvWZrknPuvgjlEQ8= github.com/classic-terra/wasmd v0.46.0-classic.2 h1:CoyigbNmee6LaQk/eHg2HzS++v1U5wPxKPQz10y1FfE= github.com/classic-terra/wasmd v0.46.0-classic.2/go.mod h1:A892kIOeNGDqzh1YeTcTzzIxr/mCOjiXpJRU/f9oBxU= github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= @@ -412,6 +410,8 @@ github.com/cosmos/iavl v0.20.1 h1:rM1kqeG3/HBT85vsZdoSNsehciqUQPWrR4BYmqE2+zg= github.com/cosmos/iavl v0.20.1/go.mod h1:WO7FyvaZJoH65+HFOsDir7xU9FWk2w9cHXNW1XHcl7A= github.com/cosmos/ibc-apps/modules/ibc-hooks/v7 v7.0.0-20240321032823-2733d24a1b99 h1:AC05pevT3jIVTxJ0mABlN3hxyHESPpELhVQjwQVT6Pw= github.com/cosmos/ibc-apps/modules/ibc-hooks/v7 v7.0.0-20240321032823-2733d24a1b99/go.mod h1:JwHFbo1oX/ht4fPpnPvmhZr+dCkYK1Vihw+vZE9umR4= +github.com/cosmos/ibc-go/v7 v7.4.1 h1:95hR5Mdgk2/Z6Ynsq537BOU8LJAOsHR5g0N0ffhFLYg= +github.com/cosmos/ibc-go/v7 v7.4.1/go.mod h1:L/KaEhzV5TGUCTfGysVgMBQtl5Dm7hHitfpk+GIeoAo= github.com/cosmos/ics23/go v0.10.0 h1:iXqLLgp2Lp+EdpIuwXTYIQU+AiHj9mOC2X9ab++bZDM= github.com/cosmos/ics23/go v0.10.0/go.mod h1:ZfJSmng/TBNTBkFemHHHj5YY7VAU/MBU980F4VU1NG0= github.com/cosmos/keyring v1.2.0 h1:8C1lBP9xhImmIabyXW4c3vFjjLiBdGCmfLUfeZlV1Yo=