mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-06-08 07:58:40 +00:00
core: misc fixes (claude)
This commit is contained in:
parent
95574903c0
commit
87a4f9c674
9 changed files with 287 additions and 65 deletions
|
|
@ -19,6 +19,7 @@ package state
|
|||
import (
|
||||
"fmt"
|
||||
"maps"
|
||||
"os"
|
||||
"slices"
|
||||
"sort"
|
||||
|
||||
|
|
@ -251,7 +252,7 @@ func (j *journal) stateChangedBytes(revid int, stateObjects map[common.Address]*
|
|||
var subcallBytes int64
|
||||
visit := func(e journalEntry) {
|
||||
switch e := e.(type) {
|
||||
case createContractChange:
|
||||
case createObjectChange:
|
||||
created[e.account] = true
|
||||
case codeChange:
|
||||
codeChanged[e.account] = true
|
||||
|
|
@ -262,19 +263,27 @@ func (j *journal) stateChangedBytes(revid int, stateObjects map[common.Address]*
|
|||
}
|
||||
}
|
||||
}
|
||||
pos := rev.journalIndex
|
||||
for _, child := range rev.closedChildren {
|
||||
for ; pos < child.start; pos++ {
|
||||
if excludeSubcalls {
|
||||
// Walk only this frame's own entries, skipping closed child ranges.
|
||||
pos := rev.journalIndex
|
||||
for _, child := range rev.closedChildren {
|
||||
for ; pos < child.start; pos++ {
|
||||
visit(j.entries[pos])
|
||||
}
|
||||
pos = child.end
|
||||
}
|
||||
for ; pos < len(j.entries); pos++ {
|
||||
visit(j.entries[pos])
|
||||
}
|
||||
if !excludeSubcalls {
|
||||
// Add the cached cost for this subcall.
|
||||
subcallBytes += j.stateBytesCharged[child.start]
|
||||
} else {
|
||||
// Walk all entries (including subcall ranges) to compute the full
|
||||
// diff for this frame. The caller is responsible for subtracting
|
||||
// `already_paid` (= state_gas_used so far) before charging the
|
||||
// difference, matching the spec's `this_call_cost = growth_cost -
|
||||
// already_paid` formula.
|
||||
for pos := rev.journalIndex; pos < len(j.entries); pos++ {
|
||||
visit(j.entries[pos])
|
||||
}
|
||||
pos = child.end
|
||||
}
|
||||
for ; pos < len(j.entries); pos++ {
|
||||
visit(j.entries[pos])
|
||||
}
|
||||
|
||||
var totalBytes int64
|
||||
|
|
@ -319,6 +328,12 @@ func (j *journal) stateChangedBytes(revid int, stateObjects map[common.Address]*
|
|||
|
||||
// Cache so the parent can look up this frame's total cost.
|
||||
j.stateBytesCharged[rev.journalIndex] = totalBytes
|
||||
if os.Getenv("DEBUG_8037") != "" {
|
||||
fmt.Fprintf(os.Stderr, " scb(rev=%d,idx=%d,!s=%v): cre=%d sl=%d cc=%d sub=%d tot=%d cls=%v\n",
|
||||
revid, rev.journalIndex, excludeSubcalls,
|
||||
len(created), len(slots), len(codeChanged),
|
||||
subcallBytes, totalBytes, rev.closedChildren)
|
||||
}
|
||||
return totalBytes
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import (
|
|||
"fmt"
|
||||
"math"
|
||||
"math/big"
|
||||
"os"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/tracing"
|
||||
|
|
@ -557,6 +558,13 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
|
|||
if !sufficient {
|
||||
return nil, fmt.Errorf("%w: have %d, want %d", ErrIntrinsicGas, st.gasRemaining.RegularGas, cost.RegularGas)
|
||||
}
|
||||
if rules.IsAmsterdam {
|
||||
// EIP-8037: intrinsic state gas is deducted from the reservoir but
|
||||
// does NOT count toward StateGasUsed (it is added back later via
|
||||
// tx_state_gas = intrinsic_state + state_gas_used). Reset only the
|
||||
// state-gas tracker; regular intrinsic stays accounted.
|
||||
st.gasRemaining.StateGasUsed -= cost.StateGas
|
||||
}
|
||||
if t := st.evm.Config.Tracer; t != nil && t.OnGasChange != nil {
|
||||
if rules.IsAmsterdam {
|
||||
t.OnGasChange(msg.GasLimit, st.gasRemaining.RegularGas+st.gasRemaining.StateGas, tracing.GasChangeTxIntrinsicGas)
|
||||
|
|
@ -599,23 +607,31 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
|
|||
vmerr error // vm errors do not effect consensus and are therefore not assigned to err
|
||||
)
|
||||
|
||||
// Take a snapshot for gas calculation
|
||||
outerSnapshot := st.state.Snapshot()
|
||||
|
||||
var execGasUsed vm.GasUsed
|
||||
if contractCreation {
|
||||
ret, _, st.gasRemaining, vmerr = st.evm.Create(msg.From, msg.Data, st.gasRemaining, value)
|
||||
} else {
|
||||
if !contractCreation {
|
||||
// Increment the nonce for the next transaction.
|
||||
st.state.SetNonce(msg.From, st.state.GetNonce(msg.From)+1, tracing.NonceChangeEoACall)
|
||||
|
||||
// Apply EIP-7702 authorizations.
|
||||
// Apply EIP-7702 authorizations BEFORE the outer state-gas snapshot
|
||||
// so that the authorization code-write does not appear in the diff
|
||||
// (it's already paid for by the per-auth intrinsic state gas).
|
||||
if msg.SetCodeAuthorizations != nil {
|
||||
for _, auth := range msg.SetCodeAuthorizations {
|
||||
// Note errors are ignored, we simply skip invalid authorizations here.
|
||||
st.applyAuthorization(rules, &auth)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Take a snapshot for gas calculation. For CREATE txs, the account
|
||||
// creation in evm.Create() will be inside this snapshot and bubble up
|
||||
// via cached child frames; we subtract the intrinsic-covered portion
|
||||
// at frame end. For CALL txs, auths have already been applied so their
|
||||
// code changes are not in the diff.
|
||||
outerSnapshot := st.state.Snapshot()
|
||||
|
||||
if contractCreation {
|
||||
ret, _, st.gasRemaining, vmerr = st.evm.Create(msg.From, msg.Data, st.gasRemaining, value)
|
||||
} else {
|
||||
|
||||
// Perform convenience warming of sender's delegation target. Although the
|
||||
// sender is already warmed in Prepare(..), it's possible a delegation to
|
||||
|
|
@ -636,12 +652,23 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
|
|||
outerBytes := st.state.StateChangedBytes(outerSnapshot, false)
|
||||
// Refund state gas for selfdestructed accounts.
|
||||
outerBytes -= st.state.SelfDestructRefundBytes()
|
||||
st.gasRemaining.Charge(vm.GasCosts{StateGas: outerBytes * int64(st.evm.Context.CostPerStateByte)})
|
||||
} else {
|
||||
if execGasUsed.StateGas > 0 {
|
||||
st.gasRemaining.StateGas += uint64(execGasUsed.StateGas)
|
||||
// For contract-creation txs the intrinsic already paid for the
|
||||
// account creation; subtract it so we don't double-charge.
|
||||
if contractCreation {
|
||||
outerBytes -= int64(params.AccountCreationSize)
|
||||
}
|
||||
execGasUsed.StateGas = 0
|
||||
// EIP-8037 spec: this_call_cost = growth_cost - already_paid.
|
||||
alreadyPaid := st.gasRemaining.StateGasUsed
|
||||
thisCallCost := outerBytes*int64(st.evm.Context.CostPerStateByte) - alreadyPaid
|
||||
st.gasRemaining.Charge(vm.GasCosts{StateGas: thisCallCost})
|
||||
} else {
|
||||
// On top-level error, restore state-gas reservoir and reset
|
||||
// the state-gas-used counter; state changes are reverted, no
|
||||
// state was grown (matches execution-specs fork.py:1055).
|
||||
if st.gasRemaining.StateGasUsed > 0 {
|
||||
st.gasRemaining.StateGas += uint64(st.gasRemaining.StateGasUsed)
|
||||
}
|
||||
st.gasRemaining.StateGasUsed = 0
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -672,12 +699,20 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
|
|||
|
||||
returned := st.returnGas()
|
||||
if rules.IsAmsterdam {
|
||||
// EIP-8037: 2D gas accounting for Amsterdam.
|
||||
// tx_regular = initialBudget.RegularGas - gasRemaining.RegularGas
|
||||
// tx_state = initialBudget.StateGas - gasRemaining.StateGas
|
||||
txRegular := st.initialBudget.RegularGas - st.gasRemaining.RegularGas
|
||||
// EIP-8037: tx_regular = intrinsic_regular + execution_regular_gas_used
|
||||
// (RegularGasUsed already includes intrinsic since it stays counted).
|
||||
// tx_state = intrinsic_state + execution_state_gas_used (StateGasUsed
|
||||
// excludes intrinsic — it was undone after the intrinsic Charge).
|
||||
txRegular := st.gasRemaining.RegularGasUsed
|
||||
txRegular = max(txRegular, floorDataGas)
|
||||
txState := st.initialBudget.StateGas - st.gasRemaining.StateGas
|
||||
txState := uint64(int64(cost.StateGas) + st.gasRemaining.StateGasUsed)
|
||||
if int64(cost.StateGas)+st.gasRemaining.StateGasUsed < 0 {
|
||||
txState = 0
|
||||
}
|
||||
if os.Getenv("DEBUG_8037") != "" {
|
||||
fmt.Fprintf(os.Stderr, "state_transition: txRegular=%d, txState=%d (cost.StateGas=%d, StateGasUsed=%d), gasUsed=%d\n",
|
||||
txRegular, txState, cost.StateGas, st.gasRemaining.StateGasUsed, st.gasUsed())
|
||||
}
|
||||
if err := st.gp.ReturnGasAmsterdam(txRegular, txState, st.gasUsed()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -173,8 +173,14 @@ func enable3529(jt *JumpTable) {
|
|||
// enable8037 enables EIP-8037 SSTORE repricing: the regular-gas portion of
|
||||
// new slot creation and same-tx 0→X→0 reset is reduced; the state-gas
|
||||
// portion is charged/refunded at frame-end via the journal.
|
||||
//
|
||||
// Also reprices CREATE/CREATE2 base regular gas from GAS_CREATE (32,000) to
|
||||
// CREATE_BASE_AMSTERDAM (9,000); the 112 × CPSB state-gas portion is charged
|
||||
// at frame end via the journal walker.
|
||||
func enable8037(jt *JumpTable) {
|
||||
jt[SSTORE].dynamicGas = gasSStoreEIP8037
|
||||
jt[CREATE].constantGas = params.CreateGasAmsterdam
|
||||
jt[CREATE2].constantGas = params.CreateGasAmsterdam
|
||||
}
|
||||
|
||||
// enable3198 applies EIP-3198 (BASEFEE Opcode)
|
||||
|
|
|
|||
109
core/vm/evm.go
109
core/vm/evm.go
|
|
@ -18,7 +18,9 @@ package vm
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"os"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
|
|
@ -318,11 +320,31 @@ func (evm *EVM) Call(caller common.Address, addr common.Address, input []byte, g
|
|||
}
|
||||
gas.Exhaust()
|
||||
}
|
||||
// EIP-8037: on error, return the child's state-gas-used to its
|
||||
// reservoir (state was rolled back, no state was grown), and reset
|
||||
// state_gas_used to 0 so it isn't propagated to the parent.
|
||||
if evm.chainRules.IsAmsterdam && gas.StateGasUsed > 0 {
|
||||
gas.StateGas += uint64(gas.StateGasUsed)
|
||||
gas.StateGasUsed = 0
|
||||
}
|
||||
} else {
|
||||
if evm.chainRules.IsAmsterdam {
|
||||
// Charge callee's state changes to the callee's gas.
|
||||
// EIP-8037 spec: this_call_cost = growth_cost - already_paid,
|
||||
// where already_paid is the sum of state_gas_used by successful
|
||||
// descendants (tracked in gas.StateGasUsed).
|
||||
if os.Getenv("DEBUG_8037") != "" {
|
||||
fmt.Fprintf(os.Stderr, "Call to %v depth=%d: closing snapshot2=%d (gas before charge: %v, StateGasUsed=%d)\n",
|
||||
addr, evm.depth, snapshot2, gas, gas.StateGasUsed)
|
||||
}
|
||||
bytesCharged := evm.StateDB.StateChangedBytes(snapshot2, false)
|
||||
stateGasCost := GasCosts{StateGas: bytesCharged * int64(evm.Context.CostPerStateByte)}
|
||||
alreadyPaid := gas.StateGasUsed
|
||||
thisCallCost := bytesCharged*int64(evm.Context.CostPerStateByte) - alreadyPaid
|
||||
stateGasCost := GasCosts{StateGas: thisCallCost}
|
||||
if os.Getenv("DEBUG_8037") != "" {
|
||||
fmt.Fprintf(os.Stderr, " bytesCharged=%d, alreadyPaid=%d, thisCallCost=%d, canAfford=%v\n",
|
||||
bytesCharged, alreadyPaid, thisCallCost, gas.CanAfford(stateGasCost))
|
||||
}
|
||||
if !gas.CanAfford(stateGasCost) {
|
||||
evm.StateDB.RevertToSnapshot(snapshot1)
|
||||
gas.Exhaust()
|
||||
|
|
@ -366,7 +388,10 @@ func (evm *EVM) CallCode(caller common.Address, addr common.Address, input []byt
|
|||
if !evm.Context.CanTransfer(evm.StateDB, caller, value) {
|
||||
return nil, gas, ErrInsufficientBalance
|
||||
}
|
||||
var snapshot = evm.StateDB.Snapshot()
|
||||
// EIP-8037: two-snapshot pattern (matches Call/DelegateCall) to avoid
|
||||
// double counting subcall state-gas in the parent's frame computation.
|
||||
snapshot1 := evm.StateDB.Snapshot()
|
||||
snapshot2 := evm.StateDB.Snapshot()
|
||||
|
||||
// It is allowed to call precompiles, even via delegatecall
|
||||
if p, isPrecompile := evm.precompile(addr); isPrecompile {
|
||||
|
|
@ -380,24 +405,45 @@ func (evm *EVM) CallCode(caller common.Address, addr common.Address, input []byt
|
|||
gas = contract.Gas
|
||||
}
|
||||
if err != nil {
|
||||
evm.StateDB.RevertToSnapshot(snapshot)
|
||||
evm.StateDB.RevertToSnapshot(snapshot1)
|
||||
if err != ErrExecutionReverted {
|
||||
if evm.Config.Tracer != nil && evm.Config.Tracer.OnGasChange != nil {
|
||||
evm.Config.Tracer.OnGasChange(gas.RegularGas, 0, tracing.GasChangeCallFailedExecution)
|
||||
}
|
||||
gas.Exhaust()
|
||||
}
|
||||
// EIP-8037: on error, return state-gas-used to reservoir.
|
||||
if evm.chainRules.IsAmsterdam && gas.StateGasUsed > 0 {
|
||||
gas.StateGas += uint64(gas.StateGasUsed)
|
||||
gas.StateGasUsed = 0
|
||||
}
|
||||
} else {
|
||||
if evm.chainRules.IsAmsterdam {
|
||||
bytesCharged := evm.StateDB.StateChangedBytes(snapshot, false)
|
||||
stateGasCost := GasCosts{StateGas: bytesCharged * int64(evm.Context.CostPerStateByte)}
|
||||
if os.Getenv("DEBUG_8037") != "" {
|
||||
fmt.Fprintf(os.Stderr, "CallCode to %v depth=%d (gas before charge: %v, StateGasUsed=%d)\n",
|
||||
addr, evm.depth, gas, gas.StateGasUsed)
|
||||
}
|
||||
bytesCharged := evm.StateDB.StateChangedBytes(snapshot2, false)
|
||||
alreadyPaid := gas.StateGasUsed
|
||||
thisCallCost := bytesCharged*int64(evm.Context.CostPerStateByte) - alreadyPaid
|
||||
stateGasCost := GasCosts{StateGas: thisCallCost}
|
||||
if os.Getenv("DEBUG_8037") != "" {
|
||||
fmt.Fprintf(os.Stderr, " bytesCharged=%d, alreadyPaid=%d, thisCallCost=%d, canAfford=%v\n",
|
||||
bytesCharged, alreadyPaid, thisCallCost, gas.CanAfford(stateGasCost))
|
||||
}
|
||||
if !gas.CanAfford(stateGasCost) {
|
||||
gas.Exhaust()
|
||||
return ret, gas, ErrOutOfGas
|
||||
}
|
||||
gas.Charge(stateGasCost)
|
||||
}
|
||||
evm.StateDB.CloseSnapshot(snapshot)
|
||||
evm.StateDB.CloseSnapshot(snapshot2)
|
||||
if evm.chainRules.IsAmsterdam {
|
||||
// Cache snapshot1's own bytes (no setup work for callcode),
|
||||
// excluding the snapshot2 subcall which was already charged.
|
||||
evm.StateDB.StateChangedBytes(snapshot1, true)
|
||||
}
|
||||
evm.StateDB.CloseSnapshot(snapshot1)
|
||||
}
|
||||
return ret, gas, err
|
||||
}
|
||||
|
|
@ -420,7 +466,11 @@ func (evm *EVM) DelegateCall(originCaller common.Address, caller common.Address,
|
|||
if evm.depth > int(params.CallCreateDepth) {
|
||||
return nil, gas, ErrDepth
|
||||
}
|
||||
var snapshot = evm.StateDB.Snapshot()
|
||||
// EIP-8037: two-snapshot pattern (matches Call). snapshot1 covers
|
||||
// any caller-frame setup (none for delegatecall, kept for symmetry);
|
||||
// snapshot2 covers the callee execution.
|
||||
snapshot1 := evm.StateDB.Snapshot()
|
||||
snapshot2 := evm.StateDB.Snapshot()
|
||||
|
||||
// It is allowed to call precompiles, even via delegatecall
|
||||
if p, isPrecompile := evm.precompile(addr); isPrecompile {
|
||||
|
|
@ -435,24 +485,45 @@ func (evm *EVM) DelegateCall(originCaller common.Address, caller common.Address,
|
|||
gas = contract.Gas
|
||||
}
|
||||
if err != nil {
|
||||
evm.StateDB.RevertToSnapshot(snapshot)
|
||||
evm.StateDB.RevertToSnapshot(snapshot1)
|
||||
if err != ErrExecutionReverted {
|
||||
if evm.Config.Tracer != nil && evm.Config.Tracer.OnGasChange != nil {
|
||||
evm.Config.Tracer.OnGasChange(gas.RegularGas, 0, tracing.GasChangeCallFailedExecution)
|
||||
}
|
||||
gas.Exhaust()
|
||||
}
|
||||
// EIP-8037: on error, return state-gas-used to reservoir.
|
||||
if evm.chainRules.IsAmsterdam && gas.StateGasUsed > 0 {
|
||||
gas.StateGas += uint64(gas.StateGasUsed)
|
||||
gas.StateGasUsed = 0
|
||||
}
|
||||
} else {
|
||||
if evm.chainRules.IsAmsterdam {
|
||||
bytesCharged := evm.StateDB.StateChangedBytes(snapshot, false)
|
||||
stateGasCost := GasCosts{StateGas: bytesCharged * int64(evm.Context.CostPerStateByte)}
|
||||
if os.Getenv("DEBUG_8037") != "" {
|
||||
fmt.Fprintf(os.Stderr, "DelegateCall to %v depth=%d (gas before charge: %v, StateGasUsed=%d)\n",
|
||||
addr, evm.depth, gas, gas.StateGasUsed)
|
||||
}
|
||||
bytesCharged := evm.StateDB.StateChangedBytes(snapshot2, false)
|
||||
alreadyPaid := gas.StateGasUsed
|
||||
thisCallCost := bytesCharged*int64(evm.Context.CostPerStateByte) - alreadyPaid
|
||||
stateGasCost := GasCosts{StateGas: thisCallCost}
|
||||
if os.Getenv("DEBUG_8037") != "" {
|
||||
fmt.Fprintf(os.Stderr, " bytesCharged=%d, alreadyPaid=%d, thisCallCost=%d, canAfford=%v\n",
|
||||
bytesCharged, alreadyPaid, thisCallCost, gas.CanAfford(stateGasCost))
|
||||
}
|
||||
if !gas.CanAfford(stateGasCost) {
|
||||
gas.Exhaust()
|
||||
return ret, gas, ErrOutOfGas
|
||||
}
|
||||
gas.Charge(stateGasCost)
|
||||
}
|
||||
evm.StateDB.CloseSnapshot(snapshot)
|
||||
evm.StateDB.CloseSnapshot(snapshot2)
|
||||
if evm.chainRules.IsAmsterdam {
|
||||
// Cache snapshot1's own bytes (= 0 for delegatecall, no setup),
|
||||
// excluding the snapshot2 subcall which was already charged.
|
||||
evm.StateDB.StateChangedBytes(snapshot1, true)
|
||||
}
|
||||
evm.StateDB.CloseSnapshot(snapshot1)
|
||||
}
|
||||
|
||||
return ret, gas, err
|
||||
|
|
@ -509,6 +580,10 @@ func (evm *EVM) StaticCall(caller common.Address, addr common.Address, input []b
|
|||
}
|
||||
gas.Exhaust()
|
||||
}
|
||||
if evm.chainRules.IsAmsterdam && gas.StateGasUsed > 0 {
|
||||
gas.StateGas += uint64(gas.StateGasUsed)
|
||||
gas.StateGasUsed = 0
|
||||
}
|
||||
} else {
|
||||
evm.StateDB.CloseSnapshot(snapshot)
|
||||
}
|
||||
|
|
@ -619,11 +694,21 @@ func (evm *EVM) create(caller common.Address, code []byte, gas GasBudget, value
|
|||
if err != ErrExecutionReverted {
|
||||
contract.UseGas(GasCosts{RegularGas: contract.Gas.RegularGas}, evm.Config.Tracer, tracing.GasChangeCallFailedExecution)
|
||||
}
|
||||
// EIP-8037: on error, return the child contract's state-gas-used
|
||||
// to its reservoir (state was rolled back). Don't propagate
|
||||
// state_gas_used to the parent.
|
||||
if evm.chainRules.IsAmsterdam && contract.Gas.StateGasUsed > 0 {
|
||||
contract.Gas.StateGas += uint64(contract.Gas.StateGasUsed)
|
||||
contract.Gas.StateGasUsed = 0
|
||||
}
|
||||
} else {
|
||||
if evm.chainRules.IsAmsterdam {
|
||||
// Charge initcode's state changes to the created contract's gas.
|
||||
// EIP-8037 spec: this_call_cost = growth_cost - already_paid.
|
||||
bytesCharged := evm.StateDB.StateChangedBytes(snapshot2, false)
|
||||
stateGasCost := GasCosts{StateGas: bytesCharged * int64(evm.Context.CostPerStateByte)}
|
||||
alreadyPaid := contract.Gas.StateGasUsed
|
||||
thisCallCost := bytesCharged*int64(evm.Context.CostPerStateByte) - alreadyPaid
|
||||
stateGasCost := GasCosts{StateGas: thisCallCost}
|
||||
if !contract.Gas.CanAfford(stateGasCost) {
|
||||
evm.StateDB.RevertToSnapshot(snapshot1)
|
||||
contract.Gas.Exhaust()
|
||||
|
|
|
|||
|
|
@ -430,16 +430,21 @@ func gasCallIntrinsic(evm *EVM, contract *Contract, stack *Stack, mem *Memory, m
|
|||
return GasCosts{}, ErrOutOfGas
|
||||
}
|
||||
// Stateful check
|
||||
var stateGas uint64
|
||||
if evm.chainRules.IsEIP158 {
|
||||
if transfersValue && evm.StateDB.Empty(address) {
|
||||
// EIP-8037: under Amsterdam the regular-gas portion of GAS_NEW_ACCOUNT
|
||||
// is removed (covered by CALL_VALUE 9000); the state-gas portion of
|
||||
// 112 × CPSB is charged at frame end via the journal walker.
|
||||
if !evm.chainRules.IsAmsterdam {
|
||||
var stateGas uint64
|
||||
if evm.chainRules.IsEIP158 {
|
||||
if transfersValue && evm.StateDB.Empty(address) {
|
||||
stateGas += params.CallNewAccountGas
|
||||
}
|
||||
} else if !evm.StateDB.Exist(address) {
|
||||
stateGas += params.CallNewAccountGas
|
||||
}
|
||||
} else if !evm.StateDB.Exist(address) {
|
||||
stateGas += params.CallNewAccountGas
|
||||
}
|
||||
if gas, overflow = math.SafeAdd(gas, stateGas); overflow {
|
||||
return GasCosts{}, ErrGasUintOverflow
|
||||
if gas, overflow = math.SafeAdd(gas, stateGas); overflow {
|
||||
return GasCosts{}, ErrGasUintOverflow
|
||||
}
|
||||
}
|
||||
return GasCosts{RegularGas: gas}, nil
|
||||
}
|
||||
|
|
@ -489,13 +494,18 @@ func gasSelfdestruct(evm *EVM, contract *Contract, stack *Stack, mem *Memory, me
|
|||
gas = params.SelfdestructGasEIP150
|
||||
var address = common.Address(stack.Back(0).Bytes20())
|
||||
|
||||
if evm.chainRules.IsEIP158 {
|
||||
// if empty and transfers value
|
||||
if evm.StateDB.Empty(address) && evm.StateDB.GetBalance(contract.Address()).Sign() != 0 {
|
||||
// EIP-8037: CreateBySelfdestructGas (25000 regular) is removed under
|
||||
// Amsterdam — account creation is now charged via state gas at frame
|
||||
// end via the journal walker.
|
||||
if !evm.chainRules.IsAmsterdam {
|
||||
if evm.chainRules.IsEIP158 {
|
||||
// if empty and transfers value
|
||||
if evm.StateDB.Empty(address) && evm.StateDB.GetBalance(contract.Address()).Sign() != 0 {
|
||||
gas += params.CreateBySelfdestructGas
|
||||
}
|
||||
} else if !evm.StateDB.Exist(address) {
|
||||
gas += params.CreateBySelfdestructGas
|
||||
}
|
||||
} else if !evm.StateDB.Exist(address) {
|
||||
gas += params.CreateBySelfdestructGas
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -59,6 +59,13 @@ type GasBudget struct {
|
|||
// Tracks the gas refunds in this call frame. Needed so we can
|
||||
// revert the refunds if the call frame reverts.
|
||||
StateGasRefund uint64
|
||||
|
||||
// EIP-8037 per-dimension usage trackers. RegularGasUsed is incremented
|
||||
// only for regular-gas charges; StateGasUsed is incremented for the
|
||||
// full state-gas portion of a charge regardless of whether it was
|
||||
// satisfied from the reservoir or spilled into RegularGas.
|
||||
RegularGasUsed uint64
|
||||
StateGasUsed int64
|
||||
}
|
||||
|
||||
// NewGasBudgetReg creates a GasBudget with the given initial regular gas allowance.
|
||||
|
|
@ -76,13 +83,22 @@ func (g GasBudget) Used(initial GasBudget) uint64 {
|
|||
return (initial.RegularGas + initial.StateGas) - (g.RegularGas + g.StateGas)
|
||||
}
|
||||
|
||||
// Exhaust burns the remaining regular gas on exceptional halt.
|
||||
// Exhaust burns the remaining regular gas on exceptional halt. The full
|
||||
// remaining regular gas is moved to RegularGasUsed so the per-tx tally
|
||||
// reflects the burn (per spec process_message exceptional-halt branch).
|
||||
func (g *GasBudget) Exhaust() {
|
||||
g.RegularGasUsed += g.RegularGas
|
||||
g.RegularGas = 0
|
||||
}
|
||||
|
||||
func (g *GasBudget) Copy() GasBudget {
|
||||
return GasBudget{RegularGas: g.RegularGas, StateGas: g.StateGas}
|
||||
return GasBudget{
|
||||
RegularGas: g.RegularGas,
|
||||
StateGas: g.StateGas,
|
||||
StateGasRefund: g.StateGasRefund,
|
||||
RegularGasUsed: g.RegularGasUsed,
|
||||
StateGasUsed: g.StateGasUsed,
|
||||
}
|
||||
}
|
||||
|
||||
// String returns a visual representation of the gas budget vector.
|
||||
|
|
@ -117,6 +133,8 @@ func (g *GasBudget) Charge(cost GasCosts) (uint64, bool) {
|
|||
return prior, false
|
||||
}
|
||||
g.RegularGas -= cost.RegularGas
|
||||
g.RegularGasUsed += cost.RegularGas
|
||||
g.StateGasUsed += cost.StateGas
|
||||
if cost.StateGas < 0 {
|
||||
g.StateGas -= uint64(cost.StateGas)
|
||||
return prior, true
|
||||
|
|
@ -132,10 +150,14 @@ func (g *GasBudget) Charge(cost GasCosts) (uint64, bool) {
|
|||
}
|
||||
|
||||
// Refund adds the given gas budget back. It returns the pre-refund regular gas
|
||||
// value and whether the budget was actually changed.
|
||||
// value and whether the budget was actually changed. Used trackers from the
|
||||
// other budget are accumulated so child-frame state-gas usage propagates to
|
||||
// the caller.
|
||||
func (g *GasBudget) Refund(other GasBudget) (uint64, bool) {
|
||||
prior := g.RegularGas
|
||||
g.RegularGas += other.RegularGas
|
||||
g.StateGas += other.StateGas
|
||||
g.RegularGasUsed += other.RegularGasUsed
|
||||
g.StateGasUsed += other.StateGasUsed
|
||||
return prior, other.RegularGas != 0 || other.StateGas != 0
|
||||
}
|
||||
|
|
|
|||
|
|
@ -669,7 +669,12 @@ func opCreate(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
|||
|
||||
scope.Contract.UseGas(GasCosts{RegularGas: gas}, evm.Config.Tracer, tracing.GasChangeCallContractCreation)
|
||||
|
||||
res, addr, returnGas, suberr := evm.Create(scope.Contract.Address(), input, NewGasBudgetReg(gas), &value)
|
||||
// EIP-8037: gas given to child via Create will be tracked there and
|
||||
// propagated via RefundGas. Uncount here to avoid double-counting.
|
||||
scope.Contract.Gas.RegularGasUsed -= gas
|
||||
childStateGas := scope.Contract.Gas.StateGas
|
||||
scope.Contract.Gas.StateGas = 0
|
||||
res, addr, returnGas, suberr := evm.Create(scope.Contract.Address(), input, NewGasBudget(gas, childStateGas), &value)
|
||||
// Push item on the stack based on the returned error. If the ruleset is
|
||||
// homestead we must check for CodeStoreOutOfGasError (homestead only
|
||||
// rule) and treat as an error, if the ruleset is frontier we must
|
||||
|
|
@ -708,9 +713,14 @@ func opCreate2(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
|||
// Apply EIP150
|
||||
gas -= gas / 64
|
||||
scope.Contract.UseGas(GasCosts{RegularGas: gas}, evm.Config.Tracer, tracing.GasChangeCallContractCreation2)
|
||||
// EIP-8037: gas given to child via Create2 will be tracked there and
|
||||
// propagated via RefundGas. Uncount here to avoid double-counting.
|
||||
scope.Contract.Gas.RegularGasUsed -= gas
|
||||
childStateGas := scope.Contract.Gas.StateGas
|
||||
scope.Contract.Gas.StateGas = 0
|
||||
// reuse size int for stackvalue
|
||||
stackvalue := size
|
||||
res, addr, returnGas, suberr := evm.Create2(scope.Contract.Address(), input, NewGasBudgetReg(gas),
|
||||
res, addr, returnGas, suberr := evm.Create2(scope.Contract.Address(), input, NewGasBudget(gas, childStateGas),
|
||||
&endowment, &salt)
|
||||
// Push item on the stack based on the returned error.
|
||||
if suberr != nil {
|
||||
|
|
@ -744,10 +754,24 @@ func opCall(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
|||
if evm.readOnly && !value.IsZero() {
|
||||
return nil, ErrWriteProtection
|
||||
}
|
||||
// EIP-8037: callGasTemp was already added to RegularGasUsed via the
|
||||
// dynamicGas mechanism, but the child will track its own usage and
|
||||
// propagate via RefundGas. Uncount it here to avoid double-counting.
|
||||
scope.Contract.Gas.RegularGasUsed -= evm.callGasTemp
|
||||
if !value.IsZero() {
|
||||
gas += params.CallStipend
|
||||
// EIP-8037: the stipend is "free" gas given to the child; it is
|
||||
// not charged to the caller's regular_gas_used. The child will
|
||||
// nevertheless track stipend usage via charge_gas and propagate
|
||||
// via RefundGas, so subtract it here so the caller doesn't pay
|
||||
// for stipend usage (matches spec MessageCallGas semantics).
|
||||
scope.Contract.Gas.RegularGasUsed -= params.CallStipend
|
||||
}
|
||||
ret, returnGas, err := evm.Call(scope.Contract.Address(), toAddr, args, NewGasBudgetReg(gas), &value)
|
||||
// EIP-8037: pass the parent's full state-gas reservoir to the child;
|
||||
// reset parent's reservoir to zero. Leftover comes back via RefundGas.
|
||||
childStateGas := scope.Contract.Gas.StateGas
|
||||
scope.Contract.Gas.StateGas = 0
|
||||
ret, returnGas, err := evm.Call(scope.Contract.Address(), toAddr, args, NewGasBudget(gas, childStateGas), &value)
|
||||
|
||||
if err != nil {
|
||||
temp.Clear()
|
||||
|
|
@ -777,11 +801,15 @@ func opCallCode(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
|||
// Get arguments from the memory.
|
||||
args := scope.Memory.GetPtr(inOffset.Uint64(), inSize.Uint64())
|
||||
|
||||
scope.Contract.Gas.RegularGasUsed -= evm.callGasTemp
|
||||
if !value.IsZero() {
|
||||
gas += params.CallStipend
|
||||
scope.Contract.Gas.RegularGasUsed -= params.CallStipend
|
||||
}
|
||||
|
||||
ret, returnGas, err := evm.CallCode(scope.Contract.Address(), toAddr, args, NewGasBudgetReg(gas), &value)
|
||||
childStateGas := scope.Contract.Gas.StateGas
|
||||
scope.Contract.Gas.StateGas = 0
|
||||
ret, returnGas, err := evm.CallCode(scope.Contract.Address(), toAddr, args, NewGasBudget(gas, childStateGas), &value)
|
||||
if err != nil {
|
||||
temp.Clear()
|
||||
} else {
|
||||
|
|
@ -810,7 +838,10 @@ func opDelegateCall(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
|||
// Get arguments from the memory.
|
||||
args := scope.Memory.GetPtr(inOffset.Uint64(), inSize.Uint64())
|
||||
|
||||
ret, returnGas, err := evm.DelegateCall(scope.Contract.Caller(), scope.Contract.Address(), toAddr, args, NewGasBudgetReg(gas), scope.Contract.value)
|
||||
scope.Contract.Gas.RegularGasUsed -= evm.callGasTemp
|
||||
childStateGas := scope.Contract.Gas.StateGas
|
||||
scope.Contract.Gas.StateGas = 0
|
||||
ret, returnGas, err := evm.DelegateCall(scope.Contract.Caller(), scope.Contract.Address(), toAddr, args, NewGasBudget(gas, childStateGas), scope.Contract.value)
|
||||
if err != nil {
|
||||
temp.Clear()
|
||||
} else {
|
||||
|
|
@ -839,7 +870,10 @@ func opStaticCall(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
|||
// Get arguments from the memory.
|
||||
args := scope.Memory.GetPtr(inOffset.Uint64(), inSize.Uint64())
|
||||
|
||||
ret, returnGas, err := evm.StaticCall(scope.Contract.Address(), toAddr, args, NewGasBudgetReg(gas))
|
||||
scope.Contract.Gas.RegularGasUsed -= evm.callGasTemp
|
||||
childStateGas := scope.Contract.Gas.StateGas
|
||||
scope.Contract.Gas.StateGas = 0
|
||||
ret, returnGas, err := evm.StaticCall(scope.Contract.Address(), toAddr, args, NewGasBudget(gas, childStateGas))
|
||||
if err != nil {
|
||||
temp.Clear()
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -196,6 +196,7 @@ func (evm *EVM) Run(contract *Contract, input []byte, readOnly bool) (ret []byte
|
|||
return nil, ErrOutOfGas
|
||||
} else {
|
||||
contract.Gas.RegularGas -= cost
|
||||
contract.Gas.RegularGasUsed += cost
|
||||
}
|
||||
|
||||
// All ops with a dynamic memory usage also has a dynamic gas cost.
|
||||
|
|
@ -229,6 +230,7 @@ func (evm *EVM) Run(contract *Contract, input []byte, readOnly bool) (ret []byte
|
|||
return nil, ErrOutOfGas
|
||||
} else {
|
||||
contract.Gas.RegularGas -= dynamicCost.RegularGas
|
||||
contract.Gas.RegularGasUsed += dynamicCost.RegularGas
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -186,6 +186,10 @@ func makeCallVariantGasCallEIP2929(oldCalculator gasFunc, addressPosition int) g
|
|||
// outside of this function, as part of the dynamic gas, and that will make it
|
||||
// also become correctly reported to tracers.
|
||||
contract.Gas.RegularGas += coldCost
|
||||
// Also undo the RegularGasUsed bookkeeping so coldCost isn't
|
||||
// double-counted (it will be re-added by the dynamicGas
|
||||
// mechanism via the returned cost).
|
||||
contract.Gas.RegularGasUsed -= coldCost
|
||||
|
||||
gas := gasCost.RegularGas
|
||||
var overflow bool
|
||||
|
|
@ -318,8 +322,13 @@ func makeSelfdestructGasFn(refundsEnabled bool) gasFunc {
|
|||
}
|
||||
}
|
||||
// if empty and transfers value
|
||||
if evm.StateDB.Empty(address) && evm.StateDB.GetBalance(contract.Address()).Sign() != 0 {
|
||||
gas += params.CreateBySelfdestructGas
|
||||
// EIP-8037: under Amsterdam the regular-gas portion of
|
||||
// CreateBySelfdestructGas (25,000) is removed; account creation
|
||||
// is charged via state gas at frame end.
|
||||
if !evm.chainRules.IsAmsterdam {
|
||||
if evm.StateDB.Empty(address) && evm.StateDB.GetBalance(contract.Address()).Sign() != 0 {
|
||||
gas += params.CreateBySelfdestructGas
|
||||
}
|
||||
}
|
||||
if refundsEnabled && !evm.StateDB.HasSelfDestructed(contract.Address()) {
|
||||
evm.StateDB.AddRefund(params.SelfdestructRefundGas)
|
||||
|
|
@ -411,6 +420,10 @@ func makeCallVariantGasCallEIP7702(intrinsicFunc gasFunc) gasFunc {
|
|||
// part of the dynamic gas. This will ensure it is correctly reported to
|
||||
// tracers.
|
||||
contract.Gas.RegularGas += eip2929Cost + eip7702Cost
|
||||
// Also undo the RegularGasUsed bookkeeping so eip2929/7702 costs aren't
|
||||
// double-counted (they will be re-added by the dynamicGas mechanism via
|
||||
// the returned cost).
|
||||
contract.Gas.RegularGasUsed -= eip2929Cost + eip7702Cost
|
||||
|
||||
// Aggregate the gas costs from all components, including EIP-2929, EIP-7702,
|
||||
// the CALL opcode itself, and the cost incurred by nested calls.
|
||||
|
|
|
|||
Loading…
Reference in a new issue