diff --git a/cmd/evm/internal/t8ntool/execution.go b/cmd/evm/internal/t8ntool/execution.go index 1979a8226e..803747b358 100644 --- a/cmd/evm/internal/t8ntool/execution.go +++ b/cmd/evm/internal/t8ntool/execution.go @@ -260,7 +260,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) + receipt, _, 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) @@ -271,6 +271,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, if receipt.Logs == nil { receipt.Logs = []*types.Log{} } + gasUsed += receipt.GasUsed includedTxs = append(includedTxs, tx) if hashError != nil { return nil, nil, nil, NewError(ErrorMissingBlockhash, hashError) diff --git a/core/chain_makers.go b/core/chain_makers.go index 7ce86b14e9..7fe5a2627d 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -38,11 +38,12 @@ import ( // BlockGen creates blocks for testing. // See GenerateChain for a detailed explanation. type BlockGen struct { - i int - cm *chainMaker - parent *types.Block - header *types.Header - statedb *state.StateDB + i int + cumulativeGas uint64 + cm *chainMaker + parent *types.Block + header *types.Header + statedb *state.StateDB gasPool *GasPool txs []*types.Transaction @@ -115,12 +116,15 @@ func (b *BlockGen) addTx(bc *BlockChain, vmConfig vm.Config, tx *types.Transacti var ( blockContext = NewEVMBlockContext(b.header, bc, &b.header.Coinbase) evm = vm.NewEVM(blockContext, b.statedb, b.cm.config, vmConfig) + receipt *types.Receipt + err error ) b.statedb.SetTxContext(tx.Hash(), len(b.txs)) - receipt, err := ApplyTransaction(evm, b.gasPool, b.statedb, b.header, tx, &b.header.GasUsed) + receipt, b.cumulativeGas, err = ApplyTransaction(evm, b.gasPool, b.statedb, b.header, tx, b.cumulativeGas) if err != nil { panic(err) } + b.header.GasUsed += receipt.GasUsed // 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 b.statedb.Database().TrieDB().IsVerkle() { diff --git a/core/state_processor.go b/core/state_processor.go index 6eea74bdd8..e862223d39 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -63,12 +63,17 @@ func (p *StateProcessor) Process(ctx context.Context, block *types.Block, stated var ( config = p.chainConfig() receipts types.Receipts - usedGas = new(uint64) header = block.Header() blockHash = block.Hash() blockNumber = block.Number() allLogs []*types.Log gp = new(GasPool).AddGas(block.GasLimit()) + // We are maintaining two counters here: + // one counts all the cumulativeGas which includes refunds + // while the other counts only the usedGas which excludes refunds after Amsterdam + // We need the cumulativeGas for receipts and the usedGas for the block gas limit + cumulativeGas = uint64(0) + usedGas = uint64(0) ) var tracingStateDB = vm.StateDB(statedb) @@ -107,13 +112,17 @@ func (p *StateProcessor) Process(ctx context.Context, block *types.Block, stated telemetry.StringAttribute("tx.hash", tx.Hash().Hex()), telemetry.Int64Attribute("tx.index", int64(i)), ) - receipt, err := ApplyTransactionWithEVM(msg, gp, statedb, blockNumber, blockHash, context.Time, tx, usedGas, evm) - spanEnd(&err) + + var receipt *types.Receipt + receipt, cumulativeGas, err = ApplyTransactionWithEVM(msg, gp, statedb, blockNumber, blockHash, context.Time, tx, cumulativeGas, evm) if err != nil { return nil, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err) } + usedGas += receipt.GasUsed receipts = append(receipts, receipt) allLogs = append(allLogs, receipt.Logs...) + + spanEnd(&err) } requests, err := postExecution(ctx, config, block, allLogs, evm) if err != nil { @@ -127,7 +136,7 @@ func (p *StateProcessor) Process(ctx context.Context, block *types.Block, stated Receipts: receipts, Requests: requests, Logs: allLogs, - GasUsed: *usedGas, + GasUsed: usedGas, }, nil } @@ -159,7 +168,7 @@ func postExecution(ctx context.Context, config *params.ChainConfig, block *types // 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, cumulativeGas uint64, evm *vm.EVM) (receipt *types.Receipt, cGas uint64, err error) { if hooks := evm.Config.Tracer; hooks != nil { if hooks.OnTxStart != nil { hooks.OnTxStart(evm.GetVMContext(), tx, msg.From) @@ -171,7 +180,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, cumulativeGas, err } // Update the state with pending changes. var root []byte @@ -180,28 +189,32 @@ func ApplyTransactionWithEVM(msg *Message, gp *GasPool, statedb *state.StateDB, } else { root = statedb.IntermediateRoot(evm.ChainConfig().IsEIP158(blockNumber)).Bytes() } - *usedGas += result.UsedGas + cGas = cumulativeGas + 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, cGas, root), cGas, nil } // MakeReceipt generates the receipt object for a transaction given its execution result. -func MakeReceipt(evm *vm.EVM, result *ExecutionResult, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, blockTime uint64, tx *types.Transaction, usedGas uint64, root []byte) *types.Receipt { +func MakeReceipt(evm *vm.EVM, result *ExecutionResult, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, blockTime uint64, tx *types.Transaction, cumulativeGas uint64, root []byte) *types.Receipt { // Create a new receipt for the transaction, storing the intermediate root and gas used // by the tx. - receipt := &types.Receipt{Type: tx.Type(), PostState: root, CumulativeGasUsed: usedGas} + receipt := &types.Receipt{Type: tx.Type(), PostState: root, CumulativeGasUsed: cumulativeGas} if result.Failed() { receipt.Status = types.ReceiptStatusFailed } else { receipt.Status = types.ReceiptStatusSuccessful } receipt.TxHash = tx.Hash() - receipt.GasUsed = result.UsedGas + if evm.ChainConfig().IsAmsterdam(blockNumber, blockTime) { + receipt.GasUsed = result.MaxUsedGas + } else { + receipt.GasUsed = result.UsedGas + } if tx.Type() == types.BlobTxType { receipt.BlobGasUsed = uint64(len(tx.BlobHashes()) * params.BlobTxBlobGasPerBlob) @@ -224,15 +237,15 @@ func MakeReceipt(evm *vm.EVM, result *ExecutionResult, statedb *state.StateDB, b // ApplyTransaction attempts to apply a transaction to the given state database // 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 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, cumulativeGas 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, cumulativeGas, err } // Create a new context to be used in the EVM environment - return ApplyTransactionWithEVM(msg, gp, statedb, header.Number, header.Hash(), header.Time, tx, usedGas, evm) + return ApplyTransactionWithEVM(msg, gp, statedb, header.Number, header.Hash(), header.Time, tx, cumulativeGas, evm) } // ProcessBeaconBlockRoot applies the EIP-4788 system call to the beacon block root diff --git a/core/state_transition.go b/core/state_transition.go index 62474b5f5b..095bf17b41 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -542,7 +542,14 @@ func (st *stateTransition) execute() (*ExecutionResult, error) { peakGasUsed = floorDataGas } } + // Return gas to the user st.returnGas() + // Return gas to the gas pool + if rules.IsAmsterdam { + st.gp.AddGas(st.initialGas - peakGasUsed) + } else { + st.gp.AddGas(st.gasRemaining) + } effectiveTip := msg.GasPrice if rules.IsLondon { @@ -660,10 +667,6 @@ func (st *stateTransition) returnGas() { if st.evm.Config.Tracer != nil && st.evm.Config.Tracer.OnGasChange != nil && st.gasRemaining > 0 { st.evm.Config.Tracer.OnGasChange(st.gasRemaining, 0, tracing.GasChangeTxLeftOverReturned) } - - // Also return remaining gas to the block gas counter so it is - // available for the next transaction. - st.gp.AddGas(st.gasRemaining) } // gasUsed returns the amount of gas used up by the state transition. diff --git a/core/tracing/hooks.go b/core/tracing/hooks.go index c85abe6482..6d0131ce70 100644 --- a/core/tracing/hooks.go +++ b/core/tracing/hooks.go @@ -358,7 +358,7 @@ const ( // this generates an increase in gas. There is at most one of such gas change per transaction. GasChangeTxRefunds GasChangeReason = 3 // GasChangeTxLeftOverReturned is the amount of gas left over at the end of transaction's execution that will be returned - // to the chain. This change will always be a negative change as we "drain" left over gas towards 0. If there was no gas + // to the account. This change will always be a negative change as we "drain" left over gas towards 0. If there was no gas // left at the end of execution, no such even will be emitted. The returned gas's value in Wei is returned to caller. // There is at most one of such gas change per transaction. GasChangeTxLeftOverReturned GasChangeReason = 4 diff --git a/eth/tracers/api.go b/eth/tracers/api.go index 5f2f16627a..d8e2128bc2 100644 --- a/eth/tracers/api.go +++ b/eth/tracers/api.go @@ -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) } diff --git a/eth/tracers/internal/tracetest/selfdestruct_state_test.go b/eth/tracers/internal/tracetest/selfdestruct_state_test.go index 2c714b6dce..53ea8ab63f 100644 --- a/eth/tracers/internal/tracetest/selfdestruct_state_test.go +++ b/eth/tracers/internal/tracetest/selfdestruct_state_test.go @@ -620,8 +620,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, uint64(0), evm) if err != nil { t.Fatalf("failed to execute transaction: %v", err) } diff --git a/miner/worker.go b/miner/worker.go index b5fb7a344d..93b1a081c3 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -56,13 +56,14 @@ func (miner *Miner) maxBlobsPerBlock(time uint64) int { // environment is the worker's current environment and holds all // information of the sealing block generation. type environment struct { - signer types.Signer - state *state.StateDB // apply state changes here - tcount int // tx count in cycle - size uint64 // size of the block we are building - gasPool *core.GasPool // available gas used to pack transactions - coinbase common.Address - evm *vm.EVM + signer types.Signer + state *state.StateDB // apply state changes here + tcount int // tx count in cycle + size uint64 // size of the block we are building + gasPool *core.GasPool // available gas used to pack transactions + coinbase common.Address + evm *vm.EVM + cumulativeGas uint64 header *types.Header txs []*types.Transaction @@ -374,12 +375,15 @@ 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, cumulativeGas, err := core.ApplyTransaction(env.evm, env.gasPool, env.state, env.header, tx, env.cumulativeGas) if err != nil { env.state.RevertToSnapshot(snap) env.gasPool.SetGas(gp) + return nil, err } - return receipt, err + env.cumulativeGas = cumulativeGas + env.header.GasUsed += receipt.GasUsed + return receipt, nil } func (miner *Miner) commitTransactions(env *environment, plainTxs, blobTxs *transactionsByPriceAndNonce, interrupt *atomic.Int32) error {