mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-04 04:01:13 +00:00
core/vm: pre-charge account creation in Create family unconditionally
This commit is contained in:
parent
7d74166d3d
commit
0b3dffcf00
11 changed files with 259 additions and 44 deletions
|
|
@ -686,12 +686,13 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// Execute the transaction's creation.
|
// Execute the transaction's creation.
|
||||||
ret, _, result, vmerr = st.evm.Create(msg.From, msg.Data, st.gasRemaining.ForwardAll(), value)
|
var creation bool
|
||||||
|
ret, _, result, creation, vmerr = st.evm.Create(msg.From, msg.Data, st.gasRemaining.ForwardAll(), value)
|
||||||
st.gasRemaining.Absorb(result, forwarded)
|
st.gasRemaining.Absorb(result, forwarded)
|
||||||
|
|
||||||
// If the contract creation failed, refund the account-creation state
|
// If the contract creation failed, or the destination was pre-existing,
|
||||||
// gas pre-charged in IntrinsicGas.
|
// refund the account-creation state gas pre-charged in IntrinsicGas.
|
||||||
if rules.IsAmsterdam && vmerr != nil {
|
if rules.IsAmsterdam && !creation {
|
||||||
st.gasRemaining.RefundState(params.AccountCreationSize * st.evm.Context.CostPerStateByte)
|
st.gasRemaining.RefundState(params.AccountCreationSize * st.evm.Context.CostPerStateByte)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -28,17 +28,17 @@ func _() {
|
||||||
_ = x[GasChangeWitnessCodeChunk-17]
|
_ = x[GasChangeWitnessCodeChunk-17]
|
||||||
_ = x[GasChangeWitnessContractCollisionCheck-18]
|
_ = x[GasChangeWitnessContractCollisionCheck-18]
|
||||||
_ = x[GasChangeTxDataFloor-19]
|
_ = x[GasChangeTxDataFloor-19]
|
||||||
_ = x[GasChangeAccountCreation-20]
|
_ = x[GasChangeRefundAccountCreation-20]
|
||||||
_ = x[GasChangeIgnored-255]
|
_ = x[GasChangeIgnored-255]
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
_GasChangeReason_name_0 = "UnspecifiedTxInitialBalanceTxIntrinsicGasTxRefundsTxLeftOverReturnedCallInitialBalanceCallLeftOverReturnedCallLeftOverRefundedCallContractCreationCallContractCreation2CallCodeStorageCallOpCodeCallPrecompiledContractCallStorageColdAccessCallFailedExecutionWitnessContractInitWitnessContractCreationWitnessCodeChunkWitnessContractCollisionCheckTxDataFloorAccountCreation"
|
_GasChangeReason_name_0 = "UnspecifiedTxInitialBalanceTxIntrinsicGasTxRefundsTxLeftOverReturnedCallInitialBalanceCallLeftOverReturnedCallLeftOverRefundedCallContractCreationCallContractCreation2CallCodeStorageCallOpCodeCallPrecompiledContractCallStorageColdAccessCallFailedExecutionWitnessContractInitWitnessContractCreationWitnessCodeChunkWitnessContractCollisionCheckTxDataFloorRefundAccountCreation"
|
||||||
_GasChangeReason_name_1 = "Ignored"
|
_GasChangeReason_name_1 = "Ignored"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
_GasChangeReason_index_0 = [...]uint16{0, 11, 27, 41, 50, 68, 86, 106, 126, 146, 167, 182, 192, 215, 236, 255, 274, 297, 313, 342, 353, 368}
|
_GasChangeReason_index_0 = [...]uint16{0, 11, 27, 41, 50, 68, 86, 106, 126, 146, 167, 182, 192, 215, 236, 255, 274, 297, 313, 342, 353, 374}
|
||||||
)
|
)
|
||||||
|
|
||||||
func (i GasChangeReason) String() string {
|
func (i GasChangeReason) String() string {
|
||||||
|
|
|
||||||
|
|
@ -472,9 +472,9 @@ const (
|
||||||
// transaction data. This change will always be a negative change.
|
// transaction data. This change will always be a negative change.
|
||||||
GasChangeTxDataFloor GasChangeReason = 19
|
GasChangeTxDataFloor GasChangeReason = 19
|
||||||
|
|
||||||
// GasChangeAccountCreation represents the state gas charging for account
|
// GasChangeRefundAccountCreation represents the cancellation of a
|
||||||
// creation inside the call/create frame.
|
// pre-charged account-creation cost when no account is created.
|
||||||
GasChangeAccountCreation GasChangeReason = 20
|
GasChangeRefundAccountCreation GasChangeReason = 20
|
||||||
|
|
||||||
// GasChangeIgnored is a special value that can be used to indicate that the gas change should be ignored as
|
// GasChangeIgnored is a special value that can be used to indicate that the gas change should be ignored as
|
||||||
// it will be "manually" tracked by a direct emit of the gas change event.
|
// it will be "manually" tracked by a direct emit of the gas change event.
|
||||||
|
|
|
||||||
|
|
@ -152,6 +152,16 @@ func (c *Contract) chargeState(s uint64, logger *tracing.Hooks, reason tracing.G
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// refundState refunds the pre-charged state gas back to state reservoir.
|
||||||
|
func (c *Contract) refundState(s uint64, logger *tracing.Hooks, reason tracing.GasChangeReason) {
|
||||||
|
prior := c.Gas
|
||||||
|
c.Gas.RefundState(s)
|
||||||
|
|
||||||
|
if s != 0 && logger.HasGasHook() && reason != tracing.GasChangeIgnored {
|
||||||
|
logger.EmitGasChange(prior.AsTracing(), c.Gas.AsTracing(), reason)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// refundGas absorbs a sub-call's leftover GasBudget into this contract's gas state.
|
// refundGas absorbs a sub-call's leftover GasBudget into this contract's gas state.
|
||||||
func (c *Contract) refundGas(child GasBudget, forwarded uint64, logger *tracing.Hooks, reason tracing.GasChangeReason) {
|
func (c *Contract) refundGas(child GasBudget, forwarded uint64, logger *tracing.Hooks, reason tracing.GasChangeReason) {
|
||||||
prior := c.Gas
|
prior := c.Gas
|
||||||
|
|
|
||||||
|
|
@ -595,7 +595,10 @@ func enable7843(jt *JumpTable) {
|
||||||
// enable8037 enables the multidimensional-metering as specified in EIP-8037.
|
// enable8037 enables the multidimensional-metering as specified in EIP-8037.
|
||||||
func enable8037(jt *JumpTable) {
|
func enable8037(jt *JumpTable) {
|
||||||
jt[CREATE].constantGas = params.CreateGasAmsterdam
|
jt[CREATE].constantGas = params.CreateGasAmsterdam
|
||||||
|
jt[CREATE].dynamicGas = gasCreateEip8037
|
||||||
jt[CREATE2].constantGas = params.CreateGasAmsterdam
|
jt[CREATE2].constantGas = params.CreateGasAmsterdam
|
||||||
|
jt[CREATE2].dynamicGas = gasCreate2Eip8037
|
||||||
|
jt[CALL].dynamicGas = gasCallEIP8037
|
||||||
jt[SELFDESTRUCT].dynamicGas = gasSelfdestruct8037
|
jt[SELFDESTRUCT].dynamicGas = gasSelfdestruct8037
|
||||||
jt[SSTORE].dynamicGas = gasSStore8037
|
jt[SSTORE].dynamicGas = gasSStore8037
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -289,16 +289,6 @@ func (evm *EVM) Call(caller common.Address, addr common.Address, input []byte, g
|
||||||
}
|
}
|
||||||
evm.StateDB.CreateAccount(addr)
|
evm.StateDB.CreateAccount(addr)
|
||||||
}
|
}
|
||||||
if evm.chainRules.IsAmsterdam && !value.IsZero() && evm.StateDB.Empty(addr) {
|
|
||||||
prev, ok := gas.ChargeState(params.AccountCreationSize * evm.Context.CostPerStateByte)
|
|
||||||
if !ok {
|
|
||||||
evm.StateDB.RevertToSnapshot(snapshot)
|
|
||||||
return nil, gas.ExitHalt(reservoir), ErrOutOfGas
|
|
||||||
}
|
|
||||||
if evm.Config.Tracer.HasGasHook() {
|
|
||||||
evm.Config.Tracer.EmitGasChange(prev.AsTracing(), gas.AsTracing(), tracing.GasChangeAccountCreation)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Perform the value transfer only in non-syscall mode.
|
// Perform the value transfer only in non-syscall mode.
|
||||||
// Calling this is required even for zero-value transfers,
|
// Calling this is required even for zero-value transfers,
|
||||||
// to ensure the state clearing mechanism is applied.
|
// to ensure the state clearing mechanism is applied.
|
||||||
|
|
@ -484,7 +474,7 @@ func (evm *EVM) StaticCall(caller common.Address, addr common.Address, input []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 GasBudget, value *uint256.Int, address common.Address, typ OpCode) (ret []byte, createAddress common.Address, result GasBudget, 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, result GasBudget, creation bool, err error) {
|
||||||
// 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.
|
||||||
var nonce uint64
|
var nonce uint64
|
||||||
|
|
@ -505,7 +495,7 @@ func (evm *EVM) create(caller common.Address, code []byte, gas GasBudget, value
|
||||||
}(gas)
|
}(gas)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, common.Address{}, gas, err
|
return nil, common.Address{}, gas, false, err
|
||||||
}
|
}
|
||||||
// Increment the caller's nonce after passing all validations
|
// Increment the caller's nonce after passing all validations
|
||||||
evm.StateDB.SetNonce(caller, nonce+1, tracing.NonceChangeContractCreator)
|
evm.StateDB.SetNonce(caller, nonce+1, tracing.NonceChangeContractCreator)
|
||||||
|
|
@ -516,7 +506,7 @@ func (evm *EVM) create(caller common.Address, code []byte, gas GasBudget, value
|
||||||
statelessGas := evm.AccessEvents.ContractCreatePreCheckGas(address, gas.RegularGas)
|
statelessGas := evm.AccessEvents.ContractCreatePreCheckGas(address, gas.RegularGas)
|
||||||
prior, ok := gas.Charge(GasCosts{RegularGas: statelessGas})
|
prior, ok := gas.Charge(GasCosts{RegularGas: statelessGas})
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, common.Address{}, gas.ExitHalt(reservoir), ErrOutOfGas
|
return nil, common.Address{}, gas.ExitHalt(reservoir), false, ErrOutOfGas
|
||||||
}
|
}
|
||||||
if evm.Config.Tracer.HasGasHook() {
|
if evm.Config.Tracer.HasGasHook() {
|
||||||
evm.Config.Tracer.EmitGasChange(prior.AsTracing(), gas.AsTracing(), tracing.GasChangeWitnessContractCollisionCheck)
|
evm.Config.Tracer.EmitGasChange(prior.AsTracing(), gas.AsTracing(), tracing.GasChangeWitnessContractCollisionCheck)
|
||||||
|
|
@ -543,7 +533,7 @@ func (evm *EVM) create(caller common.Address, code []byte, gas GasBudget, value
|
||||||
}
|
}
|
||||||
// EIP-8037 collision rule: the state reservoir is fully preserved on
|
// EIP-8037 collision rule: the state reservoir is fully preserved on
|
||||||
// address collision while regular gas is burnt.
|
// address collision while regular gas is burnt.
|
||||||
return nil, common.Address{}, halt, ErrContractAddressCollision
|
return nil, common.Address{}, halt, false, 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
|
||||||
|
|
@ -551,18 +541,7 @@ func (evm *EVM) create(caller common.Address, code []byte, gas GasBudget, value
|
||||||
snapshot := evm.StateDB.Snapshot()
|
snapshot := evm.StateDB.Snapshot()
|
||||||
if !evm.StateDB.Exist(address) {
|
if !evm.StateDB.Exist(address) {
|
||||||
evm.StateDB.CreateAccount(address)
|
evm.StateDB.CreateAccount(address)
|
||||||
|
creation = true
|
||||||
if evm.chainRules.IsAmsterdam && evm.depth > 0 {
|
|
||||||
// Only charge state gas if we are not doing a create transaction.
|
|
||||||
// Prevents double charging with IntrinsicGas.
|
|
||||||
prev, ok := gas.ChargeState(params.AccountCreationSize * evm.Context.CostPerStateByte)
|
|
||||||
if !ok {
|
|
||||||
return nil, common.Address{}, gas.ExitHalt(reservoir), ErrOutOfGas
|
|
||||||
}
|
|
||||||
if evm.Config.Tracer.HasGasHook() {
|
|
||||||
evm.Config.Tracer.EmitGasChange(prev.AsTracing(), gas.AsTracing(), tracing.GasChangeAccountCreation)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// CreateContract means that regardless of whether the account previously existed
|
// CreateContract means that regardless of whether the account previously existed
|
||||||
// in the state trie or not, it _now_ becomes created as a _contract_ account.
|
// in the state trie or not, it _now_ becomes created as a _contract_ account.
|
||||||
|
|
@ -577,7 +556,7 @@ func (evm *EVM) create(caller common.Address, code []byte, gas GasBudget, value
|
||||||
if evm.chainRules.IsEIP4762 {
|
if evm.chainRules.IsEIP4762 {
|
||||||
consumed, wanted := evm.AccessEvents.ContractCreateInitGas(address, gas.RegularGas)
|
consumed, wanted := evm.AccessEvents.ContractCreateInitGas(address, gas.RegularGas)
|
||||||
if consumed < wanted {
|
if consumed < wanted {
|
||||||
return nil, common.Address{}, gas.ExitHalt(reservoir), ErrOutOfGas
|
return nil, common.Address{}, gas.ExitHalt(reservoir), false, ErrOutOfGas
|
||||||
}
|
}
|
||||||
prior, _ := gas.Charge(GasCosts{RegularGas: consumed})
|
prior, _ := gas.Charge(GasCosts{RegularGas: consumed})
|
||||||
if evm.Config.Tracer.HasGasHook() {
|
if evm.Config.Tracer.HasGasHook() {
|
||||||
|
|
@ -608,11 +587,11 @@ func (evm *EVM) create(caller common.Address, code []byte, gas GasBudget, value
|
||||||
evm.Config.Tracer.EmitGasChange(contract.Gas.AsTracing(), exit.AsTracing(), tracing.GasChangeCallFailedExecution)
|
evm.Config.Tracer.EmitGasChange(contract.Gas.AsTracing(), exit.AsTracing(), tracing.GasChangeCallFailedExecution)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ret, address, exit, err
|
return ret, address, exit, false, err
|
||||||
}
|
}
|
||||||
// Either success, or pre-Homestead ErrCodeStoreOutOfGas (gas preserved).
|
// Either success, or pre-Homestead ErrCodeStoreOutOfGas (gas preserved).
|
||||||
// Both packaged as a success-form GasBudget.
|
// Both packaged as a success-form GasBudget.
|
||||||
return ret, address, contract.Gas.ExitSuccess(), err
|
return ret, address, contract.Gas.ExitSuccess(), creation, 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
|
||||||
|
|
@ -668,7 +647,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 GasBudget, value *uint256.Int) (ret []byte, contractAddr common.Address, result GasBudget, err error) {
|
func (evm *EVM) Create(caller common.Address, code []byte, gas GasBudget, value *uint256.Int) (ret []byte, contractAddr common.Address, result GasBudget, creation bool, 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)
|
||||||
}
|
}
|
||||||
|
|
@ -677,7 +656,7 @@ func (evm *EVM) Create(caller common.Address, code []byte, gas GasBudget, value
|
||||||
//
|
//
|
||||||
// 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 GasBudget, endowment *uint256.Int, salt *uint256.Int) (ret []byte, contractAddr common.Address, result GasBudget, err error) {
|
func (evm *EVM) Create2(caller common.Address, code []byte, gas GasBudget, endowment *uint256.Int, salt *uint256.Int) (ret []byte, contractAddr common.Address, result GasBudget, creation bool, 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)
|
||||||
|
|
|
||||||
|
|
@ -519,6 +519,117 @@ func gasSelfdestruct(evm *EVM, contract *Contract, stack *Stack, mem *Memory, me
|
||||||
return GasCosts{RegularGas: gas}, nil
|
return GasCosts{RegularGas: gas}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func gasCreateEip8037(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||||
|
if evm.readOnly {
|
||||||
|
return GasCosts{}, ErrWriteProtection
|
||||||
|
}
|
||||||
|
gas, err := memoryGasCost(mem, memorySize)
|
||||||
|
if err != nil {
|
||||||
|
return GasCosts{}, err
|
||||||
|
}
|
||||||
|
size, overflow := stack.back(2).Uint64WithOverflow()
|
||||||
|
if overflow {
|
||||||
|
return GasCosts{}, ErrGasUintOverflow
|
||||||
|
}
|
||||||
|
if err := CheckMaxInitCodeSize(&evm.chainRules, size); err != nil {
|
||||||
|
return GasCosts{}, err
|
||||||
|
}
|
||||||
|
// Since size <= MaxInitCodeSizeAmsterdam, these multiplications cannot overflow
|
||||||
|
words := (size + 31) / 32
|
||||||
|
wordGas := params.InitCodeWordGas * words
|
||||||
|
|
||||||
|
// Unconditionally pre-charge the account creation and refunds if the creation
|
||||||
|
// doesn't happen after the create-frame.
|
||||||
|
return GasCosts{
|
||||||
|
RegularGas: gas + wordGas,
|
||||||
|
StateGas: params.AccountCreationSize * evm.Context.CostPerStateByte,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func gasCreate2Eip8037(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||||
|
if evm.readOnly {
|
||||||
|
return GasCosts{}, ErrWriteProtection
|
||||||
|
}
|
||||||
|
gas, err := memoryGasCost(mem, memorySize)
|
||||||
|
if err != nil {
|
||||||
|
return GasCosts{}, err
|
||||||
|
}
|
||||||
|
size, overflow := stack.back(2).Uint64WithOverflow()
|
||||||
|
if overflow {
|
||||||
|
return GasCosts{}, ErrGasUintOverflow
|
||||||
|
}
|
||||||
|
if err := CheckMaxInitCodeSize(&evm.chainRules, size); err != nil {
|
||||||
|
return GasCosts{}, err
|
||||||
|
}
|
||||||
|
// Since size <= MaxInitCodeSizeAmsterdam, these multiplications cannot overflow
|
||||||
|
words := (size + 31) / 32
|
||||||
|
|
||||||
|
// CREATE2 charges both InitCodeWordGas (EIP-3860) and Keccak256WordGas
|
||||||
|
// (for address hashing).
|
||||||
|
wordGas := (params.InitCodeWordGas + params.Keccak256WordGas) * words
|
||||||
|
|
||||||
|
// Unconditionally pre-charge the account creation and refunds if the creation
|
||||||
|
// doesn't happen after the create-frame.
|
||||||
|
return GasCosts{
|
||||||
|
RegularGas: gas + wordGas,
|
||||||
|
StateGas: params.AccountCreationSize * evm.Context.CostPerStateByte,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// regularGasCall8037 is the intrinsic gas calculator for CALL in Amsterdam.
|
||||||
|
// It computes memory expansion + value transfer gas but excludes new account
|
||||||
|
// creation, which is handled as state gas by the wrapper.
|
||||||
|
func regularGasCall8037(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||||
|
var (
|
||||||
|
gas uint64
|
||||||
|
transfersValue = !stack.back(2).IsZero()
|
||||||
|
)
|
||||||
|
if evm.readOnly && transfersValue {
|
||||||
|
return 0, ErrWriteProtection
|
||||||
|
}
|
||||||
|
memoryGas, err := memoryGasCost(mem, memorySize)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
var transferGas uint64
|
||||||
|
if transfersValue && !evm.chainRules.IsEIP4762 {
|
||||||
|
transferGas = params.CallValueTransferGas
|
||||||
|
}
|
||||||
|
var overflow bool
|
||||||
|
if gas, overflow = math.SafeAdd(memoryGas, transferGas); overflow {
|
||||||
|
return 0, ErrGasUintOverflow
|
||||||
|
}
|
||||||
|
return gas, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// stateGasCall8037 is the stateful gas calculator for CALL in Amsterdam (EIP-8037).
|
||||||
|
// It only returns the state-dependent gas (account creation as state gas).
|
||||||
|
// Memory gas, transfer gas, and callGas are handled by gasCallStateless and
|
||||||
|
// makeCallVariantGasCall.
|
||||||
|
func stateGasCall8037(evm *EVM, contract *Contract, stack *Stack) (uint64, error) {
|
||||||
|
var (
|
||||||
|
gas uint64
|
||||||
|
transfersValue = !stack.back(2).IsZero()
|
||||||
|
address = common.Address(stack.back(1).Bytes20())
|
||||||
|
)
|
||||||
|
// TODO(rjl, marius), can EIP8037 implicitly means the EIP158 is also activated?
|
||||||
|
// It's technically possible to skip the EIP158 but very unlikely in practice.
|
||||||
|
if evm.chainRules.IsEIP158 {
|
||||||
|
// Important: use StateDB.Empty instead of !StateDB.Exist. An account may exist
|
||||||
|
// in the current state yet still be considered non-existent by EIP-161 if its
|
||||||
|
// nonce, balance, and code are all zero. Such accounts can appear temporarily
|
||||||
|
// during execution (e.g. via SELFDESTRUCT) and are removed at tx end.
|
||||||
|
//
|
||||||
|
// Funding such an account makes it permanent state growth and must be charged.
|
||||||
|
if transfersValue && evm.StateDB.Empty(address) {
|
||||||
|
gas += params.AccountCreationSize * evm.Context.CostPerStateByte
|
||||||
|
}
|
||||||
|
} else if !evm.StateDB.Exist(address) {
|
||||||
|
gas += params.AccountCreationSize * evm.Context.CostPerStateByte
|
||||||
|
}
|
||||||
|
return gas, nil
|
||||||
|
}
|
||||||
|
|
||||||
func gasSelfdestruct8037(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
func gasSelfdestruct8037(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||||
if evm.readOnly {
|
if evm.readOnly {
|
||||||
return GasCosts{}, ErrWriteProtection
|
return GasCosts{}, ErrWriteProtection
|
||||||
|
|
|
||||||
|
|
@ -662,7 +662,7 @@ func opCreate(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
||||||
stackvalue := size
|
stackvalue := size
|
||||||
|
|
||||||
child := scope.Contract.forwardGas(forward, evm.Config.Tracer, tracing.GasChangeCallContractCreation)
|
child := scope.Contract.forwardGas(forward, evm.Config.Tracer, tracing.GasChangeCallContractCreation)
|
||||||
res, addr, result, suberr := evm.Create(scope.Contract.Address(), input, child, &value)
|
res, addr, result, creation, suberr := evm.Create(scope.Contract.Address(), input, child, &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
|
||||||
|
|
@ -679,6 +679,10 @@ func opCreate(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
||||||
// Refund the leftover gas back to current frame
|
// Refund the leftover gas back to current frame
|
||||||
scope.Contract.refundGas(result, forward, evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded)
|
scope.Contract.refundGas(result, forward, evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded)
|
||||||
|
|
||||||
|
// Refund the state gas of account-creation if creation doesn't happen
|
||||||
|
if evm.GetRules().IsAmsterdam && !creation {
|
||||||
|
scope.Contract.refundState(params.AccountCreationSize*evm.Context.CostPerStateByte, evm.Config.Tracer, tracing.GasChangeRefundAccountCreation)
|
||||||
|
}
|
||||||
if suberr == ErrExecutionReverted {
|
if suberr == ErrExecutionReverted {
|
||||||
evm.returnData = res // set REVERT data to return data buffer
|
evm.returnData = res // set REVERT data to return data buffer
|
||||||
return res, nil
|
return res, nil
|
||||||
|
|
@ -701,7 +705,7 @@ func opCreate2(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
||||||
// reuse size int for stackvalue
|
// reuse size int for stackvalue
|
||||||
stackvalue := size
|
stackvalue := size
|
||||||
child := scope.Contract.forwardGas(forward, evm.Config.Tracer, tracing.GasChangeCallContractCreation2)
|
child := scope.Contract.forwardGas(forward, evm.Config.Tracer, tracing.GasChangeCallContractCreation2)
|
||||||
res, addr, result, suberr := evm.Create2(scope.Contract.Address(), input, child, &endowment, &salt)
|
res, addr, result, creation, suberr := evm.Create2(scope.Contract.Address(), input, child, &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 {
|
||||||
stackvalue.Clear()
|
stackvalue.Clear()
|
||||||
|
|
@ -713,6 +717,10 @@ func opCreate2(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
||||||
// Refund the leftover gas back to current frame
|
// Refund the leftover gas back to current frame
|
||||||
scope.Contract.refundGas(result, forward, evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded)
|
scope.Contract.refundGas(result, forward, evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded)
|
||||||
|
|
||||||
|
// Refund the state gas of account-creation if creation doesn't happen
|
||||||
|
if evm.GetRules().IsAmsterdam && !creation {
|
||||||
|
scope.Contract.refundState(params.AccountCreationSize*evm.Context.CostPerStateByte, evm.Config.Tracer, tracing.GasChangeRefundAccountCreation)
|
||||||
|
}
|
||||||
if suberr == ErrExecutionReverted {
|
if suberr == ErrExecutionReverted {
|
||||||
evm.returnData = res // set REVERT data to return data buffer
|
evm.returnData = res // set REVERT data to return data buffer
|
||||||
return res, nil
|
return res, nil
|
||||||
|
|
@ -758,6 +766,11 @@ func opCall(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
||||||
}
|
}
|
||||||
scope.Contract.refundGas(result, gas, evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded)
|
scope.Contract.refundGas(result, gas, evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded)
|
||||||
|
|
||||||
|
// If the call frame reverts or halts exceptionally, the charged state-gas
|
||||||
|
// is refilled back to the state reservoir in Amsterdam.
|
||||||
|
if evm.chainRules.IsAmsterdam && err != nil && !value.IsZero() && evm.StateDB.Empty(toAddr) {
|
||||||
|
scope.Contract.refundState(params.AccountCreationSize*evm.Context.CostPerStateByte, evm.Config.Tracer, tracing.GasChangeRefundAccountCreation)
|
||||||
|
}
|
||||||
evm.returnData = ret
|
evm.returnData = ret
|
||||||
return ret, nil
|
return ret, nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,9 @@ type (
|
||||||
intrinsicGasFunc func(*EVM, *Contract, *Stack, *Memory, uint64) (uint64, error) // last parameter is the requested memory size as a uint64
|
intrinsicGasFunc func(*EVM, *Contract, *Stack, *Memory, uint64) (uint64, error) // last parameter is the requested memory size as a uint64
|
||||||
// memorySizeFunc returns the required size, and whether the operation overflowed a uint64
|
// memorySizeFunc returns the required size, and whether the operation overflowed a uint64
|
||||||
memorySizeFunc func(*Stack) (size uint64, overflow bool)
|
memorySizeFunc func(*Stack) (size uint64, overflow bool)
|
||||||
|
|
||||||
|
regularGasFunc func(*EVM, *Contract, *Stack, *Memory, uint64) (uint64, error)
|
||||||
|
stateGasFunc func(*EVM, *Contract, *Stack) (uint64, error)
|
||||||
)
|
)
|
||||||
|
|
||||||
type operation struct {
|
type operation struct {
|
||||||
|
|
|
||||||
|
|
@ -262,6 +262,7 @@ var (
|
||||||
gasDelegateCallEIP7702 = makeCallVariantGasCallEIP7702(gasDelegateCallIntrinsic)
|
gasDelegateCallEIP7702 = makeCallVariantGasCallEIP7702(gasDelegateCallIntrinsic)
|
||||||
gasStaticCallEIP7702 = makeCallVariantGasCallEIP7702(gasStaticCallIntrinsic)
|
gasStaticCallEIP7702 = makeCallVariantGasCallEIP7702(gasStaticCallIntrinsic)
|
||||||
gasCallCodeEIP7702 = makeCallVariantGasCallEIP7702(gasCallCodeIntrinsic)
|
gasCallCodeEIP7702 = makeCallVariantGasCallEIP7702(gasCallCodeIntrinsic)
|
||||||
|
innerGasCallEIP8037 = makeCallVariantGasCallEIP8037(regularGasCall8037, stateGasCall8037)
|
||||||
)
|
)
|
||||||
|
|
||||||
func gasCallEIP7702(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
func gasCallEIP7702(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||||
|
|
@ -276,6 +277,14 @@ func gasCallEIP7702(evm *EVM, contract *Contract, stack *Stack, mem *Memory, mem
|
||||||
return innerGasCallEIP7702(evm, contract, stack, mem, memorySize)
|
return innerGasCallEIP7702(evm, contract, stack, mem, memorySize)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func gasCallEIP8037(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||||
|
transfersValue := !stack.back(2).IsZero()
|
||||||
|
if evm.readOnly && transfersValue {
|
||||||
|
return GasCosts{}, ErrWriteProtection
|
||||||
|
}
|
||||||
|
return innerGasCallEIP8037(evm, contract, stack, mem, memorySize)
|
||||||
|
}
|
||||||
|
|
||||||
func makeCallVariantGasCallEIP7702(intrinsicFunc intrinsicGasFunc) gasFunc {
|
func makeCallVariantGasCallEIP7702(intrinsicFunc intrinsicGasFunc) gasFunc {
|
||||||
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||||
var (
|
var (
|
||||||
|
|
@ -362,3 +371,89 @@ func makeCallVariantGasCallEIP7702(intrinsicFunc intrinsicGasFunc) gasFunc {
|
||||||
return GasCosts{RegularGas: totalCost}, nil
|
return GasCosts{RegularGas: totalCost}, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// makeCallVariantGasCallEIP8037 creates a call gas function for Amsterdam (EIP-8037).
|
||||||
|
// It extends the EIP-7702 pattern with state gas handling and GasUsed tracking.
|
||||||
|
// intrinsicFunc computes the regular gas (memory + transfer, no new account creation).
|
||||||
|
// stateGasFunc computes the state gas (new account creation as state gas).
|
||||||
|
func makeCallVariantGasCallEIP8037(regularFunc regularGasFunc, stateGasFunc stateGasFunc) gasFunc {
|
||||||
|
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||||
|
var (
|
||||||
|
eip2929Cost uint64
|
||||||
|
eip7702Cost uint64
|
||||||
|
addr = common.Address(stack.back(1).Bytes20())
|
||||||
|
)
|
||||||
|
// EIP-2929 cold access check.
|
||||||
|
if !evm.StateDB.AddressInAccessList(addr) {
|
||||||
|
evm.StateDB.AddAddressToAccessList(addr)
|
||||||
|
eip2929Cost = params.ColdAccountAccessCostEIP2929 - params.WarmStorageReadCostEIP2929
|
||||||
|
if !contract.chargeRegular(eip2929Cost, evm.Config.Tracer, tracing.GasChangeCallStorageColdAccess) {
|
||||||
|
return GasCosts{}, ErrOutOfGas
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute regular cost (memory + transfer, no new account creation).
|
||||||
|
regularCost, err := regularFunc(evm, contract, stack, mem, memorySize)
|
||||||
|
if err != nil {
|
||||||
|
return GasCosts{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Charge intrinsic cost directly (regular gas). This must happen
|
||||||
|
// BEFORE state gas to prevent reservoir inflation, and also serves
|
||||||
|
// as the OOG guard before stateful operations.
|
||||||
|
if !contract.chargeRegular(regularCost, evm.Config.Tracer, tracing.GasChangeCallOpCode) {
|
||||||
|
return GasCosts{}, ErrOutOfGas
|
||||||
|
}
|
||||||
|
|
||||||
|
// EIP-7702 delegation check.
|
||||||
|
if target, ok := types.ParseDelegation(evm.StateDB.GetCode(addr)); ok {
|
||||||
|
if evm.StateDB.AddressInAccessList(target) {
|
||||||
|
eip7702Cost = params.WarmStorageReadCostEIP2929
|
||||||
|
} else {
|
||||||
|
evm.StateDB.AddAddressToAccessList(target)
|
||||||
|
eip7702Cost = params.ColdAccountAccessCostEIP2929
|
||||||
|
}
|
||||||
|
if !contract.chargeRegular(eip7702Cost, evm.Config.Tracer, tracing.GasChangeCallStorageColdAccess) {
|
||||||
|
return GasCosts{}, ErrOutOfGas
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute and charge state gas (new account creation) AFTER regular gas.
|
||||||
|
stateGas, err := stateGasFunc(evm, contract, stack)
|
||||||
|
if err != nil {
|
||||||
|
return GasCosts{}, err
|
||||||
|
}
|
||||||
|
if stateGas > 0 {
|
||||||
|
if _, ok := contract.Gas.ChargeState(stateGas); !ok {
|
||||||
|
return GasCosts{}, ErrOutOfGas
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate the gas budget for the nested call (63/64 rule).
|
||||||
|
evm.callGasTemp, err = callGas(evm.chainRules.IsEIP150, contract.Gas.RegularGas, 0, stack.back(0))
|
||||||
|
if err != nil {
|
||||||
|
return GasCosts{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Temporarily undo direct regular charges for tracer reporting.
|
||||||
|
// The interpreter will charge the returned totalCost.
|
||||||
|
contract.Gas.RegularGas += eip2929Cost + eip7702Cost + regularCost
|
||||||
|
contract.Gas.UsedRegularGas -= eip2929Cost + eip7702Cost + regularCost
|
||||||
|
|
||||||
|
// Aggregate total cost.
|
||||||
|
var (
|
||||||
|
overflow bool
|
||||||
|
totalCost uint64
|
||||||
|
)
|
||||||
|
if totalCost, overflow = math.SafeAdd(eip2929Cost, eip7702Cost); overflow {
|
||||||
|
return GasCosts{}, ErrGasUintOverflow
|
||||||
|
}
|
||||||
|
if totalCost, overflow = math.SafeAdd(totalCost, regularCost); overflow {
|
||||||
|
return GasCosts{}, ErrGasUintOverflow
|
||||||
|
}
|
||||||
|
if totalCost, overflow = math.SafeAdd(totalCost, evm.callGasTemp); overflow {
|
||||||
|
return GasCosts{}, ErrGasUintOverflow
|
||||||
|
}
|
||||||
|
return GasCosts{RegularGas: totalCost}, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -179,7 +179,7 @@ func Create(input []byte, cfg *Config) ([]byte, common.Address, uint64, error) {
|
||||||
// - reset transient storage(eip 1153)
|
// - reset transient storage(eip 1153)
|
||||||
cfg.State.Prepare(rules, cfg.Origin, cfg.Coinbase, nil, vm.ActivePrecompiles(rules), nil)
|
cfg.State.Prepare(rules, cfg.Origin, cfg.Coinbase, nil, vm.ActivePrecompiles(rules), nil)
|
||||||
// Call the code with the given configuration.
|
// Call the code with the given configuration.
|
||||||
code, address, result, err := vmenv.Create(
|
code, address, result, _, err := vmenv.Create(
|
||||||
cfg.Origin,
|
cfg.Origin,
|
||||||
input,
|
input,
|
||||||
vm.NewGasBudget(cfg.GasLimit, 0),
|
vm.NewGasBudget(cfg.GasLimit, 0),
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue