fix: use EffectiveGasPrice in ante handler for dynamic fee tx (#817)

* Use effectiveGasPrice in ante handler for dynamic fee tx

Closes: #814

Solution:
- use effectiveGasPrice when check minimal-gas-prices, and deduct fee in ante handler
- implement an EthMempoolFeeDecorator

* add effectiveGasPrice to tx receipt

* changelog

* fix unit test

* fix comments

* add comments

* Apply suggestions from code review

Co-authored-by: Thomas Nguy <81727899+thomas-nguy@users.noreply.github.com>

* review suggestions

Co-authored-by: Thomas Nguy <81727899+thomas-nguy@users.noreply.github.com>
Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com>
This commit is contained in:
yihuang
2021-12-15 02:17:03 +00:00
committed by GitHub
co-authored by Thomas Nguy Federico Kunze Küllmer
parent 50e463725e
commit ccc6f5b53d
12 changed files with 145 additions and 31 deletions
+2 -2
View File
@@ -53,8 +53,8 @@ func NewAnteHandler(
// handle as *evmtypes.MsgEthereumTx
anteHandler = sdk.ChainAnteDecorators(
NewEthSetUpContextDecorator(), // outermost AnteDecorator. SetUpContext must be called first
authante.NewMempoolFeeDecorator(),
NewEthSetUpContextDecorator(), // outermost AnteDecorator. SetUpContext must be called first
NewEthMempoolFeeDecorator(evmKeeper, feeMarketKeeper), // Check eth effective gas price against minimal-gas-prices
authante.NewTxTimeoutHeightDecorator(),
authante.NewValidateMemoDecorator(ak),
NewEthValidateBasicDecorator(evmKeeper),
+5 -6
View File
@@ -193,7 +193,7 @@ func (suite AnteTestSuite) TestAnteHandler() {
// bigger than MaxGasWanted
txBuilder.SetGasLimit(uint64(1 << 63))
return txBuilder.GetTx()
}, false, true, false,
}, true, false, false,
},
{
"fail - CheckTx (memo too long)",
@@ -286,7 +286,7 @@ func (suite AnteTestSuite) TestAnteHandler() {
for _, tc := range testCases {
suite.Run(tc.name, func() {
suite.ctx = suite.ctx.WithIsCheckTx(tc.reCheckTx).WithIsReCheckTx(tc.reCheckTx)
suite.ctx = suite.ctx.WithIsCheckTx(tc.checkTx).WithIsReCheckTx(tc.reCheckTx)
// expConsumed := params.TxGasContractCreation + params.TxGas
_, err := suite.anteHandler(suite.ctx, tc.txFn(), false)
@@ -314,8 +314,6 @@ func (suite AnteTestSuite) TestAnteHandlerWithDynamicTxFee() {
suite.Require().NoError(acc.SetSequence(1))
suite.app.AccountKeeper.SetAccount(suite.ctx, acc)
suite.app.EvmKeeper.AddBalance(addr, big.NewInt((ethparams.InitialBaseFee+10)*100000))
testCases := []struct {
name string
txFn func() sdk.Tx
@@ -501,7 +499,7 @@ func (suite AnteTestSuite) TestAnteHandlerWithDynamicTxFee() {
// bigger than MaxGasWanted
txBuilder.SetGasLimit(uint64(1 << 63))
return txBuilder.GetTx()
}, false, true, false,
}, true, false, false,
},
{
"fail - CheckTx (memo too long)",
@@ -530,7 +528,8 @@ func (suite AnteTestSuite) TestAnteHandlerWithDynamicTxFee() {
for _, tc := range testCases {
suite.Run(tc.name, func() {
suite.ctx = suite.ctx.WithIsCheckTx(tc.reCheckTx).WithIsReCheckTx(tc.reCheckTx)
suite.ctx = suite.ctx.WithIsCheckTx(tc.checkTx).WithIsReCheckTx(tc.reCheckTx)
suite.app.EvmKeeper.AddBalance(addr, big.NewInt((ethparams.InitialBaseFee+10)*100000))
_, err := suite.anteHandler(suite.ctx, tc.txFn(), false)
if tc.expPass {
suite.Require().NoError(err)
+55
View File
@@ -543,3 +543,58 @@ func (esc EthSetupContextDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simul
newCtx = ctx.WithGasMeter(sdk.NewInfiniteGasMeter())
return next(newCtx, tx, simulate)
}
// EthMempoolFeeDecorator will check if the transaction's effective 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 EthMempoolFeeDecorator struct {
feemarketKeeper evmtypes.FeeMarketKeeper
evmKeeper EVMKeeper
}
func NewEthMempoolFeeDecorator(ek EVMKeeper, fmk evmtypes.FeeMarketKeeper) EthMempoolFeeDecorator {
return EthMempoolFeeDecorator{
feemarketKeeper: fmk,
evmKeeper: ek,
}
}
// AnteHandle ensures that the provided fees meet a minimum threshold for the validator,
// if this is a CheckTx. This is only for local mempool purposes, and thus
// is only ran on check tx.
func (mfd EthMempoolFeeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) {
if ctx.IsCheckTx() && !simulate {
if len(tx.GetMsgs()) != 1 {
return ctx, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "only 1 ethereum msg supported per tx")
}
msg, ok := tx.GetMsgs()[0].(*evmtypes.MsgEthereumTx)
if !ok {
return ctx, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "invalid transaction type %T, expected %T", tx, (*evmtypes.MsgEthereumTx)(nil))
}
var feeAmt *big.Int
feeMktParams := mfd.feemarketKeeper.GetParams(ctx)
params := mfd.evmKeeper.GetParams(ctx)
chainID := mfd.evmKeeper.ChainID()
ethCfg := params.ChainConfig.EthereumConfig(chainID)
evmDenom := params.EvmDenom
if evmtypes.IsLondon(ethCfg, ctx.BlockHeight()) && !feeMktParams.NoBaseFee {
baseFee := mfd.feemarketKeeper.GetBaseFee(ctx)
feeAmt = msg.GetEffectiveFee(baseFee)
} else {
feeAmt = msg.GetFee()
}
glDec := sdk.NewDec(int64(msg.GetGas()))
requiredFee := ctx.MinGasPrices().AmountOf(evmDenom).Mul(glDec)
if sdk.NewDecFromBigInt(feeAmt).LT(requiredFee) {
return ctx, sdkerrors.Wrapf(sdkerrors.ErrInsufficientFee, "insufficient fees; got: %s required: %s", feeAmt, requiredFee)
}
}
return next(ctx, tx, simulate)
}