From 9f7d2f649b0ecc9ec515a23c5405f52c7611d4be Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Isabel=20Sch=C3=B6ps=20Thiel?=
<155141998+IST-Github@users.noreply.github.com>
Date: Mon, 1 Jan 2024 00:50:15 +0100
Subject: [PATCH] Delete eth/gasprice directory
---
eth/gasprice/feehistory.go | 328 --------------------------------
eth/gasprice/feehistory_test.go | 91 ---------
eth/gasprice/gasprice.go | 276 ---------------------------
eth/gasprice/gasprice_test.go | 215 ---------------------
4 files changed, 910 deletions(-)
delete mode 100644 eth/gasprice/feehistory.go
delete mode 100644 eth/gasprice/feehistory_test.go
delete mode 100644 eth/gasprice/gasprice.go
delete mode 100644 eth/gasprice/gasprice_test.go
diff --git a/eth/gasprice/feehistory.go b/eth/gasprice/feehistory.go
deleted file mode 100644
index 226991b24b..0000000000
--- a/eth/gasprice/feehistory.go
+++ /dev/null
@@ -1,328 +0,0 @@
-// Copyright 2021 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package gasprice
-
-import (
- "context"
- "encoding/binary"
- "errors"
- "fmt"
- "math"
- "math/big"
- "sync/atomic"
-
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/consensus/misc/eip1559"
- "github.com/ethereum/go-ethereum/core/types"
- "github.com/ethereum/go-ethereum/log"
- "github.com/ethereum/go-ethereum/rpc"
- "golang.org/x/exp/slices"
-)
-
-var (
- errInvalidPercentile = errors.New("invalid reward percentile")
- errRequestBeyondHead = errors.New("request beyond head block")
-)
-
-const (
- // maxBlockFetchers is the max number of goroutines to spin up to pull blocks
- // for the fee history calculation (mostly relevant for LES).
- maxBlockFetchers = 4
-)
-
-// blockFees represents a single block for processing
-type blockFees struct {
- // set by the caller
- blockNumber uint64
- header *types.Header
- block *types.Block // only set if reward percentiles are requested
- receipts types.Receipts
- // filled by processBlock
- results processedFees
- err error
-}
-
-type cacheKey struct {
- number uint64
- percentiles string
-}
-
-// processedFees contains the results of a processed block.
-type processedFees struct {
- reward []*big.Int
- baseFee, nextBaseFee *big.Int
- gasUsedRatio float64
-}
-
-// txGasAndReward is sorted in ascending order based on reward
-type txGasAndReward struct {
- gasUsed uint64
- reward *big.Int
-}
-
-// processBlock takes a blockFees structure with the blockNumber, the header and optionally
-// the block field filled in, retrieves the block from the backend if not present yet and
-// fills in the rest of the fields.
-func (oracle *Oracle) processBlock(bf *blockFees, percentiles []float64) {
- chainconfig := oracle.backend.ChainConfig()
- if bf.results.baseFee = bf.header.BaseFee; bf.results.baseFee == nil {
- bf.results.baseFee = new(big.Int)
- }
- if chainconfig.IsLondon(big.NewInt(int64(bf.blockNumber + 1))) {
- bf.results.nextBaseFee = eip1559.CalcBaseFee(chainconfig, bf.header)
- } else {
- bf.results.nextBaseFee = new(big.Int)
- }
- bf.results.gasUsedRatio = float64(bf.header.GasUsed) / float64(bf.header.GasLimit)
- if len(percentiles) == 0 {
- // rewards were not requested, return null
- return
- }
- if bf.block == nil || (bf.receipts == nil && len(bf.block.Transactions()) != 0) {
- log.Error("Block or receipts are missing while reward percentiles are requested")
- return
- }
-
- bf.results.reward = make([]*big.Int, len(percentiles))
- if len(bf.block.Transactions()) == 0 {
- // return an all zero row if there are no transactions to gather data from
- for i := range bf.results.reward {
- bf.results.reward[i] = new(big.Int)
- }
- return
- }
-
- sorter := make([]txGasAndReward, len(bf.block.Transactions()))
- for i, tx := range bf.block.Transactions() {
- reward, _ := tx.EffectiveGasTip(bf.block.BaseFee())
- sorter[i] = txGasAndReward{gasUsed: bf.receipts[i].GasUsed, reward: reward}
- }
- slices.SortStableFunc(sorter, func(a, b txGasAndReward) int {
- return a.reward.Cmp(b.reward)
- })
-
- var txIndex int
- sumGasUsed := sorter[0].gasUsed
-
- for i, p := range percentiles {
- thresholdGasUsed := uint64(float64(bf.block.GasUsed()) * p / 100)
- for sumGasUsed < thresholdGasUsed && txIndex < len(bf.block.Transactions())-1 {
- txIndex++
- sumGasUsed += sorter[txIndex].gasUsed
- }
- bf.results.reward[i] = sorter[txIndex].reward
- }
-}
-
-// resolveBlockRange resolves the specified block range to absolute block numbers while also
-// enforcing backend specific limitations. The pending block and corresponding receipts are
-// also returned if requested and available.
-// Note: an error is only returned if retrieving the head header has failed. If there are no
-// retrievable blocks in the specified range then zero block count is returned with no error.
-func (oracle *Oracle) resolveBlockRange(ctx context.Context, reqEnd rpc.BlockNumber, blocks uint64) (*types.Block, []*types.Receipt, uint64, uint64, error) {
- var (
- headBlock *types.Header
- pendingBlock *types.Block
- pendingReceipts types.Receipts
- err error
- )
-
- // Get the chain's current head.
- if headBlock, err = oracle.backend.HeaderByNumber(ctx, rpc.LatestBlockNumber); err != nil {
- return nil, nil, 0, 0, err
- }
- head := rpc.BlockNumber(headBlock.Number.Uint64())
-
- // Fail if request block is beyond the chain's current head.
- if head < reqEnd {
- return nil, nil, 0, 0, fmt.Errorf("%w: requested %d, head %d", errRequestBeyondHead, reqEnd, head)
- }
-
- // Resolve block tag.
- if reqEnd < 0 {
- var (
- resolved *types.Header
- err error
- )
- switch reqEnd {
- case rpc.PendingBlockNumber:
- if pendingBlock, pendingReceipts = oracle.backend.PendingBlockAndReceipts(); pendingBlock != nil {
- resolved = pendingBlock.Header()
- } else {
- // Pending block not supported by backend, process only until latest block.
- resolved = headBlock
-
- // Update total blocks to return to account for this.
- blocks--
- }
- case rpc.LatestBlockNumber:
- // Retrieved above.
- resolved = headBlock
- case rpc.SafeBlockNumber:
- resolved, err = oracle.backend.HeaderByNumber(ctx, rpc.SafeBlockNumber)
- case rpc.FinalizedBlockNumber:
- resolved, err = oracle.backend.HeaderByNumber(ctx, rpc.FinalizedBlockNumber)
- case rpc.EarliestBlockNumber:
- resolved, err = oracle.backend.HeaderByNumber(ctx, rpc.EarliestBlockNumber)
- }
- if resolved == nil || err != nil {
- return nil, nil, 0, 0, err
- }
- // Absolute number resolved.
- reqEnd = rpc.BlockNumber(resolved.Number.Uint64())
- }
-
- // If there are no blocks to return, short circuit.
- if blocks == 0 {
- return nil, nil, 0, 0, nil
- }
- // Ensure not trying to retrieve before genesis.
- if uint64(reqEnd+1) < blocks {
- blocks = uint64(reqEnd + 1)
- }
- return pendingBlock, pendingReceipts, uint64(reqEnd), blocks, nil
-}
-
-// FeeHistory returns data relevant for fee estimation based on the specified range of blocks.
-// The range can be specified either with absolute block numbers or ending with the latest
-// or pending block. Backends may or may not support gathering data from the pending block
-// or blocks older than a certain age (specified in maxHistory). The first block of the
-// actually processed range is returned to avoid ambiguity when parts of the requested range
-// are not available or when the head has changed during processing this request.
-// Three arrays are returned based on the processed blocks:
-// - reward: the requested percentiles of effective priority fees per gas of transactions in each
-// block, sorted in ascending order and weighted by gas used.
-// - baseFee: base fee per gas in the given block
-// - gasUsedRatio: gasUsed/gasLimit in the given block
-//
-// Note: baseFee includes the next block after the newest of the returned range, because this
-// value can be derived from the newest block.
-func (oracle *Oracle) FeeHistory(ctx context.Context, blocks uint64, unresolvedLastBlock rpc.BlockNumber, rewardPercentiles []float64) (*big.Int, [][]*big.Int, []*big.Int, []float64, error) {
- if blocks < 1 {
- return common.Big0, nil, nil, nil, nil // returning with no data and no error means there are no retrievable blocks
- }
- maxFeeHistory := oracle.maxHeaderHistory
- if len(rewardPercentiles) != 0 {
- maxFeeHistory = oracle.maxBlockHistory
- }
- if blocks > maxFeeHistory {
- log.Warn("Sanitizing fee history length", "requested", blocks, "truncated", maxFeeHistory)
- blocks = maxFeeHistory
- }
- for i, p := range rewardPercentiles {
- if p < 0 || p > 100 {
- return common.Big0, nil, nil, nil, fmt.Errorf("%w: %f", errInvalidPercentile, p)
- }
- if i > 0 && p < rewardPercentiles[i-1] {
- return common.Big0, nil, nil, nil, fmt.Errorf("%w: #%d:%f > #%d:%f", errInvalidPercentile, i-1, rewardPercentiles[i-1], i, p)
- }
- }
- var (
- pendingBlock *types.Block
- pendingReceipts []*types.Receipt
- err error
- )
- pendingBlock, pendingReceipts, lastBlock, blocks, err := oracle.resolveBlockRange(ctx, unresolvedLastBlock, blocks)
- if err != nil || blocks == 0 {
- return common.Big0, nil, nil, nil, err
- }
- oldestBlock := lastBlock + 1 - blocks
-
- var next atomic.Uint64
- next.Store(oldestBlock)
- results := make(chan *blockFees, blocks)
-
- percentileKey := make([]byte, 8*len(rewardPercentiles))
- for i, p := range rewardPercentiles {
- binary.LittleEndian.PutUint64(percentileKey[i*8:(i+1)*8], math.Float64bits(p))
- }
- for i := 0; i < maxBlockFetchers && i < int(blocks); i++ {
- go func() {
- for {
- // Retrieve the next block number to fetch with this goroutine
- blockNumber := next.Add(1) - 1
- if blockNumber > lastBlock {
- return
- }
-
- fees := &blockFees{blockNumber: blockNumber}
- if pendingBlock != nil && blockNumber >= pendingBlock.NumberU64() {
- fees.block, fees.receipts = pendingBlock, pendingReceipts
- fees.header = fees.block.Header()
- oracle.processBlock(fees, rewardPercentiles)
- results <- fees
- } else {
- cacheKey := cacheKey{number: blockNumber, percentiles: string(percentileKey)}
-
- if p, ok := oracle.historyCache.Get(cacheKey); ok {
- fees.results = p
- results <- fees
- } else {
- if len(rewardPercentiles) != 0 {
- fees.block, fees.err = oracle.backend.BlockByNumber(ctx, rpc.BlockNumber(blockNumber))
- if fees.block != nil && fees.err == nil {
- fees.receipts, fees.err = oracle.backend.GetReceipts(ctx, fees.block.Hash())
- fees.header = fees.block.Header()
- }
- } else {
- fees.header, fees.err = oracle.backend.HeaderByNumber(ctx, rpc.BlockNumber(blockNumber))
- }
- if fees.header != nil && fees.err == nil {
- oracle.processBlock(fees, rewardPercentiles)
- if fees.err == nil {
- oracle.historyCache.Add(cacheKey, fees.results)
- }
- }
- // send to results even if empty to guarantee that blocks items are sent in total
- results <- fees
- }
- }
- }
- }()
- }
- var (
- reward = make([][]*big.Int, blocks)
- baseFee = make([]*big.Int, blocks+1)
- gasUsedRatio = make([]float64, blocks)
- firstMissing = blocks
- )
- for ; blocks > 0; blocks-- {
- fees := <-results
- if fees.err != nil {
- return common.Big0, nil, nil, nil, fees.err
- }
- i := fees.blockNumber - oldestBlock
- if fees.results.baseFee != nil {
- reward[i], baseFee[i], baseFee[i+1], gasUsedRatio[i] = fees.results.reward, fees.results.baseFee, fees.results.nextBaseFee, fees.results.gasUsedRatio
- } else {
- // getting no block and no error means we are requesting into the future (might happen because of a reorg)
- if i < firstMissing {
- firstMissing = i
- }
- }
- }
- if firstMissing == 0 {
- return common.Big0, nil, nil, nil, nil
- }
- if len(rewardPercentiles) != 0 {
- reward = reward[:firstMissing]
- } else {
- reward = nil
- }
- baseFee, gasUsedRatio = baseFee[:firstMissing+1], gasUsedRatio[:firstMissing]
- return new(big.Int).SetUint64(oldestBlock), reward, baseFee, gasUsedRatio, nil
-}
diff --git a/eth/gasprice/feehistory_test.go b/eth/gasprice/feehistory_test.go
deleted file mode 100644
index 1bcfb287a5..0000000000
--- a/eth/gasprice/feehistory_test.go
+++ /dev/null
@@ -1,91 +0,0 @@
-// Copyright 2021 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package gasprice
-
-import (
- "context"
- "errors"
- "math/big"
- "testing"
-
- "github.com/ethereum/go-ethereum/rpc"
-)
-
-func TestFeeHistory(t *testing.T) {
- var cases = []struct {
- pending bool
- maxHeader, maxBlock uint64
- count uint64
- last rpc.BlockNumber
- percent []float64
- expFirst uint64
- expCount int
- expErr error
- }{
- {false, 1000, 1000, 10, 30, nil, 21, 10, nil},
- {false, 1000, 1000, 10, 30, []float64{0, 10}, 21, 10, nil},
- {false, 1000, 1000, 10, 30, []float64{20, 10}, 0, 0, errInvalidPercentile},
- {false, 1000, 1000, 1000000000, 30, nil, 0, 31, nil},
- {false, 1000, 1000, 1000000000, rpc.LatestBlockNumber, nil, 0, 33, nil},
- {false, 1000, 1000, 10, 40, nil, 0, 0, errRequestBeyondHead},
- {true, 1000, 1000, 10, 40, nil, 0, 0, errRequestBeyondHead},
- {false, 20, 2, 100, rpc.LatestBlockNumber, nil, 13, 20, nil},
- {false, 20, 2, 100, rpc.LatestBlockNumber, []float64{0, 10}, 31, 2, nil},
- {false, 20, 2, 100, 32, []float64{0, 10}, 31, 2, nil},
- {false, 1000, 1000, 1, rpc.PendingBlockNumber, nil, 0, 0, nil},
- {false, 1000, 1000, 2, rpc.PendingBlockNumber, nil, 32, 1, nil},
- {true, 1000, 1000, 2, rpc.PendingBlockNumber, nil, 32, 2, nil},
- {true, 1000, 1000, 2, rpc.PendingBlockNumber, []float64{0, 10}, 32, 2, nil},
- {false, 1000, 1000, 2, rpc.FinalizedBlockNumber, []float64{0, 10}, 24, 2, nil},
- {false, 1000, 1000, 2, rpc.SafeBlockNumber, []float64{0, 10}, 24, 2, nil},
- }
- for i, c := range cases {
- config := Config{
- MaxHeaderHistory: c.maxHeader,
- MaxBlockHistory: c.maxBlock,
- }
- backend := newTestBackend(t, big.NewInt(16), c.pending)
- oracle := NewOracle(backend, config)
-
- first, reward, baseFee, ratio, err := oracle.FeeHistory(context.Background(), c.count, c.last, c.percent)
- backend.teardown()
- expReward := c.expCount
- if len(c.percent) == 0 {
- expReward = 0
- }
- expBaseFee := c.expCount
- if expBaseFee != 0 {
- expBaseFee++
- }
-
- if first.Uint64() != c.expFirst {
- t.Fatalf("Test case %d: first block mismatch, want %d, got %d", i, c.expFirst, first)
- }
- if len(reward) != expReward {
- t.Fatalf("Test case %d: reward array length mismatch, want %d, got %d", i, expReward, len(reward))
- }
- if len(baseFee) != expBaseFee {
- t.Fatalf("Test case %d: baseFee array length mismatch, want %d, got %d", i, expBaseFee, len(baseFee))
- }
- if len(ratio) != c.expCount {
- t.Fatalf("Test case %d: gasUsedRatio array length mismatch, want %d, got %d", i, c.expCount, len(ratio))
- }
- if err != c.expErr && !errors.Is(err, c.expErr) {
- t.Fatalf("Test case %d: error mismatch, want %v, got %v", i, c.expErr, err)
- }
- }
-}
diff --git a/eth/gasprice/gasprice.go b/eth/gasprice/gasprice.go
deleted file mode 100644
index b719649811..0000000000
--- a/eth/gasprice/gasprice.go
+++ /dev/null
@@ -1,276 +0,0 @@
-// Copyright 2015 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package gasprice
-
-import (
- "context"
- "math/big"
- "sync"
-
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/common/lru"
- "github.com/ethereum/go-ethereum/core"
- "github.com/ethereum/go-ethereum/core/types"
- "github.com/ethereum/go-ethereum/event"
- "github.com/ethereum/go-ethereum/log"
- "github.com/ethereum/go-ethereum/params"
- "github.com/ethereum/go-ethereum/rpc"
- "golang.org/x/exp/slices"
-)
-
-const sampleNumber = 3 // Number of transactions sampled in a block
-
-var (
- DefaultMaxPrice = big.NewInt(500 * params.GWei)
- DefaultIgnorePrice = big.NewInt(2 * params.Wei)
-)
-
-type Config struct {
- Blocks int
- Percentile int
- MaxHeaderHistory uint64
- MaxBlockHistory uint64
- Default *big.Int `toml:",omitempty"`
- MaxPrice *big.Int `toml:",omitempty"`
- IgnorePrice *big.Int `toml:",omitempty"`
-}
-
-// OracleBackend includes all necessary background APIs for oracle.
-type OracleBackend interface {
- HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error)
- BlockByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Block, error)
- GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error)
- PendingBlockAndReceipts() (*types.Block, types.Receipts)
- ChainConfig() *params.ChainConfig
- SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription
-}
-
-// Oracle recommends gas prices based on the content of recent
-// blocks. Suitable for both light and full clients.
-type Oracle struct {
- backend OracleBackend
- lastHead common.Hash
- lastPrice *big.Int
- maxPrice *big.Int
- ignorePrice *big.Int
- cacheLock sync.RWMutex
- fetchLock sync.Mutex
-
- checkBlocks, percentile int
- maxHeaderHistory, maxBlockHistory uint64
-
- historyCache *lru.Cache[cacheKey, processedFees]
-}
-
-// NewOracle returns a new gasprice oracle which can recommend suitable
-// gasprice for newly created transaction.
-func NewOracle(backend OracleBackend, params Config) *Oracle {
- blocks := params.Blocks
- if blocks < 1 {
- blocks = 1
- log.Warn("Sanitizing invalid gasprice oracle sample blocks", "provided", params.Blocks, "updated", blocks)
- }
- percent := params.Percentile
- if percent < 0 {
- percent = 0
- log.Warn("Sanitizing invalid gasprice oracle sample percentile", "provided", params.Percentile, "updated", percent)
- } else if percent > 100 {
- percent = 100
- log.Warn("Sanitizing invalid gasprice oracle sample percentile", "provided", params.Percentile, "updated", percent)
- }
- maxPrice := params.MaxPrice
- if maxPrice == nil || maxPrice.Int64() <= 0 {
- maxPrice = DefaultMaxPrice
- log.Warn("Sanitizing invalid gasprice oracle price cap", "provided", params.MaxPrice, "updated", maxPrice)
- }
- ignorePrice := params.IgnorePrice
- if ignorePrice == nil || ignorePrice.Int64() <= 0 {
- ignorePrice = DefaultIgnorePrice
- log.Warn("Sanitizing invalid gasprice oracle ignore price", "provided", params.IgnorePrice, "updated", ignorePrice)
- } else if ignorePrice.Int64() > 0 {
- log.Info("Gasprice oracle is ignoring threshold set", "threshold", ignorePrice)
- }
- maxHeaderHistory := params.MaxHeaderHistory
- if maxHeaderHistory < 1 {
- maxHeaderHistory = 1
- log.Warn("Sanitizing invalid gasprice oracle max header history", "provided", params.MaxHeaderHistory, "updated", maxHeaderHistory)
- }
- maxBlockHistory := params.MaxBlockHistory
- if maxBlockHistory < 1 {
- maxBlockHistory = 1
- log.Warn("Sanitizing invalid gasprice oracle max block history", "provided", params.MaxBlockHistory, "updated", maxBlockHistory)
- }
-
- cache := lru.NewCache[cacheKey, processedFees](2048)
- headEvent := make(chan core.ChainHeadEvent, 1)
- backend.SubscribeChainHeadEvent(headEvent)
- go func() {
- var lastHead common.Hash
- for ev := range headEvent {
- if ev.Block.ParentHash() != lastHead {
- cache.Purge()
- }
- lastHead = ev.Block.Hash()
- }
- }()
-
- return &Oracle{
- backend: backend,
- lastPrice: params.Default,
- maxPrice: maxPrice,
- ignorePrice: ignorePrice,
- checkBlocks: blocks,
- percentile: percent,
- maxHeaderHistory: maxHeaderHistory,
- maxBlockHistory: maxBlockHistory,
- historyCache: cache,
- }
-}
-
-// SuggestTipCap returns a tip cap so that newly created transaction can have a
-// very high chance to be included in the following blocks.
-//
-// Note, for legacy transactions and the legacy eth_gasPrice RPC call, it will be
-// necessary to add the basefee to the returned number to fall back to the legacy
-// behavior.
-func (oracle *Oracle) SuggestTipCap(ctx context.Context) (*big.Int, error) {
- head, _ := oracle.backend.HeaderByNumber(ctx, rpc.LatestBlockNumber)
- headHash := head.Hash()
-
- // If the latest gasprice is still available, return it.
- oracle.cacheLock.RLock()
- lastHead, lastPrice := oracle.lastHead, oracle.lastPrice
- oracle.cacheLock.RUnlock()
- if headHash == lastHead {
- return new(big.Int).Set(lastPrice), nil
- }
- oracle.fetchLock.Lock()
- defer oracle.fetchLock.Unlock()
-
- // Try checking the cache again, maybe the last fetch fetched what we need
- oracle.cacheLock.RLock()
- lastHead, lastPrice = oracle.lastHead, oracle.lastPrice
- oracle.cacheLock.RUnlock()
- if headHash == lastHead {
- return new(big.Int).Set(lastPrice), nil
- }
- var (
- sent, exp int
- number = head.Number.Uint64()
- result = make(chan results, oracle.checkBlocks)
- quit = make(chan struct{})
- results []*big.Int
- )
- for sent < oracle.checkBlocks && number > 0 {
- go oracle.getBlockValues(ctx, number, sampleNumber, oracle.ignorePrice, result, quit)
- sent++
- exp++
- number--
- }
- for exp > 0 {
- res := <-result
- if res.err != nil {
- close(quit)
- return new(big.Int).Set(lastPrice), res.err
- }
- exp--
- // Nothing returned. There are two special cases here:
- // - The block is empty
- // - All the transactions included are sent by the miner itself.
- // In these cases, use the latest calculated price for sampling.
- if len(res.values) == 0 {
- res.values = []*big.Int{lastPrice}
- }
- // Besides, in order to collect enough data for sampling, if nothing
- // meaningful returned, try to query more blocks. But the maximum
- // is 2*checkBlocks.
- if len(res.values) == 1 && len(results)+1+exp < oracle.checkBlocks*2 && number > 0 {
- go oracle.getBlockValues(ctx, number, sampleNumber, oracle.ignorePrice, result, quit)
- sent++
- exp++
- number--
- }
- results = append(results, res.values...)
- }
- price := lastPrice
- if len(results) > 0 {
- slices.SortFunc(results, func(a, b *big.Int) int { return a.Cmp(b) })
- price = results[(len(results)-1)*oracle.percentile/100]
- }
- if price.Cmp(oracle.maxPrice) > 0 {
- price = new(big.Int).Set(oracle.maxPrice)
- }
- oracle.cacheLock.Lock()
- oracle.lastHead = headHash
- oracle.lastPrice = price
- oracle.cacheLock.Unlock()
-
- return new(big.Int).Set(price), nil
-}
-
-type results struct {
- values []*big.Int
- err error
-}
-
-// getBlockValues calculates the lowest transaction gas price in a given block
-// and sends it to the result channel. If the block is empty or all transactions
-// are sent by the miner itself(it doesn't make any sense to include this kind of
-// transaction prices for sampling), nil gasprice is returned.
-func (oracle *Oracle) getBlockValues(ctx context.Context, blockNum uint64, limit int, ignoreUnder *big.Int, result chan results, quit chan struct{}) {
- block, err := oracle.backend.BlockByNumber(ctx, rpc.BlockNumber(blockNum))
- if block == nil {
- select {
- case result <- results{nil, err}:
- case <-quit:
- }
- return
- }
- signer := types.MakeSigner(oracle.backend.ChainConfig(), block.Number(), block.Time())
-
- // Sort the transaction by effective tip in ascending sort.
- txs := block.Transactions()
- sortedTxs := make([]*types.Transaction, len(txs))
- copy(sortedTxs, txs)
- baseFee := block.BaseFee()
- slices.SortFunc(sortedTxs, func(a, b *types.Transaction) int {
- // It's okay to discard the error because a tx would never be
- // accepted into a block with an invalid effective tip.
- tip1, _ := a.EffectiveGasTip(baseFee)
- tip2, _ := b.EffectiveGasTip(baseFee)
- return tip1.Cmp(tip2)
- })
-
- var prices []*big.Int
- for _, tx := range sortedTxs {
- tip, _ := tx.EffectiveGasTip(baseFee)
- if ignoreUnder != nil && tip.Cmp(ignoreUnder) == -1 {
- continue
- }
- sender, err := types.Sender(signer, tx)
- if err == nil && sender != block.Coinbase() {
- prices = append(prices, tip)
- if len(prices) >= limit {
- break
- }
- }
- }
- select {
- case result <- results{prices, nil}:
- case <-quit:
- }
-}
diff --git a/eth/gasprice/gasprice_test.go b/eth/gasprice/gasprice_test.go
deleted file mode 100644
index 4ee5a0d1b2..0000000000
--- a/eth/gasprice/gasprice_test.go
+++ /dev/null
@@ -1,215 +0,0 @@
-// Copyright 2020 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package gasprice
-
-import (
- "context"
- "math"
- "math/big"
- "testing"
-
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/consensus/ethash"
- "github.com/ethereum/go-ethereum/core"
- "github.com/ethereum/go-ethereum/core/rawdb"
- "github.com/ethereum/go-ethereum/core/types"
- "github.com/ethereum/go-ethereum/core/vm"
- "github.com/ethereum/go-ethereum/crypto"
- "github.com/ethereum/go-ethereum/event"
- "github.com/ethereum/go-ethereum/params"
- "github.com/ethereum/go-ethereum/rpc"
-)
-
-const testHead = 32
-
-type testBackend struct {
- chain *core.BlockChain
- pending bool // pending block available
-}
-
-func (b *testBackend) HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error) {
- if number > testHead {
- return nil, nil
- }
- if number == rpc.EarliestBlockNumber {
- number = 0
- }
- if number == rpc.FinalizedBlockNumber {
- return b.chain.CurrentFinalBlock(), nil
- }
- if number == rpc.SafeBlockNumber {
- return b.chain.CurrentSafeBlock(), nil
- }
- if number == rpc.LatestBlockNumber {
- number = testHead
- }
- if number == rpc.PendingBlockNumber {
- if b.pending {
- number = testHead + 1
- } else {
- return nil, nil
- }
- }
- return b.chain.GetHeaderByNumber(uint64(number)), nil
-}
-
-func (b *testBackend) BlockByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Block, error) {
- if number > testHead {
- return nil, nil
- }
- if number == rpc.EarliestBlockNumber {
- number = 0
- }
- if number == rpc.FinalizedBlockNumber {
- number = rpc.BlockNumber(b.chain.CurrentFinalBlock().Number.Uint64())
- }
- if number == rpc.SafeBlockNumber {
- number = rpc.BlockNumber(b.chain.CurrentSafeBlock().Number.Uint64())
- }
- if number == rpc.LatestBlockNumber {
- number = testHead
- }
- if number == rpc.PendingBlockNumber {
- if b.pending {
- number = testHead + 1
- } else {
- return nil, nil
- }
- }
- return b.chain.GetBlockByNumber(uint64(number)), nil
-}
-
-func (b *testBackend) GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error) {
- return b.chain.GetReceiptsByHash(hash), nil
-}
-
-func (b *testBackend) PendingBlockAndReceipts() (*types.Block, types.Receipts) {
- if b.pending {
- block := b.chain.GetBlockByNumber(testHead + 1)
- return block, b.chain.GetReceiptsByHash(block.Hash())
- }
- return nil, nil
-}
-
-func (b *testBackend) ChainConfig() *params.ChainConfig {
- return b.chain.Config()
-}
-
-func (b *testBackend) SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription {
- return nil
-}
-
-func (b *testBackend) teardown() {
- b.chain.Stop()
-}
-
-// newTestBackend creates a test backend. OBS: don't forget to invoke tearDown
-// after use, otherwise the blockchain instance will mem-leak via goroutines.
-func newTestBackend(t *testing.T, londonBlock *big.Int, pending bool) *testBackend {
- var (
- key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
- addr = crypto.PubkeyToAddress(key.PublicKey)
- config = *params.TestChainConfig // needs copy because it is modified below
- gspec = &core.Genesis{
- Config: &config,
- Alloc: core.GenesisAlloc{addr: {Balance: big.NewInt(math.MaxInt64)}},
- }
- signer = types.LatestSigner(gspec.Config)
- )
- config.LondonBlock = londonBlock
- config.ArrowGlacierBlock = londonBlock
- config.GrayGlacierBlock = londonBlock
- config.TerminalTotalDifficulty = common.Big0
- engine := ethash.NewFaker()
-
- // Generate testing blocks
- _, blocks, _ := core.GenerateChainWithGenesis(gspec, engine, testHead+1, func(i int, b *core.BlockGen) {
- b.SetCoinbase(common.Address{1})
-
- var txdata types.TxData
- if londonBlock != nil && b.Number().Cmp(londonBlock) >= 0 {
- txdata = &types.DynamicFeeTx{
- ChainID: gspec.Config.ChainID,
- Nonce: b.TxNonce(addr),
- To: &common.Address{},
- Gas: 30000,
- GasFeeCap: big.NewInt(100 * params.GWei),
- GasTipCap: big.NewInt(int64(i+1) * params.GWei),
- Data: []byte{},
- }
- } else {
- txdata = &types.LegacyTx{
- Nonce: b.TxNonce(addr),
- To: &common.Address{},
- Gas: 21000,
- GasPrice: big.NewInt(int64(i+1) * params.GWei),
- Value: big.NewInt(100),
- Data: []byte{},
- }
- }
- b.AddTx(types.MustSignNewTx(key, signer, txdata))
- })
- // Construct testing chain
- chain, err := core.NewBlockChain(rawdb.NewMemoryDatabase(), &core.CacheConfig{TrieCleanNoPrefetch: true}, gspec, nil, engine, vm.Config{}, nil, nil)
- if err != nil {
- t.Fatalf("Failed to create local chain, %v", err)
- }
- chain.InsertChain(blocks)
- chain.SetFinalized(chain.GetBlockByNumber(25).Header())
- chain.SetSafe(chain.GetBlockByNumber(25).Header())
- return &testBackend{chain: chain, pending: pending}
-}
-
-func (b *testBackend) CurrentHeader() *types.Header {
- return b.chain.CurrentHeader()
-}
-
-func (b *testBackend) GetBlockByNumber(number uint64) *types.Block {
- return b.chain.GetBlockByNumber(number)
-}
-
-func TestSuggestTipCap(t *testing.T) {
- config := Config{
- Blocks: 3,
- Percentile: 60,
- Default: big.NewInt(params.GWei),
- }
- var cases = []struct {
- fork *big.Int // London fork number
- expect *big.Int // Expected gasprice suggestion
- }{
- {nil, big.NewInt(params.GWei * int64(30))},
- {big.NewInt(0), big.NewInt(params.GWei * int64(30))}, // Fork point in genesis
- {big.NewInt(1), big.NewInt(params.GWei * int64(30))}, // Fork point in first block
- {big.NewInt(32), big.NewInt(params.GWei * int64(30))}, // Fork point in last block
- {big.NewInt(33), big.NewInt(params.GWei * int64(30))}, // Fork point in the future
- }
- for _, c := range cases {
- backend := newTestBackend(t, c.fork, false)
- oracle := NewOracle(backend, config)
-
- // The gas price sampled is: 32G, 31G, 30G, 29G, 28G, 27G
- got, err := oracle.SuggestTipCap(context.Background())
- backend.teardown()
- if err != nil {
- t.Fatalf("Failed to retrieve recommended gas price: %v", err)
- }
- if got.Cmp(c.expect) != 0 {
- t.Fatalf("Gas price mismatch, want %d, got %d", c.expect, got)
- }
- }
-}