mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-06-08 07:58:40 +00:00
core/vm, eth, tests: introduce gas budget (#34712)
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:
parent
5af5510b1e
commit
29e0a6f404
25 changed files with 235 additions and 168 deletions
|
|
@ -133,15 +133,15 @@ func Transaction(ctx *cli.Context) error {
|
|||
}
|
||||
// Check intrinsic gas
|
||||
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 {
|
||||
r.Error = err
|
||||
results = append(results, r)
|
||||
continue
|
||||
}
|
||||
r.IntrinsicGas = gas
|
||||
if tx.Gas() < gas {
|
||||
r.Error = fmt.Errorf("%w: have %d, want %d", core.ErrIntrinsicGas, tx.Gas(), gas)
|
||||
r.IntrinsicGas = cost.RegularGas
|
||||
if tx.Gas() < cost.RegularGas {
|
||||
r.Error = fmt.Errorf("%w: have %d, want %d", core.ErrIntrinsicGas, tx.Gas(), cost.RegularGas)
|
||||
results = append(results, r)
|
||||
continue
|
||||
}
|
||||
|
|
|
|||
|
|
@ -89,7 +89,7 @@ func genValueTx(nbytes int) func(int, *BlockGen) {
|
|||
data := make([]byte, nbytes)
|
||||
return func(i int, gen *BlockGen) {
|
||||
toaddr := common.Address{}
|
||||
gas, _ := IntrinsicGas(data, nil, nil, false, false, false, false)
|
||||
cost, _ := IntrinsicGas(data, nil, nil, false, false, false, false)
|
||||
signer := gen.Signer()
|
||||
gasPrice := big.NewInt(0)
|
||||
if gen.header.BaseFee != nil {
|
||||
|
|
@ -99,7 +99,7 @@ func genValueTx(nbytes int) func(int, *BlockGen) {
|
|||
Nonce: gen.TxNonce(benchRootAddr),
|
||||
To: &toaddr,
|
||||
Value: big.NewInt(1),
|
||||
Gas: gas,
|
||||
Gas: cost.RegularGas,
|
||||
Data: data,
|
||||
GasPrice: gasPrice,
|
||||
})
|
||||
|
|
|
|||
|
|
@ -97,11 +97,11 @@ func TestProcessUBT(t *testing.T) {
|
|||
|
||||
txCost1 := params.TxGas
|
||||
txCost2 := params.TxGas
|
||||
contractCreationCost := intrinsicContractCreationGas +
|
||||
contractCreationCost := intrinsicContractCreationGas.RegularGas +
|
||||
params.WitnessChunkReadCost + params.WitnessChunkWriteCost + params.WitnessBranchReadCost + params.WitnessBranchWriteCost + /* creation */
|
||||
params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* creation with value */
|
||||
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 (CREATE at pc=0x20) */
|
||||
params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* write code hash */
|
||||
|
|
|
|||
|
|
@ -259,7 +259,7 @@ func ProcessBeaconBlockRoot(beaconRoot common.Hash, evm *vm.EVM) {
|
|||
}
|
||||
evm.SetTxContext(NewEVMTxContext(msg))
|
||||
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 {
|
||||
evm.StateDB.AccessEvents().Merge(evm.AccessEvents)
|
||||
}
|
||||
|
|
@ -286,7 +286,7 @@ func ProcessParentBlockHash(prevHash common.Hash, evm *vm.EVM) {
|
|||
}
|
||||
evm.SetTxContext(NewEVMTxContext(msg))
|
||||
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 {
|
||||
panic(err)
|
||||
}
|
||||
|
|
@ -325,7 +325,7 @@ func processRequestsSystemCall(requests *[][]byte, evm *vm.EVM, requestType byte
|
|||
}
|
||||
evm.SetTxContext(NewEVMTxContext(msg))
|
||||
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 {
|
||||
evm.StateDB.AccessEvents().Merge(evm.AccessEvents)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ func (result *ExecutionResult) Revert() []byte {
|
|||
}
|
||||
|
||||
// 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
|
||||
var gas uint64
|
||||
if isContractCreation && isHomestead {
|
||||
|
|
@ -89,19 +89,19 @@ func IntrinsicGas(data []byte, accessList types.AccessList, authList []types.Set
|
|||
nonZeroGas = params.TxDataNonZeroGasEIP2028
|
||||
}
|
||||
if (math.MaxUint64-gas)/nonZeroGas < nz {
|
||||
return 0, ErrGasUintOverflow
|
||||
return vm.GasCosts{}, ErrGasUintOverflow
|
||||
}
|
||||
gas += nz * nonZeroGas
|
||||
|
||||
if (math.MaxUint64-gas)/params.TxDataZeroGas < z {
|
||||
return 0, ErrGasUintOverflow
|
||||
return vm.GasCosts{}, ErrGasUintOverflow
|
||||
}
|
||||
gas += z * params.TxDataZeroGas
|
||||
|
||||
if isContractCreation && isEIP3860 {
|
||||
lenWords := toWordSize(dataLen)
|
||||
if (math.MaxUint64-gas)/params.InitCodeWordGas < lenWords {
|
||||
return 0, ErrGasUintOverflow
|
||||
return vm.GasCosts{}, ErrGasUintOverflow
|
||||
}
|
||||
gas += lenWords * params.InitCodeWordGas
|
||||
}
|
||||
|
|
@ -113,7 +113,7 @@ func IntrinsicGas(data []byte, accessList types.AccessList, authList []types.Set
|
|||
if authList != nil {
|
||||
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).
|
||||
|
|
@ -242,12 +242,12 @@ func ApplyMessage(evm *vm.EVM, msg *Message, gp *GasPool) (*ExecutionResult, err
|
|||
// 5. Run Script section
|
||||
// 6. Derive new state root
|
||||
type stateTransition struct {
|
||||
gp *GasPool
|
||||
msg *Message
|
||||
gasRemaining uint64
|
||||
initialGas uint64
|
||||
state vm.StateDB
|
||||
evm *vm.EVM
|
||||
gp *GasPool
|
||||
msg *Message
|
||||
initialBudget vm.GasBudget
|
||||
gasRemaining vm.GasBudget
|
||||
state vm.StateDB
|
||||
evm *vm.EVM
|
||||
}
|
||||
|
||||
// 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 {
|
||||
st.evm.Config.Tracer.OnGasChange(0, st.msg.GasLimit, tracing.GasChangeTxInitialBalance)
|
||||
}
|
||||
st.gasRemaining = st.msg.GasLimit
|
||||
st.initialGas = st.msg.GasLimit
|
||||
st.gasRemaining = vm.NewGasBudget(st.msg.GasLimit)
|
||||
st.initialBudget = st.gasRemaining.Copy()
|
||||
|
||||
mgvalU256, _ := uint256.FromBig(mgval)
|
||||
st.state.SubBalance(st.msg.From, mgvalU256, tracing.BalanceDecreaseGasBuy)
|
||||
|
|
@ -446,14 +446,17 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
|
|||
contractCreation = msg.To == nil
|
||||
floorDataGas uint64
|
||||
)
|
||||
|
||||
// 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 {
|
||||
return nil, err
|
||||
}
|
||||
if st.gasRemaining < gas {
|
||||
return nil, fmt.Errorf("%w: have %d, want %d", ErrIntrinsicGas, st.gasRemaining, gas)
|
||||
prior, sufficient := st.gasRemaining.Charge(cost)
|
||||
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)
|
||||
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)
|
||||
}
|
||||
}
|
||||
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 {
|
||||
st.evm.AccessEvents.AddTxOrigin(msg.From)
|
||||
|
|
@ -535,14 +534,14 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
|
|||
peakGasUsed := st.gasUsed()
|
||||
|
||||
// Compute refund counter, capped to a refund quotient.
|
||||
st.gasRemaining += st.calcRefund()
|
||||
st.gasRemaining.Refund(st.calcRefund())
|
||||
|
||||
if rules.IsPrague {
|
||||
// After EIP-7623: Data-heavy transactions pay the floor gas.
|
||||
if st.gasUsed() < floorDataGas {
|
||||
prev := st.gasRemaining
|
||||
st.gasRemaining = st.initialGas - floorDataGas
|
||||
if used := st.gasUsed(); used < floorDataGas {
|
||||
prior, _ := st.gasRemaining.Charge(vm.GasCosts{RegularGas: floorDataGas - used})
|
||||
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 {
|
||||
|
|
@ -555,10 +554,10 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
|
|||
// Return gas to the gas pool
|
||||
if rules.IsAmsterdam {
|
||||
// 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 {
|
||||
// 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 {
|
||||
return nil, err
|
||||
|
|
@ -655,7 +654,7 @@ func (st *stateTransition) applyAuthorization(auth *types.SetCodeAuthorization)
|
|||
}
|
||||
|
||||
// calcRefund computes refund counter, capped to a refund quotient.
|
||||
func (st *stateTransition) calcRefund() uint64 {
|
||||
func (st *stateTransition) calcRefund() vm.GasBudget {
|
||||
var refund uint64
|
||||
if !st.evm.ChainConfig().IsLondon(st.evm.Context.BlockNumber) {
|
||||
// Before EIP-3529: refunds were capped to gasUsed / 2
|
||||
|
|
@ -668,26 +667,26 @@ func (st *stateTransition) calcRefund() uint64 {
|
|||
refund = st.state.GetRefund()
|
||||
}
|
||||
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,
|
||||
// exchanged at the original rate.
|
||||
func (st *stateTransition) returnGas() {
|
||||
remaining := uint256.NewInt(st.gasRemaining)
|
||||
remaining := uint256.NewInt(st.gasRemaining.RegularGas)
|
||||
remaining.Mul(remaining, uint256.MustFromBig(st.msg.GasPrice))
|
||||
st.state.AddBalance(st.msg.From, remaining, tracing.BalanceIncreaseGasReturn)
|
||||
|
||||
if st.evm.Config.Tracer != nil && st.evm.Config.Tracer.OnGasChange != nil && st.gasRemaining > 0 {
|
||||
st.evm.Config.Tracer.OnGasChange(st.gasRemaining, 0, tracing.GasChangeTxLeftOverReturned)
|
||||
if st.evm.Config.Tracer != nil && st.evm.Config.Tracer.OnGasChange != nil && st.gasRemaining.RegularGas > 0 {
|
||||
st.evm.Config.Tracer.OnGasChange(st.gasRemaining.RegularGas, 0, tracing.GasChangeTxLeftOverReturned)
|
||||
}
|
||||
}
|
||||
|
||||
// gasUsed returns the amount of gas used up by the state transition.
|
||||
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.
|
||||
|
|
|
|||
|
|
@ -167,6 +167,8 @@ type (
|
|||
// GasChangeHook is invoked when the gas changes.
|
||||
GasChangeHook = func(old, new uint64, reason GasChangeReason)
|
||||
|
||||
// TODO(sina, rjl), please add GasChangeV2Hook by landing the multi-dimensional gas
|
||||
|
||||
/*
|
||||
- Chain events -
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -129,8 +129,8 @@ func ValidateTransaction(tx *types.Transaction, head *types.Header, signer types
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if tx.Gas() < intrGas {
|
||||
return fmt.Errorf("%w: gas %v, minimum needed %v", core.ErrIntrinsicGas, tx.Gas(), intrGas)
|
||||
if tx.Gas() < intrGas.RegularGas {
|
||||
return fmt.Errorf("%w: gas %v, minimum needed %v", core.ErrIntrinsicGas, tx.Gas(), intrGas.RegularGas)
|
||||
}
|
||||
// Ensure the transaction can cover floor data gas.
|
||||
if rules.IsPrague {
|
||||
|
|
|
|||
|
|
@ -42,12 +42,12 @@ type Contract struct {
|
|||
IsDeployment bool
|
||||
IsSystemCall bool
|
||||
|
||||
Gas GasCosts
|
||||
Gas GasBudget
|
||||
value *uint256.Int
|
||||
}
|
||||
|
||||
// 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
|
||||
if jumpDests == nil {
|
||||
jumpDests = newMapJumpDests()
|
||||
|
|
@ -56,7 +56,7 @@ func NewContract(caller common.Address, address common.Address, value *uint256.I
|
|||
caller: caller,
|
||||
address: address,
|
||||
jumpDests: jumpDests,
|
||||
Gas: GasCosts{RegularGas: gas},
|
||||
Gas: gas,
|
||||
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
|
||||
func (c *Contract) UseGas(gas uint64, logger *tracing.Hooks, reason tracing.GasChangeReason) (ok bool) {
|
||||
if c.Gas.RegularGas < gas {
|
||||
func (c *Contract) UseGas(cost GasCosts, logger *tracing.Hooks, reason tracing.GasChangeReason) (ok bool) {
|
||||
prior, ok := c.Gas.Charge(cost)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
// RefundGas refunds gas to the contract
|
||||
func (c *Contract) RefundGas(gas uint64, logger *tracing.Hooks, reason tracing.GasChangeReason) {
|
||||
if gas == 0 {
|
||||
// RefundGas refunds the leftover gas budget back to the contract.
|
||||
func (c *Contract) RefundGas(refund GasBudget, logger *tracing.Hooks, reason tracing.GasChangeReason) {
|
||||
prior, changed := c.Gas.Refund(refund)
|
||||
if !changed {
|
||||
return
|
||||
}
|
||||
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
|
||||
|
|
|
|||
|
|
@ -260,25 +260,25 @@ func ActivePrecompiles(rules params.Rules) []common.Address {
|
|||
// RunPrecompiledContract runs and evaluates the output of a precompiled contract.
|
||||
// It returns
|
||||
// - the returned bytes,
|
||||
// - the _remaining_ gas,
|
||||
// - the remaining gas budget,
|
||||
// - 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)
|
||||
if suppliedGas < gasCost {
|
||||
return nil, 0, ErrOutOfGas
|
||||
prior, ok := gas.Charge(GasCosts{RegularGas: gasCost})
|
||||
if !ok {
|
||||
gas.Exhaust()
|
||||
return nil, gas, ErrOutOfGas
|
||||
}
|
||||
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
|
||||
// fork is activated.
|
||||
if stateDB != nil {
|
||||
stateDB.Exist(address)
|
||||
}
|
||||
output, err := p.Run(input)
|
||||
return output, suppliedGas, err
|
||||
return output, gas, err
|
||||
}
|
||||
|
||||
// ecrecover implemented as a native contract.
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ func FuzzPrecompiledContracts(f *testing.F) {
|
|||
return
|
||||
}
|
||||
inWant := string(input)
|
||||
RunPrecompiledContract(nil, p, a, input, gas, nil)
|
||||
RunPrecompiledContract(nil, p, a, input, NewGasBudget(gas), nil)
|
||||
if inHave := string(input); inWant != inHave {
|
||||
t.Errorf("Precompiled %v modified input data", a)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -99,7 +99,7 @@ func testPrecompiled(addr string, test precompiledTest, t *testing.T) {
|
|||
in := common.Hex2Bytes(test.Input)
|
||||
gas := p.RequiredGas(in)
|
||||
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)
|
||||
} else if common.Bytes2Hex(res) != test.Expected {
|
||||
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
|
||||
|
||||
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" {
|
||||
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)
|
||||
gas := p.RequiredGas(in)
|
||||
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 {
|
||||
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()
|
||||
for bench.Loop() {
|
||||
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))
|
||||
if elapsed < 1 {
|
||||
|
|
|
|||
|
|
@ -382,7 +382,7 @@ func opExtCodeCopyEIP4762(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, er
|
|||
code := evm.StateDB.GetCode(addr)
|
||||
paddedCodeCopy, copyOffset, nonPaddedCopyLength := getDataAndAdjustedBounds(code, uint64CodeOffset, length.Uint64())
|
||||
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 {
|
||||
return nil, ErrOutOfGas
|
||||
}
|
||||
|
|
@ -408,7 +408,7 @@ func opPush1EIP4762(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
|||
// advanced past this boundary.
|
||||
contractAddr := scope.Contract.Address()
|
||||
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 {
|
||||
return nil, ErrOutOfGas
|
||||
}
|
||||
|
|
@ -436,7 +436,7 @@ func makePushEIP4762(size uint64, pushByteSize int) executionFunc {
|
|||
if !scope.Contract.IsDeployment && !scope.Contract.IsSystemCall {
|
||||
contractAddr := scope.Contract.Address()
|
||||
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 {
|
||||
return nil, ErrOutOfGas
|
||||
}
|
||||
|
|
|
|||
110
core/vm/evm.go
110
core/vm/evm.go
|
|
@ -236,13 +236,13 @@ func isSystemCall(caller common.Address) bool {
|
|||
// 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
|
||||
// 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
|
||||
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) {
|
||||
evm.captureEnd(evm.depth, startGas, leftOverGas, ret, err)
|
||||
}(gas)
|
||||
evm.captureEnd(evm.depth, startGas, leftOverGas.RegularGas, ret, err)
|
||||
}(gas.RegularGas)
|
||||
}
|
||||
// Fail if we're trying to execute above the call depth limit
|
||||
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
|
||||
// hash leaf to the access list, then account creation will proceed unimpaired.
|
||||
// Thus, only pay for the creation of the code hash leaf here.
|
||||
wgas := evm.AccessEvents.CodeHashGas(addr, true, gas, false)
|
||||
if gas < wgas {
|
||||
wgas := evm.AccessEvents.CodeHashGas(addr, true, gas.RegularGas, false)
|
||||
if _, ok := gas.Charge(GasCosts{RegularGas: wgas}); !ok {
|
||||
evm.StateDB.RevertToSnapshot(snapshot)
|
||||
return nil, 0, ErrOutOfGas
|
||||
gas.Exhaust()
|
||||
return nil, gas, ErrOutOfGas
|
||||
}
|
||||
gas -= wgas
|
||||
}
|
||||
|
||||
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.SetCallCode(evm.resolveCodeHash(addr), code)
|
||||
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
|
||||
|
|
@ -313,9 +313,9 @@ func (evm *EVM) Call(caller common.Address, addr common.Address, input []byte, g
|
|||
evm.StateDB.RevertToSnapshot(snapshot)
|
||||
if err != ErrExecutionReverted {
|
||||
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:
|
||||
//} 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'
|
||||
// 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
|
||||
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) {
|
||||
evm.captureEnd(evm.depth, startGas, leftOverGas, ret, err)
|
||||
}(gas)
|
||||
evm.captureEnd(evm.depth, startGas, leftOverGas.RegularGas, ret, err)
|
||||
}(gas.RegularGas)
|
||||
}
|
||||
// Fail if we're trying to execute above the call depth limit
|
||||
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.SetCallCode(evm.resolveCodeHash(addr), evm.resolveCode(addr))
|
||||
ret, err = evm.Run(contract, input, false)
|
||||
gas = contract.Gas.RegularGas
|
||||
gas = contract.Gas
|
||||
}
|
||||
if err != nil {
|
||||
evm.StateDB.RevertToSnapshot(snapshot)
|
||||
if err != ErrExecutionReverted {
|
||||
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
|
||||
|
|
@ -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'
|
||||
// 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
|
||||
if evm.Config.Tracer != nil {
|
||||
// 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) {
|
||||
evm.captureEnd(evm.depth, startGas, leftOverGas, ret, err)
|
||||
}(gas)
|
||||
evm.captureEnd(evm.depth, startGas, leftOverGas.RegularGas, ret, err)
|
||||
}(gas.RegularGas)
|
||||
}
|
||||
// Fail if we're trying to execute above the call depth limit
|
||||
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.SetCallCode(evm.resolveCodeHash(addr), evm.resolveCode(addr))
|
||||
ret, err = evm.Run(contract, input, false)
|
||||
gas = contract.Gas.RegularGas
|
||||
gas = contract.Gas
|
||||
}
|
||||
if err != nil {
|
||||
evm.StateDB.RevertToSnapshot(snapshot)
|
||||
if err != ErrExecutionReverted {
|
||||
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
|
||||
|
|
@ -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.
|
||||
// Opcodes that attempt to perform such modifications will result in exceptions
|
||||
// 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
|
||||
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) {
|
||||
evm.captureEnd(evm.depth, startGas, leftOverGas, ret, err)
|
||||
}(gas)
|
||||
evm.captureEnd(evm.depth, startGas, leftOverGas.RegularGas, ret, err)
|
||||
}(gas.RegularGas)
|
||||
}
|
||||
// Fail if we're trying to execute above the call depth limit
|
||||
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
|
||||
// when we're in Homestead this also counts for code storage gas errors.
|
||||
ret, err = evm.Run(contract, input, true)
|
||||
gas = contract.Gas.RegularGas
|
||||
gas = contract.Gas
|
||||
}
|
||||
if err != nil {
|
||||
evm.StateDB.RevertToSnapshot(snapshot)
|
||||
if err != ErrExecutionReverted {
|
||||
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
|
||||
}
|
||||
|
||||
// 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 {
|
||||
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) {
|
||||
evm.captureEnd(evm.depth, startGas, leftOverGas, ret, err)
|
||||
}(gas)
|
||||
evm.captureEnd(evm.depth, startGas, leftOverGas.RegularGas, ret, err)
|
||||
}(gas.RegularGas)
|
||||
}
|
||||
// Depth check execution. Fail if we're trying to execute above the
|
||||
// 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
|
||||
if evm.chainRules.IsEIP4762 {
|
||||
statelessGas := evm.AccessEvents.ContractCreatePreCheckGas(address, gas)
|
||||
if statelessGas > gas {
|
||||
return nil, common.Address{}, 0, ErrOutOfGas
|
||||
statelessGas := evm.AccessEvents.ContractCreatePreCheckGas(address, gas.RegularGas)
|
||||
prior, ok := gas.Charge(GasCosts{RegularGas: statelessGas})
|
||||
if !ok {
|
||||
gas.Exhaust()
|
||||
return nil, common.Address{}, gas, ErrOutOfGas
|
||||
}
|
||||
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
|
||||
|
|
@ -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
|
||||
isEIP7610RejectedAccount(evm.ChainConfig().ChainID, address, evm.chainRules.IsEIP158) {
|
||||
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.
|
||||
// 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
|
||||
if evm.chainRules.IsEIP4762 {
|
||||
consumed, wanted := evm.AccessEvents.ContractCreateInitGas(address, gas)
|
||||
consumed, wanted := evm.AccessEvents.ContractCreateInitGas(address, gas.RegularGas)
|
||||
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 {
|
||||
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)
|
||||
|
||||
|
|
@ -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) {
|
||||
evm.StateDB.RevertToSnapshot(snapshot)
|
||||
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
|
||||
|
|
@ -608,12 +610,12 @@ func (evm *EVM) initNewContract(contract *Contract, address common.Address) ([]b
|
|||
|
||||
if !evm.chainRules.IsEIP4762 {
|
||||
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
|
||||
}
|
||||
} else {
|
||||
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) {
|
||||
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.
|
||||
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))
|
||||
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:]
|
||||
// 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)
|
||||
contractAddr = crypto.CreateAddress2(caller, salt.Bytes32(), inithash[:])
|
||||
return evm.create(caller, code, gas, endowment, contractAddr, CREATE2)
|
||||
|
|
|
|||
|
|
@ -97,12 +97,12 @@ func TestEIP2200(t *testing.T) {
|
|||
Transfer: func(StateDB, common.Address, common.Address, *uint256.Int, *params.Rules) {},
|
||||
}
|
||||
evm := NewEVM(vmctx, statedb, params.AllEthashProtocolChanges, Config{ExtraEips: []int{2200}})
|
||||
|
||||
_, gas, err := evm.Call(common.Address{}, address, nil, tt.gaspool, new(uint256.Int))
|
||||
initialGas := NewGasBudget(tt.gaspool)
|
||||
_, leftOver, err := evm.Call(common.Address{}, address, nil, initialGas.Copy(), new(uint256.Int))
|
||||
if !errors.Is(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)
|
||||
}
|
||||
if refund := evm.StateDB.GetRefund(); refund != tt.refund {
|
||||
|
|
@ -157,12 +157,12 @@ func TestCreateGas(t *testing.T) {
|
|||
}
|
||||
|
||||
evm := NewEVM(vmctx, statedb, chainConfig, config)
|
||||
var startGas = uint64(testGas)
|
||||
ret, gas, err := evm.Call(common.Address{}, address, nil, startGas, new(uint256.Int))
|
||||
initialGas := NewGasBudget(uint64(testGas))
|
||||
ret, leftOver, err := evm.Call(common.Address{}, address, nil, initialGas.Copy(), new(uint256.Int))
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
gasUsed = startGas - gas
|
||||
gasUsed = leftOver.Used(initialGas)
|
||||
if len(ret) != 32 {
|
||||
t.Fatalf("test %d: expected 32 bytes returned, have %d", i, len(ret))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,8 @@ package vm
|
|||
import "fmt"
|
||||
|
||||
// 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 {
|
||||
RegularGas uint64
|
||||
StateGas uint64
|
||||
|
|
@ -34,3 +35,63 @@ func (g GasCosts) Sum() uint64 {
|
|||
func (g GasCosts) String() string {
|
||||
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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -667,9 +667,9 @@ func opCreate(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
|||
// reuse size int for stackvalue
|
||||
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
|
||||
// homestead we must check for CodeStoreOutOfGasError (homestead only
|
||||
// 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
|
||||
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
|
||||
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)
|
||||
// Push item on the stack based on the returned error.
|
||||
if suberr != nil {
|
||||
|
|
@ -747,7 +747,7 @@ func opCall(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
|||
if !value.IsZero() {
|
||||
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 {
|
||||
temp.Clear()
|
||||
|
|
@ -781,7 +781,7 @@ func opCallCode(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
|||
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 {
|
||||
temp.Clear()
|
||||
} else {
|
||||
|
|
@ -810,7 +810,7 @@ func opDelegateCall(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
|||
// Get arguments from the memory.
|
||||
args := scope.Memory.GetPtr(inOffset.Uint64(), inSize.Uint64())
|
||||
|
||||
ret, returnGas, err := evm.DelegateCall(scope.Contract.Caller(), scope.Contract.Address(), toAddr, args, 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 {
|
||||
temp.Clear()
|
||||
} else {
|
||||
|
|
@ -839,7 +839,7 @@ func opStaticCall(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
|||
// Get arguments from the memory.
|
||||
args := scope.Memory.GetPtr(inOffset.Uint64(), inSize.Uint64())
|
||||
|
||||
ret, returnGas, err := evm.StaticCall(scope.Contract.Address(), toAddr, args, gas)
|
||||
ret, returnGas, err := evm.StaticCall(scope.Contract.Address(), toAddr, args, NewGasBudget(gas))
|
||||
if err != nil {
|
||||
temp.Clear()
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -565,7 +565,7 @@ func TestOpTstore(t *testing.T) {
|
|||
mem = NewMemory()
|
||||
caller = common.Address{}
|
||||
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}
|
||||
value = common.Hex2Bytes("abcdef00000000000000abba000000000deaf000000c0de00100000000133700")
|
||||
)
|
||||
|
|
|
|||
|
|
@ -174,7 +174,7 @@ func (evm *EVM) Run(contract *Contract, input []byte, readOnly bool) (ret []byte
|
|||
// associated costs.
|
||||
contractAddr := contract.Address()
|
||||
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 {
|
||||
return nil, ErrOutOfGas
|
||||
}
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ func TestLoopInterrupt(t *testing.T) {
|
|||
timeout := make(chan bool)
|
||||
|
||||
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
|
||||
}(evm)
|
||||
|
||||
|
|
@ -85,7 +85,7 @@ func BenchmarkInterpreter(b *testing.B) {
|
|||
value = uint256.NewInt(0)
|
||||
stack = newstack()
|
||||
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))
|
||||
|
|
|
|||
|
|
@ -168,7 +168,7 @@ func makeCallVariantGasCallEIP2929(oldCalculator gasFunc, addressPosition int) g
|
|||
evm.StateDB.AddAddressToAccessList(addr)
|
||||
// Charge the remaining difference here already, to correctly calculate available
|
||||
// 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
|
||||
}
|
||||
}
|
||||
|
|
@ -295,7 +295,7 @@ func makeCallVariantGasCallEIP7702(intrinsicFunc gasFunc) gasFunc {
|
|||
|
||||
// Charge the remaining difference here already, to correctly calculate
|
||||
// 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
|
||||
}
|
||||
}
|
||||
|
|
@ -324,7 +324,7 @@ func makeCallVariantGasCallEIP7702(intrinsicFunc gasFunc) gasFunc {
|
|||
evm.StateDB.AddAddressToAccessList(target)
|
||||
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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -148,11 +148,11 @@ func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) {
|
|||
cfg.Origin,
|
||||
common.BytesToAddress([]byte("contract")),
|
||||
input,
|
||||
cfg.GasLimit,
|
||||
vm.NewGasBudget(cfg.GasLimit),
|
||||
uint256.MustFromBig(cfg.Value),
|
||||
)
|
||||
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
|
||||
}
|
||||
|
|
@ -182,13 +182,13 @@ func Create(input []byte, cfg *Config) ([]byte, common.Address, uint64, error) {
|
|||
code, address, leftOverGas, err := vmenv.Create(
|
||||
cfg.Origin,
|
||||
input,
|
||||
cfg.GasLimit,
|
||||
vm.NewGasBudget(cfg.GasLimit),
|
||||
uint256.MustFromBig(cfg.Value),
|
||||
)
|
||||
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
|
||||
|
|
@ -217,11 +217,11 @@ func Call(address common.Address, input []byte, cfg *Config) ([]byte, uint64, er
|
|||
cfg.Origin,
|
||||
address,
|
||||
input,
|
||||
cfg.GasLimit,
|
||||
vm.NewGasBudget(cfg.GasLimit),
|
||||
uint256.MustFromBig(cfg.Value),
|
||||
)
|
||||
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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ func runTrace(tracer *tracers.Tracer, vmctx *vmContext, chaincfg *params.ChainCo
|
|||
gasLimit uint64 = 31000
|
||||
startGas uint64 = 10000
|
||||
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)
|
||||
contract.Code = []byte{byte(vm.PUSH1), 0x1, byte(vm.PUSH1), 0x1, 0x0}
|
||||
|
|
@ -183,7 +183,7 @@ func TestHaltBetweenSteps(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
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.SetTxContext(vm.TxContext{GasPrice: uint256.NewInt(1)})
|
||||
|
|
@ -281,7 +281,7 @@ func TestEnterExit(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
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.OnExit(1, []byte{}, 400, nil, false)
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ func TestStoreCapture(t *testing.T) {
|
|||
var (
|
||||
logger = NewStructLogger(nil)
|
||||
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)}
|
||||
var index common.Hash
|
||||
|
|
|
|||
|
|
@ -312,7 +312,7 @@ func runBenchmark(b *testing.B, t *StateTest) {
|
|||
evm.SetTxContext(txContext)
|
||||
|
||||
// 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 (
|
||||
gasUsed uint64
|
||||
|
|
@ -326,8 +326,10 @@ func runBenchmark(b *testing.B, t *StateTest) {
|
|||
b.StartTimer()
|
||||
start := time.Now()
|
||||
|
||||
initialGas := vm.NewGasBudget(msg.GasLimit)
|
||||
|
||||
// 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 {
|
||||
b.Error(err)
|
||||
return
|
||||
|
|
@ -336,7 +338,7 @@ func runBenchmark(b *testing.B, t *StateTest) {
|
|||
b.StopTimer()
|
||||
elapsed += uint64(time.Since(start))
|
||||
refund += state.StateDB.GetRefund()
|
||||
gasUsed += msg.GasLimit - leftOverGas
|
||||
gasUsed += leftOverGas.Used(initialGas)
|
||||
|
||||
state.StateDB.RevertToSnapshot(snapshot)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -81,10 +81,11 @@ func (tt *TransactionTest) Run() error {
|
|||
return
|
||||
}
|
||||
// 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 {
|
||||
return
|
||||
}
|
||||
requiredGas = cost.RegularGas
|
||||
if requiredGas > tx.Gas() {
|
||||
return sender, hash, 0, fmt.Errorf("insufficient gas ( %d < %d )", tx.Gas(), requiredGas)
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue