core/vm, eth, tests: introduce gas budget (#34712)
Some checks are pending
/ Linux Build (arm) (push) Waiting to run
/ Keeper Build (push) Waiting to run
/ Windows Build (push) Waiting to run
/ Docker Image (push) Waiting to run
/ Linux Build (push) Waiting to run

This PR introduces a gasBudget struct to track the available gas for EVM
execution.

With the upcoming EIP-8037, multi-dimensional gas accounting will be
introduced, requiring multiple gas budget counters to be tracked 
simultaneously. To support this, the counters are grouped into a gasBudget 
structure.

This change is a prerequisite for internal refactoring in preparation
for EIP-8037.

---------

Co-authored-by: MariusVanDerWijden <m.vanderwijden@live.de>
This commit is contained in:
rjl493456442 2026-04-20 15:33:29 +08:00 committed by GitHub
parent 5af5510b1e
commit 29e0a6f404
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
25 changed files with 235 additions and 168 deletions

View file

@ -133,15 +133,15 @@ 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)
gas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.SetCodeAuthorizations(), tx.To() == nil, rules.IsHomestead, rules.IsIstanbul, rules.IsShanghai) cost, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.SetCodeAuthorizations(), tx.To() == nil, rules.IsHomestead, rules.IsIstanbul, rules.IsShanghai)
if err != nil { if err != nil {
r.Error = err r.Error = err
results = append(results, r) results = append(results, r)
continue continue
} }
r.IntrinsicGas = gas r.IntrinsicGas = cost.RegularGas
if tx.Gas() < gas { if tx.Gas() < cost.RegularGas {
r.Error = fmt.Errorf("%w: have %d, want %d", core.ErrIntrinsicGas, tx.Gas(), gas) r.Error = fmt.Errorf("%w: have %d, want %d", core.ErrIntrinsicGas, tx.Gas(), cost.RegularGas)
results = append(results, r) results = append(results, r)
continue continue
} }

View file

@ -89,7 +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{}
gas, _ := IntrinsicGas(data, nil, nil, false, false, false, false) cost, _ := IntrinsicGas(data, nil, nil, false, false, false, false)
signer := gen.Signer() signer := gen.Signer()
gasPrice := big.NewInt(0) gasPrice := big.NewInt(0)
if gen.header.BaseFee != nil { if gen.header.BaseFee != nil {
@ -99,7 +99,7 @@ func genValueTx(nbytes int) func(int, *BlockGen) {
Nonce: gen.TxNonce(benchRootAddr), Nonce: gen.TxNonce(benchRootAddr),
To: &toaddr, To: &toaddr,
Value: big.NewInt(1), Value: big.NewInt(1),
Gas: gas, Gas: cost.RegularGas,
Data: data, Data: data,
GasPrice: gasPrice, GasPrice: gasPrice,
}) })

View file

@ -97,11 +97,11 @@ func TestProcessUBT(t *testing.T) {
txCost1 := params.TxGas txCost1 := params.TxGas
txCost2 := params.TxGas txCost2 := params.TxGas
contractCreationCost := intrinsicContractCreationGas + contractCreationCost := intrinsicContractCreationGas.RegularGas +
params.WitnessChunkReadCost + params.WitnessChunkWriteCost + params.WitnessBranchReadCost + params.WitnessBranchWriteCost + /* creation */ params.WitnessChunkReadCost + params.WitnessChunkWriteCost + params.WitnessBranchReadCost + params.WitnessBranchWriteCost + /* creation */
params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* creation with value */ params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* creation with value */
739 /* execution costs */ 739 /* execution costs */
codeWithExtCodeCopyGas := intrinsicCodeWithExtCodeCopyGas + codeWithExtCodeCopyGas := intrinsicCodeWithExtCodeCopyGas.RegularGas +
params.WitnessChunkReadCost + params.WitnessChunkWriteCost + params.WitnessBranchReadCost + params.WitnessBranchWriteCost + /* creation (tx) */ params.WitnessChunkReadCost + params.WitnessChunkWriteCost + params.WitnessBranchReadCost + params.WitnessBranchWriteCost + /* creation (tx) */
params.WitnessChunkReadCost + params.WitnessChunkWriteCost + params.WitnessBranchReadCost + params.WitnessBranchWriteCost + /* creation (CREATE at pc=0x20) */ params.WitnessChunkReadCost + params.WitnessChunkWriteCost + params.WitnessBranchReadCost + params.WitnessBranchWriteCost + /* creation (CREATE at pc=0x20) */
params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* write code hash */ params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* write code hash */

View file

@ -259,7 +259,7 @@ func ProcessBeaconBlockRoot(beaconRoot common.Hash, evm *vm.EVM) {
} }
evm.SetTxContext(NewEVMTxContext(msg)) evm.SetTxContext(NewEVMTxContext(msg))
evm.StateDB.AddAddressToAccessList(params.BeaconRootsAddress) evm.StateDB.AddAddressToAccessList(params.BeaconRootsAddress)
_, _, _ = evm.Call(msg.From, *msg.To, msg.Data, 30_000_000, common.U2560) _, _, _ = evm.Call(msg.From, *msg.To, msg.Data, vm.NewGasBudget(30_000_000), common.U2560)
if evm.StateDB.AccessEvents() != nil { if evm.StateDB.AccessEvents() != nil {
evm.StateDB.AccessEvents().Merge(evm.AccessEvents) evm.StateDB.AccessEvents().Merge(evm.AccessEvents)
} }
@ -286,7 +286,7 @@ func ProcessParentBlockHash(prevHash common.Hash, evm *vm.EVM) {
} }
evm.SetTxContext(NewEVMTxContext(msg)) evm.SetTxContext(NewEVMTxContext(msg))
evm.StateDB.AddAddressToAccessList(params.HistoryStorageAddress) evm.StateDB.AddAddressToAccessList(params.HistoryStorageAddress)
_, _, err := evm.Call(msg.From, *msg.To, msg.Data, 30_000_000, common.U2560) _, _, err := evm.Call(msg.From, *msg.To, msg.Data, vm.NewGasBudget(30_000_000), common.U2560)
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -325,7 +325,7 @@ func processRequestsSystemCall(requests *[][]byte, evm *vm.EVM, requestType byte
} }
evm.SetTxContext(NewEVMTxContext(msg)) evm.SetTxContext(NewEVMTxContext(msg))
evm.StateDB.AddAddressToAccessList(addr) evm.StateDB.AddAddressToAccessList(addr)
ret, _, err := evm.Call(msg.From, *msg.To, msg.Data, 30_000_000, common.U2560) ret, _, err := evm.Call(msg.From, *msg.To, msg.Data, vm.NewGasBudget(30_000_000), 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

@ -68,7 +68,7 @@ func (result *ExecutionResult) Revert() []byte {
} }
// IntrinsicGas computes the 'intrinsic gas' for a message with the given data. // IntrinsicGas computes the 'intrinsic gas' for a message with the given data.
func IntrinsicGas(data []byte, accessList types.AccessList, authList []types.SetCodeAuthorization, isContractCreation, isHomestead, isEIP2028, isEIP3860 bool) (uint64, error) { func IntrinsicGas(data []byte, accessList types.AccessList, authList []types.SetCodeAuthorization, isContractCreation, isHomestead, isEIP2028, isEIP3860 bool) (vm.GasCosts, error) {
// Set the starting gas for the raw transaction // Set the starting gas for the raw transaction
var gas uint64 var gas uint64
if isContractCreation && isHomestead { if isContractCreation && isHomestead {
@ -89,19 +89,19 @@ func IntrinsicGas(data []byte, accessList types.AccessList, authList []types.Set
nonZeroGas = params.TxDataNonZeroGasEIP2028 nonZeroGas = params.TxDataNonZeroGasEIP2028
} }
if (math.MaxUint64-gas)/nonZeroGas < nz { if (math.MaxUint64-gas)/nonZeroGas < nz {
return 0, ErrGasUintOverflow return vm.GasCosts{}, ErrGasUintOverflow
} }
gas += nz * nonZeroGas gas += nz * nonZeroGas
if (math.MaxUint64-gas)/params.TxDataZeroGas < z { if (math.MaxUint64-gas)/params.TxDataZeroGas < z {
return 0, ErrGasUintOverflow return vm.GasCosts{}, ErrGasUintOverflow
} }
gas += z * params.TxDataZeroGas gas += z * params.TxDataZeroGas
if isContractCreation && isEIP3860 { if isContractCreation && isEIP3860 {
lenWords := toWordSize(dataLen) lenWords := toWordSize(dataLen)
if (math.MaxUint64-gas)/params.InitCodeWordGas < lenWords { if (math.MaxUint64-gas)/params.InitCodeWordGas < lenWords {
return 0, ErrGasUintOverflow return vm.GasCosts{}, ErrGasUintOverflow
} }
gas += lenWords * params.InitCodeWordGas gas += lenWords * params.InitCodeWordGas
} }
@ -113,7 +113,7 @@ func IntrinsicGas(data []byte, accessList types.AccessList, authList []types.Set
if authList != nil { if authList != nil {
gas += uint64(len(authList)) * params.CallNewAccountGas gas += uint64(len(authList)) * params.CallNewAccountGas
} }
return gas, nil return vm.GasCosts{RegularGas: gas}, nil
} }
// FloorDataGas computes the minimum gas required for a transaction based on its data tokens (EIP-7623). // FloorDataGas computes the minimum gas required for a transaction based on its data tokens (EIP-7623).
@ -242,12 +242,12 @@ func ApplyMessage(evm *vm.EVM, msg *Message, gp *GasPool) (*ExecutionResult, err
// 5. Run Script section // 5. Run Script section
// 6. Derive new state root // 6. Derive new state root
type stateTransition struct { type stateTransition struct {
gp *GasPool gp *GasPool
msg *Message msg *Message
gasRemaining uint64 initialBudget vm.GasBudget
initialGas uint64 gasRemaining vm.GasBudget
state vm.StateDB state vm.StateDB
evm *vm.EVM evm *vm.EVM
} }
// newStateTransition initialises and returns a new state transition object. // newStateTransition initialises and returns a new state transition object.
@ -304,8 +304,8 @@ func (st *stateTransition) buyGas() error {
if st.evm.Config.Tracer != nil && st.evm.Config.Tracer.OnGasChange != nil { if st.evm.Config.Tracer != nil && st.evm.Config.Tracer.OnGasChange != nil {
st.evm.Config.Tracer.OnGasChange(0, st.msg.GasLimit, tracing.GasChangeTxInitialBalance) st.evm.Config.Tracer.OnGasChange(0, st.msg.GasLimit, tracing.GasChangeTxInitialBalance)
} }
st.gasRemaining = st.msg.GasLimit st.gasRemaining = vm.NewGasBudget(st.msg.GasLimit)
st.initialGas = st.msg.GasLimit st.initialBudget = st.gasRemaining.Copy()
mgvalU256, _ := uint256.FromBig(mgval) mgvalU256, _ := uint256.FromBig(mgval)
st.state.SubBalance(st.msg.From, mgvalU256, tracing.BalanceDecreaseGasBuy) st.state.SubBalance(st.msg.From, mgvalU256, tracing.BalanceDecreaseGasBuy)
@ -446,14 +446,17 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
contractCreation = msg.To == nil contractCreation = msg.To == nil
floorDataGas uint64 floorDataGas uint64
) )
// Check clauses 4-5, subtract intrinsic gas if everything is correct // Check clauses 4-5, subtract intrinsic gas if everything is correct
gas, err := IntrinsicGas(msg.Data, msg.AccessList, msg.SetCodeAuthorizations, contractCreation, rules.IsHomestead, rules.IsIstanbul, rules.IsShanghai) cost, err := IntrinsicGas(msg.Data, msg.AccessList, msg.SetCodeAuthorizations, contractCreation, rules.IsHomestead, rules.IsIstanbul, rules.IsShanghai)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if st.gasRemaining < gas { prior, sufficient := st.gasRemaining.Charge(cost)
return nil, fmt.Errorf("%w: have %d, want %d", ErrIntrinsicGas, st.gasRemaining, gas) if !sufficient {
return nil, fmt.Errorf("%w: have %d, want %d", ErrIntrinsicGas, st.gasRemaining.RegularGas, cost.RegularGas)
}
if t := st.evm.Config.Tracer; t != nil && t.OnGasChange != nil {
t.OnGasChange(prior, st.gasRemaining.RegularGas, tracing.GasChangeTxIntrinsicGas)
} }
// Gas limit suffices for the floor data cost (EIP-7623) // Gas limit suffices for the floor data cost (EIP-7623)
if rules.IsPrague { if rules.IsPrague {
@ -465,10 +468,6 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
return nil, fmt.Errorf("%w: have %d, want %d", ErrFloorDataGas, msg.GasLimit, floorDataGas) return nil, fmt.Errorf("%w: have %d, want %d", ErrFloorDataGas, msg.GasLimit, floorDataGas)
} }
} }
if t := st.evm.Config.Tracer; t != nil && t.OnGasChange != nil {
t.OnGasChange(st.gasRemaining, st.gasRemaining-gas, tracing.GasChangeTxIntrinsicGas)
}
st.gasRemaining -= gas
if rules.IsEIP4762 { if rules.IsEIP4762 {
st.evm.AccessEvents.AddTxOrigin(msg.From) st.evm.AccessEvents.AddTxOrigin(msg.From)
@ -535,14 +534,14 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
peakGasUsed := st.gasUsed() peakGasUsed := st.gasUsed()
// Compute refund counter, capped to a refund quotient. // Compute refund counter, capped to a refund quotient.
st.gasRemaining += st.calcRefund() st.gasRemaining.Refund(st.calcRefund())
if rules.IsPrague { if rules.IsPrague {
// After EIP-7623: Data-heavy transactions pay the floor gas. // After EIP-7623: Data-heavy transactions pay the floor gas.
if st.gasUsed() < floorDataGas { if used := st.gasUsed(); used < floorDataGas {
prev := st.gasRemaining prior, _ := st.gasRemaining.Charge(vm.GasCosts{RegularGas: floorDataGas - used})
st.gasRemaining = st.initialGas - floorDataGas
if t := st.evm.Config.Tracer; t != nil && t.OnGasChange != nil { if t := st.evm.Config.Tracer; t != nil && t.OnGasChange != nil {
t.OnGasChange(prev, st.gasRemaining, tracing.GasChangeTxDataFloor) t.OnGasChange(prior, st.gasRemaining.RegularGas, tracing.GasChangeTxDataFloor)
} }
} }
if peakGasUsed < floorDataGas { if peakGasUsed < floorDataGas {
@ -555,10 +554,10 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
// Return gas to the gas pool // Return gas to the gas pool
if rules.IsAmsterdam { if rules.IsAmsterdam {
// Refund is excluded for returning // Refund is excluded for returning
err = st.gp.ReturnGas(st.initialGas-peakGasUsed, st.gasUsed()) err = st.gp.ReturnGas(st.initialBudget.RegularGas-peakGasUsed, st.gasUsed())
} else { } else {
// Refund is included for returning // Refund is included for returning
err = st.gp.ReturnGas(st.gasRemaining, st.gasUsed()) err = st.gp.ReturnGas(st.gasRemaining.RegularGas, st.gasUsed())
} }
if err != nil { if err != nil {
return nil, err return nil, err
@ -655,7 +654,7 @@ func (st *stateTransition) applyAuthorization(auth *types.SetCodeAuthorization)
} }
// calcRefund computes refund counter, capped to a refund quotient. // calcRefund computes refund counter, capped to a refund quotient.
func (st *stateTransition) calcRefund() uint64 { func (st *stateTransition) calcRefund() vm.GasBudget {
var refund uint64 var refund uint64
if !st.evm.ChainConfig().IsLondon(st.evm.Context.BlockNumber) { if !st.evm.ChainConfig().IsLondon(st.evm.Context.BlockNumber) {
// Before EIP-3529: refunds were capped to gasUsed / 2 // Before EIP-3529: refunds were capped to gasUsed / 2
@ -668,26 +667,26 @@ func (st *stateTransition) calcRefund() uint64 {
refund = st.state.GetRefund() refund = st.state.GetRefund()
} }
if st.evm.Config.Tracer != nil && st.evm.Config.Tracer.OnGasChange != nil && refund > 0 { if st.evm.Config.Tracer != nil && st.evm.Config.Tracer.OnGasChange != nil && refund > 0 {
st.evm.Config.Tracer.OnGasChange(st.gasRemaining, st.gasRemaining+refund, tracing.GasChangeTxRefunds) st.evm.Config.Tracer.OnGasChange(st.gasRemaining.RegularGas, st.gasRemaining.RegularGas+refund, tracing.GasChangeTxRefunds)
} }
return refund return vm.NewGasBudget(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() { func (st *stateTransition) returnGas() {
remaining := uint256.NewInt(st.gasRemaining) remaining := uint256.NewInt(st.gasRemaining.RegularGas)
remaining.Mul(remaining, uint256.MustFromBig(st.msg.GasPrice)) remaining.Mul(remaining, uint256.MustFromBig(st.msg.GasPrice))
st.state.AddBalance(st.msg.From, remaining, tracing.BalanceIncreaseGasReturn) st.state.AddBalance(st.msg.From, remaining, tracing.BalanceIncreaseGasReturn)
if st.evm.Config.Tracer != nil && st.evm.Config.Tracer.OnGasChange != nil && st.gasRemaining > 0 { if st.evm.Config.Tracer != nil && st.evm.Config.Tracer.OnGasChange != nil && st.gasRemaining.RegularGas > 0 {
st.evm.Config.Tracer.OnGasChange(st.gasRemaining, 0, tracing.GasChangeTxLeftOverReturned) st.evm.Config.Tracer.OnGasChange(st.gasRemaining.RegularGas, 0, tracing.GasChangeTxLeftOverReturned)
} }
} }
// gasUsed returns the amount of gas used up by the state transition. // gasUsed returns the amount of gas used up by the state transition.
func (st *stateTransition) gasUsed() uint64 { func (st *stateTransition) gasUsed() uint64 {
return st.initialGas - st.gasRemaining return st.gasRemaining.Used(st.initialBudget)
} }
// blobGasUsed returns the amount of blob gas used by the message. // blobGasUsed returns the amount of blob gas used by the message.

View file

@ -167,6 +167,8 @@ type (
// GasChangeHook is invoked when the gas changes. // GasChangeHook is invoked when the gas changes.
GasChangeHook = func(old, new uint64, reason GasChangeReason) GasChangeHook = func(old, new uint64, reason GasChangeReason)
// TODO(sina, rjl), please add GasChangeV2Hook by landing the multi-dimensional gas
/* /*
- Chain events - - Chain events -
*/ */

View file

@ -129,8 +129,8 @@ func ValidateTransaction(tx *types.Transaction, head *types.Header, signer types
if err != nil { if err != nil {
return err return err
} }
if tx.Gas() < intrGas { if tx.Gas() < intrGas.RegularGas {
return fmt.Errorf("%w: gas %v, minimum needed %v", core.ErrIntrinsicGas, tx.Gas(), intrGas) return fmt.Errorf("%w: gas %v, minimum needed %v", core.ErrIntrinsicGas, tx.Gas(), intrGas.RegularGas)
} }
// Ensure the transaction can cover floor data gas. // Ensure the transaction can cover floor data gas.
if rules.IsPrague { if rules.IsPrague {

View file

@ -42,12 +42,12 @@ type Contract struct {
IsDeployment bool IsDeployment bool
IsSystemCall bool IsSystemCall bool
Gas GasCosts Gas GasBudget
value *uint256.Int value *uint256.Int
} }
// NewContract returns a new contract environment for the execution of EVM. // NewContract returns a new contract environment for the execution of EVM.
func NewContract(caller common.Address, address common.Address, value *uint256.Int, gas uint64, jumpDests JumpDestCache) *Contract { func NewContract(caller common.Address, address common.Address, value *uint256.Int, gas GasBudget, jumpDests JumpDestCache) *Contract {
// Initialize the jump analysis cache if it's nil, mostly for tests // Initialize the jump analysis cache if it's nil, mostly for tests
if jumpDests == nil { if jumpDests == nil {
jumpDests = newMapJumpDests() jumpDests = newMapJumpDests()
@ -56,7 +56,7 @@ func NewContract(caller common.Address, address common.Address, value *uint256.I
caller: caller, caller: caller,
address: address, address: address,
jumpDests: jumpDests, jumpDests: jumpDests,
Gas: GasCosts{RegularGas: gas}, Gas: gas,
value: value, value: value,
} }
} }
@ -126,26 +126,26 @@ func (c *Contract) Caller() common.Address {
} }
// UseGas attempts the use gas and subtracts it and returns true on success // UseGas attempts the use gas and subtracts it and returns true on success
func (c *Contract) UseGas(gas uint64, logger *tracing.Hooks, reason tracing.GasChangeReason) (ok bool) { func (c *Contract) UseGas(cost GasCosts, logger *tracing.Hooks, reason tracing.GasChangeReason) (ok bool) {
if c.Gas.RegularGas < gas { prior, ok := c.Gas.Charge(cost)
if !ok {
return false return false
} }
if logger != nil && logger.OnGasChange != nil && reason != tracing.GasChangeIgnored { if logger != nil && logger.OnGasChange != nil && reason != tracing.GasChangeIgnored {
logger.OnGasChange(c.Gas.RegularGas, c.Gas.RegularGas-gas, reason) logger.OnGasChange(prior, c.Gas.RegularGas, reason)
} }
c.Gas.RegularGas -= gas
return true return true
} }
// RefundGas refunds gas to the contract // RefundGas refunds the leftover gas budget back to the contract.
func (c *Contract) RefundGas(gas uint64, logger *tracing.Hooks, reason tracing.GasChangeReason) { func (c *Contract) RefundGas(refund GasBudget, logger *tracing.Hooks, reason tracing.GasChangeReason) {
if gas == 0 { prior, changed := c.Gas.Refund(refund)
if !changed {
return return
} }
if logger != nil && logger.OnGasChange != nil && reason != tracing.GasChangeIgnored { if logger != nil && logger.OnGasChange != nil && reason != tracing.GasChangeIgnored {
logger.OnGasChange(c.Gas.RegularGas, c.Gas.RegularGas+gas, reason) logger.OnGasChange(prior, c.Gas.RegularGas, reason)
} }
c.Gas.RegularGas += gas
} }
// Address returns the contracts address // Address returns the contracts address

View file

@ -260,25 +260,25 @@ func ActivePrecompiles(rules params.Rules) []common.Address {
// RunPrecompiledContract runs and evaluates the output of a precompiled contract. // RunPrecompiledContract runs and evaluates the output of a precompiled contract.
// It returns // It returns
// - the returned bytes, // - the returned bytes,
// - the _remaining_ gas, // - the remaining gas budget,
// - any error that occurred // - any error that occurred
func RunPrecompiledContract(stateDB StateDB, p PrecompiledContract, address common.Address, input []byte, suppliedGas uint64, logger *tracing.Hooks) (ret []byte, remainingGas uint64, err error) { func RunPrecompiledContract(stateDB StateDB, p PrecompiledContract, address common.Address, input []byte, gas GasBudget, logger *tracing.Hooks) (ret []byte, remaining GasBudget, err error) {
gasCost := p.RequiredGas(input) gasCost := p.RequiredGas(input)
if suppliedGas < gasCost { prior, ok := gas.Charge(GasCosts{RegularGas: gasCost})
return nil, 0, ErrOutOfGas if !ok {
gas.Exhaust()
return nil, gas, ErrOutOfGas
} }
if logger != nil && logger.OnGasChange != nil { if logger != nil && logger.OnGasChange != nil {
logger.OnGasChange(suppliedGas, suppliedGas-gasCost, tracing.GasChangeCallPrecompiledContract) logger.OnGasChange(prior, gas.RegularGas, tracing.GasChangeCallPrecompiledContract)
} }
suppliedGas -= gasCost
// Touch the precompile for block-level accessList recording once Amsterdam // Touch the precompile for block-level accessList recording once Amsterdam
// fork is activated. // fork is activated.
if stateDB != nil { if stateDB != nil {
stateDB.Exist(address) stateDB.Exist(address)
} }
output, err := p.Run(input) output, err := p.Run(input)
return output, suppliedGas, err return output, gas, err
} }
// ecrecover implemented as a native contract. // ecrecover implemented as a native contract.

View file

@ -36,7 +36,7 @@ func FuzzPrecompiledContracts(f *testing.F) {
return return
} }
inWant := string(input) inWant := string(input)
RunPrecompiledContract(nil, p, a, input, gas, nil) RunPrecompiledContract(nil, p, a, input, NewGasBudget(gas), nil)
if inHave := string(input); inWant != inHave { if inHave := string(input); inWant != inHave {
t.Errorf("Precompiled %v modified input data", a) t.Errorf("Precompiled %v modified input data", a)
} }

View file

@ -99,7 +99,7 @@ func testPrecompiled(addr string, test precompiledTest, t *testing.T) {
in := common.Hex2Bytes(test.Input) in := common.Hex2Bytes(test.Input)
gas := p.RequiredGas(in) gas := p.RequiredGas(in)
t.Run(fmt.Sprintf("%s-Gas=%d", test.Name, gas), func(t *testing.T) { t.Run(fmt.Sprintf("%s-Gas=%d", test.Name, gas), func(t *testing.T) {
if res, _, err := RunPrecompiledContract(nil, p, common.HexToAddress(addr), in, gas, nil); err != nil { if res, _, err := RunPrecompiledContract(nil, p, common.HexToAddress(addr), in, NewGasBudget(gas), nil); err != nil {
t.Error(err) t.Error(err)
} else if common.Bytes2Hex(res) != test.Expected { } else if common.Bytes2Hex(res) != test.Expected {
t.Errorf("Expected %v, got %v", test.Expected, common.Bytes2Hex(res)) t.Errorf("Expected %v, got %v", test.Expected, common.Bytes2Hex(res))
@ -121,7 +121,7 @@ func testPrecompiledOOG(addr string, test precompiledTest, t *testing.T) {
gas := test.Gas - 1 gas := test.Gas - 1
t.Run(fmt.Sprintf("%s-Gas=%d", test.Name, gas), func(t *testing.T) { t.Run(fmt.Sprintf("%s-Gas=%d", test.Name, gas), func(t *testing.T) {
_, _, err := RunPrecompiledContract(nil, p, common.HexToAddress(addr), in, gas, nil) _, _, err := RunPrecompiledContract(nil, p, common.HexToAddress(addr), in, NewGasBudget(gas), nil)
if err.Error() != "out of gas" { if err.Error() != "out of gas" {
t.Errorf("Expected error [out of gas], got [%v]", err) t.Errorf("Expected error [out of gas], got [%v]", err)
} }
@ -138,7 +138,7 @@ func testPrecompiledFailure(addr string, test precompiledFailureTest, t *testing
in := common.Hex2Bytes(test.Input) in := common.Hex2Bytes(test.Input)
gas := p.RequiredGas(in) gas := p.RequiredGas(in)
t.Run(test.Name, func(t *testing.T) { t.Run(test.Name, func(t *testing.T) {
_, _, err := RunPrecompiledContract(nil, p, common.HexToAddress(addr), in, gas, nil) _, _, err := RunPrecompiledContract(nil, p, common.HexToAddress(addr), in, NewGasBudget(gas), nil)
if err.Error() != test.ExpectedError { if err.Error() != test.ExpectedError {
t.Errorf("Expected error [%v], got [%v]", test.ExpectedError, err) t.Errorf("Expected error [%v], got [%v]", test.ExpectedError, err)
} }
@ -169,7 +169,7 @@ func benchmarkPrecompiled(addr string, test precompiledTest, bench *testing.B) {
start := time.Now() start := time.Now()
for bench.Loop() { for bench.Loop() {
copy(data, in) copy(data, in)
res, _, err = RunPrecompiledContract(nil, p, common.HexToAddress(addr), data, reqGas, nil) res, _, err = RunPrecompiledContract(nil, p, common.HexToAddress(addr), data, NewGasBudget(reqGas), nil)
} }
elapsed := uint64(time.Since(start)) elapsed := uint64(time.Since(start))
if elapsed < 1 { if elapsed < 1 {

View file

@ -382,7 +382,7 @@ func opExtCodeCopyEIP4762(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, er
code := evm.StateDB.GetCode(addr) code := evm.StateDB.GetCode(addr)
paddedCodeCopy, copyOffset, nonPaddedCopyLength := getDataAndAdjustedBounds(code, uint64CodeOffset, length.Uint64()) paddedCodeCopy, copyOffset, nonPaddedCopyLength := getDataAndAdjustedBounds(code, uint64CodeOffset, length.Uint64())
consumed, wanted := evm.AccessEvents.CodeChunksRangeGas(addr, copyOffset, nonPaddedCopyLength, uint64(len(code)), false, scope.Contract.Gas.RegularGas) consumed, wanted := evm.AccessEvents.CodeChunksRangeGas(addr, copyOffset, nonPaddedCopyLength, uint64(len(code)), false, scope.Contract.Gas.RegularGas)
scope.Contract.UseGas(consumed, evm.Config.Tracer, tracing.GasChangeUnspecified) scope.Contract.UseGas(GasCosts{RegularGas: consumed}, evm.Config.Tracer, tracing.GasChangeUnspecified)
if consumed < wanted { if consumed < wanted {
return nil, ErrOutOfGas return nil, ErrOutOfGas
} }
@ -408,7 +408,7 @@ func opPush1EIP4762(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
// advanced past this boundary. // advanced past this boundary.
contractAddr := scope.Contract.Address() contractAddr := scope.Contract.Address()
consumed, wanted := evm.AccessEvents.CodeChunksRangeGas(contractAddr, *pc+1, uint64(1), uint64(len(scope.Contract.Code)), false, scope.Contract.Gas.RegularGas) consumed, wanted := evm.AccessEvents.CodeChunksRangeGas(contractAddr, *pc+1, uint64(1), uint64(len(scope.Contract.Code)), false, scope.Contract.Gas.RegularGas)
scope.Contract.UseGas(wanted, evm.Config.Tracer, tracing.GasChangeUnspecified) scope.Contract.UseGas(GasCosts{RegularGas: wanted}, evm.Config.Tracer, tracing.GasChangeUnspecified)
if consumed < wanted { if consumed < wanted {
return nil, ErrOutOfGas return nil, ErrOutOfGas
} }
@ -436,7 +436,7 @@ func makePushEIP4762(size uint64, pushByteSize int) executionFunc {
if !scope.Contract.IsDeployment && !scope.Contract.IsSystemCall { if !scope.Contract.IsDeployment && !scope.Contract.IsSystemCall {
contractAddr := scope.Contract.Address() contractAddr := scope.Contract.Address()
consumed, wanted := evm.AccessEvents.CodeChunksRangeGas(contractAddr, uint64(start), uint64(pushByteSize), uint64(len(scope.Contract.Code)), false, scope.Contract.Gas.RegularGas) consumed, wanted := evm.AccessEvents.CodeChunksRangeGas(contractAddr, uint64(start), uint64(pushByteSize), uint64(len(scope.Contract.Code)), false, scope.Contract.Gas.RegularGas)
scope.Contract.UseGas(consumed, evm.Config.Tracer, tracing.GasChangeUnspecified) scope.Contract.UseGas(GasCosts{RegularGas: consumed}, evm.Config.Tracer, tracing.GasChangeUnspecified)
if consumed < wanted { if consumed < wanted {
return nil, ErrOutOfGas return nil, ErrOutOfGas
} }

View file

@ -236,13 +236,13 @@ func isSystemCall(caller common.Address) bool {
// parameters. It also handles any necessary value transfer required and takse // parameters. It also handles any necessary value transfer required and takse
// the necessary steps to create accounts and reverses the state in case of an // the necessary steps to create accounts and reverses the state in case of an
// execution error or failed value transfer. // execution error or failed value transfer.
func (evm *EVM) Call(caller common.Address, addr common.Address, input []byte, gas uint64, value *uint256.Int) (ret []byte, leftOverGas uint64, err error) { func (evm *EVM) Call(caller common.Address, addr common.Address, input []byte, gas GasBudget, value *uint256.Int) (ret []byte, leftOverGas GasBudget, err error) {
// Capture the tracer start/end events in debug mode // Capture the tracer start/end events in debug mode
if evm.Config.Tracer != nil { if evm.Config.Tracer != nil {
evm.captureBegin(evm.depth, CALL, caller, addr, input, gas, value.ToBig()) evm.captureBegin(evm.depth, CALL, caller, addr, input, gas.RegularGas, value.ToBig())
defer func(startGas uint64) { defer func(startGas uint64) {
evm.captureEnd(evm.depth, startGas, leftOverGas, ret, err) evm.captureEnd(evm.depth, startGas, leftOverGas.RegularGas, ret, err)
}(gas) }(gas.RegularGas)
} }
// Fail if we're trying to execute above the call depth limit // Fail if we're trying to execute above the call depth limit
if evm.depth > int(params.CallCreateDepth) { if evm.depth > int(params.CallCreateDepth) {
@ -265,12 +265,12 @@ func (evm *EVM) Call(caller common.Address, addr common.Address, input []byte, g
// list in write mode. If there is enough gas paying for the addition of the code // list in write mode. If there is enough gas paying for the addition of the code
// hash leaf to the access list, then account creation will proceed unimpaired. // hash leaf to the access list, then account creation will proceed unimpaired.
// Thus, only pay for the creation of the code hash leaf here. // Thus, only pay for the creation of the code hash leaf here.
wgas := evm.AccessEvents.CodeHashGas(addr, true, gas, false) wgas := evm.AccessEvents.CodeHashGas(addr, true, gas.RegularGas, false)
if gas < wgas { if _, ok := gas.Charge(GasCosts{RegularGas: wgas}); !ok {
evm.StateDB.RevertToSnapshot(snapshot) evm.StateDB.RevertToSnapshot(snapshot)
return nil, 0, ErrOutOfGas gas.Exhaust()
return nil, gas, ErrOutOfGas
} }
gas -= wgas
} }
if !isPrecompile && evm.chainRules.IsEIP158 && value.IsZero() { if !isPrecompile && evm.chainRules.IsEIP158 && value.IsZero() {
@ -303,7 +303,7 @@ func (evm *EVM) Call(caller common.Address, addr common.Address, input []byte, g
contract.IsSystemCall = isSystemCall(caller) contract.IsSystemCall = isSystemCall(caller)
contract.SetCallCode(evm.resolveCodeHash(addr), code) contract.SetCallCode(evm.resolveCodeHash(addr), code)
ret, err = evm.Run(contract, input, false) ret, err = evm.Run(contract, input, false)
gas = contract.Gas.RegularGas gas = contract.Gas
} }
} }
// When an error was returned by the EVM or when setting the creation code // When an error was returned by the EVM or when setting the creation code
@ -313,9 +313,9 @@ func (evm *EVM) Call(caller common.Address, addr common.Address, input []byte, g
evm.StateDB.RevertToSnapshot(snapshot) evm.StateDB.RevertToSnapshot(snapshot)
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, 0, tracing.GasChangeCallFailedExecution) evm.Config.Tracer.OnGasChange(gas.RegularGas, 0, tracing.GasChangeCallFailedExecution)
} }
gas = 0 gas.Exhaust()
} }
// TODO: consider clearing up unused snapshots: // TODO: consider clearing up unused snapshots:
//} else { //} else {
@ -331,13 +331,13 @@ func (evm *EVM) Call(caller common.Address, addr common.Address, input []byte, g
// //
// CallCode differs from Call in the sense that it executes the given address' // CallCode differs from Call in the sense that it executes the given address'
// code with the caller as context. // code with the caller as context.
func (evm *EVM) CallCode(caller common.Address, addr common.Address, input []byte, gas uint64, value *uint256.Int) (ret []byte, leftOverGas uint64, err error) { func (evm *EVM) CallCode(caller common.Address, addr common.Address, input []byte, gas GasBudget, value *uint256.Int) (ret []byte, leftOverGas GasBudget, err error) {
// Invoke tracer hooks that signal entering/exiting a call frame // Invoke tracer hooks that signal entering/exiting a call frame
if evm.Config.Tracer != nil { if evm.Config.Tracer != nil {
evm.captureBegin(evm.depth, CALLCODE, caller, addr, input, gas, value.ToBig()) evm.captureBegin(evm.depth, CALLCODE, caller, addr, input, gas.RegularGas, value.ToBig())
defer func(startGas uint64) { defer func(startGas uint64) {
evm.captureEnd(evm.depth, startGas, leftOverGas, ret, err) evm.captureEnd(evm.depth, startGas, leftOverGas.RegularGas, ret, err)
}(gas) }(gas.RegularGas)
} }
// Fail if we're trying to execute above the call depth limit // Fail if we're trying to execute above the call depth limit
if evm.depth > int(params.CallCreateDepth) { if evm.depth > int(params.CallCreateDepth) {
@ -365,15 +365,15 @@ func (evm *EVM) CallCode(caller common.Address, addr common.Address, input []byt
contract := NewContract(caller, caller, value, gas, evm.jumpDests) contract := NewContract(caller, 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.RegularGas gas = contract.Gas
} }
if err != nil { if err != nil {
evm.StateDB.RevertToSnapshot(snapshot) evm.StateDB.RevertToSnapshot(snapshot)
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, 0, tracing.GasChangeCallFailedExecution) evm.Config.Tracer.OnGasChange(gas.RegularGas, 0, tracing.GasChangeCallFailedExecution)
} }
gas = 0 gas.Exhaust()
} }
} }
return ret, gas, err return ret, gas, err
@ -384,14 +384,14 @@ func (evm *EVM) CallCode(caller common.Address, addr common.Address, input []byt
// //
// DelegateCall differs from CallCode in the sense that it executes the given address' // DelegateCall differs from CallCode in the sense that it executes the given address'
// code with the caller as context and the caller is set to the caller of the caller. // code with the caller as context and the caller is set to the caller of the caller.
func (evm *EVM) DelegateCall(originCaller common.Address, caller common.Address, addr common.Address, input []byte, gas uint64, value *uint256.Int) (ret []byte, leftOverGas uint64, err error) { func (evm *EVM) DelegateCall(originCaller common.Address, caller common.Address, addr common.Address, input []byte, gas GasBudget, value *uint256.Int) (ret []byte, leftOverGas GasBudget, err error) {
// Invoke tracer hooks that signal entering/exiting a call frame // Invoke tracer hooks that signal entering/exiting a call frame
if evm.Config.Tracer != nil { if evm.Config.Tracer != nil {
// DELEGATECALL inherits value from parent call // DELEGATECALL inherits value from parent call
evm.captureBegin(evm.depth, DELEGATECALL, caller, addr, input, gas, value.ToBig()) evm.captureBegin(evm.depth, DELEGATECALL, caller, addr, input, gas.RegularGas, value.ToBig())
defer func(startGas uint64) { defer func(startGas uint64) {
evm.captureEnd(evm.depth, startGas, leftOverGas, ret, err) evm.captureEnd(evm.depth, startGas, leftOverGas.RegularGas, ret, err)
}(gas) }(gas.RegularGas)
} }
// Fail if we're trying to execute above the call depth limit // Fail if we're trying to execute above the call depth limit
if evm.depth > int(params.CallCreateDepth) { if evm.depth > int(params.CallCreateDepth) {
@ -413,15 +413,15 @@ func (evm *EVM) DelegateCall(originCaller common.Address, caller common.Address,
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.RegularGas gas = contract.Gas
} }
if err != nil { if err != nil {
evm.StateDB.RevertToSnapshot(snapshot) evm.StateDB.RevertToSnapshot(snapshot)
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, 0, tracing.GasChangeCallFailedExecution) evm.Config.Tracer.OnGasChange(gas.RegularGas, 0, tracing.GasChangeCallFailedExecution)
} }
gas = 0 gas.Exhaust()
} }
} }
return ret, gas, err return ret, gas, err
@ -431,13 +431,13 @@ func (evm *EVM) DelegateCall(originCaller common.Address, caller common.Address,
// as parameters while disallowing any modifications to the state during the call. // as parameters while disallowing any modifications to the state during the call.
// Opcodes that attempt to perform such modifications will result in exceptions // Opcodes that attempt to perform such modifications will result in exceptions
// instead of performing the modifications. // instead of performing the modifications.
func (evm *EVM) StaticCall(caller common.Address, addr common.Address, input []byte, gas uint64) (ret []byte, leftOverGas uint64, err error) { func (evm *EVM) StaticCall(caller common.Address, addr common.Address, input []byte, gas GasBudget) (ret []byte, leftOverGas GasBudget, err error) {
// Invoke tracer hooks that signal entering/exiting a call frame // Invoke tracer hooks that signal entering/exiting a call frame
if evm.Config.Tracer != nil { if evm.Config.Tracer != nil {
evm.captureBegin(evm.depth, STATICCALL, caller, addr, input, gas, nil) evm.captureBegin(evm.depth, STATICCALL, caller, addr, input, gas.RegularGas, nil)
defer func(startGas uint64) { defer func(startGas uint64) {
evm.captureEnd(evm.depth, startGas, leftOverGas, ret, err) evm.captureEnd(evm.depth, startGas, leftOverGas.RegularGas, ret, err)
}(gas) }(gas.RegularGas)
} }
// Fail if we're trying to execute above the call depth limit // Fail if we're trying to execute above the call depth limit
if evm.depth > int(params.CallCreateDepth) { if evm.depth > int(params.CallCreateDepth) {
@ -472,28 +472,27 @@ func (evm *EVM) StaticCall(caller common.Address, addr common.Address, input []b
// above we revert to the snapshot and consume any gas remaining. Additionally // above we revert to the snapshot and consume any gas remaining. Additionally
// when we're in Homestead this also counts for code storage gas errors. // when we're in Homestead this also counts for code storage gas errors.
ret, err = evm.Run(contract, input, true) ret, err = evm.Run(contract, input, true)
gas = contract.Gas.RegularGas gas = contract.Gas
} }
if err != nil { if err != nil {
evm.StateDB.RevertToSnapshot(snapshot) evm.StateDB.RevertToSnapshot(snapshot)
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, 0, tracing.GasChangeCallFailedExecution) evm.Config.Tracer.OnGasChange(gas.RegularGas, 0, tracing.GasChangeCallFailedExecution)
} }
gas.Exhaust()
gas = 0
} }
} }
return ret, gas, err return ret, gas, err
} }
// create creates a new contract using code as deployment code. // create creates a new contract using code as deployment code.
func (evm *EVM) create(caller common.Address, code []byte, gas uint64, value *uint256.Int, address common.Address, typ OpCode) (ret []byte, createAddress common.Address, leftOverGas uint64, err error) { func (evm *EVM) create(caller common.Address, code []byte, gas GasBudget, value *uint256.Int, address common.Address, typ OpCode) (ret []byte, createAddress common.Address, leftOverGas GasBudget, err error) {
if evm.Config.Tracer != nil { if evm.Config.Tracer != nil {
evm.captureBegin(evm.depth, typ, caller, address, code, gas, value.ToBig()) evm.captureBegin(evm.depth, typ, caller, address, code, gas.RegularGas, value.ToBig())
defer func(startGas uint64) { defer func(startGas uint64) {
evm.captureEnd(evm.depth, startGas, leftOverGas, ret, err) evm.captureEnd(evm.depth, startGas, leftOverGas.RegularGas, ret, err)
}(gas) }(gas.RegularGas)
} }
// Depth check execution. Fail if we're trying to execute above the // Depth check execution. Fail if we're trying to execute above the
// limit. // limit.
@ -511,14 +510,15 @@ func (evm *EVM) create(caller common.Address, code []byte, gas uint64, value *ui
// Charge the contract creation init gas in verkle mode // Charge the contract creation init gas in verkle mode
if evm.chainRules.IsEIP4762 { if evm.chainRules.IsEIP4762 {
statelessGas := evm.AccessEvents.ContractCreatePreCheckGas(address, gas) statelessGas := evm.AccessEvents.ContractCreatePreCheckGas(address, gas.RegularGas)
if statelessGas > gas { prior, ok := gas.Charge(GasCosts{RegularGas: statelessGas})
return nil, common.Address{}, 0, ErrOutOfGas if !ok {
gas.Exhaust()
return nil, common.Address{}, gas, ErrOutOfGas
} }
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, gas-statelessGas, tracing.GasChangeWitnessContractCollisionCheck) evm.Config.Tracer.OnGasChange(prior, gas.RegularGas, tracing.GasChangeWitnessContractCollisionCheck)
} }
gas = gas - statelessGas
} }
// We add this to the access list _before_ taking a snapshot. Even if the // We add this to the access list _before_ taking a snapshot. Even if the
@ -536,9 +536,10 @@ func (evm *EVM) create(caller common.Address, code []byte, gas uint64, value *ui
(contractHash != (common.Hash{}) && contractHash != types.EmptyCodeHash) || // non-empty code (contractHash != (common.Hash{}) && contractHash != types.EmptyCodeHash) || // non-empty code
isEIP7610RejectedAccount(evm.ChainConfig().ChainID, address, evm.chainRules.IsEIP158) { isEIP7610RejectedAccount(evm.ChainConfig().ChainID, address, evm.chainRules.IsEIP158) {
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, 0, tracing.GasChangeCallFailedExecution) evm.Config.Tracer.OnGasChange(gas.RegularGas, 0, tracing.GasChangeCallFailedExecution)
} }
return nil, common.Address{}, 0, ErrContractAddressCollision gas.Exhaust()
return nil, common.Address{}, gas, ErrContractAddressCollision
} }
// Create a new account on the state only if the object was not present. // Create a new account on the state only if the object was not present.
// It might be possible the contract code is deployed to a pre-existent // It might be possible the contract code is deployed to a pre-existent
@ -558,14 +559,15 @@ func (evm *EVM) create(caller common.Address, code []byte, gas uint64, value *ui
} }
// Charge the contract creation init gas in verkle mode // Charge the contract creation init gas in verkle mode
if evm.chainRules.IsEIP4762 { if evm.chainRules.IsEIP4762 {
consumed, wanted := evm.AccessEvents.ContractCreateInitGas(address, gas) consumed, wanted := evm.AccessEvents.ContractCreateInitGas(address, gas.RegularGas)
if consumed < wanted { if consumed < wanted {
return nil, common.Address{}, 0, ErrOutOfGas gas.Exhaust()
return nil, common.Address{}, gas, ErrOutOfGas
} }
prior, _ := gas.Charge(GasCosts{RegularGas: consumed})
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, gas-consumed, tracing.GasChangeWitnessContractInit) evm.Config.Tracer.OnGasChange(prior, gas.RegularGas, tracing.GasChangeWitnessContractInit)
} }
gas = gas - consumed
} }
evm.Context.Transfer(evm.StateDB, caller, address, value, &evm.chainRules) evm.Context.Transfer(evm.StateDB, caller, address, value, &evm.chainRules)
@ -582,10 +584,10 @@ func (evm *EVM) create(caller common.Address, code []byte, gas uint64, value *ui
if err != nil && (evm.chainRules.IsHomestead || err != ErrCodeStoreOutOfGas) { if err != nil && (evm.chainRules.IsHomestead || err != ErrCodeStoreOutOfGas) {
evm.StateDB.RevertToSnapshot(snapshot) evm.StateDB.RevertToSnapshot(snapshot)
if err != ErrExecutionReverted { if err != ErrExecutionReverted {
contract.UseGas(contract.Gas.RegularGas, evm.Config.Tracer, tracing.GasChangeCallFailedExecution) contract.UseGas(GasCosts{RegularGas: contract.Gas.RegularGas}, evm.Config.Tracer, tracing.GasChangeCallFailedExecution)
} }
} }
return ret, address, contract.Gas.RegularGas, err return ret, address, contract.Gas, err
} }
// initNewContract runs a new contract's creation code, performs checks on the // initNewContract runs a new contract's creation code, performs checks on the
@ -608,12 +610,12 @@ func (evm *EVM) initNewContract(contract *Contract, address common.Address) ([]b
if !evm.chainRules.IsEIP4762 { if !evm.chainRules.IsEIP4762 {
createDataGas := uint64(len(ret)) * params.CreateDataGas createDataGas := uint64(len(ret)) * params.CreateDataGas
if !contract.UseGas(createDataGas, evm.Config.Tracer, tracing.GasChangeCallCodeStorage) { if !contract.UseGas(GasCosts{RegularGas: createDataGas}, evm.Config.Tracer, tracing.GasChangeCallCodeStorage) {
return ret, ErrCodeStoreOutOfGas return ret, ErrCodeStoreOutOfGas
} }
} else { } else {
consumed, wanted := evm.AccessEvents.CodeChunksRangeGas(address, 0, uint64(len(ret)), uint64(len(ret)), true, contract.Gas.RegularGas) consumed, wanted := evm.AccessEvents.CodeChunksRangeGas(address, 0, uint64(len(ret)), uint64(len(ret)), true, contract.Gas.RegularGas)
contract.UseGas(consumed, evm.Config.Tracer, tracing.GasChangeWitnessCodeChunk) contract.UseGas(GasCosts{RegularGas: consumed}, evm.Config.Tracer, tracing.GasChangeWitnessCodeChunk)
if len(ret) > 0 && (consumed < wanted) { if len(ret) > 0 && (consumed < wanted) {
return ret, ErrCodeStoreOutOfGas return ret, ErrCodeStoreOutOfGas
} }
@ -626,7 +628,7 @@ func (evm *EVM) initNewContract(contract *Contract, address common.Address) ([]b
} }
// Create creates a new contract using code as deployment code. // Create creates a new contract using code as deployment code.
func (evm *EVM) Create(caller common.Address, code []byte, gas uint64, value *uint256.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) { func (evm *EVM) Create(caller common.Address, code []byte, gas GasBudget, value *uint256.Int) (ret []byte, contractAddr common.Address, leftOverGas GasBudget, err error) {
contractAddr = crypto.CreateAddress(caller, evm.StateDB.GetNonce(caller)) contractAddr = crypto.CreateAddress(caller, evm.StateDB.GetNonce(caller))
return evm.create(caller, code, gas, value, contractAddr, CREATE) return evm.create(caller, code, gas, value, contractAddr, CREATE)
} }
@ -635,7 +637,7 @@ func (evm *EVM) Create(caller common.Address, code []byte, gas uint64, value *ui
// //
// The different between Create2 with Create is Create2 uses keccak256(0xff ++ msg.sender ++ salt ++ keccak256(init_code))[12:] // The different between Create2 with Create is Create2 uses keccak256(0xff ++ msg.sender ++ salt ++ keccak256(init_code))[12:]
// instead of the usual sender-and-nonce-hash as the address where the contract is initialized at. // instead of the usual sender-and-nonce-hash as the address where the contract is initialized at.
func (evm *EVM) Create2(caller common.Address, code []byte, gas uint64, endowment *uint256.Int, salt *uint256.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) { func (evm *EVM) Create2(caller common.Address, code []byte, gas GasBudget, endowment *uint256.Int, salt *uint256.Int) (ret []byte, contractAddr common.Address, leftOverGas GasBudget, err error) {
inithash := crypto.Keccak256Hash(code) inithash := crypto.Keccak256Hash(code)
contractAddr = crypto.CreateAddress2(caller, salt.Bytes32(), inithash[:]) contractAddr = crypto.CreateAddress2(caller, salt.Bytes32(), inithash[:])
return evm.create(caller, code, gas, endowment, contractAddr, CREATE2) return evm.create(caller, code, gas, endowment, contractAddr, CREATE2)

View file

@ -97,12 +97,12 @@ func TestEIP2200(t *testing.T) {
Transfer: func(StateDB, common.Address, common.Address, *uint256.Int, *params.Rules) {}, Transfer: func(StateDB, common.Address, common.Address, *uint256.Int, *params.Rules) {},
} }
evm := NewEVM(vmctx, statedb, params.AllEthashProtocolChanges, Config{ExtraEips: []int{2200}}) evm := NewEVM(vmctx, statedb, params.AllEthashProtocolChanges, Config{ExtraEips: []int{2200}})
initialGas := NewGasBudget(tt.gaspool)
_, gas, err := evm.Call(common.Address{}, address, nil, tt.gaspool, new(uint256.Int)) _, leftOver, err := evm.Call(common.Address{}, address, nil, initialGas.Copy(), new(uint256.Int))
if !errors.Is(err, tt.failure) { if !errors.Is(err, tt.failure) {
t.Errorf("test %d: failure mismatch: have %v, want %v", i, err, tt.failure) t.Errorf("test %d: failure mismatch: have %v, want %v", i, err, tt.failure)
} }
if used := tt.gaspool - gas; used != tt.used { if used := leftOver.Used(initialGas); used != tt.used {
t.Errorf("test %d: gas used mismatch: have %v, want %v", i, used, tt.used) t.Errorf("test %d: gas used mismatch: have %v, want %v", i, used, tt.used)
} }
if refund := evm.StateDB.GetRefund(); refund != tt.refund { if refund := evm.StateDB.GetRefund(); refund != tt.refund {
@ -157,12 +157,12 @@ func TestCreateGas(t *testing.T) {
} }
evm := NewEVM(vmctx, statedb, chainConfig, config) evm := NewEVM(vmctx, statedb, chainConfig, config)
var startGas = uint64(testGas) initialGas := NewGasBudget(uint64(testGas))
ret, gas, err := evm.Call(common.Address{}, address, nil, startGas, new(uint256.Int)) ret, leftOver, err := evm.Call(common.Address{}, address, nil, initialGas.Copy(), new(uint256.Int))
if err != nil { if err != nil {
return false return false
} }
gasUsed = startGas - gas gasUsed = leftOver.Used(initialGas)
if len(ret) != 32 { if len(ret) != 32 {
t.Fatalf("test %d: expected 32 bytes returned, have %d", i, len(ret)) t.Fatalf("test %d: expected 32 bytes returned, have %d", i, len(ret))
} }

View file

@ -19,7 +19,8 @@ package vm
import "fmt" import "fmt"
// GasCosts denotes a vector of gas costs in the // GasCosts denotes a vector of gas costs in the
// multidimensional metering paradigm. // multidimensional metering paradigm. It represents the cost
// charged by an individual operation.
type GasCosts struct { type GasCosts struct {
RegularGas uint64 RegularGas uint64
StateGas uint64 StateGas uint64
@ -34,3 +35,63 @@ func (g GasCosts) Sum() uint64 {
func (g GasCosts) String() string { func (g GasCosts) String() string {
return fmt.Sprintf("<%v,%v>", g.RegularGas, g.StateGas) return fmt.Sprintf("<%v,%v>", g.RegularGas, g.StateGas)
} }
// GasBudget denotes a vector of remaining gas allowances available
// for EVM execution in the multidimensional metering paradigm.
// Unlike GasCosts which represents the price of an operation,
// GasBudget tracks how much gas is left to spend.
type GasBudget struct {
RegularGas uint64 // The leftover gas for execution and state gas usage
StateGas uint64 // The state gas reservoir
}
// NewGasBudget creates a GasBudget with the given initial regular gas allowance.
func NewGasBudget(gas uint64) GasBudget {
return GasBudget{RegularGas: gas}
}
// Used returns the amount of regular gas consumed so far.
func (g GasBudget) Used(initial GasBudget) uint64 {
return initial.RegularGas - g.RegularGas
}
// Exhaust sets all remaining gas to zero, preserving the initial amount
// for usage tracking.
func (g *GasBudget) Exhaust() {
g.RegularGas = 0
g.StateGas = 0
}
func (g *GasBudget) Copy() GasBudget {
return GasBudget{RegularGas: g.RegularGas, StateGas: g.StateGas}
}
// String returns a visual representation of the gas budget vector.
func (g GasBudget) String() string {
return fmt.Sprintf("<%v,%v>", g.RegularGas, g.StateGas)
}
// CanAfford reports whether the budget has sufficient gas to cover the cost.
func (g GasBudget) CanAfford(cost GasCosts) bool {
return g.RegularGas >= cost.RegularGas
}
// Charge deducts the given gas cost from the budget. It returns the
// pre-charge gas value and false if the budget does not have sufficient
// gas to cover the cost.
func (g *GasBudget) Charge(cost GasCosts) (uint64, bool) {
prior := g.RegularGas
if prior < cost.RegularGas {
return prior, false
}
g.RegularGas -= cost.RegularGas
return prior, true
}
// Refund adds the given gas budget back. It returns the pre-refund gas
// value and whether the budget was actually changed.
func (g *GasBudget) Refund(other GasBudget) (uint64, bool) {
prior := g.RegularGas
g.RegularGas += other.RegularGas
return prior, g.RegularGas != prior
}

View file

@ -667,9 +667,9 @@ func opCreate(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
// reuse size int for stackvalue // reuse size int for stackvalue
stackvalue := size stackvalue := size
scope.Contract.UseGas(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, gas, &value) res, addr, returnGas, suberr := evm.Create(scope.Contract.Address(), input, NewGasBudget(gas), &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
@ -707,10 +707,10 @@ func opCreate2(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
// Apply EIP150 // Apply EIP150
gas -= gas / 64 gas -= gas / 64
scope.Contract.UseGas(gas, evm.Config.Tracer, tracing.GasChangeCallContractCreation2) scope.Contract.UseGas(GasCosts{RegularGas: gas}, evm.Config.Tracer, tracing.GasChangeCallContractCreation2)
// reuse size int for stackvalue // reuse size int for stackvalue
stackvalue := size stackvalue := size
res, addr, returnGas, suberr := evm.Create2(scope.Contract.Address(), input, gas, res, addr, returnGas, suberr := evm.Create2(scope.Contract.Address(), input, NewGasBudget(gas),
&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 {
@ -747,7 +747,7 @@ func opCall(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
if !value.IsZero() { if !value.IsZero() {
gas += params.CallStipend gas += params.CallStipend
} }
ret, returnGas, err := evm.Call(scope.Contract.Address(), toAddr, args, gas, &value) ret, returnGas, err := evm.Call(scope.Contract.Address(), toAddr, args, NewGasBudget(gas), &value)
if err != nil { if err != nil {
temp.Clear() temp.Clear()
@ -781,7 +781,7 @@ func opCallCode(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
gas += params.CallStipend gas += params.CallStipend
} }
ret, returnGas, err := evm.CallCode(scope.Contract.Address(), toAddr, args, gas, &value) ret, returnGas, err := evm.CallCode(scope.Contract.Address(), toAddr, args, NewGasBudget(gas), &value)
if err != nil { if err != nil {
temp.Clear() temp.Clear()
} else { } else {
@ -810,7 +810,7 @@ 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, gas, scope.Contract.value) ret, returnGas, err := evm.DelegateCall(scope.Contract.Caller(), scope.Contract.Address(), toAddr, args, NewGasBudget(gas), scope.Contract.value)
if err != nil { if err != nil {
temp.Clear() temp.Clear()
} else { } else {
@ -839,7 +839,7 @@ 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, gas) ret, returnGas, err := evm.StaticCall(scope.Contract.Address(), toAddr, args, NewGasBudget(gas))
if err != nil { if err != nil {
temp.Clear() temp.Clear()
} else { } else {

View file

@ -565,7 +565,7 @@ func TestOpTstore(t *testing.T) {
mem = NewMemory() mem = NewMemory()
caller = common.Address{} caller = common.Address{}
to = common.Address{1} to = common.Address{1}
contract = NewContract(caller, to, new(uint256.Int), 0, nil) contract = NewContract(caller, to, new(uint256.Int), GasBudget{}, nil)
scopeContext = ScopeContext{mem, stack, contract} scopeContext = ScopeContext{mem, stack, contract}
value = common.Hex2Bytes("abcdef00000000000000abba000000000deaf000000c0de00100000000133700") value = common.Hex2Bytes("abcdef00000000000000abba000000000deaf000000c0de00100000000133700")
) )

View file

@ -174,7 +174,7 @@ func (evm *EVM) Run(contract *Contract, input []byte, readOnly bool) (ret []byte
// associated costs. // associated costs.
contractAddr := contract.Address() contractAddr := contract.Address()
consumed, wanted := evm.TxContext.AccessEvents.CodeChunksRangeGas(contractAddr, pc, 1, uint64(len(contract.Code)), false, contract.Gas.RegularGas) consumed, wanted := evm.TxContext.AccessEvents.CodeChunksRangeGas(contractAddr, pc, 1, uint64(len(contract.Code)), false, contract.Gas.RegularGas)
contract.UseGas(consumed, evm.Config.Tracer, tracing.GasChangeWitnessCodeChunk) contract.UseGas(GasCosts{RegularGas: consumed}, evm.Config.Tracer, tracing.GasChangeWitnessCodeChunk)
if consumed < wanted { if consumed < wanted {
return nil, ErrOutOfGas return nil, ErrOutOfGas
} }

View file

@ -55,7 +55,7 @@ func TestLoopInterrupt(t *testing.T) {
timeout := make(chan bool) timeout := make(chan bool)
go func(evm *EVM) { go func(evm *EVM) {
_, _, err := evm.Call(common.Address{}, address, nil, math.MaxUint64, new(uint256.Int)) _, _, err := evm.Call(common.Address{}, address, nil, NewGasBudget(math.MaxUint64), new(uint256.Int))
errChannel <- err errChannel <- err
}(evm) }(evm)
@ -85,7 +85,7 @@ func BenchmarkInterpreter(b *testing.B) {
value = uint256.NewInt(0) value = uint256.NewInt(0)
stack = newstack() stack = newstack()
mem = NewMemory() mem = NewMemory()
contract = NewContract(common.Address{}, common.Address{}, value, startGas, nil) contract = NewContract(common.Address{}, common.Address{}, value, NewGasBudget(startGas), nil)
) )
stack.push(uint256.NewInt(123)) stack.push(uint256.NewInt(123))
stack.push(uint256.NewInt(123)) stack.push(uint256.NewInt(123))

View file

@ -168,7 +168,7 @@ func makeCallVariantGasCallEIP2929(oldCalculator gasFunc, addressPosition int) g
evm.StateDB.AddAddressToAccessList(addr) evm.StateDB.AddAddressToAccessList(addr)
// Charge the remaining difference here already, to correctly calculate available // Charge the remaining difference here already, to correctly calculate available
// gas for call // gas for call
if !contract.UseGas(coldCost, evm.Config.Tracer, tracing.GasChangeCallStorageColdAccess) { if !contract.UseGas(GasCosts{RegularGas: coldCost}, evm.Config.Tracer, tracing.GasChangeCallStorageColdAccess) {
return GasCosts{}, ErrOutOfGas return GasCosts{}, ErrOutOfGas
} }
} }
@ -295,7 +295,7 @@ func makeCallVariantGasCallEIP7702(intrinsicFunc gasFunc) gasFunc {
// Charge the remaining difference here already, to correctly calculate // Charge the remaining difference here already, to correctly calculate
// available gas for call // available gas for call
if !contract.UseGas(eip2929Cost, evm.Config.Tracer, tracing.GasChangeCallStorageColdAccess) { if !contract.UseGas(GasCosts{RegularGas: eip2929Cost}, evm.Config.Tracer, tracing.GasChangeCallStorageColdAccess) {
return GasCosts{}, ErrOutOfGas return GasCosts{}, ErrOutOfGas
} }
} }
@ -324,7 +324,7 @@ func makeCallVariantGasCallEIP7702(intrinsicFunc gasFunc) gasFunc {
evm.StateDB.AddAddressToAccessList(target) evm.StateDB.AddAddressToAccessList(target)
eip7702Cost = params.ColdAccountAccessCostEIP2929 eip7702Cost = params.ColdAccountAccessCostEIP2929
} }
if !contract.UseGas(eip7702Cost, evm.Config.Tracer, tracing.GasChangeCallStorageColdAccess) { if !contract.UseGas(GasCosts{RegularGas: eip7702Cost}, evm.Config.Tracer, tracing.GasChangeCallStorageColdAccess) {
return GasCosts{}, ErrOutOfGas return GasCosts{}, ErrOutOfGas
} }
} }

View file

@ -148,11 +148,11 @@ func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) {
cfg.Origin, cfg.Origin,
common.BytesToAddress([]byte("contract")), common.BytesToAddress([]byte("contract")),
input, input,
cfg.GasLimit, vm.NewGasBudget(cfg.GasLimit),
uint256.MustFromBig(cfg.Value), uint256.MustFromBig(cfg.Value),
) )
if cfg.EVMConfig.Tracer != nil && cfg.EVMConfig.Tracer.OnTxEnd != nil { if cfg.EVMConfig.Tracer != nil && cfg.EVMConfig.Tracer.OnTxEnd != nil {
cfg.EVMConfig.Tracer.OnTxEnd(&types.Receipt{GasUsed: cfg.GasLimit - leftOverGas}, err) cfg.EVMConfig.Tracer.OnTxEnd(&types.Receipt{GasUsed: cfg.GasLimit - leftOverGas.RegularGas}, err)
} }
return ret, cfg.State, err return ret, cfg.State, err
} }
@ -182,13 +182,13 @@ func Create(input []byte, cfg *Config) ([]byte, common.Address, uint64, error) {
code, address, leftOverGas, err := vmenv.Create( code, address, leftOverGas, err := vmenv.Create(
cfg.Origin, cfg.Origin,
input, input,
cfg.GasLimit, vm.NewGasBudget(cfg.GasLimit),
uint256.MustFromBig(cfg.Value), uint256.MustFromBig(cfg.Value),
) )
if cfg.EVMConfig.Tracer != nil && cfg.EVMConfig.Tracer.OnTxEnd != nil { if cfg.EVMConfig.Tracer != nil && cfg.EVMConfig.Tracer.OnTxEnd != nil {
cfg.EVMConfig.Tracer.OnTxEnd(&types.Receipt{GasUsed: cfg.GasLimit - leftOverGas}, err) cfg.EVMConfig.Tracer.OnTxEnd(&types.Receipt{GasUsed: cfg.GasLimit - leftOverGas.RegularGas}, err)
} }
return code, address, leftOverGas, err return code, address, leftOverGas.RegularGas, err
} }
// Call executes the code given by the contract's address. It will return the // Call executes the code given by the contract's address. It will return the
@ -217,11 +217,11 @@ func Call(address common.Address, input []byte, cfg *Config) ([]byte, uint64, er
cfg.Origin, cfg.Origin,
address, address,
input, input,
cfg.GasLimit, vm.NewGasBudget(cfg.GasLimit),
uint256.MustFromBig(cfg.Value), uint256.MustFromBig(cfg.Value),
) )
if cfg.EVMConfig.Tracer != nil && cfg.EVMConfig.Tracer.OnTxEnd != nil { if cfg.EVMConfig.Tracer != nil && cfg.EVMConfig.Tracer.OnTxEnd != nil {
cfg.EVMConfig.Tracer.OnTxEnd(&types.Receipt{GasUsed: cfg.GasLimit - leftOverGas}, err) cfg.EVMConfig.Tracer.OnTxEnd(&types.Receipt{GasUsed: cfg.GasLimit - leftOverGas.RegularGas}, err)
} }
return ret, leftOverGas, err return ret, leftOverGas.RegularGas, err
} }

View file

@ -55,7 +55,7 @@ func runTrace(tracer *tracers.Tracer, vmctx *vmContext, chaincfg *params.ChainCo
gasLimit uint64 = 31000 gasLimit uint64 = 31000
startGas uint64 = 10000 startGas uint64 = 10000
value = uint256.NewInt(0) value = uint256.NewInt(0)
contract = vm.NewContract(common.Address{}, common.Address{}, value, startGas, nil) contract = vm.NewContract(common.Address{}, common.Address{}, value, vm.NewGasBudget(startGas), nil)
) )
evm.SetTxContext(vmctx.txCtx) evm.SetTxContext(vmctx.txCtx)
contract.Code = []byte{byte(vm.PUSH1), 0x1, byte(vm.PUSH1), 0x1, 0x0} contract.Code = []byte{byte(vm.PUSH1), 0x1, byte(vm.PUSH1), 0x1, 0x0}
@ -183,7 +183,7 @@ func TestHaltBetweenSteps(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
scope := &vm.ScopeContext{ scope := &vm.ScopeContext{
Contract: vm.NewContract(common.Address{}, common.Address{}, uint256.NewInt(0), 0, nil), Contract: vm.NewContract(common.Address{}, common.Address{}, uint256.NewInt(0), vm.GasBudget{}, nil),
} }
evm := vm.NewEVM(vm.BlockContext{BlockNumber: big.NewInt(1)}, &dummyStatedb{}, chainConfig, vm.Config{Tracer: tracer.Hooks}) evm := vm.NewEVM(vm.BlockContext{BlockNumber: big.NewInt(1)}, &dummyStatedb{}, chainConfig, vm.Config{Tracer: tracer.Hooks})
evm.SetTxContext(vm.TxContext{GasPrice: uint256.NewInt(1)}) evm.SetTxContext(vm.TxContext{GasPrice: uint256.NewInt(1)})
@ -281,7 +281,7 @@ func TestEnterExit(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
scope := &vm.ScopeContext{ scope := &vm.ScopeContext{
Contract: vm.NewContract(common.Address{}, common.Address{}, uint256.NewInt(0), 0, nil), Contract: vm.NewContract(common.Address{}, common.Address{}, uint256.NewInt(0), vm.GasBudget{}, nil),
} }
tracer.OnEnter(1, byte(vm.CALL), scope.Contract.Caller(), scope.Contract.Address(), []byte{}, 1000, new(big.Int)) tracer.OnEnter(1, byte(vm.CALL), scope.Contract.Caller(), scope.Contract.Address(), []byte{}, 1000, new(big.Int))
tracer.OnExit(1, []byte{}, 400, nil, false) tracer.OnExit(1, []byte{}, 400, nil, false)

View file

@ -47,7 +47,7 @@ func TestStoreCapture(t *testing.T) {
var ( var (
logger = NewStructLogger(nil) logger = NewStructLogger(nil)
evm = vm.NewEVM(vm.BlockContext{}, &dummyStatedb{}, params.TestChainConfig, vm.Config{Tracer: logger.Hooks()}) evm = vm.NewEVM(vm.BlockContext{}, &dummyStatedb{}, params.TestChainConfig, vm.Config{Tracer: logger.Hooks()})
contract = vm.NewContract(common.Address{}, common.Address{}, new(uint256.Int), 100000, nil) contract = vm.NewContract(common.Address{}, common.Address{}, new(uint256.Int), vm.NewGasBudget(100000), nil)
) )
contract.Code = []byte{byte(vm.PUSH1), 0x1, byte(vm.PUSH1), 0x0, byte(vm.SSTORE)} contract.Code = []byte{byte(vm.PUSH1), 0x1, byte(vm.PUSH1), 0x0, byte(vm.SSTORE)}
var index common.Hash var index common.Hash

View file

@ -312,7 +312,7 @@ func runBenchmark(b *testing.B, t *StateTest) {
evm.SetTxContext(txContext) evm.SetTxContext(txContext)
// Create "contract" for sender to cache code analysis. // Create "contract" for sender to cache code analysis.
sender := vm.NewContract(msg.From, msg.From, nil, 0, nil) sender := vm.NewContract(msg.From, msg.From, nil, vm.GasBudget{}, nil)
var ( var (
gasUsed uint64 gasUsed uint64
@ -326,8 +326,10 @@ func runBenchmark(b *testing.B, t *StateTest) {
b.StartTimer() b.StartTimer()
start := time.Now() start := time.Now()
initialGas := vm.NewGasBudget(msg.GasLimit)
// Execute the message. // Execute the message.
_, leftOverGas, err := evm.Call(sender.Address(), *msg.To, msg.Data, msg.GasLimit, uint256.MustFromBig(msg.Value)) _, leftOverGas, err := evm.Call(sender.Address(), *msg.To, msg.Data, initialGas.Copy(), uint256.MustFromBig(msg.Value))
if err != nil { if err != nil {
b.Error(err) b.Error(err)
return return
@ -336,7 +338,7 @@ func runBenchmark(b *testing.B, t *StateTest) {
b.StopTimer() b.StopTimer()
elapsed += uint64(time.Since(start)) elapsed += uint64(time.Since(start))
refund += state.StateDB.GetRefund() refund += state.StateDB.GetRefund()
gasUsed += msg.GasLimit - leftOverGas gasUsed += leftOverGas.Used(initialGas)
state.StateDB.RevertToSnapshot(snapshot) state.StateDB.RevertToSnapshot(snapshot)
} }

View file

@ -81,10 +81,11 @@ func (tt *TransactionTest) Run() error {
return return
} }
// Intrinsic gas // Intrinsic gas
requiredGas, err = core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.SetCodeAuthorizations(), tx.To() == nil, rules.IsHomestead, rules.IsIstanbul, rules.IsShanghai) cost, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.SetCodeAuthorizations(), tx.To() == nil, rules.IsHomestead, rules.IsIstanbul, rules.IsShanghai)
if err != nil { if err != nil {
return return
} }
requiredGas = cost.RegularGas
if requiredGas > tx.Gas() { if requiredGas > tx.Gas() {
return sender, hash, 0, fmt.Errorf("insufficient gas ( %d < %d )", tx.Gas(), requiredGas) return sender, hash, 0, fmt.Errorf("insufficient gas ( %d < %d )", tx.Gas(), requiredGas)
} }