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) evm.Config.Tracer.OnGasChange(gas.RegularGas, 0, tracing.GasChangeCallFailedExecution)
} }
// Burn all gas on collision // Burn all gas on collision
collisionUsed := GasUsed{RegularGas: gas.RegularGas} return nil, common.Address{}, GasBudget{}, GasUsed{}, ErrContractAddressCollision
gas.RegularGas = 0
return nil, common.Address{}, gas, collisionUsed, 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

View file

@ -291,10 +291,19 @@ var (
gasMLoad = pureMemoryGascost gasMLoad = pureMemoryGascost
gasMStore8 = pureMemoryGascost gasMStore8 = pureMemoryGascost
gasMStore = 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) { 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) gas, err := memoryGasCost(mem, memorySize)
if err != nil { if err != nil {
return GasCosts{}, err 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) { 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) gas, err := memoryGasCost(mem, memorySize)
if err != nil { if err != nil {
return GasCosts{}, err 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) { 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) gas, err := memoryGasCost(mem, memorySize)
if err != nil { if err != nil {
return GasCosts{}, err 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) { 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) gas, err := memoryGasCost(mem, memorySize)
if err != nil { if err != nil {
return GasCosts{}, err return GasCosts{}, err
@ -540,19 +558,20 @@ func gasCreateEip8037(evm *EVM, contract *Contract, stack *Stack, mem *Memory, m
if overflow { if overflow {
return GasCosts{}, ErrGasUintOverflow return GasCosts{}, ErrGasUintOverflow
} }
// Cap word gas at MaxInitCodeSizeAmsterdam to avoid overflow. if err := CheckMaxInitCodeSize(&evm.chainRules, size); err != nil {
// The actual init code size check happens in create() for graceful failure. return GasCosts{}, err
wordSize := min(size, params.MaxInitCodeSizeAmsterdam) }
words := (wordSize + 31) / 32 // Since size <= MaxInitCodeSizeAmsterdam, these multiplications cannot overflow
// Account creation is a fixed state gas cost, not proportional to init code size. words := (size + 31) / 32
// 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.
wordGas := params.InitCodeWordGas * words wordGas := params.InitCodeWordGas * words
stateGas := params.AccountCreationSize * evm.Context.CostPerGasByte
return GasCosts{RegularGas: gas + wordGas, StateGas: stateGas}, nil return GasCosts{RegularGas: gas + wordGas, StateGas: stateGas}, nil
} }
func gasCreate2Eip8037(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) { func gasCreate2Eip8037(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
if evm.readOnly {
return GasCosts{}, ErrWriteProtection
}
gas, err := memoryGasCost(mem, memorySize) gas, err := memoryGasCost(mem, memorySize)
if err != nil { if err != nil {
return GasCosts{}, err return GasCosts{}, err
@ -561,15 +580,14 @@ func gasCreate2Eip8037(evm *EVM, contract *Contract, stack *Stack, mem *Memory,
if overflow { if overflow {
return GasCosts{}, ErrGasUintOverflow return GasCosts{}, ErrGasUintOverflow
} }
// Cap word gas at MaxInitCodeSizeAmsterdam to avoid overflow. if err := CheckMaxInitCodeSize(&evm.chainRules, size); err != nil {
// The actual init code size check happens in create() for graceful failure. return GasCosts{}, err
wordSize := min(size, params.MaxInitCodeSizeAmsterdam) }
words := (wordSize + 31) / 32 // Since size <= MaxInitCodeSizeAmsterdam, these multiplications cannot overflow
// Account creation is a fixed state gas cost, not proportional to init code size. words := (size + 31) / 32
// Code storage state gas is charged separately in initNewContract.
stateGas := params.AccountCreationSize * evm.Context.CostPerGasByte
// CREATE2 charges both InitCodeWordGas (EIP-3860) and Keccak256WordGas (for address hashing). // CREATE2 charges both InitCodeWordGas (EIP-3860) and Keccak256WordGas (for address hashing).
wordGas := (params.InitCodeWordGas + params.Keccak256WordGas) * words wordGas := (params.InitCodeWordGas + params.Keccak256WordGas) * words
stateGas := params.AccountCreationSize * evm.Context.CostPerGasByte
return GasCosts{RegularGas: gas + wordGas, StateGas: stateGas}, nil 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) { func opCreate(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
if evm.readOnly {
return nil, ErrWriteProtection
}
var ( var (
value = scope.Stack.pop() value = scope.Stack.pop()
offset, size = scope.Stack.pop(), scope.Stack.pop() offset, size = scope.Stack.pop(), scope.Stack.pop()
input = scope.Memory.GetCopy(offset.Uint64(), size.Uint64()) input = scope.Memory.GetCopy(offset.Uint64(), size.Uint64())
gas = scope.Contract.Gas
) )
gas := scope.Contract.Gas
if evm.chainRules.IsEIP150 { if evm.chainRules.IsEIP150 {
gas.RegularGas -= gas.RegularGas / 64 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 // reuse size int for stackvalue
stackvalue := size 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) { func opCreate2(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
if evm.readOnly {
return nil, ErrWriteProtection
}
var ( var (
endowment = scope.Stack.pop() endowment = scope.Stack.pop()
offset, size = scope.Stack.pop(), scope.Stack.pop() offset, size = scope.Stack.pop(), scope.Stack.pop()
salt = scope.Stack.pop() salt = scope.Stack.pop()
input = scope.Memory.GetCopy(offset.Uint64(), size.Uint64()) input = scope.Memory.GetCopy(offset.Uint64(), size.Uint64())
gas = scope.Contract.Gas
) )
// Apply EIP150 // Apply EIP150
gas := scope.Contract.Gas
gas.RegularGas -= gas.RegularGas / 64 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 regularGasUsed := scope.Contract.GasUsed.RegularGas
scope.Contract.UseGas(GasCosts{RegularGas: gas.RegularGas}, evm.Config.Tracer, tracing.GasChangeCallContractCreation2) scope.Contract.UseGas(GasCosts{RegularGas: gas.RegularGas}, evm.Config.Tracer, tracing.GasChangeCallContractCreation2)