From 75d553674c815e8c7f39b99106bf962499792d42 Mon Sep 17 00:00:00 2001 From: Thomas Nguy <81727899+thomas-nguy@users.noreply.github.com> Date: Wed, 13 Oct 2021 22:39:47 +0900 Subject: [PATCH] evm, rpc: disable `BaseFee` for non London block (#662) * disable basefee if not london block * add london block check in state transition * fix linter * add unit test * clean code * add changelog --- CHANGELOG.md | 1 + app/ante/eth.go | 6 ++--- rpc/ethereum/backend/backend.go | 39 ++++++++++++++++++++++-------- rpc/ethereum/namespaces/eth/api.go | 18 ++++++++++++-- x/evm/keeper/state_transition.go | 10 ++++++-- x/evm/types/params.go | 8 ++++++ x/evm/types/params_test.go | 31 ++++++++++++++++++++++++ 7 files changed, 96 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8267196b..c6329375 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -45,6 +45,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ ### Features * (rpc) [tharsis#624](https://github.com/tharsis/ethermint/pull/624) Implement new JSON-RPC endpoints from latest geth version +* (evm) [tharsis#662](https://github.com/tharsis/ethermint/pull/662) Disable basefee for non london blocks ### Bug Fixes diff --git a/app/ante/eth.go b/app/ante/eth.go index 8a0d94cc..951d0f29 100644 --- a/app/ante/eth.go +++ b/app/ante/eth.go @@ -359,7 +359,7 @@ func (ctd CanTransferDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate } var baseFee *big.Int - if !feeMktParams.NoBaseFee { + if evmtypes.IsLondon(ethCfg, ctx.BlockHeight()) && !feeMktParams.NoBaseFee { baseFee = ctd.feemarketKeeper.GetBaseFee(ctx) } @@ -383,14 +383,14 @@ func (ctd CanTransferDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate ) } - if !feeMktParams.NoBaseFee && baseFee == nil { + if evmtypes.IsLondon(ethCfg, ctx.BlockHeight()) && !feeMktParams.NoBaseFee && baseFee == nil { return ctx, stacktrace.Propagate( sdkerrors.Wrap(evmtypes.ErrInvalidBaseFee, "base fee is supported but evm block context value is nil"), "address %s", coreMsg.From(), ) } - if !feeMktParams.NoBaseFee && baseFee != nil && coreMsg.GasFeeCap().Cmp(baseFee) < 0 { + if evmtypes.IsLondon(ethCfg, ctx.BlockHeight()) && !feeMktParams.NoBaseFee && baseFee != nil && coreMsg.GasFeeCap().Cmp(baseFee) < 0 { return ctx, stacktrace.Propagate( sdkerrors.Wrapf(evmtypes.ErrInvalidBaseFee, "max fee per gas less than block base fee (%s < %s)", coreMsg.GasFeeCap(), baseFee), "address %s", coreMsg.From(), diff --git a/rpc/ethereum/backend/backend.go b/rpc/ethereum/backend/backend.go index 33b72ade..e7edc2d3 100644 --- a/rpc/ethereum/backend/backend.go +++ b/rpc/ethereum/backend/backend.go @@ -62,7 +62,7 @@ type Backend interface { GetTransactionByHash(txHash common.Hash) (*types.RPCTransaction, error) GetTxByEthHash(txHash common.Hash) (*tmrpctypes.ResultTx, error) EstimateGas(args evmtypes.TransactionArgs, blockNrOptional *types.BlockNumber) (hexutil.Uint64, error) - BaseFee() (*big.Int, error) + BaseFee(height int64) (*big.Int, error) // Filter API BloomStatus() (uint64, uint64) @@ -238,7 +238,7 @@ func (e *EVMBackend) EthBlockFromTendermint( ctx := types.ContextWithHeight(block.Height) - baseFee, err := e.BaseFee() + baseFee, err := e.BaseFee(block.Height) if err != nil { return nil, err } @@ -370,7 +370,7 @@ func (e *EVMBackend) HeaderByNumber(blockNum types.BlockNumber) (*ethtypes.Heade e.logger.Debug("HeaderByNumber BlockBloom failed", "height", resBlock.Block.Height) } - baseFee, err := e.BaseFee() + baseFee, err := e.BaseFee(resBlock.Block.Height) if err != nil { e.logger.Debug("HeaderByNumber BaseFee failed", "height", resBlock.Block.Height, "error", err.Error()) return nil, err @@ -398,7 +398,7 @@ func (e *EVMBackend) HeaderByHash(blockHash common.Hash) (*ethtypes.Header, erro e.logger.Debug("HeaderByHash BlockBloom failed", "height", resBlock.Block.Height) } - baseFee, err := e.BaseFee() + baseFee, err := e.BaseFee(resBlock.Block.Height) if err != nil { e.logger.Debug("HeaderByHash BaseFee failed", "height", resBlock.Block.Height, "error", err.Error()) return nil, err @@ -710,7 +710,21 @@ func (e *EVMBackend) EstimateGas(args evmtypes.TransactionArgs, blockNrOptional return 0, err } - baseFee, err := e.BaseFee() + height := blockNr.Int64() + currentBlockNumber, _ := e.BlockNumber() + switch blockNr { + case types.EthLatestBlockNumber: + if currentBlockNumber > 0 { + height = int64(currentBlockNumber) + } + case types.EthPendingBlockNumber: + if currentBlockNumber > 0 { + height = int64(currentBlockNumber) + } + case types.EthEarliestBlockNumber: + height = 1 + } + baseFee, err := e.BaseFee(height) if err != nil { return 0, err } @@ -730,7 +744,7 @@ func (e *EVMBackend) EstimateGas(args evmtypes.TransactionArgs, blockNrOptional // From ContextWithHeight: if the provided height is 0, // it will return an empty context and the gRPC query will use // the latest block height for querying. - res, err := e.queryClient.EstimateGas(types.ContextWithHeight(blockNr.Int64()), &req) + res, err := e.queryClient.EstimateGas(types.ContextWithHeight(height), &req) if err != nil { return 0, err } @@ -805,9 +819,14 @@ func (e *EVMBackend) SuggestGasTipCap() (*big.Int, error) { } // BaseFee returns the base fee tracked by the Fee Market module. If the base fee is not enabled, -// it returns the initial base fee amount. -func (e *EVMBackend) BaseFee() (*big.Int, error) { - res, err := e.queryClient.FeeMarket.BaseFee(e.ctx, &feemarkettypes.QueryBaseFeeRequest{}) +// it returns the initial base fee amount. Return nil if London is not activated. +func (e *EVMBackend) BaseFee(height int64) (*big.Int, error) { + cfg := e.ChainConfig() + if !cfg.IsLondon(new(big.Int).SetInt64(height)) { + return nil, nil + } + + res, err := e.queryClient.FeeMarket.BaseFee(types.ContextWithHeight(height), &feemarkettypes.QueryBaseFeeRequest{}) if err != nil { return nil, err } @@ -816,7 +835,7 @@ func (e *EVMBackend) BaseFee() (*big.Int, error) { return res.BaseFee.BigInt(), nil } - resParams, err := e.queryClient.FeeMarket.Params(e.ctx, &feemarkettypes.QueryParamsRequest{}) + resParams, err := e.queryClient.FeeMarket.Params(types.ContextWithHeight(height), &feemarkettypes.QueryParamsRequest{}) if err != nil { return nil, err } diff --git a/rpc/ethereum/namespaces/eth/api.go b/rpc/ethereum/namespaces/eth/api.go index 545df143..6fca1366 100644 --- a/rpc/ethereum/namespaces/eth/api.go +++ b/rpc/ethereum/namespaces/eth/api.go @@ -587,7 +587,21 @@ func (e *PublicAPI) doCall( return nil, err } - baseFee, err := e.backend.BaseFee() + currentBlockNumber, _ := e.BlockNumber() + height := blockNr.Int64() + switch blockNr { + case rpctypes.EthLatestBlockNumber: + if currentBlockNumber > 0 { + height = int64(currentBlockNumber) + } + case rpctypes.EthPendingBlockNumber: + if currentBlockNumber > 0 { + height = int64(currentBlockNumber) + } + case rpctypes.EthEarliestBlockNumber: + height = 1 + } + baseFee, err := e.backend.BaseFee(height) if err != nil { return nil, err } @@ -680,7 +694,7 @@ func (e *PublicAPI) GetTransactionByBlockHashAndIndex(hash common.Hash, idx hexu return nil, err } - baseFee, err := e.backend.BaseFee() + baseFee, err := e.backend.BaseFee(resBlock.Block.Height) if err != nil { return nil, err } diff --git a/x/evm/keeper/state_transition.go b/x/evm/keeper/state_transition.go index 137e523a..faaffa25 100644 --- a/x/evm/keeper/state_transition.go +++ b/x/evm/keeper/state_transition.go @@ -159,7 +159,10 @@ func (k *Keeper) ApplyTransaction(tx *ethtypes.Transaction) (*types.MsgEthereumT // get the latest signer according to the chain rules from the config signer := ethtypes.MakeSigner(ethCfg, big.NewInt(ctx.BlockHeight())) - baseFee := k.feeMarketKeeper.GetBaseFee(ctx) + var baseFee *big.Int + if types.IsLondon(ethCfg, ctx.BlockHeight()) { + baseFee = k.feeMarketKeeper.GetBaseFee(ctx) + } msg, err := tx.AsMessage(signer, baseFee) if err != nil { @@ -368,7 +371,10 @@ func (k *Keeper) ApplyNativeMessage(msg core.Message) (*types.MsgEthereumTxRespo return nil, stacktrace.Propagate(err, "failed to obtain coinbase address") } - baseFee := k.feeMarketKeeper.GetBaseFee(ctx) + var baseFee *big.Int + if types.IsLondon(ethCfg, ctx.BlockHeight()) { + baseFee = k.feeMarketKeeper.GetBaseFee(ctx) + } tracer := types.NewTracer(k.tracer, msg, ethCfg, ctx.BlockHeight(), k.debug) evm := k.NewEVM(msg, ethCfg, params, coinbase, baseFee, tracer) diff --git a/x/evm/types/params.go b/x/evm/types/params.go index 921be389..5276d589 100644 --- a/x/evm/types/params.go +++ b/x/evm/types/params.go @@ -2,6 +2,9 @@ package types import ( "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/params" sdk "github.com/cosmos/cosmos-sdk/types" paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" @@ -132,3 +135,8 @@ func validateChainConfig(i interface{}) error { return cfg.Validate() } + +func IsLondon(ethConfig *params.ChainConfig, height int64) bool { + rules := ethConfig.Rules(big.NewInt(height)) + return rules.IsLondon +} diff --git a/x/evm/types/params_test.go b/x/evm/types/params_test.go index 48614b06..aa09fd5a 100644 --- a/x/evm/types/params_test.go +++ b/x/evm/types/params_test.go @@ -3,6 +3,8 @@ package types import ( "testing" + "github.com/ethereum/go-ethereum/params" + paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" "github.com/stretchr/testify/require" ) @@ -104,3 +106,32 @@ func TestValidateChainConfig(t *testing.T) { } } } + +func TestIsLondon(t *testing.T) { + testCases := []struct { + name string + height int64 + result bool + }{ + { + "Before london block", + 5, + false, + }, + { + "After london block", + 12_965_001, + true, + }, + { + "london block", + 12_965_000, + true, + }, + } + + for _, tc := range testCases { + ethConfig := params.MainnetChainConfig + require.Equal(t, IsLondon(ethConfig, tc.height), tc.result) + } +}