mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-19 10:22:23 +00:00
EXTCALL test fixes
* use corret stack heights for value and address in EXTCALL * New extCallGas function that handles min retained and min callee gas * Handle min gas failures via tempCallGas == 0 * remove stipend refund for EXT*CALL * Call Stack too deep should return 1
This commit is contained in:
parent
8d68a86bf0
commit
4a4d3b07e4
5 changed files with 78 additions and 31 deletions
|
|
@ -1055,13 +1055,20 @@ func opExtCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]
|
||||||
if interpreter.readOnly && !value.IsZero() {
|
if interpreter.readOnly && !value.IsZero() {
|
||||||
return nil, ErrWriteProtection
|
return nil, ErrWriteProtection
|
||||||
}
|
}
|
||||||
if !value.IsZero() {
|
|
||||||
gas += params.CallStipend
|
var (
|
||||||
|
ret []byte
|
||||||
|
returnGas uint64
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
if interpreter.evm.callGasTemp == 0 {
|
||||||
|
// zero temp call gas indicates a min retained gas error
|
||||||
|
ret, returnGas, err = nil, 0, ErrExecutionReverted
|
||||||
|
} else {
|
||||||
|
ret, returnGas, err = interpreter.evm.Call(scope.Contract, toAddr, args, gas, &value)
|
||||||
}
|
}
|
||||||
|
|
||||||
ret, returnGas, err := interpreter.evm.Call(scope.Contract, toAddr, args, gas, &value)
|
if err == ErrExecutionReverted || err == ErrInsufficientBalance || err == ErrDepth {
|
||||||
|
|
||||||
if err == ErrExecutionReverted {
|
|
||||||
temp.SetOne()
|
temp.SetOne()
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
temp.SetUint64(2)
|
temp.SetUint64(2)
|
||||||
|
|
@ -1102,11 +1109,14 @@ func opExtDelegateCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeCont
|
||||||
err = ErrExecutionReverted
|
err = ErrExecutionReverted
|
||||||
ret = nil
|
ret = nil
|
||||||
returnGas = gas
|
returnGas = gas
|
||||||
|
} else if interpreter.evm.callGasTemp == 0 {
|
||||||
|
// zero temp call gas indicates a min retained gas error
|
||||||
|
ret, returnGas, err = nil, 0, ErrExecutionReverted
|
||||||
} else {
|
} else {
|
||||||
ret, returnGas, err = interpreter.evm.DelegateCall(scope.Contract, toAddr, args, gas, true)
|
ret, returnGas, err = interpreter.evm.DelegateCall(scope.Contract, toAddr, args, gas, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err == ErrExecutionReverted {
|
if err == ErrExecutionReverted || err == ErrDepth {
|
||||||
temp.SetOne()
|
temp.SetOne()
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
temp.SetUint64(2)
|
temp.SetUint64(2)
|
||||||
|
|
@ -1136,8 +1146,19 @@ func opExtStaticCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContex
|
||||||
// Get arguments from the memory.
|
// Get arguments from the memory.
|
||||||
args := scope.Memory.GetPtr(inOffset.Uint64(), inSize.Uint64())
|
args := scope.Memory.GetPtr(inOffset.Uint64(), inSize.Uint64())
|
||||||
|
|
||||||
ret, returnGas, err := interpreter.evm.StaticCall(scope.Contract, toAddr, args, gas)
|
var (
|
||||||
if err == ErrExecutionReverted {
|
ret []byte
|
||||||
|
returnGas uint64
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
if interpreter.evm.callGasTemp == 0 {
|
||||||
|
// zero temp call gas indicates a min retained gas error
|
||||||
|
ret, returnGas, err = nil, 0, ErrExecutionReverted
|
||||||
|
} else {
|
||||||
|
ret, returnGas, err = interpreter.evm.StaticCall(scope.Contract, toAddr, args, gas)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err == ErrExecutionReverted || err == ErrDepth {
|
||||||
temp.SetOne()
|
temp.SetOne()
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
temp.SetUint64(2)
|
temp.SetUint64(2)
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,7 @@ var (
|
||||||
ErrInvalidEOFInitcode = errors.New("invalid eof initcode")
|
ErrInvalidEOFInitcode = errors.New("invalid eof initcode")
|
||||||
ErrNonceUintOverflow = errors.New("nonce uint64 overflow")
|
ErrNonceUintOverflow = errors.New("nonce uint64 overflow")
|
||||||
ErrInvalidNumberOfOutputs = errors.New("invalid number of outputs")
|
ErrInvalidNumberOfOutputs = errors.New("invalid number of outputs")
|
||||||
ErrInvalidNonReturningFlag = errors.New("Invalid non-returning flag, bad RETF")
|
ErrInvalidNonReturningFlag = errors.New("invalid non-returning flag, bad RETF")
|
||||||
|
|
||||||
// errStopToken is an internal token indicating interpreter loop termination,
|
// errStopToken is an internal token indicating interpreter loop termination,
|
||||||
// never returned to outside callers.
|
// never returned to outside callers.
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
package vm
|
package vm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/holiman/uint256"
|
"github.com/holiman/uint256"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -53,3 +54,32 @@ func callGas(isEip150 bool, availableGas, base uint64, callCost *uint256.Int) (u
|
||||||
|
|
||||||
return callCost.Uint64(), nil
|
return callCost.Uint64(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// extCallGas returns the actual gas cost for ext*call operations.
|
||||||
|
//
|
||||||
|
// EOF v1 includes EIP-150 rules (all but 1/64) with a floor of MIN_RETAINED_GAS (5000)
|
||||||
|
// and a minimum returned value of MIN_CALLE_GASS (2300).
|
||||||
|
// There is also no call gas, so all available gas is used.
|
||||||
|
//
|
||||||
|
// If the minimum retained gas constraint is violated, zero gas and no error is returned
|
||||||
|
func extCallGas(availableGas, base uint64) (uint64, error) {
|
||||||
|
if availableGas < base {
|
||||||
|
return 0, ErrOutOfGas
|
||||||
|
}
|
||||||
|
availableGas = availableGas - base
|
||||||
|
if availableGas < params.ExtCallMinRetainedGas {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
retainedGas := availableGas / 64
|
||||||
|
if retainedGas < params.ExtCallMinRetainedGas {
|
||||||
|
retainedGas = params.ExtCallMinRetainedGas
|
||||||
|
}
|
||||||
|
gas := availableGas - retainedGas
|
||||||
|
|
||||||
|
if gas < params.ExtCallMinCalleeGas {
|
||||||
|
return 0, nil
|
||||||
|
} else {
|
||||||
|
return gas, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,6 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/math"
|
"github.com/ethereum/go-ethereum/common/math"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/holiman/uint256"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// memoryGasCost calculates the quadratic gas for memory expansion. It does so
|
// memoryGasCost calculates the quadratic gas for memory expansion. It does so
|
||||||
|
|
@ -484,37 +483,32 @@ func gasStaticCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memo
|
||||||
func gasExtCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
func gasExtCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||||
var (
|
var (
|
||||||
gas uint64
|
gas uint64
|
||||||
transfersValue = !stack.Back(2).IsZero()
|
transfersValue = !stack.Back(3).IsZero()
|
||||||
address = common.Address(stack.Back(1).Bytes20())
|
address = common.Address(stack.Back(0).Bytes20())
|
||||||
|
overflow bool
|
||||||
)
|
)
|
||||||
if evm.chainRules.IsEIP158 {
|
if transfersValue {
|
||||||
if transfersValue && evm.StateDB.Empty(address) {
|
if evm.StateDB.Empty(address) {
|
||||||
gas += params.CallNewAccountGas
|
gas += params.CallNewAccountGas
|
||||||
}
|
}
|
||||||
} else if !evm.StateDB.Exist(address) {
|
if evm.chainRules.IsEIP4762 {
|
||||||
gas += params.CallNewAccountGas
|
gas, overflow = math.SafeAdd(gas, evm.AccessEvents.ValueTransferGas(contract.Address(), address))
|
||||||
}
|
if overflow {
|
||||||
if transfersValue && !evm.chainRules.IsEIP4762 {
|
return 0, ErrGasUintOverflow
|
||||||
gas += params.CallValueTransferGas
|
}
|
||||||
|
} else {
|
||||||
|
gas += params.CallValueTransferGas
|
||||||
|
}
|
||||||
}
|
}
|
||||||
memoryGas, err := memoryGasCost(mem, memorySize)
|
memoryGas, err := memoryGasCost(mem, memorySize)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
var overflow bool
|
|
||||||
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 {
|
|
||||||
if transfersValue {
|
|
||||||
gas, overflow = math.SafeAdd(gas, evm.AccessEvents.ValueTransferGas(contract.Address(), address))
|
|
||||||
if overflow {
|
|
||||||
return 0, ErrGasUintOverflow
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
evm.callGasTemp, err = callGas(true, contract.Gas, gas, new(uint256.Int).SetUint64(contract.Gas))
|
evm.callGasTemp, err = extCallGas(contract.Gas, gas)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
@ -531,7 +525,7 @@ func gasExtDelegateCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory,
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
evm.callGasTemp, err = callGas(true, contract.Gas, gas, new(uint256.Int).SetUint64(contract.Gas))
|
evm.callGasTemp, err = extCallGas(contract.Gas, gas)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
@ -547,7 +541,7 @@ func gasExtStaticCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, m
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
evm.callGasTemp, err = callGas(true, contract.Gas, gas, new(uint256.Int).SetUint64(contract.Gas))
|
evm.callGasTemp, err = extCallGas(contract.Gas, gas)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -89,6 +89,8 @@ const (
|
||||||
CreateNGasEip4762 uint64 = 1000 // Once per CREATEn operations post-verkle
|
CreateNGasEip4762 uint64 = 1000 // Once per CREATEn operations post-verkle
|
||||||
SelfdestructRefundGas uint64 = 24000 // Refunded following a selfdestruct operation.
|
SelfdestructRefundGas uint64 = 24000 // Refunded following a selfdestruct operation.
|
||||||
MemoryGas uint64 = 3 // Times the address of the (highest referenced byte in memory + 1). NOTE: referencing happens on read, write and in instructions such as RETURN and CALL.
|
MemoryGas uint64 = 3 // Times the address of the (highest referenced byte in memory + 1). NOTE: referencing happens on read, write and in instructions such as RETURN and CALL.
|
||||||
|
ExtCallMinRetainedGas uint64 = 5000 // For EXT*CALL this is the minimum gas that the EIp158 1/64th rule must retain
|
||||||
|
ExtCallMinCalleeGas uint64 = 2300 // For EXT*CALL this is the minimum gas that must be passed to the callee, ignoring 63/64
|
||||||
|
|
||||||
TxDataNonZeroGasFrontier uint64 = 68 // Per byte of data attached to a transaction that is not equal to zero. NOTE: Not payable on data of calls between transactions.
|
TxDataNonZeroGasFrontier uint64 = 68 // Per byte of data attached to a transaction that is not equal to zero. NOTE: Not payable on data of calls between transactions.
|
||||||
TxDataNonZeroGasEIP2028 uint64 = 16 // Per byte of non zero data attached to a transaction after EIP 2028 (part in Istanbul)
|
TxDataNonZeroGasEIP2028 uint64 = 16 // Per byte of non zero data attached to a transaction after EIP 2028 (part in Istanbul)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue