Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions cmd/evm/internal/t8ntool/execution.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,8 +153,9 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
blockHash = common.Hash{0x13, 0x37}
rejectedTxs []*rejectedTx
includedTxs types.Transactions
gasUsed = uint64(0)
blobGasUsed = uint64(0)
gasUsed = uint64(0)
blockGasUsed = uint64(0)
blobGasUsed = uint64(0)
receipts = make(types.Receipts, 0)
)
gaspool.AddGas(pre.Env.GasLimit)
Expand Down Expand Up @@ -260,7 +261,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
snapshot = statedb.Snapshot()
prevGas = gaspool.Gas()
)
receipt, err := core.ApplyTransactionWithEVM(msg, gaspool, statedb, vmContext.BlockNumber, blockHash, pre.Env.Timestamp, tx, &gasUsed, evm)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The receipt.GasUsed contains the gas used as it counts towards the block gas limit (the gas charged to the account + any gas that was returned via refunds). Thus, I think there's no need to return this value separately and we can simplify the fix that this PR makes.

Going to try to see if I can make the fix accordingly.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Feel free to close this PR if you have a better solution, but please keep in mind: ethereum/EIPs#11191

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no. I'm wrong receipt.GasUsed subtracts refunds.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, the issue right now is afaik is in the block building. On the verification side, Geth seems to be correct (we pass all the tests for 7778). This PR does fix other spots (chain makers, t8n), and we want to incorporate a fix into those areas.

receipt, txBlockGas, err := core.ApplyTransactionWithEVM(msg, gaspool, statedb, vmContext.BlockNumber, blockHash, pre.Env.Timestamp, tx, &gasUsed, evm)
if err != nil {
statedb.RevertToSnapshot(snapshot)
log.Info("rejected tx", "index", i, "hash", tx.Hash(), "from", msg.From, "error", err)
Expand All @@ -269,6 +270,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
continue
}
includedTxs = append(includedTxs, tx)
blockGasUsed += txBlockGas
if hashError != nil {
return nil, nil, nil, NewError(ErrorMissingBlockhash, hashError)
}
Expand Down Expand Up @@ -349,7 +351,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
Receipts: receipts,
Rejected: rejectedTxs,
Difficulty: (*math.HexOrDecimal256)(vmContext.Difficulty),
GasUsed: (math.HexOrDecimal64)(gasUsed),
GasUsed: (math.HexOrDecimal64)(blockGasUsed),
BaseFee: (*math.HexOrDecimal256)(vmContext.BaseFee),
}
if pre.Env.Withdrawals != nil {
Expand Down
1 change: 1 addition & 0 deletions core/block_validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package core
import (
"errors"
"fmt"

"github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
Expand Down
14 changes: 8 additions & 6 deletions core/chain_makers.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,12 @@ type BlockGen struct {
header *types.Header
statedb *state.StateDB

gasPool *GasPool
txs []*types.Transaction
receipts []*types.Receipt
uncles []*types.Header
withdrawals []*types.Withdrawal
gasPool *GasPool
txs []*types.Transaction
receipts []*types.Receipt
uncles []*types.Header
withdrawals []*types.Withdrawal
receiptGasUsed uint64

engine consensus.Engine
}
Expand Down Expand Up @@ -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)
)
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 {
panic(err)
}
Expand Down
27 changes: 16 additions & 11 deletions core/parallel_state_processor.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ func NewParallelStateProcessor(chain *HeaderChain, vmConfig *vm.Config) Parallel
// performs post-tx state transition (system contracts and withdrawals)
// and calculates the ProcessResult, returning it to be sent on resCh
// 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)
var requests [][]byte
tPostprocessStart := time.Now()
Expand Down Expand Up @@ -147,17 +147,18 @@ func (p *ParallelStateProcessor) prepareExecResult(block *types.Block, allStateR
Receipts: receipts,
Requests: requests,
Logs: allLogs,
GasUsed: cumulativeGasUsed,
GasUsed: blockGasUsed,
},
PostProcessTime: tPostprocess,
ExecTime: tExec,
}
}

type txExecResult struct {
idx int // transaction index
receipt *types.Receipt
err error // non-EVM error which would render the block invalid
idx int // transaction index
receipt *types.Receipt
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
}
Expand All @@ -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
// 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 blockGasUsed uint64
gp := new(GasPool)
gp.SetGas(block.GasLimit())
var execErr error
Expand All @@ -184,10 +186,12 @@ func (p *ParallelStateProcessor) resultHandler(block *types.Block, preTxStateRea
if res.err != nil {
execErr = res.err
} 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
} else {
receipts = append(receipts, res.receipt)
blockGasUsed += res.blockGasUsed
allReads.Merge(res.stateReads)
}
}
Expand All @@ -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

if execResults.ProcessResult.Error != nil {
Expand Down Expand Up @@ -271,7 +275,7 @@ func (p *ParallelStateProcessor) execTx(block *types.Block, tx *types.Transactio
gp.SetGas(block.GasLimit())
db.SetTxContext(tx.Hash(), txIdx)
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 {
err := fmt.Errorf("could not apply tx %d [%v]: %w", txIdx, tx.Hash().Hex(), err)
return &txExecResult{err: err}
Expand All @@ -283,9 +287,10 @@ func (p *ParallelStateProcessor) execTx(block *types.Block, tx *types.Transactio
}

return &txExecResult{
idx: txIdx,
receipt: receipt,
stateReads: accesses,
idx: txIdx,
receipt: receipt,
blockGasUsed: txBlockGas,
stateReads: accesses,
}
}

Expand Down
59 changes: 28 additions & 31 deletions core/state_processor.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,15 +60,15 @@ func (p *StateProcessor) chainConfig() *params.ChainConfig {
// 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) {
var (
config = p.chainConfig()
receipts types.Receipts
chargedGas = new(uint64)
usedGas = uint64(0)
header = block.Header()
blockHash = block.Hash()
blockNumber = block.Number()
allLogs []*types.Log
gp = new(GasPool).AddGas(block.GasLimit())
config = p.chainConfig()
receipts types.Receipts
usedGas = new(uint64)
blockGasUsed = uint64(0)
header = block.Header()
blockHash = block.Hash()
blockNumber = block.Number()
allLogs []*types.Log
gp = new(GasPool).AddGas(block.GasLimit())
)

var tracingStateDB = vm.StateDB(statedb)
Expand Down Expand Up @@ -104,20 +104,14 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
}
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 {
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)
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.
Expand Down Expand Up @@ -149,14 +143,14 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
Receipts: receipts,
Requests: requests,
Logs: allLogs,
GasUsed: usedGas,
GasUsed: blockGasUsed,
}, nil
}

// ApplyTransactionWithEVM attempts to apply a transaction to the given state database
// and uses the input parameters for its environment similar to ApplyTransaction. However,
// 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.OnTxStart != nil {
hooks.OnTxStart(evm.GetVMContext(), tx, msg.From)
Expand All @@ -168,7 +162,7 @@ func ApplyTransactionWithEVM(msg *Message, gp *GasPool, statedb *state.StateDB,
// Apply the transaction to the current state (included in the env).
result, err := ApplyMessage(evm, msg, gp)
if err != nil {
return nil, err
return nil, 0, err
}
if evm.ChainConfig().IsAmsterdam(blockNumber, blockTime) {
// Emit Selfdesctruct logs where accounts with non-empty balances have been deleted
Expand All @@ -191,12 +185,19 @@ func ApplyTransactionWithEVM(msg *Message, gp *GasPool, statedb *state.StateDB,
}
*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
// all values, so that the witness can be built.
if statedb.Database().TrieDB().IsVerkle() {
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.
Expand All @@ -210,11 +211,7 @@ func MakeReceipt(evm *vm.EVM, result *ExecutionResult, statedb *state.StateDB, b
receipt.Status = types.ReceiptStatusSuccessful
}
receipt.TxHash = tx.Hash()
if evm.ChainConfig().IsAmsterdam(blockNumber, blockTime) {
receipt.GasUsed = result.MaxUsedGas
} else {
receipt.GasUsed = result.UsedGas
}
receipt.GasUsed = result.UsedGas

if tx.Type() == types.BlobTxType {
receipt.BlobGasUsed = uint64(len(tx.BlobHashes()) * params.BlobTxBlobGasPerBlob)
Expand All @@ -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
// for the transaction, gas used and an error if the transaction failed,
// 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)
if err != nil {
return nil, err
return nil, 0, err
}
// 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)
return receipts, err
receipt, blockGasUsed, err := ApplyTransactionWithEVM(msg, gp, statedb, header.Number, header.Hash(), header.Time, tx, usedGas, evm)
return receipt, blockGasUsed, err
}

// ProcessBeaconBlockRoot applies the EIP-4788 system call to the beacon block root
Expand Down
2 changes: 1 addition & 1 deletion eth/tracers/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
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 {
return nil, fmt.Errorf("tracing failed: %w", err)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -621,7 +621,7 @@ func TestSelfdestructStateTracer(t *testing.T) {
context := core.NewEVMBlockContext(block.Header(), blockchain, nil)
evm := vm.NewEVM(context, hookedState, tt.genesis.Config, vm.Config{Tracer: tracer.Hooks()})
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 {
t.Fatalf("failed to execute transaction: %v", err)
}
Expand Down
12 changes: 9 additions & 3 deletions internal/ethapi/simulate.go
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ func (sim *simulator) processBlock(ctx context.Context, block *simBlock, header,
return nil, nil, nil, err
}
var (
gasUsed, blobGasUsed uint64
gasUsed, blockGasUsed, blobGasUsed uint64
txes = make([]*types.Transaction, len(block.Calls))
callResults = make([]simCallResult, len(block.Calls))
receipts = make([]*types.Receipt, len(block.Calls))
Expand Down Expand Up @@ -275,7 +275,7 @@ func (sim *simulator) processBlock(ctx context.Context, block *simBlock, header,
if err := ctx.Err(); err != nil {
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
}
var (
Expand All @@ -301,6 +301,12 @@ func (sim *simulator) processBlock(ctx context.Context, block *simBlock, header,
root = sim.state.IntermediateRoot(sim.chainConfig.IsEIP158(blockContext.BlockNumber)).Bytes()
}
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)
blobGasUsed += receipts[i].BlobGasUsed
logs := tracer.Logs()
Expand All @@ -320,7 +326,7 @@ func (sim *simulator) processBlock(ctx context.Context, block *simBlock, header,
}
callResults[i] = callRes
}
header.GasUsed = gasUsed
header.GasUsed = blockGasUsed
if sim.chainConfig.IsCancun(header.Number, header.Time) {
header.BlobGasUsed = &blobGasUsed
}
Expand Down
17 changes: 11 additions & 6 deletions miner/worker.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,11 +66,12 @@ type environment struct {
coinbase common.Address
evm *vm.EVM

header *types.Header
txs []*types.Transaction
receipts []*types.Receipt
sidecars []*types.BlobTxSidecar
blobs int
header *types.Header
txs []*types.Transaction
receipts []*types.Receipt
sidecars []*types.BlobTxSidecar
blobs int
receiptGasUsed uint64 // cumulative post-refund gas for receipt CumulativeGasUsed

witness *stateless.Witness
alTracer *core.BlockAccessListTracer
Expand Down Expand Up @@ -382,10 +383,14 @@ func (miner *Miner) applyTransaction(env *environment, tx *types.Transaction) (*
snap = env.state.Snapshot()
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 {
env.state.RevertToSnapshot(snap)
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
}
Expand Down