core: improve EIP-8037 implementations

This commit is contained in:
Gary Rong 2026-05-18 11:19:25 +08:00
parent b86cca23a8
commit 5963fc8408
22 changed files with 333 additions and 288 deletions

View file

@ -133,8 +133,7 @@ func Transaction(ctx *cli.Context) error {
} }
// Check intrinsic gas // Check intrinsic gas
rules := chainConfig.Rules(common.Big0, true, 0) rules := chainConfig.Rules(common.Big0, true, 0)
gasCostPerStateByte := core.CostPerStateByte(&types.Header{}, chainConfig) cost, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.SetCodeAuthorizations(), tx.To() == nil, rules, params.CostPerStateByte)
cost, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.SetCodeAuthorizations(), tx.To() == nil, rules, gasCostPerStateByte)
if err != nil { if err != nil {
r.Error = err r.Error = err
results = append(results, r) results = append(results, r)

View file

@ -89,8 +89,7 @@ func genValueTx(nbytes int) func(int, *BlockGen) {
data := make([]byte, nbytes) data := make([]byte, nbytes)
return func(i int, gen *BlockGen) { return func(i int, gen *BlockGen) {
toaddr := common.Address{} toaddr := common.Address{}
gasCostPerStateByte := CostPerStateByte(gen.header, gen.cm.config) cost, _ := IntrinsicGas(data, nil, nil, false, params.Rules{}, params.CostPerStateByte)
cost, _ := IntrinsicGas(data, nil, nil, false, params.Rules{}, gasCostPerStateByte)
signer := gen.Signer() signer := gen.Signer()
gasPrice := big.NewInt(0) gasPrice := big.NewInt(0)
if gen.header.BaseFee != nil { if gen.header.BaseFee != nil {

View file

@ -168,7 +168,7 @@ func (b *BlockGen) AddTxWithChain(bc *BlockChain, tx *types.Transaction) {
// been set, the block's coinbase is set to the zero address. // been set, the block's coinbase is set to the zero address.
// The evm interpreter can be customized with the provided vm config. // The evm interpreter can be customized with the provided vm config.
func (b *BlockGen) AddTxWithVMConfig(tx *types.Transaction, config vm.Config) { func (b *BlockGen) AddTxWithVMConfig(tx *types.Transaction, config vm.Config) {
b.addTx(&BlockChain{chainConfig: b.cm.config}, config, tx) b.addTx(nil, config, tx)
} }
// GetBalance returns the balance of the given address at the generated block. // GetBalance returns the balance of the given address at the generated block.

View file

@ -80,19 +80,10 @@ func NewEVMBlockContext(header *types.Header, chain ChainContext, author *common
GasLimit: header.GasLimit, GasLimit: header.GasLimit,
Random: random, Random: random,
SlotNum: slotNum, SlotNum: slotNum,
CostPerStateByte: CostPerStateByte(header, chain.Config()), CostPerStateByte: params.CostPerStateByte,
} }
} }
// CostPerStateByte computes the cost per one byte of state creation
// after EIP-8037.
func CostPerStateByte(header *types.Header, config *params.ChainConfig) uint64 {
if !config.IsAmsterdam(header.Number, header.Time) {
return 0
}
return params.CostPerStateByte
}
// NewEVMTxContext creates a new transaction context for a single transaction. // NewEVMTxContext creates a new transaction context for a single transaction.
func NewEVMTxContext(msg *Message) vm.TxContext { func NewEVMTxContext(msg *Message) vm.TxContext {
ctx := vm.TxContext{ ctx := vm.TxContext{

View file

@ -42,9 +42,9 @@ func NewGasPool(amount uint64) *GasPool {
} }
} }
// SubGas deducts the given amount from the pool if enough gas is // CheckGasLegacy deducts the given amount from the pool if enough gas is
// available and returns an error otherwise. // available and returns an error otherwise.
func (gp *GasPool) SubGas(amount uint64) error { func (gp *GasPool) CheckGasLegacy(amount uint64) error {
if gp.remaining < amount { if gp.remaining < amount {
return ErrGasLimitReached return ErrGasLimitReached
} }
@ -65,31 +65,24 @@ func (gp *GasPool) CheckGasAmsterdam(regularReservation, stateReservation uint64
return nil return nil
} }
// ReturnGas adds the refunded gas back to the pool and updates // ChargeGasLegacy adds the refunded gas back to the pool and updates
// the cumulative gas usage accordingly. // the cumulative gas usage accordingly.
func (gp *GasPool) ReturnGas(returned uint64, gasUsed uint64) error { func (gp *GasPool) ChargeGasLegacy(returned uint64, gasUsed uint64) error {
if gp.remaining > math.MaxUint64-returned { if gp.remaining > math.MaxUint64-returned {
return fmt.Errorf("%w: remaining: %d, returned: %d", ErrGasLimitOverflow, gp.remaining, returned) return fmt.Errorf("%w: remaining: %d, returned: %d", ErrGasLimitOverflow, gp.remaining, returned)
} }
// The returned gas calculation differs across forks.
//
// - Pre-Amsterdam:
// returned = purchased - remaining (refund included) // returned = purchased - remaining (refund included)
//
// - Post-Amsterdam:
// returned = purchased - gasUsed (refund excluded)
gp.remaining += returned gp.remaining += returned
// gasUsed = max(txGasUsed - gasRefund, calldataFloorGasCost) // gasUsed = max(txGasUsed - gasRefund, calldataFloorGasCost)
// regardless of Amsterdam is activated or not.
gp.cumulativeUsed += gasUsed gp.cumulativeUsed += gasUsed
return nil return nil
} }
// ChargeGasAmsterdam calculates the new remaining gas in the pool after the // ChargeGasAmsterdam calculates the new remaining gas in the pool after the
// execution of a message. Previously we subtracted and re-added gas to the // execution of a message. Previously we subtracted and re-added gas to the
// gaspool. After Amsterdam we only check if we can include the transaction and charge the // gaspool. After Amsterdam we only check if we can include the transaction
// gaspool at the end. // and charge the gaspool at the end.
func (gp *GasPool) ChargeGasAmsterdam(txRegular, txState, receiptGasUsed uint64) error { func (gp *GasPool) ChargeGasAmsterdam(txRegular, txState, receiptGasUsed uint64) error {
gp.cumulativeRegular += txRegular gp.cumulativeRegular += txRegular
gp.cumulativeState += txState gp.cumulativeState += txState

View file

@ -683,34 +683,6 @@ func (s *StateDB) IsNewContract(addr common.Address) bool {
return obj.newContract return obj.newContract
} }
// SameTxSelfDestructs returns the addresses that were both created and
// self-destructed in the current transaction (EIP-6780).
func (s *StateDB) SameTxSelfDestructs() []common.Address {
var out []common.Address
for addr, obj := range s.stateObjects {
if obj.newContract && obj.selfDestructed {
out = append(out, addr)
}
}
return out
}
// NewStorageSlotCount returns the number of storage slots that were written
// to a non-zero value in the current transaction on the given account.
func (s *StateDB) NewStorageSlotCount(addr common.Address) int {
obj, ok := s.stateObjects[addr]
if !ok {
return 0
}
var count int
for _, v := range obj.dirtyStorage {
if v != (common.Hash{}) {
count++
}
}
return count
}
// Copy creates a deep, independent copy of the state. // Copy creates a deep, independent copy of the state.
// Snapshots of the copied state cannot be applied to the copy. // Snapshots of the copied state cannot be applied to the copy.
func (s *StateDB) Copy() *StateDB { func (s *StateDB) Copy() *StateDB {

View file

@ -59,14 +59,6 @@ func (s *hookedStateDB) IsNewContract(addr common.Address) bool {
return s.inner.IsNewContract(addr) return s.inner.IsNewContract(addr)
} }
func (s *hookedStateDB) SameTxSelfDestructs() []common.Address {
return s.inner.SameTxSelfDestructs()
}
func (s *hookedStateDB) NewStorageSlotCount(addr common.Address) int {
return s.inner.NewStorageSlotCount(addr)
}
func (s *hookedStateDB) GetBalance(addr common.Address) *uint256.Int { func (s *hookedStateDB) GetBalance(addr common.Address) *uint256.Int {
return s.inner.GetBalance(addr) return s.inner.GetBalance(addr)
} }

View file

@ -277,9 +277,15 @@ func ProcessBeaconBlockRoot(beaconRoot common.Hash, evm *vm.EVM, blockAccessList
defer tracer.OnSystemCallEnd() defer tracer.OnSystemCallEnd()
} }
} }
// SYSTEM_MAX_SSTORES_PER_CALL = 16 is the upper bound on the number of
// new storage slots a single system call is expected to write.
//
// This value matches MAX_WITHDRAWAL_REQUESTS_PER_BLOCK (EIP-7002), the
// largest per-block bound across the existing system contracts.
stateBudget := params.SystemMaxSStoresPerCall * evm.Context.CostPerStateByte * params.StorageCreationSize
msg := &Message{ msg := &Message{
From: params.SystemAddress, From: params.SystemAddress,
GasLimit: 30_000_000, GasLimit: 30_000_000 + stateBudget,
GasPrice: uint256.NewInt(0), GasPrice: uint256.NewInt(0),
GasFeeCap: uint256.NewInt(0), GasFeeCap: uint256.NewInt(0),
GasTipCap: uint256.NewInt(0), GasTipCap: uint256.NewInt(0),
@ -290,7 +296,7 @@ func ProcessBeaconBlockRoot(beaconRoot common.Hash, evm *vm.EVM, blockAccessList
evm.StateDB.Prepare(evm.GetRules(), common.Address{}, common.Address{}, nil, nil, nil) evm.StateDB.Prepare(evm.GetRules(), common.Address{}, common.Address{}, nil, nil, nil)
evm.StateDB.SetTxContext(common.Hash{}, 0, 0) evm.StateDB.SetTxContext(common.Hash{}, 0, 0)
evm.StateDB.AddAddressToAccessList(params.BeaconRootsAddress) evm.StateDB.AddAddressToAccessList(params.BeaconRootsAddress)
_, _, _ = evm.Call(msg.From, *msg.To, msg.Data, vm.NewGasBudget(30_000_000, 0), common.U2560) _, _, _ = evm.Call(msg.From, *msg.To, msg.Data, vm.NewGasBudget(30_000_000, stateBudget), common.U2560)
if evm.StateDB.AccessEvents() != nil { if evm.StateDB.AccessEvents() != nil {
evm.StateDB.AccessEvents().Merge(evm.AccessEvents) evm.StateDB.AccessEvents().Merge(evm.AccessEvents)
} }
@ -306,9 +312,15 @@ func ProcessParentBlockHash(prevHash common.Hash, evm *vm.EVM, blockAccessList *
defer tracer.OnSystemCallEnd() defer tracer.OnSystemCallEnd()
} }
} }
// SYSTEM_MAX_SSTORES_PER_CALL = 16 is the upper bound on the number of
// new storage slots a single system call is expected to write.
//
// This value matches MAX_WITHDRAWAL_REQUESTS_PER_BLOCK (EIP-7002), the
// largest per-block bound across the existing system contracts.
stateBudget := params.SystemMaxSStoresPerCall * evm.Context.CostPerStateByte * params.StorageCreationSize
msg := &Message{ msg := &Message{
From: params.SystemAddress, From: params.SystemAddress,
GasLimit: 30_000_000, GasLimit: 30_000_000 + stateBudget,
GasPrice: uint256.NewInt(0), GasPrice: uint256.NewInt(0),
GasFeeCap: uint256.NewInt(0), GasFeeCap: uint256.NewInt(0),
GasTipCap: uint256.NewInt(0), GasTipCap: uint256.NewInt(0),
@ -319,7 +331,7 @@ func ProcessParentBlockHash(prevHash common.Hash, evm *vm.EVM, blockAccessList *
evm.StateDB.Prepare(evm.GetRules(), common.Address{}, common.Address{}, nil, nil, nil) evm.StateDB.Prepare(evm.GetRules(), common.Address{}, common.Address{}, nil, nil, nil)
evm.StateDB.SetTxContext(common.Hash{}, 0, 0) evm.StateDB.SetTxContext(common.Hash{}, 0, 0)
evm.StateDB.AddAddressToAccessList(params.HistoryStorageAddress) evm.StateDB.AddAddressToAccessList(params.HistoryStorageAddress)
_, _, err := evm.Call(msg.From, *msg.To, msg.Data, vm.NewGasBudget(30_000_000, 0), common.U2560) _, _, err := evm.Call(msg.From, *msg.To, msg.Data, vm.NewGasBudget(30_000_000, stateBudget), common.U2560)
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -348,9 +360,15 @@ func processRequestsSystemCall(requests *[][]byte, rules params.Rules, evm *vm.E
defer tracer.OnSystemCallEnd() defer tracer.OnSystemCallEnd()
} }
} }
// SYSTEM_MAX_SSTORES_PER_CALL = 16 is the upper bound on the number of
// new storage slots a single system call is expected to write.
//
// This value matches MAX_WITHDRAWAL_REQUESTS_PER_BLOCK (EIP-7002), the
// largest per-block bound across the existing system contracts.
stateBudget := params.SystemMaxSStoresPerCall * evm.Context.CostPerStateByte * params.StorageCreationSize
msg := &Message{ msg := &Message{
From: params.SystemAddress, From: params.SystemAddress,
GasLimit: 30_000_000, GasLimit: 30_000_000 + stateBudget,
GasPrice: uint256.NewInt(0), GasPrice: uint256.NewInt(0),
GasFeeCap: uint256.NewInt(0), GasFeeCap: uint256.NewInt(0),
GasTipCap: uint256.NewInt(0), GasTipCap: uint256.NewInt(0),
@ -360,7 +378,7 @@ func processRequestsSystemCall(requests *[][]byte, rules params.Rules, evm *vm.E
evm.StateDB.Prepare(rules, common.Address{}, common.Address{}, nil, nil, nil) evm.StateDB.Prepare(rules, common.Address{}, common.Address{}, nil, nil, nil)
evm.StateDB.SetTxContext(common.Hash{}, 0, blockAccessIndex) evm.StateDB.SetTxContext(common.Hash{}, 0, blockAccessIndex)
evm.StateDB.AddAddressToAccessList(addr) evm.StateDB.AddAddressToAccessList(addr)
ret, _, err := evm.Call(msg.From, *msg.To, msg.Data, vm.NewGasBudget(30_000_000, 0), common.U2560) ret, _, err := evm.Call(msg.From, *msg.To, msg.Data, vm.NewGasBudget(30_000_000, stateBudget), common.U2560)
if evm.StateDB.AccessEvents() != nil { if evm.StateDB.AccessEvents() != nil {
evm.StateDB.AccessEvents().Merge(evm.AccessEvents) evm.StateDB.AccessEvents().Merge(evm.AccessEvents)
} }

View file

@ -27,6 +27,7 @@ import (
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto/kzg4844" "github.com/ethereum/go-ethereum/crypto/kzg4844"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/holiman/uint256" "github.com/holiman/uint256"
) )
@ -375,6 +376,24 @@ func (st *stateTransition) to() common.Address {
return *st.msg.To return *st.msg.To
} }
// buyGas pre-pays gas from the sender's balance and initializes the
// transaction's gas budget. It is invoked at the tail of preCheck.
//
// The balance requirement is the worst-case ETH the tx may need to lock
// up: `msg.GasLimit × max(msg.GasPrice, msg.GasFeeCap) + msg.Value`,
// plus `blobGas × msg.BlobGasFeeCap` under Cancun. Insufficient balance
// returns ErrInsufficientFunds. After the check, the sender is actually
// debited `msg.GasLimit × msg.GasPrice` (plus `blobGas × blobBaseFee`
// under Cancun), the cap-vs-tip differential is settled at tx end.
//
// The gas budget is seeded into both `initialBudget` (frozen snapshot
// for tx-end accounting) and `gasRemaining` (live running balance):
//
// - Pre-Amsterdam: one-dimensional regular budget equal to
// `msg.GasLimit`; the state-gas reservoir is zero.
// - Amsterdam+ (EIP-8037): two-dimensional budget. Regular gas is
// capped at `MaxTxGas` (EIP-7825, 16_777_216); any excess from
// `msg.GasLimit` above that cap becomes the state-gas reservoir.
func (st *stateTransition) buyGas() error { func (st *stateTransition) buyGas() error {
mgval := new(uint256.Int).SetUint64(st.msg.GasLimit) mgval := new(uint256.Int).SetUint64(st.msg.GasLimit)
_, overflow := mgval.MulOverflow(mgval, st.msg.GasPrice) _, overflow := mgval.MulOverflow(mgval, st.msg.GasPrice)
@ -428,7 +447,7 @@ func (st *stateTransition) buyGas() error {
return fmt.Errorf("%w: address %v have %v want %v", ErrInsufficientFunds, st.msg.From.Hex(), have, want) return fmt.Errorf("%w: address %v have %v want %v", ErrInsufficientFunds, st.msg.From.Hex(), have, want)
} }
// After Amsterdam we limit the regular gas to 16k, the data gas to the transaction limit // After Amsterdam we limit the regular gas to 16M, the data gas to the transaction limit
limit := st.msg.GasLimit limit := st.msg.GasLimit
if st.evm.ChainConfig().IsAmsterdam(st.evm.Context.BlockNumber, st.evm.Context.Time) { if st.evm.ChainConfig().IsAmsterdam(st.evm.Context.BlockNumber, st.evm.Context.Time) {
limit = min(st.msg.GasLimit, params.MaxTxGas) limit = min(st.msg.GasLimit, params.MaxTxGas)
@ -437,13 +456,32 @@ func (st *stateTransition) buyGas() error {
st.gasRemaining = st.initialBudget.Copy() st.gasRemaining = st.initialBudget.Copy()
if st.evm.Config.Tracer.HasGasHook() { if st.evm.Config.Tracer.HasGasHook() {
empty := vm.GasBudget{} st.evm.Config.Tracer.EmitGasChange(tracing.Gas{}, st.gasRemaining.AsTracing(), tracing.GasChangeTxInitialBalance)
st.evm.Config.Tracer.EmitGasChange(empty.AsTracing(), st.gasRemaining.AsTracing(), tracing.GasChangeTxInitialBalance)
} }
// Deduct the gas cost from the sender's balance
st.state.SubBalance(st.msg.From, mgval, tracing.BalanceDecreaseGasBuy) st.state.SubBalance(st.msg.From, mgval, tracing.BalanceDecreaseGasBuy)
return nil return nil
} }
// preCheck performs all pre-execution validation that does not require
// the EVM to run, then ends by calling buyGas to lock in the gas budget.
// It returns a consensus error if any of the following fail:
//
// - Sender nonce matches state and is not at 2^64-1 (EIP-2681).
// - EIP-7825 per-tx gas-limit cap on Osaka chains pre-Amsterdam
// (the cap also bounds the regular dimension after Amsterdam, but
// it is enforced there via the two-dimensional budget in buyGas).
// - EIP-3607 sender-is-EOA, allowing accounts whose only code is an
// EIP-7702 delegation designator.
// - EIP-1559 fee-cap, tip-cap and base-fee constraints (London+).
// - Blob-tx structural checks: non-nil `To`, non-empty hash list,
// valid KZG versioned hashes, count below `BlobTxMaxBlobs` (Osaka+).
// - Blob fee-cap not below the current blob base fee (Cancun+).
// - EIP-7702 set-code-tx shape: non-nil `To` and non-empty
// authorization list.
//
// The SkipNonceChecks / SkipTransactionChecks / NoBaseFee flags bypass
// subsets of these checks for simulation paths (eth_call, eth_estimateGas).
func (st *stateTransition) preCheck() error { func (st *stateTransition) preCheck() error {
// Only check transactions that are not fake // Only check transactions that are not fake
msg := st.msg msg := st.msg
@ -461,8 +499,10 @@ func (st *stateTransition) preCheck() error {
msg.From.Hex(), stNonce) msg.From.Hex(), stNonce)
} }
} }
isOsaka := st.evm.ChainConfig().IsOsaka(st.evm.Context.BlockNumber, st.evm.Context.Time) var (
isAmsterdam := st.evm.ChainConfig().IsAmsterdam(st.evm.Context.BlockNumber, st.evm.Context.Time) isOsaka = st.evm.ChainConfig().IsOsaka(st.evm.Context.BlockNumber, st.evm.Context.Time)
isAmsterdam = st.evm.ChainConfig().IsAmsterdam(st.evm.Context.BlockNumber, st.evm.Context.Time)
)
if !msg.SkipTransactionChecks { if !msg.SkipTransactionChecks {
// Verify tx gas limit does not exceed EIP-7825 cap. // Verify tx gas limit does not exceed EIP-7825 cap.
if !isAmsterdam && isOsaka && msg.GasLimit > params.MaxTxGas { if !isAmsterdam && isOsaka && msg.GasLimit > params.MaxTxGas {
@ -539,28 +579,72 @@ func (st *stateTransition) preCheck() error {
return st.buyGas() return st.buyGas()
} }
// execute will transition the state by applying the current message and // reserveBlockGasBudget checks if the remaining gas budget in the block pool is
// returning the evm execution result with following fields. // sufficient for including this transaction.
// func (st *stateTransition) reserveBlockGasBudget(rules params.Rules, gasLimit uint64, intrinsicCost vm.GasCosts) error {
// - used gas: total gas used (including gas being refunded) var err error
// - returndata: the returned data from evm if rules.IsAmsterdam {
// - concrete execution error: various EVM errors which abort the execution, e.g. // EIP-8037 per-tx 2D block-inclusion check. For each dimension,
// ErrOutOfGas, ErrExecutionReverted // the worst-case contribution is tx.gas minus the other
// // dimension's intrinsic (capped at MaxTxGas for the regular
// However if any consensus issue encountered, return the error directly with // dimension).
// nil evm execution result. regularReservation := gasLimit
func (st *stateTransition) execute() (*ExecutionResult, error) { if regularReservation > intrinsicCost.StateGas {
// First check this message satisfies all consensus rules before regularReservation -= intrinsicCost.StateGas
// applying the message. The rules include these clauses } else {
// regularReservation = 0
// 1. the nonce of the message caller is correct }
// 2. caller has enough balance to cover transaction fee(gaslimit * gasprice) regularReservation = min(regularReservation, params.MaxTxGas)
// 3. the amount of gas required is available in the block
// 4. the purchased gas is enough to cover intrinsic usage
// 5. there is no overflow when calculating intrinsic gas
// 6. caller has enough balance to cover asset transfer for **topmost** call
// Check clauses 1-3, buy gas if everything is correct stateReservation := gasLimit
if stateReservation > intrinsicCost.RegularGas {
stateReservation -= intrinsicCost.RegularGas
} else {
stateReservation = 0
}
err = st.gp.CheckGasAmsterdam(regularReservation, stateReservation)
} else {
err = st.gp.CheckGasLegacy(gasLimit)
}
return err
}
// execute transitions the state by applying the current message and
// returns the EVM execution result with the following fields:
//
// - used gas: total gas used, including gas refunded
// - peak used gas: maximum gas used before applying refunds
// - returndata: data returned by the EVM
// - execution error: EVM-level errors that abort execution, such as
// ErrOutOfGas or ErrExecutionReverted
//
// If a consensus error is encountered, it is returned directly with a
// nil EVM execution result.
func (st *stateTransition) execute() (*ExecutionResult, error) {
// The state-transition pipeline below runs in stages. Each stage may
// abort with a consensus error before the EVM is invoked:
//
// 1. preCheck: nonce, fee-cap, blob and EIP-7702 structural
// checks; ends by calling buyGas to debit the
// sender and seed the two-dimensional gas budget
// (EIP-8037).
// 2. Intrinsic: charges the intrinsic regular + state cost from
// the running budget with overflow detection.
// 3. Block pool: per-dimension inclusion reservation against the
// block gas pool (two-dimensional after Amsterdam,
// EIP-8037).
// 4. Floor pre: EIP-7623 calldata floor must fit in the gas allowance.
// 5. Top-call: run the top-most call, ensuring sender can cover
// the value transfer of the top call frame; init-code
// size respects the cap.
//
// After the EVM has run, the result path applies EIP-8037 state-gas
// refunds, the EIP-3529 regular-refund cap, and the EIP-7623 scalar
// floor (`tx_gas_used = max(tx_gas_used_after_refund, floor)`),
// returns leftover gas to the sender, settles the block pool and
// pays the coinbase tip.
// Stage 1: validate the message and pre-pay gas.
if err := st.preCheck(); err != nil { if err := st.preCheck(); err != nil {
return nil, err return nil, err
} }
@ -572,7 +656,9 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
floorDataGas uint64 floorDataGas uint64
) )
// Check clauses 4-5, subtract intrinsic gas if everything is correct // Stage 2: charge intrinsic gas (with overflow detection inside
// IntrinsicGas). Under Amsterdam the cost is two-dimensional and
// Charge debits both regular and state in one step.
cost, err := IntrinsicGas(msg.Data, msg.AccessList, msg.SetCodeAuthorizations, contractCreation, rules, st.evm.Context.CostPerStateByte) cost, err := IntrinsicGas(msg.Data, msg.AccessList, msg.SetCodeAuthorizations, contractCreation, rules, st.evm.Context.CostPerStateByte)
if err != nil { if err != nil {
return nil, err return nil, err
@ -585,62 +671,33 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
st.evm.Config.Tracer.EmitGasChange(prior.AsTracing(), st.gasRemaining.AsTracing(), tracing.GasChangeTxIntrinsicGas) st.evm.Config.Tracer.EmitGasChange(prior.AsTracing(), st.gasRemaining.AsTracing(), tracing.GasChangeTxIntrinsicGas)
} }
// Check if we have enough gas in the block // Stage 3: reserve this tx's share of the block gas pool. Under
if rules.IsAmsterdam { // Amsterdam this is a two-dimensional per-tx inclusion check; before
// EIP-8037 per-tx 2D block-inclusion check. For each dimension, // Amsterdam it is a single scalar subtraction.
// the worst-case contribution is tx.gas minus the other if err := st.reserveBlockGasBudget(rules, msg.GasLimit, cost); err != nil {
// dimension's intrinsic (capped at MaxTxGas for the regular
// dimension).
regularReservation := msg.GasLimit
if regularReservation > cost.StateGas {
regularReservation -= cost.StateGas
} else {
regularReservation = 0
}
regularReservation = min(regularReservation, params.MaxTxGas)
stateReservation := msg.GasLimit
if stateReservation > cost.RegularGas {
stateReservation -= cost.RegularGas
} else {
stateReservation = 0
}
if err := st.gp.CheckGasAmsterdam(regularReservation, stateReservation); err != nil {
return nil, err return nil, err
} }
} else {
if err := st.gp.SubGas(msg.GasLimit); err != nil {
return nil, err
}
}
// Compute the floor data cost (EIP-7623), needed for both Prague and Amsterdam validation. // Stage 4: validate the EIP-7623 calldata floor against the gas limit.
// The floor inflates the total gas usage at tx end, so the gas limit
// must be sufficient to cover that.
if rules.IsPrague { if rules.IsPrague {
floorDataGas, err = FloorDataGas(rules, msg.Data, msg.AccessList) floorDataGas, err = FloorDataGas(rules, msg.Data, msg.AccessList)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// Make sure the transaction has sufficient gas allowance to
// pay the floor cost.
if msg.GasLimit < floorDataGas { if msg.GasLimit < floorDataGas {
return nil, fmt.Errorf("%w: have %d, want %d", ErrIntrinsicGas, msg.GasLimit, floorDataGas) return nil, fmt.Errorf("%w: have %d, want %d", ErrIntrinsicGas, msg.GasLimit, floorDataGas)
} }
} // In Amsterdam, the transaction gas limit is allowed to exceed
// params.MaxTxGas, but the calldata floor cost is capped by it.
if rules.IsAmsterdam { if rules.IsAmsterdam {
if cost.Sum() > msg.GasLimit { if max(cost.RegularGas, floorDataGas) > params.MaxTxGas {
return nil, fmt.Errorf("%w: have %d, want %d", ErrIntrinsicGas, msg.GasLimit, cost.Sum()) return nil, fmt.Errorf("%w: regular intrisic cost %v, floor: %v", ErrFloorDataGas, cost.RegularGas, floorDataGas)
}
// RegularGas must by < 16M
maxRegularGas := max(cost.RegularGas, floorDataGas)
if maxRegularGas > params.MaxTxGas {
return nil, fmt.Errorf("%w: max regular gas %d exceeds limit %d", ErrIntrinsicGas, maxRegularGas, params.MaxTxGas)
} }
} }
before, ok := st.gasRemaining.Charge(cost)
if !ok {
return nil, fmt.Errorf("%w: have %d, want %d", ErrIntrinsicGas, st.gasRemaining.RegularGas, cost.RegularGas)
}
if st.evm.Config.Tracer.HasGasHook() {
st.evm.Config.Tracer.EmitGasChange(before.AsTracing(), st.gasRemaining.AsTracing(), tracing.GasChangeTxIntrinsicGas)
} }
if rules.IsEIP4762 { if rules.IsEIP4762 {
@ -651,7 +708,8 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
} }
} }
// Check clause 6 // Stage 5: top-call affordability, the sender must still be able
// to cover the value transfer of the top frame after gas pre-pay.
value := msg.Value value := msg.Value
if value == nil { if value == nil {
value = new(uint256.Int) value = new(uint256.Int)
@ -689,7 +747,7 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
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)
} }
} }
@ -706,58 +764,44 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
ret, result, vmerr = st.evm.Call(msg.From, st.to(), msg.Data, st.gasRemaining.ForwardAll(), value) ret, result, vmerr = st.evm.Call(msg.From, st.to(), msg.Data, st.gasRemaining.ForwardAll(), value)
st.gasRemaining.Absorb(result) st.gasRemaining.Absorb(result)
} }
// If this was a failed contract creation, refund the account creation costs.
if rules.IsAmsterdam { if rules.IsAmsterdam {
if vmerr != nil { if vmerr != nil && contractCreation {
// If this was a contract creation, refund the account creation costs.
if contractCreation {
refund := params.AccountCreationSize * st.evm.Context.CostPerStateByte refund := params.AccountCreationSize * st.evm.Context.CostPerStateByte
st.gasRemaining.RefundState(refund) st.gasRemaining.RefundState(refund)
} }
} else {
// Compute refunds for selfdestructed slots
cpsb := st.evm.Context.CostPerStateByte
var sdRefund uint64
for _, addr := range st.state.SameTxSelfDestructs() {
r := params.AccountCreationSize * cpsb
r += uint64(st.state.NewStorageSlotCount(addr)) * params.StorageCreationSize * cpsb
r += uint64(st.state.GetCodeSize(addr)) * cpsb
sdRefund += r
}
if sdRefund > 0 {
st.gasRemaining.RefundState(sdRefund)
}
}
} }
// Record the gas used excluding gas refunds. This value represents the actual // Record the gas used excluding gas refunds. This value represents the actual
// gas allowance required to complete execution. // gas allowance required to complete execution.
peakGasUsed := st.gasUsed() peakGasUsed := st.gasUsed()
peakRegular := st.gasRemaining.UsedRegularGas
// Compute refund counter, capped to a refund quotient. // Compute refund counter, capped to a refund quotient.
st.gasRemaining.RefundRegular(st.calcRefund()) st.gasRemaining.RefundRegular(st.calcRefund())
if rules.IsPrague { if rules.IsPrague {
// After EIP-7623: Data-heavy transactions pay the floor gas. // We can always guarantee that the initial regular gas allowance
// is sufficient to cover the floor cost.
//
// Pre-Amsterdam, there is a single dimension and gas limit is greater
// than the floor cost.
//
// Since Amsterdam:
// - If GasLimit <= 16M, the state reservoir is initialized to 0,
// and regular_gas_budget >= floor_cost always holds.
// - If GasLimit > 16M, the state reservoir is non-zero, while
// regular_gas_budget == 16M, which is still guaranteed to be
// greater than the floor cost. The extra cost should be deducted
// from the regular even the state reservoir is non-zero.
if used := st.gasUsed(); used < floorDataGas { if used := st.gasUsed(); used < floorDataGas {
/* prior, _ := st.gasRemaining.ChargeRegular(floorDataGas - used)
prior, _ := st.gasRemaining.Charge(vm.GasCosts{RegularGas: floorDataGas - used})
if st.evm.Config.Tracer.HasGasHook() { if st.evm.Config.Tracer.HasGasHook() {
st.evm.Config.Tracer.EmitGasChange(prior.AsTracing(), st.gasRemaining.AsTracing(), tracing.GasChangeTxDataFloor) st.evm.Config.Tracer.EmitGasChange(prior.AsTracing(), st.gasRemaining.AsTracing(), tracing.GasChangeTxDataFloor)
} }
*/
prev := st.gasRemaining.RegularGas
// When the calldata floor exceeds actual gas used, any
// remaining state gas must also be consumed.
targetRemaining := (st.initialBudget.RegularGas + st.initialBudget.StateGas) - floorDataGas
st.gasRemaining.StateGas = 0
st.gasRemaining.RegularGas = targetRemaining
if t := st.evm.Config.Tracer; t != nil && t.OnGasChange != nil {
t.OnGasChange(prev, st.gasRemaining.RegularGas, tracing.GasChangeTxDataFloor)
}
}
if peakGasUsed < floorDataGas {
peakGasUsed = floorDataGas
} }
peakGasUsed = max(peakGasUsed, floorDataGas)
peakRegular = max(peakRegular, floorDataGas)
} }
returned := st.returnGas() returned := st.returnGas()
@ -766,19 +810,21 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
// st.gasRemaining.UsedRegularGas / UsedStateGas already include both // st.gasRemaining.UsedRegularGas / UsedStateGas already include both
// the intrinsic charge (from st.gasRemaining.Charge(cost) above) and // the intrinsic charge (from st.gasRemaining.Charge(cost) above) and
// the per-frame exec contributions absorbed from evm.Call / evm.Create. // the per-frame exec contributions absorbed from evm.Call / evm.Create.
// UsedStateGas may be negative when inline refunds (SSTORE 0→x→0, //
// CREATE-failure refund, 7702 auth refund, same-tx-SD refund) exceed // UsedStateGas should never become negative in the top-most frame, since
// intrinsic + exec state charges. Clamp at 0. // state gas refunds only occur when state creation is reverted within the
// same transaction, while clearing pre-existing state is never refunded.
var txState uint64 var txState uint64
if st.gasRemaining.UsedStateGas > 0 { if st.gasRemaining.UsedStateGas >= 0 {
txState = uint64(st.gasRemaining.UsedStateGas) txState = uint64(st.gasRemaining.UsedStateGas)
} else {
log.Error("Negative top-most frame state gas usage", "amount", st.gasRemaining.UsedStateGas)
} }
txRegular := max(st.gasRemaining.UsedRegularGas, floorDataGas) if err := st.gp.ChargeGasAmsterdam(peakRegular, txState, st.gasUsed()); err != nil {
if err := st.gp.ChargeGasAmsterdam(txRegular, txState, st.gasUsed()); err != nil {
return nil, err return nil, err
} }
} else { } else {
if err = st.gp.ReturnGas(returned, st.gasUsed()); err != nil { if err = st.gp.ChargeGasLegacy(returned, st.gasUsed()); err != nil {
return nil, err return nil, err
} }
} }
@ -909,18 +955,15 @@ func (st *stateTransition) calcRefund() uint64 {
return refund return refund
} }
// returnGas returns ETH for remaining gas, // returnGas returns ETH for remaining gas, exchanged at the original rate.
// exchanged at the original rate.
func (st *stateTransition) returnGas() uint64 { func (st *stateTransition) returnGas() uint64 {
gas := st.gasRemaining.RegularGas + st.gasRemaining.StateGas gas := st.gasRemaining.RegularGas + st.gasRemaining.StateGas
remaining := uint256.NewInt(st.gasRemaining.RegularGas) remaining := uint256.NewInt(st.gasRemaining.RegularGas)
remaining.Mul(remaining, st.msg.GasPrice) remaining.Mul(remaining, st.msg.GasPrice)
st.state.AddBalance(st.msg.From, remaining, tracing.BalanceIncreaseGasReturn) st.state.AddBalance(st.msg.From, remaining, tracing.BalanceIncreaseGasReturn)
if st.gasRemaining.RegularGas > 0 && st.evm.Config.Tracer.HasGasHook() { if !st.gasRemaining.IsZero() && st.evm.Config.Tracer.HasGasHook() {
after := st.gasRemaining st.evm.Config.Tracer.EmitGasChange(st.gasRemaining.AsTracing(), tracing.Gas{}, tracing.GasChangeTxLeftOverReturned)
after.RegularGas = 0
st.evm.Config.Tracer.EmitGasChange(st.gasRemaining.AsTracing(), after.AsTracing(), tracing.GasChangeTxLeftOverReturned)
} }
return gas return gas
} }

View file

@ -472,6 +472,10 @@ const (
// transaction data. This change will always be a negative change. // transaction data. This change will always be a negative change.
GasChangeTxDataFloor GasChangeReason = 19 GasChangeTxDataFloor GasChangeReason = 19
// GasChangeStateGasRefund represents the amount of pre-charged state gas
// refunded back to the state reservoir.
GasChangeStateGasRefund GasChangeReason = 20
// GasChangeIgnored is a special value that can be used to indicate that the gas change should be ignored as // GasChangeIgnored is a special value that can be used to indicate that the gas change should be ignored as
// it will be "manually" tracked by a direct emit of the gas change event. // it will be "manually" tracked by a direct emit of the gas change event.
GasChangeIgnored GasChangeReason = 0xFF GasChangeIgnored GasChangeReason = 0xFF

View file

@ -125,8 +125,7 @@ func ValidateTransaction(tx *types.Transaction, head *types.Header, signer types
} }
// Ensure the transaction has more gas than the bare minimum needed to cover // Ensure the transaction has more gas than the bare minimum needed to cover
// the transaction metadata // the transaction metadata
gasCostPerStateByte := core.CostPerStateByte(head, opts.Config) intrGas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.SetCodeAuthorizations(), tx.To() == nil, rules, params.CostPerStateByte)
intrGas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.SetCodeAuthorizations(), tx.To() == nil, rules, gasCostPerStateByte)
if err != nil { if err != nil {
return err return err
} }
@ -139,9 +138,18 @@ func ValidateTransaction(tx *types.Transaction, head *types.Header, signer types
if err != nil { if err != nil {
return err return err
} }
// Make sure the transaction has sufficient gas allowance to
// pay the floor cost.
if tx.Gas() < floorDataGas { if tx.Gas() < floorDataGas {
return fmt.Errorf("%w: gas %v, minimum needed %v", core.ErrFloorDataGas, tx.Gas(), floorDataGas) return fmt.Errorf("%w: gas %v, minimum needed %v", core.ErrFloorDataGas, tx.Gas(), floorDataGas)
} }
// In Amsterdam, the transaction gas limit is allowed to exceed
// params.MaxTxGas, but the calldata floor cost is capped by it.
if rules.IsAmsterdam {
if max(intrGas.RegularGas, floorDataGas) > params.MaxTxGas {
return fmt.Errorf("%w: regular intrisic cost %v, floor: %v", core.ErrFloorDataGas, intrGas.RegularGas, floorDataGas)
}
}
} }
// Ensure the gasprice is high enough to cover the requirement of the calling pool // Ensure the gasprice is high enough to cover the requirement of the calling pool
if tx.GasTipCapIntCmp(opts.MinTip) < 0 { if tx.GasTipCapIntCmp(opts.MinTip) < 0 {

View file

@ -152,9 +152,19 @@ func (c *Contract) chargeState(s uint64, logger *tracing.Hooks, reason tracing.G
return true return true
} }
// RefundGas absorbs a sub-call's leftover GasBudget into this contract's gas // refundState refunds the pre-charged state gas back to state reservoir.
func (c *Contract) refundState(s uint64, logger *tracing.Hooks, reason tracing.GasChangeReason) {
prior := c.Gas
c.Gas.RefundState(s)
if s != 0 && logger.HasGasHook() && reason != tracing.GasChangeIgnored {
logger.EmitGasChange(prior.AsTracing(), c.Gas.AsTracing(), reason)
}
}
// refundGas absorbs a sub-call's leftover GasBudget into this contract's gas
// state. Thin wrapper around GasBudget.Absorb with tracer integration. // state. Thin wrapper around GasBudget.Absorb with tracer integration.
func (c *Contract) RefundGas(child GasBudget, logger *tracing.Hooks, reason tracing.GasChangeReason) { func (c *Contract) refundGas(child GasBudget, logger *tracing.Hooks, reason tracing.GasChangeReason) {
prior := c.Gas prior := c.Gas
c.Gas.Absorb(child) c.Gas.Absorb(child)
if logger.HasGasHook() && reason != tracing.GasChangeIgnored { if logger.HasGasHook() && reason != tracing.GasChangeIgnored {

View file

@ -264,7 +264,7 @@ func ActivePrecompiles(rules params.Rules) []common.Address {
// - any error that occurred // - any error that occurred
func RunPrecompiledContract(stateDB StateDB, p PrecompiledContract, address common.Address, input []byte, gas GasBudget, logger *tracing.Hooks, rules params.Rules) (ret []byte, remaining GasBudget, err error) { func RunPrecompiledContract(stateDB StateDB, p PrecompiledContract, address common.Address, input []byte, gas GasBudget, logger *tracing.Hooks, rules params.Rules) (ret []byte, remaining GasBudget, err error) {
gasCost := p.RequiredGas(input) gasCost := p.RequiredGas(input)
prior, ok := gas.Charge(GasCosts{RegularGas: gasCost}) prior, ok := gas.ChargeRegular(gasCost)
if !ok { if !ok {
return nil, gas, ErrOutOfGas return nil, gas, ErrOutOfGas
} }

View file

@ -299,11 +299,7 @@ func (evm *EVM) Call(caller common.Address, addr common.Address, input []byte, g
} }
if isPrecompile { if isPrecompile {
var stateDB StateDB ret, gas, err = RunPrecompiledContract(evm.StateDB, p, addr, input, gas, evm.Config.Tracer, evm.chainRules)
if evm.chainRules.IsAmsterdam {
stateDB = evm.StateDB
}
ret, gas, err = RunPrecompiledContract(stateDB, p, addr, input, gas, evm.Config.Tracer, evm.chainRules)
} else { } else {
// Initialise a new contract and set the code that is to be used by the EVM. // Initialise a new contract and set the code that is to be used by the EVM.
code := evm.resolveCode(addr) code := evm.resolveCode(addr)
@ -318,18 +314,20 @@ func (evm *EVM) Call(caller common.Address, addr common.Address, input []byte, g
gas = contract.Gas gas = contract.Gas
} }
} }
// When an error was returned by the EVM or when setting the creation code
// above we revert to the snapshot. gasFromExec below handles the // Calculate the remaining gas at the end of frame
// regular-gas burn on halt. exitGas := gas.Exit(err, initialStateGas)
if err != nil { if err != nil {
evm.StateDB.RevertToSnapshot(snapshot) evm.StateDB.RevertToSnapshot(snapshot)
// Drain the leftover regular gas if unexceptional halt occurs
if err != ErrExecutionReverted { if err != ErrExecutionReverted {
if evm.Config.Tracer.HasGasHook() { if evm.Config.Tracer.HasGasHook() {
evm.Config.Tracer.EmitGasChange(gas.AsTracing(), tracing.Gas{}, tracing.GasChangeCallFailedExecution) evm.Config.Tracer.EmitGasChange(gas.AsTracing(), exitGas.AsTracing(), tracing.GasChangeCallFailedExecution)
} }
} }
} }
return ret, gas.ExitFromErr(err, initialStateGas), err return ret, exitGas, err
} }
// CallCode executes the contract associated with the addr with the given input // CallCode executes the contract associated with the addr with the given input
@ -361,11 +359,7 @@ func (evm *EVM) CallCode(caller common.Address, addr common.Address, input []byt
// 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 {
var stateDB StateDB ret, gas, err = RunPrecompiledContract(evm.StateDB, p, addr, input, gas, evm.Config.Tracer, evm.chainRules)
if evm.chainRules.IsAmsterdam {
stateDB = evm.StateDB
}
ret, gas, err = RunPrecompiledContract(stateDB, p, addr, input, gas, evm.Config.Tracer, evm.chainRules)
} else { } else {
// Initialise a new contract and set the code that is to be used by the EVM. // Initialise a new contract and set the code that is to be used by the EVM.
// The contract is a scoped environment for this execution context only. // The contract is a scoped environment for this execution context only.
@ -374,15 +368,20 @@ func (evm *EVM) CallCode(caller common.Address, addr common.Address, input []byt
ret, err = evm.Run(contract, input, false) ret, err = evm.Run(contract, input, false)
gas = contract.Gas gas = contract.Gas
} }
// Calculate the remaining gas at the end of frame
exitGas := gas.Exit(err, initialStateGas)
if err != nil { if err != nil {
evm.StateDB.RevertToSnapshot(snapshot) evm.StateDB.RevertToSnapshot(snapshot)
// Drain the leftover regular gas if unexceptional halt occurs
if err != ErrExecutionReverted { if err != ErrExecutionReverted {
if evm.Config.Tracer.HasGasHook() { if evm.Config.Tracer.HasGasHook() {
evm.Config.Tracer.EmitGasChange(gas.AsTracing(), tracing.Gas{}, tracing.GasChangeCallFailedExecution) evm.Config.Tracer.EmitGasChange(gas.AsTracing(), exitGas.AsTracing(), tracing.GasChangeCallFailedExecution)
} }
} }
} }
return ret, gas.ExitFromErr(err, initialStateGas), err return ret, exitGas, err
} }
// DelegateCall executes the contract associated with the addr with the given input // DelegateCall executes the contract associated with the addr with the given input
@ -409,26 +408,27 @@ func (evm *EVM) DelegateCall(originCaller common.Address, caller common.Address,
// 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 {
var stateDB StateDB ret, gas, err = RunPrecompiledContract(evm.StateDB, p, addr, input, gas, evm.Config.Tracer, evm.chainRules)
if evm.chainRules.IsAmsterdam {
stateDB = evm.StateDB
}
ret, gas, err = RunPrecompiledContract(stateDB, p, addr, input, gas, evm.Config.Tracer, evm.chainRules)
} else { } else {
contract := NewContract(originCaller, caller, value, gas, evm.jumpDests) contract := NewContract(originCaller, caller, value, gas, evm.jumpDests)
contract.SetCallCode(evm.resolveCodeHash(addr), evm.resolveCode(addr)) contract.SetCallCode(evm.resolveCodeHash(addr), evm.resolveCode(addr))
ret, err = evm.Run(contract, input, false) ret, err = evm.Run(contract, input, false)
gas = contract.Gas gas = contract.Gas
} }
// Calculate the remaining gas at the end of frame
exitGas := gas.Exit(err, initialStateGas)
if err != nil { if err != nil {
evm.StateDB.RevertToSnapshot(snapshot) evm.StateDB.RevertToSnapshot(snapshot)
// Drain the leftover regular gas if unexceptional halt occurs
if err != ErrExecutionReverted { if err != ErrExecutionReverted {
if evm.Config.Tracer.HasGasHook() { if evm.Config.Tracer.HasGasHook() {
evm.Config.Tracer.EmitGasChange(gas.AsTracing(), tracing.Gas{}, tracing.GasChangeCallFailedExecution) evm.Config.Tracer.EmitGasChange(gas.AsTracing(), exitGas.AsTracing(), tracing.GasChangeCallFailedExecution)
} }
} }
} }
return ret, gas.ExitFromErr(err, initialStateGas), err return ret, exitGas, err
} }
// StaticCall executes the contract associated with the addr with the given input // StaticCall executes the contract associated with the addr with the given input
@ -463,26 +463,25 @@ func (evm *EVM) StaticCall(caller common.Address, addr common.Address, input []b
evm.StateDB.AddBalance(addr, new(uint256.Int), tracing.BalanceChangeTouchAccount) evm.StateDB.AddBalance(addr, new(uint256.Int), tracing.BalanceChangeTouchAccount)
if p, isPrecompile := evm.precompile(addr); isPrecompile { if p, isPrecompile := evm.precompile(addr); isPrecompile {
var stateDB StateDB ret, gas, err = RunPrecompiledContract(evm.StateDB, p, addr, input, gas, evm.Config.Tracer, evm.chainRules)
if evm.chainRules.IsAmsterdam {
stateDB = evm.StateDB
}
ret, gas, err = RunPrecompiledContract(stateDB, p, addr, input, gas, evm.Config.Tracer, evm.chainRules)
} else { } else {
contract := NewContract(caller, addr, new(uint256.Int), gas, evm.jumpDests) contract := NewContract(caller, addr, new(uint256.Int), gas, evm.jumpDests)
contract.SetCallCode(evm.resolveCodeHash(addr), evm.resolveCode(addr)) contract.SetCallCode(evm.resolveCodeHash(addr), evm.resolveCode(addr))
ret, err = evm.Run(contract, input, true) ret, err = evm.Run(contract, input, true)
gas = contract.Gas gas = contract.Gas
} }
// Calculate the remaining gas at the end of frame
exitGas := gas.Exit(err, initialStateGas)
if err != nil { if err != nil {
evm.StateDB.RevertToSnapshot(snapshot) evm.StateDB.RevertToSnapshot(snapshot)
if err != ErrExecutionReverted { if err != ErrExecutionReverted {
if evm.Config.Tracer.HasGasHook() { if evm.Config.Tracer.HasGasHook() {
evm.Config.Tracer.EmitGasChange(gas.AsTracing(), tracing.Gas{}, tracing.GasChangeCallFailedExecution) evm.Config.Tracer.EmitGasChange(gas.AsTracing(), exitGas.AsTracing(), tracing.GasChangeCallFailedExecution)
} }
} }
} }
return ret, gas.ExitFromErr(err, initialStateGas), err return ret, exitGas, err
} }
// create creates a new contract using code as deployment code. // create creates a new contract using code as deployment code.
@ -593,12 +592,14 @@ func (evm *EVM) create(caller common.Address, code []byte, gas GasBudget, value
// state and gas is preserved (i.e., treated as success). // state and gas is preserved (i.e., treated as success).
if err != nil && (evm.chainRules.IsHomestead || err != ErrCodeStoreOutOfGas) { if err != nil && (evm.chainRules.IsHomestead || err != ErrCodeStoreOutOfGas) {
evm.StateDB.RevertToSnapshot(snapshot) evm.StateDB.RevertToSnapshot(snapshot)
exit := contract.Gas.Exit(err, initialStateGas)
if err != ErrExecutionReverted { if err != ErrExecutionReverted {
if evm.Config.Tracer != nil && evm.Config.Tracer.OnGasChange != nil { if evm.Config.Tracer.HasGasHook() {
evm.Config.Tracer.OnGasChange(contract.Gas.RegularGas, 0, tracing.GasChangeCallFailedExecution) evm.Config.Tracer.EmitGasChange(contract.Gas.AsTracing(), exit.AsTracing(), tracing.GasChangeCallFailedExecution)
} }
} }
return ret, address, contract.Gas.ExitFromErr(err, initialStateGas), err return ret, address, exit, err
} }
// Either success, or pre-Homestead ErrCodeStoreOutOfGas (gas preserved). // Either success, or pre-Homestead ErrCodeStoreOutOfGas (gas preserved).
// Both packaged as a success-form GasBudget. // Both packaged as a success-form GasBudget.

View file

@ -368,7 +368,7 @@ func gasCreate2Eip3860(evm *EVM, contract *Contract, stack *Stack, mem *Memory,
} }
func gasExpFrontier(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) { func gasExpFrontier(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
expByteLen := uint64(((*stack.back(1)).BitLen() + 7) / 8) expByteLen := uint64((stack.back(1).BitLen() + 7) / 8)
var ( var (
gas = expByteLen * params.ExpByteFrontier // no overflow check required. Max is 256 * ExpByte gas gas = expByteLen * params.ExpByteFrontier // no overflow check required. Max is 256 * ExpByte gas
@ -381,7 +381,7 @@ func gasExpFrontier(evm *EVM, contract *Contract, stack *Stack, mem *Memory, mem
} }
func gasExpEIP158(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) { func gasExpEIP158(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
expByteLen := uint64(((*stack.back(1)).BitLen() + 7) / 8) expByteLen := uint64((stack.back(1).BitLen() + 7) / 8)
var ( var (
gas = expByteLen * params.ExpByteEIP158 // no overflow check required. Max is 256 * ExpByte gas gas = expByteLen * params.ExpByteEIP158 // no overflow check required. Max is 256 * ExpByte gas
@ -460,10 +460,10 @@ func gasCallIntrinsic(evm *EVM, contract *Contract, stack *Stack, mem *Memory, m
return gas, nil return gas, nil
} }
// gasCallIntrinsic8037 is the intrinsic gas calculator for CALL in Amsterdam. // regularGasCall8037 is the intrinsic gas calculator for CALL in Amsterdam.
// It computes memory expansion + value transfer gas but excludes new account // It computes memory expansion + value transfer gas but excludes new account
// creation, which is handled as state gas by the wrapper. // creation, which is handled as state gas by the wrapper.
func gasCallIntrinsic8037(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { func regularGasCall8037(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
var ( var (
gas uint64 gas uint64
transfersValue = !stack.back(2).IsZero() transfersValue = !stack.back(2).IsZero()
@ -571,7 +571,10 @@ func gasCreateEip8037(evm *EVM, contract *Contract, stack *Stack, mem *Memory, m
words := (size + 31) / 32 words := (size + 31) / 32
wordGas := params.InitCodeWordGas * words wordGas := params.InitCodeWordGas * words
stateGas := params.AccountCreationSize * evm.Context.CostPerStateByte stateGas := params.AccountCreationSize * evm.Context.CostPerStateByte
return GasCosts{RegularGas: gas + wordGas, StateGas: stateGas}, nil return GasCosts{
RegularGas: gas + wordGas,
StateGas: stateGas,
}, nil
} }
func gasCreate2Eip8037(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) { func gasCreate2Eip8037(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
@ -591,28 +594,35 @@ func gasCreate2Eip8037(evm *EVM, contract *Contract, stack *Stack, mem *Memory,
} }
// Since size <= MaxInitCodeSizeAmsterdam, these multiplications cannot overflow // Since size <= MaxInitCodeSizeAmsterdam, these multiplications cannot overflow
words := (size + 31) / 32 words := (size + 31) / 32
// CREATE2 charges both InitCodeWordGas (EIP-3860) and Keccak256WordGas (for address hashing).
// CREATE2 charges both InitCodeWordGas (EIP-3860) and Keccak256WordGas
// (for address hashing).
wordGas := (params.InitCodeWordGas + params.Keccak256WordGas) * words wordGas := (params.InitCodeWordGas + params.Keccak256WordGas) * words
stateGas := params.AccountCreationSize * evm.Context.CostPerStateByte stateGas := params.AccountCreationSize * evm.Context.CostPerStateByte
return GasCosts{RegularGas: gas + wordGas, StateGas: stateGas}, nil return GasCosts{
RegularGas: gas + wordGas,
StateGas: stateGas,
}, nil
} }
// gasCall8037 is the stateful gas calculator for CALL in Amsterdam (EIP-8037). // stateGasCall8037 is the stateful gas calculator for CALL in Amsterdam (EIP-8037).
// It only returns the state-dependent gas (account creation as state gas). // It only returns the state-dependent gas (account creation as state gas).
// Memory gas, transfer gas, and callGas are handled by gasCallStateless and // Memory gas, transfer gas, and callGas are handled by gasCallStateless and
// makeCallVariantGasCall. // makeCallVariantGasCall.
func gasCall8037(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) { func stateGasCall8037(evm *EVM, contract *Contract, stack *Stack) (uint64, error) {
var ( var (
gas GasCosts gas uint64
transfersValue = !stack.back(2).IsZero() transfersValue = !stack.back(2).IsZero()
address = common.Address(stack.back(1).Bytes20()) address = common.Address(stack.back(1).Bytes20())
) )
// TODO(rjl, marius), can EIP8037 implicitly means the EIP158 is also activated?
// It's technically possible to skip the EIP158 but very unlikely in practice.
if evm.chainRules.IsEIP158 { if evm.chainRules.IsEIP158 {
if transfersValue && evm.StateDB.Empty(address) { if transfersValue && evm.StateDB.Empty(address) {
gas.StateGas += params.AccountCreationSize * evm.Context.CostPerStateByte gas += params.AccountCreationSize * evm.Context.CostPerStateByte
} }
} else if !evm.StateDB.Exist(address) { } else if !evm.StateDB.Exist(address) {
gas.StateGas += params.AccountCreationSize * evm.Context.CostPerStateByte gas += params.AccountCreationSize * evm.Context.CostPerStateByte
} }
return gas, nil return gas, nil
} }
@ -671,14 +681,9 @@ func gasSStore8037(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memo
} }
if original == current { if original == current {
if original == (common.Hash{}) { // create slot (2.1.1) if original == (common.Hash{}) { // create slot (2.1.1)
// EIP-8037: Return both regular and state gas. System calls do not charge state gas.
var stateGas uint64
if !contract.IsSystemCall {
stateGas = params.StorageCreationSize * evm.Context.CostPerStateByte
}
return GasCosts{ return GasCosts{
RegularGas: cost.RegularGas + params.SstoreResetGasEIP2200 - params.ColdSloadCostEIP2929, RegularGas: cost.RegularGas + params.SstoreResetGasEIP2200 - params.ColdSloadCostEIP2929,
StateGas: stateGas, StateGas: params.StorageCreationSize * evm.Context.CostPerStateByte,
}, nil }, nil
} }
if value == (common.Hash{}) { // delete slot (2.1.2b) if value == (common.Hash{}) { // delete slot (2.1.2b)

View file

@ -55,7 +55,7 @@ func (g GasCosts) String() string {
// in lockstep. // in lockstep.
// //
// - At frame exit: Preserved / ExitSuccess / ExitRevert / ExitHalt / // - At frame exit: Preserved / ExitSuccess / ExitRevert / ExitHalt /
// ExitFromErr produce a new GasBudget in "leftover" form that packages // Exit produce a new GasBudget in "leftover" form that packages
// the result for the caller. // the result for the caller.
// //
// - At absorption: the caller's Absorb method merges the child's leftover // - At absorption: the caller's Absorb method merges the child's leftover
@ -263,7 +263,7 @@ func (g GasBudget) ExitHalt(initialStateGas uint64) GasBudget {
} }
} }
// ExitFromErr dispatches on err to the appropriate exit-form constructor // Exit dispatches on err to the appropriate exit-form constructor
// for the post-evm.Run path: // for the post-evm.Run path:
// //
// - err == nil → ExitSuccess // - err == nil → ExitSuccess
@ -272,7 +272,7 @@ func (g GasBudget) ExitHalt(initialStateGas uint64) GasBudget {
// //
// Soft validation failures (occurring BEFORE evm.Run) should call Preserved // Soft validation failures (occurring BEFORE evm.Run) should call Preserved
// directly instead of going through this dispatcher. // directly instead of going through this dispatcher.
func (g GasBudget) ExitFromErr(err error, initialStateGas uint64) GasBudget { func (g GasBudget) Exit(err error, initialStateGas uint64) GasBudget {
switch { switch {
case err == nil: case err == nil:
return g.ExitSuccess() return g.ExitSuccess()

View file

@ -675,7 +675,7 @@ func opCreate(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
} }
scope.Stack.push(&stackvalue) scope.Stack.push(&stackvalue)
scope.Contract.RefundGas(result, evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded) scope.Contract.refundGas(result, evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded)
if evm.chainRules.IsAmsterdam && suberr != nil { if evm.chainRules.IsAmsterdam && suberr != nil {
scope.Contract.Gas.RefundState(params.AccountCreationSize * evm.Context.CostPerStateByte) scope.Contract.Gas.RefundState(params.AccountCreationSize * evm.Context.CostPerStateByte)
} }
@ -710,11 +710,13 @@ func opCreate2(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
stackvalue.SetBytes(addr.Bytes()) stackvalue.SetBytes(addr.Bytes())
} }
scope.Stack.push(&stackvalue) scope.Stack.push(&stackvalue)
scope.Contract.RefundGas(result, evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded) scope.Contract.refundGas(result, evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded)
if evm.chainRules.IsAmsterdam && suberr != nil {
scope.Contract.Gas.RefundState(params.AccountCreationSize * evm.Context.CostPerStateByte)
}
// If the creation frame reverts or halts exceptionally, the charged state-gas
// is refilled back to the state reservoir in Amsterdam.
if evm.chainRules.IsAmsterdam && suberr != nil {
scope.Contract.refundState(params.AccountCreationSize*evm.Context.CostPerStateByte, evm.Config.Tracer, tracing.GasChangeStateGasRefund)
}
if suberr == ErrExecutionReverted { if suberr == ErrExecutionReverted {
evm.returnData = res // set REVERT data to return data buffer evm.returnData = res // set REVERT data to return data buffer
return res, nil return res, nil
@ -754,11 +756,24 @@ func opCall(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
temp.SetOne() temp.SetOne()
} }
stack.push(&temp) stack.push(&temp)
if err == nil || err == ErrExecutionReverted { if err == nil || err == ErrExecutionReverted {
scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
} }
scope.Contract.refundGas(result, evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded)
scope.Contract.RefundGas(result, evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded) // If the call frame reverts or halts exceptionally, the charged state-gas
// is refilled back to the state reservoir in Amsterdam.
//
// The state-gas should only be refunded if the state creation doesn't
// happens, such as ErrDepth, ErrInsufficientBalance.
//
// TODO(rjl) it's so ugly, please rework it.
if evm.chainRules.IsAmsterdam && err != nil {
if (err == ErrDepth || err == ErrInsufficientBalance) && !value.IsZero() && evm.StateDB.Empty(toAddr) {
scope.Contract.refundState(params.AccountCreationSize*evm.Context.CostPerStateByte, evm.Config.Tracer, tracing.GasChangeStateGasRefund)
}
}
evm.returnData = ret evm.returnData = ret
return ret, nil return ret, nil
@ -792,7 +807,7 @@ func opCallCode(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
} }
scope.Contract.RefundGas(result, evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded) scope.Contract.refundGas(result, evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded)
evm.returnData = ret evm.returnData = ret
return ret, nil return ret, nil
@ -821,7 +836,7 @@ func opDelegateCall(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
if err == nil || err == ErrExecutionReverted { if err == nil || err == ErrExecutionReverted {
scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
} }
scope.Contract.RefundGas(result, evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded) scope.Contract.refundGas(result, evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded)
evm.returnData = ret evm.returnData = ret
return ret, nil return ret, nil
@ -851,7 +866,7 @@ func opStaticCall(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
} }
scope.Contract.RefundGas(result, evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded) scope.Contract.refundGas(result, evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded)
evm.returnData = ret evm.returnData = ret
return ret, nil return ret, nil

View file

@ -71,14 +71,6 @@ type StateDB interface {
// during the current transaction. // during the current transaction.
IsNewContract(addr common.Address) bool IsNewContract(addr common.Address) bool
// SameTxSelfDestructs returns addresses that were created and then
// self-destructed in the current transaction.
SameTxSelfDestructs() []common.Address
// NewStorageSlotCount returns the number of storage slots written to a
// non-zero value in the current transaction on the given address.
NewStorageSlotCount(addr common.Address) int
// Empty returns whether the given account is empty. Empty // Empty returns whether the given account is empty. Empty
// is defined according to EIP161 (balance = nonce = code = 0). // is defined according to EIP161 (balance = nonce = code = 0).
Empty(common.Address) bool Empty(common.Address) bool

View file

@ -28,6 +28,9 @@ type (
intrinsicGasFunc func(*EVM, *Contract, *Stack, *Memory, uint64) (uint64, error) // last parameter is the requested memory size as a uint64 intrinsicGasFunc func(*EVM, *Contract, *Stack, *Memory, uint64) (uint64, error) // last parameter is the requested memory size as a uint64
// memorySizeFunc returns the required size, and whether the operation overflowed a uint64 // memorySizeFunc returns the required size, and whether the operation overflowed a uint64
memorySizeFunc func(*Stack) (size uint64, overflow bool) memorySizeFunc func(*Stack) (size uint64, overflow bool)
regularGasFunc func(*EVM, *Contract, *Stack, *Memory, uint64) (uint64, error)
stateGasFunc func(*EVM, *Contract, *Stack) (uint64, error)
) )
type operation struct { type operation struct {

View file

@ -267,7 +267,7 @@ var (
gasStaticCallEIP7702 = makeCallVariantGasCallEIP7702(gasStaticCallIntrinsic) gasStaticCallEIP7702 = makeCallVariantGasCallEIP7702(gasStaticCallIntrinsic)
gasCallCodeEIP7702 = makeCallVariantGasCallEIP7702(gasCallCodeIntrinsic) gasCallCodeEIP7702 = makeCallVariantGasCallEIP7702(gasCallCodeIntrinsic)
innerGasCallEIP8037 = makeCallVariantGasCallEIP8037(gasCallIntrinsic8037, gasCall8037) innerGasCallEIP8037 = makeCallVariantGasCallEIP8037(regularGasCall8037, stateGasCall8037)
) )
func gasCallEIP7702(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) { func gasCallEIP7702(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
@ -381,7 +381,7 @@ func makeCallVariantGasCallEIP7702(intrinsicFunc intrinsicGasFunc) gasFunc {
// It extends the EIP-7702 pattern with state gas handling and GasUsed tracking. // It extends the EIP-7702 pattern with state gas handling and GasUsed tracking.
// intrinsicFunc computes the regular gas (memory + transfer, no new account creation). // intrinsicFunc computes the regular gas (memory + transfer, no new account creation).
// stateGasFunc computes the state gas (new account creation as state gas). // stateGasFunc computes the state gas (new account creation as state gas).
func makeCallVariantGasCallEIP8037(intrinsicFunc intrinsicGasFunc, stateGasFunc gasFunc) gasFunc { func makeCallVariantGasCallEIP8037(regularFunc regularGasFunc, stateGasFunc stateGasFunc) gasFunc {
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) { return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
var ( var (
eip2929Cost uint64 eip2929Cost uint64
@ -397,8 +397,8 @@ func makeCallVariantGasCallEIP8037(intrinsicFunc intrinsicGasFunc, stateGasFunc
} }
} }
// Compute intrinsic cost (memory + transfer, no new account creation). // Compute regular cost (memory + transfer, no new account creation).
intrinsicCost, err := intrinsicFunc(evm, contract, stack, mem, memorySize) regularCost, err := regularFunc(evm, contract, stack, mem, memorySize)
if err != nil { if err != nil {
return GasCosts{}, err return GasCosts{}, err
} }
@ -406,7 +406,7 @@ func makeCallVariantGasCallEIP8037(intrinsicFunc intrinsicGasFunc, stateGasFunc
// Charge intrinsic cost directly (regular gas). This must happen // Charge intrinsic cost directly (regular gas). This must happen
// BEFORE state gas to prevent reservoir inflation, and also serves // BEFORE state gas to prevent reservoir inflation, and also serves
// as the OOG guard before stateful operations. // as the OOG guard before stateful operations.
if !contract.chargeRegular(intrinsicCost, evm.Config.Tracer, tracing.GasChangeCallOpCode) { if !contract.chargeRegular(regularCost, evm.Config.Tracer, tracing.GasChangeCallOpCode) {
return GasCosts{}, ErrOutOfGas return GasCosts{}, ErrOutOfGas
} }
@ -424,13 +424,12 @@ func makeCallVariantGasCallEIP8037(intrinsicFunc intrinsicGasFunc, stateGasFunc
} }
// Compute and charge state gas (new account creation) AFTER regular gas. // Compute and charge state gas (new account creation) AFTER regular gas.
stateGas, err := stateGasFunc(evm, contract, stack, mem, memorySize) stateGas, err := stateGasFunc(evm, contract, stack)
if err != nil { if err != nil {
return GasCosts{}, err return GasCosts{}, err
} }
if stateGas.StateGas > 0 { if stateGas > 0 {
// Charge updates contract.Gas.UsedStateGas in lockstep. if _, ok := contract.Gas.ChargeState(stateGas); !ok {
if _, ok := contract.Gas.Charge(GasCosts{StateGas: stateGas.StateGas}); !ok {
return GasCosts{}, ErrOutOfGas return GasCosts{}, ErrOutOfGas
} }
} }
@ -443,8 +442,8 @@ func makeCallVariantGasCallEIP8037(intrinsicFunc intrinsicGasFunc, stateGasFunc
// Temporarily undo direct regular charges for tracer reporting. // Temporarily undo direct regular charges for tracer reporting.
// The interpreter will charge the returned totalCost. // The interpreter will charge the returned totalCost.
contract.Gas.RegularGas += eip2929Cost + eip7702Cost + intrinsicCost contract.Gas.RegularGas += eip2929Cost + eip7702Cost + regularCost
contract.Gas.UsedRegularGas -= eip2929Cost + eip7702Cost + intrinsicCost contract.Gas.UsedRegularGas -= eip2929Cost + eip7702Cost + regularCost
// Aggregate total cost. // Aggregate total cost.
var ( var (
@ -454,7 +453,7 @@ func makeCallVariantGasCallEIP8037(intrinsicFunc intrinsicGasFunc, stateGasFunc
if totalCost, overflow = math.SafeAdd(eip2929Cost, eip7702Cost); overflow { if totalCost, overflow = math.SafeAdd(eip2929Cost, eip7702Cost); overflow {
return GasCosts{}, ErrGasUintOverflow return GasCosts{}, ErrGasUintOverflow
} }
if totalCost, overflow = math.SafeAdd(totalCost, intrinsicCost); overflow { if totalCost, overflow = math.SafeAdd(totalCost, regularCost); overflow {
return GasCosts{}, ErrGasUintOverflow return GasCosts{}, ErrGasUintOverflow
} }
if totalCost, overflow = math.SafeAdd(totalCost, evm.callGasTemp); overflow { if totalCost, overflow = math.SafeAdd(totalCost, evm.callGasTemp); overflow {

View file

@ -199,10 +199,11 @@ const (
// don't consume block gas. // don't consume block gas.
BALItemCost uint64 = 2000 BALItemCost uint64 = 2000
AccountCreationSize = 112 AccountCreationSize = 120
StorageCreationSize = 32 StorageCreationSize = 64
AuthorizationCreationSize = 23 AuthorizationCreationSize = 23
CostPerStateByte = 1174 CostPerStateByte = 1530
SystemMaxSStoresPerCall = 16
) )
// Bls12381G1MultiExpDiscountTable is the gas discount table for BLS12-381 G1 multi exponentiation operation // Bls12381G1MultiExpDiscountTable is the gas discount table for BLS12-381 G1 multi exponentiation operation

View file

@ -81,7 +81,7 @@ func (tt *TransactionTest) Run() error {
return return
} }
// Intrinsic cost // Intrinsic cost
cost, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.SetCodeAuthorizations(), tx.To() == nil, rules, 0) cost, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.SetCodeAuthorizations(), tx.To() == nil, rules, params.CostPerStateByte)
if err != nil { if err != nil {
return return
} }