From 492ca8b76bfe1bab82b708b196b7f47f41b7f8c3 Mon Sep 17 00:00:00 2001 From: Ryan Schneider Date: Fri, 1 Mar 2024 09:30:34 -0800 Subject: [PATCH 01/11] eth,internal: Add eth_blobBaseFee RPC --- eth/api_backend.go | 10 ++++++++++ internal/ethapi/api.go | 5 +++++ internal/ethapi/api_test.go | 17 +++++++++-------- internal/ethapi/backend.go | 3 ++- internal/ethapi/transaction_args_test.go | 3 +++ 5 files changed, 29 insertions(+), 9 deletions(-) diff --git a/eth/api_backend.go b/eth/api_backend.go index a97942599c97..589851c36f16 100644 --- a/eth/api_backend.go +++ b/eth/api_backend.go @@ -26,6 +26,7 @@ import ( "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/consensus" + "github.com/ethereum/go-ethereum/consensus/misc/eip4844" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/bloombits" "github.com/ethereum/go-ethereum/core/rawdb" @@ -365,6 +366,15 @@ func (b *EthAPIBackend) FeeHistory(ctx context.Context, blockCount uint64, lastB return b.gpo.FeeHistory(ctx, blockCount, lastBlock, rewardPercentiles) } +func (b *EthAPIBackend) BlobBaseFee(ctx context.Context) (*big.Int, error) { + if excessBlobGas := b.CurrentHeader().ExcessBlobGas; excessBlobGas != nil { + blobBaseFee := eip4844.CalcBlobFee(*excessBlobGas) + return blobBaseFee, nil + } else { + return nil, nil + } +} + func (b *EthAPIBackend) ChainDb() ethdb.Database { return b.eth.ChainDb() } diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index f965c91375a5..a09cbb62917a 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -124,6 +124,11 @@ func (s *EthereumAPI) FeeHistory(ctx context.Context, blockCount math.HexOrDecim return results, nil } +func (s *EthereumAPI) BlobBaseFee(ctx context.Context) (*hexutil.Big, error) { + blobBaseFee, err := s.b.BlobBaseFee(ctx) + return (*hexutil.Big)(blobBaseFee), err +} + // Syncing returns false in case the node is currently not syncing with the network. It can be up-to-date or has not // yet received the latest block headers from its pears. In case it is synchronizing: // - startingBlock: block number this node started to synchronize from diff --git a/internal/ethapi/api_test.go b/internal/ethapi/api_test.go index 6aad4097fe84..20fd6f8bcfbf 100644 --- a/internal/ethapi/api_test.go +++ b/internal/ethapi/api_test.go @@ -471,14 +471,15 @@ func (b testBackend) SuggestGasTipCap(ctx context.Context) (*big.Int, error) { func (b testBackend) FeeHistory(ctx context.Context, blockCount uint64, lastBlock rpc.BlockNumber, rewardPercentiles []float64) (*big.Int, [][]*big.Int, []*big.Int, []float64, error) { return nil, nil, nil, nil, nil } -func (b testBackend) ChainDb() ethdb.Database { return b.db } -func (b testBackend) AccountManager() *accounts.Manager { return b.accman } -func (b testBackend) ExtRPCEnabled() bool { return false } -func (b testBackend) RPCGasCap() uint64 { return 10000000 } -func (b testBackend) RPCEVMTimeout() time.Duration { return time.Second } -func (b testBackend) RPCTxFeeCap() float64 { return 0 } -func (b testBackend) UnprotectedAllowed() bool { return false } -func (b testBackend) SetHead(number uint64) {} +func (b testBackend) BlobBaseFee(ctx context.Context) (*big.Int, error) { return big.NewInt(0), nil } +func (b testBackend) ChainDb() ethdb.Database { return b.db } +func (b testBackend) AccountManager() *accounts.Manager { return b.accman } +func (b testBackend) ExtRPCEnabled() bool { return false } +func (b testBackend) RPCGasCap() uint64 { return 10000000 } +func (b testBackend) RPCEVMTimeout() time.Duration { return time.Second } +func (b testBackend) RPCTxFeeCap() float64 { return 0 } +func (b testBackend) UnprotectedAllowed() bool { return false } +func (b testBackend) SetHead(number uint64) {} func (b testBackend) HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error) { if number == rpc.LatestBlockNumber { return b.chain.CurrentBlock(), nil diff --git a/internal/ethapi/backend.go b/internal/ethapi/backend.go index fd2f5699eabf..f4a3582339c8 100644 --- a/internal/ethapi/backend.go +++ b/internal/ethapi/backend.go @@ -44,7 +44,8 @@ type Backend interface { SyncProgress() ethereum.SyncProgress SuggestGasTipCap(ctx context.Context) (*big.Int, error) - FeeHistory(ctx context.Context, blockCount uint64, lastBlock rpc.BlockNumber, rewardPercentiles []float64) (*big.Int, [][]*big.Int, []*big.Int, []float64, error) + FeeHistory(ctx context.Context, blockCount uint64, lastBlock rpc.BlockNumber, rewardPercentiles []float64) (*big.Int, [][]*big.Int, []*big.Int, []float64, []*big.Int, []float64, error) + BlobBaseFee(ctx context.Context) (*big.Int, error) ChainDb() ethdb.Database AccountManager() *accounts.Manager ExtRPCEnabled() bool diff --git a/internal/ethapi/transaction_args_test.go b/internal/ethapi/transaction_args_test.go index 24ecb1dee4e7..feb4cc4be334 100644 --- a/internal/ethapi/transaction_args_test.go +++ b/internal/ethapi/transaction_args_test.go @@ -322,6 +322,9 @@ func (b *backendMock) SyncProgress() ethereum.SyncProgress { return ethereum.Syn func (b *backendMock) FeeHistory(ctx context.Context, blockCount uint64, lastBlock rpc.BlockNumber, rewardPercentiles []float64) (*big.Int, [][]*big.Int, []*big.Int, []float64, error) { return nil, nil, nil, nil, nil } +func (b *backendMock) BlobBaseFee(ctx context.Context) (*big.Int, error) { + return big.NewInt(42), nil +} func (b *backendMock) ChainDb() ethdb.Database { return nil } func (b *backendMock) AccountManager() *accounts.Manager { return nil } func (b *backendMock) ExtRPCEnabled() bool { return false } From 4fdde0f956bcfd4e75b3fa76cecc50a35d79961f Mon Sep 17 00:00:00 2001 From: Ryan Schneider Date: Fri, 1 Mar 2024 09:31:18 -0800 Subject: [PATCH 02/11] eth,internal: Add blob results to eth_feeHistory RPC --- eth/api_backend.go | 2 +- eth/gasprice/feehistory.go | 43 +++++++--- eth/gasprice/feehistory_test.go | 7 +- eth/gasprice/gasprice_test.go | 104 +++++++++++++++++++++-- internal/ethapi/api.go | 26 ++++-- internal/ethapi/api_test.go | 9 +- internal/ethapi/transaction_args_test.go | 4 +- 7 files changed, 159 insertions(+), 36 deletions(-) diff --git a/eth/api_backend.go b/eth/api_backend.go index 589851c36f16..648750cc33ed 100644 --- a/eth/api_backend.go +++ b/eth/api_backend.go @@ -362,7 +362,7 @@ func (b *EthAPIBackend) SuggestGasTipCap(ctx context.Context) (*big.Int, error) return b.gpo.SuggestTipCap(ctx) } -func (b *EthAPIBackend) FeeHistory(ctx context.Context, blockCount uint64, lastBlock rpc.BlockNumber, rewardPercentiles []float64) (firstBlock *big.Int, reward [][]*big.Int, baseFee []*big.Int, gasUsedRatio []float64, err error) { +func (b *EthAPIBackend) FeeHistory(ctx context.Context, blockCount uint64, lastBlock rpc.BlockNumber, rewardPercentiles []float64) (firstBlock *big.Int, reward [][]*big.Int, baseFee []*big.Int, gasUsedRatio []float64, baseFeePerBlobGas []*big.Int, blobGasUsedRatio []float64, err error) { return b.gpo.FeeHistory(ctx, blockCount, lastBlock, rewardPercentiles) } diff --git a/eth/gasprice/feehistory.go b/eth/gasprice/feehistory.go index 1c7f8ba42a26..aafdfe9395e5 100644 --- a/eth/gasprice/feehistory.go +++ b/eth/gasprice/feehistory.go @@ -26,10 +26,14 @@ import ( "slices" "sync/atomic" + "golang.org/x/exp/slices" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/consensus/misc/eip1559" + "github.com/ethereum/go-ethereum/consensus/misc/eip4844" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rpc" ) @@ -66,6 +70,8 @@ type processedFees struct { reward []*big.Int baseFee, nextBaseFee *big.Int gasUsedRatio float64 + blobGasUsedRatio float64 + blobBaseFee *big.Int } // txGasAndReward is sorted in ascending order based on reward @@ -88,6 +94,16 @@ func (oracle *Oracle) processBlock(bf *blockFees, percentiles []float64) { bf.results.nextBaseFee = new(big.Int) } bf.results.gasUsedRatio = float64(bf.header.GasUsed) / float64(bf.header.GasLimit) + bf.results.blobGasUsedRatio = 0.0 + bf.results.blobBaseFee = new(big.Int) + if bf.header != nil && chainconfig.IsCancun(bf.header.Number, bf.header.Time) { + if blobGasUsed := bf.header.BlobGasUsed; blobGasUsed != nil { + bf.results.blobGasUsedRatio = float64(*blobGasUsed) / params.MaxBlobGasPerBlock + } + if excessBlobGas := bf.header.ExcessBlobGas; excessBlobGas != nil { + bf.results.blobBaseFee = eip4844.CalcBlobFee(*excessBlobGas) + } + } if len(percentiles) == 0 { // rewards were not requested, return null return @@ -211,9 +227,9 @@ func (oracle *Oracle) resolveBlockRange(ctx context.Context, reqEnd rpc.BlockNum // // 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) { +func (oracle *Oracle) FeeHistory(ctx context.Context, blocks uint64, unresolvedLastBlock rpc.BlockNumber, rewardPercentiles []float64) (*big.Int, [][]*big.Int, []*big.Int, []float64, []*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 + return common.Big0, nil, nil, nil, nil, nil, nil // returning with no data and no error means there are no retrievable blocks } maxFeeHistory := oracle.maxHeaderHistory if len(rewardPercentiles) != 0 { @@ -225,10 +241,10 @@ func (oracle *Oracle) FeeHistory(ctx context.Context, blocks uint64, unresolvedL } for i, p := range rewardPercentiles { if p < 0 || p > 100 { - return common.Big0, nil, nil, nil, fmt.Errorf("%w: %f", errInvalidPercentile, p) + return common.Big0, nil, nil, 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) + return common.Big0, nil, nil, nil, nil, nil, fmt.Errorf("%w: #%d:%f >= #%d:%f", errInvalidPercentile, i-1, rewardPercentiles[i-1], i, p) } } var ( @@ -238,7 +254,7 @@ func (oracle *Oracle) FeeHistory(ctx context.Context, blocks uint64, unresolvedL ) pendingBlock, pendingReceipts, lastBlock, blocks, err := oracle.resolveBlockRange(ctx, unresolvedLastBlock, blocks) if err != nil || blocks == 0 { - return common.Big0, nil, nil, nil, err + return common.Big0, nil, nil, nil, nil, nil, err } oldestBlock := lastBlock + 1 - blocks @@ -295,19 +311,22 @@ func (oracle *Oracle) FeeHistory(ctx context.Context, blocks uint64, unresolvedL }() } var ( - reward = make([][]*big.Int, blocks) - baseFee = make([]*big.Int, blocks+1) - gasUsedRatio = make([]float64, blocks) - firstMissing = blocks + reward = make([][]*big.Int, blocks) + baseFee = make([]*big.Int, blocks+1) + gasUsedRatio = make([]float64, blocks) + blobGasUsedRatio = make([]float64, blocks) + blobBaseFee = make([]*big.Int, blocks) + firstMissing = blocks ) for ; blocks > 0; blocks-- { fees := <-results if fees.err != nil { - return common.Big0, nil, nil, nil, fees.err + return common.Big0, nil, nil, 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 + blobGasUsedRatio[i], blobBaseFee[i] = fees.results.blobGasUsedRatio, fees.results.blobBaseFee } else { // getting no block and no error means we are requesting into the future (might happen because of a reorg) if i < firstMissing { @@ -316,7 +335,7 @@ func (oracle *Oracle) FeeHistory(ctx context.Context, blocks uint64, unresolvedL } } if firstMissing == 0 { - return common.Big0, nil, nil, nil, nil + return common.Big0, nil, nil, nil, nil, nil, nil } if len(rewardPercentiles) != 0 { reward = reward[:firstMissing] @@ -324,5 +343,5 @@ func (oracle *Oracle) FeeHistory(ctx context.Context, blocks uint64, unresolvedL reward = nil } baseFee, gasUsedRatio = baseFee[:firstMissing+1], gasUsedRatio[:firstMissing] - return new(big.Int).SetUint64(oldestBlock), reward, baseFee, gasUsedRatio, nil + return new(big.Int).SetUint64(oldestBlock), reward, baseFee, gasUsedRatio, blobBaseFee, blobGasUsedRatio, nil } diff --git a/eth/gasprice/feehistory_test.go b/eth/gasprice/feehistory_test.go index 1bcfb287a571..cd1c13ceb1f6 100644 --- a/eth/gasprice/feehistory_test.go +++ b/eth/gasprice/feehistory_test.go @@ -58,10 +58,10 @@ func TestFeeHistory(t *testing.T) { MaxHeaderHistory: c.maxHeader, MaxBlockHistory: c.maxBlock, } - backend := newTestBackend(t, big.NewInt(16), c.pending) + backend := newTestBackend(t, big.NewInt(16), c.pending, true) oracle := NewOracle(backend, config) - first, reward, baseFee, ratio, err := oracle.FeeHistory(context.Background(), c.count, c.last, c.percent) + first, reward, baseFee, ratio, blobBaseFee, blobUsed, err := oracle.FeeHistory(context.Background(), c.count, c.last, c.percent) backend.teardown() expReward := c.expCount if len(c.percent) == 0 { @@ -84,6 +84,9 @@ func TestFeeHistory(t *testing.T) { if len(ratio) != c.expCount { t.Fatalf("Test case %d: gasUsedRatio array length mismatch, want %d, got %d", i, c.expCount, len(ratio)) } + if len(blobBaseFee) != len(blobUsed) || len(blobBaseFee) != c.expCount { + t.Fatalf("Test case %d: blob arrays length mismatch, want %d, got %d and %d", i, c.expCount, len(blobBaseFee), len(blobUsed)) + } 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_test.go b/eth/gasprice/gasprice_test.go index 1d2e02cde6e1..d1cf0095bdaf 100644 --- a/eth/gasprice/gasprice_test.go +++ b/eth/gasprice/gasprice_test.go @@ -18,11 +18,18 @@ package gasprice import ( "context" + "crypto/ecdsa" + "crypto/sha256" + "fmt" "math" "math/big" "testing" + "github.com/holiman/uint256" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus" + "github.com/ethereum/go-ethereum/consensus/beacon" "github.com/ethereum/go-ethereum/consensus/ethash" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/rawdb" @@ -30,6 +37,7 @@ import ( "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/crypto/kzg4844" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rpc" @@ -121,7 +129,7 @@ func (b *testBackend) teardown() { // 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 { +func newTestBackend(t *testing.T, londonBlock *big.Int, pending bool, addCancunBlocks bool) *testBackend { var ( key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") addr = crypto.PubkeyToAddress(key.PublicKey) @@ -130,17 +138,32 @@ func newTestBackend(t *testing.T, londonBlock *big.Int, pending bool) *testBacke Config: &config, Alloc: types.GenesisAlloc{addr: {Balance: big.NewInt(math.MaxInt64)}}, } - signer = types.LatestSigner(gspec.Config) + signer = types.LatestSigner(gspec.Config) + blobSigners = [6]struct { + key *ecdsa.PrivateKey + addr common.Address + }{} ) config.LondonBlock = londonBlock config.ArrowGlacierBlock = londonBlock config.GrayGlacierBlock = londonBlock - config.TerminalTotalDifficulty = common.Big0 - engine := ethash.NewFaker() + var engine consensus.Engine = beacon.New(ethash.NewFaker()) + td := int(params.GenesisDifficulty.Uint64()) + + if addCancunBlocks { + for i := range blobSigners { + key, _ := crypto.GenerateKey() + addr := crypto.PubkeyToAddress(key.PublicKey) + blobSigners[i].key = key + blobSigners[i].addr = addr + gspec.Alloc[addr] = types.Account{Balance: big.NewInt(math.MaxInt64)} + } + } // Generate testing blocks - _, blocks, _ := core.GenerateChainWithGenesis(gspec, engine, testHead+1, func(i int, b *core.BlockGen) { + genDb, blocks, _ := core.GenerateChainWithGenesis(gspec, engine, testHead+1, func(i int, b *core.BlockGen) { b.SetCoinbase(common.Address{1}) + td += int(b.Difficulty().Uint64()) var txdata types.TxData if londonBlock != nil && b.Number().Cmp(londonBlock) >= 0 { @@ -166,13 +189,78 @@ func newTestBackend(t *testing.T, londonBlock *big.Int, pending bool) *testBacke 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) + db := rawdb.NewMemoryDatabase() + merger := consensus.NewMerger(db) + chain, err := core.NewBlockChain(db, &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) + if i, err := chain.InsertChain(blocks); err != nil { + panic(fmt.Errorf("error inserting block %d: %w", i, err)) + } chain.SetFinalized(chain.GetBlockByNumber(25).Header()) chain.SetSafe(chain.GetBlockByNumber(25).Header()) + + if addCancunBlocks { + if err := chain.SetHead(26); err != nil { + panic(err) + } + + head := chain.GetBlockByNumber(26) + ts := head.Time() + config.ShanghaiTime = &ts + config.CancunTime = &ts + ttd := chain.GetTd(head.Hash(), head.NumberU64()) + config.TerminalTotalDifficulty = ttd + signer = types.LatestSigner(gspec.Config) + + merger.ReachTTD() + merger.FinalizePoS() + + var ( + emptyBlob = kzg4844.Blob{} + emptyBlobCommit, _ = kzg4844.BlobToCommitment(emptyBlob) + emptyBlobVHash = kzg4844.CalcBlobHashV1(sha256.New(), &emptyBlobCommit) + ) + + postBlocks, _ := core.GenerateChain(gspec.Config, head, engine, genDb, 6+1, func(i int, b *core.BlockGen) { + b.SetPoS() + + 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{}, + } + b.AddTx(types.MustSignNewTx(key, signer, txdata)) + + // put more blobs in each new block + for j := 0; j < i && j < 6; j++ { + blobTx := &types.BlobTx{ + ChainID: uint256.MustFromBig(gspec.Config.ChainID), + Nonce: b.TxNonce(blobSigners[j].addr), + To: common.Address{}, + Gas: 30000, + GasFeeCap: uint256.NewInt(100 * params.GWei), + GasTipCap: uint256.NewInt(uint64(i+1) * params.GWei), + Data: []byte{}, + BlobFeeCap: uint256.NewInt(1), + BlobHashes: []common.Hash{emptyBlobVHash}, + Value: uint256.NewInt(100), + Sidecar: nil, + } + b.AddTx(types.MustSignNewTx(blobSigners[j].key, signer, blobTx)) + } + }) + + if i, err := chain.InsertChain(postBlocks); err != nil { + panic(fmt.Errorf("error inserting block %d: %w", i, err)) + } + } + return &testBackend{chain: chain, pending: pending} } @@ -201,7 +289,7 @@ func TestSuggestTipCap(t *testing.T) { {big.NewInt(33), big.NewInt(params.GWei * int64(30))}, // Fork point in the future } for _, c := range cases { - backend := newTestBackend(t, c.fork, false) + backend := newTestBackend(t, c.fork, false, false) oracle := NewOracle(backend, config) // The gas price sampled is: 32G, 31G, 30G, 29G, 28G, 27G diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index a09cbb62917a..fe931e0643d1 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -26,6 +26,9 @@ import ( "time" "github.com/davecgh/go-spew/spew" + "github.com/holiman/uint256" + "github.com/tyler-smith/go-bip39" + "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/accounts/keystore" "github.com/ethereum/go-ethereum/accounts/scwallet" @@ -48,8 +51,6 @@ import ( "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/trie" - "github.com/holiman/uint256" - "github.com/tyler-smith/go-bip39" ) // estimateGasErrorRatio is the amount of overestimation eth_estimateGas is @@ -90,15 +91,17 @@ func (s *EthereumAPI) MaxPriorityFeePerGas(ctx context.Context) (*hexutil.Big, e } type feeHistoryResult struct { - OldestBlock *hexutil.Big `json:"oldestBlock"` - Reward [][]*hexutil.Big `json:"reward,omitempty"` - BaseFee []*hexutil.Big `json:"baseFeePerGas,omitempty"` - GasUsedRatio []float64 `json:"gasUsedRatio"` + OldestBlock *hexutil.Big `json:"oldestBlock"` + Reward [][]*hexutil.Big `json:"reward,omitempty"` + BaseFee []*hexutil.Big `json:"baseFeePerGas,omitempty"` + GasUsedRatio []float64 `json:"gasUsedRatio"` + BlobBaseFee []*hexutil.Big `json:"baseFeePerBlobGas,omitempty"` + BlobGasUsedRatio []float64 `json:"blobGasUsedRatio,omitempty"` } // FeeHistory returns the fee market history. func (s *EthereumAPI) FeeHistory(ctx context.Context, blockCount math.HexOrDecimal64, lastBlock rpc.BlockNumber, rewardPercentiles []float64) (*feeHistoryResult, error) { - oldest, reward, baseFee, gasUsed, err := s.b.FeeHistory(ctx, uint64(blockCount), lastBlock, rewardPercentiles) + oldest, reward, baseFee, gasUsed, blobBaseFee, blobGasUsed, err := s.b.FeeHistory(ctx, uint64(blockCount), lastBlock, rewardPercentiles) if err != nil { return nil, err } @@ -121,6 +124,15 @@ func (s *EthereumAPI) FeeHistory(ctx context.Context, blockCount math.HexOrDecim results.BaseFee[i] = (*hexutil.Big)(v) } } + if blobBaseFee != nil { + results.BlobBaseFee = make([]*hexutil.Big, len(blobBaseFee)) + for i, v := range blobBaseFee { + results.BlobBaseFee[i] = (*hexutil.Big)(v) + } + } + if blobGasUsed != nil { + results.BlobGasUsedRatio = blobGasUsed + } return results, nil } diff --git a/internal/ethapi/api_test.go b/internal/ethapi/api_test.go index 20fd6f8bcfbf..4ed6c7fd7834 100644 --- a/internal/ethapi/api_test.go +++ b/internal/ethapi/api_test.go @@ -32,6 +32,9 @@ import ( "testing" "time" + "github.com/holiman/uint256" + "github.com/stretchr/testify/require" + "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/accounts/keystore" @@ -53,8 +56,6 @@ import ( "github.com/ethereum/go-ethereum/internal/blocktest" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rpc" - "github.com/holiman/uint256" - "github.com/stretchr/testify/require" ) func testTransactionMarshal(t *testing.T, tests []txData, config *params.ChainConfig) { @@ -468,8 +469,8 @@ func (b testBackend) SyncProgress() ethereum.SyncProgress { return ethereum.Sync func (b testBackend) SuggestGasTipCap(ctx context.Context) (*big.Int, error) { return big.NewInt(0), nil } -func (b testBackend) FeeHistory(ctx context.Context, blockCount uint64, lastBlock rpc.BlockNumber, rewardPercentiles []float64) (*big.Int, [][]*big.Int, []*big.Int, []float64, error) { - return nil, nil, nil, nil, nil +func (b testBackend) FeeHistory(ctx context.Context, blockCount uint64, lastBlock rpc.BlockNumber, rewardPercentiles []float64) (*big.Int, [][]*big.Int, []*big.Int, []float64, []*big.Int, []float64, error) { + return nil, nil, nil, nil, nil, nil, nil } func (b testBackend) BlobBaseFee(ctx context.Context) (*big.Int, error) { return big.NewInt(0), nil } func (b testBackend) ChainDb() ethdb.Database { return b.db } diff --git a/internal/ethapi/transaction_args_test.go b/internal/ethapi/transaction_args_test.go index feb4cc4be334..dd69c15e4f91 100644 --- a/internal/ethapi/transaction_args_test.go +++ b/internal/ethapi/transaction_args_test.go @@ -319,8 +319,8 @@ func (b *backendMock) ChainConfig() *params.ChainConfig { return b.config } // Other methods needed to implement Backend interface. func (b *backendMock) SyncProgress() ethereum.SyncProgress { return ethereum.SyncProgress{} } -func (b *backendMock) FeeHistory(ctx context.Context, blockCount uint64, lastBlock rpc.BlockNumber, rewardPercentiles []float64) (*big.Int, [][]*big.Int, []*big.Int, []float64, error) { - return nil, nil, nil, nil, nil +func (b *backendMock) FeeHistory(ctx context.Context, blockCount uint64, lastBlock rpc.BlockNumber, rewardPercentiles []float64) (*big.Int, [][]*big.Int, []*big.Int, []float64, []*big.Int, []float64, error) { + return nil, nil, nil, nil, nil, nil, nil } func (b *backendMock) BlobBaseFee(ctx context.Context) (*big.Int, error) { return big.NewInt(42), nil From 80a12cab9bfe921f862c2891eb21677510adb700 Mon Sep 17 00:00:00 2001 From: Ryan Schneider Date: Fri, 1 Mar 2024 17:27:08 -0800 Subject: [PATCH 03/11] eth/gasprice: Update FeeHistory comments --- eth/gasprice/feehistory.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/eth/gasprice/feehistory.go b/eth/gasprice/feehistory.go index aafdfe9395e5..96266c5a15d4 100644 --- a/eth/gasprice/feehistory.go +++ b/eth/gasprice/feehistory.go @@ -219,14 +219,16 @@ func (oracle *Oracle) resolveBlockRange(ctx context.Context, reqEnd rpc.BlockNum // 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: +// Five 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 +// - blobBaseFee: the blob base fee per gas in the given block +// - blobGasUsedRatio: blobGasUsed/blobGasLimit 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. +// Note: baseFee and blobBaseFee both include 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, []*big.Int, []float64, error) { if blocks < 1 { return common.Big0, nil, nil, nil, nil, nil, nil // returning with no data and no error means there are no retrievable blocks From 9ec859b73cfd0dfe6279e96d2c50f01154fe909e Mon Sep 17 00:00:00 2001 From: Ryan Schneider Date: Fri, 1 Mar 2024 19:21:36 -0800 Subject: [PATCH 04/11] eth/gasprice: Actually return the "next" blob base fee --- eth/gasprice/feehistory.go | 42 ++++++++++++++++++++++----------- eth/gasprice/feehistory_test.go | 9 ++++--- 2 files changed, 34 insertions(+), 17 deletions(-) diff --git a/eth/gasprice/feehistory.go b/eth/gasprice/feehistory.go index 96266c5a15d4..f098cc5d130d 100644 --- a/eth/gasprice/feehistory.go +++ b/eth/gasprice/feehistory.go @@ -51,10 +51,11 @@ const ( // 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 + blockNumber uint64 + header *types.Header + parentHeader *types.Header + block *types.Block // only set if reward percentiles are requested + receipts types.Receipts // filled by processBlock results processedFees err error @@ -67,11 +68,11 @@ type cacheKey struct { // processedFees contains the results of a processed block. type processedFees struct { - reward []*big.Int - baseFee, nextBaseFee *big.Int - gasUsedRatio float64 - blobGasUsedRatio float64 - blobBaseFee *big.Int + reward []*big.Int + baseFee, nextBaseFee *big.Int + gasUsedRatio float64 + blobGasUsedRatio float64 + blobBaseFee, nextBlobBaseFee *big.Int } // txGasAndReward is sorted in ascending order based on reward @@ -96,13 +97,21 @@ func (oracle *Oracle) processBlock(bf *blockFees, percentiles []float64) { bf.results.gasUsedRatio = float64(bf.header.GasUsed) / float64(bf.header.GasLimit) bf.results.blobGasUsedRatio = 0.0 bf.results.blobBaseFee = new(big.Int) + + getBlobBaseFee := func(h *types.Header) *big.Int { + if h != nil && chainconfig.IsCancun(h.Number, h.Time) { + if excessBlobGas := h.ExcessBlobGas; excessBlobGas != nil { + return eip4844.CalcBlobFee(*excessBlobGas) + } + } + return new(big.Int) + } + bf.results.blobBaseFee = getBlobBaseFee(bf.parentHeader) + bf.results.nextBlobBaseFee = getBlobBaseFee(bf.header) if bf.header != nil && chainconfig.IsCancun(bf.header.Number, bf.header.Time) { if blobGasUsed := bf.header.BlobGasUsed; blobGasUsed != nil { bf.results.blobGasUsedRatio = float64(*blobGasUsed) / params.MaxBlobGasPerBlock } - if excessBlobGas := bf.header.ExcessBlobGas; excessBlobGas != nil { - bf.results.blobBaseFee = eip4844.CalcBlobFee(*excessBlobGas) - } } if len(percentiles) == 0 { // rewards were not requested, return null @@ -281,6 +290,7 @@ func (oracle *Oracle) FeeHistory(ctx context.Context, blocks uint64, unresolvedL if pendingBlock != nil && blockNumber >= pendingBlock.NumberU64() { fees.block, fees.receipts = pendingBlock, pendingReceipts fees.header = fees.block.Header() + fees.parentHeader, fees.err = oracle.backend.HeaderByNumber(ctx, rpc.BlockNumber(pendingBlock.NumberU64()-1)) oracle.processBlock(fees, rewardPercentiles) results <- fees } else { @@ -300,6 +310,9 @@ func (oracle *Oracle) FeeHistory(ctx context.Context, blocks uint64, unresolvedL fees.header, fees.err = oracle.backend.HeaderByNumber(ctx, rpc.BlockNumber(blockNumber)) } if fees.header != nil && fees.err == nil { + if blockNumber > 0 { + fees.parentHeader, fees.err = oracle.backend.HeaderByNumber(ctx, rpc.BlockNumber(blockNumber-1)) + } oracle.processBlock(fees, rewardPercentiles) if fees.err == nil { oracle.historyCache.Add(cacheKey, fees.results) @@ -317,7 +330,7 @@ func (oracle *Oracle) FeeHistory(ctx context.Context, blocks uint64, unresolvedL baseFee = make([]*big.Int, blocks+1) gasUsedRatio = make([]float64, blocks) blobGasUsedRatio = make([]float64, blocks) - blobBaseFee = make([]*big.Int, blocks) + blobBaseFee = make([]*big.Int, blocks+1) firstMissing = blocks ) for ; blocks > 0; blocks-- { @@ -328,7 +341,7 @@ func (oracle *Oracle) FeeHistory(ctx context.Context, blocks uint64, unresolvedL 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 - blobGasUsedRatio[i], blobBaseFee[i] = fees.results.blobGasUsedRatio, fees.results.blobBaseFee + blobGasUsedRatio[i], blobBaseFee[i], blobBaseFee[i+1] = fees.results.blobGasUsedRatio, fees.results.blobBaseFee, fees.results.nextBlobBaseFee } else { // getting no block and no error means we are requesting into the future (might happen because of a reorg) if i < firstMissing { @@ -345,5 +358,6 @@ func (oracle *Oracle) FeeHistory(ctx context.Context, blocks uint64, unresolvedL reward = nil } baseFee, gasUsedRatio = baseFee[:firstMissing+1], gasUsedRatio[:firstMissing] + blobBaseFee, blobGasUsedRatio = blobBaseFee[:firstMissing+1], blobGasUsedRatio[:firstMissing] return new(big.Int).SetUint64(oldestBlock), reward, baseFee, gasUsedRatio, blobBaseFee, blobGasUsedRatio, nil } diff --git a/eth/gasprice/feehistory_test.go b/eth/gasprice/feehistory_test.go index cd1c13ceb1f6..42a2290b17e8 100644 --- a/eth/gasprice/feehistory_test.go +++ b/eth/gasprice/feehistory_test.go @@ -61,7 +61,7 @@ func TestFeeHistory(t *testing.T) { backend := newTestBackend(t, big.NewInt(16), c.pending, true) oracle := NewOracle(backend, config) - first, reward, baseFee, ratio, blobBaseFee, blobUsed, err := oracle.FeeHistory(context.Background(), c.count, c.last, c.percent) + first, reward, baseFee, ratio, blobBaseFee, blobRatio, err := oracle.FeeHistory(context.Background(), c.count, c.last, c.percent) backend.teardown() expReward := c.expCount if len(c.percent) == 0 { @@ -84,8 +84,11 @@ func TestFeeHistory(t *testing.T) { if len(ratio) != c.expCount { t.Fatalf("Test case %d: gasUsedRatio array length mismatch, want %d, got %d", i, c.expCount, len(ratio)) } - if len(blobBaseFee) != len(blobUsed) || len(blobBaseFee) != c.expCount { - t.Fatalf("Test case %d: blob arrays length mismatch, want %d, got %d and %d", i, c.expCount, len(blobBaseFee), len(blobUsed)) + if len(blobRatio) != c.expCount { + t.Fatalf("Test case %d: blobGasUsedRatio array length mismatch, want %d, got %d", i, c.expCount, len(blobRatio)) + } + if len(blobBaseFee) != len(baseFee) { + t.Fatalf("Test case %d: blobBaseFee array length mismatch, want %d, got %d", i, len(baseFee), len(blobBaseFee)) } if err != c.expErr && !errors.Is(err, c.expErr) { t.Fatalf("Test case %d: error mismatch, want %v, got %v", i, c.expErr, err) From 7305023635f5961287a32f792189d93313272ff4 Mon Sep 17 00:00:00 2001 From: lightclient Date: Wed, 6 Mar 2024 07:29:05 -0700 Subject: [PATCH 05/11] eth/gasprice: fix blob base fee ordering, simplify tests --- eth/gasprice/feehistory.go | 59 +++++++++--------- eth/gasprice/feehistory_test.go | 2 +- eth/gasprice/gasprice_test.go | 106 ++++++++++---------------------- 3 files changed, 64 insertions(+), 103 deletions(-) diff --git a/eth/gasprice/feehistory.go b/eth/gasprice/feehistory.go index f098cc5d130d..ab9badf176bb 100644 --- a/eth/gasprice/feehistory.go +++ b/eth/gasprice/feehistory.go @@ -51,11 +51,10 @@ const ( // blockFees represents a single block for processing type blockFees struct { // set by the caller - blockNumber uint64 - header *types.Header - parentHeader *types.Header - block *types.Block // only set if reward percentiles are requested - receipts types.Receipts + 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 @@ -85,34 +84,31 @@ type txGasAndReward struct { // 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() + config := oracle.backend.ChainConfig() + + // Fill in base fee and next base fee. 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) + if config.IsLondon(big.NewInt(int64(bf.blockNumber + 1))) { + bf.results.nextBaseFee = eip1559.CalcBaseFee(config, bf.header) } else { bf.results.nextBaseFee = new(big.Int) } - bf.results.gasUsedRatio = float64(bf.header.GasUsed) / float64(bf.header.GasLimit) - bf.results.blobGasUsedRatio = 0.0 - bf.results.blobBaseFee = new(big.Int) - - getBlobBaseFee := func(h *types.Header) *big.Int { - if h != nil && chainconfig.IsCancun(h.Number, h.Time) { - if excessBlobGas := h.ExcessBlobGas; excessBlobGas != nil { - return eip4844.CalcBlobFee(*excessBlobGas) - } - } - return new(big.Int) + // Fill in blob base fee and next blob base fee. + if excessBlobGas := bf.header.ExcessBlobGas; excessBlobGas != nil { + bf.results.blobBaseFee = eip4844.CalcBlobFee(*excessBlobGas) + bf.results.nextBlobBaseFee = eip4844.CalcBlobFee(eip4844.CalcExcessBlobGas(*excessBlobGas, *bf.header.BlobGasUsed)) + } else { + bf.results.blobBaseFee = new(big.Int) + bf.results.nextBlobBaseFee = new(big.Int) } - bf.results.blobBaseFee = getBlobBaseFee(bf.parentHeader) - bf.results.nextBlobBaseFee = getBlobBaseFee(bf.header) - if bf.header != nil && chainconfig.IsCancun(bf.header.Number, bf.header.Time) { - if blobGasUsed := bf.header.BlobGasUsed; blobGasUsed != nil { - bf.results.blobGasUsedRatio = float64(*blobGasUsed) / params.MaxBlobGasPerBlock - } + // Compute gas used ratio for normal and blob gas. + bf.results.gasUsedRatio = float64(bf.header.GasUsed) / float64(bf.header.GasLimit) + if blobGasUsed := bf.header.BlobGasUsed; blobGasUsed != nil { + bf.results.blobGasUsedRatio = float64(*blobGasUsed) / params.MaxBlobGasPerBlock } + if len(percentiles) == 0 { // rewards were not requested, return null return @@ -153,6 +149,15 @@ func (oracle *Oracle) processBlock(bf *blockFees, percentiles []float64) { } } +// getBlobBaseFee is a helper function which computes and returns the blob base +// fee if excessBlobGas exists. +func getBlobBaseFee(h *types.Header) *big.Int { + if excessBlobGas := h.ExcessBlobGas; excessBlobGas != nil { + return eip4844.CalcBlobFee(*excessBlobGas) + } + return new(big.Int) +} + // 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. @@ -290,7 +295,6 @@ func (oracle *Oracle) FeeHistory(ctx context.Context, blocks uint64, unresolvedL if pendingBlock != nil && blockNumber >= pendingBlock.NumberU64() { fees.block, fees.receipts = pendingBlock, pendingReceipts fees.header = fees.block.Header() - fees.parentHeader, fees.err = oracle.backend.HeaderByNumber(ctx, rpc.BlockNumber(pendingBlock.NumberU64()-1)) oracle.processBlock(fees, rewardPercentiles) results <- fees } else { @@ -310,9 +314,6 @@ func (oracle *Oracle) FeeHistory(ctx context.Context, blocks uint64, unresolvedL fees.header, fees.err = oracle.backend.HeaderByNumber(ctx, rpc.BlockNumber(blockNumber)) } if fees.header != nil && fees.err == nil { - if blockNumber > 0 { - fees.parentHeader, fees.err = oracle.backend.HeaderByNumber(ctx, rpc.BlockNumber(blockNumber-1)) - } oracle.processBlock(fees, rewardPercentiles) if fees.err == nil { oracle.historyCache.Add(cacheKey, fees.results) diff --git a/eth/gasprice/feehistory_test.go b/eth/gasprice/feehistory_test.go index 42a2290b17e8..3d426db46fef 100644 --- a/eth/gasprice/feehistory_test.go +++ b/eth/gasprice/feehistory_test.go @@ -58,7 +58,7 @@ func TestFeeHistory(t *testing.T) { MaxHeaderHistory: c.maxHeader, MaxBlockHistory: c.maxBlock, } - backend := newTestBackend(t, big.NewInt(16), c.pending, true) + backend := newTestBackend(t, big.NewInt(16), big.NewInt(28), c.pending) oracle := NewOracle(backend, config) first, reward, baseFee, ratio, blobBaseFee, blobRatio, err := oracle.FeeHistory(context.Background(), c.count, c.last, c.percent) diff --git a/eth/gasprice/gasprice_test.go b/eth/gasprice/gasprice_test.go index d1cf0095bdaf..cbf2ce4c4935 100644 --- a/eth/gasprice/gasprice_test.go +++ b/eth/gasprice/gasprice_test.go @@ -18,7 +18,6 @@ package gasprice import ( "context" - "crypto/ecdsa" "crypto/sha256" "fmt" "math" @@ -32,7 +31,6 @@ import ( "github.com/ethereum/go-ethereum/consensus/beacon" "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/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" @@ -129,7 +127,10 @@ func (b *testBackend) teardown() { // 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, addCancunBlocks bool) *testBackend { +func newTestBackend(t *testing.T, londonBlock *big.Int, cancunBlock *big.Int, pending bool) *testBackend { + if londonBlock != nil && cancunBlock != nil && londonBlock.Cmp(cancunBlock) == 1 { + panic("cannot define test backend with cancun before london") + } var ( key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") addr = crypto.PubkeyToAddress(key.PublicKey) @@ -138,32 +139,29 @@ func newTestBackend(t *testing.T, londonBlock *big.Int, pending bool, addCancunB Config: &config, Alloc: types.GenesisAlloc{addr: {Balance: big.NewInt(math.MaxInt64)}}, } - signer = types.LatestSigner(gspec.Config) - blobSigners = [6]struct { - key *ecdsa.PrivateKey - addr common.Address - }{} + signer = types.LatestSigner(gspec.Config) + + // Compute empty blob hash. + emptyBlob = kzg4844.Blob{} + emptyBlobCommit, _ = kzg4844.BlobToCommitment(emptyBlob) + emptyBlobVHash = kzg4844.CalcBlobHashV1(sha256.New(), &emptyBlobCommit) ) config.LondonBlock = londonBlock config.ArrowGlacierBlock = londonBlock config.GrayGlacierBlock = londonBlock var engine consensus.Engine = beacon.New(ethash.NewFaker()) - td := int(params.GenesisDifficulty.Uint64()) + td := params.GenesisDifficulty.Uint64() - if addCancunBlocks { - for i := range blobSigners { - key, _ := crypto.GenerateKey() - addr := crypto.PubkeyToAddress(key.PublicKey) - blobSigners[i].key = key - blobSigners[i].addr = addr - gspec.Alloc[addr] = types.Account{Balance: big.NewInt(math.MaxInt64)} - } + if cancunBlock != nil { + ts := gspec.Timestamp + cancunBlock.Uint64()*10 // fixed 10 sec block time in blockgen + config.ShanghaiTime = &ts + config.CancunTime = &ts + signer = types.LatestSigner(gspec.Config) } // Generate testing blocks - genDb, blocks, _ := core.GenerateChainWithGenesis(gspec, engine, testHead+1, func(i int, b *core.BlockGen) { + db, blocks, _ := core.GenerateChainWithGenesis(gspec, engine, testHead+1, func(i int, b *core.BlockGen) { b.SetCoinbase(common.Address{1}) - td += int(b.Difficulty().Uint64()) var txdata types.TxData if londonBlock != nil && b.Number().Cmp(londonBlock) >= 0 { @@ -187,61 +185,15 @@ func newTestBackend(t *testing.T, londonBlock *big.Int, pending bool, addCancunB } } b.AddTx(types.MustSignNewTx(key, signer, txdata)) - }) - // Construct testing chain - db := rawdb.NewMemoryDatabase() - merger := consensus.NewMerger(db) - chain, err := core.NewBlockChain(db, &core.CacheConfig{TrieCleanNoPrefetch: true}, gspec, nil, engine, vm.Config{}, nil, nil) - if err != nil { - t.Fatalf("Failed to create local chain, %v", err) - } - if i, err := chain.InsertChain(blocks); err != nil { - panic(fmt.Errorf("error inserting block %d: %w", i, err)) - } - chain.SetFinalized(chain.GetBlockByNumber(25).Header()) - chain.SetSafe(chain.GetBlockByNumber(25).Header()) - - if addCancunBlocks { - if err := chain.SetHead(26); err != nil { - panic(err) - } - - head := chain.GetBlockByNumber(26) - ts := head.Time() - config.ShanghaiTime = &ts - config.CancunTime = &ts - ttd := chain.GetTd(head.Hash(), head.NumberU64()) - config.TerminalTotalDifficulty = ttd - signer = types.LatestSigner(gspec.Config) - - merger.ReachTTD() - merger.FinalizePoS() - - var ( - emptyBlob = kzg4844.Blob{} - emptyBlobCommit, _ = kzg4844.BlobToCommitment(emptyBlob) - emptyBlobVHash = kzg4844.CalcBlobHashV1(sha256.New(), &emptyBlobCommit) - ) - postBlocks, _ := core.GenerateChain(gspec.Config, head, engine, genDb, 6+1, func(i int, b *core.BlockGen) { + if cancunBlock != nil && b.Number().Cmp(cancunBlock) >= 0 { b.SetPoS() - 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{}, - } - b.AddTx(types.MustSignNewTx(key, signer, txdata)) - // put more blobs in each new block for j := 0; j < i && j < 6; j++ { blobTx := &types.BlobTx{ ChainID: uint256.MustFromBig(gspec.Config.ChainID), - Nonce: b.TxNonce(blobSigners[j].addr), + Nonce: b.TxNonce(addr), To: common.Address{}, Gas: 30000, GasFeeCap: uint256.NewInt(100 * params.GWei), @@ -252,14 +204,22 @@ func newTestBackend(t *testing.T, londonBlock *big.Int, pending bool, addCancunB Value: uint256.NewInt(100), Sidecar: nil, } - b.AddTx(types.MustSignNewTx(blobSigners[j].key, signer, blobTx)) + b.AddTx(types.MustSignNewTx(key, signer, blobTx)) } - }) - - if i, err := chain.InsertChain(postBlocks); err != nil { - panic(fmt.Errorf("error inserting block %d: %w", i, err)) } + td += b.Difficulty().Uint64() + }) + // Construct testing chain + gspec.Config.TerminalTotalDifficulty = new(big.Int).SetUint64(td) + chain, err := core.NewBlockChain(db, &core.CacheConfig{TrieCleanNoPrefetch: true}, gspec, nil, engine, vm.Config{}, nil, nil) + if err != nil { + t.Fatalf("Failed to create local chain, %v", err) } + if i, err := chain.InsertChain(blocks); err != nil { + panic(fmt.Errorf("error inserting block %d: %w", i, err)) + } + chain.SetFinalized(chain.GetBlockByNumber(25).Header()) + chain.SetSafe(chain.GetBlockByNumber(25).Header()) return &testBackend{chain: chain, pending: pending} } @@ -289,7 +249,7 @@ func TestSuggestTipCap(t *testing.T) { {big.NewInt(33), big.NewInt(params.GWei * int64(30))}, // Fork point in the future } for _, c := range cases { - backend := newTestBackend(t, c.fork, false, false) + backend := newTestBackend(t, c.fork, nil, false) oracle := NewOracle(backend, config) // The gas price sampled is: 32G, 31G, 30G, 29G, 28G, 27G From 5fb6b036a504725a932728773da92b55bb160901 Mon Sep 17 00:00:00 2001 From: lightclient Date: Thu, 7 Mar 2024 06:59:25 -0700 Subject: [PATCH 06/11] internal/jsre: add eth_blobBaseFee to console --- internal/jsre/deps/web3.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/internal/jsre/deps/web3.js b/internal/jsre/deps/web3.js index 4196cb8db0ee..b0fd8fcafc3e 100644 --- a/internal/jsre/deps/web3.js +++ b/internal/jsre/deps/web3.js @@ -5519,6 +5519,11 @@ var properties = function () { getter: 'eth_gasPrice', outputFormatter: formatters.outputBigNumberFormatter }), + new Property({ + name: 'blobBaseFee', + getter: 'eth_blobBaseFee', + outputFormatter: formatters.outputBigNumberFormatter + }), new Property({ name: 'accounts', getter: 'eth_accounts' From 396424c79529ed0e1abe1ca7bafa5ee2b882e6bd Mon Sep 17 00:00:00 2001 From: lightclient Date: Thu, 7 Mar 2024 07:23:13 -0700 Subject: [PATCH 07/11] internal/ethapi: add doc for blob base fee method --- internal/ethapi/api.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index fe931e0643d1..7b303024de2d 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -136,6 +136,7 @@ func (s *EthereumAPI) FeeHistory(ctx context.Context, blockCount math.HexOrDecim return results, nil } +// BlobBaseFee returns the base fee for blob gas at the current head. func (s *EthereumAPI) BlobBaseFee(ctx context.Context) (*hexutil.Big, error) { blobBaseFee, err := s.b.BlobBaseFee(ctx) return (*hexutil.Big)(blobBaseFee), err From d10789959812da7592d42970a540ba85ee7680f9 Mon Sep 17 00:00:00 2001 From: lightclient Date: Thu, 7 Mar 2024 07:31:53 -0700 Subject: [PATCH 08/11] eth,internal/ethapi: remove error return value from blob base fee getter --- eth/api_backend.go | 10 ++++------ internal/ethapi/api.go | 5 ++--- internal/ethapi/api_test.go | 18 +++++++++--------- internal/ethapi/backend.go | 2 +- internal/ethapi/transaction_args_test.go | 5 ++--- 5 files changed, 18 insertions(+), 22 deletions(-) diff --git a/eth/api_backend.go b/eth/api_backend.go index 648750cc33ed..8a9898b956f3 100644 --- a/eth/api_backend.go +++ b/eth/api_backend.go @@ -366,13 +366,11 @@ func (b *EthAPIBackend) FeeHistory(ctx context.Context, blockCount uint64, lastB return b.gpo.FeeHistory(ctx, blockCount, lastBlock, rewardPercentiles) } -func (b *EthAPIBackend) BlobBaseFee(ctx context.Context) (*big.Int, error) { - if excessBlobGas := b.CurrentHeader().ExcessBlobGas; excessBlobGas != nil { - blobBaseFee := eip4844.CalcBlobFee(*excessBlobGas) - return blobBaseFee, nil - } else { - return nil, nil +func (b *EthAPIBackend) BlobBaseFee(ctx context.Context) *big.Int { + if excess := b.CurrentHeader().ExcessBlobGas; excess != nil { + return eip4844.CalcBlobFee(*excess) } + return nil } func (b *EthAPIBackend) ChainDb() ethdb.Database { diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 7b303024de2d..8b15d211d1c4 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -137,9 +137,8 @@ func (s *EthereumAPI) FeeHistory(ctx context.Context, blockCount math.HexOrDecim } // BlobBaseFee returns the base fee for blob gas at the current head. -func (s *EthereumAPI) BlobBaseFee(ctx context.Context) (*hexutil.Big, error) { - blobBaseFee, err := s.b.BlobBaseFee(ctx) - return (*hexutil.Big)(blobBaseFee), err +func (s *EthereumAPI) BlobBaseFee(ctx context.Context) *hexutil.Big { + return (*hexutil.Big)(s.b.BlobBaseFee(ctx)) } // Syncing returns false in case the node is currently not syncing with the network. It can be up-to-date or has not diff --git a/internal/ethapi/api_test.go b/internal/ethapi/api_test.go index 4ed6c7fd7834..1f62d0c6bde3 100644 --- a/internal/ethapi/api_test.go +++ b/internal/ethapi/api_test.go @@ -472,15 +472,15 @@ func (b testBackend) SuggestGasTipCap(ctx context.Context) (*big.Int, error) { func (b testBackend) FeeHistory(ctx context.Context, blockCount uint64, lastBlock rpc.BlockNumber, rewardPercentiles []float64) (*big.Int, [][]*big.Int, []*big.Int, []float64, []*big.Int, []float64, error) { return nil, nil, nil, nil, nil, nil, nil } -func (b testBackend) BlobBaseFee(ctx context.Context) (*big.Int, error) { return big.NewInt(0), nil } -func (b testBackend) ChainDb() ethdb.Database { return b.db } -func (b testBackend) AccountManager() *accounts.Manager { return b.accman } -func (b testBackend) ExtRPCEnabled() bool { return false } -func (b testBackend) RPCGasCap() uint64 { return 10000000 } -func (b testBackend) RPCEVMTimeout() time.Duration { return time.Second } -func (b testBackend) RPCTxFeeCap() float64 { return 0 } -func (b testBackend) UnprotectedAllowed() bool { return false } -func (b testBackend) SetHead(number uint64) {} +func (b testBackend) BlobBaseFee(ctx context.Context) *big.Int { return new(big.Int) } +func (b testBackend) ChainDb() ethdb.Database { return b.db } +func (b testBackend) AccountManager() *accounts.Manager { return b.accman } +func (b testBackend) ExtRPCEnabled() bool { return false } +func (b testBackend) RPCGasCap() uint64 { return 10000000 } +func (b testBackend) RPCEVMTimeout() time.Duration { return time.Second } +func (b testBackend) RPCTxFeeCap() float64 { return 0 } +func (b testBackend) UnprotectedAllowed() bool { return false } +func (b testBackend) SetHead(number uint64) {} func (b testBackend) HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error) { if number == rpc.LatestBlockNumber { return b.chain.CurrentBlock(), nil diff --git a/internal/ethapi/backend.go b/internal/ethapi/backend.go index f4a3582339c8..2a45ba09210f 100644 --- a/internal/ethapi/backend.go +++ b/internal/ethapi/backend.go @@ -45,7 +45,7 @@ type Backend interface { SuggestGasTipCap(ctx context.Context) (*big.Int, error) FeeHistory(ctx context.Context, blockCount uint64, lastBlock rpc.BlockNumber, rewardPercentiles []float64) (*big.Int, [][]*big.Int, []*big.Int, []float64, []*big.Int, []float64, error) - BlobBaseFee(ctx context.Context) (*big.Int, error) + BlobBaseFee(ctx context.Context) *big.Int ChainDb() ethdb.Database AccountManager() *accounts.Manager ExtRPCEnabled() bool diff --git a/internal/ethapi/transaction_args_test.go b/internal/ethapi/transaction_args_test.go index dd69c15e4f91..6750fc07a944 100644 --- a/internal/ethapi/transaction_args_test.go +++ b/internal/ethapi/transaction_args_test.go @@ -314,6 +314,8 @@ func (b *backendMock) setFork(fork string) error { func (b *backendMock) SuggestGasTipCap(ctx context.Context) (*big.Int, error) { return big.NewInt(42), nil } +func (b *backendMock) BlobBaseFee(ctx context.Context) *big.Int { return big.NewInt(42) } + func (b *backendMock) CurrentHeader() *types.Header { return b.current } func (b *backendMock) ChainConfig() *params.ChainConfig { return b.config } @@ -322,9 +324,6 @@ func (b *backendMock) SyncProgress() ethereum.SyncProgress { return ethereum.Syn func (b *backendMock) FeeHistory(ctx context.Context, blockCount uint64, lastBlock rpc.BlockNumber, rewardPercentiles []float64) (*big.Int, [][]*big.Int, []*big.Int, []float64, []*big.Int, []float64, error) { return nil, nil, nil, nil, nil, nil, nil } -func (b *backendMock) BlobBaseFee(ctx context.Context) (*big.Int, error) { - return big.NewInt(42), nil -} func (b *backendMock) ChainDb() ethdb.Database { return nil } func (b *backendMock) AccountManager() *accounts.Manager { return nil } func (b *backendMock) ExtRPCEnabled() bool { return false } From 429ed384dd5b724faf6d2ed62f7644f72fb0fb03 Mon Sep 17 00:00:00 2001 From: lightclient Date: Mon, 11 Mar 2024 10:15:27 -0600 Subject: [PATCH 09/11] eth/gasprice: rm unused function --- eth/gasprice/feehistory.go | 9 --------- 1 file changed, 9 deletions(-) diff --git a/eth/gasprice/feehistory.go b/eth/gasprice/feehistory.go index ab9badf176bb..f807cfdc4d35 100644 --- a/eth/gasprice/feehistory.go +++ b/eth/gasprice/feehistory.go @@ -149,15 +149,6 @@ func (oracle *Oracle) processBlock(bf *blockFees, percentiles []float64) { } } -// getBlobBaseFee is a helper function which computes and returns the blob base -// fee if excessBlobGas exists. -func getBlobBaseFee(h *types.Header) *big.Int { - if excessBlobGas := h.ExcessBlobGas; excessBlobGas != nil { - return eip4844.CalcBlobFee(*excessBlobGas) - } - return new(big.Int) -} - // 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. From 87b9af8c768b4eab2d0e0550d90d5fb91b6c350a Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Mon, 22 Apr 2024 11:00:31 +0200 Subject: [PATCH 10/11] eth/gasprice: fix import --- eth/gasprice/feehistory.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/eth/gasprice/feehistory.go b/eth/gasprice/feehistory.go index f807cfdc4d35..0410ae6b2de3 100644 --- a/eth/gasprice/feehistory.go +++ b/eth/gasprice/feehistory.go @@ -26,8 +26,6 @@ import ( "slices" "sync/atomic" - "golang.org/x/exp/slices" - "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/consensus/misc/eip1559" "github.com/ethereum/go-ethereum/consensus/misc/eip4844" From 13bde9bb8572898abab200be7bd1616f5650257e Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Mon, 22 Apr 2024 11:04:14 +0200 Subject: [PATCH 11/11] eth/gasprice: fix issue in test --- eth/gasprice/gasprice_test.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/eth/gasprice/gasprice_test.go b/eth/gasprice/gasprice_test.go index cbf2ce4c4935..b22e75666fb9 100644 --- a/eth/gasprice/gasprice_test.go +++ b/eth/gasprice/gasprice_test.go @@ -24,8 +24,6 @@ import ( "math/big" "testing" - "github.com/holiman/uint256" - "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/consensus/beacon" @@ -39,6 +37,7 @@ import ( "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rpc" + "github.com/holiman/uint256" ) const testHead = 32 @@ -143,7 +142,7 @@ func newTestBackend(t *testing.T, londonBlock *big.Int, cancunBlock *big.Int, pe // Compute empty blob hash. emptyBlob = kzg4844.Blob{} - emptyBlobCommit, _ = kzg4844.BlobToCommitment(emptyBlob) + emptyBlobCommit, _ = kzg4844.BlobToCommitment(&emptyBlob) emptyBlobVHash = kzg4844.CalcBlobHashV1(sha256.New(), &emptyBlobCommit) ) config.LondonBlock = londonBlock