From 206573e3a51241e1bb1fbd01f7bc909df0a55408 Mon Sep 17 00:00:00 2001 From: ChanHongMing <91173675+ChanHongMing@users.noreply.github.com> Date: Wed, 24 Dec 2025 02:02:21 +0800 Subject: [PATCH 1/6] Verify BlobScheduleConfig before calling CalcBlobFee --- core/blockchain.go | 2 +- core/blockchain_reader.go | 2 +- core/chain_makers.go | 4 ++-- core/evm.go | 8 ++++++-- core/txpool/blobpool/blobpool.go | 2 +- eth/api_backend.go | 2 +- eth/tracers/live/supply.go | 2 +- internal/ethapi/transaction_args.go | 2 +- 8 files changed, 14 insertions(+), 10 deletions(-) diff --git a/core/blockchain.go b/core/blockchain.go index 71493f09f1..058ae4f7f1 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -3684,7 +3684,7 @@ func (bc *BlockChain) recoverAncestors(block *types.Block, makeWitness bool) (co // processing of a block. These logs are later announced as deleted or reborn. func (bc *BlockChain) collectLogs(b *types.Block, removed bool) []*types.Log { var blobGasPrice *big.Int - if b.ExcessBlobGas() != nil { + if b.ExcessBlobGas() != nil && bc.chainConfig.BlobScheduleConfig != nil { blobGasPrice = eip4844.CalcBlobFee(bc.chainConfig, b.Header()) } receipts := rawdb.ReadRawReceipts(bc.db, b.Hash(), b.NumberU64()) diff --git a/core/blockchain_reader.go b/core/blockchain_reader.go index 5bc13ec425..4fc9fc2c2b 100644 --- a/core/blockchain_reader.go +++ b/core/blockchain_reader.go @@ -266,7 +266,7 @@ func (bc *BlockChain) GetCanonicalReceipt(tx *types.Transaction, blockHash commo return nil, fmt.Errorf("block header is not found, %d, %x", blockNumber, blockHash) } var blobGasPrice *big.Int - if header.ExcessBlobGas != nil { + if header.ExcessBlobGas != nil && bc.chainConfig.BlobScheduleConfig != nil { blobGasPrice = eip4844.CalcBlobFee(bc.chainConfig, header) } receipt, ctx, err := rawdb.ReadCanonicalRawReceipt(bc.db, blockHash, blockNumber, txIndex) diff --git a/core/chain_makers.go b/core/chain_makers.go index 06065baf35..d64b118ef7 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -464,7 +464,7 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse txs = txs[:len(receipts)] } var blobGasPrice *big.Int - if block.ExcessBlobGas() != nil { + if block.ExcessBlobGas() != nil && cm.config.BlobScheduleConfig != nil { blobGasPrice = eip4844.CalcBlobFee(cm.config, block.Header()) } if err := receipts.DeriveFields(config, block.Hash(), block.NumberU64(), block.Time(), block.BaseFee(), blobGasPrice, txs); err != nil { @@ -577,7 +577,7 @@ func GenerateVerkleChain(config *params.ChainConfig, parent *types.Block, engine txs = txs[:len(receipts)] } var blobGasPrice *big.Int - if block.ExcessBlobGas() != nil { + if block.ExcessBlobGas() != nil && cm.config.BlobScheduleConfig != nil { blobGasPrice = eip4844.CalcBlobFee(cm.config, block.Header()) } if err := receipts.DeriveFields(config, block.Hash(), block.NumberU64(), block.Time(), block.BaseFee(), blobGasPrice, txs); err != nil { diff --git a/core/evm.go b/core/evm.go index b13cd41f82..15653874ef 100644 --- a/core/evm.go +++ b/core/evm.go @@ -71,8 +71,12 @@ func NewEVMBlockContext(header *types.Header, chain ChainContext, author *common if header.BaseFee != nil { baseFee = new(big.Int).Set(header.BaseFee) } - if header.ExcessBlobGas != nil { - blobBaseFee = eip4844.CalcBlobFee(chain.Config(), header) + // Only calculate blob fee if the fork actually supports blob transactions (Cancun or later) + // and the chain has a BlobScheduleConfig configured + if header.ExcessBlobGas != nil && chain.Config().BlobScheduleConfig != nil { + if chain.Config().IsCancun(header.Number) || chain.Config().IsPrague(header.Number) || chain.Config().IsOsaka(header.Number) { + blobBaseFee = eip4844.CalcBlobFee(chain.Config(), header) + } } if header.Difficulty.Sign() == 0 { random = &header.MixDigest diff --git a/core/txpool/blobpool/blobpool.go b/core/txpool/blobpool/blobpool.go index c7268e6ce2..f370948adc 100644 --- a/core/txpool/blobpool/blobpool.go +++ b/core/txpool/blobpool/blobpool.go @@ -424,7 +424,7 @@ func (p *BlobPool) Init(gasTip uint64, head *types.Header, reserver txpool.Reser basefee = uint256.MustFromBig(eip1559.CalcBaseFee(p.chain.Config(), p.head)) blobfee = uint256.NewInt(params.BlobTxMinBlobGasprice) ) - if p.head.ExcessBlobGas != nil { + if p.head.ExcessBlobGas != nil && p.chain.Config().BlobScheduleConfig != nil { blobfee = uint256.MustFromBig(eip4844.CalcBlobFee(p.chain.Config(), p.head)) } p.evict = newPriceHeap(basefee, blobfee, p.index) diff --git a/eth/api_backend.go b/eth/api_backend.go index 8dfcc383ca..d0ca940419 100644 --- a/eth/api_backend.go +++ b/eth/api_backend.go @@ -511,7 +511,7 @@ func (b *EthAPIBackend) FeeHistory(ctx context.Context, blockCount uint64, lastB } func (b *EthAPIBackend) BlobBaseFee(ctx context.Context) *big.Int { - if excess := b.CurrentHeader().ExcessBlobGas; excess != nil { + if excess := b.CurrentHeader().ExcessBlobGas; excess != nil && b.ChainConfig().BlobScheduleConfig != nil { return eip4844.CalcBlobFee(b.ChainConfig(), b.CurrentHeader()) } return nil diff --git a/eth/tracers/live/supply.go b/eth/tracers/live/supply.go index bae7445cb4..fedd135872 100644 --- a/eth/tracers/live/supply.go +++ b/eth/tracers/live/supply.go @@ -166,7 +166,7 @@ func (s *supplyTracer) onBlockStart(ev tracing.BlockEvent) { s.delta.Burn.EIP1559 = burn } // Blob burnt gas - if blobGas := ev.Block.BlobGasUsed(); blobGas != nil && *blobGas > 0 && ev.Block.ExcessBlobGas() != nil { + if blobGas := ev.Block.BlobGasUsed(); blobGas != nil && *blobGas > 0 && ev.Block.ExcessBlobGas() != nil && s.chainConfig.BlobScheduleConfig != nil { var ( baseFee = eip4844.CalcBlobFee(s.chainConfig, ev.Block.Header()) burn = new(big.Int).Mul(new(big.Int).SetUint64(*blobGas), baseFee) diff --git a/internal/ethapi/transaction_args.go b/internal/ethapi/transaction_args.go index c14796222f..9a8742dc19 100644 --- a/internal/ethapi/transaction_args.go +++ b/internal/ethapi/transaction_args.go @@ -255,7 +255,7 @@ func (args *TransactionArgs) setFeeDefaults(ctx context.Context, b Backend, head // setCancunFeeDefaults fills in reasonable default fee values for unspecified fields. func (args *TransactionArgs) setCancunFeeDefaults(config *params.ChainConfig, head *types.Header) { // Set maxFeePerBlobGas if it is missing. - if args.BlobHashes != nil && args.BlobFeeCap == nil { + if args.BlobHashes != nil && args.BlobFeeCap == nil && config.BlobScheduleConfig != nil { blobBaseFee := eip4844.CalcBlobFee(config, head) // Set the max fee to be 2 times larger than the previous block's blob base fee. // The additional slack allows the tx to not become invalidated if the base From ebc7da00c6619d8285f5d77ab6be42c1d4234a49 Mon Sep 17 00:00:00 2001 From: ChanHongMing <91173675+ChanHongMing@users.noreply.github.com> Date: Wed, 24 Dec 2025 02:14:05 +0800 Subject: [PATCH 2/6] set Nil withdrawals for bor simulation --- internal/ethapi/simulate.go | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/internal/ethapi/simulate.go b/internal/ethapi/simulate.go index 143e945684..92cdf0e116 100644 --- a/internal/ethapi/simulate.go +++ b/internal/ethapi/simulate.go @@ -352,7 +352,12 @@ func (sim *simulator) processBlock(ctx context.Context, block *simBlock, header, reqHash := types.CalcRequestsHash(requests) header.RequestsHash = &reqHash } - blockBody := &types.Body{Transactions: txes, Withdrawals: *block.BlockOverrides.Withdrawals} + // For Bor chains, Withdrawals will be nil, so we need to handle that + var withdrawals types.Withdrawals + if block.BlockOverrides.Withdrawals != nil { + withdrawals = *block.BlockOverrides.Withdrawals + } + blockBody := &types.Body{Transactions: txes, Withdrawals: withdrawals} chainHeadReader := &simChainHeadReader{ctx, sim.b} b, receipts, err := sim.b.Engine().FinalizeAndAssemble(chainHeadReader, header, sim.state, blockBody, receipts) _ = receipts // mark unused @@ -420,7 +425,9 @@ func (sim *simulator) sanitizeChain(blocks []simBlock) ([]simBlock, error) { n := new(big.Int).Add(prevNumber, big.NewInt(1)) block.BlockOverrides.Number = (*hexutil.Big)(n) } - if block.BlockOverrides.Withdrawals == nil { + // Only set withdrawals for non-Bor chains (Ethereum mainnet and testnets) + // Bor/Polygon doesn't support withdrawals even post-Shanghai + if block.BlockOverrides.Withdrawals == nil && sim.chainConfig.Bor == nil { block.BlockOverrides.Withdrawals = &types.Withdrawals{} } diff := new(big.Int).Sub(block.BlockOverrides.Number.ToInt(), prevNumber) @@ -437,12 +444,16 @@ func (sim *simulator) sanitizeChain(blocks []simBlock) ([]simBlock, error) { for i := uint64(0); i < gap.Uint64(); i++ { n := new(big.Int).Add(prevNumber, big.NewInt(int64(i+1))) t := prevTimestamp + timestampIncrement + overrides := &override.BlockOverrides{ + Number: (*hexutil.Big)(n), + Time: (*hexutil.Uint64)(&t), + } + // Only set withdrawals for non-Bor chains + if sim.chainConfig.Bor == nil { + overrides.Withdrawals = &types.Withdrawals{} + } b := simBlock{ - BlockOverrides: &override.BlockOverrides{ - Number: (*hexutil.Big)(n), - Time: (*hexutil.Uint64)(&t), - Withdrawals: &types.Withdrawals{}, - }, + BlockOverrides: overrides, } prevTimestamp = t res = append(res, b) @@ -482,7 +493,8 @@ func (sim *simulator) makeHeaders(blocks []simBlock) ([]*types.Header, error) { overrides := block.BlockOverrides var withdrawalsHash *common.Hash - if sim.chainConfig.IsShanghai(overrides.Number.ToInt()) { + // Only set withdrawals hash for non-Bor chains that have Shanghai fork + if sim.chainConfig.IsShanghai(overrides.Number.ToInt()) && sim.chainConfig.Bor == nil { withdrawalsHash = &types.EmptyWithdrawalsHash } var parentBeaconRoot *common.Hash From 44a5da56442bb7788acd40873b82a9613e68aaaf Mon Sep 17 00:00:00 2001 From: ChanHongMing <91173675+ChanHongMing@users.noreply.github.com> Date: Wed, 24 Dec 2025 02:15:47 +0800 Subject: [PATCH 3/6] simChainHeadReader implements core.BorStateSyncer --- internal/ethapi/simulate.go | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/internal/ethapi/simulate.go b/internal/ethapi/simulate.go index 92cdf0e116..50cae24610 100644 --- a/internal/ethapi/simulate.go +++ b/internal/ethapi/simulate.go @@ -33,6 +33,7 @@ import ( "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/internal/ethapi/override" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rpc" @@ -137,6 +138,21 @@ func (m *simChainHeadReader) GetHeader(hash common.Hash, number uint64) *types.H return header } +// SetStateSync implements core.BorStateSyncer for Bor consensus compatibility. +// Since this is a simulation, we don't need to actually store state sync data. +func (m *simChainHeadReader) SetStateSync(stateData []*types.StateSyncData) { + // No-op for simulation +} + +// SubscribeStateSyncEvent implements core.BorStateSyncer for Bor consensus compatibility. +// Returns a no-op subscription since we don't need state sync events in simulation. +func (m *simChainHeadReader) SubscribeStateSyncEvent(ch chan<- core.StateSyncEvent) event.Subscription { + return event.NewSubscription(func(quit <-chan struct{}) error { + <-quit + return nil + }) +} + func (m *simChainHeadReader) GetHeaderByNumber(number uint64) *types.Header { header, err := m.Backend.HeaderByNumber(m.Context, rpc.BlockNumber(number)) if err != nil { From 504ba114a340808c10e60af6339f169538b334f8 Mon Sep 17 00:00:00 2001 From: ChanHongMing <91173675+ChanHongMing@users.noreply.github.com> Date: Sat, 27 Dec 2025 16:42:44 +0800 Subject: [PATCH 4/6] simplify fork checking --- core/evm.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/evm.go b/core/evm.go index 15653874ef..c820ee964f 100644 --- a/core/evm.go +++ b/core/evm.go @@ -74,7 +74,7 @@ func NewEVMBlockContext(header *types.Header, chain ChainContext, author *common // Only calculate blob fee if the fork actually supports blob transactions (Cancun or later) // and the chain has a BlobScheduleConfig configured if header.ExcessBlobGas != nil && chain.Config().BlobScheduleConfig != nil { - if chain.Config().IsCancun(header.Number) || chain.Config().IsPrague(header.Number) || chain.Config().IsOsaka(header.Number) { + if chain.Config().IsCancun(header.Number){ blobBaseFee = eip4844.CalcBlobFee(chain.Config(), header) } } From 3be59682bd10c6adc3359fa216e1ceab59882788 Mon Sep 17 00:00:00 2001 From: ChanHongMing <91173675+ChanHongMing@users.noreply.github.com> Date: Mon, 29 Dec 2025 14:44:04 +0800 Subject: [PATCH 5/6] lint fixing --- core/evm.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/evm.go b/core/evm.go index c820ee964f..3c919f75e7 100644 --- a/core/evm.go +++ b/core/evm.go @@ -74,7 +74,7 @@ func NewEVMBlockContext(header *types.Header, chain ChainContext, author *common // Only calculate blob fee if the fork actually supports blob transactions (Cancun or later) // and the chain has a BlobScheduleConfig configured if header.ExcessBlobGas != nil && chain.Config().BlobScheduleConfig != nil { - if chain.Config().IsCancun(header.Number){ + if chain.Config().IsCancun(header.Number) { blobBaseFee = eip4844.CalcBlobFee(chain.Config(), header) } } From e3be791d6b4f2ad76a4459748b6496ff2d45a6c0 Mon Sep 17 00:00:00 2001 From: ChanHongMing <91173675+ChanHongMing@users.noreply.github.com> Date: Mon, 29 Dec 2025 19:03:34 +0800 Subject: [PATCH 6/6] Add non nil chainConfig for testcase --- internal/ethapi/simulate_test.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/internal/ethapi/simulate_test.go b/internal/ethapi/simulate_test.go index c747b76477..8553bf5f9e 100644 --- a/internal/ethapi/simulate_test.go +++ b/internal/ethapi/simulate_test.go @@ -23,6 +23,7 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/internal/ethapi/override" + "github.com/ethereum/go-ethereum/params" ) func TestSimulateSanitizeBlockOrder(t *testing.T) { @@ -80,7 +81,10 @@ func TestSimulateSanitizeBlockOrder(t *testing.T) { err: "block timestamps must be in order: 72 <= 72", }, } { - sim := &simulator{base: &types.Header{Number: big.NewInt(int64(tc.baseNumber)), Time: tc.baseTimestamp}} + sim := &simulator{ + base: &types.Header{Number: big.NewInt(int64(tc.baseNumber)), Time: tc.baseTimestamp}, + chainConfig: params.TestChainConfig, // In real usage, sanitizeChain is always called with chainConfig + } res, err := sim.sanitizeChain(tc.blocks) if err != nil { if err.Error() == tc.err {