Skip to content
Open
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
2 changes: 1 addition & 1 deletion eth/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -406,7 +406,7 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
if eth.APIBackend.allowUnprotectedTxs {
log.Info("Unprotected transactions allowed")
}
eth.APIBackend.gpo = gasprice.NewOracle(eth.APIBackend, config.GPO, config.Miner.GasPrice)
eth.APIBackend.gpo = gasprice.NewOracle(eth.APIBackend, config.GPO, config.Miner.GasPrice, config.Miner.MaxDABlockSize)

if config.RollupSequencerHTTP != "" {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
Expand Down
2 changes: 1 addition & 1 deletion eth/gasprice/feehistory_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ func testFeeHistory(t *testing.T, opStack bool) {
MaxBlockHistory: c.maxBlock,
}
backend := newTestBackend(t, big.NewInt(16), big.NewInt(28), c.pending, opStack)
oracle := NewOracle(backend, config, nil)
oracle := NewOracle(backend, config, nil, big.NewInt(1000000))

first, reward, baseFee, ratio, blobBaseFee, blobRatio, err := oracle.FeeHistory(context.Background(), c.count, c.last, c.percent)
backend.teardown()
Expand Down
7 changes: 6 additions & 1 deletion eth/gasprice/gasprice.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ type Oracle struct {

checkBlocks, percentile int
maxHeaderHistory, maxBlockHistory uint64
maxDABlockSize *big.Int

historyCache *lru.Cache[cacheKey, processedFees]

Expand All @@ -84,7 +85,7 @@ type Oracle struct {

// NewOracle returns a new gasprice oracle which can recommend suitable
// gasprice for newly created transaction.
func NewOracle(backend OracleBackend, params Config, startPrice *big.Int) *Oracle {
func NewOracle(backend OracleBackend, params Config, startPrice *big.Int, maxDABlockSize *big.Int) *Oracle {
blocks := params.Blocks
if blocks < 1 {
blocks = 1
Expand Down Expand Up @@ -123,6 +124,9 @@ func NewOracle(backend OracleBackend, params Config, startPrice *big.Int) *Oracl
if startPrice == nil {
startPrice = new(big.Int)
}
if maxDABlockSize == nil {
maxDABlockSize = new(big.Int)
}

cache := lru.NewCache[cacheKey, processedFees](2048)
headEvent := make(chan core.ChainHeadEvent, 1)
Expand Down Expand Up @@ -153,6 +157,7 @@ func NewOracle(backend OracleBackend, params Config, startPrice *big.Int) *Oracl
percentile: percent,
maxHeaderHistory: maxHeaderHistory,
maxBlockHistory: maxBlockHistory,
maxDABlockSize: maxDABlockSize,
historyCache: cache,
}

Expand Down
2 changes: 1 addition & 1 deletion eth/gasprice/gasprice_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,7 @@ func TestSuggestTipCap(t *testing.T) {
}
for _, c := range cases {
backend := newTestBackend(t, c.fork, nil, false, false)
oracle := NewOracle(backend, config, big.NewInt(params.GWei))
oracle := NewOracle(backend, config, big.NewInt(params.GWei), big.NewInt(1000000))

// The gas price sampled is: 32G, 31G, 30G, 29G, 28G, 27G
got, err := oracle.SuggestTipCap(context.Background())
Expand Down
65 changes: 50 additions & 15 deletions eth/gasprice/optimism-gasprice.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ import (
func (oracle *Oracle) SuggestOptimismPriorityFee(ctx context.Context, h *types.Header, headHash common.Hash) *big.Int {
suggestion := new(big.Int).Set(oracle.minSuggestedPriorityFee)

// find the maximum gas used by any of the transactions in the block to use as the capacity
// margin
// 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, headHash)
if receipts == nil || err != nil {
log.Error("failed to get block receipts", "err", err)
Expand All @@ -59,19 +59,54 @@ func (oracle *Oracle) SuggestOptimismPriorityFee(ctx context.Context, h *types.H
return suggestion
}

if h.GasUsed+maxTxGasUsed > h.GasLimit {
// A block is "at capacity" if, when it is built, there is a pending tx in the txpool that
// could not be included because the block's gas limit would be exceeded. Since we don't
// have access to the txpool, we instead adopt the following heuristic: consider a block as
// at capacity if the total gas consumed by its transactions is within max-tx-gas-used of
// the block limit, where max-tx-gas-used is the most gas used by any one transaction
// within the block. This heuristic is almost perfectly accurate when transactions always
// consume the same amount of gas, but becomes less accurate as tx gas consumption begins
// to vary. The typical error is we assume a block is at capacity when it was not because
// max-tx-gas-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.
// find the maximum transaction size by any of the transactions in the block to use as the block
// size limit capacity margin
var (
maxTxSizeUsed uint64
totalTxSizeUsed uint64
)
block, err := oracle.backend.BlockByNumber(ctx, rpc.BlockNumber(h.Number.Int64()))
if block == nil || err != nil {
log.Error("failed to get last block", "err", err)
return suggestion
}
txs := block.Transactions()

for i := range txs {
su := txs[i].Size()
if su > maxTxSizeUsed {
maxTxSizeUsed = su
}
totalTxSizeUsed = totalTxSizeUsed + su
}

if maxTxSizeUsed > oracle.maxDABlockSize.Uint64() {
log.Error("found tx consuming more size than the block size limit", "size", maxTxSizeUsed)
return suggestion
}

if h.GasUsed+maxTxGasUsed > h.GasLimit ||
totalTxSizeUsed+maxTxSizeUsed > oracle.maxDABlockSize.Uint64() {
// 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.
block, err := oracle.backend.BlockByNumber(ctx, rpc.BlockNumber(h.Number.Int64()))
if block == nil || err != nil {
log.Error("failed to get last block", "err", err)
Expand Down
2 changes: 1 addition & 1 deletion eth/gasprice/optimism-gasprice_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ func TestSuggestOptimismPriorityFee(t *testing.T) {
}
for i, c := range cases {
backend := newOpTestBackend(t, c.txdata)
oracle := NewOracle(backend, Config{MinSuggestedPriorityFee: minSuggestion}, big.NewInt(params.GWei))
oracle := NewOracle(backend, Config{MinSuggestedPriorityFee: minSuggestion}, big.NewInt(params.GWei), big.NewInt(1000000))
got := oracle.SuggestOptimismPriorityFee(context.Background(), backend.block.Header(), backend.block.Hash())
if got.Cmp(c.want) != 0 {
t.Errorf("Gas price mismatch for test case %d: want %d, got %d", i, c.want, got)
Expand Down