Skip to content
This repository has been archived by the owner on Apr 4, 2024. It is now read-only.

fix(rpc): align nextBaseFee and invalid check for fee history #1720

Closed
wants to merge 12 commits into from
Closed
Show file tree
Hide file tree
Changes from all 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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ Ref: https://keepachangelog.com/en/1.0.0/

## Unreleased

### Bug Fixes

* (rpc) [#1720](https://github.com/evmos/ethermint/pull/1720) Fix next block fee for historical block and calculate base fee by params.

### State Machine Breaking

- (deps) [#1168](https://github.com/evmos/ethermint/pull/1716) Bump Cosmos-SDK to v0.46.11, Tendermint to v0.34.27, IAVL v0.19.5 and btcd to v0.23.4
Expand Down
20 changes: 10 additions & 10 deletions gomod2nix.toml
Original file line number Diff line number Diff line change
Expand Up @@ -409,8 +409,8 @@ schema = 3
version = "v0.9.1"
hash = "sha256-YLGNrHHM+mN4ElW/XWuylOnFrA/VjSY+eBuC4LN//5c="
[mod."github.com/rs/cors"]
version = "v1.8.3"
hash = "sha256-VgVB4HKAhPSjNg96mIEUN1bt5ZQng8Fi3ZABy3CDWQE="
version = "v1.9.0"
hash = "sha256-CNBCGXOydU6MIEZ0B5eXBjBm3smH2ryYHvgRJA2udBM="
[mod."github.com/rs/zerolog"]
version = "v1.27.0"
hash = "sha256-BxQtP2TROeSSpj9l1irocuSfxn55UL4ugzB/og7r8eE="
Expand Down Expand Up @@ -512,23 +512,23 @@ schema = 3
version = "v0.0.0-20230131160201-f062dba9d201"
hash = "sha256-sxLT/VOe93v0h3miChJSHS9gscTZS/B71+390ju/e20="
[mod."golang.org/x/net"]
version = "v0.8.0"
hash = "sha256-2cOtqa7aJ5mn64kZ+8+PVjJ4uGbhpXTpC1vm/+iaZzM="
version = "v0.9.0"
hash = "sha256-EG5GRDq282twyce8uugsDTjMz1pNn6zPcyVTZmSiJ14="
[mod."golang.org/x/oauth2"]
version = "v0.4.0"
hash = "sha256-Dj9wHbSbs0Ghr9Hef0hSfanaR8L0GShI18jGBT3yNn8="
[mod."golang.org/x/sync"]
version = "v0.1.0"
hash = "sha256-Hygjq9euZ0qz6TvHYQwOZEjNiTbTh1nSLRAWZ6KFGR8="
[mod."golang.org/x/sys"]
version = "v0.6.0"
hash = "sha256-zAgxiTuL24sGhbXrna9R1UYqLQh46ldztpumOScmduY="
version = "v0.7.0"
hash = "sha256-GotRHJaas/q3L+tFam0q3oQ1rc8GDStt7wnz9h8MTEU="
[mod."golang.org/x/term"]
version = "v0.6.0"
hash = "sha256-Ao0yXpwY8GyG+/23dVfJUYrfEfNUTES3RF45v1VhUAk="
version = "v0.7.0"
hash = "sha256-VYnXZ50OXTsylzncIMceVC2ZBKdbyp+V367Qbq3Vlqk="
[mod."golang.org/x/text"]
version = "v0.8.0"
hash = "sha256-hgWFnT01DRmywBEXKYEVaOee7i6z8Ydz7zGbjcWwOgI="
version = "v0.9.0"
hash = "sha256-tkhDeMsSQZr3jo7vmKehWs3DvWetwXR0IB+DCLbQ4nk="
[mod."golang.org/x/tools"]
version = "v0.7.0"
hash = "sha256-ZEjfFulQd6U9r4mEJ5RZOnW49NZnQnrCFLMKCgLg7go="
Expand Down
124 changes: 82 additions & 42 deletions rpc/backend/chain_info.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@ package backend

import (
"fmt"
"math"
"math/big"
"strconv"
"sync"

sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/ethereum/go-ethereum/common/hexutil"
Expand Down Expand Up @@ -153,29 +155,42 @@ func (b *Backend) GetCoinbase() (sdk.AccAddress, error) {
return address, nil
}

var (
errInvalidPercentile = fmt.Errorf("invalid reward percentile")
errRequestBeyondHead = fmt.Errorf("request beyond head block")
)

// FeeHistory returns data relevant for fee estimation based on the specified range of blocks.
func (b *Backend) FeeHistory(
userBlockCount rpc.DecimalOrHex, // number blocks to fetch, maximum is 100
lastBlock rpc.BlockNumber, // the block to start search , to oldest
rewardPercentiles []float64, // percentiles to fetch reward
) (*rpctypes.FeeHistoryResult, error) {
for i, p := range rewardPercentiles {
if p < 0 || p > 100 {
return nil, fmt.Errorf("%w: %f", errInvalidPercentile, p)
}
if i > 0 && p < rewardPercentiles[i-1] {
return nil, fmt.Errorf("%w: #%d:%f > #%d:%f", errInvalidPercentile, i-1, rewardPercentiles[i-1], i, p)
}
}
blockNumber, err := b.BlockNumber()
if err != nil {
return nil, err
}
blockEnd := int64(lastBlock)

if blockEnd < 0 {
blockNumber, err := b.BlockNumber()
if err != nil {
return nil, err
}
blockEnd = int64(blockNumber)
} else if int64(blockNumber) < blockEnd {

Check failure

Code scanning / gosec

Potential integer overflow by integer type conversion

Potential integer overflow by integer type conversion
return nil, fmt.Errorf("%w: requested %d, head %d", errRequestBeyondHead, blockEnd, int64(blockNumber))

Check failure

Code scanning / gosec

Potential integer overflow by integer type conversion

Potential integer overflow by integer type conversion
}

blocks := int64(userBlockCount)
maxBlockCount := int64(b.cfg.JSONRPC.FeeHistoryCap)
if blocks > maxBlockCount {
return nil, fmt.Errorf("FeeHistory user block count %d higher than %d", blocks, maxBlockCount)
}

if blockEnd+1 < blocks {
if blockEnd < math.MaxInt64 && blockEnd+1 < blocks {
blocks = blockEnd + 1
}
// Ensure not trying to retrieve before genesis.
Expand All @@ -194,46 +209,71 @@ func (b *Backend) FeeHistory(

// rewards should only be calculated if reward percentiles were included
calculateRewards := rewardCount != 0
const maxBlockFetchers = 4
for blockID := blockStart; blockID <= blockEnd; blockID += maxBlockFetchers {
wg := sync.WaitGroup{}
wgDone := make(chan bool)
chanErr := make(chan error)
for i := 0; i < maxBlockFetchers; i++ {
if blockID+int64(i) >= blockEnd+1 {
break
}
wg.Add(1)
go func(index int32) {
defer wg.Done()
// fetch block
// tendermint block
blockNum := rpctypes.BlockNumber(blockStart + int64(index))
tendermintblock, err := b.TendermintBlockByNumber(blockNum)
if tendermintblock == nil {
chanErr <- err
return
}

// fetch block
for blockID := blockStart; blockID <= blockEnd; blockID++ {
index := int32(blockID - blockStart)
// tendermint block
tendermintblock, err := b.TendermintBlockByNumber(rpctypes.BlockNumber(blockID))
if tendermintblock == nil {
return nil, err
}

// eth block
ethBlock, err := b.GetBlockByNumber(rpctypes.BlockNumber(blockID), true)
if ethBlock == nil {
return nil, err
}
// eth block
ethBlock, err := b.GetBlockByNumber(blockNum, true)
if ethBlock == nil {
chanErr <- err
return
}

// tendermint block result
tendermintBlockResult, err := b.TendermintBlockResultByNumber(&tendermintblock.Block.Height)
if tendermintBlockResult == nil {
b.logger.Debug("block result not found", "height", tendermintblock.Block.Height, "error", err.Error())
return nil, err
}
// tendermint block result
tendermintBlockResult, err := b.TendermintBlockResultByNumber(&tendermintblock.Block.Height)
if tendermintBlockResult == nil {
b.logger.Debug("block result not found", "height", tendermintblock.Block.Height, "error", err.Error())
chanErr <- err
return
}

oneFeeHistory := rpctypes.OneFeeHistory{}
err = b.processBlock(tendermintblock, &ethBlock, rewardPercentiles, tendermintBlockResult, &oneFeeHistory)
if err != nil {
return nil, err
}
oneFeeHistory := rpctypes.OneFeeHistory{}
err = b.processBlock(tendermintblock, &ethBlock, rewardPercentiles, tendermintBlockResult, &oneFeeHistory)
if err != nil {
chanErr <- err
return
}

// copy
thisBaseFee[index] = (*hexutil.Big)(oneFeeHistory.BaseFee)
thisBaseFee[index+1] = (*hexutil.Big)(oneFeeHistory.NextBaseFee)
thisGasUsedRatio[index] = oneFeeHistory.GasUsedRatio
if calculateRewards {
for j := 0; j < rewardCount; j++ {
reward[index][j] = (*hexutil.Big)(oneFeeHistory.Reward[j])
if reward[index][j] == nil {
reward[index][j] = (*hexutil.Big)(big.NewInt(0))
// copy
thisBaseFee[index] = (*hexutil.Big)(oneFeeHistory.BaseFee)
thisBaseFee[index+1] = (*hexutil.Big)(oneFeeHistory.NextBaseFee)
thisGasUsedRatio[index] = oneFeeHistory.GasUsedRatio
if calculateRewards {
for j := 0; j < rewardCount; j++ {
reward[index][j] = (*hexutil.Big)(oneFeeHistory.Reward[j])
if reward[index][j] == nil {
reward[index][j] = (*hexutil.Big)(big.NewInt(0))
}
}
}
}
}(int32(blockID - blockStart + int64(i)))

Check failure

Code scanning / gosec

Potential integer overflow by integer type conversion

Potential integer overflow by integer type conversion
Comment on lines +222 to +267

Check notice

Code scanning / CodeQL

Spawning a Go routine

Spawning a Go routine may be a possible source of non-determinism
}
go func() {
wg.Wait()
close(wgDone)
}()
Comment on lines +269 to +272

Check notice

Code scanning / CodeQL

Spawning a Go routine

Spawning a Go routine may be a possible source of non-determinism
select {
case <-wgDone:
case err := <-chanErr:
return nil, err
}
}

Expand Down
4 changes: 2 additions & 2 deletions rpc/backend/chain_info_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -405,17 +405,17 @@ func (suite *BackendTestSuite) TestFeeHistory() {
{
"pass - Valid FeeHistoryResults object",
func(validator sdk.AccAddress) {
var header metadata.MD
baseFee := sdk.NewInt(1)
queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient)
feeMarketClient := suite.backend.queryClient.FeeMarket.(*mocks.FeeMarketQueryClient)
client := suite.backend.clientCtx.Client.(*mocks.Client)
suite.backend.cfg.JSONRPC.FeeHistoryCap = 2
RegisterBlock(client, ethrpc.BlockNumber(1).Int64(), nil)
RegisterBlockResults(client, 1)
RegisterBaseFee(queryClient, baseFee)
RegisterValidatorAccount(queryClient, validator)
RegisterConsensusParams(client, 1)
RegisterParams(queryClient, &header, 1)
RegisterFeeMarketParams(feeMarketClient, 1)
RegisterParamsWithoutHeader(queryClient, 1)
},
1,
Expand Down
70 changes: 64 additions & 6 deletions rpc/backend/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,17 @@ import (

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/consensus/misc"
"github.com/ethereum/go-ethereum/common/math"
ethtypes "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/params"

abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/libs/log"
tmrpctypes "github.com/tendermint/tendermint/rpc/core/types"

"github.com/evmos/ethermint/rpc/types"
evmtypes "github.com/evmos/ethermint/x/evm/types"
feemarkettypes "github.com/evmos/ethermint/x/feemarket/types"
"github.com/tendermint/tendermint/proto/tendermint/crypto"
)

Expand Down Expand Up @@ -117,6 +119,47 @@ func (b *Backend) getAccountNonce(accAddr common.Address, pending bool, height i
return nonce, nil
}

// CalcBaseFee calculates the basefee of the header.
func CalcBaseFee(config *params.ChainConfig, parent *ethtypes.Header, baseFeeChangeDenominator, elasticityMultiplier uint32) *big.Int {
// If the current block is the first EIP-1559 block, return the InitialBaseFee.
if !config.IsLondon(parent.Number) {
return new(big.Int).SetUint64(params.InitialBaseFee)
}

parentGasTarget := parent.GasLimit / uint64(elasticityMultiplier)
// If the parent gasUsed is the same as the target, the baseFee remains unchanged.
if parent.GasUsed == parentGasTarget {
return new(big.Int).Set(parent.BaseFee)
}

var (
num = new(big.Int)
denom = new(big.Int)
)

if parent.GasUsed > parentGasTarget {
// If the parent block used more gas than its target, the baseFee should increase.
// max(1, parentBaseFee * gasUsedDelta / parentGasTarget / baseFeeChangeDenominator)
num.SetUint64(parent.GasUsed - parentGasTarget)
num.Mul(num, parent.BaseFee)
num.Div(num, denom.SetUint64(parentGasTarget))
num.Div(num, denom.SetUint64(uint64(baseFeeChangeDenominator)))
baseFeeDelta := math.BigMax(num, common.Big1)

return num.Add(parent.BaseFee, baseFeeDelta)
} else {
// Otherwise if the parent block used less gas than its target, the baseFee should decrease.
// max(0, parentBaseFee * gasUsedDelta / parentGasTarget / baseFeeChangeDenominator)
num.SetUint64(parentGasTarget - parent.GasUsed)
num.Mul(num, parent.BaseFee)
num.Div(num, denom.SetUint64(parentGasTarget))
num.Div(num, denom.SetUint64(uint64(baseFeeChangeDenominator)))
baseFee := num.Sub(parent.BaseFee, num)

return math.BigMax(baseFee, common.Big0)
}
}

// output: targetOneFeeHistory
func (b *Backend) processBlock(
tendermintBlock *tmrpctypes.ResultBlock,
Expand All @@ -134,11 +177,6 @@ func (b *Backend) processBlock(
// set basefee
targetOneFeeHistory.BaseFee = blockBaseFee
cfg := b.ChainConfig()
if cfg.IsLondon(big.NewInt(blockHeight + 1)) {
targetOneFeeHistory.NextBaseFee = misc.CalcBaseFee(cfg, b.CurrentHeader())
} else {
targetOneFeeHistory.NextBaseFee = new(big.Int)
}
// set gas used ratio
gasLimitUint64, ok := (*ethBlock)["gasLimit"].(hexutil.Uint64)
if !ok {
Expand All @@ -150,6 +188,26 @@ func (b *Backend) processBlock(
return fmt.Errorf("invalid gas used type: %T", (*ethBlock)["gasUsed"])
}

baseFee, ok := (*ethBlock)["baseFeePerGas"].(*hexutil.Big)
if !ok {
return fmt.Errorf("invalid baseFee: %T", (*ethBlock)["baseFeePerGas"])
}

if cfg.IsLondon(big.NewInt(blockHeight + 1)) {
var header ethtypes.Header
header.Number = new(big.Int).SetInt64(blockHeight)
header.BaseFee = baseFee.ToInt()
header.GasLimit = uint64(gasLimitUint64)
header.GasUsed = gasUsedBig.ToInt().Uint64()
params, err := b.queryClient.FeeMarket.Params(b.ctx, &feemarkettypes.QueryParamsRequest{})
if err != nil {
return err
}
targetOneFeeHistory.NextBaseFee = CalcBaseFee(cfg, &header, params.Params.BaseFeeChangeDenominator, params.Params.ElasticityMultiplier)
} else {
targetOneFeeHistory.NextBaseFee = new(big.Int)
}

gasusedfloat, _ := new(big.Float).SetInt(gasUsedBig.ToInt()).Float64()

if gasLimitUint64 <= 0 {
Expand Down
16 changes: 16 additions & 0 deletions tests/integration_tests/configs/fee-history.jsonnet
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
local config = import 'default.jsonnet';

config {
'ethermint_9000-1'+: {
genesis+: {
app_state+: {
feemarket+: {
params+: {
elasticity_multiplier: 3,
base_fee_change_denominator: 100000000,
},
},
},
},
},
}
4 changes: 2 additions & 2 deletions tests/integration_tests/cosmoscli.py
Original file line number Diff line number Diff line change
Expand Up @@ -273,9 +273,9 @@ def validators(self):
)
)["validators"]

def staking_params(self):
def get_params(self, module):
return json.loads(
self.raw("query", "staking", "params", output="json", node=self.node_rpc)
self.raw("query", module, "params", output="json", node=self.node_rpc)
)

def staking_pool(self, bonded=True):
Expand Down
Loading