Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: bypass min fee check for custom message types #1447

Merged
merged 8 commits into from
Apr 27, 2022
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,13 @@ Ref: https://keepachangelog.com/en/1.0.0/

## [Unreleased]

- [#1447](https://github.com/cosmos/gaia/pull/1447) Support custom message types to bypass minimum fee checks for.
If a transaction contains only bypassed message types, the transaction will not have minimum fee
checks performed during `CheckTx`. Operators can supply these message types via the `bypass-min-fee-msg-types`
configuration in `app.toml`. Note, by default they include various IBC message types.

## [v7.0.0] - 2022-03-24

- (gaia) bump [cosmos-sdk](https://github.com/cosmos/cosmos-sdk) to [v0.45.1](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.45.1). See [CHANGELOG.md](https://github.com/cosmos/cosmos-sdk/blob/v0.45.1/CHANGELOG.md#v0451---2022-02-03) for details.
- (gaia) bump [ibc-go](https://github.com/cosmos/ibc-go) module to [v3.0.0](https://github.com/cosmos/ibc-go/releases/tag/v3.0.0). See [CHANGELOG.md](https://github.com/cosmos/ibc-go/blob/v3.0.0/CHANGELOG.md#v300---2022-03-15) for details.
- (gaia) add [interchain account](https://github.com/cosmos/ibc-go/tree/main/modules/apps/27-interchain-accounts) module (interhchain-account module is part of ibc-go module).
Expand All @@ -45,6 +51,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
- (gaia) add migration logs for upgrade process.

## [v6.0.4] - 2022-03-10

* (gaia) Bump [Liquidity](https://github.com/gravity-devs/liquidity) module to [v1.4.6](https://github.com/Gravity-Devs/liquidity/releases/tag/v1.4.6).
* (gaia) Bump [IBC](https://github.com/cosmos/ibc-go) module to [2.0.3](https://github.com/cosmos/ibc-go/releases/tag/v2.0.3).
* (gaia) [#1230](https://github.com/cosmos/gaia/pull/1230) Fix: update gRPC Web Configuration in `contrib/testnets/test_platform`.
Expand Down
36 changes: 19 additions & 17 deletions app/ante_handler.go → ante/ante.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package gaia
package ante

import (
sdk "github.com/cosmos/cosmos-sdk/types"
Expand All @@ -12,41 +12,43 @@ import (
// channel keeper.
type HandlerOptions struct {
ante.HandlerOptions
IBCkeeper *ibckeeper.Keeper

IBCkeeper *ibckeeper.Keeper
BypassMinFeeMsgTypes []string
}

func NewAnteHandler(options HandlerOptions) (sdk.AnteHandler, error) {
if options.AccountKeeper == nil {
func NewAnteHandler(opts HandlerOptions) (sdk.AnteHandler, error) {
if opts.AccountKeeper == nil {
return nil, sdkerrors.Wrap(sdkerrors.ErrLogic, "account keeper is required for AnteHandler")
}
if options.BankKeeper == nil {
if opts.BankKeeper == nil {
return nil, sdkerrors.Wrap(sdkerrors.ErrLogic, "bank keeper is required for AnteHandler")
}
if options.SignModeHandler == nil {
if opts.SignModeHandler == nil {
return nil, sdkerrors.Wrap(sdkerrors.ErrLogic, "sign mode handler is required for ante builder")
}

var sigGasConsumer = options.SigGasConsumer
var sigGasConsumer = opts.SigGasConsumer
if sigGasConsumer == nil {
sigGasConsumer = ante.DefaultSigVerificationGasConsumer
}

anteDecorators := []sdk.AnteDecorator{
ante.NewSetUpContextDecorator(),
ante.NewRejectExtensionOptionsDecorator(),
ante.NewMempoolFeeDecorator(),
NewMempoolFeeDecorator(opts.BypassMinFeeMsgTypes),
ante.NewValidateBasicDecorator(),
ante.NewTxTimeoutHeightDecorator(),
ante.NewValidateMemoDecorator(options.AccountKeeper),
ante.NewConsumeGasForTxSizeDecorator(options.AccountKeeper),
ante.NewDeductFeeDecorator(options.AccountKeeper, options.BankKeeper, options.FeegrantKeeper),
ante.NewValidateMemoDecorator(opts.AccountKeeper),
ante.NewConsumeGasForTxSizeDecorator(opts.AccountKeeper),
ante.NewDeductFeeDecorator(opts.AccountKeeper, opts.BankKeeper, opts.FeegrantKeeper),
// SetPubKeyDecorator must be called before all signature verification decorators
ante.NewSetPubKeyDecorator(options.AccountKeeper),
ante.NewValidateSigCountDecorator(options.AccountKeeper),
ante.NewSigGasConsumeDecorator(options.AccountKeeper, sigGasConsumer),
ante.NewSigVerificationDecorator(options.AccountKeeper, options.SignModeHandler),
ante.NewIncrementSequenceDecorator(options.AccountKeeper),
ibcante.NewAnteDecorator(options.IBCkeeper),
ante.NewSetPubKeyDecorator(opts.AccountKeeper),
ante.NewValidateSigCountDecorator(opts.AccountKeeper),
ante.NewSigGasConsumeDecorator(opts.AccountKeeper, sigGasConsumer),
ante.NewSigVerificationDecorator(opts.AccountKeeper, opts.SignModeHandler),
ante.NewIncrementSequenceDecorator(opts.AccountKeeper),
ibcante.NewAnteDecorator(opts.IBCkeeper),
}

return sdk.ChainAnteDecorators(anteDecorators...), nil
Expand Down
99 changes: 99 additions & 0 deletions ante/ante_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package ante_test

import (
"fmt"
"testing"

"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/tx"
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
"github.com/cosmos/cosmos-sdk/simapp"
"github.com/cosmos/cosmos-sdk/testutil/testdata"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/tx/signing"
xauthsigning "github.com/cosmos/cosmos-sdk/x/auth/signing"
"github.com/stretchr/testify/suite"
tmrand "github.com/tendermint/tendermint/libs/rand"
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"

gaiaapp "github.com/cosmos/gaia/v7/app"
gaiahelpers "github.com/cosmos/gaia/v7/app/helpers"
)

type IntegrationTestSuite struct {
suite.Suite

app *gaiaapp.GaiaApp
anteHandler sdk.AnteHandler
ctx sdk.Context
clientCtx client.Context
txBuilder client.TxBuilder
}

func TestIntegrationTestSuite(t *testing.T) {
suite.Run(t, new(IntegrationTestSuite))
}

func (s *IntegrationTestSuite) SetupTest() {
app := gaiahelpers.Setup(s.T(), false, 1)
ctx := app.BaseApp.NewContext(false, tmproto.Header{
ChainID: fmt.Sprintf("test-chain-%s", tmrand.Str(4)),
Height: 1,
})

encodingConfig := simapp.MakeTestEncodingConfig()
encodingConfig.Amino.RegisterConcrete(&testdata.TestMsg{}, "testdata.TestMsg", nil)
testdata.RegisterInterfaces(encodingConfig.InterfaceRegistry)

s.app = app
s.ctx = ctx
s.clientCtx = client.Context{}.WithTxConfig(encodingConfig.TxConfig)
}

func (s *IntegrationTestSuite) CreateTestTx(privs []cryptotypes.PrivKey, accNums []uint64, accSeqs []uint64, chainID string) (xauthsigning.Tx, error) {
var sigsV2 []signing.SignatureV2
for i, priv := range privs {
sigV2 := signing.SignatureV2{
PubKey: priv.PubKey(),
Data: &signing.SingleSignatureData{
SignMode: s.clientCtx.TxConfig.SignModeHandler().DefaultMode(),
Signature: nil,
},
Sequence: accSeqs[i],
}

sigsV2 = append(sigsV2, sigV2)
}

if err := s.txBuilder.SetSignatures(sigsV2...); err != nil {
return nil, err
}

sigsV2 = []signing.SignatureV2{}
for i, priv := range privs {
signerData := xauthsigning.SignerData{
ChainID: chainID,
AccountNumber: accNums[i],
Sequence: accSeqs[i],
}
sigV2, err := tx.SignWithPrivKey(
s.clientCtx.TxConfig.SignModeHandler().DefaultMode(),
signerData,
s.txBuilder,
priv,
s.clientCtx.TxConfig,
accSeqs[i],
)
if err != nil {
return nil, err
}

sigsV2 = append(sigsV2, sigV2)
}

if err := s.txBuilder.SetSignatures(sigsV2...); err != nil {
return nil, err
}

return s.txBuilder.GetTx(), nil
}
75 changes: 75 additions & 0 deletions ante/fee.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package ante

import (
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
tmstrings "github.com/tendermint/tendermint/libs/strings"
)

const maxBypassMinFeeMsgGasUsage = uint64(200_000)

// MempoolFeeDecorator will check if the transaction's fee is at least as large
// as the local validator's minimum gasFee (defined in validator config).
//
// If fee is too low, decorator returns error and tx is rejected from mempool.
// Note this only applies when ctx.CheckTx = true. If fee is high enough or not
// CheckTx, then call next AnteHandler.
//
// CONTRACT: Tx must implement FeeTx to use MempoolFeeDecorator
type MempoolFeeDecorator struct {
BypassMinFeeMsgTypes []string
}

func NewMempoolFeeDecorator(bypassMsgTypes []string) MempoolFeeDecorator {
return MempoolFeeDecorator{
BypassMinFeeMsgTypes: bypassMsgTypes,
}
}

func (mfd MempoolFeeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) {
feeTx, ok := tx.(sdk.FeeTx)
if !ok {
return ctx, sdkerrors.Wrap(sdkerrors.ErrTxDecode, "Tx must be a FeeTx")
}

feeCoins := feeTx.GetFee()
gas := feeTx.GetGas()
msgs := feeTx.GetMsgs()

// Only check for minimum fees if the execution mode is CheckTx and the tx does
// not contain operator configured bypass messages. If the tx does contain
// operator configured bypass messages only, it's total gas must be less than
// or equal to a constant, otherwise minimum fees are checked to prevent spam.
if ctx.IsCheckTx() && !simulate && !(mfd.bypassMinFeeMsgs(msgs) && gas <= uint64(len(msgs))*maxBypassMinFeeMsgGasUsage) {
minGasPrices := ctx.MinGasPrices()
if !minGasPrices.IsZero() {
requiredFees := make(sdk.Coins, len(minGasPrices))

// Determine the required fees by multiplying each required minimum gas
// price by the gas limit, where fee = ceil(minGasPrice * gasLimit).
glDec := sdk.NewDec(int64(gas))
for i, gp := range minGasPrices {
fee := gp.Amount.Mul(glDec)
requiredFees[i] = sdk.NewCoin(gp.Denom, fee.Ceil().RoundInt())
}

if !feeCoins.IsAnyGTE(requiredFees) {
return ctx, sdkerrors.Wrapf(sdkerrors.ErrInsufficientFee, "insufficient fees; got: %s required: %s", feeCoins, requiredFees)
}
}
}

return next(ctx, tx, simulate)
}

func (mfd MempoolFeeDecorator) bypassMinFeeMsgs(msgs []sdk.Msg) bool {
for _, msg := range msgs {
if tmstrings.StringInSlice(sdk.MsgTypeURL(msg), mfd.BypassMinFeeMsgTypes) {
continue
}

return false
}

return true
}
59 changes: 59 additions & 0 deletions ante/fee_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package ante_test

import (
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
"github.com/cosmos/cosmos-sdk/testutil/testdata"
sdk "github.com/cosmos/cosmos-sdk/types"
ibcclienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types"
ibcchanneltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types"

"github.com/cosmos/gaia/v7/ante"
)

func (s *IntegrationTestSuite) TestMempoolFeeDecorator() {
s.SetupTest()
s.txBuilder = s.clientCtx.TxConfig.NewTxBuilder()

mfd := ante.NewMempoolFeeDecorator([]string{
sdk.MsgTypeURL(&ibcchanneltypes.MsgRecvPacket{}),
sdk.MsgTypeURL(&ibcchanneltypes.MsgAcknowledgement{}),
sdk.MsgTypeURL(&ibcclienttypes.MsgUpdateClient{}),
})
antehandler := sdk.ChainAnteDecorators(mfd)
priv1, _, addr1 := testdata.KeyTestPubAddr()

msg := testdata.NewTestMsg(addr1)
feeAmount := testdata.NewTestFeeAmount()
gasLimit := testdata.NewTestGasLimit()
s.Require().NoError(s.txBuilder.SetMsgs(msg))
s.txBuilder.SetFeeAmount(feeAmount)
s.txBuilder.SetGasLimit(gasLimit)

privs, accNums, accSeqs := []cryptotypes.PrivKey{priv1}, []uint64{0}, []uint64{0}
tx, err := s.CreateTestTx(privs, accNums, accSeqs, s.ctx.ChainID())
s.Require().NoError(err)

// Set high gas price so standard test fee fails
feeAmt := sdk.NewDecCoinFromDec("uatom", sdk.NewDec(200).Quo(sdk.NewDec(100000)))
minGasPrice := []sdk.DecCoin{feeAmt}
s.ctx = s.ctx.WithMinGasPrices(minGasPrice).WithIsCheckTx(true)

// antehandler errors with insufficient fees
_, err = antehandler(s.ctx, tx, false)
s.Require().Error(err, "expected error due to low fee")

// ensure no fees for certain IBC msgs
s.Require().NoError(s.txBuilder.SetMsgs(
ibcchanneltypes.NewMsgRecvPacket(ibcchanneltypes.Packet{}, nil, ibcclienttypes.Height{}, ""),
))

oracleTx, err := s.CreateTestTx(privs, accNums, accSeqs, s.ctx.ChainID())
_, err = antehandler(s.ctx, oracleTx, false)
s.Require().NoError(err, "expected min fee bypass for IBC messages")

s.ctx = s.ctx.WithIsCheckTx(false)

// antehandler should not error since we do not check min gas prices in DeliverTx
_, err = antehandler(s.ctx, tx, false)
s.Require().NoError(err, "unexpected error during DeliverTx")
}
11 changes: 7 additions & 4 deletions app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,11 +104,13 @@ import (
tmos "github.com/tendermint/tendermint/libs/os"
dbm "github.com/tendermint/tm-db"

gaiaappparams "github.com/cosmos/gaia/v7/app/params"
"github.com/strangelove-ventures/packet-forward-middleware/v2/router"
routerkeeper "github.com/strangelove-ventures/packet-forward-middleware/v2/router/keeper"
routertypes "github.com/strangelove-ventures/packet-forward-middleware/v2/router/types"

gaiaante "github.com/cosmos/gaia/v7/ante"
gaiaappparams "github.com/cosmos/gaia/v7/app/params"

// unnamed import of statik for swagger UI support
_ "github.com/cosmos/cosmos-sdk/client/docs/statik"
)
Expand Down Expand Up @@ -602,16 +604,17 @@ func NewGaiaApp(
app.MountTransientStores(tkeys)
app.MountMemoryStores(memKeys)

anteHandler, err := NewAnteHandler(
HandlerOptions{
anteHandler, err := gaiaante.NewAnteHandler(
gaiaante.HandlerOptions{
HandlerOptions: ante.HandlerOptions{
AccountKeeper: app.AccountKeeper,
BankKeeper: app.BankKeeper,
FeegrantKeeper: app.FeeGrantKeeper,
SignModeHandler: encodingConfig.TxConfig.SignModeHandler(),
SigGasConsumer: ante.DefaultSigVerificationGasConsumer,
},
IBCkeeper: app.IBCKeeper,
IBCkeeper: app.IBCKeeper,
BypassMinFeeMsgTypes: cast.ToStringSlice(appOpts.Get(gaiaappparams.BypassMinFeeMsgTypesKey)),
},
)
if err != nil {
Expand Down
Loading