core/vm: fix two bugs

Fixes the collision bugs and create gas ordering
This commit is contained in:
Marius van der Wijden 2026-04-09 15:04:04 +02:00
parent 407cf11930
commit cd2095fb4a
3 changed files with 37 additions and 45 deletions

View file

@ -539,9 +539,7 @@ func (evm *EVM) create(caller common.Address, code []byte, gas GasBudget, value
evm.Config.Tracer.OnGasChange(gas.RegularGas, 0, tracing.GasChangeCallFailedExecution)
}
// Burn all gas on collision
collisionUsed := GasUsed{RegularGas: gas.RegularGas}
gas.RegularGas = 0
return nil, common.Address{}, gas, collisionUsed, ErrContractAddressCollision
return nil, common.Address{}, GasBudget{}, GasUsed{}, 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

View file

@ -291,10 +291,19 @@ var (
gasMLoad = pureMemoryGascost
gasMStore8 = pureMemoryGascost
gasMStore = pureMemoryGascost
gasCreate = pureMemoryGascost
)
func gasCreate(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
if evm.readOnly {
return GasCosts{}, ErrWriteProtection
}
return pureMemoryGascost(evm, contract, stack, mem, memorySize)
}
func gasCreate2(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
@ -313,6 +322,9 @@ func gasCreate2(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memoryS
}
func gasCreateEip3860(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
@ -333,6 +345,9 @@ func gasCreateEip3860(evm *EVM, contract *Contract, stack *Stack, mem *Memory, m
}
func gasCreate2Eip3860(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
@ -532,6 +547,9 @@ func gasSelfdestruct(evm *EVM, contract *Contract, stack *Stack, mem *Memory, me
}
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
@ -540,19 +558,20 @@ func gasCreateEip8037(evm *EVM, contract *Contract, stack *Stack, mem *Memory, m
if overflow {
return GasCosts{}, ErrGasUintOverflow
}
// Cap word gas at MaxInitCodeSizeAmsterdam to avoid overflow.
// The actual init code size check happens in create() for graceful failure.
wordSize := min(size, params.MaxInitCodeSizeAmsterdam)
words := (wordSize + 31) / 32
// Account creation is a fixed state gas cost, not proportional to init code size.
// Code storage state gas is charged separately in initNewContract.
stateGas := params.AccountCreationSize * evm.Context.CostPerGasByte
// CREATE uses InitCodeWordGas (EIP-3860); Keccak256WordGas is only for CREATE2.
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
stateGas := params.AccountCreationSize * evm.Context.CostPerGasByte
return GasCosts{RegularGas: gas + wordGas, StateGas: stateGas}, 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
@ -561,15 +580,14 @@ func gasCreate2Eip8037(evm *EVM, contract *Contract, stack *Stack, mem *Memory,
if overflow {
return GasCosts{}, ErrGasUintOverflow
}
// Cap word gas at MaxInitCodeSizeAmsterdam to avoid overflow.
// The actual init code size check happens in create() for graceful failure.
wordSize := min(size, params.MaxInitCodeSizeAmsterdam)
words := (wordSize + 31) / 32
// Account creation is a fixed state gas cost, not proportional to init code size.
// Code storage state gas is charged separately in initNewContract.
stateGas := params.AccountCreationSize * evm.Context.CostPerGasByte
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
stateGas := params.AccountCreationSize * evm.Context.CostPerGasByte
return GasCosts{RegularGas: gas + wordGas, StateGas: stateGas}, nil
}

View file

@ -648,28 +648,16 @@ func opSwap16(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
}
func opCreate(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
if evm.readOnly {
return nil, ErrWriteProtection
}
var (
value = scope.Stack.pop()
offset, size = scope.Stack.pop(), scope.Stack.pop()
input = scope.Memory.GetCopy(offset.Uint64(), size.Uint64())
gas = scope.Contract.Gas
)
gas := scope.Contract.Gas
if evm.chainRules.IsEIP150 {
gas.RegularGas -= gas.RegularGas / 64
}
// EIP-7954: check init code size after gas is charged (by the gas function)
// but before execution. This aborts the caller's execution, ensuring all
// regular gas is consumed while the state gas spill is tracked.
if evm.chainRules.IsAmsterdam {
if err := CheckMaxInitCodeSize(&evm.chainRules, uint64(len(input))); err != nil {
return nil, err
}
}
// reuse size int for stackvalue
stackvalue := size
@ -701,29 +689,17 @@ func opCreate(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
}
func opCreate2(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
if evm.readOnly {
return nil, ErrWriteProtection
}
var (
endowment = scope.Stack.pop()
offset, size = scope.Stack.pop(), scope.Stack.pop()
salt = scope.Stack.pop()
input = scope.Memory.GetCopy(offset.Uint64(), size.Uint64())
gas = scope.Contract.Gas
)
// Apply EIP150
gas := scope.Contract.Gas
gas.RegularGas -= gas.RegularGas / 64
// EIP-7954: check init code size after gas is charged (by the gas function)
// but before execution. This aborts the caller's execution, ensuring all
// regular gas is consumed while the state gas spill is tracked.
if evm.chainRules.IsAmsterdam {
if err := CheckMaxInitCodeSize(&evm.chainRules, uint64(len(input))); err != nil {
return nil, err
}
}
regularGasUsed := scope.Contract.GasUsed.RegularGas
scope.Contract.UseGas(GasCosts{RegularGas: gas.RegularGas}, evm.Config.Tracer, tracing.GasChangeCallContractCreation2)