Filter out the evm denom from min gas prices in the cosmos-sdk ante (#1253)

* Filter out the evm denom from min gas prices in the cosmos-sdk ante
chain; this allows '0ukava;1000000000akava' to be used in order to
still support zero fees for cosmos based transactions

* support called context in mock

* ensure min gas prices is always a valid (and sorted) DecCoins

* add doc comments
This commit is contained in:
Nick DeLuca 2022-05-20 17:12:56 -07:00 committed by GitHub
parent 833eedd3fc
commit 23bf19efb0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 135 additions and 0 deletions

View File

@ -103,6 +103,7 @@ func newCosmosAnteHandler(options HandlerOptions) sdk.AnteHandler {
decorators = append(decorators, NewAuthenticatedMempoolDecorator(options.AddressFetchers...))
}
decorators = append(decorators,
NewEvmMinGasFilter(options.EvmKeeper), // filter out evm denom from min-gas-prices
authante.NewMempoolFeeDecorator(),
NewVestingAccountDecorator(),
NewAuthzLimiterDecorator(

View File

@ -16,10 +16,12 @@ var _ sdk.AnteHandler = (&MockAnteHandler{}).AnteHandle
type MockAnteHandler struct {
WasCalled bool
CalledCtx sdk.Context
}
func (mah *MockAnteHandler) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool) (sdk.Context, error) {
mah.WasCalled = true
mah.CalledCtx = ctx
return ctx, nil
}

View File

@ -0,0 +1,44 @@
package ante
import (
sdk "github.com/cosmos/cosmos-sdk/types"
evmtypes "github.com/tharsis/ethermint/x/evm/types"
)
var _ sdk.AnteDecorator = EvmMinGasFilter{}
// EVMKeeper specifies the interface that EvmMinGasFilter requires
type EVMKeeper interface {
GetParams(ctx sdk.Context) evmtypes.Params
}
// EvmMinGasFilter filters out the EvmDenom min gas price and calls the next ante handle with an updated context
type EvmMinGasFilter struct {
evmKeeper EVMKeeper
}
// NewEvmMinGasFilter takes an EVMKeeper and returns a new min gas filter for it's EvmDenom
func NewEvmMinGasFilter(evmKeeper EVMKeeper) EvmMinGasFilter {
return EvmMinGasFilter{
evmKeeper: evmKeeper,
}
}
// AnteHandle checks the EvmDenom from the evmKeeper and filters out the EvmDenom from the ctx
func (emgf EvmMinGasFilter) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (sdk.Context, error) {
evmDenom := emgf.evmKeeper.GetParams(ctx).EvmDenom
if ctx.MinGasPrices().AmountOf(evmDenom).IsPositive() {
filteredMinGasPrices := sdk.NewDecCoins()
for _, gasPrice := range ctx.MinGasPrices() {
if gasPrice.Denom != evmDenom {
filteredMinGasPrices = filteredMinGasPrices.Add(gasPrice)
}
}
ctx = ctx.WithMinGasPrices(filteredMinGasPrices)
}
return next(ctx, tx, simulate)
}

View File

@ -0,0 +1,88 @@
package ante_test
import (
"strings"
"testing"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
tmtime "github.com/tendermint/tendermint/types/time"
evmtypes "github.com/tharsis/ethermint/x/evm/types"
"github.com/kava-labs/kava/app"
"github.com/kava-labs/kava/app/ante"
)
func mustParseDecCoins(value string) sdk.DecCoins {
coins, err := sdk.ParseDecCoins(strings.ReplaceAll(value, ";", ","))
if err != nil {
panic(err)
}
return coins
}
func TestEvmMinGasFilter(t *testing.T) {
tApp := app.NewTestApp()
handler := ante.NewEvmMinGasFilter(tApp.GetEvmKeeper())
ctx := tApp.NewContext(true, tmproto.Header{Height: 1, Time: tmtime.Now()})
tApp.GetEvmKeeper().SetParams(ctx, evmtypes.Params{
EvmDenom: "akava",
})
testCases := []struct {
name string
minGasPrices sdk.DecCoins
expectedMinGasPrices sdk.DecCoins
}{
{
"no min gas prices",
mustParseDecCoins(""),
mustParseDecCoins(""),
},
{
"zero ukava gas price",
mustParseDecCoins("0ukava"),
mustParseDecCoins("0ukava"),
},
{
"non-zero ukava gas price",
mustParseDecCoins("0.001ukava"),
mustParseDecCoins("0.001ukava"),
},
{
"zero ukava gas price, min akava price",
mustParseDecCoins("0ukava;100000akava"),
mustParseDecCoins("0ukava"), // akava is removed
},
{
"zero ukava gas price, min akava price, other token",
mustParseDecCoins("0ukava;100000akava;0.001other"),
mustParseDecCoins("0ukava;0.001other"), // akava is removed
},
{
"non-zero ukava gas price, min akava price",
mustParseDecCoins("0.25ukava;100000akava;0.001other"),
mustParseDecCoins("0.25ukava;0.001other"), // akava is removed
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
ctx := tApp.NewContext(true, tmproto.Header{Height: 1, Time: tmtime.Now()})
ctx = ctx.WithMinGasPrices(tc.minGasPrices)
mmd := MockAnteHandler{}
_, err := handler.AnteHandle(ctx, nil, false, mmd.AnteHandle)
require.NoError(t, err)
require.True(t, mmd.WasCalled)
assert.NoError(t, mmd.CalledCtx.MinGasPrices().Validate())
assert.Equal(t, tc.expectedMinGasPrices, mmd.CalledCtx.MinGasPrices())
})
}
}