mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-06-10 17:01:35 +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 (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"maps"
|
"maps"
|
||||||
|
"os"
|
||||||
"slices"
|
"slices"
|
||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
|
|
@ -251,7 +252,7 @@ func (j *journal) stateChangedBytes(revid int, stateObjects map[common.Address]*
|
||||||
var subcallBytes int64
|
var subcallBytes int64
|
||||||
visit := func(e journalEntry) {
|
visit := func(e journalEntry) {
|
||||||
switch e := e.(type) {
|
switch e := e.(type) {
|
||||||
case createContractChange:
|
case createObjectChange:
|
||||||
created[e.account] = true
|
created[e.account] = true
|
||||||
case codeChange:
|
case codeChange:
|
||||||
codeChanged[e.account] = true
|
codeChanged[e.account] = true
|
||||||
|
|
@ -262,19 +263,27 @@ func (j *journal) stateChangedBytes(revid int, stateObjects map[common.Address]*
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pos := rev.journalIndex
|
if excludeSubcalls {
|
||||||
for _, child := range rev.closedChildren {
|
// Walk only this frame's own entries, skipping closed child ranges.
|
||||||
for ; pos < child.start; pos++ {
|
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])
|
visit(j.entries[pos])
|
||||||
}
|
}
|
||||||
if !excludeSubcalls {
|
} else {
|
||||||
// Add the cached cost for this subcall.
|
// Walk all entries (including subcall ranges) to compute the full
|
||||||
subcallBytes += j.stateBytesCharged[child.start]
|
// 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
|
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.
|
// Cache so the parent can look up this frame's total cost.
|
||||||
j.stateBytesCharged[rev.journalIndex] = totalBytes
|
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
|
return totalBytes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
"os"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core/tracing"
|
"github.com/ethereum/go-ethereum/core/tracing"
|
||||||
|
|
@ -557,6 +558,13 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
|
||||||
if !sufficient {
|
if !sufficient {
|
||||||
return nil, fmt.Errorf("%w: have %d, want %d", ErrIntrinsicGas, st.gasRemaining.RegularGas, cost.RegularGas)
|
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 t := st.evm.Config.Tracer; t != nil && t.OnGasChange != nil {
|
||||||
if rules.IsAmsterdam {
|
if rules.IsAmsterdam {
|
||||||
t.OnGasChange(msg.GasLimit, st.gasRemaining.RegularGas+st.gasRemaining.StateGas, tracing.GasChangeTxIntrinsicGas)
|
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
|
vmerr error // vm errors do not effect consensus and are therefore not assigned to err
|
||||||
)
|
)
|
||||||
|
|
||||||
// Take a snapshot for gas calculation
|
if !contractCreation {
|
||||||
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 {
|
|
||||||
// Increment the nonce for the next transaction.
|
// Increment the nonce for the next transaction.
|
||||||
st.state.SetNonce(msg.From, st.state.GetNonce(msg.From)+1, tracing.NonceChangeEoACall)
|
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 {
|
if msg.SetCodeAuthorizations != nil {
|
||||||
for _, auth := range msg.SetCodeAuthorizations {
|
for _, auth := range msg.SetCodeAuthorizations {
|
||||||
// Note errors are ignored, we simply skip invalid authorizations here.
|
// Note errors are ignored, we simply skip invalid authorizations here.
|
||||||
st.applyAuthorization(rules, &auth)
|
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
|
// Perform convenience warming of sender's delegation target. Although the
|
||||||
// sender is already warmed in Prepare(..), it's possible a delegation to
|
// 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)
|
outerBytes := st.state.StateChangedBytes(outerSnapshot, false)
|
||||||
// Refund state gas for selfdestructed accounts.
|
// Refund state gas for selfdestructed accounts.
|
||||||
outerBytes -= st.state.SelfDestructRefundBytes()
|
outerBytes -= st.state.SelfDestructRefundBytes()
|
||||||
st.gasRemaining.Charge(vm.GasCosts{StateGas: outerBytes * int64(st.evm.Context.CostPerStateByte)})
|
// For contract-creation txs the intrinsic already paid for the
|
||||||
} else {
|
// account creation; subtract it so we don't double-charge.
|
||||||
if execGasUsed.StateGas > 0 {
|
if contractCreation {
|
||||||
st.gasRemaining.StateGas += uint64(execGasUsed.StateGas)
|
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()
|
returned := st.returnGas()
|
||||||
if rules.IsAmsterdam {
|
if rules.IsAmsterdam {
|
||||||
// EIP-8037: 2D gas accounting for Amsterdam.
|
// EIP-8037: tx_regular = intrinsic_regular + execution_regular_gas_used
|
||||||
// tx_regular = initialBudget.RegularGas - gasRemaining.RegularGas
|
// (RegularGasUsed already includes intrinsic since it stays counted).
|
||||||
// tx_state = initialBudget.StateGas - gasRemaining.StateGas
|
// tx_state = intrinsic_state + execution_state_gas_used (StateGasUsed
|
||||||
txRegular := st.initialBudget.RegularGas - st.gasRemaining.RegularGas
|
// excludes intrinsic — it was undone after the intrinsic Charge).
|
||||||
|
txRegular := st.gasRemaining.RegularGasUsed
|
||||||
txRegular = max(txRegular, floorDataGas)
|
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 {
|
if err := st.gp.ReturnGasAmsterdam(txRegular, txState, st.gasUsed()); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -173,8 +173,14 @@ func enable3529(jt *JumpTable) {
|
||||||
// enable8037 enables EIP-8037 SSTORE repricing: the regular-gas portion of
|
// 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
|
// 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.
|
// 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) {
|
func enable8037(jt *JumpTable) {
|
||||||
jt[SSTORE].dynamicGas = gasSStoreEIP8037
|
jt[SSTORE].dynamicGas = gasSStoreEIP8037
|
||||||
|
jt[CREATE].constantGas = params.CreateGasAmsterdam
|
||||||
|
jt[CREATE2].constantGas = params.CreateGasAmsterdam
|
||||||
}
|
}
|
||||||
|
|
||||||
// enable3198 applies EIP-3198 (BASEFEE Opcode)
|
// enable3198 applies EIP-3198 (BASEFEE Opcode)
|
||||||
|
|
|
||||||
109
core/vm/evm.go
109
core/vm/evm.go
|
|
@ -18,7 +18,9 @@ package vm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
"os"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"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()
|
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 {
|
} else {
|
||||||
if evm.chainRules.IsAmsterdam {
|
if evm.chainRules.IsAmsterdam {
|
||||||
// Charge callee's state changes to the callee's gas.
|
// 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)
|
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) {
|
if !gas.CanAfford(stateGasCost) {
|
||||||
evm.StateDB.RevertToSnapshot(snapshot1)
|
evm.StateDB.RevertToSnapshot(snapshot1)
|
||||||
gas.Exhaust()
|
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) {
|
if !evm.Context.CanTransfer(evm.StateDB, caller, value) {
|
||||||
return nil, gas, ErrInsufficientBalance
|
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
|
// It is allowed to call precompiles, even via delegatecall
|
||||||
if p, isPrecompile := evm.precompile(addr); isPrecompile {
|
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
|
gas = contract.Gas
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
evm.StateDB.RevertToSnapshot(snapshot)
|
evm.StateDB.RevertToSnapshot(snapshot1)
|
||||||
if err != ErrExecutionReverted {
|
if err != ErrExecutionReverted {
|
||||||
if evm.Config.Tracer != nil && evm.Config.Tracer.OnGasChange != nil {
|
if evm.Config.Tracer != nil && evm.Config.Tracer.OnGasChange != nil {
|
||||||
evm.Config.Tracer.OnGasChange(gas.RegularGas, 0, tracing.GasChangeCallFailedExecution)
|
evm.Config.Tracer.OnGasChange(gas.RegularGas, 0, tracing.GasChangeCallFailedExecution)
|
||||||
}
|
}
|
||||||
gas.Exhaust()
|
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 {
|
} else {
|
||||||
if evm.chainRules.IsAmsterdam {
|
if evm.chainRules.IsAmsterdam {
|
||||||
bytesCharged := evm.StateDB.StateChangedBytes(snapshot, false)
|
if os.Getenv("DEBUG_8037") != "" {
|
||||||
stateGasCost := GasCosts{StateGas: bytesCharged * int64(evm.Context.CostPerStateByte)}
|
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) {
|
if !gas.CanAfford(stateGasCost) {
|
||||||
gas.Exhaust()
|
gas.Exhaust()
|
||||||
return ret, gas, ErrOutOfGas
|
return ret, gas, ErrOutOfGas
|
||||||
}
|
}
|
||||||
gas.Charge(stateGasCost)
|
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
|
return ret, gas, err
|
||||||
}
|
}
|
||||||
|
|
@ -420,7 +466,11 @@ func (evm *EVM) DelegateCall(originCaller common.Address, caller common.Address,
|
||||||
if evm.depth > int(params.CallCreateDepth) {
|
if evm.depth > int(params.CallCreateDepth) {
|
||||||
return nil, gas, ErrDepth
|
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
|
// It is allowed to call precompiles, even via delegatecall
|
||||||
if p, isPrecompile := evm.precompile(addr); isPrecompile {
|
if p, isPrecompile := evm.precompile(addr); isPrecompile {
|
||||||
|
|
@ -435,24 +485,45 @@ func (evm *EVM) DelegateCall(originCaller common.Address, caller common.Address,
|
||||||
gas = contract.Gas
|
gas = contract.Gas
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
evm.StateDB.RevertToSnapshot(snapshot)
|
evm.StateDB.RevertToSnapshot(snapshot1)
|
||||||
if err != ErrExecutionReverted {
|
if err != ErrExecutionReverted {
|
||||||
if evm.Config.Tracer != nil && evm.Config.Tracer.OnGasChange != nil {
|
if evm.Config.Tracer != nil && evm.Config.Tracer.OnGasChange != nil {
|
||||||
evm.Config.Tracer.OnGasChange(gas.RegularGas, 0, tracing.GasChangeCallFailedExecution)
|
evm.Config.Tracer.OnGasChange(gas.RegularGas, 0, tracing.GasChangeCallFailedExecution)
|
||||||
}
|
}
|
||||||
gas.Exhaust()
|
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 {
|
} else {
|
||||||
if evm.chainRules.IsAmsterdam {
|
if evm.chainRules.IsAmsterdam {
|
||||||
bytesCharged := evm.StateDB.StateChangedBytes(snapshot, false)
|
if os.Getenv("DEBUG_8037") != "" {
|
||||||
stateGasCost := GasCosts{StateGas: bytesCharged * int64(evm.Context.CostPerStateByte)}
|
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) {
|
if !gas.CanAfford(stateGasCost) {
|
||||||
gas.Exhaust()
|
gas.Exhaust()
|
||||||
return ret, gas, ErrOutOfGas
|
return ret, gas, ErrOutOfGas
|
||||||
}
|
}
|
||||||
gas.Charge(stateGasCost)
|
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
|
return ret, gas, err
|
||||||
|
|
@ -509,6 +580,10 @@ func (evm *EVM) StaticCall(caller common.Address, addr common.Address, input []b
|
||||||
}
|
}
|
||||||
gas.Exhaust()
|
gas.Exhaust()
|
||||||
}
|
}
|
||||||
|
if evm.chainRules.IsAmsterdam && gas.StateGasUsed > 0 {
|
||||||
|
gas.StateGas += uint64(gas.StateGasUsed)
|
||||||
|
gas.StateGasUsed = 0
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
evm.StateDB.CloseSnapshot(snapshot)
|
evm.StateDB.CloseSnapshot(snapshot)
|
||||||
}
|
}
|
||||||
|
|
@ -619,11 +694,21 @@ func (evm *EVM) create(caller common.Address, code []byte, gas GasBudget, value
|
||||||
if err != ErrExecutionReverted {
|
if err != ErrExecutionReverted {
|
||||||
contract.UseGas(GasCosts{RegularGas: contract.Gas.RegularGas}, evm.Config.Tracer, tracing.GasChangeCallFailedExecution)
|
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 {
|
} else {
|
||||||
if evm.chainRules.IsAmsterdam {
|
if evm.chainRules.IsAmsterdam {
|
||||||
// Charge initcode's state changes to the created contract's gas.
|
// 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)
|
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) {
|
if !contract.Gas.CanAfford(stateGasCost) {
|
||||||
evm.StateDB.RevertToSnapshot(snapshot1)
|
evm.StateDB.RevertToSnapshot(snapshot1)
|
||||||
contract.Gas.Exhaust()
|
contract.Gas.Exhaust()
|
||||||
|
|
|
||||||
|
|
@ -430,16 +430,21 @@ func gasCallIntrinsic(evm *EVM, contract *Contract, stack *Stack, mem *Memory, m
|
||||||
return GasCosts{}, ErrOutOfGas
|
return GasCosts{}, ErrOutOfGas
|
||||||
}
|
}
|
||||||
// Stateful check
|
// Stateful check
|
||||||
var stateGas uint64
|
// EIP-8037: under Amsterdam the regular-gas portion of GAS_NEW_ACCOUNT
|
||||||
if evm.chainRules.IsEIP158 {
|
// is removed (covered by CALL_VALUE 9000); the state-gas portion of
|
||||||
if transfersValue && evm.StateDB.Empty(address) {
|
// 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
|
stateGas += params.CallNewAccountGas
|
||||||
}
|
}
|
||||||
} else if !evm.StateDB.Exist(address) {
|
if gas, overflow = math.SafeAdd(gas, stateGas); overflow {
|
||||||
stateGas += params.CallNewAccountGas
|
return GasCosts{}, ErrGasUintOverflow
|
||||||
}
|
}
|
||||||
if gas, overflow = math.SafeAdd(gas, stateGas); overflow {
|
|
||||||
return GasCosts{}, ErrGasUintOverflow
|
|
||||||
}
|
}
|
||||||
return GasCosts{RegularGas: gas}, nil
|
return GasCosts{RegularGas: gas}, nil
|
||||||
}
|
}
|
||||||
|
|
@ -489,13 +494,18 @@ func gasSelfdestruct(evm *EVM, contract *Contract, stack *Stack, mem *Memory, me
|
||||||
gas = params.SelfdestructGasEIP150
|
gas = params.SelfdestructGasEIP150
|
||||||
var address = common.Address(stack.Back(0).Bytes20())
|
var address = common.Address(stack.Back(0).Bytes20())
|
||||||
|
|
||||||
if evm.chainRules.IsEIP158 {
|
// EIP-8037: CreateBySelfdestructGas (25000 regular) is removed under
|
||||||
// if empty and transfers value
|
// Amsterdam — account creation is now charged via state gas at frame
|
||||||
if evm.StateDB.Empty(address) && evm.StateDB.GetBalance(contract.Address()).Sign() != 0 {
|
// 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
|
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
|
// Tracks the gas refunds in this call frame. Needed so we can
|
||||||
// revert the refunds if the call frame reverts.
|
// revert the refunds if the call frame reverts.
|
||||||
StateGasRefund uint64
|
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.
|
// 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)
|
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() {
|
func (g *GasBudget) Exhaust() {
|
||||||
|
g.RegularGasUsed += g.RegularGas
|
||||||
g.RegularGas = 0
|
g.RegularGas = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *GasBudget) Copy() GasBudget {
|
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.
|
// 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
|
return prior, false
|
||||||
}
|
}
|
||||||
g.RegularGas -= cost.RegularGas
|
g.RegularGas -= cost.RegularGas
|
||||||
|
g.RegularGasUsed += cost.RegularGas
|
||||||
|
g.StateGasUsed += cost.StateGas
|
||||||
if cost.StateGas < 0 {
|
if cost.StateGas < 0 {
|
||||||
g.StateGas -= uint64(cost.StateGas)
|
g.StateGas -= uint64(cost.StateGas)
|
||||||
return prior, true
|
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
|
// 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) {
|
func (g *GasBudget) Refund(other GasBudget) (uint64, bool) {
|
||||||
prior := g.RegularGas
|
prior := g.RegularGas
|
||||||
g.RegularGas += other.RegularGas
|
g.RegularGas += other.RegularGas
|
||||||
g.StateGas += other.StateGas
|
g.StateGas += other.StateGas
|
||||||
|
g.RegularGasUsed += other.RegularGasUsed
|
||||||
|
g.StateGasUsed += other.StateGasUsed
|
||||||
return prior, other.RegularGas != 0 || other.StateGas != 0
|
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)
|
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
|
// Push item on the stack based on the returned error. If the ruleset is
|
||||||
// homestead we must check for CodeStoreOutOfGasError (homestead only
|
// homestead we must check for CodeStoreOutOfGasError (homestead only
|
||||||
// rule) and treat as an error, if the ruleset is frontier we must
|
// 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
|
// Apply EIP150
|
||||||
gas -= gas / 64
|
gas -= gas / 64
|
||||||
scope.Contract.UseGas(GasCosts{RegularGas: gas}, evm.Config.Tracer, tracing.GasChangeCallContractCreation2)
|
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
|
// reuse size int for stackvalue
|
||||||
stackvalue := size
|
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)
|
&endowment, &salt)
|
||||||
// Push item on the stack based on the returned error.
|
// Push item on the stack based on the returned error.
|
||||||
if suberr != nil {
|
if suberr != nil {
|
||||||
|
|
@ -744,10 +754,24 @@ func opCall(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
||||||
if evm.readOnly && !value.IsZero() {
|
if evm.readOnly && !value.IsZero() {
|
||||||
return nil, ErrWriteProtection
|
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() {
|
if !value.IsZero() {
|
||||||
gas += params.CallStipend
|
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 {
|
if err != nil {
|
||||||
temp.Clear()
|
temp.Clear()
|
||||||
|
|
@ -777,11 +801,15 @@ func opCallCode(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
||||||
// Get arguments from the memory.
|
// Get arguments from the memory.
|
||||||
args := scope.Memory.GetPtr(inOffset.Uint64(), inSize.Uint64())
|
args := scope.Memory.GetPtr(inOffset.Uint64(), inSize.Uint64())
|
||||||
|
|
||||||
|
scope.Contract.Gas.RegularGasUsed -= evm.callGasTemp
|
||||||
if !value.IsZero() {
|
if !value.IsZero() {
|
||||||
gas += params.CallStipend
|
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 {
|
if err != nil {
|
||||||
temp.Clear()
|
temp.Clear()
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -810,7 +838,10 @@ func opDelegateCall(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
||||||
// Get arguments from the memory.
|
// Get arguments from the memory.
|
||||||
args := scope.Memory.GetPtr(inOffset.Uint64(), inSize.Uint64())
|
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 {
|
if err != nil {
|
||||||
temp.Clear()
|
temp.Clear()
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -839,7 +870,10 @@ func opStaticCall(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
||||||
// Get arguments from the memory.
|
// Get arguments from the memory.
|
||||||
args := scope.Memory.GetPtr(inOffset.Uint64(), inSize.Uint64())
|
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 {
|
if err != nil {
|
||||||
temp.Clear()
|
temp.Clear()
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -196,6 +196,7 @@ func (evm *EVM) Run(contract *Contract, input []byte, readOnly bool) (ret []byte
|
||||||
return nil, ErrOutOfGas
|
return nil, ErrOutOfGas
|
||||||
} else {
|
} else {
|
||||||
contract.Gas.RegularGas -= cost
|
contract.Gas.RegularGas -= cost
|
||||||
|
contract.Gas.RegularGasUsed += cost
|
||||||
}
|
}
|
||||||
|
|
||||||
// All ops with a dynamic memory usage also has a dynamic gas 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
|
return nil, ErrOutOfGas
|
||||||
} else {
|
} else {
|
||||||
contract.Gas.RegularGas -= dynamicCost.RegularGas
|
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
|
// outside of this function, as part of the dynamic gas, and that will make it
|
||||||
// also become correctly reported to tracers.
|
// also become correctly reported to tracers.
|
||||||
contract.Gas.RegularGas += coldCost
|
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
|
gas := gasCost.RegularGas
|
||||||
var overflow bool
|
var overflow bool
|
||||||
|
|
@ -318,8 +322,13 @@ func makeSelfdestructGasFn(refundsEnabled bool) gasFunc {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// if empty and transfers value
|
// if empty and transfers value
|
||||||
if evm.StateDB.Empty(address) && evm.StateDB.GetBalance(contract.Address()).Sign() != 0 {
|
// EIP-8037: under Amsterdam the regular-gas portion of
|
||||||
gas += params.CreateBySelfdestructGas
|
// 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()) {
|
if refundsEnabled && !evm.StateDB.HasSelfDestructed(contract.Address()) {
|
||||||
evm.StateDB.AddRefund(params.SelfdestructRefundGas)
|
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
|
// part of the dynamic gas. This will ensure it is correctly reported to
|
||||||
// tracers.
|
// tracers.
|
||||||
contract.Gas.RegularGas += eip2929Cost + eip7702Cost
|
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,
|
// Aggregate the gas costs from all components, including EIP-2929, EIP-7702,
|
||||||
// the CALL opcode itself, and the cost incurred by nested calls.
|
// the CALL opcode itself, and the cost incurred by nested calls.
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue