core: fix EIP-7778 block gas accounting to exclude refunds

EIP-7778 requires block-level gas accounting to exclude refunds, but
receipts must continue using post-refund CumulativeGasUsed. The previous
code used a single gas value for both purposes, causing:
1. Miner's header.GasUsed (post-refund) diverging from validator's
   expected value (pre-refund), rejecting all refund-heavy blocks.
2. Receipt root hash mismatches with other clients (e.g., Besu) since
   CumulativeGasUsed was incorrectly set to pre-refund gas.

Fix by returning two gas values from ApplyTransactionWithEVM:
- receipt: contains post-refund CumulativeGasUsed (unchanged behavior)
- blockGasUsed: pre-refund gas for Amsterdam, post-refund otherwise

All callers updated: state_processor, chain_makers, parallel processor,
miner/worker, t8ntool, simulate, tracers.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
qu0b 2026-02-04 13:03:56 +00:00
parent 022e460d1e
commit b6c983dbf6
9 changed files with 81 additions and 63 deletions

View file

@ -153,8 +153,9 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
blockHash = common.Hash{0x13, 0x37} blockHash = common.Hash{0x13, 0x37}
rejectedTxs []*rejectedTx rejectedTxs []*rejectedTx
includedTxs types.Transactions includedTxs types.Transactions
gasUsed = uint64(0) gasUsed = uint64(0)
blobGasUsed = uint64(0) blockGasUsed = uint64(0)
blobGasUsed = uint64(0)
receipts = make(types.Receipts, 0) receipts = make(types.Receipts, 0)
) )
gaspool.AddGas(pre.Env.GasLimit) gaspool.AddGas(pre.Env.GasLimit)
@ -260,7 +261,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
snapshot = statedb.Snapshot() snapshot = statedb.Snapshot()
prevGas = gaspool.Gas() prevGas = gaspool.Gas()
) )
receipt, err := core.ApplyTransactionWithEVM(msg, gaspool, statedb, vmContext.BlockNumber, blockHash, pre.Env.Timestamp, tx, &gasUsed, evm) receipt, txBlockGas, err := core.ApplyTransactionWithEVM(msg, gaspool, statedb, vmContext.BlockNumber, blockHash, pre.Env.Timestamp, tx, &gasUsed, evm)
if err != nil { if err != nil {
statedb.RevertToSnapshot(snapshot) statedb.RevertToSnapshot(snapshot)
log.Info("rejected tx", "index", i, "hash", tx.Hash(), "from", msg.From, "error", err) log.Info("rejected tx", "index", i, "hash", tx.Hash(), "from", msg.From, "error", err)
@ -269,6 +270,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
continue continue
} }
includedTxs = append(includedTxs, tx) includedTxs = append(includedTxs, tx)
blockGasUsed += txBlockGas
if hashError != nil { if hashError != nil {
return nil, nil, nil, NewError(ErrorMissingBlockhash, hashError) return nil, nil, nil, NewError(ErrorMissingBlockhash, hashError)
} }
@ -349,7 +351,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
Receipts: receipts, Receipts: receipts,
Rejected: rejectedTxs, Rejected: rejectedTxs,
Difficulty: (*math.HexOrDecimal256)(vmContext.Difficulty), Difficulty: (*math.HexOrDecimal256)(vmContext.Difficulty),
GasUsed: (math.HexOrDecimal64)(gasUsed), GasUsed: (math.HexOrDecimal64)(blockGasUsed),
BaseFee: (*math.HexOrDecimal256)(vmContext.BaseFee), BaseFee: (*math.HexOrDecimal256)(vmContext.BaseFee),
} }
if pre.Env.Withdrawals != nil { if pre.Env.Withdrawals != nil {

View file

@ -19,6 +19,7 @@ package core
import ( import (
"errors" "errors"
"fmt" "fmt"
"github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"

View file

@ -44,11 +44,12 @@ type BlockGen struct {
header *types.Header header *types.Header
statedb *state.StateDB statedb *state.StateDB
gasPool *GasPool gasPool *GasPool
txs []*types.Transaction txs []*types.Transaction
receipts []*types.Receipt receipts []*types.Receipt
uncles []*types.Header uncles []*types.Header
withdrawals []*types.Withdrawal withdrawals []*types.Withdrawal
receiptGasUsed uint64
engine consensus.Engine engine consensus.Engine
} }
@ -117,7 +118,8 @@ func (b *BlockGen) addTx(bc *BlockChain, vmConfig vm.Config, tx *types.Transacti
evm = vm.NewEVM(blockContext, b.statedb, b.cm.config, vmConfig) evm = vm.NewEVM(blockContext, b.statedb, b.cm.config, vmConfig)
) )
b.statedb.SetTxContext(tx.Hash(), len(b.txs)) b.statedb.SetTxContext(tx.Hash(), len(b.txs))
receipt, err := ApplyTransaction(evm, b.gasPool, b.statedb, b.header, tx, &b.header.GasUsed) receipt, blockGasUsed, err := ApplyTransaction(evm, b.gasPool, b.statedb, b.header, tx, &b.receiptGasUsed)
b.header.GasUsed += blockGasUsed
if err != nil { if err != nil {
panic(err) panic(err)
} }

View file

@ -45,7 +45,7 @@ func NewParallelStateProcessor(chain *HeaderChain, vmConfig *vm.Config) Parallel
// performs post-tx state transition (system contracts and withdrawals) // performs post-tx state transition (system contracts and withdrawals)
// and calculates the ProcessResult, returning it to be sent on resCh // and calculates the ProcessResult, returning it to be sent on resCh
// by resultHandler // by resultHandler
func (p *ParallelStateProcessor) prepareExecResult(block *types.Block, allStateReads *bal.StateAccesses, tExecStart time.Time, postTxState *state.StateDB, receipts types.Receipts) *ProcessResultWithMetrics { func (p *ParallelStateProcessor) prepareExecResult(block *types.Block, allStateReads *bal.StateAccesses, tExecStart time.Time, postTxState *state.StateDB, receipts types.Receipts, blockGasUsed uint64) *ProcessResultWithMetrics {
tExec := time.Since(tExecStart) tExec := time.Since(tExecStart)
var requests [][]byte var requests [][]byte
tPostprocessStart := time.Now() tPostprocessStart := time.Now()
@ -147,7 +147,7 @@ func (p *ParallelStateProcessor) prepareExecResult(block *types.Block, allStateR
Receipts: receipts, Receipts: receipts,
Requests: requests, Requests: requests,
Logs: allLogs, Logs: allLogs,
GasUsed: cumulativeGasUsed, GasUsed: blockGasUsed,
}, },
PostProcessTime: tPostprocess, PostProcessTime: tPostprocess,
ExecTime: tExec, ExecTime: tExec,
@ -155,9 +155,10 @@ func (p *ParallelStateProcessor) prepareExecResult(block *types.Block, allStateR
} }
type txExecResult struct { type txExecResult struct {
idx int // transaction index idx int // transaction index
receipt *types.Receipt receipt *types.Receipt
err error // non-EVM error which would render the block invalid blockGasUsed uint64 // pre-refund gas for block-level accounting (EIP-7778)
err error // non-EVM error which would render the block invalid
stateReads bal.StateAccesses stateReads bal.StateAccesses
} }
@ -168,6 +169,7 @@ func (p *ParallelStateProcessor) resultHandler(block *types.Block, preTxStateRea
// 1. if the block has transactions, receive the execution results from all of them and return an error on resCh if any txs err'd // 1. if the block has transactions, receive the execution results from all of them and return an error on resCh if any txs err'd
// 2. once all txs are executed, compute the post-tx state transition and produce the ProcessResult sending it on resCh (or an error if the post-tx state didn't match what is reported in the BAL) // 2. once all txs are executed, compute the post-tx state transition and produce the ProcessResult sending it on resCh (or an error if the post-tx state didn't match what is reported in the BAL)
var receipts []*types.Receipt var receipts []*types.Receipt
var blockGasUsed uint64
gp := new(GasPool) gp := new(GasPool)
gp.SetGas(block.GasLimit()) gp.SetGas(block.GasLimit())
var execErr error var execErr error
@ -184,10 +186,12 @@ func (p *ParallelStateProcessor) resultHandler(block *types.Block, preTxStateRea
if res.err != nil { if res.err != nil {
execErr = res.err execErr = res.err
} else { } else {
if err := gp.SubGas(res.receipt.GasUsed); err != nil { // EIP-7778: use pre-refund gas for block-level gas pool checks
if err := gp.SubGas(res.blockGasUsed); err != nil {
execErr = err execErr = err
} else { } else {
receipts = append(receipts, res.receipt) receipts = append(receipts, res.receipt)
blockGasUsed += res.blockGasUsed
allReads.Merge(res.stateReads) allReads.Merge(res.stateReads)
} }
} }
@ -205,7 +209,7 @@ func (p *ParallelStateProcessor) resultHandler(block *types.Block, preTxStateRea
} }
} }
execResults := p.prepareExecResult(block, &allReads, tExecStart, postTxState, receipts) execResults := p.prepareExecResult(block, &allReads, tExecStart, postTxState, receipts, blockGasUsed)
rootCalcRes := <-stateRootCalcResCh rootCalcRes := <-stateRootCalcResCh
if execResults.ProcessResult.Error != nil { if execResults.ProcessResult.Error != nil {
@ -271,7 +275,7 @@ func (p *ParallelStateProcessor) execTx(block *types.Block, tx *types.Transactio
gp.SetGas(block.GasLimit()) gp.SetGas(block.GasLimit())
db.SetTxContext(tx.Hash(), txIdx) db.SetTxContext(tx.Hash(), txIdx)
var gasUsed uint64 var gasUsed uint64
receipt, err := ApplyTransactionWithEVM(msg, gp, db, block.Number(), block.Hash(), context.Time, tx, &gasUsed, evm) receipt, txBlockGas, err := ApplyTransactionWithEVM(msg, gp, db, block.Number(), block.Hash(), context.Time, tx, &gasUsed, evm)
if err != nil { if err != nil {
err := fmt.Errorf("could not apply tx %d [%v]: %w", txIdx, tx.Hash().Hex(), err) err := fmt.Errorf("could not apply tx %d [%v]: %w", txIdx, tx.Hash().Hex(), err)
return &txExecResult{err: err} return &txExecResult{err: err}
@ -283,9 +287,10 @@ func (p *ParallelStateProcessor) execTx(block *types.Block, tx *types.Transactio
} }
return &txExecResult{ return &txExecResult{
idx: txIdx, idx: txIdx,
receipt: receipt, receipt: receipt,
stateReads: accesses, blockGasUsed: txBlockGas,
stateReads: accesses,
} }
} }

View file

@ -60,15 +60,15 @@ func (p *StateProcessor) chainConfig() *params.ChainConfig {
// transactions failed to execute due to insufficient gas it will return an error. // transactions failed to execute due to insufficient gas it will return an error.
func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (*ProcessResult, error) { func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (*ProcessResult, error) {
var ( var (
config = p.chainConfig() config = p.chainConfig()
receipts types.Receipts receipts types.Receipts
chargedGas = new(uint64) usedGas = new(uint64)
usedGas = uint64(0) blockGasUsed = uint64(0)
header = block.Header() header = block.Header()
blockHash = block.Hash() blockHash = block.Hash()
blockNumber = block.Number() blockNumber = block.Number()
allLogs []*types.Log allLogs []*types.Log
gp = new(GasPool).AddGas(block.GasLimit()) gp = new(GasPool).AddGas(block.GasLimit())
) )
var tracingStateDB = vm.StateDB(statedb) var tracingStateDB = vm.StateDB(statedb)
@ -104,20 +104,14 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
} }
statedb.SetTxContext(tx.Hash(), i) statedb.SetTxContext(tx.Hash(), i)
receipt, err := ApplyTransactionWithEVM(msg, gp, statedb, blockNumber, blockHash, context.Time, tx, chargedGas, evm) receipt, txBlockGas, err := ApplyTransactionWithEVM(msg, gp, statedb, blockNumber, blockHash, context.Time, tx, usedGas, evm)
if err != nil { if err != nil {
return nil, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err) return nil, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err)
} }
usedGas += receipt.GasUsed // EIP-7778: block gas accounting excludes refunds.
blockGasUsed += txBlockGas
receipts = append(receipts, receipt) receipts = append(receipts, receipt)
allLogs = append(allLogs, receipt.Logs...) allLogs = append(allLogs, receipt.Logs...)
/*
enc, _ := json.MarshalIndent(receipt, "", " ")
fmt.Printf("receipt json %s\n", string(enc))
encRLP, _ := rlp.EncodeToBytes(receipt)
fmt.Printf("receipt rlp %x\n", encRLP)
*/
} }
// Read requests if Prague is enabled. // Read requests if Prague is enabled.
@ -149,14 +143,14 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
Receipts: receipts, Receipts: receipts,
Requests: requests, Requests: requests,
Logs: allLogs, Logs: allLogs,
GasUsed: usedGas, GasUsed: blockGasUsed,
}, nil }, nil
} }
// ApplyTransactionWithEVM attempts to apply a transaction to the given state database // ApplyTransactionWithEVM attempts to apply a transaction to the given state database
// and uses the input parameters for its environment similar to ApplyTransaction. However, // and uses the input parameters for its environment similar to ApplyTransaction. However,
// this method takes an already created EVM instance as input. // this method takes an already created EVM instance as input.
func ApplyTransactionWithEVM(msg *Message, gp *GasPool, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, blockTime uint64, tx *types.Transaction, usedGas *uint64, evm *vm.EVM) (receipt *types.Receipt, err error) { func ApplyTransactionWithEVM(msg *Message, gp *GasPool, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, blockTime uint64, tx *types.Transaction, usedGas *uint64, evm *vm.EVM) (receipt *types.Receipt, blockGasUsed uint64, err error) {
if hooks := evm.Config.Tracer; hooks != nil { if hooks := evm.Config.Tracer; hooks != nil {
if hooks.OnTxStart != nil { if hooks.OnTxStart != nil {
hooks.OnTxStart(evm.GetVMContext(), tx, msg.From) hooks.OnTxStart(evm.GetVMContext(), tx, msg.From)
@ -168,7 +162,7 @@ func ApplyTransactionWithEVM(msg *Message, gp *GasPool, statedb *state.StateDB,
// Apply the transaction to the current state (included in the env). // Apply the transaction to the current state (included in the env).
result, err := ApplyMessage(evm, msg, gp) result, err := ApplyMessage(evm, msg, gp)
if err != nil { if err != nil {
return nil, err return nil, 0, err
} }
if evm.ChainConfig().IsAmsterdam(blockNumber, blockTime) { if evm.ChainConfig().IsAmsterdam(blockNumber, blockTime) {
// Emit Selfdesctruct logs where accounts with non-empty balances have been deleted // Emit Selfdesctruct logs where accounts with non-empty balances have been deleted
@ -191,12 +185,19 @@ func ApplyTransactionWithEVM(msg *Message, gp *GasPool, statedb *state.StateDB,
} }
*usedGas += result.UsedGas *usedGas += result.UsedGas
// EIP-7778: block gas accounting excludes refunds.
if evm.ChainConfig().IsAmsterdam(blockNumber, blockTime) {
blockGasUsed = result.MaxUsedGas
} else {
blockGasUsed = result.UsedGas
}
// Merge the tx-local access event into the "block-local" one, in order to collect // Merge the tx-local access event into the "block-local" one, in order to collect
// all values, so that the witness can be built. // all values, so that the witness can be built.
if statedb.Database().TrieDB().IsVerkle() { if statedb.Database().TrieDB().IsVerkle() {
statedb.AccessEvents().Merge(evm.AccessEvents) statedb.AccessEvents().Merge(evm.AccessEvents)
} }
return MakeReceipt(evm, result, statedb, blockNumber, blockHash, blockTime, tx, *usedGas, root), nil return MakeReceipt(evm, result, statedb, blockNumber, blockHash, blockTime, tx, *usedGas, root), blockGasUsed, nil
} }
// MakeReceipt generates the receipt object for a transaction given its execution result. // MakeReceipt generates the receipt object for a transaction given its execution result.
@ -210,11 +211,7 @@ func MakeReceipt(evm *vm.EVM, result *ExecutionResult, statedb *state.StateDB, b
receipt.Status = types.ReceiptStatusSuccessful receipt.Status = types.ReceiptStatusSuccessful
} }
receipt.TxHash = tx.Hash() receipt.TxHash = tx.Hash()
if evm.ChainConfig().IsAmsterdam(blockNumber, blockTime) { receipt.GasUsed = result.UsedGas
receipt.GasUsed = result.MaxUsedGas
} else {
receipt.GasUsed = result.UsedGas
}
if tx.Type() == types.BlobTxType { if tx.Type() == types.BlobTxType {
receipt.BlobGasUsed = uint64(len(tx.BlobHashes()) * params.BlobTxBlobGasPerBlob) receipt.BlobGasUsed = uint64(len(tx.BlobHashes()) * params.BlobTxBlobGasPerBlob)
@ -239,14 +236,14 @@ func MakeReceipt(evm *vm.EVM, result *ExecutionResult, statedb *state.StateDB, b
// and uses the input parameters for its environment. It returns the receipt // and uses the input parameters for its environment. It returns the receipt
// for the transaction, gas used and an error if the transaction failed, // for the transaction, gas used and an error if the transaction failed,
// indicating the block was invalid. // indicating the block was invalid.
func ApplyTransaction(evm *vm.EVM, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *uint64) (*types.Receipt, error) { func ApplyTransaction(evm *vm.EVM, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *uint64) (*types.Receipt, uint64, error) {
msg, err := TransactionToMessage(tx, types.MakeSigner(evm.ChainConfig(), header.Number, header.Time), header.BaseFee) msg, err := TransactionToMessage(tx, types.MakeSigner(evm.ChainConfig(), header.Number, header.Time), header.BaseFee)
if err != nil { if err != nil {
return nil, err return nil, 0, err
} }
// Create a new context to be used in the EVM environment // Create a new context to be used in the EVM environment
receipts, err := ApplyTransactionWithEVM(msg, gp, statedb, header.Number, header.Hash(), header.Time, tx, usedGas, evm) receipt, blockGasUsed, err := ApplyTransactionWithEVM(msg, gp, statedb, header.Number, header.Hash(), header.Time, tx, usedGas, evm)
return receipts, err return receipt, blockGasUsed, err
} }
// ProcessBeaconBlockRoot applies the EIP-4788 system call to the beacon block root // ProcessBeaconBlockRoot applies the EIP-4788 system call to the beacon block root

View file

@ -1055,7 +1055,7 @@ func (api *API) traceTx(ctx context.Context, tx *types.Transaction, message *cor
// Call Prepare to clear out the statedb access list // Call Prepare to clear out the statedb access list
statedb.SetTxContext(txctx.TxHash, txctx.TxIndex) statedb.SetTxContext(txctx.TxHash, txctx.TxIndex)
_, err = core.ApplyTransactionWithEVM(message, new(core.GasPool).AddGas(message.GasLimit), statedb, vmctx.BlockNumber, txctx.BlockHash, vmctx.Time, tx, &usedGas, evm) _, _, err = core.ApplyTransactionWithEVM(message, new(core.GasPool).AddGas(message.GasLimit), statedb, vmctx.BlockNumber, txctx.BlockHash, vmctx.Time, tx, &usedGas, evm)
if err != nil { if err != nil {
return nil, fmt.Errorf("tracing failed: %w", err) return nil, fmt.Errorf("tracing failed: %w", err)
} }

View file

@ -621,7 +621,7 @@ func TestSelfdestructStateTracer(t *testing.T) {
context := core.NewEVMBlockContext(block.Header(), blockchain, nil) context := core.NewEVMBlockContext(block.Header(), blockchain, nil)
evm := vm.NewEVM(context, hookedState, tt.genesis.Config, vm.Config{Tracer: tracer.Hooks()}) evm := vm.NewEVM(context, hookedState, tt.genesis.Config, vm.Config{Tracer: tracer.Hooks()})
usedGas := uint64(0) usedGas := uint64(0)
_, err = core.ApplyTransactionWithEVM(msg, new(core.GasPool).AddGas(tx.Gas()), statedb, block.Number(), block.Hash(), block.Time(), tx, &usedGas, evm) _, _, err = core.ApplyTransactionWithEVM(msg, new(core.GasPool).AddGas(tx.Gas()), statedb, block.Number(), block.Hash(), block.Time(), tx, &usedGas, evm)
if err != nil { if err != nil {
t.Fatalf("failed to execute transaction: %v", err) t.Fatalf("failed to execute transaction: %v", err)
} }

View file

@ -239,7 +239,7 @@ func (sim *simulator) processBlock(ctx context.Context, block *simBlock, header,
return nil, nil, nil, err return nil, nil, nil, err
} }
var ( var (
gasUsed, blobGasUsed uint64 gasUsed, blockGasUsed, blobGasUsed uint64
txes = make([]*types.Transaction, len(block.Calls)) txes = make([]*types.Transaction, len(block.Calls))
callResults = make([]simCallResult, len(block.Calls)) callResults = make([]simCallResult, len(block.Calls))
receipts = make([]*types.Receipt, len(block.Calls)) receipts = make([]*types.Receipt, len(block.Calls))
@ -275,7 +275,7 @@ func (sim *simulator) processBlock(ctx context.Context, block *simBlock, header,
if err := ctx.Err(); err != nil { if err := ctx.Err(); err != nil {
return nil, nil, nil, err return nil, nil, nil, err
} }
if err := sim.sanitizeCall(&call, sim.state, header, blockContext, &gasUsed); err != nil { if err := sim.sanitizeCall(&call, sim.state, header, blockContext, &blockGasUsed); err != nil {
return nil, nil, nil, err return nil, nil, nil, err
} }
var ( var (
@ -301,6 +301,12 @@ func (sim *simulator) processBlock(ctx context.Context, block *simBlock, header,
root = sim.state.IntermediateRoot(sim.chainConfig.IsEIP158(blockContext.BlockNumber)).Bytes() root = sim.state.IntermediateRoot(sim.chainConfig.IsEIP158(blockContext.BlockNumber)).Bytes()
} }
gasUsed += result.UsedGas gasUsed += result.UsedGas
// EIP-7778: block gas accounting excludes refunds.
if sim.chainConfig.IsAmsterdam(blockContext.BlockNumber, blockContext.Time) {
blockGasUsed += result.MaxUsedGas
} else {
blockGasUsed += result.UsedGas
}
receipts[i] = core.MakeReceipt(evm, result, sim.state, blockContext.BlockNumber, common.Hash{}, blockContext.Time, tx, gasUsed, root) receipts[i] = core.MakeReceipt(evm, result, sim.state, blockContext.BlockNumber, common.Hash{}, blockContext.Time, tx, gasUsed, root)
blobGasUsed += receipts[i].BlobGasUsed blobGasUsed += receipts[i].BlobGasUsed
logs := tracer.Logs() logs := tracer.Logs()
@ -320,7 +326,7 @@ func (sim *simulator) processBlock(ctx context.Context, block *simBlock, header,
} }
callResults[i] = callRes callResults[i] = callRes
} }
header.GasUsed = gasUsed header.GasUsed = blockGasUsed
if sim.chainConfig.IsCancun(header.Number, header.Time) { if sim.chainConfig.IsCancun(header.Number, header.Time) {
header.BlobGasUsed = &blobGasUsed header.BlobGasUsed = &blobGasUsed
} }

View file

@ -66,11 +66,12 @@ type environment struct {
coinbase common.Address coinbase common.Address
evm *vm.EVM evm *vm.EVM
header *types.Header header *types.Header
txs []*types.Transaction txs []*types.Transaction
receipts []*types.Receipt receipts []*types.Receipt
sidecars []*types.BlobTxSidecar sidecars []*types.BlobTxSidecar
blobs int blobs int
receiptGasUsed uint64 // cumulative post-refund gas for receipt CumulativeGasUsed
witness *stateless.Witness witness *stateless.Witness
alTracer *core.BlockAccessListTracer alTracer *core.BlockAccessListTracer
@ -382,10 +383,14 @@ func (miner *Miner) applyTransaction(env *environment, tx *types.Transaction) (*
snap = env.state.Snapshot() snap = env.state.Snapshot()
gp = env.gasPool.Gas() gp = env.gasPool.Gas()
) )
receipt, err := core.ApplyTransaction(env.evm, env.gasPool, env.state, env.header, tx, &env.header.GasUsed) receipt, blockGasUsed, err := core.ApplyTransaction(env.evm, env.gasPool, env.state, env.header, tx, &env.receiptGasUsed)
if err != nil { if err != nil {
env.state.RevertToSnapshot(snap) env.state.RevertToSnapshot(snap)
env.gasPool.SetGas(gp) env.gasPool.SetGas(gp)
} else {
// EIP-7778: block header uses pre-refund gas (blockGasUsed),
// while receipts use post-refund gas (accumulated in receiptGasUsed).
env.header.GasUsed += blockGasUsed
} }
return receipt, err return receipt, err
} }