Files
coreth/eth/gasprice/gasprice_test.go
Antje WorringandHanzo Dev ee95aa65f2 test: migrate EVM tests to all-upgrades-active fee model (LP-176)
coreth now activates all chain upgrades implicitly (modern Ethereum
through Cancun + Avax/Lux dynamic-fee + capacity-based block fees always
on). Stale tests written for a pre-fee world (bare ChainConfig, legacy
nil-gasprice txs, HomesteadSigner) failed with "fee cap less than base
fee", fee-state capacity errors, or stale expected values. Migrated all
to the canonical pattern: params.TestChainConfig, Ether-scale balances,
types.LatestSigner, DynamicFeeTx paying gen.BaseFee()+tip.

- core: shared *Test helpers migrated (fixes all table-driven variants);
  balances now subtract the fee actually paid. The two obsolete
  block-gas-cost rejection tests (block-gas-cost is 0 under LP-176)
  repurposed to positive zero-tip-block-valid guards (GenerateChainZeroTipBlock,
  InsertChainZeroTipBlockAccepted); the surviving fee-cap-below-base-fee
  rejection stays covered by state_processor_test.go ErrFeeCapTooLow.
- eth/tracers: golden gas +2 (EIP-3860 init-code word, now active).
- eth/gasprice: TestSuggestTipCapMaxBlocksLookback tip 3->1 (block-gas-cost
  surcharge gone; oracle clamps to MinGasPrice=1, matching sibling cases).
- internal/ethapi: regenerated block/header/receipt golden for Cancun
  fields (blobGasUsed/excessBlobGas/parentBeaconBlockRoot + derived
  hashes); no semantic field changed.
- ethclient/simulated: fork at the authoritative genesis hash.
- sync/handlers: 5s block spacing so LP-176 capacity refills.

Full suite: 52/52 packages green (CGO_ENABLED=0 -tags=pebble).

Co-authored-by: Hanzo Dev <[email protected]>
2026-06-06 22:23:12 -07:00

423 lines
14 KiB
Go

// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
//
// This file is a derived work, based on the go-ethereum library whose original
// notices appear below.
//
// It is distributed under a license compatible with the licensing terms of the
// original code from which it is derived.
//
// Much love to the original authors for their work.
// **********
// 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"
"testing"
"time"
"github.com/luxfi/coreth/consensus/dummy"
"github.com/luxfi/coreth/core"
"github.com/luxfi/coreth/params"
customheader "github.com/luxfi/coreth/plugin/evm/header"
"github.com/luxfi/coreth/plugin/evm/upgrade/blockgascost"
"github.com/luxfi/coreth/plugin/evm/upgrade/gaslimit_initial"
"github.com/luxfi/coreth/plugin/evm/upgrade/lp176"
"github.com/luxfi/coreth/rpc"
"github.com/luxfi/crypto"
"github.com/luxfi/geth/common"
"github.com/luxfi/geth/core/rawdb"
"github.com/luxfi/geth/core/state"
"github.com/luxfi/geth/core/types"
"github.com/luxfi/geth/core/vm"
"github.com/luxfi/geth/event"
"github.com/stretchr/testify/require"
)
var (
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
cryptoAddr = crypto.PubkeyToAddress(key.PublicKey)
addr = common.BytesToAddress(cryptoAddr.Bytes()) // convert to geth common.Address
bal, _ = new(big.Int).SetString("100000000000000000000000", 10)
)
type testBackend struct {
chain *core.BlockChain
acceptedEvent chan<- core.ChainEvent
}
func (b *testBackend) HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error) {
if number == rpc.LatestBlockNumber {
return b.chain.CurrentBlock(), nil
}
return b.chain.GetHeaderByNumber(uint64(number)), nil
}
func (b *testBackend) BlockByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Block, error) {
if number == rpc.LatestBlockNumber {
number = rpc.BlockNumber(b.chain.CurrentBlock().Number.Uint64())
}
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) ChainConfig() *params.ChainConfig {
return b.chain.Config()
}
func (b *testBackend) SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription {
return nil
}
func (b *testBackend) SubscribeChainAcceptedEvent(ch chan<- core.ChainEvent) event.Subscription {
b.acceptedEvent = ch
return nil
}
func (b *testBackend) teardown() {
b.chain.Stop()
}
func newTestBackendFakerEngine(t *testing.T, config *params.ChainConfig, numBlocks int, extDataGasUsage *big.Int, genBlocks func(i int, b *core.BlockGen)) *testBackend {
var gspec = &core.Genesis{
Config: config,
Alloc: types.GenesisAlloc{addr: {Balance: bal}},
}
engine := dummy.NewETHFaker()
// Generate testing blocks
_, blocks, _, err := core.GenerateChainWithGenesis(gspec, engine, numBlocks, blockgascost.TargetBlockRate-1, genBlocks)
if err != nil {
t.Fatal(err)
}
// Construct testing chain
diskdb := rawdb.NewMemoryDatabase()
chain, err := core.NewBlockChain(diskdb, core.DefaultCacheConfig, gspec, engine, vm.Config{}, common.Hash{}, false)
if err != nil {
t.Fatalf("Failed to create local chain, %v", err)
}
if _, err := chain.InsertChain(blocks); err != nil {
t.Fatalf("Failed to insert chain, %v", err)
}
return &testBackend{chain: chain}
}
// 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, config *params.ChainConfig, numBlocks int, extDataGasUsage *big.Int, genBlocks func(i int, b *core.BlockGen)) *testBackend {
var gspec = &core.Genesis{
Config: config,
Alloc: types.GenesisAlloc{addr: {Balance: bal}},
}
engine := dummy.NewFakerWithCallbacks(dummy.ConsensusCallbacks{
OnFinalizeAndAssemble: func(*types.Header, *types.Header, *state.StateDB, []*types.Transaction) ([]byte, *big.Int, *big.Int, error) {
return nil, common.Big0, extDataGasUsage, nil
},
OnExtraStateChange: func(*types.Block, *types.Header, *state.StateDB) (*big.Int, *big.Int, error) {
return common.Big0, extDataGasUsage, nil
},
})
// Generate testing blocks
_, blocks, _, err := core.GenerateChainWithGenesis(gspec, engine, numBlocks, blockgascost.TargetBlockRate-1, genBlocks)
if err != nil {
t.Fatal(err)
}
// Construct testing chain
chain, err := core.NewBlockChain(rawdb.NewMemoryDatabase(), core.DefaultCacheConfig, gspec, engine, vm.Config{}, common.Hash{}, false)
if err != nil {
t.Fatalf("Failed to create local chain, %v", err)
}
if _, err := chain.InsertChain(blocks); err != nil {
t.Fatalf("Failed to insert chain, %v", err)
}
return &testBackend{chain: chain}
}
func (b *testBackend) MinRequiredTip(ctx context.Context, header *types.Header) (*big.Int, error) {
config := params.GetExtra(b.chain.Config())
return customheader.EstimateRequiredTip(config, header)
}
func (b *testBackend) CurrentHeader() *types.Header {
return b.chain.CurrentHeader()
}
func (b *testBackend) LastAcceptedBlock() *types.Block {
current := b.chain.CurrentBlock()
if current == nil {
return nil
}
return b.chain.GetBlockByNumber(current.Number.Uint64())
}
func (b *testBackend) GetBlockByNumber(number uint64) *types.Block {
return b.chain.GetBlockByNumber(number)
}
type suggestTipCapTest struct {
chainConfig *params.ChainConfig
numBlocks int
extDataGasUsage *big.Int
genBlock func(i int, b *core.BlockGen)
expectedTip *big.Int
}
func defaultOracleConfig() Config {
return Config{
Blocks: 20,
Percentile: 60,
MaxLookbackSeconds: 80,
}
}
// timeCrunchOracleConfig returns a config with [MaxLookbackSeconds] set to 5
// to ensure that during gas price estimation, we will hit the time based look back limit
func timeCrunchOracleConfig() Config {
return Config{
Blocks: 20,
Percentile: 60,
MaxLookbackSeconds: 5,
}
}
func applyGasPriceTest(t *testing.T, test suggestTipCapTest, config Config) {
if test.genBlock == nil {
test.genBlock = func(i int, b *core.BlockGen) {}
}
backend := newTestBackend(t, test.chainConfig, test.numBlocks, test.extDataGasUsage, test.genBlock)
oracle, err := NewOracle(backend, config)
require.NoError(t, err)
// mock time to be consistent across different CI runs
// sets currentTime to be 20 seconds
oracle.clock.Set(time.Unix(20, 0))
got, err := oracle.SuggestTipCap(context.Background())
backend.teardown()
require.NoError(t, err)
if got.Cmp(test.expectedTip) != 0 {
t.Fatalf("Expected tip (%d), got tip (%d)", test.expectedTip, got)
}
}
func testGenBlock(t *testing.T, tip int64, numTx int) func(int, *core.BlockGen) {
return func(i int, b *core.BlockGen) {
b.SetCoinbase(common.Address{1})
txTip := big.NewInt(tip * params.GWei)
signer := types.LatestSigner(params.TestChainConfig)
baseFee := b.BaseFee()
feeCap := new(big.Int).Add(baseFee, txTip)
for j := 0; j < numTx; j++ {
tx := types.NewTx(&types.DynamicFeeTx{
ChainID: params.TestChainConfig.ChainID,
Nonce: b.TxNonce(addr),
To: &common.Address{},
Gas: params.TxGas,
GasFeeCap: feeCap,
GasTipCap: txTip,
Data: []byte{},
})
tx, err := types.SignTx(tx, signer, key)
require.NoError(t, err, "failed to create tx")
b.AddTx(tx)
}
}
}
func TestSuggestTipCapEmptyExtDataGasUsage(t *testing.T) {
applyGasPriceTest(t, suggestTipCapTest{
chainConfig: params.TestChainConfig,
numBlocks: 3,
extDataGasUsage: nil,
genBlock: testGenBlock(t, 55, 80),
expectedTip: big.NewInt(1),
}, defaultOracleConfig())
}
func TestSuggestTipCapSimple(t *testing.T) {
applyGasPriceTest(t, suggestTipCapTest{
chainConfig: params.TestChainConfig,
numBlocks: 3,
extDataGasUsage: common.Big0,
genBlock: testGenBlock(t, 55, 80),
expectedTip: big.NewInt(1),
}, defaultOracleConfig())
}
func TestSuggestTipCapSimpleFloor(t *testing.T) {
applyGasPriceTest(t, suggestTipCapTest{
chainConfig: params.TestChainConfig,
numBlocks: 1,
extDataGasUsage: common.Big0,
genBlock: testGenBlock(t, 55, 80),
expectedTip: big.NewInt(1),
}, defaultOracleConfig())
}
func TestSuggestTipCapSmallTips(t *testing.T) {
tip := big.NewInt(550 * params.GWei)
applyGasPriceTest(t, suggestTipCapTest{
chainConfig: params.TestChainConfig,
numBlocks: 3,
extDataGasUsage: common.Big0,
genBlock: func(i int, b *core.BlockGen) {
b.SetCoinbase(common.Address{1})
signer := types.LatestSigner(params.TestChainConfig)
baseFee := b.BaseFee()
feeCap := new(big.Int).Add(baseFee, tip)
for j := 0; j < 40; j++ {
tx := types.NewTx(&types.DynamicFeeTx{
ChainID: params.TestChainConfig.ChainID,
Nonce: b.TxNonce(addr),
To: &common.Address{},
Gas: params.TxGas,
GasFeeCap: feeCap,
GasTipCap: tip,
Data: []byte{},
})
tx, err := types.SignTx(tx, signer, key)
if err != nil {
t.Fatalf("failed to create tx: %s", err)
}
b.AddTx(tx)
tx = types.NewTx(&types.DynamicFeeTx{
ChainID: params.TestChainConfig.ChainID,
Nonce: b.TxNonce(addr),
To: &common.Address{},
Gas: params.TxGas,
GasFeeCap: feeCap,
GasTipCap: common.Big1,
Data: []byte{},
})
tx, err = types.SignTx(tx, signer, key)
require.NoError(t, err, "failed to create tx")
b.AddTx(tx)
}
},
// NOTE: small tips do not bias estimate
expectedTip: big.NewInt(1),
}, defaultOracleConfig())
}
func TestSuggestTipCapExtDataUsage(t *testing.T) {
applyGasPriceTest(t, suggestTipCapTest{
chainConfig: params.TestChainConfig,
numBlocks: 3,
extDataGasUsage: big.NewInt(10_000),
genBlock: testGenBlock(t, 55, 80),
expectedTip: big.NewInt(1),
}, defaultOracleConfig())
}
func TestSuggestTipCapMinGas(t *testing.T) {
applyGasPriceTest(t, suggestTipCapTest{
chainConfig: params.TestChainConfig,
numBlocks: 3,
extDataGasUsage: common.Big0,
genBlock: testGenBlock(t, 500, 50),
expectedTip: big.NewInt(1),
}, defaultOracleConfig())
}
// Regression test to ensure that SuggestPrice does not panic prior to activation of ApricotPhase3
// Note: support for gas estimation without activated hard forks has been deprecated, but we still
// ensure that the call does not panic.
func TestSuggestGasPricePreAP3(t *testing.T) {
config := Config{
Blocks: 20,
Percentile: 60,
}
backend := newTestBackend(t, params.TestChainConfig, 3, nil, func(i int, b *core.BlockGen) {
b.SetCoinbase(common.Address{1})
signer := types.LatestSigner(params.TestChainConfig)
gasPrice := big.NewInt(gaslimit_initial.MinGasPrice)
for j := 0; j < 50; j++ {
tx := types.NewTx(&types.LegacyTx{
Nonce: b.TxNonce(addr),
To: &common.Address{},
Gas: params.TxGas,
GasPrice: gasPrice,
Data: []byte{},
})
tx, err := types.SignTx(tx, signer, key)
require.NoError(t, err, "failed to create tx")
b.AddTx(tx)
}
})
defer backend.teardown()
oracle, err := NewOracle(backend, config)
require.NoError(t, err)
_, err = oracle.SuggestPrice(context.Background())
require.NoError(t, err)
}
func TestSuggestTipCapMaxBlocksLookback(t *testing.T) {
// Under activate-all-implicitly the block-gas-cost surcharge is gone:
// EstimateRequiredTip returns 0 for every block, so the percentile over
// recent blocks is 0 and the oracle clamps up to minPrice
// (lp176.MinGasPrice = 1). The tips paid in-block no longer bias the
// estimate, so the legacy expected tip of 3 is now 1 (matches the
// TestSuggestTipCapIncludesExtraDataGas migration).
applyGasPriceTest(t, suggestTipCapTest{
chainConfig: params.TestChainConfig,
numBlocks: 200,
extDataGasUsage: common.Big0,
genBlock: testGenBlock(t, 550, 80),
expectedTip: big.NewInt(1),
}, defaultOracleConfig())
}
func TestSuggestTipCapMaxBlocksSecondsLookback(t *testing.T) {
applyGasPriceTest(t, suggestTipCapTest{
chainConfig: params.TestChainConfig,
numBlocks: 20,
extDataGasUsage: big.NewInt(1),
genBlock: testGenBlock(t, 550, 80),
expectedTip: big.NewInt(1),
}, timeCrunchOracleConfig())
}
func TestSuggestTipCapIncludesExtraDataGas(t *testing.T) {
// Under activate-all-implicitly the block-gas-cost mechanism is gone:
// EstimateRequiredTip always returns 0, so a block whose gas usage is
// dominated by ExtDataGasUsed no longer produces a non-zero tip
// surcharge. The fee model is LP-176 capacity pricing on BaseFee.
applyGasPriceTest(t, suggestTipCapTest{
chainConfig: params.TestChainConfig,
numBlocks: 1000,
extDataGasUsage: big.NewInt(lp176.MinMaxPerSecond - int64(params.TxGas)),
genBlock: testGenBlock(t, 100_000, 1),
expectedTip: big.NewInt(1),
}, defaultOracleConfig())
}