mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-23 13:16:42 +00:00
feat: suggest gas price based on latest block's transactions (#1220)
* feat: suggest gas price based on latest block's transactions * remove debug logs * rename values * fix: typo * lint * fix: ci test * update comments * chore: auto version bump [bot] * fix: feehistory logic * Update eth/gasprice/feehistory.go Co-authored-by: Jonas Theis <4181434+jonastheis@users.noreply.github.com> * address comments * address comments * chore: remove used flag * fix: ci * fix: test * fix: lint * chore: auto version bump [bot] * test: add scroll gasprice unit test * address comments * address comments * add benchmark * perf: fee_history also use cache * perf: fee_history also use cache * fix: lastIsCongested lock --------- Co-authored-by: Jonas Theis <4181434+jonastheis@users.noreply.github.com>
This commit is contained in:
parent
daff298710
commit
f641f00a02
10 changed files with 583 additions and 139 deletions
|
|
@ -163,7 +163,6 @@ var (
|
||||||
utils.GpoPercentileFlag,
|
utils.GpoPercentileFlag,
|
||||||
utils.GpoMaxGasPriceFlag,
|
utils.GpoMaxGasPriceFlag,
|
||||||
utils.GpoIgnoreGasPriceFlag,
|
utils.GpoIgnoreGasPriceFlag,
|
||||||
utils.GpoCongestionThresholdFlag,
|
|
||||||
|
|
||||||
utils.MinerNotifyFullFlag,
|
utils.MinerNotifyFullFlag,
|
||||||
configFileFlag,
|
configFileFlag,
|
||||||
|
|
|
||||||
|
|
@ -208,7 +208,6 @@ var AppHelpFlagGroups = []flags.FlagGroup{
|
||||||
utils.GpoPercentileFlag,
|
utils.GpoPercentileFlag,
|
||||||
utils.GpoMaxGasPriceFlag,
|
utils.GpoMaxGasPriceFlag,
|
||||||
utils.GpoIgnoreGasPriceFlag,
|
utils.GpoIgnoreGasPriceFlag,
|
||||||
utils.GpoCongestionThresholdFlag,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -749,11 +749,6 @@ var (
|
||||||
Usage: "Gas price below which gpo will ignore transactions",
|
Usage: "Gas price below which gpo will ignore transactions",
|
||||||
Value: ethconfig.Defaults.GPO.IgnorePrice.Int64(),
|
Value: ethconfig.Defaults.GPO.IgnorePrice.Int64(),
|
||||||
}
|
}
|
||||||
GpoCongestionThresholdFlag = cli.IntFlag{
|
|
||||||
Name: "gpo.congestionthreshold",
|
|
||||||
Usage: "Number of pending transactions to consider the network congested and suggest a minimum tip cap",
|
|
||||||
Value: ethconfig.Defaults.GPO.CongestedThreshold,
|
|
||||||
}
|
|
||||||
GpoDefaultGasTipCapFlag = cli.Int64Flag{
|
GpoDefaultGasTipCapFlag = cli.Int64Flag{
|
||||||
Name: "gpo.defaultgastipcap",
|
Name: "gpo.defaultgastipcap",
|
||||||
Usage: "Default minimum gas tip cap (in wei) to be used after Curie fork (EIP-1559) (default: 100)",
|
Usage: "Default minimum gas tip cap (in wei) to be used after Curie fork (EIP-1559) (default: 100)",
|
||||||
|
|
@ -1552,9 +1547,6 @@ func setGPO(ctx *cli.Context, cfg *gasprice.Config, light bool) {
|
||||||
if ctx.GlobalIsSet(GpoIgnoreGasPriceFlag.Name) {
|
if ctx.GlobalIsSet(GpoIgnoreGasPriceFlag.Name) {
|
||||||
cfg.IgnorePrice = big.NewInt(ctx.GlobalInt64(GpoIgnoreGasPriceFlag.Name))
|
cfg.IgnorePrice = big.NewInt(ctx.GlobalInt64(GpoIgnoreGasPriceFlag.Name))
|
||||||
}
|
}
|
||||||
if ctx.GlobalIsSet(GpoCongestionThresholdFlag.Name) {
|
|
||||||
cfg.CongestedThreshold = ctx.GlobalInt(GpoCongestionThresholdFlag.Name)
|
|
||||||
}
|
|
||||||
if ctx.GlobalIsSet(GpoDefaultGasTipCapFlag.Name) {
|
if ctx.GlobalIsSet(GpoDefaultGasTipCapFlag.Name) {
|
||||||
cfg.DefaultGasTipCap = big.NewInt(ctx.GlobalInt64(GpoDefaultGasTipCapFlag.Name))
|
cfg.DefaultGasTipCap = big.NewInt(ctx.GlobalInt64(GpoDefaultGasTipCapFlag.Name))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -245,18 +245,14 @@ func (oracle *Oracle) FeeHistory(ctx context.Context, blocks int, unresolvedLast
|
||||||
}
|
}
|
||||||
oldestBlock := lastBlock + 1 - uint64(blocks)
|
oldestBlock := lastBlock + 1 - uint64(blocks)
|
||||||
|
|
||||||
// If pending txs are less than oracle.congestedThreshold, we consider the network to be non-congested and suggest
|
// If the suggestedGasPrice equals to oracle.defaultGasTipCap or oracle.defaultBasePrice (before Curie (EIP-1559)),
|
||||||
// a minimal tip cap. This is to prevent users from overpaying for gas when the network is not congested and a few
|
// it means the latest block is NOT out of capacity. We consider the network to be non-congested and suggest a minimal
|
||||||
// high-priced txs are causing the suggested tip cap to be high.
|
// tip cap. This is to prevent users from overpaying for gas when the network is not congested and a few high-priced
|
||||||
|
// transactions are causing the suggested tip cap to be high.
|
||||||
var nonCongestedPrice *big.Int
|
var nonCongestedPrice *big.Int
|
||||||
pendingTxCount, _ := oracle.backend.StatsWithMinBaseFee(headHeader.BaseFee)
|
suggestedGasPrice, isCongested := oracle.calculateSuggestPriorityFee(ctx, headHeader)
|
||||||
if pendingTxCount < oracle.congestedThreshold {
|
if !isCongested {
|
||||||
// Before Curie (EIP-1559), we need to return the total suggested gas price. After Curie we return defaultGasTipCap wei as the tip cap,
|
nonCongestedPrice = suggestedGasPrice
|
||||||
// as the base fee is set separately or added manually for legacy transactions.
|
|
||||||
nonCongestedPrice = oracle.defaultGasTipCap
|
|
||||||
if !oracle.backend.ChainConfig().IsCurie(headHeader.Number) {
|
|
||||||
nonCongestedPrice = oracle.defaultBasePrice
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
|
||||||
|
|
@ -49,16 +49,15 @@ var (
|
||||||
)
|
)
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Blocks int
|
Blocks int
|
||||||
Percentile int
|
Percentile int
|
||||||
MaxHeaderHistory int
|
MaxHeaderHistory int
|
||||||
MaxBlockHistory int
|
MaxBlockHistory int
|
||||||
Default *big.Int `toml:",omitempty"`
|
Default *big.Int `toml:",omitempty"`
|
||||||
MaxPrice *big.Int `toml:",omitempty"`
|
MaxPrice *big.Int `toml:",omitempty"`
|
||||||
IgnorePrice *big.Int `toml:",omitempty"`
|
IgnorePrice *big.Int `toml:",omitempty"`
|
||||||
CongestedThreshold int // Number of pending transactions to consider the network congested and suggest a minimum tip cap.
|
DefaultBasePrice *big.Int `toml:",omitempty"` // Base price to set when CongestedThreshold is reached before Curie (EIP 1559).
|
||||||
DefaultBasePrice *big.Int `toml:",omitempty"` // Base price to set when CongestedThreshold is reached before Curie (EIP 1559).
|
DefaultGasTipCap *big.Int `toml:",omitempty"` // Default minimum gas tip cap to use after Curie (EIP 1559).
|
||||||
DefaultGasTipCap *big.Int `toml:",omitempty"` // Default minimum gas tip cap to use after Curie (EIP 1559).
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// OracleBackend includes all necessary background APIs for oracle.
|
// OracleBackend includes all necessary background APIs for oracle.
|
||||||
|
|
@ -70,24 +69,22 @@ type OracleBackend interface {
|
||||||
ChainConfig() *params.ChainConfig
|
ChainConfig() *params.ChainConfig
|
||||||
SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription
|
SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription
|
||||||
StateAt(root common.Hash) (*state.StateDB, error)
|
StateAt(root common.Hash) (*state.StateDB, error)
|
||||||
Stats() (pending int, queued int)
|
|
||||||
StatsWithMinBaseFee(minBaseFee *big.Int) (pending int, queued int)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Oracle recommends gas prices based on the content of recent
|
// Oracle recommends gas prices based on the content of recent
|
||||||
// blocks. Suitable for both light and full clients.
|
// blocks. Suitable for both light and full clients.
|
||||||
type Oracle struct {
|
type Oracle struct {
|
||||||
backend OracleBackend
|
backend OracleBackend
|
||||||
lastHead common.Hash
|
lastHead common.Hash
|
||||||
lastPrice *big.Int
|
lastPrice *big.Int
|
||||||
maxPrice *big.Int
|
lastIsCongested bool
|
||||||
ignorePrice *big.Int
|
maxPrice *big.Int
|
||||||
cacheLock sync.RWMutex
|
ignorePrice *big.Int
|
||||||
fetchLock sync.Mutex
|
cacheLock sync.RWMutex
|
||||||
|
fetchLock sync.Mutex
|
||||||
|
|
||||||
checkBlocks, percentile int
|
checkBlocks, percentile int
|
||||||
maxHeaderHistory, maxBlockHistory int
|
maxHeaderHistory, maxBlockHistory int
|
||||||
congestedThreshold int // Number of pending transactions to consider the network congested and suggest a minimum tip cap.
|
|
||||||
defaultBasePrice *big.Int // Base price to set when CongestedThreshold is reached before Curie (EIP 1559).
|
defaultBasePrice *big.Int // Base price to set when CongestedThreshold is reached before Curie (EIP 1559).
|
||||||
defaultGasTipCap *big.Int // Default gas tip cap to suggest after Curie (EIP 1559) when the network is not congested.
|
defaultGasTipCap *big.Int // Default gas tip cap to suggest after Curie (EIP 1559) when the network is not congested.
|
||||||
historyCache *lru.Cache
|
historyCache *lru.Cache
|
||||||
|
|
@ -131,11 +128,6 @@ func NewOracle(backend OracleBackend, params Config) *Oracle {
|
||||||
maxBlockHistory = 1
|
maxBlockHistory = 1
|
||||||
log.Warn("Sanitizing invalid gasprice oracle max block history", "provided", params.MaxBlockHistory, "updated", maxBlockHistory)
|
log.Warn("Sanitizing invalid gasprice oracle max block history", "provided", params.MaxBlockHistory, "updated", maxBlockHistory)
|
||||||
}
|
}
|
||||||
congestedThreshold := params.CongestedThreshold
|
|
||||||
if congestedThreshold < 0 {
|
|
||||||
congestedThreshold = 0
|
|
||||||
log.Warn("Sanitizing invalid gasprice oracle congested threshold", "provided", params.CongestedThreshold, "updated", congestedThreshold)
|
|
||||||
}
|
|
||||||
defaultBasePrice := params.DefaultBasePrice
|
defaultBasePrice := params.DefaultBasePrice
|
||||||
if defaultBasePrice == nil || defaultBasePrice.Int64() < 0 {
|
if defaultBasePrice == nil || defaultBasePrice.Int64() < 0 {
|
||||||
defaultBasePrice = DefaultBasePrice
|
defaultBasePrice = DefaultBasePrice
|
||||||
|
|
@ -161,18 +153,17 @@ func NewOracle(backend OracleBackend, params Config) *Oracle {
|
||||||
}()
|
}()
|
||||||
|
|
||||||
return &Oracle{
|
return &Oracle{
|
||||||
backend: backend,
|
backend: backend,
|
||||||
lastPrice: params.Default,
|
lastPrice: params.Default,
|
||||||
maxPrice: maxPrice,
|
maxPrice: maxPrice,
|
||||||
ignorePrice: ignorePrice,
|
ignorePrice: ignorePrice,
|
||||||
checkBlocks: blocks,
|
checkBlocks: blocks,
|
||||||
percentile: percent,
|
percentile: percent,
|
||||||
maxHeaderHistory: maxHeaderHistory,
|
maxHeaderHistory: maxHeaderHistory,
|
||||||
maxBlockHistory: maxBlockHistory,
|
maxBlockHistory: maxBlockHistory,
|
||||||
congestedThreshold: congestedThreshold,
|
defaultBasePrice: defaultBasePrice,
|
||||||
defaultBasePrice: defaultBasePrice,
|
defaultGasTipCap: defaultGasTipCap,
|
||||||
defaultGasTipCap: defaultGasTipCap,
|
historyCache: cache,
|
||||||
historyCache: cache,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -186,6 +177,10 @@ func (oracle *Oracle) SuggestTipCap(ctx context.Context) (*big.Int, error) {
|
||||||
head, _ := oracle.backend.HeaderByNumber(ctx, rpc.LatestBlockNumber)
|
head, _ := oracle.backend.HeaderByNumber(ctx, rpc.LatestBlockNumber)
|
||||||
headHash := head.Hash()
|
headHash := head.Hash()
|
||||||
|
|
||||||
|
if oracle.backend.ChainConfig().IsScroll() {
|
||||||
|
return oracle.SuggestScrollPriorityFee(ctx, head), nil
|
||||||
|
}
|
||||||
|
|
||||||
// If the latest gasprice is still available, return it.
|
// If the latest gasprice is still available, return it.
|
||||||
oracle.cacheLock.RLock()
|
oracle.cacheLock.RLock()
|
||||||
lastHead, lastPrice := oracle.lastHead, oracle.lastPrice
|
lastHead, lastPrice := oracle.lastHead, oracle.lastPrice
|
||||||
|
|
@ -204,26 +199,6 @@ func (oracle *Oracle) SuggestTipCap(ctx context.Context) (*big.Int, error) {
|
||||||
return new(big.Int).Set(lastPrice), nil
|
return new(big.Int).Set(lastPrice), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// If pending txs are less than oracle.congestedThreshold, we consider the network to be non-congested and suggest
|
|
||||||
// a minimal tip cap. This is to prevent users from overpaying for gas when the network is not congested and a few
|
|
||||||
// high-priced txs are causing the suggested tip cap to be high.
|
|
||||||
pendingTxCount, _ := oracle.backend.StatsWithMinBaseFee(head.BaseFee)
|
|
||||||
if pendingTxCount < oracle.congestedThreshold {
|
|
||||||
// Before Curie (EIP-1559), we need to return the total suggested gas price. After Curie we return defaultGasTipCap wei as the tip cap,
|
|
||||||
// as the base fee is set separately or added manually for legacy transactions.
|
|
||||||
price := oracle.defaultGasTipCap
|
|
||||||
if !oracle.backend.ChainConfig().IsCurie(head.Number) {
|
|
||||||
price = oracle.defaultBasePrice
|
|
||||||
}
|
|
||||||
|
|
||||||
oracle.cacheLock.Lock()
|
|
||||||
oracle.lastHead = headHash
|
|
||||||
oracle.lastPrice = price
|
|
||||||
oracle.cacheLock.Unlock()
|
|
||||||
|
|
||||||
return new(big.Int).Set(price), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
sent, exp int
|
sent, exp int
|
||||||
number = head.Number.Uint64()
|
number = head.Number.Uint64()
|
||||||
|
|
|
||||||
|
|
@ -210,63 +210,3 @@ func TestSuggestTipCap(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSuggestTipCapCongestedThreshold(t *testing.T) {
|
|
||||||
expectedDefaultBasePricePreCurie := big.NewInt(2000)
|
|
||||||
expectedDefaultBasePricePostCurie := big.NewInt(100)
|
|
||||||
|
|
||||||
config := Config{
|
|
||||||
Blocks: 3,
|
|
||||||
Percentile: 60,
|
|
||||||
Default: big.NewInt(params.GWei),
|
|
||||||
CongestedThreshold: 50,
|
|
||||||
DefaultBasePrice: expectedDefaultBasePricePreCurie,
|
|
||||||
}
|
|
||||||
var cases = []struct {
|
|
||||||
fork *big.Int // London fork number
|
|
||||||
pendingTx int // Number of pending transactions in the mempool
|
|
||||||
expect *big.Int // Expected gasprice suggestion
|
|
||||||
}{
|
|
||||||
{nil, 0, expectedDefaultBasePricePreCurie}, // No congestion - default base price
|
|
||||||
{nil, 49, expectedDefaultBasePricePreCurie}, // No congestion - default base price
|
|
||||||
{nil, 50, big.NewInt(params.GWei * int64(30))}, // Congestion - normal behavior
|
|
||||||
{nil, 100, big.NewInt(params.GWei * int64(30))}, // Congestion - normal behavior
|
|
||||||
|
|
||||||
// Fork point in genesis
|
|
||||||
{big.NewInt(0), 0, expectedDefaultBasePricePostCurie}, // No congestion - default base price
|
|
||||||
{big.NewInt(0), 49, expectedDefaultBasePricePostCurie}, // No congestion - default base price
|
|
||||||
{big.NewInt(0), 50, big.NewInt(params.GWei * int64(30))}, // Congestion - normal behavior
|
|
||||||
{big.NewInt(0), 100, big.NewInt(params.GWei * int64(30))}, // Congestion - normal behavior
|
|
||||||
|
|
||||||
// Fork point in first block
|
|
||||||
{big.NewInt(1), 0, expectedDefaultBasePricePostCurie}, // No congestion - default base price
|
|
||||||
{big.NewInt(1), 49, expectedDefaultBasePricePostCurie}, // No congestion - default base price
|
|
||||||
{big.NewInt(1), 50, big.NewInt(params.GWei * int64(30))}, // Congestion - normal behavior
|
|
||||||
{big.NewInt(1), 100, big.NewInt(params.GWei * int64(30))}, // Congestion - normal behavior
|
|
||||||
|
|
||||||
// Fork point in last block
|
|
||||||
{big.NewInt(32), 0, expectedDefaultBasePricePostCurie}, // No congestion - default base price
|
|
||||||
{big.NewInt(32), 49, expectedDefaultBasePricePostCurie}, // No congestion - default base price
|
|
||||||
{big.NewInt(32), 50, big.NewInt(params.GWei * int64(30))}, // Congestion - normal behavior
|
|
||||||
{big.NewInt(32), 100, big.NewInt(params.GWei * int64(30))}, // Congestion - normal behavior
|
|
||||||
|
|
||||||
// Fork point in the future
|
|
||||||
{big.NewInt(33), 0, expectedDefaultBasePricePreCurie}, // No congestion - default base price
|
|
||||||
{big.NewInt(33), 49, expectedDefaultBasePricePreCurie}, // No congestion - default base price
|
|
||||||
{big.NewInt(33), 50, big.NewInt(params.GWei * int64(30))}, // Congestion - normal behavior
|
|
||||||
{big.NewInt(33), 100, big.NewInt(params.GWei * int64(30))}, // Congestion - normal behavior
|
|
||||||
}
|
|
||||||
for _, c := range cases {
|
|
||||||
backend := newTestBackend(t, c.fork, false, c.pendingTx)
|
|
||||||
oracle := NewOracle(backend, config)
|
|
||||||
|
|
||||||
// The gas price sampled is: 32G, 31G, 30G, 29G, 28G, 27G
|
|
||||||
got, err := oracle.SuggestTipCap(context.Background())
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
176
eth/gasprice/scroll_gasprice.go
Normal file
176
eth/gasprice/scroll_gasprice.go
Normal file
|
|
@ -0,0 +1,176 @@
|
||||||
|
package gasprice
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"math/big"
|
||||||
|
"sort"
|
||||||
|
|
||||||
|
"github.com/scroll-tech/go-ethereum/common"
|
||||||
|
"github.com/scroll-tech/go-ethereum/core/types"
|
||||||
|
"github.com/scroll-tech/go-ethereum/log"
|
||||||
|
"github.com/scroll-tech/go-ethereum/rpc"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (oracle *Oracle) calculateSuggestPriorityFee(ctx context.Context, header *types.Header) (*big.Int, bool) {
|
||||||
|
headHash := header.Hash()
|
||||||
|
// If the latest gasprice is still available, return it.
|
||||||
|
oracle.cacheLock.RLock()
|
||||||
|
lastHead, lastPrice, lastIsCongested := oracle.lastHead, oracle.lastPrice, oracle.lastIsCongested
|
||||||
|
oracle.cacheLock.RUnlock()
|
||||||
|
if headHash == lastHead {
|
||||||
|
return new(big.Int).Set(lastPrice), lastIsCongested
|
||||||
|
}
|
||||||
|
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, lastIsCongested = oracle.lastHead, oracle.lastPrice, oracle.lastIsCongested
|
||||||
|
oracle.cacheLock.RUnlock()
|
||||||
|
if headHash == lastHead {
|
||||||
|
return new(big.Int).Set(lastPrice), lastIsCongested
|
||||||
|
}
|
||||||
|
|
||||||
|
var isCongested bool
|
||||||
|
// Before Curie (EIP-1559), we need to return the total suggested gas price. After Curie we return defaultGasTipCap wei as the tip cap,
|
||||||
|
// as the base fee is set separately or added manually for legacy transactions.
|
||||||
|
suggestion := oracle.defaultGasTipCap
|
||||||
|
if !oracle.backend.ChainConfig().IsCurie(header.Number) {
|
||||||
|
suggestion = oracle.defaultBasePrice
|
||||||
|
}
|
||||||
|
|
||||||
|
// find the maximum gas used by any of the transactions in the block to use as the gas limit
|
||||||
|
// capacity margin
|
||||||
|
receipts, err := oracle.backend.GetReceipts(ctx, header.Hash())
|
||||||
|
if receipts == nil || err != nil {
|
||||||
|
log.Error("failed to get block receipts", "block number", header.Number, "err", err)
|
||||||
|
return suggestion, isCongested
|
||||||
|
}
|
||||||
|
var maxTxGasUsed uint64
|
||||||
|
|
||||||
|
for i := range receipts {
|
||||||
|
gu := receipts[i].GasUsed
|
||||||
|
if gu > maxTxGasUsed {
|
||||||
|
maxTxGasUsed = gu
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// find the maximum transaction size by any of the transactions in the block to use as the block
|
||||||
|
// size limit capacity margin
|
||||||
|
var (
|
||||||
|
maxTxSizeUsed common.StorageSize
|
||||||
|
totalTxSizeUsed common.StorageSize
|
||||||
|
)
|
||||||
|
block, err := oracle.backend.BlockByNumber(ctx, rpc.BlockNumber(header.Number.Int64()))
|
||||||
|
if block == nil || err != nil {
|
||||||
|
log.Error("failed to get last block", "err", err)
|
||||||
|
return suggestion, isCongested
|
||||||
|
}
|
||||||
|
txs := block.Transactions()
|
||||||
|
|
||||||
|
for i := range txs {
|
||||||
|
su := txs[i].Size()
|
||||||
|
if su > maxTxSizeUsed {
|
||||||
|
maxTxSizeUsed = su
|
||||||
|
}
|
||||||
|
totalTxSizeUsed = totalTxSizeUsed + su
|
||||||
|
}
|
||||||
|
|
||||||
|
// sanity check the max gas used and transaction size value
|
||||||
|
if maxTxGasUsed > header.GasLimit {
|
||||||
|
log.Error("found tx consuming more gas than the block limit", "gas", maxTxGasUsed)
|
||||||
|
return suggestion, isCongested
|
||||||
|
}
|
||||||
|
if !oracle.backend.ChainConfig().Scroll.IsValidBlockSize(maxTxSizeUsed) {
|
||||||
|
log.Error("found tx consuming more size than the block size limit", "size", maxTxSizeUsed)
|
||||||
|
return suggestion, isCongested
|
||||||
|
}
|
||||||
|
|
||||||
|
if header.GasUsed+maxTxGasUsed > header.GasLimit ||
|
||||||
|
!oracle.backend.ChainConfig().Scroll.IsValidBlockSizeForMining(totalTxSizeUsed+maxTxSizeUsed) {
|
||||||
|
// There are two cases that represent a block is "at capacity":
|
||||||
|
// 1. When building the block, there is a pending transaction in the txpool that could not be
|
||||||
|
// included because adding it would exceed the block's gas limit.
|
||||||
|
// 2. Or, there is a pending transaction that could not be included because adding it would
|
||||||
|
// exceed the block's transaction payload size (block size limit).
|
||||||
|
//
|
||||||
|
// Since we don't have access to the txpool, we instead adopt the following heuristic:
|
||||||
|
// consider a block as at capacity if either:
|
||||||
|
// - the total gas consumed by its transactions is within max-tx-gas-used of the block gas
|
||||||
|
// limit, where max-tx-gas-used is the most gas used by any one transaction within the block, or
|
||||||
|
// - the total transaction payload size is within max-tx-size-used of the block size limit,
|
||||||
|
// where max-tx-size-used is the largest transaction size in the block.
|
||||||
|
//
|
||||||
|
// This heuristic is almost perfectly accurate when transactions always consume the same amount
|
||||||
|
// of gas and have similar sizes, but becomes less accurate as gas usage or payload size varies
|
||||||
|
// between transactions. The typical error is that we assume a block is at capacity when it was
|
||||||
|
// not, because max-tx-gas-used or max-tx-size-used will in most cases over-estimate the
|
||||||
|
// "capacity margin". But it's better to err on the side of returning a higher-than-needed
|
||||||
|
// suggestion than a lower-than-needed one, in order to satisfy our desire for high chance of
|
||||||
|
// inclusion and rising fees under high demand.
|
||||||
|
baseFee := block.BaseFee()
|
||||||
|
if len(txs) == 0 {
|
||||||
|
log.Error("block was at capacity but doesn't have transactions")
|
||||||
|
return suggestion, isCongested
|
||||||
|
}
|
||||||
|
tips := bigIntArray(make([]*big.Int, len(txs)))
|
||||||
|
for i := range txs {
|
||||||
|
tips[i] = txs[i].EffectiveGasTipValue(baseFee)
|
||||||
|
}
|
||||||
|
sort.Sort(tips)
|
||||||
|
median := tips[len(tips)/2]
|
||||||
|
newSuggestion := new(big.Int).Add(median, new(big.Int).Div(median, big.NewInt(10)))
|
||||||
|
isCongested = true
|
||||||
|
// use the new suggestion only if it's bigger than the minimum
|
||||||
|
if newSuggestion.Cmp(suggestion) > 0 {
|
||||||
|
suggestion = newSuggestion
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// the suggestion should be capped by oracle.maxPrice
|
||||||
|
if suggestion.Cmp(oracle.maxPrice) > 0 {
|
||||||
|
suggestion.Set(oracle.maxPrice)
|
||||||
|
}
|
||||||
|
|
||||||
|
// update the cache only if it's latest block header
|
||||||
|
latestHeader, _ := oracle.backend.HeaderByNumber(ctx, rpc.LatestBlockNumber)
|
||||||
|
if header.Hash() == latestHeader.Hash() {
|
||||||
|
oracle.cacheLock.Lock()
|
||||||
|
oracle.lastHead = header.Hash()
|
||||||
|
oracle.lastPrice = suggestion
|
||||||
|
oracle.lastIsCongested = isCongested
|
||||||
|
oracle.cacheLock.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
return suggestion, isCongested
|
||||||
|
}
|
||||||
|
|
||||||
|
// SuggestScrollPriorityFee returns a max priority fee value that can be used such that newly
|
||||||
|
// created transactions have a very high chance to be included in the following blocks, using a
|
||||||
|
// simplified and more predictable algorithm appropriate for chains like Scroll with a single
|
||||||
|
// known block builder.
|
||||||
|
//
|
||||||
|
// In the typical case, which results whenever the last block had room for more transactions, this
|
||||||
|
// function returns a minimum suggested priority fee value. Otherwise it returns the higher of this
|
||||||
|
// minimum suggestion or 10% over the median effective priority fee from the last block.
|
||||||
|
//
|
||||||
|
// Rationale: For a chain such as Scroll where there is a single block builder whose behavior is
|
||||||
|
// known, we know priority fee (as long as it is non-zero) has no impact on the probability for tx
|
||||||
|
// inclusion as long as there is capacity for it in the block. In this case then, there's no reason
|
||||||
|
// to return any value higher than some fixed minimum. Blocks typically reach capacity only under
|
||||||
|
// extreme events such as airdrops, meaning predicting whether the next block is going to be at
|
||||||
|
// capacity is difficult *except* in the case where we're already experiencing the increased demand
|
||||||
|
// from such an event. We therefore expect whether the last known block is at capacity to be one of
|
||||||
|
// the best predictors of whether the next block is likely to be at capacity. (An even better
|
||||||
|
// predictor is to look at the state of the transaction pool, but we want an algorithm that works
|
||||||
|
// even if the txpool is private or unavailable.)
|
||||||
|
//
|
||||||
|
// In the event the next block may be at capacity, the algorithm should allow for average fees to
|
||||||
|
// rise in order to reach a market price that appropriately reflects demand. We accomplish this by
|
||||||
|
// returning a suggestion that is a significant amount (10%) higher than the median effective
|
||||||
|
// priority fee from the previous block.
|
||||||
|
func (oracle *Oracle) SuggestScrollPriorityFee(ctx context.Context, header *types.Header) *big.Int {
|
||||||
|
suggestion, _ := oracle.calculateSuggestPriorityFee(ctx, header)
|
||||||
|
|
||||||
|
return new(big.Int).Set(suggestion)
|
||||||
|
}
|
||||||
362
eth/gasprice/scroll_gasprice_test.go
Normal file
362
eth/gasprice/scroll_gasprice_test.go
Normal file
|
|
@ -0,0 +1,362 @@
|
||||||
|
// 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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package gasprice
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"math/big"
|
||||||
|
"math/rand"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/scroll-tech/go-ethereum/common"
|
||||||
|
"github.com/scroll-tech/go-ethereum/core"
|
||||||
|
"github.com/scroll-tech/go-ethereum/core/state"
|
||||||
|
"github.com/scroll-tech/go-ethereum/core/types"
|
||||||
|
"github.com/scroll-tech/go-ethereum/crypto"
|
||||||
|
"github.com/scroll-tech/go-ethereum/event"
|
||||||
|
"github.com/scroll-tech/go-ethereum/params"
|
||||||
|
"github.com/scroll-tech/go-ethereum/rpc"
|
||||||
|
"github.com/scroll-tech/go-ethereum/trie"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
blockGasLimit = params.TxGas * 3
|
||||||
|
maxTxPayloadBytesPerBlock = 790
|
||||||
|
)
|
||||||
|
|
||||||
|
type testTxData struct {
|
||||||
|
priorityFee int64
|
||||||
|
gasLimit uint64
|
||||||
|
payloadSize uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
type scrollTestBackend struct {
|
||||||
|
block *types.Block
|
||||||
|
receipts []*types.Receipt
|
||||||
|
curieBlock *big.Int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *scrollTestBackend) HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error) {
|
||||||
|
if number == rpc.LatestBlockNumber || number == rpc.BlockNumber(b.block.NumberU64()) {
|
||||||
|
return b.block.Header(), nil
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *scrollTestBackend) BlockByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Block, error) {
|
||||||
|
return b.block, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *scrollTestBackend) GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error) {
|
||||||
|
return b.receipts, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *scrollTestBackend) Pending() (*types.Block, types.Receipts, *state.StateDB) {
|
||||||
|
panic("not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *scrollTestBackend) ChainConfig() *params.ChainConfig {
|
||||||
|
config := params.TestChainConfig
|
||||||
|
config.Scroll.MaxTxPayloadBytesPerBlock = &maxTxPayloadBytesPerBlock
|
||||||
|
config.CurieBlock = b.curieBlock
|
||||||
|
return config
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *scrollTestBackend) SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *scrollTestBackend) PendingBlockAndReceipts() (*types.Block, types.Receipts) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *scrollTestBackend) StateAt(root common.Hash) (*state.StateDB, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ OracleBackend = (*scrollTestBackend)(nil)
|
||||||
|
|
||||||
|
func GenerateRandomBytes(length int) []byte {
|
||||||
|
b := make([]byte, length)
|
||||||
|
for i := range b {
|
||||||
|
b[i] = byte(rand.Intn(256))
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
func newScrollTestBackend(_ *testing.T, txs []testTxData, curieBlock *big.Int) *scrollTestBackend {
|
||||||
|
var (
|
||||||
|
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||||
|
signer = types.LatestSigner(params.TestChainConfig)
|
||||||
|
)
|
||||||
|
// only the most recent block is considered for optimism priority fee suggestions, so this is
|
||||||
|
// where we add the test transactions
|
||||||
|
ts := []*types.Transaction{}
|
||||||
|
rs := []*types.Receipt{}
|
||||||
|
header := types.Header{}
|
||||||
|
header.GasLimit = blockGasLimit
|
||||||
|
var nonce uint64
|
||||||
|
for _, tx := range txs {
|
||||||
|
txdata := &types.DynamicFeeTx{
|
||||||
|
ChainID: params.TestChainConfig.ChainID,
|
||||||
|
Nonce: nonce,
|
||||||
|
To: &common.Address{},
|
||||||
|
Gas: params.TxGas,
|
||||||
|
GasFeeCap: big.NewInt(100 * params.GWei),
|
||||||
|
GasTipCap: big.NewInt(tx.priorityFee),
|
||||||
|
Data: GenerateRandomBytes(int(tx.payloadSize)),
|
||||||
|
}
|
||||||
|
t := types.MustSignNewTx(key, signer, txdata)
|
||||||
|
ts = append(ts, t)
|
||||||
|
r := types.Receipt{}
|
||||||
|
r.GasUsed = tx.gasLimit
|
||||||
|
header.GasUsed += r.GasUsed
|
||||||
|
rs = append(rs, &r)
|
||||||
|
nonce++
|
||||||
|
}
|
||||||
|
hasher := trie.NewStackTrie(nil)
|
||||||
|
b := types.NewBlock(&header, ts, nil, nil, hasher)
|
||||||
|
return &scrollTestBackend{block: b, receipts: rs, curieBlock: curieBlock}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSuggestScrollPriorityFee(t *testing.T) {
|
||||||
|
expectedDefaultBasePricePreCurie := big.NewInt(20000)
|
||||||
|
expectedDefaultBasePricePostCurie := big.NewInt(100)
|
||||||
|
cases := []struct {
|
||||||
|
curieBlock *big.Int
|
||||||
|
txdata []testTxData
|
||||||
|
want *big.Int
|
||||||
|
}{
|
||||||
|
|
||||||
|
// Pre-Curie block gas limit test cases
|
||||||
|
{
|
||||||
|
// block gas limit well under capacity, expect min priority fee suggestion
|
||||||
|
curieBlock: big.NewInt(1),
|
||||||
|
txdata: []testTxData{{params.GWei, 21000, 0}},
|
||||||
|
want: expectedDefaultBasePricePreCurie,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// 2 txs, gas limit still under capacity, expect min priority fee suggestion
|
||||||
|
curieBlock: big.NewInt(1),
|
||||||
|
txdata: []testTxData{{params.GWei, 21000, 0}, {params.GWei, 21000, 0}},
|
||||||
|
want: expectedDefaultBasePricePreCurie,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// 2 txs w same priority fee (1 gwei), but second tx puts it right over gas limit capacity
|
||||||
|
curieBlock: big.NewInt(1),
|
||||||
|
txdata: []testTxData{{params.GWei, 21000, 0}, {params.GWei, 21001, 0}},
|
||||||
|
want: big.NewInt(1100000000), // 10 percent over 1 gwei, the median
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// 3 txs, full block. return 10% over the median tx (10 gwei * 10% == 11 gwei)
|
||||||
|
curieBlock: big.NewInt(1),
|
||||||
|
txdata: []testTxData{{10 * params.GWei, 21000, 0}, {1 * params.GWei, 21000, 0}, {100 * params.GWei, 21000, 0}},
|
||||||
|
want: big.NewInt(11 * params.GWei),
|
||||||
|
},
|
||||||
|
|
||||||
|
// Pre-Curie block payload size test cases
|
||||||
|
{
|
||||||
|
// block block payload well under capacity, expect min priority fee suggestion
|
||||||
|
curieBlock: big.NewInt(1),
|
||||||
|
txdata: []testTxData{{params.GWei, 0, 139}},
|
||||||
|
want: expectedDefaultBasePricePreCurie,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// 2 txs, still under block payload capacity, expect min priority fee suggestion
|
||||||
|
curieBlock: big.NewInt(1),
|
||||||
|
txdata: []testTxData{{params.GWei, 0, 139}, {params.GWei, 0, 139}},
|
||||||
|
want: expectedDefaultBasePricePreCurie,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// 2 txs w same priority fee (1 gwei), but second tx puts it right over block payload capacity
|
||||||
|
curieBlock: big.NewInt(1),
|
||||||
|
txdata: []testTxData{{params.GWei, 0, 139}, {params.GWei, 0, 140}},
|
||||||
|
want: big.NewInt(1100000000), // 10 percent over 1 gwei, the median
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// 3 txs, full block. return 10% over the median tx (10 gwei * 10% == 11 gwei)
|
||||||
|
curieBlock: big.NewInt(1),
|
||||||
|
txdata: []testTxData{{20 * params.GWei, 0, 139}, {1 * params.GWei, 0, 140}, {100 * params.GWei, 0, 140}},
|
||||||
|
want: big.NewInt(22 * params.GWei),
|
||||||
|
},
|
||||||
|
|
||||||
|
// Post Curie block gas limit test cases
|
||||||
|
{
|
||||||
|
// block gas limit well under capacity, expect min priority fee suggestion
|
||||||
|
curieBlock: big.NewInt(0),
|
||||||
|
txdata: []testTxData{{params.GWei, 21000, 0}},
|
||||||
|
want: expectedDefaultBasePricePostCurie,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// 2 txs, gas limit still under capacity, expect min priority fee suggestion
|
||||||
|
curieBlock: big.NewInt(0),
|
||||||
|
txdata: []testTxData{{params.GWei, 21000, 0}, {params.GWei, 21000, 0}},
|
||||||
|
want: expectedDefaultBasePricePostCurie,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// 2 txs w same priority fee (1 gwei), but second tx puts it right over gas limit capacity
|
||||||
|
curieBlock: big.NewInt(0),
|
||||||
|
txdata: []testTxData{{params.GWei, 21000, 0}, {params.GWei, 21001, 0}},
|
||||||
|
want: big.NewInt(1100000000), // 10 percent over 1 gwei, the median
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// 3 txs, full block. return 10% over the median tx (10 gwei * 10% == 11 gwei)
|
||||||
|
curieBlock: big.NewInt(0),
|
||||||
|
txdata: []testTxData{{10 * params.GWei, 21000, 0}, {1 * params.GWei, 21000, 0}, {100 * params.GWei, 21000, 0}},
|
||||||
|
want: big.NewInt(11 * params.GWei),
|
||||||
|
},
|
||||||
|
|
||||||
|
// Post Curie block payload size test cases
|
||||||
|
{
|
||||||
|
// block block payload well under capacity, expect min priority fee suggestion
|
||||||
|
curieBlock: big.NewInt(0),
|
||||||
|
txdata: []testTxData{{params.GWei, 0, 139}},
|
||||||
|
want: expectedDefaultBasePricePostCurie,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// 2 txs, still under block payload capacity, expect min priority fee suggestion
|
||||||
|
curieBlock: big.NewInt(0),
|
||||||
|
txdata: []testTxData{{params.GWei, 0, 139}, {params.GWei, 0, 139}},
|
||||||
|
want: expectedDefaultBasePricePostCurie,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// 2 txs w same priority fee (1 gwei), but second tx puts it right over block payload capacity
|
||||||
|
curieBlock: big.NewInt(0),
|
||||||
|
txdata: []testTxData{{params.GWei, 0, 139}, {params.GWei, 0, 140}},
|
||||||
|
want: big.NewInt(1100000000), // 10 percent over 1 gwei, the median
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// 3 txs, full block. return 10% over the median tx (10 gwei * 10% == 11 gwei)
|
||||||
|
curieBlock: big.NewInt(0),
|
||||||
|
txdata: []testTxData{{20 * params.GWei, 0, 139}, {1 * params.GWei, 0, 140}, {100 * params.GWei, 0, 140}},
|
||||||
|
want: big.NewInt(22 * params.GWei),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for i, c := range cases {
|
||||||
|
backend := newScrollTestBackend(t, c.txdata, c.curieBlock)
|
||||||
|
oracle := NewOracle(backend, Config{DefaultBasePrice: expectedDefaultBasePricePreCurie, DefaultGasTipCap: expectedDefaultBasePricePostCurie})
|
||||||
|
got := oracle.SuggestScrollPriorityFee(context.Background(), backend.block.Header())
|
||||||
|
if got.Cmp(c.want) != 0 {
|
||||||
|
t.Errorf("Gas price mismatch for test case %d: want %d, got %d", i, c.want, got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Benchmark API QPS for gas price oracle with different transaction patterns
|
||||||
|
func BenchmarkScrollGasPriceAPIQPS(b *testing.B) {
|
||||||
|
// Create diverse transaction patterns to fill blocks
|
||||||
|
createTransactionSet := func(blockIndex int, txCount int) []testTxData {
|
||||||
|
txs := make([]testTxData, txCount)
|
||||||
|
for i := 0; i < txCount; i++ {
|
||||||
|
// Create varied transactions to avoid cache hits on tx.Size()
|
||||||
|
txs[i] = testTxData{
|
||||||
|
priorityFee: int64((blockIndex*txCount + i + 1) * params.GWei), // Unique priority fees
|
||||||
|
gasLimit: uint64(21000 + (i%10)*1000), // Varied gas limits
|
||||||
|
payloadSize: uint64(i%10) * 256, // Varied payload sizes: 0, 256, 512, 768, 1024, 1280, 1536, 1792, 2048, 2304 bytes
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return txs
|
||||||
|
}
|
||||||
|
|
||||||
|
testScenarios := []struct {
|
||||||
|
name string
|
||||||
|
curieBlock *big.Int
|
||||||
|
blocksCount int
|
||||||
|
txsPerBlock int
|
||||||
|
description string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "LightLoad_PreCurie",
|
||||||
|
curieBlock: big.NewInt(100), // After current block
|
||||||
|
blocksCount: 1,
|
||||||
|
txsPerBlock: 5,
|
||||||
|
description: "Light load with 5 txs, pre-Curie",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "LightLoad_PostCurie",
|
||||||
|
curieBlock: big.NewInt(0), // Curie from genesis
|
||||||
|
blocksCount: 1,
|
||||||
|
txsPerBlock: 5,
|
||||||
|
description: "Light load with 5 txs, post-Curie",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "MediumLoad_PreCurie",
|
||||||
|
curieBlock: big.NewInt(100),
|
||||||
|
blocksCount: 3,
|
||||||
|
txsPerBlock: 20,
|
||||||
|
description: "Medium load with 20 txs per block, 3 blocks",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "MediumLoad_PostCurie",
|
||||||
|
curieBlock: big.NewInt(0),
|
||||||
|
blocksCount: 3,
|
||||||
|
txsPerBlock: 20,
|
||||||
|
description: "Medium load with 20 txs per block, 3 blocks",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "HighLoad_PostCurie",
|
||||||
|
curieBlock: big.NewInt(0),
|
||||||
|
blocksCount: 1,
|
||||||
|
txsPerBlock: 100,
|
||||||
|
description: "High load with 100 txs, testing congestion scenarios",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, scenario := range testScenarios {
|
||||||
|
b.Run(scenario.name, func(b *testing.B) {
|
||||||
|
// Create backend with diverse transactions
|
||||||
|
txs := createTransactionSet(0, scenario.txsPerBlock)
|
||||||
|
backend := newScrollTestBackend(nil, txs, scenario.curieBlock)
|
||||||
|
|
||||||
|
config := Config{
|
||||||
|
DefaultBasePrice: big.NewInt(20000),
|
||||||
|
DefaultGasTipCap: big.NewInt(100),
|
||||||
|
MaxPrice: big.NewInt(500 * params.GWei),
|
||||||
|
}
|
||||||
|
oracle := NewOracle(backend, config)
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
// Sub-benchmarks for different API methods
|
||||||
|
b.Run("SuggestScrollPriorityFee", func(b *testing.B) {
|
||||||
|
b.ResetTimer()
|
||||||
|
b.RunParallel(func(pb *testing.PB) {
|
||||||
|
for pb.Next() {
|
||||||
|
_ = oracle.SuggestScrollPriorityFee(ctx, backend.block.Header())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
b.Run("SuggestTipCap", func(b *testing.B) {
|
||||||
|
b.ResetTimer()
|
||||||
|
b.RunParallel(func(pb *testing.PB) {
|
||||||
|
for pb.Next() {
|
||||||
|
_, _ = oracle.SuggestTipCap(ctx)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
b.Run("calculateSuggestPriorityFee", func(b *testing.B) {
|
||||||
|
b.ResetTimer()
|
||||||
|
b.RunParallel(func(pb *testing.PB) {
|
||||||
|
for pb.Next() {
|
||||||
|
_, _ = oracle.calculateSuggestPriorityFee(ctx, backend.block.Header())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1019,6 +1019,11 @@ func (c *ChainConfig) IsFeynmanTransitionBlock(blockTimestamp uint64, parentTime
|
||||||
return isForkedTime(blockTimestamp, c.FeynmanTime) && !isForkedTime(parentTimestamp, c.FeynmanTime)
|
return isForkedTime(blockTimestamp, c.FeynmanTime) && !isForkedTime(parentTimestamp, c.FeynmanTime)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsScroll returns whether the node is an scroll node or not.
|
||||||
|
func (c *ChainConfig) IsScroll() bool {
|
||||||
|
return c.Scroll.L1Config != nil
|
||||||
|
}
|
||||||
|
|
||||||
// IsTerminalPoWBlock returns whether the given block is the last block of PoW stage.
|
// IsTerminalPoWBlock returns whether the given block is the last block of PoW stage.
|
||||||
func (c *ChainConfig) IsTerminalPoWBlock(parentTotalDiff *big.Int, totalDiff *big.Int) bool {
|
func (c *ChainConfig) IsTerminalPoWBlock(parentTotalDiff *big.Int, totalDiff *big.Int) bool {
|
||||||
if c.TerminalTotalDifficulty == nil {
|
if c.TerminalTotalDifficulty == nil {
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ import (
|
||||||
const (
|
const (
|
||||||
VersionMajor = 5 // Major version component of the current release
|
VersionMajor = 5 // Major version component of the current release
|
||||||
VersionMinor = 8 // Minor version component of the current release
|
VersionMinor = 8 // Minor version component of the current release
|
||||||
VersionPatch = 68 // Patch version component of the current release
|
VersionPatch = 69 // Patch version component of the current release
|
||||||
VersionMeta = "mainnet" // Version metadata to append to the version string
|
VersionMeta = "mainnet" // Version metadata to append to the version string
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue