core/vm: don't use pointers on the stack anymore

This commit is contained in:
MariusVanDerWijden 2025-05-06 18:22:43 +02:00
parent 7afd3c5323
commit dd232413c5
7 changed files with 70 additions and 50 deletions

View file

@ -25,7 +25,7 @@ import (
// calcMemSize64 calculates the required memory size, and returns // calcMemSize64 calculates the required memory size, and returns
// the size and whether the result overflowed uint64 // the size and whether the result overflowed uint64
func calcMemSize64(off, l *uint256.Int) (uint64, bool) { func calcMemSize64(off, l uint256.Int) (uint64, bool) {
if !l.IsUint64() { if !l.IsUint64() {
return 0, true return 0, true
} }
@ -35,7 +35,7 @@ func calcMemSize64(off, l *uint256.Int) (uint64, bool) {
// calcMemSize64WithUint calculates the required memory size, and returns // calcMemSize64WithUint calculates the required memory size, and returns
// the size and whether the result overflowed uint64 // the size and whether the result overflowed uint64
// Identical to calcMemSize64, but length is a uint64 // Identical to calcMemSize64, but length is a uint64
func calcMemSize64WithUint(off *uint256.Int, length64 uint64) (uint64, bool) { func calcMemSize64WithUint(off uint256.Int, length64 uint64) (uint64, bool) {
// if length is zero, memsize is always zero, regardless of offset // if length is zero, memsize is always zero, regardless of offset
if length64 == 0 { if length64 == 0 {
return 0, false return 0, false

View file

@ -35,7 +35,7 @@ const (
// //
// The cost of gas was changed during the homestead price change HF. // The cost of gas was changed during the homestead price change HF.
// As part of EIP 150 (TangerineWhistle), the returned gas is gas - base * 63 / 64. // As part of EIP 150 (TangerineWhistle), the returned gas is gas - base * 63 / 64.
func callGas(isEip150 bool, availableGas, base uint64, callCost *uint256.Int) (uint64, error) { func callGas(isEip150 bool, availableGas, base uint64, callCost uint256.Int) (uint64, error) {
if isEip150 { if isEip150 {
availableGas = availableGas - base availableGas = availableGas - base
gas := availableGas - availableGas/64 gas := availableGas - availableGas/64

View file

@ -72,7 +72,8 @@ func memoryCopierGas(stackpos int) gasFunc {
return 0, err return 0, err
} }
// And gas for copying data, charged per word at param.CopyGas // And gas for copying data, charged per word at param.CopyGas
words, overflow := stack.Back(stackpos).Uint64WithOverflow() size := stack.Back(stackpos)
words, overflow := size.Uint64WithOverflow()
if overflow { if overflow {
return 0, ErrGasUintOverflow return 0, ErrGasUintOverflow
} }
@ -225,7 +226,8 @@ func gasSStoreEIP2200(evm *EVM, contract *Contract, stack *Stack, mem *Memory, m
func makeGasLog(n uint64) gasFunc { func makeGasLog(n uint64) gasFunc {
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
requestedSize, overflow := stack.Back(1).Uint64WithOverflow() size := stack.Back(1)
requestedSize, overflow := size.Uint64WithOverflow()
if overflow { if overflow {
return 0, ErrGasUintOverflow return 0, ErrGasUintOverflow
} }
@ -258,7 +260,8 @@ func gasKeccak256(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memor
if err != nil { if err != nil {
return 0, err return 0, err
} }
wordGas, overflow := stack.Back(1).Uint64WithOverflow() size := stack.Back(1)
wordGas, overflow := size.Uint64WithOverflow()
if overflow { if overflow {
return 0, ErrGasUintOverflow return 0, ErrGasUintOverflow
} }
@ -292,7 +295,8 @@ func gasCreate2(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memoryS
if err != nil { if err != nil {
return 0, err return 0, err
} }
wordGas, overflow := stack.Back(2).Uint64WithOverflow() length := stack.Back(2)
wordGas, overflow := length.Uint64WithOverflow()
if overflow { if overflow {
return 0, ErrGasUintOverflow return 0, ErrGasUintOverflow
} }
@ -310,7 +314,8 @@ func gasCreateEip3860(evm *EVM, contract *Contract, stack *Stack, mem *Memory, m
if err != nil { if err != nil {
return 0, err return 0, err
} }
size, overflow := stack.Back(2).Uint64WithOverflow() length := stack.Back(2)
size, overflow := length.Uint64WithOverflow()
if overflow { if overflow {
return 0, ErrGasUintOverflow return 0, ErrGasUintOverflow
} }
@ -329,7 +334,8 @@ func gasCreate2Eip3860(evm *EVM, contract *Contract, stack *Stack, mem *Memory,
if err != nil { if err != nil {
return 0, err return 0, err
} }
size, overflow := stack.Back(2).Uint64WithOverflow() length := stack.Back(2)
size, overflow := length.Uint64WithOverflow()
if overflow { if overflow {
return 0, ErrGasUintOverflow return 0, ErrGasUintOverflow
} }
@ -345,7 +351,8 @@ func gasCreate2Eip3860(evm *EVM, contract *Contract, stack *Stack, mem *Memory,
} }
func gasExpFrontier(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { func gasExpFrontier(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
expByteLen := uint64((stack.Back(1).BitLen() + 7) / 8) bytes := stack.Back(1)
expByteLen := uint64((bytes.BitLen() + 7) / 8)
var ( var (
gas = expByteLen * params.ExpByteFrontier // no overflow check required. Max is 256 * ExpByte gas gas = expByteLen * params.ExpByteFrontier // no overflow check required. Max is 256 * ExpByte gas
@ -358,7 +365,8 @@ func gasExpFrontier(evm *EVM, contract *Contract, stack *Stack, mem *Memory, mem
} }
func gasExpEIP158(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { func gasExpEIP158(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
expByteLen := uint64((stack.Back(1).BitLen() + 7) / 8) bytes := stack.Back(1)
expByteLen := uint64((bytes.BitLen() + 7) / 8)
var ( var (
gas = expByteLen * params.ExpByteEIP158 // no overflow check required. Max is 256 * ExpByte gas gas = expByteLen * params.ExpByteEIP158 // no overflow check required. Max is 256 * ExpByte gas
@ -373,8 +381,9 @@ func gasExpEIP158(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memor
func gasCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { func gasCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
var ( var (
gas uint64 gas uint64
transfersValue = !stack.Back(2).IsZero() value, addr = stack.Back(2), stack.Back(1)
address = common.Address(stack.Back(1).Bytes20()) transfersValue = !value.IsZero()
address = common.Address(addr.Bytes20())
) )
if evm.chainRules.IsEIP158 { if evm.chainRules.IsEIP158 {
if transfersValue && evm.StateDB.Empty(address) { if transfersValue && evm.StateDB.Empty(address) {
@ -419,18 +428,19 @@ func gasCallCode(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memory
return 0, err return 0, err
} }
var ( var (
gas uint64 gas uint64
overflow bool overflow bool
value, addr = stack.Back(2), stack.Back(1)
) )
if stack.Back(2).Sign() != 0 && !evm.chainRules.IsEIP4762 { if value.Sign() != 0 && !evm.chainRules.IsEIP4762 {
gas += params.CallValueTransferGas gas += params.CallValueTransferGas
} }
if gas, overflow = math.SafeAdd(gas, memoryGas); overflow { if gas, overflow = math.SafeAdd(gas, memoryGas); overflow {
return 0, ErrGasUintOverflow return 0, ErrGasUintOverflow
} }
if evm.chainRules.IsEIP4762 && !contract.IsSystemCall { if evm.chainRules.IsEIP4762 && !contract.IsSystemCall {
address := common.Address(stack.Back(1).Bytes20()) address := common.Address(addr.Bytes20())
transfersValue := !stack.Back(2).IsZero() transfersValue := !value.IsZero()
if transfersValue { if transfersValue {
gas, overflow = math.SafeAdd(gas, evm.AccessEvents.ValueTransferGas(contract.Address(), address)) gas, overflow = math.SafeAdd(gas, evm.AccessEvents.ValueTransferGas(contract.Address(), address))
if overflow { if overflow {
@ -485,7 +495,8 @@ func gasSelfdestruct(evm *EVM, contract *Contract, stack *Stack, mem *Memory, me
// EIP150 homestead gas reprice fork: // EIP150 homestead gas reprice fork:
if evm.chainRules.IsEIP150 { if evm.chainRules.IsEIP150 {
gas = params.SelfdestructGasEIP150 gas = params.SelfdestructGasEIP150
var address = common.Address(stack.Back(0).Bytes20()) addr := stack.Back(0)
var address = common.Address(addr.Bytes20())
if evm.chainRules.IsEIP158 { if evm.chainRules.IsEIP158 {
// if empty and transfers value // if empty and transfers value

View file

@ -50,8 +50,9 @@ func memoryMStore(stack *Stack) (uint64, bool) {
func memoryMcopy(stack *Stack) (uint64, bool) { func memoryMcopy(stack *Stack) (uint64, bool) {
mStart := stack.Back(0) // stack[0]: dest mStart := stack.Back(0) // stack[0]: dest
if stack.Back(1).Gt(mStart) { source := stack.Back(1)
mStart = stack.Back(1) // stack[1]: source if source.Gt(&mStart) {
mStart = source // stack[1]: source
} }
return calcMemSize64(mStart, stack.Back(2)) // stack[2]: length return calcMemSize64(mStart, stack.Back(2)) // stack[2]: length
} }

View file

@ -120,10 +120,11 @@ func gasExtCodeCopyEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memo
if err != nil { if err != nil {
return 0, err return 0, err
} }
addr := common.Address(stack.Back(0).Bytes20()) addr := stack.Back(0)
address := common.Address(addr.Bytes20())
// Check slot presence in the access list // Check slot presence in the access list
if !evm.StateDB.AddressInAccessList(addr) { if !evm.StateDB.AddressInAccessList(address) {
evm.StateDB.AddAddressToAccessList(addr) evm.StateDB.AddAddressToAccessList(address)
var overflow bool var overflow bool
// We charge (cold-warm), since 'warm' is already charged as constantGas // We charge (cold-warm), since 'warm' is already charged as constantGas
if gas, overflow = math.SafeAdd(gas, params.ColdAccountAccessCostEIP2929-params.WarmStorageReadCostEIP2929); overflow { if gas, overflow = math.SafeAdd(gas, params.ColdAccountAccessCostEIP2929-params.WarmStorageReadCostEIP2929); overflow {
@ -142,11 +143,12 @@ func gasExtCodeCopyEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memo
// - extcodesize, // - extcodesize,
// - (ext) balance // - (ext) balance
func gasEip2929AccountCheck(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { func gasEip2929AccountCheck(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
addr := common.Address(stack.Back(0).Bytes20()) addr := stack.Back(0)
address := common.Address(addr.Bytes20())
// Check slot presence in the access list // Check slot presence in the access list
if !evm.StateDB.AddressInAccessList(addr) { if !evm.StateDB.AddressInAccessList(address) {
// If the caller cannot afford the cost, this change will be rolled back // If the caller cannot afford the cost, this change will be rolled back
evm.StateDB.AddAddressToAccessList(addr) evm.StateDB.AddAddressToAccessList(address)
// The warm storage read cost is already charged as constantGas // The warm storage read cost is already charged as constantGas
return params.ColdAccountAccessCostEIP2929 - params.WarmStorageReadCostEIP2929, nil return params.ColdAccountAccessCostEIP2929 - params.WarmStorageReadCostEIP2929, nil
} }
@ -155,14 +157,15 @@ func gasEip2929AccountCheck(evm *EVM, contract *Contract, stack *Stack, mem *Mem
func makeCallVariantGasCallEIP2929(oldCalculator gasFunc, addressPosition int) gasFunc { func makeCallVariantGasCallEIP2929(oldCalculator gasFunc, addressPosition int) gasFunc {
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
addr := common.Address(stack.Back(addressPosition).Bytes20()) addr := stack.Back(addressPosition)
address := common.Address(addr.Bytes20())
// Check slot presence in the access list // Check slot presence in the access list
warmAccess := evm.StateDB.AddressInAccessList(addr) warmAccess := evm.StateDB.AddressInAccessList(address)
// The WarmStorageReadCostEIP2929 (100) is already deducted in the form of a constant cost, so // The WarmStorageReadCostEIP2929 (100) is already deducted in the form of a constant cost, so
// the cost to charge for cold access, if any, is Cold - Warm // the cost to charge for cold access, if any, is Cold - Warm
coldCost := params.ColdAccountAccessCostEIP2929 - params.WarmStorageReadCostEIP2929 coldCost := params.ColdAccountAccessCostEIP2929 - params.WarmStorageReadCostEIP2929
if !warmAccess { if !warmAccess {
evm.StateDB.AddAddressToAccessList(addr) evm.StateDB.AddAddressToAccessList(address)
// Charge the remaining difference here already, to correctly calculate available // Charge the remaining difference here already, to correctly calculate available
// gas for call // gas for call
if !contract.UseGas(coldCost, evm.Config.Tracer, tracing.GasChangeCallStorageColdAccess) { if !contract.UseGas(coldCost, evm.Config.Tracer, tracing.GasChangeCallStorageColdAccess) {
@ -225,7 +228,8 @@ func makeSelfdestructGasFn(refundsEnabled bool) gasFunc {
gasFunc := func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { gasFunc := func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
var ( var (
gas uint64 gas uint64
address = common.Address(stack.Back(0).Bytes20()) addr = stack.Back(0)
address = common.Address(addr.Bytes20())
) )
if !evm.StateDB.AddressInAccessList(address) { if !evm.StateDB.AddressInAccessList(address) {
// If the caller cannot afford the cost, this change will be rolled back // If the caller cannot afford the cost, this change will be rolled back
@ -254,13 +258,14 @@ var (
func makeCallVariantGasCallEIP7702(oldCalculator gasFunc) gasFunc { func makeCallVariantGasCallEIP7702(oldCalculator gasFunc) gasFunc {
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
var ( var (
total uint64 // total dynamic gas used total uint64 // total dynamic gas used
addr = common.Address(stack.Back(1).Bytes20()) addr = stack.Back(1)
address = common.Address(addr.Bytes20())
) )
// Check slot presence in the access list // Check slot presence in the access list
if !evm.StateDB.AddressInAccessList(addr) { if !evm.StateDB.AddressInAccessList(address) {
evm.StateDB.AddAddressToAccessList(addr) evm.StateDB.AddAddressToAccessList(address)
// The WarmStorageReadCostEIP2929 (100) is already deducted in the form of a constant cost, so // The WarmStorageReadCostEIP2929 (100) is already deducted in the form of a constant cost, so
// the cost to charge for cold access, if any, is Cold - Warm // the cost to charge for cold access, if any, is Cold - Warm
coldCost := params.ColdAccountAccessCostEIP2929 - params.WarmStorageReadCostEIP2929 coldCost := params.ColdAccountAccessCostEIP2929 - params.WarmStorageReadCostEIP2929
@ -273,7 +278,7 @@ func makeCallVariantGasCallEIP7702(oldCalculator gasFunc) gasFunc {
} }
// Check if code is a delegation and if so, charge for resolution. // Check if code is a delegation and if so, charge for resolution.
if target, ok := types.ParseDelegation(evm.StateDB.GetCode(addr)); ok { if target, ok := types.ParseDelegation(evm.StateDB.GetCode(address)); ok {
var cost uint64 var cost uint64
if evm.StateDB.AddressInAccessList(target) { if evm.StateDB.AddressInAccessList(target) {
cost = params.WarmStorageReadCostEIP2929 cost = params.WarmStorageReadCostEIP2929

View file

@ -25,7 +25,8 @@ import (
) )
func gasSStore4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { func gasSStore4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
gas := evm.AccessEvents.SlotGas(contract.Address(), stack.Back(0).Bytes32(), true) slot := stack.Back(0)
gas := evm.AccessEvents.SlotGas(contract.Address(), slot.Bytes32(), true)
if gas == 0 { if gas == 0 {
gas = params.WarmStorageReadCostEIP2929 gas = params.WarmStorageReadCostEIP2929
} }
@ -33,7 +34,8 @@ func gasSStore4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memo
} }
func gasSLoad4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { func gasSLoad4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
gas := evm.AccessEvents.SlotGas(contract.Address(), stack.Back(0).Bytes32(), false) slot := stack.Back(0)
gas := evm.AccessEvents.SlotGas(contract.Address(), slot.Bytes32(), false)
if gas == 0 { if gas == 0 {
gas = params.WarmStorageReadCostEIP2929 gas = params.WarmStorageReadCostEIP2929
} }
@ -44,7 +46,8 @@ func gasBalance4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, mem
if contract.IsSystemCall { if contract.IsSystemCall {
return 0, nil return 0, nil
} }
address := stack.peek().Bytes20() addr := stack.Back(0)
address := addr.Bytes20()
gas := evm.AccessEvents.BasicDataGas(address, false) gas := evm.AccessEvents.BasicDataGas(address, false)
if gas == 0 { if gas == 0 {
gas = params.WarmStorageReadCostEIP2929 gas = params.WarmStorageReadCostEIP2929
@ -53,7 +56,8 @@ func gasBalance4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, mem
} }
func gasExtCodeSize4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { func gasExtCodeSize4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
address := stack.Back(0).Bytes20() addr := stack.Back(0)
address := addr.Bytes20()
if _, isPrecompile := evm.precompile(address); isPrecompile { if _, isPrecompile := evm.precompile(address); isPrecompile {
return 0, nil return 0, nil
} }
@ -71,7 +75,8 @@ func gasExtCodeHash4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory,
if contract.IsSystemCall { if contract.IsSystemCall {
return 0, nil return 0, nil
} }
address := stack.peek().Bytes20() addr := stack.Back(0)
address := addr.Bytes20()
if _, isPrecompile := evm.precompile(address); isPrecompile { if _, isPrecompile := evm.precompile(address); isPrecompile {
return 0, nil return 0, nil
} }
@ -110,7 +115,8 @@ var (
) )
func gasSelfdestructEIP4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { func gasSelfdestructEIP4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
beneficiaryAddr := common.Address(stack.Back(0).Bytes20()) addr := stack.Back(0)
beneficiaryAddr := common.Address(addr.Bytes20())
if _, isPrecompile := evm.precompile(beneficiaryAddr); isPrecompile { if _, isPrecompile := evm.precompile(beneficiaryAddr); isPrecompile {
return 0, nil return 0, nil
} }
@ -161,8 +167,9 @@ func gasExtCodeCopyEIP4762(evm *EVM, contract *Contract, stack *Stack, mem *Memo
if contract.IsSystemCall { if contract.IsSystemCall {
return gas, nil return gas, nil
} }
addr := common.Address(stack.peek().Bytes20()) addr := stack.Back(0)
wgas := evm.AccessEvents.BasicDataGas(addr, false) address := common.Address(addr.Bytes20())
wgas := evm.AccessEvents.BasicDataGas(address, false)
if wgas == 0 { if wgas == 0 {
wgas = params.WarmStorageReadCostEIP2929 wgas = params.WarmStorageReadCostEIP2929
} }

View file

@ -181,11 +181,7 @@ func (s *Stack) dup(n int) {
s.size++ s.size++
} }
func (s *Stack) peek() *uint256.Int {
return &s.inner.data[s.bottom+s.size-1]
}
// Back returns the n'th item in stack // Back returns the n'th item in stack
func (s *Stack) Back(n int) *uint256.Int { func (s *Stack) Back(n int) uint256.Int {
return &s.inner.data[s.bottom+s.size-n-1] return s.inner.data[s.bottom+s.size-n-1]
} }